Geeks making the world a bit better.

Sending Key Events to pygame programs

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)

5 comments

#1 Steve Lee on 11.19.07 at 4:43 am

Another approach on linux with Gnome is to use the AT-SPI’s generateKeyboardEvent() and related functions

Here’s an example with an additional dependecy on GTK

import pyatspi
import gtk.keysyms as keysyms

mod_key_codes = {'C' : 37, 'A' : 64, 'S' : 50 }

def sendKeyCombination(key, mods):
  if key[0].isdigit():
    keysym_name = '_'+key
  else:
    keysym_name = key

  try:
    keysym = getattr(keysyms, keysym_name)
  except AttributeError:
    return

  mod_codes = [mod_key_codes[c] for c in mods]
  for mod in mod_codes:
    pyatspi.Registry.generateKeyboardEvent(mod, None, pyatspi.KEY_PRESS)
  #wait?
  pyatspi.Registry.generateKeyboardEvent(keysym, None, pyatspi.KEY_SYM)
  #wait?
  for mod in mod_codes:
    pyatspi.Registry.generateKeyboardEvent(mod, None, pyatspi.KEY_RELEASE)

def sendString(s):
  for ch in s:
    keyval = gtk.gdk.unicode_to_keyval(ord(ch))
    pyatspi.Registry.generateKeyboardEvent(keyval, None, pyatspi.KEY_SYM)
    #wait?
#2 gb on 11.19.07 at 8:12 am

Thanks for the example. I tried that first but couldn’t get it to work on Feisty. I got some complaint about argument marshaling from the generateKeyboardEvent call. Perhaps now on Gutsy that will work.

#3 Pam on 03.26.09 at 2:16 pm

Will this work with windows? I am running pygame and trying to send a keystroke to invoke a trigger. It doesn’t seem to work with windows.

#4 gb on 03.26.09 at 4:18 pm

Yes, my code was for windows.

#5 Cefn Hoile on 09.10.09 at 6:12 am

If you want to use python bindings to X Libraries to generate more ’symbolic’ keypresses, such as XK_Backspace, you can refer to the kefsymdef.h file from a linux distribution. This enables you to get keysyms like…

# codes from keysymdef.h
class codes:
backspace = Xlib.XKeysymToKeycode(dpy,0xff08) # XK_BackSpace
delete = Xlib.XKeysymToKeycode(dpy,0xffff) # XK_Delete
escape = Xlib.XKeysymToKeycode(dpy,0xff1b) # XK_Escape
tab = Xlib.XKeysymToKeycode(dpy,0xff09) # XK_Tab
returnkey = Xlib.XKeysymToKeycode(dpy,0xff0d) # XK_Return

Leave a Comment