Created
March 15, 2019 20:26
-
-
Save dusekdan/d84dd7627cbb46ec4f7f93dd515a476e to your computer and use it in GitHub Desktop.
Sending Messages to Game Windows Through DirectInput and Win32API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import ctypes | |
import win32api | |
w = WindowMgr() | |
PUL = ctypes.POINTER(ctypes.c_ulong) | |
class KeyBdInput(ctypes.Structure): | |
_fields_ = [("wVk", ctypes.c_ushort), | |
("wScan", ctypes.c_ushort), | |
("dwFlags", ctypes.c_ulong), | |
("time", ctypes.c_ulong), | |
("dwExtraInfo", PUL)] | |
class HardwareInput(ctypes.Structure): | |
_fields_ = [("uMsg", ctypes.c_ulong), | |
("wParamL", ctypes.c_short), | |
("wParamH", ctypes.c_ushort)] | |
class MouseInput(ctypes.Structure): | |
_fields_ = [("dx", ctypes.c_long), | |
("dy", ctypes.c_long), | |
("mouseData", ctypes.c_ulong), | |
("dwFlags", ctypes.c_ulong), | |
("time", ctypes.c_ulong), | |
("dwExtraInfo", PUL)] | |
class Input_I(ctypes.Union): | |
_fields_ = [("ki", KeyBdInput), | |
("mi", MouseInput), | |
("hi", HardwareInput)] | |
class Input(ctypes.Structure): | |
_fields_ = [("type", ctypes.c_ulong), | |
("ii", Input_I)] | |
def press_key(key): | |
extra = ctypes.c_ulong(0) | |
ii_ = Input_I() | |
flags = 0x0008 | |
ii_.ki = KeyBdInput(0, key, flags, 0, ctypes.pointer(extra)) | |
x = Input(ctypes.c_ulong(1), ii_) | |
ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x)) | |
def release_key(key): | |
extra = ctypes.c_ulong(0) | |
ii_ = Input_I() | |
flags = 0x0008 | 0x0002 | |
ii_.ki = KeyBdInput(0, key, flags, 0, ctypes.pointer(extra)) | |
x = Input(ctypes.c_ulong(1), ii_) | |
ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x)) | |
def get_game_window(): | |
w.find_window_wildcard("Minecraft 1*") # Game window is named 'Minecraft 1.13.1' for example. | |
w.set_foreground() | |
# Character map | |
char_map = { | |
'q': 0x10, 'w': 0x11, 'e': 0x12, 'r': 0x13, 't': 0x14, 'z': 0x15, 'u': 0x16, 'i': 0x17, 'o': 0x18, 'p':0x19, | |
'a': 0x1E, 's': 0x1F, 'd': 0x20, 'f': 0x21, 'g': 0x22, 'h': 0x23, 'j': 0x24, 'k': 0x25, 'l': 0x26, | |
'y': 0x2C, 'x': 0x2D, 'c': 0x2E, 'v': 0x2F, 'b': 0x30, 'n': 0x31, 'm': 0x32 } | |
# Sending the message using the character map | |
get_game_window() | |
press_key(char_map['t']) # t - opens chat | |
release_key(char_map['t']) | |
press_key(char_map['h']);release_key(char_map['h']); # h | |
press_key(char_map['e']);release_key(char_map['e']); # e | |
press_key(char_map['l']);release_key(char_map['l']); # l | |
press_key(char_map['l']);release_key(char_map['l']); # l | |
press_key(char_map['o']);release_key(char_map['o']); # o | |
press_key(0x1C);release_key(0x1C); # Submit it (0x1C is ENTER key -> possible char_map extension? ;)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import win32gui | |
import re | |
class WindowMgr: | |
"""Encapsulates some calls to the winapi for window management""" | |
def __init__ (self): | |
"""Constructor""" | |
self._handle = None | |
def find_window(self, class_name, window_name=None): | |
"""find a window by its class_name""" | |
self._handle = win32gui.FindWindow(class_name, window_name) | |
def _window_enum_callback(self, hwnd, wildcard): | |
"""Pass to win32gui.EnumWindows() to check all the opened windows""" | |
if re.match(wildcard, str(win32gui.GetWindowText(hwnd))) is not None: | |
self._handle = hwnd | |
def find_window_wildcard(self, wildcard): | |
"""find a window whose title matches the wildcard regex""" | |
self._handle = None | |
win32gui.EnumWindows(self._window_enum_callback, wildcard) | |
def set_foreground(self): | |
"""put the window in the foreground""" | |
win32gui.SetForegroundWindow(self._handle) | |
def get_hwnd(self): | |
"""returns hwnd for further use""" | |
return self._handle |
Thank you for the quick response, i really appreciate it.
splitgate and 0.A.D, but that just for testing.
I am building something like a splitkeyboard / joystick-controller for pc. The brain of the project is an arduino pro micro. I am using it in combination with firmata and python.
I decided to use python because i wanna have more than on setup / layout, for each programm it's own layout.
For example I would like to have a layout for blender, inkscape or for gaming, but one layout for each game or program.
could you give me more advices how to get it to work?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi @ape-devil, unfortunatelly, this will not work on linux -
win32*
is a windows dependency and the whole mechanics of obtaining window handle etc. leverages windows API, which is not available on Linux.In your use case, try searching for something along the lines of "X11 get window handle" (X11 is a name for window system on Linux; in python this has many interfaces that can be used to interact with it, but I have personally never used it (yet)). Other possible terms that could help on your search: xlib, x11 interface python, x11 python get window handle.
What game are you trying to automate, by the way? :)
Also, definitely not stupid for trying! I am sure you will learn a lot of stuff while working on whatever project it is that you are working on. This is usually the best way to learn - to have a project or a problem at mind and do your best to work it out.