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
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?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.
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.
Yes, my code was for windows.
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