{"id":15671346,"url":"https://github.com/winstxnhdw/keywin","last_synced_at":"2025-05-06T20:24:05.149Z","repository":{"id":108152563,"uuid":"569686388","full_name":"winstxnhdw/KeyWin","owner":"winstxnhdw","description":"A fast Python wrapper for Win32's SendInput function using C extensions.","archived":false,"fork":false,"pushed_at":"2025-05-04T21:37:42.000Z","size":268,"stargazers_count":11,"open_issues_count":3,"forks_count":3,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-05-04T22:33:08.511Z","etag":null,"topics":["python-c-extension","sendinput","win32-api"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/winstxnhdw.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-11-23T11:45:58.000Z","updated_at":"2025-05-04T21:37:44.000Z","dependencies_parsed_at":"2023-11-07T10:28:42.314Z","dependency_job_id":"8313f1d9-8c7c-4ac1-9945-aca2d8d52103","html_url":"https://github.com/winstxnhdw/KeyWin","commit_stats":{"total_commits":128,"total_committers":5,"mean_commits":25.6,"dds":0.4453125,"last_synced_commit":"bd0714de07712aec45ba1a6bf659baccf64e8953"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/winstxnhdw%2FKeyWin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/winstxnhdw%2FKeyWin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/winstxnhdw%2FKeyWin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/winstxnhdw%2FKeyWin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/winstxnhdw","download_url":"https://codeload.github.com/winstxnhdw/KeyWin/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252762504,"owners_count":21800319,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["python-c-extension","sendinput","win32-api"],"created_at":"2024-10-03T15:01:59.000Z","updated_at":"2025-05-06T20:24:05.123Z","avatar_url":"https://github.com/winstxnhdw.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# KeyWin\n\n[![python](https://img.shields.io/badge/python-3.9%20|%203.10%20|%203.11%20|%203.12%20|%203.13-blue)](https://www.python.org/)\n[![linting: pylint](https://img.shields.io/badge/linting-pylint-yellowgreen)](https://github.com/PyCQA/pylint)\n[![main.yml](https://github.com/winstxnhdw/KeyWin/actions/workflows/main.yml/badge.svg)](https://github.com/winstxnhdw/KeyWin/actions/workflows/main.yml)\n[![formatter.yml](https://github.com/winstxnhdw/KeyWin/actions/workflows/formatter.yml/badge.svg)](https://github.com/winstxnhdw/KeyWin/actions/workflows/formatter.yml)\n\n`KeyWin` is a fast Python wrapper for Win32's SendInput function using C extensions. It is designed to be used in applications that require low-latency inputs. All functions drop the Global Interpreter Lock (GIL).\n\n## Installation\n\n```bash\npip install git+https://github.com/winstxnhdw/KeyWin\n```\n\n## Usage\n\n### Keyboard\n\n`KeyWin` provides a low-level API for keyboard inputs based on Microsoft's [Virtual-Key Codes](https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes).\n\n#### Pre-mapped Key Codes\n\n`KeyWin` provides a set of pre-mapped key codes for common keys. These key codes are defined [here](keywin/keyboard/codes/__init__.py).\n\n```python\nfrom keywin import KeyCode, keyboard\n\n# Enter\nkeyboard.press(KeyCode.VK_RETURN)\n\n# Win + D\nkeyboard.press(KeyCode.VK_LWIN, KeyCode.VK_D)\n\n# Hold Shift + A\nkeyboard.hold(KeyCode.VK_SHIFT, KeyCode.VK_A)\n\n# Release Shift + A\nkeyboard.release(KeyCode.VK_SHIFT, KeyCode.VK_A)\n\n# Hold unicode character\nkeyboard.hold_unicode('!')\n\n# Release unicode character\nkeyboard.release_unicode('!')\n```\n\n#### Manual Key Codes\n\nIf you are unable to find the key code you need, you can enter the hex key values manually.\n\n```python\nfrom keywin import keyboard\n\n# Enter\nkeyboard.press(0x0D)\n\n# Win + D\nkeyboard.press(0x5B, 0x44)\n```\n\n#### Unicode Inputs\n\nYou may also send long unicode inputs. Certain unicode, such as `\\n`, cannot be converted into a keystroke and will be ignored by Windows. It is also more performant to use this function rather than `press()` for long unicode inputs.\n\n```python\nfrom keywin import keyboard\n\nkeyboard.write('Hello, world!')\n```\n\n### Mouse\n\nSimilar to the keyboard, `KeyWin` provides a low-level API for mouse inputs based on Microsoft's [MOUSEINPUT](https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput) structure.\n\n#### Helpers\n\n`KeyWin` provides a set of helper functions for common mouse inputs.\n\n```python\nfrom keywin import mouse\n\n# Move mouse 100 down and 100 right from current position\nmouse.move_relative(100, 100)\n\n# Move mouse to (100, 100)\nmouse.move(100, 100)\n\n# Left click\nmouse.left_click()\n\n# Right click\nmouse.right_click()\n\n# Middle click\nmouse.middle_click()\n\n# xbutton1 click\nmouse.xbutton1_click()\n\n# xbutton2 click\nmouse.xbutton2_click()\n\n# Press left button\nmouse.left_press()\n\n# Press right button\nmouse.right_press()\n\n# Press middle button\nmouse.middle_press()\n\n# Press xbutton1\nmouse.xbutton1_press()\n\n# Press xbutton2\nmouse.xbutton2_press()\n\n# Release left button\nmouse.left_release()\n\n# Release right button\nmouse.right_release()\n\n# Release middle button\nmouse.middle_release()\n\n# Release xbutton1\nmouse.xbutton1_release()\n\n# Release xbutton2\nmouse.xbutton2_release()\n\n# Scroll up\nmouse.scroll(10)\n\n# Scroll down\nmouse.scroll(-10)\n\n# Scroll left\nmouse.scroll_horizontal(10)\n\n# Scroll right\nmouse.scroll_horizontal(-10)\n```\n\n#### Low-level Access\n\nThe `mouse` helpers have _some_ overhead due to indirection. You can avoid this by directly accessing the low-level API. `MouseEvent` is a `NamedTuple` which can be passed to the low-level wrapper function `send_events()`.\n\n```python\nfrom keywin.mouse import MouseCode, send_events\nfrom keywin.mouse.helpers import MouseEvent\n\nleft_click_event = MouseEvent(MouseCode.MOUSE_LEFT_CLICK, 100, 100)\nright_click_event = MouseEvent(MouseCode.MOUSE_RIGHT_CLICK, 100, 100)\nsend_events(left_click_event, right_click_event)\n```\n\n## Benchmarks\n\n`KeyWin` is designed to be used in applications that require low-latency inputs. The following benchmarks were performed against boppreh's [keyboard](https://github.com/boppreh/keyboard) and [mouse](https://github.com/boppreh/mouse) libraries. In all cases, `KeyWin` is magnitudes faster than the other libraries.\n\n### Keyboard Benchmark\n\n\u003cdetails\u003e\n\n\u003csummary\u003eKeyWin\u003c/summary\u003e\n\n```python\nimport cProfile as profile\n\nfrom keywin import keyboard, KeyCode\n\n\ndef keywin():\n\n    keyboard.press(KeyCode.VK_SPACE)\n\n\nif __name__ == '__main__':\n    profile.run('keywin()')\n```\n\n\u003c/details\u003e\n\n```txt\n6 function calls in 0.000 seconds\n\nncalls  tottime  percall  cumtime  percall filename:lineno(function)\n    1    0.000    0.000    0.000    0.000 \u003cstring\u003e:1(\u003cmodule\u003e)\n    1    0.000    0.000    0.000    0.000 keyboard.py:6(press)\n    1    0.000    0.000    0.000    0.000 test.py:6(keywin)\n    1    0.000    0.000    0.000    0.000 {built-in method builtins.exec}\n    1    0.000    0.000    0.000    0.000 {built-in method keywin.send_input.press_keyboard}\n    1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}\n```\n\n\u003cdetails\u003e\n\n\u003csummary\u003e\u003ca href=\"https://github.com/boppreh/keyboard\"\u003eboppreh/keyboard\u003c/a\u003e\u003c/summary\u003e\n\n```python\nimport cProfile as profile\n\nfrom keyboard import press_and_release\n\n\ndef keyboard():\n\n    press_and_release(\"space\")\n\n\nif __name__ == '__main__':\n    profile.run('keyboard()')\n```\n\n\u003c/details\u003e\n\n```txt\n172510 function calls (172499 primitive calls) in 0.177 seconds\n\nncalls  tottime  percall  cumtime  percall filename:lineno(function)\n    1    0.000    0.000    0.177    0.177 \u003cstring\u003e:1(\u003cmodule\u003e)\n    1    0.000    0.000    0.000    0.000 __init__.py:102(\u003clambda\u003e)\n    2    0.000    0.000    0.000    0.000 __init__.py:103(\u003clambda\u003e)\n    2    0.000    0.000    0.000    0.000 __init__.py:106(\u003clambda\u003e)\n    1    0.000    0.000    0.175    0.175 __init__.py:298(key_to_scan_codes)\n  121    0.000    0.000    0.175    0.001 __init__.py:317(\u003cgenexpr\u003e)\n    1    0.000    0.000    0.175    0.175 __init__.py:328(parse_hotkey)\n    2    0.000    0.000    0.175    0.088 __init__.py:358(\u003cgenexpr\u003e)\n    1    0.000    0.000    0.177    0.177 __init__.py:361(send)\n    2    0.000    0.000    0.000    0.000 __init__.py:384(__getattr__)\n    2    0.000    0.000    0.000    0.000 __init__.py:391(__getitem__)\n21209    0.015    0.000    0.022    0.000 _canonical_names.py:1233(normalize_name)\n18796    0.110    0.000    0.111    0.000 _winkeyboard.py:351(get_event_names)\n    1    0.028    0.028    0.175    0.175 _winkeyboard.py:383(_setup_name_tables)\n    1    0.000    0.000    0.000    0.000 _winkeyboard.py:393(\u003clistcomp\u003e)\n    1    0.000    0.000    0.000    0.000 _winkeyboard.py:394(\u003clistcomp\u003e)\n 4400    0.003    0.000    0.004    0.000 _winkeyboard.py:411(\u003clistcomp\u003e)\n    5    0.000    0.000    0.000    0.000 _winkeyboard.py:429(\u003clambda\u003e)\n12488    0.002    0.000    0.002    0.000 _winkeyboard.py:431(order_key)\n  121    0.000    0.000    0.175    0.001 _winkeyboard.py:567(map_name)\n    2    0.001    0.001    0.002    0.001 _winkeyboard.py:577(_send_event)\n    1    0.000    0.000    0.000    0.000 _winkeyboard.py:590(press)\n    1    0.000    0.000    0.001    0.001 _winkeyboard.py:593(release)\n    4    0.000    0.000    0.000    0.000 enum.py:359(__call__)\n    4    0.000    0.000    0.000    0.000 enum.py:678(__new__)\n    2    0.000    0.000    0.000    0.000 enum.py:986(__and__)\n    2    0.000    0.000    0.000    0.000 re.py:222(split)\n    2    0.000    0.000    0.000    0.000 re.py:288(_compile)\n    3    0.000    0.000    0.000    0.000 sre_compile.py:265(_compile_charset)\n    3    0.000    0.000    0.000    0.000 sre_compile.py:292(_optimize_charset)\n    3    0.000    0.000    0.000    0.000 sre_compile.py:447(_simple)\n    1    0.000    0.000    0.000    0.000 sre_compile.py:456(_generate_overlap_table)\n    3    0.000    0.000    0.000    0.000 sre_compile.py:477(_get_iscased)\n    2    0.000    0.000    0.000    0.000 sre_compile.py:485(_get_literal_prefix)\n    1    0.000    0.000    0.000    0.000 sre_compile.py:516(_get_charset_prefix)\n    2    0.000    0.000    0.000    0.000 sre_compile.py:560(_compile_info)\n    4    0.000    0.000    0.000    0.000 sre_compile.py:619(isstring)\n    2    0.000    0.000    0.000    0.000 sre_compile.py:622(_code)\n    2    0.000    0.000    0.000    0.000 sre_compile.py:783(compile)\n  5/2    0.000    0.000    0.000    0.000 sre_compile.py:87(_compile)\n    5    0.000    0.000    0.000    0.000 sre_parse.py:112(__init__)\n   11    0.000    0.000    0.000    0.000 sre_parse.py:161(__len__)\n   26    0.000    0.000    0.000    0.000 sre_parse.py:165(__getitem__)\n    3    0.000    0.000    0.000    0.000 sre_parse.py:169(__setitem__)\n    5    0.000    0.000    0.000    0.000 sre_parse.py:173(append)\n  5/2    0.000    0.000    0.000    0.000 sre_parse.py:175(getwidth)\n    2    0.000    0.000    0.000    0.000 sre_parse.py:225(__init__)\n   10    0.000    0.000    0.000    0.000 sre_parse.py:234(__next)\n    5    0.000    0.000    0.000    0.000 sre_parse.py:250(match)\n    8    0.000    0.000    0.000    0.000 sre_parse.py:255(get)\n    5    0.000    0.000    0.000    0.000 sre_parse.py:287(tell)\n    4    0.000    0.000    0.000    0.000 sre_parse.py:356(_escape)\n    2    0.000    0.000    0.000    0.000 sre_parse.py:436(_parse_sub)\n    2    0.000    0.000    0.000    0.000 sre_parse.py:494(_parse)\n    2    0.000    0.000    0.000    0.000 sre_parse.py:76(__init__)\n    4    0.000    0.000    0.000    0.000 sre_parse.py:82(groups)\n    2    0.000    0.000    0.000    0.000 sre_parse.py:928(fix_flags)\n    2    0.000    0.000    0.000    0.000 sre_parse.py:944(parse)\n    1    0.000    0.000    0.177    0.177 test.py:6(keyboard)\n    2    0.000    0.000    0.000    0.000 {built-in method _sre.compile}\n 2016    0.000    0.000    0.000    0.000 {built-in method builtins.chr}\n    1    0.000    0.000    0.177    0.177 {built-in method builtins.exec}\n21256    0.002    0.000    0.002    0.000 {built-in method builtins.isinstance}\n30886    0.003    0.000    0.003    0.000 {built-in method builtins.len}\n   12    0.000    0.000    0.000    0.000 {built-in method builtins.min}\n    2    0.000    0.000    0.000    0.000 {built-in method builtins.ord}\n    2    0.000    0.000    0.000    0.000 {built-in method builtins.setattr}\n  205    0.004    0.000    0.006    0.000 {built-in method builtins.sorted}\n    1    0.000    0.000    0.000    0.000 {method '__exit__' of '_thread.lock' objects}\n21284    0.002    0.000    0.002    0.000 {method 'append' of 'list' objects}\n    1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}\n    2    0.000    0.000    0.000    0.000 {method 'extend' of 'list' objects}\n    3    0.000    0.000    0.000    0.000 {method 'find' of 'bytearray' objects}\n21217    0.003    0.000    0.003    0.000 {method 'get' of 'dict' objects}\n    3    0.000    0.000    0.000    0.000 {method 'items' of 'dict' objects}\n18301    0.002    0.000    0.002    0.000 {method 'lower' of 'str' objects}\n    2    0.000    0.000    0.000    0.000 {method 'split' of 're.Pattern' objects}\n    2    0.000    0.000    0.000    0.000 {method 'startswith' of 'str' objects}\n    1    0.000    0.000    0.000    0.000 {method 'update' of 'dict' objects}\n```\n\n### Mouse Benchmark\n\n\u003cdetails\u003e\n\n\u003csummary\u003eKeyWin\u003c/summary\u003e\n\n```python\nimport cProfile as profile\n\nfrom keywin import mouse, MouseCode\n\n\ndef keywin():\n\n    desired_position = (100, 100)\n\n    # Left + Right click at (100, 100)\n    mouse.send_events(\n        [*desired_position, 0, MouseCode.MOUSE_MOVE_ABSOLUTE | MouseCode.MOUSE_LEFT_CLICK],\n        [*desired_position, 0, MouseCode.MOUSE_MOVE_ABSOLUTE | MouseCode.MOUSE_RIGHT_CLICK]\n    )\n\n\nif __name__ == '__main__':\n    profile.run('keywin()')\n```\n\n\u003c/details\u003e\n\n```txt\n6 function calls in 0.004 seconds\n\nncalls  tottime  percall  cumtime  percall filename:lineno(function)\n    1    0.000    0.000    0.004    0.004 \u003cstring\u003e:1(\u003cmodule\u003e)\n    1    0.000    0.000    0.004    0.004 mouse.py:12(send_events)\n    1    0.000    0.000    0.004    0.004 test.py:9(keywin)\n    1    0.000    0.000    0.004    0.004 {built-in method builtins.exec}\n    1    0.004    0.004    0.004    0.004 {built-in method keywin.send_input.send_mouse_event}\n    1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}\n```\n\n\u003cdetails\u003e\n\n\u003csummary\u003e\u003ca href=\"https://github.com/boppreh/mouse\"\u003eboppreh/mouse\u003c/a\u003e\u003c/summary\u003e\n\n```python\nimport cProfile as profile\n\nfrom mouse import click, move, right_click\n\n\ndef mouse():\n\n    move(100, 100)\n    click()\n    right_click()\n\n\nif __name__ == '__main__':\n    profile.run('mouse()')\n```\n\n\u003c/details\u003e\n\n```txt\n35 function calls in 0.008 seconds\n\nncalls  tottime  percall  cumtime  percall filename:lineno(function)\n    1    0.000    0.000    0.008    0.008 \u003cstring\u003e:1(\u003cmodule\u003e)\n    1    0.000    0.000    0.001    0.001 __init__.py:101(right_click)\n    1    0.000    0.000    0.000    0.000 __init__.py:109(move)\n    1    0.000    0.000    0.000    0.000 __init__.py:199(get_position)\n    3    0.000    0.000    0.000    0.000 __init__.py:391(__getitem__)\n    2    0.000    0.000    0.008    0.004 __init__.py:91(click)\n    4    0.000    0.000    0.000    0.000 _winmouse.py:179(_translate_button)\n    2    0.004    0.002    0.004    0.002 _winmouse.py:185(press)\n    2    0.003    0.002    0.003    0.002 _winmouse.py:190(release)\n    1    0.000    0.000    0.000    0.000 _winmouse.py:199(move_to)\n    1    0.000    0.000    0.000    0.000 _winmouse.py:208(get_position)\n    1    0.000    0.000    0.008    0.008 test.py:6(mouse)\n    1    0.000    0.000    0.000    0.000 {built-in method _ctypes.byref}\n    1    0.000    0.000    0.008    0.008 {built-in method builtins.exec}\n    3    0.000    0.000    0.000    0.000 {built-in method builtins.isinstance}\n    3    0.000    0.000    0.000    0.000 {built-in method builtins.setattr}\n    1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}\n    3    0.000    0.000    0.000    0.000 {method 'startswith' of 'str' objects}\n```\n\n## Development\n\nYou can build `KeyWin` with the following.\n\n```bash\npdm install\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwinstxnhdw%2Fkeywin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwinstxnhdw%2Fkeywin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwinstxnhdw%2Fkeywin/lists"}