Sending Key Events to pygame programs

| tags: programming

I needed to send key events from one python program to another. Using SendKeys on Windows worked fine in my tests but when I tried to send key events to a pygame program it completely ignored them. Some searching revealed that DirectInput ignores events generated with SendKeys. I learned that I needed to use SendInput. I found lots of partial examples but nothing that quite did the job. Here is some code (scraped together from multiple web pages) that works for me:


from ctypes import *
import time
import os

if os.name == 'nt':
    PUL = POINTER(c_ulong)
    class KeyBdInput(Structure):
        _fields_ = [("wVk", c_ushort),
                    ("wScan", c_ushort),
                    ("dwFlags", c_ulong),
                    ("time", c_ulong),
                    ("dwExtraInfo", PUL)]

    class HardwareInput(Structure):
        _fields_ = [("uMsg", c_ulong),
                    ("wParamL", c_short),
                    ("wParamH", c_ushort)]

    class MouseInput(Structure):
        _fields_ = [("dx", c_long),
                    ("dy", c_long),
                    ("mouseData", c_ulong),
                    ("dwFlags", c_ulong),
                    ("time",c_ulong),
                    ("dwExtraInfo", PUL)]

    class Input_I(Union):
        _fields_ = [("ki", KeyBdInput),
                    ("mi", MouseInput),
                    ("hi", HardwareInput)]

    class Input(Structure):
        _fields_ = [("type", c_ulong),
                    ("ii", Input_I)]

    KEYEVENTF_KEYUP = 0x2
    KEYEVENTF_UNICODE = 0x4
    KEYEVENTF_SCANCODE = 0x8
    MAPVK_VK_TO_VSC = 0

    def SendInput(txt):
        i = Input()
        i.type = 1
        extra = c_ulong(0)
        pextra = pointer(extra)
        for c in txt:
            vk = windll.user32.VkKeyScanW(ord(c))
            sc = windll.user32.MapVirtualKeyW(vk&0xff, MAPVK_VK_TO_VSC)
            i.ii.ki.wVk = 0
            i.ii.ki.wScan = sc
            i.ii.ki.dwFlags = KEYEVENTF_SCANCODE
            i.ii.ki.time = 0
            i.ii.ki.dwExtraInfo = pextra
            windll.user32.SendInput(1, byref(i), sizeof(i))
            i.ii.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP
            windll.user32.SendInput(1, byref(i), sizeof(i))

    def SendKeyPress(key):
        i = Input()
        i.type = 1
        extra = c_ulong(0)
        pextra = pointer(extra)
        vk = windll.user32.VkKeyScanW(ord(key))
        sc = windll.user32.MapVirtualKeyW(vk&0xff, MAPVK_VK_TO_VSC)
        i.ii.ki.wVk = 0
        i.ii.ki.wScan = sc
        i.ii.ki.dwFlags = KEYEVENTF_SCANCODE
        i.ii.ki.time = 0
        i.ii.ki.dwExtraInfo = pextra
        windll.user32.SendInput(1, byref(i), sizeof(i))

    def SendKeyRelease(key):
        i = Input()
        i.type = 1
        extra = c_ulong(0)
        pextra = pointer(extra)
        vk = windll.user32.VkKeyScanW(ord(key))
        sc = windll.user32.MapVirtualKeyW(vk&0xff, MAPVK_VK_TO_VSC)
        i.ii.ki.wVk = 0
        i.ii.ki.wScan = sc
        i.ii.ki.time = 0
        i.ii.ki.dwExtraInfo = pextra
        i.ii.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP
        windll.user32.SendInput(1, byref(i), sizeof(i))

This code does not handle pressing shift and control to create capital letters or control keys but that info is apparently encoded into the upper byte of vk.

Accomplishing the same result on Linux is easy. Here is the rest of the code


elif os.name == 'posix':
    Xtst = CDLL("libXtst.so.6")
    Xlib = CDLL("libX11.so.6")
    dpy = Xtst.XOpenDisplay(None)
    def SendInput( txt ):
        for c in txt:
            sym = Xlib.XStringToKeysym(c)
            code = Xlib.XKeysymToKeycode(dpy, sym)
            Xtst.XTestFakeKeyEvent(dpy, code, True, 0)
            Xtst.XTestFakeKeyEvent(dpy, code, False, 0)
        Xlib.XFlush(dpy)

    def SendKeyPress(key):
        sym = Xlib.XStringToKeysym(str(key))
        code = Xlib.XKeysymToKeycode(dpy, sym)
        Xtst.XTestFakeKeyEvent(dpy, code, True, 0)
        Xlib.XFlush(dpy)

    def SendKeyRelease(key):
        sym = Xlib.XStringToKeysym(str(key))
        code = Xlib.XKeysymToKeycode(dpy, sym)
        Xtst.XTestFakeKeyEvent(dpy, code, False, 0)
        Xlib.XFlush(dpy)