Skip to content

Instantly share code, notes, and snippets.

@Amgarak
Last active May 31, 2024 22:25
Show Gist options
  • Save Amgarak/5df8477bad67dabbc491322e74ce1c2c to your computer and use it in GitHub Desktop.
Save Amgarak/5df8477bad67dabbc491322e74ce1c2c to your computer and use it in GitHub Desktop.
Windows, Input Listener, Keyboard Events, Mouse Events, Hook, Python, Windows API, Low-Level Hook, low-level hooks, hooks, Input Monitoring, Code Snippet, Hook Python

Keyboard and Mouse Input Listener for Windows - Python

This Gist contains a Python class 'InputListener' that implements a simple input listener for keyboard and mouse events in Windows. The class provides methods to add event handlers for keyboard and mouse events, making it easy to capture and respond to user input.

Features:

Listens to keyboard events such as key down and key up. Monitors mouse events including mouse movement, clicks, wheel scrolling, and extra mouse buttons. Supports customizable event handling with callback functions. Usage:

Create an instance of 'InputListener'. Add keyboard and mouse event handlers using 'add_keyboard_handler' and 'add_mouse_handler' methods. Start listening to events using the 'listen' method. The class also includes default event types and mappings for both keyboard and mouse events, which can be modified to suit your specific needs.

Note: This code is designed for Windows and relies on the 'win32api', 'win32con', and 'win32gui' libraries.

Feel free to use and modify this code according to your requirements.

Author: Amgarak


Прослушиватель событий клавиатуры и мыши для Windows - Python

Этот код представляет собой класс 'InputListener' на языке программирования Python, который реализует простой прослушиватель событий клавиатуры и мыши в операционной системе Windows. Этот класс предоставляет методы для добавления обработчиков событий клавиатуры и мыши, что позволяет легко отлавливать и реагировать на действия пользователя.

Особенности:

Отслеживание событий клавиатуры, таких как нажатие и отпускание клавиш. Мониторинг событий мыши, включая движение мыши, клики, прокрутку колесика и дополнительные кнопки мыши. Поддержка настраиваемой обработки событий с помощью функций обратного вызова. Использование:

Создайте экземпляр 'InputListener'. Добавьте обработчики событий клавиатуры и мыши с помощью методов 'add_keyboard_handler' и 'add_mouse_handler'. Начните прослушивание событий с помощью метода 'listen'. Также, в классе предусмотрены типы событий и соответствующие им отображения для клавиатуры и мыши по умолчанию. Эти отображения могут быть изменены в соответствии с вашими потребностями.

Примечание: Этот код предназначен для работы в Windows и использует библиотеки 'win32api', 'win32con' и 'win32gui'.

Вы можете свободно использовать и изменять этот код в соответствии с вашими потребностями.

Автор: Amgarak


print('__file__={0:<35} | __name__={1:<25} | __package__={2:<25}'.format(__file__,__name__,str(__package__)))
import ctypes
import win32con
import win32api
import win32gui
import atexit
from collections import namedtuple
class InputListener:
"""
This class implements an input listener for keyboard and mouse events in Windows.
Usage:
1. Create an instance of InputListener.
2. Add keyboard and mouse event handlers using 'add_keyboard_handler' and 'add_mouse_handler' methods.
3. Start listening to events using the 'listen' method.
The class provides default event types and mappings for both keyboard and mouse events.
"""
def __init__(self):
self.keyboard_handlers = []
self.mouse_handlers = []
self.event_types = {
win32con.WM_KEYDOWN: 'key_down', # 0x100: 'key down', - WM_KeyDown for normal keys - 256
win32con.WM_KEYUP: 'key_up', # 0x101: 'key up', - WM_KeyUp for normal keys - 257
0x104: 'key_down', # WM_SYSKEYDOWN, used for Alt key - 260
0x105: 'key_up'} # WM_SYSKEYUP, used for Alt key - 261
self.mouse_types={
0x200: 'move', # WM_MOUSEMOVE - 512
0x20A: 'wheel', # WM_MOUSEWHEEL - 522 - scan_code_top: 7864320; scan_code_bot: 4287102976
0x20E: 'H_wheel', # WM_MOUSEHWHEEL - 526
0x204: 'right_down', # WM_RBUTTONDOWN - 516
0x205: 'right_up', # WM_RBUTTONUP - 517
0x201: 'left_down', # WM_LBUTTONDOWN - 513
0x202: 'left_up', # WM_LBUTTONUP - 514
0x207: 'middle_down', # WM_MBUTTONDOWN - 519
0x208: 'middle_up', # WM_MBUTTONUP - 520
0x20B: 'X_Button_down', # WM_XBUTTONDOWN - 523 - scan_code X_1: 131072; scan_code X_2: 65536
0x20C: 'X_Button_up'} # WM_XBUTTONUP - 524 - scan_code X_1: 131072; scan_code X_2: 65536
self.mouse_key={
0x200: 'move',
0x20A: 'wheel',
7864320: 'wheel_top',
4287102976: 'wheel_bot',
0x20E: 'H_wheel',
0x204: 'right',
0x205: 'right',
0x201: 'left',
0x202: 'left',
0x207: 'middle',
0x208: 'middle',
131072: 'X_Button_1',
65536: 'X_Button_2'}
self.KeyboardEvent = namedtuple('KeyboardEvent', ['event_type', 'key_code'])
self.MouseEvent = namedtuple('MouseEvent', ['mouse_type', 'key_code', 'x', 'y', 'scan_code'])
def keyboard_low_level_handler(self, nCode, wParam, lParam):
lParam = ctypes.cast(lParam, ctypes.POINTER(ctypes.c_ulong)).contents.value
event = self.KeyboardEvent(self.event_types[wParam], hex(lParam))
for handler in self.keyboard_handlers:
handler(event)
return ctypes.windll.user32.CallNextHookEx(self.keyboard_hook_id, nCode, wParam, lParam)
def mouse_low_level_handler(self, nCode, wParam, lParam):
point = ctypes.cast(lParam, ctypes.POINTER(ctypes.c_long * 2)).contents
x = point[0]
y = point[1]
default_key = "Value not defined"
event = self.MouseEvent(self.mouse_types[wParam], self.mouse_key.get(lParam[1], default_key) if lParam[1] is not None else self.mouse_key[wParam], x, y, lParam[1])
for handler in self.mouse_handlers:
handler(event)
return ctypes.windll.user32.CallNextHookEx(self.mouse_hook_id, nCode, wParam, lParam)
def add_keyboard_handler(self, handler):
self.keyboard_handlers.append(handler)
def add_mouse_handler(self, handler):
self.mouse_handlers.append(handler)
def listen(self):
CMPFUNC = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_void_p))
ctypes.windll.user32.SetWindowsHookExW.argtypes = (
ctypes.c_int,
CMPFUNC,
ctypes.c_void_p,
ctypes.c_uint
)
keyboard_pointer = CMPFUNC(self.keyboard_low_level_handler)
self.keyboard_hook_id = ctypes.windll.user32.SetWindowsHookExW(win32con.WH_KEYBOARD_LL, keyboard_pointer,
win32api.GetModuleHandle(None), 0)
mouse_pointer = CMPFUNC(self.mouse_low_level_handler)
self.mouse_hook_id = ctypes.windll.user32.SetWindowsHookExW(win32con.WH_MOUSE_LL, mouse_pointer,
win32api.GetModuleHandle(None), 0)
atexit.register(ctypes.windll.user32.UnhookWindowsHookEx, self.keyboard_hook_id)
atexit.register(ctypes.windll.user32.UnhookWindowsHookEx, self.mouse_hook_id)
while True:
msg = win32gui.GetMessage(None, 0, 0)
win32gui.TranslateMessage(ctypes.byref(msg))
win32gui.DispatchMessage(ctypes.byref(msg))
if __name__ == '__main__':
def print_keyboard_event(event):
print("K: ", event)
def print_mouse_event(event):
print("M: ", event)
input_listener = InputListener()
input_listener.add_keyboard_handler(print_keyboard_event)
input_listener.add_mouse_handler(print_mouse_event)
input_listener.listen()
print('__file__={0:<35} | __name__={1:<25} | __package__={2:<25}'.format(__file__,__name__,str(__package__)))
import ctypes
import win32con
import win32api
import win32gui
import atexit
from collections import namedtuple
import threading
import time
class InputListener:
"""
https://gist.github.com/Amgarak/5df8477bad67dabbc491322e74ce1c2c
Input Event Listener for Windows
Description:
This Python script provides functionality for listening to and handling input events (keyboard and mouse) in a Windows environment.
It uses low-level hooks to capture events like key presses, mouse movements, and button clicks.
The script utilizes the win32api, win32con, and win32gui libraries for Windows API interactions, as well as ctypes for handling pointers and data types.
Key Features:
Supports capturing keyboard events (key down, key up) and mouse events (movement, button clicks, wheel scrolling).
Allows the registration of event handlers for custom processing of captured events.
Utilizes low-level hooks for efficient event capturing.
"""
def __init__(self):
self._key_down = {}
self.handler = []
self.event_types = {
win32con.WM_KEYDOWN: 'key_down', # 0x100: 'key down', - WM_KeyDown for normal keys - 256
win32con.WM_KEYUP: 'key_up', # 0x101: 'key up', - WM_KeyUp for normal keys - 257
0x104: 'key_down', # WM_SYSKEYDOWN, used for Alt key - 260
0x105: 'key_up'} # WM_SYSKEYUP, used for Alt key - 261
self.mouse_types={
0x200: 'move', # WM_MOUSEMOVE - 512
0x20A: 'wheel', # WM_MOUSEWHEEL - 522 - scan_code_top: 7864320 and scan_code_bot: 4287102976
0x20E: 'H_wheel', # WM_MOUSEHWHEEL - 526
0x204: 'key_down', # WM_RBUTTONDOWN - 516
0x205: 'key_up', # WM_RBUTTONUP - 517
0x201: 'key_down', # WM_LBUTTONDOWN - 513
0x202: 'key_up', # WM_LBUTTONUP - 514
0x207: 'key_down', # WM_MBUTTONDOWN - 519
0x208: 'key_up', # WM_MBUTTONUP - 520
0x20B: 'key_down', # WM_XBUTTONDOWN - 523 - scan_code: 131072 and scan_code: 65536
0x20C: 'key_up'} # WM_XBUTTONUP - 524 - scan_code: 131072 and scan_code: 65536
self.mouse_key={
0x200: 'move',
0x20A: 'wheel',
7864320: 'wheel_top',
4287102976: 'wheel_bot',
0x20E: 'H_wheel',
0x204: 'right',
0x205: 'right',
0x201: 'left',
0x202: 'left',
0x207: 'middle',
0x208: 'middle',
131072: 'X_Button_1',
65536: 'X_Button_2'}
self.KeyboardEvent = namedtuple('KeyboardEvent', ['event_type', 'key_code'])
self.MouseEvent = namedtuple('MouseEvent', ['event_type', 'key_code', 'x', 'y', 'scan_code'])
self.default_key = "Value not defined"
def loopHandler(self, event):
for the_handler in self.handler:
the_handler(event)
def keyboard_low_level_handler(self, nCode, wParam, lParam):
lParam = ctypes.cast(lParam, ctypes.POINTER(ctypes.c_ulong)).contents.value
event = self.KeyboardEvent(self.event_types[wParam], lParam)
key = str(event.key_code)
# !!! - Отсекаем повторяющийся ивент key_down
if key not in self._key_down and event.event_type == 'key_down':
self._key_down[key] = event.event_type
self.handler(event)
# self.loopHandler(event) # Если для обработки событий мыши\клавиаутуры планируется больше одной функции
elif key in self._key_down and event.event_type == 'key_up':
self._key_down.pop(key)
self.handler(event)
# self.loopHandler(event) # Если для обработки событий мыши\клавиаутуры планируется больше одной функции
return ctypes.windll.user32.CallNextHookEx(self.keyboard_hook_id, nCode, wParam, lParam)
def mouse_low_level_handler(self, nCode, wParam, lParam):
point = ctypes.cast(lParam, ctypes.POINTER(ctypes.c_long * 2)).contents
x = point[0]
y = point[1]
event = self.MouseEvent(self.mouse_types[wParam], self.mouse_key.get(lParam[1], self.default_key) if lParam[1] is not None else self.mouse_key[wParam], x, y, lParam[1])
self.handler(event)
# self.loopHandler(event) # Если для обработки событий мыши\клавиаутуры планируется больше одной функции
return ctypes.windll.user32.CallNextHookEx(self.mouse_hook_id, nCode, wParam, lParam)
def listen(self):
CMPFUNC = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_void_p))
ctypes.windll.user32.SetWindowsHookExW.argtypes = (
ctypes.c_int,
CMPFUNC,
ctypes.c_void_p,
ctypes.c_uint
)
keyboard_pointer = CMPFUNC(self.keyboard_low_level_handler)
self.keyboard_hook_id = ctypes.windll.user32.SetWindowsHookExW(win32con.WH_KEYBOARD_LL, keyboard_pointer,
win32api.GetModuleHandle(None), 0)
mouse_pointer = CMPFUNC(self.mouse_low_level_handler)
self.mouse_hook_id = ctypes.windll.user32.SetWindowsHookExW(win32con.WH_MOUSE_LL, mouse_pointer,
win32api.GetModuleHandle(None), 0)
atexit.register(ctypes.windll.user32.UnhookWindowsHookEx, self.keyboard_hook_id)
atexit.register(ctypes.windll.user32.UnhookWindowsHookEx, self.mouse_hook_id)
while True:
msg = win32gui.GetMessage(None, 0, 0)
win32gui.TranslateMessage(ctypes.byref(msg))
win32gui.DispatchMessage(ctypes.byref(msg))
def add_handler(self, handler): # Регестрируем колбэк-функцию
self.handler = handler
# self.handler.append(handler) # Если для обработки событий мыши\клавиаутуры планируется больше одной функции
def start(self):
# Создаем новый поток и передаем в него функцию self.listen() - без скобок!
# Мы передаем саму функцию в качестве объекта. В этом случае, функция не вызывается немедленно.
thread = threading.Thread(target=self.listen, name="Hooks")
thread.daemon = False # Включить при использовании GUI
thread.start() # Запускаем Hooks в отдельном потоке
if __name__ == '__main__':
_key_down_Log = {}
log = True
def print_event(event):
print("->: ")
key = str(event.key_code)
if key not in _key_down_Log and event.event_type == 'key_down':
_key_down_Log[key] = event.event_type
if key in _key_down_Log and event.event_type == 'key_up':
_key_down_Log.pop(key)
if log:
print(time.strftime("%H:%M:%S"), "-", event)
print(f"Нажатые кнопки: {_key_down_Log}")
threads = threading.enumerate()
thread_names = [thread.name for thread in threads]
threads_str = ', '.join(thread_names)
print(f"Активные потоки: {threads_str}\n")
input_listener = InputListener()
input_listener.add_handler(print_event)
input_listener.listen()
#input_listener.start()
print('__file__={0:<35} | __name__={1:<25} | __package__={2:<25}'.format(__file__,__name__,str(__package__)))
import ctypes
import win32con
import win32api
import win32gui
import atexit
from collections import namedtuple
import threading
import time
import queue
class InputListener:
"""
https://gist.github.com/Amgarak/5df8477bad67dabbc491322e74ce1c2c
The provided code is a Python script that sets up a low-level input event listener for both keyboard and mouse events on a Windows system. It defines a class InputListener with various methods to handle key and mouse events.
The InputListener class has the following key components:
It initializes with dictionaries and lists to store event information and handlers.
It defines constants for various event types and their corresponding codes.
It uses the namedtuple class to create a structure for storing device events.
It provides methods for registering event handlers and conditional handlers.
It contains low-level event handler methods for keyboard and mouse events.
It creates threads for listening to events and executing handlers.
It has methods for starting and stopping the event listeners.
In the __main__ section of the code, several event handlers (print_event, print_event2, print_event3) are defined to process events, and conditional handlers (_if_block, _if2_block, _if3_block) are used to filter events based on specific conditions.
The script then creates an instance of InputListener, registers event handlers and conditional handlers, creates listener queues, sets up listener threads, and starts the event listeners.
Finally, the script runs for a duration of 10 seconds before stopping the event listeners.
If you have any specific questions or need further assistance with this code, feel free to ask!
"""
def __init__(self):
self._key_down = {}
self.if_handlers = []
self.handlers = []
self.listener_queues = {}
self.listeners =[]
self.event_types = {
win32con.WM_KEYDOWN: 'key_down', # 0x100: 'key down', - WM_KeyDown for normal keys - 256
win32con.WM_KEYUP: 'key_up', # 0x101: 'key up', - WM_KeyUp for normal keys - 257
0x104: 'key_down', # WM_SYSKEYDOWN, used for Alt key - 260
0x105: 'key_up'} # WM_SYSKEYUP, used for Alt key - 261
self.mouse_types={
0x200: 'move', # WM_MOUSEMOVE - 512
0x20A: 'wheel', # WM_MOUSEWHEEL - 522 - scan_code_top: 7864320; scan_code_bot: 4287102976
0x20E: 'H_wheel', # WM_MOUSEHWHEEL - 526
0x204: 'key_down', # WM_RBUTTONDOWN - 516
0x205: 'key_up', # WM_RBUTTONUP - 517
0x201: 'key_down', # WM_LBUTTONDOWN - 513
0x202: 'key_up', # WM_LBUTTONUP - 514
0x207: 'key_down', # WM_MBUTTONDOWN - 519
0x208: 'key_up', # WM_MBUTTONUP - 520
0x20B: 'key_down', # WM_XBUTTONDOWN - 523 - scan_code: 131072; scan_code: 65536
0x20C: 'key_up'} # WM_XBUTTONUP - 524 - scan_code: 131072; scan_code: 65536
self.mouse_key={
0x200: 'move',
0x20A: 'wheel',
7864320: 'wheel_top',
4287102976: 'wheel_bot',
0x20E: 'H_wheel',
0x204: 'right',
0x205: 'right',
0x201: 'left',
0x202: 'left',
0x207: 'middle',
0x208: 'middle',
131072: 'X_Button_1',
65536: 'X_Button_2'}
self.DeviceEvent = namedtuple('DeviceEvent', ['inputDevice', 'event_type', 'key_code', 'x', 'y', 'scan_code', 'key_down'])
self.DeviceEvent.__new__.__defaults__ = (None, None, None, None, None, None, None)
self.default_key = "Value not defined"
def key_event(self, event):
for listener_name, listener_queue in self.listener_queues.items():
listener_queue.put(event)
def key_down(self, event_type, key_code):
key = str(key_code)
if key not in self._key_down and event_type == 'key_down':
self._key_down[key] = event_type
return True
elif key in self._key_down and event_type == 'key_up':
self._key_down.pop(key)
return True
def if_block(self, event):
for if_handler in self.if_handlers:
result = if_handler(event)
if result is not None and not result:
return False
return True
def keyboard_low_level_handler(self, nCode, wParam, lParam):
lParam = ctypes.cast(lParam, ctypes.POINTER(ctypes.c_ulong)).contents.value
rezult = self.key_down(self.event_types[wParam], lParam)
event = self.DeviceEvent('Keyboard', self.event_types[wParam], lParam, key_down=self._key_down)
if rezult: # !!! - Отсекаем повторяющийся ивент key_down
self.key_event(event)
if self.if_block(event):
return ctypes.windll.user32.CallNextHookEx(self.keyboard_hook_id, nCode, wParam, lParam)
else:
return -1
def mouse_low_level_handler(self, nCode, wParam, lParam):
point = ctypes.cast(lParam, ctypes.POINTER(ctypes.c_long * 2)).contents
x = point[0]
y = point[1]
castomParam = self.mouse_key.get(lParam[1], self.default_key) if lParam[1] is not None else self.mouse_key[wParam]
self.key_down(self.mouse_types[wParam], castomParam)
event = self.DeviceEvent('Mouse', self.mouse_types[wParam], castomParam, x, y, lParam[1], self._key_down)
self.key_event(event)
if self.if_block(event):
return ctypes.windll.user32.CallNextHookEx(self.keyboard_hook_id, nCode, wParam, lParam)
else:
return -1
def listen(self):
CMPFUNC = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_void_p))
ctypes.windll.user32.SetWindowsHookExW.argtypes = (
ctypes.c_int,
CMPFUNC,
ctypes.c_void_p,
ctypes.c_uint
)
keyboard_pointer = CMPFUNC(self.keyboard_low_level_handler)
self.keyboard_hook_id = ctypes.windll.user32.SetWindowsHookExW(win32con.WH_KEYBOARD_LL, keyboard_pointer,
win32api.GetModuleHandle(None), 0)
mouse_pointer = CMPFUNC(self.mouse_low_level_handler)
self.mouse_hook_id = ctypes.windll.user32.SetWindowsHookExW(win32con.WH_MOUSE_LL, mouse_pointer,
win32api.GetModuleHandle(None), 0)
atexit.register(ctypes.windll.user32.UnhookWindowsHookEx, self.keyboard_hook_id)
atexit.register(ctypes.windll.user32.UnhookWindowsHookEx, self.mouse_hook_id)
while True:
msg = win32gui.GetMessage(None, 0, 0)
win32gui.TranslateMessage(ctypes.byref(msg))
win32gui.DispatchMessage(ctypes.byref(msg))
def add_if_handler(self, handlers): # Регестрируем колбэк-функции
self.if_handlers.append(handlers)
def add_handler(self, handlers): # Регестрируем колбэк-функции
self.handlers.append(handlers)
def create_listener_queues(self):
self.listener_queues = {func.__name__: queue.Queue() for func in self.handlers}
def create_listener(self):
self.listeners = [
threading.Thread(target=self.wrapper, args=(self.listener_queues[func.__name__], func), name=func.__name__)
for func in self.handlers
]
def start_listener(self):
for listener_thread in self.listeners:
listener_thread.daemon = True
listener_thread.start()
def wrapper(self, listener_queue, func):
while True:
event = listener_queue.get()
if event is None:
break
func(event)
print(f"Демон {func.__name__} завершил свою работу..")
def start(self):
# Создаем новый поток и передаем в него функцию self.listen() - без скобок!
# Мы передаем саму функцию в качестве объекта. В этом случае, функция не вызывается немедленно.
thread = threading.Thread(target=self.listen, name="Hooks")
thread.daemon = False # Включить при использовании GUI
thread.start() # Запускаем Hooks в отдельном потоке
def stop_listener(self): # Останавливаем слушатели
for listener_name, listener_queue in self.listener_queues.items():
listener_queue.put(None)
if __name__ == '__main__':
log = True
def print_event(event):
print("->: ")
if log:
print(time.strftime("%H:%M:%S"), "-", event.inputDevice, "->", event.event_type, "->", event.key_code)
def print_event2(event):
if log:
print( "X:", event.x, "Y:", event.y, "Scan_code:", event.scan_code)
print(f"Нажатые кнопки: {event.key_down}")
def print_event3(event):
if log:
threads = threading.enumerate()
thread_names = [thread.name for thread in threads]
threads_str = ', '.join(thread_names)
print(f"Активные потоки: {threads_str}\n")
def _if_block(event):
if event.key_code == 49:
print(f"Блокируем кнопку клавиатуры [1]")
return False
def _if2_block(event):
if event.key_code == "left":
print(f"Блокируем кнопку мыши [left]")
return False
def _if3_block(event):
# Проверка условия ограничения координат мыши
if event.inputDevice == "Mouse" and 0 <= event.y <= 250:
# Блокировка движения мыши
win32api.SetCursorPos((event.x, 250))
return False # Блокировать обработку события мыши
if event.inputDevice == "Mouse" and 1000 <= event.y <= 1080:
win32api.SetCursorPos((event.x, 1000))
return False
input_listener = InputListener()
input_listener.add_handler(print_event)
input_listener.add_handler(print_event2)
#input_listener.add_handler(print_event3)
input_listener.add_if_handler(_if_block)
#input_listener.add_if_handler(_if2_block)
#input_listener.add_if_handler(_if3_block)
input_listener.create_listener_queues()
input_listener.create_listener()
input_listener.start_listener()
#input_listener.listen()
input_listener.start()
time.sleep(10)
input_listener.stop_listener()
print('__file__={0:<35} | __name__={1:<25} | __package__={2:<25}'.format(__file__,__name__,str(__package__)))
import ctypes
import win32con
import win32api
import win32gui
import atexit
from collections import namedtuple
import threading
import time
import queue
class InputListener:
"""
https://gist.github.com/Amgarak/5df8477bad67dabbc491322e74ce1c2c
This class is designed for listening to keyboard and mouse events in a Windows environment.
It utilizes low-level hooks to capture events such as key presses,
key releases, mouse movements, and mouse button clicks.
The class provides functionalities to register event handlers and filters,
allowing custom processing of events.
It also supports the creation of threads for concurrent event handling.
"""
def __init__(self):
self.flagRun=False
self._key_down = set()
self.if_handlers = set()
self.handlers = set()
self.listener_queues = {}
self.listeners =[]
self.event_types = {
win32con.WM_KEYDOWN: 'key_down', # 0x100: 'key down', - WM_KeyDown for normal keys - 256
win32con.WM_KEYUP: 'key_up', # 0x101: 'key up', - WM_KeyUp for normal keys - 257
0x104: 'key_down', # WM_SYSKEYDOWN, used for Alt key - 260
0x105: 'key_up'} # WM_SYSKEYUP, used for Alt key - 261
self.mouse_types={
0x200: 'move', # WM_MOUSEMOVE - 512
0x20A: 'wheel', # WM_MOUSEWHEEL - 522 - scan_code_top: 7864320; scan_code_bot: 4287102976
0x20E: 'H_wheel', # WM_MOUSEHWHEEL - 526
0x204: 'key_down', # WM_RBUTTONDOWN - 516
0x205: 'key_up', # WM_RBUTTONUP - 517
0x201: 'key_down', # WM_LBUTTONDOWN - 513
0x202: 'key_up', # WM_LBUTTONUP - 514
0x207: 'key_down', # WM_MBUTTONDOWN - 519
0x208: 'key_up', # WM_MBUTTONUP - 520
0x20B: 'key_down', # WM_XBUTTONDOWN - 523 - scan_code: 131072; scan_code: 65536
0x20C: 'key_up'} # WM_XBUTTONUP - 524 - scan_code: 131072; scan_code: 65536
self.mouse_key={
0x200: 'move',
0x20A: 'wheel',
7864320: 'wheel_top',
4287102976: 'wheel_bot',
0x20E: 'H_wheel',
0x204: 'right',
0x205: 'right',
0x201: 'left',
0x202: 'left',
0x207: 'middle',
0x208: 'middle',
131072: 'X_Button_1',
65536: 'X_Button_2'}
self.DeviceEvent = namedtuple('DeviceEvent', ['inputDevice', 'event_type', 'key_code', 'x', 'y', 'scan_code', 'key_down'])
self.DeviceEvent.__new__.__defaults__ = (None, None, None, None, None, None, None)
self.default_key = "Value not defined"
def key_event(self, event):
for listener_name, listener_queue in self.listener_queues.items():
listener_queue.put(event)
def key_down(self, event_type, key_code):
key = str(key_code)
if key not in self._key_down and event_type == 'key_down':
self._key_down.add(key)
return True
elif key in self._key_down and event_type == 'key_up':
self._key_down.remove(key)
return True
def if_block(self, event):
for if_handler in self.if_handlers:
result = if_handler(event)
if result is not None and not result:
return False
return True
def keyboard_low_level_handler(self, nCode, wParam, lParam):
lParam = ctypes.cast(lParam, ctypes.POINTER(ctypes.c_ulong)).contents.value
rezult = self.key_down(self.event_types[wParam], lParam)
event = self.DeviceEvent('Keyboard', self.event_types[wParam], lParam, key_down=self._key_down)
if rezult: # !!! - Отсекаем повторяющийся ивент key_down
self.key_event(event)
if self.if_block(event):
return ctypes.windll.user32.CallNextHookEx(self.keyboard_hook_id, nCode, wParam, lParam)
else:
return -1
def mouse_low_level_handler(self, nCode, wParam, lParam):
point = ctypes.cast(lParam, ctypes.POINTER(ctypes.c_long * 2)).contents
x = point[0]
y = point[1]
castomParam = self.mouse_key.get(lParam[1], self.default_key) if lParam[1] is not None else self.mouse_key[wParam]
self.key_down(self.mouse_types[wParam], castomParam)
event = self.DeviceEvent('Mouse', self.mouse_types[wParam], castomParam, x, y, lParam[1], self._key_down)
self.key_event(event)
if self.if_block(event):
return ctypes.windll.user32.CallNextHookEx(self.mouse_hook_id, nCode, wParam, lParam)
else:
return -1
def listen(self):
CMPFUNC = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_void_p))
ctypes.windll.user32.SetWindowsHookExW.argtypes = (
ctypes.c_int,
CMPFUNC,
ctypes.c_void_p,
ctypes.c_uint
)
keyboard_pointer = CMPFUNC(self.keyboard_low_level_handler)
self.keyboard_hook_id = ctypes.windll.user32.SetWindowsHookExW(win32con.WH_KEYBOARD_LL, keyboard_pointer,
win32api.GetModuleHandle(None), 0)
mouse_pointer = CMPFUNC(self.mouse_low_level_handler)
self.mouse_hook_id = ctypes.windll.user32.SetWindowsHookExW(win32con.WH_MOUSE_LL, mouse_pointer,
win32api.GetModuleHandle(None), 0)
atexit.register(ctypes.windll.user32.UnhookWindowsHookEx, self.keyboard_hook_id)
atexit.register(ctypes.windll.user32.UnhookWindowsHookEx, self.mouse_hook_id)
while self.flagRun:
found, msg = win32gui.PeekMessage(0, 0, 0, win32con.PM_REMOVE)
if found:
win32gui.TranslateMessage(msg);
win32gui.DispatchMessage(msg);
else:
time.sleep(0.01)
def add_if_handler(self, handler): # Регестрируем колбэк-фильтров
self.if_handlers.add(handler)
def add_handler(self, handler): # Регестрируем колбэк-обработчиков
self.handlers.add(handler)
def remove_if_handler(self, handler): # Удаление колбэк-фильтров
self.if_handlers.discard(handler)
def remove_handler(self, handler): # Удаление колбэк-обработчиков
self.handlers.discard(handler)
def create_listener_queues(self):
self.listener_queues = {func.__name__: queue.Queue() for func in self.handlers}
def create_listener(self):
self.listeners = [
threading.Thread(target=self.wrapper, args=(self.listener_queues[func.__name__], func), name=func.__name__)
for func in self.handlers
]
def start_listener(self):
for listener_thread in self.listeners:
listener_thread.daemon = True
listener_thread.start()
def wrapper(self, listener_queue, func):
while True:
event = listener_queue.get()
if event is None:
break
func(event)
print(f"Демон {func.__name__} завершил свою работу..")
def start(self):
self.create_listener_queues()
self.create_listener()
self.start_listener()
self.flagRun=True
# Создаем новый поток и передаем в него функцию self.listen() - без скобок!
# Мы передаем саму функцию в качестве объекта. В этом случае, функция не вызывается немедленно.
thread = threading.Thread(target=self.listen, name="Hooks")
thread.daemon = False # Включить при использовании GUI
thread.start() # Запускаем Hooks в отдельном потоке
def stop(self): # Останавливаем слушатели\хук
for listener_name, listener_queue in self.listener_queues.items():
listener_queue.put(None)
self.flagRun=False
ctypes.windll.user32.UnhookWindowsHookEx(self.keyboard_hook_id)
ctypes.windll.user32.UnhookWindowsHookEx(self.mouse_hook_id)
self.listener_queues.clear()
self.listeners.clear()
self._key_down.clear()
if __name__ == '__main__':
log = True
def print_event(event):
print("->: ")
if log:
print(time.strftime("%H:%M:%S"), "-", event.inputDevice, "->", event.event_type, "->", event.key_code)
def print_event2(event):
if log:
print( "X:", event.x, "Y:", event.y, "Scan_code:", event.scan_code)
print(f"Нажатые кнопки: {event.key_down}")
def print_event3(event):
if log:
threads = threading.enumerate()
thread_names = [thread.name for thread in threads]
threads_str = ', '.join(thread_names)
print(f"Активные потоки: {threads_str}\n")
def _if_block(event):
if event.key_code == 49:
print(f"Блокируем кнопку клавиатуры [1]")
return False
def _if2_block(event):
if event.key_code == "left":
print(f"Блокируем кнопку мыши [left]")
return False
def _if3_block(event):
# Проверка условия ограничения координат мыши
if event.inputDevice == "Mouse" and 0 <= event.y <= 250:
# Блокировка движения мыши
win32api.SetCursorPos((event.x, 250))
return False # Блокировать обработку события мыши
if event.inputDevice == "Mouse" and 1000 <= event.y <= 1080:
win32api.SetCursorPos((event.x, 1000))
return False
input_listener = InputListener()
input_listener.add_handler(print_event)
input_listener.add_handler(print_event2)
input_listener.add_handler(print_event3)
input_listener.add_if_handler(_if_block)
input_listener.add_if_handler(_if2_block)
input_listener.add_if_handler(_if3_block)
input_listener.start()
time.sleep(5)
input_listener.stop()
time.sleep(5)
input_listener.remove_handler(print_event2)
input_listener.remove_if_handler(_if3_block)
input_listener.start()
print('__file__={0:<35} | __name__={1:<25} | __package__={2:<25}'.format(__file__,__name__,str(__package__)))
import ctypes
import win32con
import win32api
import win32gui
import atexit
from collections import namedtuple
import threading
import time
import queue
class InputListener:
"""
https://gist.github.com/Amgarak/5df8477bad67dabbc491322e74ce1c2c
This class is designed for listening to keyboard and mouse events in a Windows environment.
It utilizes low-level hooks to capture events such as key presses,
key releases, mouse movements, and mouse button clicks.
The class provides functionalities to register event handlers and filters,
allowing custom processing of events.
It also supports the control of threads for concurrent event handling.
"""
def __init__(self):
self.flagRun=False
self.signal = queue.Queue()
self._key_down = set()
self.if_handlers = set()
self.handlers = set()
self.listener_queues = {}
self.listeners =[]
self.event_types = {
win32con.WM_KEYDOWN: 'key_down', # 0x100: 'key down', - WM_KeyDown for normal keys - 256
win32con.WM_KEYUP: 'key_up', # 0x101: 'key up', - WM_KeyUp for normal keys - 257
0x104: 'key_down', # WM_SYSKEYDOWN, used for Alt key - 260
0x105: 'key_up'} # WM_SYSKEYUP, used for Alt key - 261
self.mouse_types={
0x200: 'move', # WM_MOUSEMOVE - 512
0x20A: 'wheel', # WM_MOUSEWHEEL - 522 - scan_code_top: 7864320; scan_code_bot: 4287102976
0x20E: 'H_wheel', # WM_MOUSEHWHEEL - 526
0x204: 'key_down', # WM_RBUTTONDOWN - 516
0x205: 'key_up', # WM_RBUTTONUP - 517
0x201: 'key_down', # WM_LBUTTONDOWN - 513
0x202: 'key_up', # WM_LBUTTONUP - 514
0x207: 'key_down', # WM_MBUTTONDOWN - 519
0x208: 'key_up', # WM_MBUTTONUP - 520
0x20B: 'key_down', # WM_XBUTTONDOWN - 523 - scan_code: 131072; scan_code: 65536
0x20C: 'key_up'} # WM_XBUTTONUP - 524 - scan_code: 131072; scan_code: 65536
self.mouse_key={
0x200: 'move',
0x20A: 'wheel',
7864320: 'wheel_top',
4287102976: 'wheel_bot',
0x20E: 'H_wheel',
0x204: 'right',
0x205: 'right',
0x201: 'left',
0x202: 'left',
0x207: 'middle',
0x208: 'middle',
131072: 'X_Button_1',
65536: 'X_Button_2'}
self.DeviceEvent = namedtuple('DeviceEvent', ['inputDevice', 'event_type', 'key_code', 'x', 'y', 'scan_code', 'key_down'])
self.DeviceEvent.__new__.__defaults__ = (None, None, None, None, None, None, None)
self.default_key = "Value not defined"
def key_event(self, event):
while not self.signal.empty():
self.signal_handler(self.signal.get())
for listener_name, listener_queue in self.listener_queues.items():
listener_queue.put(event)
def key_down(self, event_type, key_code):
key = str(key_code)
if key not in self._key_down and event_type == 'key_down':
self._key_down.add(key)
return True
elif key in self._key_down and event_type == 'key_up':
self._key_down.remove(key)
return True
def if_block(self, event):
for if_handler in self.if_handlers:
result = if_handler(event)
if result is not None and not result:
return False
return True
def keyboard_low_level_handler(self, nCode, wParam, lParam):
lParam = ctypes.cast(lParam, ctypes.POINTER(ctypes.c_ulong)).contents.value
rezult = self.key_down(self.event_types[wParam], lParam)
event = self.DeviceEvent('Keyboard', self.event_types[wParam], lParam, key_down=self._key_down)
if rezult: # !!! - Отсекаем повторяющийся ивент key_down
self.key_event(event)
if self.if_block(event):
return ctypes.windll.user32.CallNextHookEx(self.keyboard_hook_id, nCode, wParam, lParam)
else:
return -1
def mouse_low_level_handler(self, nCode, wParam, lParam):
point = ctypes.cast(lParam, ctypes.POINTER(ctypes.c_long * 2)).contents
x = point[0]
y = point[1]
castomParam = self.mouse_key.get(lParam[1], self.default_key) if lParam[1] is not None else self.mouse_key[wParam]
self.key_down(self.mouse_types[wParam], castomParam)
event = self.DeviceEvent('Mouse', self.mouse_types[wParam], castomParam, x, y, lParam[1], self._key_down)
self.key_event(event)
if self.if_block(event):
return ctypes.windll.user32.CallNextHookEx(self.mouse_hook_id, nCode, wParam, lParam)
else:
return -1
def listen(self):
CMPFUNC = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_void_p))
ctypes.windll.user32.SetWindowsHookExW.argtypes = (
ctypes.c_int,
CMPFUNC,
ctypes.c_void_p,
ctypes.c_uint
)
keyboard_pointer = CMPFUNC(self.keyboard_low_level_handler)
self.keyboard_hook_id = ctypes.windll.user32.SetWindowsHookExW(win32con.WH_KEYBOARD_LL, keyboard_pointer,
win32api.GetModuleHandle(None), 0)
mouse_pointer = CMPFUNC(self.mouse_low_level_handler)
self.mouse_hook_id = ctypes.windll.user32.SetWindowsHookExW(win32con.WH_MOUSE_LL, mouse_pointer,
win32api.GetModuleHandle(None), 0)
atexit.register(ctypes.windll.user32.UnhookWindowsHookEx, self.keyboard_hook_id)
atexit.register(ctypes.windll.user32.UnhookWindowsHookEx, self.mouse_hook_id)
while self.flagRun:
found, msg = win32gui.PeekMessage(0, 0, 0, win32con.PM_REMOVE)
if found:
win32gui.TranslateMessage(msg);
win32gui.DispatchMessage(msg);
else:
time.sleep(0.001)
def add_if_handler(self, func): # Регестрируем колбэк-фильтров
self.if_handlers.add(func)
def add_handler(self, func): # Регестрируем колбэк-обработчиков
self.handlers.add(func)
def remove_if_handler(self, func): # Удаление колбэк-фильтров
self.if_handlers.discard(func)
def remove_handler(self, func): # Удаление колбэк-обработчиков
self.handlers.discard(func)
def create_listener_queues(self):
self.listener_queues = {func.__name__: queue.Queue() for func in self.handlers}
def create_listener(self):
self.listeners = [
threading.Thread(target=self.wrapper, args=(self.listener_queues[func.__name__], func), name=func.__name__)
for func in self.handlers
]
def start_listener(self):
for listener_thread in self.listeners:
listener_thread.daemon = True
listener_thread.start()
def wrapper(self, listener_queue, func):
while True:
event = listener_queue.get()
if event is None:
break
func(event)
print(f"Демон {func.__name__} завершил свою работу..")
def start(self, use_daemon = False):
self.create_listener_queues()
self.create_listener()
self.start_listener()
self.flagRun=True
# Создаем новый поток и передаем в него функцию self.listen() - без скобок!
# Мы передаем саму функцию в качестве объекта. В этом случае, функция не вызывается немедленно.
thread = threading.Thread(target=self.listen, name="Hooks")
thread.daemon = use_daemon # Включить при использовании GUI
thread.start() # Запускаем Hooks в отдельном потоке
def stop(self): # Останавливаем слушатели\хуки
for listener_name, listener_queue in self.listener_queues.items():
listener_queue.put(None)
self.flagRun=False
ctypes.windll.user32.UnhookWindowsHookEx(self.keyboard_hook_id)
ctypes.windll.user32.UnhookWindowsHookEx(self.mouse_hook_id)
self.listener_queues.clear()
self.listeners.clear()
self._key_down.clear()
def hot_removal_handler(self, func): # Горячее удаление колбэк-обработчиков
signal = {"hot_removal_handler": func}
self.signal.put(signal)
def hot_plugging_handler(self, func):# Горячая регестрирация колбэк-обработчиков
signal = {"hot_plugging_handler": func}
self.signal.put(signal)
def hot_removal_if_handler(self, func): # Горячее удаление колбэк-фильтров
signal = {"hot_removal_if_handler": func}
self.signal.put(signal)
def hot_plugging_if_handler(self, func): # Горячая регестрирация колбэк-фильтров
signal = {"hot_plugging_if_handler": func}
self.signal.put(signal)
def signal_handler(self, handler_dict):
match handler_dict:
case {"hot_removal_handler": func}:
self.handlers.discard(func)
stop_signal_queue = self.listener_queues.get(func.__name__)
stop_signal_queue.put(None)
self.listener_queues.pop(func.__name__, None)
print(f"Handling hot removal for function {func.__name__}")
case {"hot_plugging_handler": func}:
self.handlers.add(func)
self.listener_queues.update({func.__name__: queue.Queue()})
listener_thread=threading.Thread(target=self.wrapper, args=(self.listener_queues[func.__name__], func), name=func.__name__)
self.listeners.append(listener_thread)
listener_thread.daemon = True
listener_thread.start()
print(f"Handling hot plugging for function {func.__name__}")
case {"hot_removal_if_handler": func}:
self.if_handlers.discard(func)
print(f"Handling conditional hot removal for function {func.__name__}")
case {"hot_plugging_if_handler": func}:
self.if_handlers.add(func)
print(f"Handling conditional hot plugging for function {func.__name__}")
case _:
print("Unknown case")
def get_if_handlers(self, objekt=None):
if objekt == "__name__":
return set(if_handler.__name__ for if_handler in self.if_handlers)
else:
return self.if_handlers
if objekt == "__name__":
return set(handler.__name__ for handler in self.handlers)
else:
return self.handlers
def get_handlers(self, objekt=None):
if objekt == "__name__":
return set(handler.__name__ for handler in self.handlers)
else:
return self.handlers
def get_status_hooks(self):
return self.flagRun
if __name__ == '__main__':
log = True
def print_event(event):
if log:
print(f"!-----------: \n"
f"{time.strftime('%H:%M:%S')} -> {event.inputDevice} -> {event.event_type} -> {event.key_code}\n"
f"X: {event.x}, Y: {event.y}, Scan_code: {event.scan_code}\n"
f"____________! \n")
def print_event2(event):
if log:
print(f"!-----------: \n"
f"Нажатые кнопки: {event.key_down}\n"
f"____________! \n")
def print_event3(event):
if log:
threads = threading.enumerate()
thread_names = [thread.name for thread in threads]
threads_str = ', '.join(thread_names)
print(f"!-----------: \n"
f"Активные потоки: {threads_str}\n"
f"____________! \n")
def _if_block(event):
if event.key_code == 49:
print(f"Блокируем кнопку клавиатуры [1]")
return False
def _if2_block(event):
if event.key_code == "left":
print(f"Блокируем кнопку мыши [left]")
return False
def _if3_block(event):
# Проверка условия ограничения координат мыши
if event.inputDevice == "Mouse" and 0 <= event.y <= 250:
# Блокировка движения мыши
win32api.SetCursorPos((event.x, 250))
return False # Блокировать обработку события мыши
if event.inputDevice == "Mouse" and 1000 <= event.y <= 1080:
win32api.SetCursorPos((event.x, 1000))
return False
input_listener = InputListener()
input_listener.add_handler(print_event)
input_listener.add_handler(print_event2)
input_listener.add_handler(print_event3)
input_listener.add_if_handler(_if_block)
input_listener.add_if_handler(_if2_block)
input_listener.add_if_handler(_if3_block)
input_listener.start()
time.sleep(5)
input_listener.stop()
time.sleep(5)
input_listener.remove_handler(print_event2) # Удаление колбэк-обработчика на холодную
input_listener.remove_if_handler(_if3_block)
input_listener.start()
time.sleep(5)
input_listener.hot_removal_if_handler(_if_block)
input_listener.hot_removal_if_handler(_if2_block)
input_listener.hot_plugging_if_handler(_if3_block)
input_listener.hot_removal_handler(print_event)
print(input_listener.get_handlers("__name__"))
print(input_listener.get_handlers())
print(input_listener.get_if_handlers("__name__"))
print(input_listener.get_if_handlers())
input_listener.hot_plugging_handler(print_event2)
input_listener.hot_removal_handler(print_event2)
time.sleep(5)
input_listener.hot_plugging_handler(print_event)
input_listener.hot_plugging_handler(print_event2)
input_listener.hot_removal_if_handler(_if3_block)
print('__file__={0:<35} | __name__={1:<25} | __package__={2:<25}'.format(__file__,__name__,str(__package__)))
import ctypes
import win32con
import win32api
import win32gui
import atexit
from collections import namedtuple
import threading
import queue
import time
class InputListener:
"""
https://gist.github.com/Amgarak/5df8477bad67dabbc491322e74ce1c2c
This class is designed for listening to keyboard and mouse events in a Windows environment.
It utilizes low-level hooks to capture events such as key presses,
key releases, mouse movements, and mouse button clicks.
The class provides functionalities to register event handlers and filters,
allowing custom processing of events.
It also supports the control of threads for concurrent event handling.
"""
def __init__(self):
self.flagRun=False
self.signal = queue.Queue()
self._key_down = set()
self.if_handlers = set()
self.handlers = set()
self.listener_queues = {}
self.listeners =[]
self.variable_key_down = {}
self.event_types = {
win32con.WM_KEYDOWN: 'key_down', # 0x100: 'key down', - WM_KeyDown for normal keys - 256
win32con.WM_KEYUP: 'key_up', # 0x101: 'key up', - WM_KeyUp for normal keys - 257
0x104: 'key_down', # WM_SYSKEYDOWN, used for Alt key - 260
0x105: 'key_up'} # WM_SYSKEYUP, used for Alt key - 261
self.mouse_types={
0x200: 'move', # WM_MOUSEMOVE - 512
0x20A: 'wheel', # WM_MOUSEWHEEL - 522 - scan_code_top: 7864320; scan_code_bot: 4287102976
0x20E: 'H_wheel', # WM_MOUSEHWHEEL - 526
0x204: 'key_down', # WM_RBUTTONDOWN - 516
0x205: 'key_up', # WM_RBUTTONUP - 517
0x201: 'key_down', # WM_LBUTTONDOWN - 513
0x202: 'key_up', # WM_LBUTTONUP - 514
0x207: 'key_down', # WM_MBUTTONDOWN - 519
0x208: 'key_up', # WM_MBUTTONUP - 520
0x20B: 'key_down', # WM_XBUTTONDOWN - 523 - scan_code: 131072; scan_code: 65536
0x20C: 'key_up'} # WM_XBUTTONUP - 524 - scan_code: 131072; scan_code: 65536
self.mouse_key={
0x200: 'move',
0x20A: 'wheel',
7864320: 'wheel_top',
4287102976: 'wheel_bot',
0x20E: 'H_wheel',
0x204: 'right',
0x205: 'right',
0x201: 'left',
0x202: 'left',
0x207: 'middle',
0x208: 'middle',
131072: 'X_Button_1',
65536: 'X_Button_2'}
self.DeviceEvent = namedtuple('DeviceEvent', ['inputDevice', 'event_type', 'key_code', 'x', 'y', 'scan_code', 'key_down'])
self.DeviceEvent.__new__.__defaults__ = (None, None, None, None, None, None, None)
self.default_key = "Value not defined"
def key_event(self, event):
while not self.signal.empty():
self.signal_handler(self.signal.get())
for listener_queue in self.listener_queues.values():
listener_queue.put(event)
def key_down(self, event_type, key_code):
key = str(key_code)
if key not in self._key_down and event_type == 'key_down':
self._key_down.add(key)
return True
elif key in self._key_down and event_type == 'key_up':
self._key_down.remove(key)
return True
def if_block(self, event):
for if_handler in self.if_handlers:
result = if_handler(event)
if result is not None and not result:
return False
return True
def keyboard_low_level_handler(self, nCode, wParam, lParam):
lParam = ctypes.cast(lParam, ctypes.POINTER(ctypes.c_ulong)).contents.value
rezult = self.key_down(self.event_types[wParam], lParam)
event = self.DeviceEvent('Keyboard', self.event_types[wParam], lParam, key_down=self._key_down)
if rezult: # !!! - Отсекаем повторяющийся ивент key_down
self.key_event(event)
if self.if_block(event):
return ctypes.windll.user32.CallNextHookEx(self.keyboard_hook_id, nCode, wParam, lParam)
else:
return -1
def mouse_low_level_handler(self, nCode, wParam, lParam):
point = ctypes.cast(lParam, ctypes.POINTER(ctypes.c_long * 2)).contents
x = point[0]
y = point[1]
castomParam = self.mouse_key.get(lParam[1], self.default_key) if lParam[1] is not None else self.mouse_key[wParam]
self.key_down(self.mouse_types[wParam], castomParam)
event = self.DeviceEvent('Mouse', self.mouse_types[wParam], castomParam, x, y, lParam[1], self._key_down)
self.key_event(event)
if self.if_block(event):
return ctypes.windll.user32.CallNextHookEx(self.mouse_hook_id, nCode, wParam, lParam)
else:
return -1
def listen(self):
CMPFUNC = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_void_p))
ctypes.windll.user32.SetWindowsHookExW.argtypes = (
ctypes.c_int,
CMPFUNC,
ctypes.c_void_p,
ctypes.c_uint
)
keyboard_pointer = CMPFUNC(self.keyboard_low_level_handler)
self.keyboard_hook_id = ctypes.windll.user32.SetWindowsHookExW(win32con.WH_KEYBOARD_LL, keyboard_pointer,
win32api.GetModuleHandle(None), 0)
mouse_pointer = CMPFUNC(self.mouse_low_level_handler)
self.mouse_hook_id = ctypes.windll.user32.SetWindowsHookExW(win32con.WH_MOUSE_LL, mouse_pointer,
win32api.GetModuleHandle(None), 0)
atexit.register(ctypes.windll.user32.UnhookWindowsHookEx, self.keyboard_hook_id)
atexit.register(ctypes.windll.user32.UnhookWindowsHookEx, self.mouse_hook_id)
win32gui.GetMessage(None, 0, 0)
print(f"Hooks завершил свою работу..")
def add_if_handler(self, func): # Регестрируем колбэк-фильтров
self.if_handlers.add(func)
def add_handler(self, func): # Регестрируем колбэк-обработчиков
self.handlers.add(func)
def remove_if_handler(self, func): # Удаление колбэк-фильтров
self.if_handlers.discard(func)
def remove_handler(self, func): # Удаление колбэк-обработчиков
self.handlers.discard(func)
self.variable_key_down.pop(func.__name__, None)
def create_listener_queues(self):
self.listener_queues = {func.__name__: queue.Queue() for func in self.handlers}
def create_listener(self):
self.listeners = [
threading.Thread(target=self.wrapper, args=(self.listener_queues[func.__name__], func), name=func.__name__)
for func in self.handlers
]
def start_listener(self):
for listener_thread in self.listeners:
listener_thread.daemon = True
listener_thread.start()
def wrapper(self, listener_queue, func):
self.variable_key_down[func.__name__] = set()
while True:
event = listener_queue.get()
if event is None:
break
self.variable_key_down[func.__name__].clear()
self.variable_key_down[func.__name__].update(event.key_down)
event = event._replace(key_down=self.variable_key_down.get(func.__name__, None))
func(event)
print(f"Демон {func.__name__} завершил свою работу..")
def start(self, use_daemon = False):
self.create_listener_queues()
self.create_listener()
self.start_listener()
self.flagRun=True
# Создаем новый поток и передаем в него функцию self.listen() - без скобок!
# Мы передаем саму функцию в качестве объекта. В этом случае, функция не вызывается немедленно.
thread = threading.Thread(target=self.listen, name="Hooks")
thread.daemon = use_daemon # Включить при использовании GUI
thread.start() # Запускаем Hooks в отдельном потоке
self.thread_id = thread.native_id # Получаем дескриптор потока, используя атрибут native_id объекта threading.Thread
def stop(self): # Останавливаем слушатели\хуки
user32 = ctypes.windll.user32
WM_QUIT = 0x0012 # Используя дискриптор потока, отправляем сообщение WM_QUIT
user32.PostThreadMessageW(self.thread_id, WM_QUIT, 0, 0) # Для остановки win32gui.GetMessage(None, 0, 0)
ctypes.windll.user32.UnhookWindowsHookEx(self.keyboard_hook_id)
ctypes.windll.user32.UnhookWindowsHookEx(self.mouse_hook_id)
for listener_queue in self.listener_queues.values():
listener_queue.put(None)
self.listener_queues.clear()
self.listeners.clear()
self._key_down.clear()
self.variable_key_down.clear()
self.flagRun=False
def hot_removal_handler(self, func): # Горячее удаление колбэк-обработчиков
signal = {"hot_removal_handler": func}
self.signal.put(signal)
def hot_plugging_handler(self, func):# Горячая регестрирация колбэк-обработчиков
signal = {"hot_plugging_handler": func}
self.signal.put(signal)
def hot_removal_if_handler(self, func): # Горячее удаление колбэк-фильтров
signal = {"hot_removal_if_handler": func}
self.signal.put(signal)
def hot_plugging_if_handler(self, func): # Горячая регестрирация колбэк-фильтров
signal = {"hot_plugging_if_handler": func}
self.signal.put(signal)
def signal_handler(self, handler_dict):
match handler_dict:
case {"hot_removal_handler": func}:
self.handlers.discard(func)
stop_signal_queue = self.listener_queues.get(func.__name__)
stop_signal_queue.put(None)
self.variable_key_down.pop(func.__name__, None)
self.listener_queues.pop(func.__name__, None)
print(f"Handling hot removal for function {func.__name__}")
case {"hot_plugging_handler": func}:
self.handlers.add(func)
self.listener_queues.update({func.__name__: queue.Queue()})
listener_thread=threading.Thread(target=self.wrapper, args=(self.listener_queues[func.__name__], func), name=func.__name__)
self.listeners.append(listener_thread)
listener_thread.daemon = True
listener_thread.start()
print(f"Handling hot plugging for function {func.__name__}")
case {"hot_removal_if_handler": func}:
self.if_handlers.discard(func)
print(f"Handling conditional hot removal for function {func.__name__}")
case {"hot_plugging_if_handler": func}:
self.if_handlers.add(func)
print(f"Handling conditional hot plugging for function {func.__name__}")
case _:
print("Unknown case")
def get_if_handlers(self, objekt=None):
if objekt == "__name__":
return set(if_handler.__name__ for if_handler in self.if_handlers)
else:
return self.if_handlers
def get_handlers(self, objekt=None):
if objekt == "__name__":
return set(handler.__name__ for handler in self.handlers)
else:
return self.handlers
def get_status_hooks(self):
return self.flagRun
if __name__ == '__main__':
log = True
def print_event(event):
if log:
print(f"!-----------: \n"
f"{time.strftime('%H:%M:%S')} -> {event.inputDevice} -> {event.event_type} -> {event.key_code}\n"
f"X: {event.x}, Y: {event.y}, Scan_code: {event.scan_code}\n"
f"____________! \n")
def print_event2(event):
if log:
print(f"!-----------: \n"
f"Нажатые кнопки: {event.key_down}\n"
f"____________! \n")
def print_event3(event):
if log:
threads = threading.enumerate()
thread_names = [thread.name for thread in threads]
threads_str = ', '.join(thread_names)
print(f"!-----------: \n"
f"Активные потоки: {threads_str}\n"
f"____________! \n")
def _if_block(event):
if event.key_code == 49:
print(f"Блокируем кнопку клавиатуры [1]")
return False
def _if2_block(event):
if event.key_code == "left":
print(f"Блокируем кнопку мыши [left]")
return False
def _if3_block(event):
# Проверка условия ограничения координат мыши
if event.inputDevice == "Mouse" and 0 <= event.y <= 250:
# Блокировка движения мыши
win32api.SetCursorPos((event.x, 250))
return False # Блокировать обработку события мыши
if event.inputDevice == "Mouse" and 1000 <= event.y <= 1080:
win32api.SetCursorPos((event.x, 1000))
return False
input_listener = InputListener()
print(input_listener.get_status_hooks())
input_listener.add_handler(print_event)
input_listener.add_handler(print_event2)
input_listener.add_handler(print_event3)
input_listener.add_if_handler(_if_block)
input_listener.add_if_handler(_if2_block)
input_listener.add_if_handler(_if3_block)
input_listener.start()
print(input_listener.get_status_hooks())
time.sleep(5)
input_listener.stop()
print(input_listener.get_status_hooks())
time.sleep(5)
input_listener.remove_handler(print_event2) # Удаление колбэк-обработчика на холодную
input_listener.remove_if_handler(_if3_block)
input_listener.start()
print(input_listener.get_status_hooks())
time.sleep(5)
input_listener.hot_removal_if_handler(_if_block)
input_listener.hot_removal_if_handler(_if2_block)
input_listener.hot_plugging_if_handler(_if3_block)
input_listener.hot_removal_handler(print_event)
print(input_listener.get_handlers("__name__"))
print(input_listener.get_handlers())
print(input_listener.get_if_handlers("__name__"))
print(input_listener.get_if_handlers())
input_listener.hot_plugging_handler(print_event2)
input_listener.hot_removal_handler(print_event2)
time.sleep(5)
input_listener.hot_plugging_handler(print_event)
input_listener.hot_plugging_handler(print_event2)
input_listener.hot_removal_if_handler(_if3_block)
print('__file__={0:<35} | __name__={1:<25} | __package__={2:<25}'.format(__file__,__name__,str(__package__)))
import ctypes
import win32con
import win32api
import win32gui
import atexit
from collections import namedtuple
import threading
import queue
import time
class InputListener:
"""https://gist.github.com/Amgarak/5df8477bad67dabbc491322e74ce1c2c
This class is designed for listening to keyboard and mouse events in a Windows environment.
It utilizes low-level hooks to capture events such as key presses,
key releases, mouse movements, and mouse button clicks.
The class provides functionalities to register event handlers and filters,
allowing custom processing of events.
It also supports the control of threads for concurrent event handling.
"""
def __init__(self):
self.flagRun=False
self.signal = queue.Queue()
self._key_down = set()
self.if_handlers = set()
self.handlers = set()
self.listener_queues = {}
self.listeners =[]
self.variable_key_down = {}
self.event_types = {
win32con.WM_KEYDOWN: 'key_down', # 0x100: 'key down', - WM_KeyDown for normal keys - 256
win32con.WM_KEYUP: 'key_up', # 0x101: 'key up', - WM_KeyUp for normal keys - 257
0x104: 'key_down', # WM_SYSKEYDOWN, used for Alt key - 260
0x105: 'key_up'} # WM_SYSKEYUP, used for Alt key - 261
self.mouse_types={
0x200: 'move', # WM_MOUSEMOVE - 512
0x20A: 'wheel', # WM_MOUSEWHEEL - 522 - scan_code_top: 7864320; scan_code_bot: 4287102976
0x20E: 'H_wheel', # WM_MOUSEHWHEEL - 526
0x204: 'key_down', # WM_RBUTTONDOWN - 516
0x205: 'key_up', # WM_RBUTTONUP - 517
0x201: 'key_down', # WM_LBUTTONDOWN - 513
0x202: 'key_up', # WM_LBUTTONUP - 514
0x207: 'key_down', # WM_MBUTTONDOWN - 519
0x208: 'key_up', # WM_MBUTTONUP - 520
0x20B: 'key_down', # WM_XBUTTONDOWN - 523 - scan_code: 131072; scan_code: 65536
0x20C: 'key_up'} # WM_XBUTTONUP - 524 - scan_code: 131072; scan_code: 65536
self.mouse_key={
0x200: 'move',
0x20A: 'wheel',
7864320: 'wheel_top',
4287102976: 'wheel_bot',
0x20E: 'H_wheel',
0x204: 'right',
0x205: 'right',
0x201: 'left',
0x202: 'left',
0x207: 'middle',
0x208: 'middle',
131072: 'X_Button_1',
65536: 'X_Button_2'}
self.DeviceEvent = namedtuple('DeviceEvent', ['inputDevice', 'event_type', 'key_code', 'x', 'y', 'scan_code', 'key_down'])
self.DeviceEvent.__new__.__defaults__ = (None, None, None, None, None, None, None)
self.default_key = "Value not defined"
def key_event(self, event):
while not self.signal.empty():
self.signal_handler(self.signal.get())
for listener_queue in self.listener_queues.values():
listener_queue.put(event)
def key_down(self, event_type, key_code):
key = str(key_code)
if key not in self._key_down and event_type == 'key_down':
self._key_down.add(key)
return True
elif key in self._key_down and event_type == 'key_up':
self._key_down.remove(key)
return True
def if_block(self, event):
for if_handler in self.if_handlers:
result = if_handler(event)
if result is not None and not result:
return False
return True
def keyboard_low_level_handler(self, nCode, wParam, lParam):
lParam = ctypes.cast(lParam, ctypes.POINTER(ctypes.c_ulong)).contents.value
rezult = self.key_down(self.event_types[wParam], lParam)
event = self.DeviceEvent('Keyboard', self.event_types[wParam], lParam, key_down=self._key_down)
if rezult: # !!! - Отсекаем повторяющийся ивент key_down
self.key_event(event)
if self.if_block(event):
return ctypes.windll.user32.CallNextHookEx(self.keyboard_hook_id, nCode, wParam, lParam)
else:
return -1
def mouse_low_level_handler(self, nCode, wParam, lParam):
point = ctypes.cast(lParam, ctypes.POINTER(ctypes.c_long * 2)).contents
x = point[0]
y = point[1]
castomParam = self.mouse_key.get(lParam[1], self.default_key) if lParam[1] is not None else self.mouse_key[wParam]
self.key_down(self.mouse_types[wParam], castomParam)
event = self.DeviceEvent('Mouse', self.mouse_types[wParam], castomParam, x, y, lParam[1], self._key_down)
self.key_event(event)
if self.if_block(event):
return ctypes.windll.user32.CallNextHookEx(self.mouse_hook_id, nCode, wParam, lParam)
else:
return -1
def listen(self):
CMPFUNC = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_void_p))
ctypes.windll.user32.SetWindowsHookExW.argtypes = (
ctypes.c_int,
CMPFUNC,
ctypes.c_void_p,
ctypes.c_uint
)
keyboard_pointer = CMPFUNC(self.keyboard_low_level_handler)
self.keyboard_hook_id = ctypes.windll.user32.SetWindowsHookExW(win32con.WH_KEYBOARD_LL, keyboard_pointer,
win32api.GetModuleHandle(None), 0)
mouse_pointer = CMPFUNC(self.mouse_low_level_handler)
self.mouse_hook_id = ctypes.windll.user32.SetWindowsHookExW(win32con.WH_MOUSE_LL, mouse_pointer,
win32api.GetModuleHandle(None), 0)
atexit.register(ctypes.windll.user32.UnhookWindowsHookEx, self.keyboard_hook_id)
atexit.register(ctypes.windll.user32.UnhookWindowsHookEx, self.mouse_hook_id)
self.flagRun=True
win32gui.GetMessage(None, 0, 0)
print(f"Hooks завершил свою работу..")
def create_listener_queues(self):
self.listener_queues = {func.__name__: queue.Queue() for func in self.handlers}
def create_listener(self):
self.listeners = [
threading.Thread(target=self.wrapper, args=(self.listener_queues[func.__name__], func), name=func.__name__)
for func in self.handlers
]
def start_listener(self):
for listener_thread in self.listeners:
listener_thread.daemon = True
listener_thread.start()
def wrapper(self, listener_queue, func):
self.variable_key_down[func.__name__] = set()
while True:
event = listener_queue.get()
if event is None:
break
if func.__name__ in self.variable_key_down:
self.variable_key_down[func.__name__].clear()
self.variable_key_down[func.__name__].update(event.key_down)
event = event._replace(key_down=self.variable_key_down.get(func.__name__, None))
func(event)
print(f"Демон {func.__name__} завершил свою работу..")
def start(self, use_daemon = False):
self.create_listener_queues()
self.create_listener()
self.start_listener()
# Создаем новый поток и передаем в него функцию self.listen() - без скобок!
# Мы передаем саму функцию в качестве объекта. В этом случае, функция не вызывается немедленно.
thread = threading.Thread(target=self.listen, name="Hooks")
thread.daemon = use_daemon # Включить при использовании GUI
thread.start() # Запускаем Hooks в отдельном потоке
self.thread_id = thread.native_id # Получаем дескриптор потока, используя атрибут native_id объекта threading.Thread
def stop(self): # Останавливаем слушатели\хуки
start_time = time.time()
while (time.time() - start_time) < 20:
if self.flagRun:
break
time.sleep(1)
else:
return False
user32 = ctypes.windll.user32
WM_QUIT = 0x0012 # Используя дискриптор потока, отправляем сообщение WM_QUIT
user32.PostThreadMessageW(self.thread_id, WM_QUIT, 0, 0) # Для остановки win32gui.GetMessage(None, 0, 0)
ctypes.windll.user32.UnhookWindowsHookEx(self.keyboard_hook_id)
ctypes.windll.user32.UnhookWindowsHookEx(self.mouse_hook_id)
for listener_queue in self.listener_queues.values():
listener_queue.put(None)
for listener_thread in self.listeners:
listener_thread.join()
self.listener_queues.clear()
self.listeners.clear()
self._key_down.clear()
self.variable_key_down.clear()
self.flagRun=False
def signal_handler(self, handler_dict):
match handler_dict:
case {"hot_removal_handler": func}:
self.handlers.discard(func)
stop_signal_queue = self.listener_queues.get(func.__name__)
stop_signal_queue.put(None)
self.variable_key_down.pop(func.__name__, None)
self.listener_queues.pop(func.__name__, None)
print(f"Handling hot removal for function {func.__name__}")
case {"hot_plugging_handler": func}:
self.handlers.add(func)
self.listener_queues.update({func.__name__: queue.Queue()})
listener_thread=threading.Thread(target=self.wrapper, args=(self.listener_queues[func.__name__], func), name=func.__name__)
self.listeners.append(listener_thread)
listener_thread.daemon = True
listener_thread.start()
print(f"Handling hot plugging for function {func.__name__}")
case {"hot_removal_if_handler": func}:
self.if_handlers.discard(func)
print(f"Handling conditional hot removal for function {func.__name__}")
case {"hot_plugging_if_handler": func}:
self.if_handlers.add(func)
print(f"Handling conditional hot plugging for function {func.__name__}")
case _:
print("Unknown case")
def get_if_handlers(self, objekt=None):
if objekt == "__name__":
return set(if_handler.__name__ for if_handler in self.if_handlers)
else:
return self.if_handlers
def get_handlers(self, objekt=None):
if objekt == "__name__":
return set(handler.__name__ for handler in self.handlers)
else:
return self.handlers
def get_status_hooks(self):
return self.flagRun
def add_handler(self): # def __call__(self):
def decorator(func_handler):
def wrapper(event): # Обертка для обработки параметра функции
if event == True:
signal = {"hot_plugging_handler": func_handler} # Горячая регестрирация колбэк-обработчиков
self.signal.put(signal)
return True
elif event == False:
signal = {"hot_removal_handler": func_handler} # Горячее удаление колбэк-обработчиков
self.signal.put(signal)
return func_handler
else:
return False
self.handlers.add(func_handler) # Добавляем обработчик в список
return wrapper # Возвращаем обернутую функцию-обработчик
return decorator
def add_filter(self):
def decorator(func_if_handler):
def wrapper(event): # Обертка для обработки параметра функции
if event == True:
signal = {"hot_plugging_if_handler": func_if_handler} # Горячая регестрирация колбэк-фильтров
self.signal.put(signal)
return True
elif event == False:
signal = {"hot_removal_if_handler": func_if_handler} # Горячее удаление колбэк-фильтров
self.signal.put(signal)
return func_if_handler
else:
return False
self.if_handlers.add(func_if_handler) # Добавляем обработчик в список
return wrapper # Возвращаем обернутую функцию-обработчик
return decorator
if __name__ == '__main__':
input_listener = InputListener()
log = True
@input_listener.add_handler()
def print_event(event):
if log:
print(f"!-----------: \n"
f"{time.strftime('%H:%M:%S')} -> {event.inputDevice} -> {event.event_type} -> {event.key_code}\n"
f"X: {event.x}, Y: {event.y}, Scan_code: {event.scan_code}\n"
f"____________! \n")
@input_listener.add_handler()
def print_event2(event):
if log:
print(f"!-----------: \n"
f"Нажатые кнопки: {event.key_down}\n"
f"____________! \n")
@input_listener.add_handler()
def print_event3(event):
if log:
threads = threading.enumerate()
thread_names = [thread.name for thread in threads]
threads_str = ', '.join(thread_names)
print(f"!-----------: \n"
f"Активные потоки: {threads_str}\n"
f"____________! \n")
@input_listener.add_filter()
def _if_block(event):
if event.key_code == 49:
print(f"Блокируем кнопку клавиатуры [1]")
return False
@input_listener.add_filter()
def _if2_block(event):
if event.key_code == "left":
print(f"Блокируем кнопку мыши [left]")
return False
@input_listener.add_filter()
def _if3_block(event):
# Проверка условия ограничения координат мыши
if event.inputDevice == "Mouse" and 0 <= event.y <= 250:
# Блокировка движения мыши
win32api.SetCursorPos((event.x, 250))
return False # Блокировать обработку события мыши
if event.inputDevice == "Mouse" and 1000 <= event.y <= 1080:
win32api.SetCursorPos((event.x, 1000))
return False
input_listener.start()
print(input_listener.get_status_hooks())
time.sleep(5)
_if_block(False)
_if2_block(False)
_if3_block(False)
print(input_listener.get_handlers("__name__"))
print(input_listener.get_handlers())
print(input_listener.get_if_handlers("__name__"))
print(input_listener.get_if_handlers())
print_event(False)
print_event2(False)
print_event2(False)
print_event3(False)
time.sleep(5)
_if_block(True)
_if2_block(True)
_if3_block(True)
print_event(True)
print_event2(True)
print_event3(True)
input_listener.stop()
print(input_listener.get_status_hooks())
input_listener.start()
print('__file__={0:<35} | __name__={1:<25} | __package__={2:<25}'.format(__file__,__name__,str(__package__)))
import ctypes
import win32con
import win32api
import win32gui
import atexit
from collections import namedtuple
import threading
import queue
import time
class InputListener:
"""https://gist.github.com/Amgarak/5df8477bad67dabbc491322e74ce1c2c
This class is designed for listening to keyboard and mouse events in a Windows environment.
It utilizes low-level hooks to capture events such as key presses,
key releases, mouse movements, and mouse button clicks.
The class provides functionalities to register event handlers and filters,
allowing custom processing of events.
It also supports the control of threads for concurrent event handling.
"""
def __init__(self):
self.flagRun=False
self.signal = queue.Queue()
self._key_down = set()
self.if_handlers = set()
self.handlers = set()
self.listener_queues = {}
self.listeners =[]
self.variable_key_down = {}
self.event_types = {
win32con.WM_KEYDOWN: 'key_down', # 0x100: 'key down', - WM_KeyDown for normal keys - 256
win32con.WM_KEYUP: 'key_up', # 0x101: 'key up', - WM_KeyUp for normal keys - 257
0x104: 'key_down', # WM_SYSKEYDOWN, used for Alt key - 260
0x105: 'key_up'} # WM_SYSKEYUP, used for Alt key - 261
self.mouse_types={
0x200: 'move', # WM_MOUSEMOVE - 512
0x20A: 'wheel', # WM_MOUSEWHEEL - 522 - scan_code_top: 7864320; scan_code_bot: 4287102976
0x20E: 'H_wheel', # WM_MOUSEHWHEEL - 526
0x204: 'key_down', # WM_RBUTTONDOWN - 516
0x205: 'key_up', # WM_RBUTTONUP - 517
0x201: 'key_down', # WM_LBUTTONDOWN - 513
0x202: 'key_up', # WM_LBUTTONUP - 514
0x207: 'key_down', # WM_MBUTTONDOWN - 519
0x208: 'key_up', # WM_MBUTTONUP - 520
0x20B: 'key_down', # WM_XBUTTONDOWN - 523 - scan_code: 131072; scan_code: 65536
0x20C: 'key_up'} # WM_XBUTTONUP - 524 - scan_code: 131072; scan_code: 65536
self.mouse_key={
0x200: 'move',
0x20A: 'wheel',
7864320: 'wheel_top',
4287102976: 'wheel_bot',
0x20E: 'H_wheel',
0x204: 'right',
0x205: 'right',
0x201: 'left',
0x202: 'left',
0x207: 'middle',
0x208: 'middle',
131072: 'X_Button_1',
65536: 'X_Button_2'}
self.DeviceEvent = namedtuple('DeviceEvent', ['inputDevice', 'event_type', 'key_code', 'x', 'y', 'scan_code', 'key_down'])
self.DeviceEvent.__new__.__defaults__ = (None, None, None, None, None, None, None)
self.default_key = "Value not defined"
def key_event(self, event):
while not self.signal.empty():
self.signal_handler(self.signal.get())
for listener_queue in self.listener_queues.values():
listener_queue.put(event)
def key_down(self, event_type, key_code):
key = str(key_code)
if key not in self._key_down and event_type == 'key_down':
self._key_down.add(key)
return True
elif key in self._key_down and event_type == 'key_up':
self._key_down.remove(key)
return True
def if_block(self, event):
for if_handler in self.if_handlers:
result = if_handler(event)
if result is not None and not result:
return False
return True
def keyboard_low_level_handler(self, nCode, wParam, lParam):
lParam = ctypes.cast(lParam, ctypes.POINTER(ctypes.c_ulong)).contents.value
rezult = self.key_down(self.event_types[wParam], lParam)
event = self.DeviceEvent('Keyboard', self.event_types[wParam], lParam, key_down=self._key_down)
if rezult: # !!! - Отсекаем повторяющийся ивент key_down
self.key_event(event)
if self.if_block(event):
return ctypes.windll.user32.CallNextHookEx(self.keyboard_hook_id, nCode, wParam, lParam)
else:
return -1
def mouse_low_level_handler(self, nCode, wParam, lParam):
point = ctypes.cast(lParam, ctypes.POINTER(ctypes.c_long * 2)).contents
x = point[0]
y = point[1]
castomParam = self.mouse_key.get(lParam[1], self.default_key) if lParam[1] is not None else self.mouse_key[wParam]
self.key_down(self.mouse_types[wParam], castomParam)
event = self.DeviceEvent('Mouse', self.mouse_types[wParam], castomParam, x, y, lParam[1], self._key_down)
self.key_event(event)
if self.if_block(event):
return ctypes.windll.user32.CallNextHookEx(self.mouse_hook_id, nCode, wParam, lParam)
else:
return -1
def listen(self):
CMPFUNC = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_void_p))
ctypes.windll.user32.SetWindowsHookExW.argtypes = (
ctypes.c_int,
CMPFUNC,
ctypes.c_void_p,
ctypes.c_uint
)
keyboard_pointer = CMPFUNC(self.keyboard_low_level_handler)
self.keyboard_hook_id = ctypes.windll.user32.SetWindowsHookExW(win32con.WH_KEYBOARD_LL, keyboard_pointer,
win32api.GetModuleHandle(None), 0)
mouse_pointer = CMPFUNC(self.mouse_low_level_handler)
self.mouse_hook_id = ctypes.windll.user32.SetWindowsHookExW(win32con.WH_MOUSE_LL, mouse_pointer,
win32api.GetModuleHandle(None), 0)
atexit.register(ctypes.windll.user32.UnhookWindowsHookEx, self.keyboard_hook_id)
atexit.register(ctypes.windll.user32.UnhookWindowsHookEx, self.mouse_hook_id)
self.flagRun = True
win32gui.GetMessage(None, 0, 0)
print(f"Hooks завершил свою работу..")
def create_listener_queues(self):
self.listener_queues = {func.__name__: queue.Queue() for func in self.handlers}
def create_listener(self):
self.listeners = [
threading.Thread(target=self.wrapper, args=(self.listener_queues[func.__name__], func), name=func.__name__)
for func in self.handlers
]
def start_listener(self):
for listener_thread in self.listeners:
listener_thread.daemon = True
listener_thread.start()
def wrapper(self, listener_queue, func):
self.variable_key_down[func.__name__] = set()
while True:
event = listener_queue.get()
if event is None:
break
if func.__name__ in self.variable_key_down:
self.variable_key_down[func.__name__].clear()
self.variable_key_down[func.__name__].update(event.key_down)
event = event._replace(key_down=self.variable_key_down.get(func.__name__, None))
func(event)
print(f"Демон {func.__name__} завершил свою работу..")
def start(self, use_daemon = False):
self.create_listener_queues()
self.create_listener()
self.start_listener()
# Создаем новый поток и передаем в него функцию self.listen() - без скобок!
# Мы передаем саму функцию в качестве объекта. В этом случае, функция не вызывается немедленно.
thread = threading.Thread(target=self.listen, name="Hooks")
thread.daemon = use_daemon # Включить при использовании GUI
thread.start() # Запускаем Hooks в отдельном потоке
self.thread_id = thread.native_id # Получаем дескриптор потока, используя атрибут native_id объекта threading.Thread
def stop(self): # Останавливаем слушатели\хуки
start_time = time.time()
while (time.time() - start_time) < 20:
if self.flagRun:
break
time.sleep(1)
else:
return False
user32 = ctypes.windll.user32
WM_QUIT = 0x0012 # Используя дискриптор потока, отправляем сообщение WM_QUIT
user32.PostThreadMessageW(self.thread_id, WM_QUIT, 0, 0) # Для остановки win32gui.GetMessage(None, 0, 0)
ctypes.windll.user32.UnhookWindowsHookEx(self.keyboard_hook_id)
ctypes.windll.user32.UnhookWindowsHookEx(self.mouse_hook_id)
for listener_queue in self.listener_queues.values():
listener_queue.put(None)
for listener_thread in self.listeners:
listener_thread.join()
self.listener_queues.clear()
self.listeners.clear()
self._key_down.clear()
self.variable_key_down.clear()
self.flagRun = False
def signal_handler(self, handler_dict):
match handler_dict:
case {"hot_removal_handler": func}:
self.handlers.discard(func)
stop_signal_queue = self.listener_queues.get(func.__name__)
stop_signal_queue.put(None)
self.variable_key_down.pop(func.__name__, None)
self.listener_queues.pop(func.__name__, None)
print(f"Handling hot removal for function {func.__name__}")
case {"hot_plugging_handler": func}:
self.handlers.add(func)
self.listener_queues.update({func.__name__: queue.Queue()})
listener_thread=threading.Thread(target=self.wrapper, args=(self.listener_queues[func.__name__], func), name=func.__name__)
self.listeners.append(listener_thread)
listener_thread.daemon = True
listener_thread.start()
print(f"Handling hot plugging for function {func.__name__}")
case {"hot_removal_if_handler": func}:
self.if_handlers.discard(func)
print(f"Handling conditional hot removal for function {func.__name__}")
case {"hot_plugging_if_handler": func}:
self.if_handlers.add(func)
print(f"Handling conditional hot plugging for function {func.__name__}")
case _:
print("Unknown case")
def get_if_handlers(self, objekt=None):
if objekt == "__name__":
return set(if_handler.__name__ for if_handler in self.if_handlers)
else:
return self.if_handlers
def get_handlers(self, objekt=None):
if objekt == "__name__":
return set(handler.__name__ for handler in self.handlers)
else:
return self.handlers
def get_status_hooks(self):
return self.flagRun
def hot_removal_handler(self, func): # Горячее удаление колбэк-обработчиков
signal = {"hot_removal_handler": func}
self.signal.put(signal)
def hot_plugging_handler(self, func):# Горячая регестрирация колбэк-обработчиков
signal = {"hot_plugging_handler": func}
self.signal.put(signal)
def hot_removal_if_handler(self, func): # Горячее удаление колбэк-фильтров
signal = {"hot_removal_if_handler": func}
self.signal.put(signal)
def hot_plugging_if_handler(self, func): # Горячая регестрирация колбэк-фильтров
signal = {"hot_plugging_if_handler": func}
self.signal.put(signal)
def add_if_handler(self, func): # Регестрируем колбэк-фильтров
self.if_handlers.add(func)
def add_handler(self, func): # Регестрируем колбэк-обработчиков
self.handlers.add(func)
def remove_if_handler(self, func): # Удаление колбэк-фильтров
self.if_handlers.discard(func)
def remove_handler(self, func): # Удаление колбэк-обработчиков
self.handlers.discard(func)
self.variable_key_down.pop(func.__name__, None)
def __call__(self):
def decorator(func_handler):
def wrapper(event): # Обертка для обработки параметра функции
if event == True:
signal = {"hot_plugging_handler": func_handler} # Горячая регестрирация колбэк-обработчиков
self.signal.put(signal)
return True
elif event == False:
signal = {"hot_removal_handler": func_handler} # Горячее удаление колбэк-обработчиков
self.signal.put(signal)
return func_handler
else:
return False
self.handlers.add(func_handler) # Добавляем обработчик в список
return wrapper # Возвращаем обернутую функцию-обработчик
return decorator
def add_filter(self):
def decorator(func_if_handler):
def wrapper(event): # Обертка для обработки параметра функции
if event == True:
signal = {"hot_plugging_if_handler": func_if_handler} # Горячая регестрирация колбэк-фильтров
self.signal.put(signal)
return True
elif event == False:
signal = {"hot_removal_if_handler": func_if_handler} # Горячее удаление колбэк-фильтров
self.signal.put(signal)
return func_if_handler
else:
return False
self.if_handlers.add(func_if_handler) # Добавляем обработчик в список
return wrapper # Возвращаем обернутую функцию-обработчик
return decorator
if __name__ == '__main__':
input_listener = InputListener()
log = True
@input_listener()
def print_event(event):
if log:
print(f"!-----------: \n"
f"{time.strftime('%H:%M:%S')} -> {event.inputDevice} -> {event.event_type} -> {event.key_code}\n"
f"X: {event.x}, Y: {event.y}, Scan_code: {event.scan_code}\n"
f"____________! \n")
@input_listener()
def print_event2(event):
if log:
print(f"!-----------: \n"
f"Нажатые кнопки: {event.key_down}\n"
f"____________! \n")
@input_listener()
def print_event3(event):
if log:
threads = threading.enumerate()
thread_names = [thread.name for thread in threads]
threads_str = ', '.join(thread_names)
print(f"!-----------: \n"
f"Активные потоки: {threads_str}\n"
f"____________! \n")
@input_listener.add_filter()
def _if_block(event):
if event.key_code == 49:
print(f"Блокируем кнопку клавиатуры [1]")
return False
@input_listener.add_filter()
def _if2_block(event):
if event.key_code == "left":
print(f"Блокируем кнопку мыши [left]")
return False
@input_listener.add_filter()
def _if3_block(event):
# Проверка условия ограничения координат мыши
if event.inputDevice == "Mouse" and 0 <= event.y <= 250:
# Блокировка движения мыши
win32api.SetCursorPos((event.x, 250))
return False # Блокировать обработку события мыши
if event.inputDevice == "Mouse" and 1000 <= event.y <= 1080:
win32api.SetCursorPos((event.x, 1000))
return False
input_listener.start()
print(input_listener.get_status_hooks())
time.sleep(5)
_if_block(False)
_if2_block(False)
_if3_block(False)
print(input_listener.get_handlers("__name__"))
print(input_listener.get_handlers())
print(input_listener.get_if_handlers("__name__"))
print(input_listener.get_if_handlers())
print_event(False)
print_event2(False)
print_event3(False)
time.sleep(5)
_if_block(True)
_if2_block(True)
_if3_block(True)
print_event(True)
print_event2(True)
print_event3(True)
input_listener.stop()
print(input_listener.get_status_hooks())
input_listener.start()
"""if __name__ == '__main__':
log = True
def print_event(event):
if log:
print(f"!-----------: \n"
f"{time.strftime('%H:%M:%S')} -> {event.inputDevice} -> {event.event_type} -> {event.key_code}\n"
f"X: {event.x}, Y: {event.y}, Scan_code: {event.scan_code}\n"
f"____________! \n")
def print_event2(event):
if log:
print(f"!-----------: \n"
f"Нажатые кнопки: {event.key_down}\n"
f"____________! \n")
def print_event3(event):
if log:
threads = threading.enumerate()
thread_names = [thread.name for thread in threads]
threads_str = ', '.join(thread_names)
print(f"!-----------: \n"
f"Активные потоки: {threads_str}\n"
f"____________! \n")
def _if_block(event):
if event.key_code == 49:
print(f"Блокируем кнопку клавиатуры [1]")
return False
def _if2_block(event):
if event.key_code == "left":
print(f"Блокируем кнопку мыши [left]")
return False
def _if3_block(event):
# Проверка условия ограничения координат мыши
if event.inputDevice == "Mouse" and 0 <= event.y <= 250:
# Блокировка движения мыши
win32api.SetCursorPos((event.x, 250))
return False # Блокировать обработку события мыши
if event.inputDevice == "Mouse" and 1000 <= event.y <= 1080:
win32api.SetCursorPos((event.x, 1000))
return False
input_listener = InputListener()
print(input_listener.get_status_hooks())
input_listener.add_handler(print_event)
input_listener.add_handler(print_event2)
input_listener.add_handler(print_event3)
input_listener.add_if_handler(_if_block)
input_listener.add_if_handler(_if2_block)
input_listener.add_if_handler(_if3_block)
input_listener.start()
print(input_listener.get_status_hooks())
time.sleep(5)
input_listener.stop()
print(input_listener.get_status_hooks())
time.sleep(5)
input_listener.remove_handler(print_event2) # Удаление колбэк-обработчика на холодную
input_listener.remove_if_handler(_if3_block)
input_listener.start()
print(input_listener.get_status_hooks())
time.sleep(5)
input_listener.hot_removal_if_handler(_if_block)
input_listener.hot_removal_if_handler(_if2_block)
input_listener.hot_plugging_if_handler(_if3_block)
input_listener.hot_removal_handler(print_event)
print(input_listener.get_handlers("__name__"))
print(input_listener.get_handlers())
print(input_listener.get_if_handlers("__name__"))
print(input_listener.get_if_handlers())
input_listener.hot_plugging_handler(print_event2)
input_listener.hot_removal_handler(print_event2)
time.sleep(5)
input_listener.hot_plugging_handler(print_event)
input_listener.hot_plugging_handler(print_event2)
input_listener.hot_removal_if_handler(_if3_block)"""
from collections import namedtuple
import ctypes
import win32con
import win32api
import win32gui
import atexit
KeyboardEvent = namedtuple('KeyboardEvent', ['event_type', 'key_code'])
event_types = {win32con.WM_KEYDOWN: 'key down',
win32con.WM_KEYUP: 'key up',
0x104: 'key down', # WM_SYSKEYDOWN, used for Alt key.
0x105: 'key up', # WM_SYSKEYUP, used for Alt key.
}
handlers = []
def listen():
def low_level_handler(nCode, wParam, lParam):
lParam = ctypes.cast(lParam, ctypes.POINTER(ctypes.c_ulong)).contents.value
event = KeyboardEvent(event_types[wParam], lParam)
for handler in handlers:
handler(event)
return ctypes.windll.user32.CallNextHookEx(hook_id, nCode, wParam, lParam)
CMPFUNC = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_void_p))
ctypes.windll.user32.SetWindowsHookExW.argtypes = (
ctypes.c_int,
CMPFUNC,
ctypes.c_void_p,
ctypes.c_uint
)
pointer = CMPFUNC(low_level_handler)
hook_id = ctypes.windll.user32.SetWindowsHookExW(win32con.WH_KEYBOARD_LL, pointer,
win32api.GetModuleHandle(None), 0)
atexit.register(ctypes.windll.user32.UnhookWindowsHookEx, hook_id)
win32gui.GetMessage(None, 0, 0)
if __name__ == '__main__':
def print_event(event):
if event.key_code == 160 or event.key_code == 162:
print("Нажат ctrl ИЛИ shift:", event)
handlers.append(print_event)
listen()
from collections import namedtuple
import ctypes
import win32con
import win32api
import win32gui
import atexit
KeyboardEvent = namedtuple('KeyboardEvent', ['event_type', 'key_code',
'scan_code', 'alt_pressed',
'time'])
handlers = []
def listen():
def low_level_handler(nCode, wParam, lParam):
event = KeyboardEvent(event_types[wParam], lParam[0], lParam[1],
lParam[2] == 32, lParam[3])
if event.key_code == 8589934641: # Код клавиши 1
print("test")
return -1 # Блокировать обработку сообщения
for handler in handlers:
handler(event)
return ctypes.windll.user32.CallNextHookEx(hook_id, nCode, wParam, lParam)
event_types = {win32con.WM_KEYDOWN: 'key down',
win32con.WM_KEYUP: 'key up',
0x104: 'key down', # WM_SYSKEYDOWN, used for Alt key.
0x105: 'key up', # WM_SYSKEYUP, used for Alt key.
}
CMPFUNC = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_void_p))
ctypes.windll.user32.SetWindowsHookExW.argtypes = (
ctypes.c_int,
CMPFUNC,
ctypes.c_void_p,
ctypes.c_uint
)
pointer = CMPFUNC(low_level_handler)
hook_id = ctypes.windll.user32.SetWindowsHookExW(win32con.WH_KEYBOARD_LL, pointer,
win32api.GetModuleHandle(None), 0)
atexit.register(ctypes.windll.user32.UnhookWindowsHookEx, hook_id)
while True:
msg = win32gui.GetMessage(None, 0, 0)
win32gui.TranslateMessage(ctypes.byref(msg))
win32gui.DispatchMessage(ctypes.byref(msg))
if __name__ == '__main__':
def print_event(event):
print("Event:", event)
print("key_code:", hex(event.key_code))
handlers.append(print_event)
listen()
print('__file__={0:<35} | __name__={1:<25} | __package__={2:<25}'.format(__file__,__name__,str(__package__)))
class KeyboardMauseKeys:
'''
Исходный словарь с именами кнопок в качестве ключей и DEC значениями в качестве значений..
'''
def __init__(self):
self.key_DEC = {
"Left_Button": "left",
"Right_Button": "right",
"Middle_Button": "middle",
"Wheel_Top": "wheel_top",
"Wheel_Bot": "wheel_bot",
"X_Button_1": "X_Button_1",
"X_Button_2": "X_Button_2",
"Break": "3",
"Backspace": "8",
"Tab": "9",
"Clear": "12",
"Enter": "13",
"Shift": "16",
"Ctrl": "17",
"Alt": "18",
"Pause": "19",
"Caps_Lock": "20",
"Kana": "21",
"Junja": "23",
"Final": "24",
"Kanji": "25",
"Esc": "27",
"Convert": "28",
"Non_Convert": "29",
"Accept": "30",
"Mode_Change": "31",
"Space": "32",
"Page_Up": "33",
"Page_Down": "34",
"End": "35",
"Home": "36",
"Arrow_Left": "37",
"Arrow_Up": "38",
"Arrow_Right": "39",
"Arrow_Down": "40",
"Select": "41",
"Print": "42",
"Execute": "43",
"Print_Screen": "44",
"Insert": "45",
"Delete": "46",
"Help": "47",
"_0": "48",
"_1": "49",
"_2": "50",
"_3": "51",
"_4": "52",
"_5": "53",
"_6": "54",
"_7": "55",
"_8": "56",
"_9": "57",
"A": "65",
"B": "66",
"C": "67",
"D": "68",
"E": "69",
"F": "70",
"G": "71",
"H": "72",
"I": "73",
"J": "74",
"K": "75",
"L": "76",
"M": "77",
"N": "78",
"O": "79",
"P": "80",
"Q": "81",
"R": "82",
"S": "83",
"T": "83",
"U": "85",
"V": "86",
"W": "87",
"X": "88",
"Y": "89",
"Z": "90",
"Left_Win": "91",
"Right_Win": "92",
"Context_Menu": "93",
"Sleep": "95",
"Numpad_0": "96",
"Numpad_1": "97",
"Numpad_2": "98",
"Numpad_3": "99",
"Numpad_4": "100",
"Numpad_5": "101",
"Numpad_6": "102",
"Numpad_7": "103",
"Numpad_8": "104",
"Numpad_9": "105",
"NumpadStar": "106",
"NumpadPlus": "107",
"Separator": "108",
"NumpadMinus": "109",
"NumpadDot": "110",
"NumpadSlash": "111",
"F1": "112",
"F2": "113",
"F3": "114",
"F4": "115",
"F5": "116",
"F6": "117",
"F7": "118",
"F8": "119",
"F9": "120",
"F10": "121",
"F11": "122",
"F12": "123",
"F13": "124",
"F14": "125",
"F15": "126",
"F16": "127",
"F17": "128",
"F18": "129",
"F19": "130",
"F20": "131",
"F21": "132",
"F22": "133",
"F23": "134",
"F24": "135",
"Num_Lock": "144",
"Scrol_Lock": "145",
"Jisho": "146",
"Mashu": "147",
"Touroku": "148",
"Loya": "149",
"Roya": "150",
"Left_Shift": "160",
"Right_Shift": "161",
"Left_Ctrl": "162",
"Right_Ctrl": "163",
"Left_Alt": "164",
"Right_Alt": "165",
"Browser_Back": "166",
"Browser_Forward": "167",
"Browser_Refresh": "168",
"Browser_Stop": "169",
"Browser_Search": "170",
"Browser_Favorites": "171",
"Browser_Home": "172",
"Volume_Mute": "173",
"Volume_Down": "174",
"Volume_Up": "175",
"Next_Track": "176",
"Previous_Track": "177",
"Stop": "178",
"Play_Pause": "179",
"Mail": "180",
"Media": "181",
"App1": "182",
"App2": "183",
";_:": "186",
"=_+": "187",
",_<": "188",
"-_-_": "189",
"._>": "190",
"/_?": "191",
"`_~": "192",
"Abnt_C1": "193",
"Abnt_C2": "193",
"[_{": "219",
"\\_|": "229",
"]_}": "221",
"\\_\"": "222",
"!_§": "223",
"Ax": "225",
">_<": "226",
"IcoHlp": "227",
"Process": "229",
"IcoClr": "230",
"Packet": "231",
"Reset": "233",
"Jump": "234",
"OemPa1": "235",
"OemPa2": "236",
"OemPa3": "237",
"WsCtrl": "238",
"CuSel": "239",
"Oem_Attn": "240",
"Finish": "241",
"Copy": "242",
"Auto": "243",
"Enlw": "244",
"Back_Tab": "245",
"Attn": "246",
"CrSel": "247",
"ExSel": "248",
"ErEof": "249",
"Play": "250",
"Zoom": "251",
"Pa1": "253",
"OemClr": "254",
}
def get_dec(self, key):
'''
Возвращает DEC значение по имени кнопки (ключу)
'''
return self.key_DEC.get(key)
def get_all_dec(self):
'''
Возвращает весь словарь DEC значений
'''
return self.key_DEC
# -*- encoding: utf-8 -*-
from collections import namedtuple
import ctypes
import win32con
import win32api
import win32gui
import atexit
MouseEvent = namedtuple('MouseEvent', ['event_type', 'x', 'y'])
handlers = []
def listen():
def low_level_handler(nCode, wParam, lParam):
point = ctypes.cast(lParam, ctypes.POINTER(ctypes.c_long * 2)).contents
x = point[0]
y = point[1]
event = MouseEvent(wParam, x, y)
for handler in handlers:
handler(event)
# Проверка условия ограничения координат мыши
if 0 <= y <= 250:
# Блокировка движения мыши
win32api.SetCursorPos((x, 250))
return -1 # Блокировать обработку события мыши
if 1000 <= y <= 1080:
win32api.SetCursorPos((x, 1000))
return -1 # Блокировать обработку события мыши
if event.event_type == win32con.WM_RBUTTONUP or event.event_type == win32con.WM_RBUTTONDOWN:
print("Правая кнопка мыши сломалась..")
return -1 # Блокировать обработку сообщения
return ctypes.windll.user32.CallNextHookEx(hook_id, nCode, wParam, lParam)
CMPFUNC = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_void_p))
ctypes.windll.user32.SetWindowsHookExW.argtypes = (
ctypes.c_int,
CMPFUNC,
ctypes.c_void_p,
ctypes.c_uint
)
pointer = CMPFUNC(low_level_handler)
hook_id = ctypes.windll.user32.SetWindowsHookExW(win32con.WH_MOUSE_LL, pointer,
win32api.GetModuleHandle(None), 0)
atexit.register(ctypes.windll.user32.UnhookWindowsHookEx, hook_id)
while True:
msg = win32gui.GetMessage(None, 0, 0)
win32gui.TranslateMessage(ctypes.byref(msg))
win32gui.DispatchMessage(ctypes.byref(msg))
if __name__ == '__main__':
def print_event(event):
print(f"Event Type: {event.event_type}")
print(f"Position: ({event.x}, {event.y})")
print()
handlers.append(print_event)
listen()
import tkinter as tk
import ctypes
import ctypes.wintypes
from threading import Thread
user32 = ctypes.windll.user32
kernel32 = ctypes.windll.kernel32
WM_QUIT = 0x0012
tid = None
def loop():
msg = ctypes.wintypes.MSG()
while user32.GetMessageW(ctypes.byref(msg), 0, 0, 0) != 0:
print('msg')
user32.TranslateMessage(msg)
user32.DispatchMessageW(msg)
print('end of loop')
def PostThreadMessage():
user32.PostThreadMessageW(tid, WM_QUIT, 0, 0)
root = tk.Tk()
t = Thread(target=loop)
t.daemon=True
t.start()
tid = t.native_id
tk.Button(root, text='PostThreadMessage', command=PostThreadMessage).pack()
root.mainloop()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment