# directkeys.py | |
# http://stackoverflow.com/questions/13564851/generate-keyboard-events | |
# msdn.microsoft.com/en-us/library/dd375731 | |
import ctypes | |
from ctypes import wintypes | |
import time | |
user32 = ctypes.WinDLL('user32', use_last_error=True) | |
INPUT_MOUSE = 0 | |
INPUT_KEYBOARD = 1 | |
INPUT_HARDWARE = 2 | |
KEYEVENTF_EXTENDEDKEY = 0x0001 | |
KEYEVENTF_KEYUP = 0x0002 | |
KEYEVENTF_UNICODE = 0x0004 | |
KEYEVENTF_SCANCODE = 0x0008 | |
MAPVK_VK_TO_VSC = 0 | |
# List of all codes for keys: | |
# # msdn.microsoft.com/en-us/library/dd375731 | |
UP = 0x26 | |
DOWN = 0x28 | |
A = 0x41 | |
# C struct definitions | |
wintypes.ULONG_PTR = wintypes.WPARAM | |
class MOUSEINPUT(ctypes.Structure): | |
_fields_ = (("dx", wintypes.LONG), | |
("dy", wintypes.LONG), | |
("mouseData", wintypes.DWORD), | |
("dwFlags", wintypes.DWORD), | |
("time", wintypes.DWORD), | |
("dwExtraInfo", wintypes.ULONG_PTR)) | |
class KEYBDINPUT(ctypes.Structure): | |
_fields_ = (("wVk", wintypes.WORD), | |
("wScan", wintypes.WORD), | |
("dwFlags", wintypes.DWORD), | |
("time", wintypes.DWORD), | |
("dwExtraInfo", wintypes.ULONG_PTR)) | |
def __init__(self, *args, **kwds): | |
super(KEYBDINPUT, self).__init__(*args, **kwds) | |
# some programs use the scan code even if KEYEVENTF_SCANCODE | |
# isn't set in dwFflags, so attempt to map the correct code. | |
if not self.dwFlags & KEYEVENTF_UNICODE: | |
self.wScan = user32.MapVirtualKeyExW(self.wVk, | |
MAPVK_VK_TO_VSC, 0) | |
class HARDWAREINPUT(ctypes.Structure): | |
_fields_ = (("uMsg", wintypes.DWORD), | |
("wParamL", wintypes.WORD), | |
("wParamH", wintypes.WORD)) | |
class INPUT(ctypes.Structure): | |
class _INPUT(ctypes.Union): | |
_fields_ = (("ki", KEYBDINPUT), | |
("mi", MOUSEINPUT), | |
("hi", HARDWAREINPUT)) | |
_anonymous_ = ("_input",) | |
_fields_ = (("type", wintypes.DWORD), | |
("_input", _INPUT)) | |
LPINPUT = ctypes.POINTER(INPUT) | |
def _check_count(result, func, args): | |
if result == 0: | |
raise ctypes.WinError(ctypes.get_last_error()) | |
return args | |
user32.SendInput.errcheck = _check_count | |
user32.SendInput.argtypes = (wintypes.UINT, # nInputs | |
LPINPUT, # pInputs | |
ctypes.c_int) # cbSize | |
# Functions | |
def PressKey(hexKeyCode): | |
x = INPUT(type=INPUT_KEYBOARD, | |
ki=KEYBDINPUT(wVk=hexKeyCode)) | |
user32.SendInput(1, ctypes.byref(x), ctypes.sizeof(x)) | |
def ReleaseKey(hexKeyCode): | |
x = INPUT(type=INPUT_KEYBOARD, | |
ki=KEYBDINPUT(wVk=hexKeyCode, | |
dwFlags=KEYEVENTF_KEYUP)) | |
user32.SendInput(1, ctypes.byref(x), ctypes.sizeof(x)) | |
if __name__ == "__main__": | |
PressKey(A) | |
time.sleep(0.5) | |
ReleaseKey(A) | |
print("Pressed") |
holy shit...this is god code
Some elaboration would be useful here
Thank you for your script that I'm using in a game.
But I have one big issue, I have a latency/delay of 4 frames, around 50ms.
Is there a way to fix it ?
This is awesome. That in combination with win32gui = perfection
Thanks! Used it in my project https://github.com/freecodecampster/DirectInputServer.
hey, my pc language is turkish and we got "ı" word in our language. My problem is even though I send ascii codes of "i" or "I" for example 0x49
it types "ı". how can i solve that?
Thank you for your script that I'm using in a game. But I have one big issue, I have a latency/delay of 4 frames, around 50ms. Is there a way to fix it ?
It's normal. The inputs are sent one after an other. Although the delay shouldn't be that notisablebe for a few inputs, the more you have the bigger the delay betwen the first and last one. If you want to have no delay the only way is using multi threading. But the code will need to be modified.
hey, my pc language is turkish and we got "ı" word in our language. My problem is even though I send ascii codes of "i" or "I" for example 0x49 it types "ı". how can i solve that?
Your problem could come from the code using virtual keys not scan keys. This is an easy fix but the code needs to be tweaked. In the lower part of the code
ki=KEYBDINPUT(wVk=hexKeyCode))
change
wvkto
wScan
sir, this code is just amazing.
Did you code this? because this needs more recognition.
I would love to see some detailed explanation of how the code works since I find it quite confusing since you are dealing with low level problem which is a rare case in python.