Metadata-Version: 2.4
Name: scanput
Version: 0.2.1
Summary: Windows SendInput wrapper for keyboard and mouse simulation
Author-email: Sam Howle <samhowle@protonmail.com>
License: MIT
Project-URL: Repository, https://github.com/sam-howle/scanput
Keywords: windows,sendinput,keyboard,mouse,automation,scancode,input
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: Microsoft :: Windows
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.7
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# scanput
Lightweight Windows Keyboard &amp; Mouse input library using hardware scan codes.

Unlike existing libraries like `pyautogui` that use virtual key presses, `scanput` uses hardware scan codes making them indistinguishable* from physical hardware keypresses to most applications.

While the `AutoHotKey` Python library does offer hardware scan code inputs, the library requires having `AutoHotKey` installed and pointing your script to the executable path. It then spawns a new `AutoHotKey.exe` subprocess *every* time you perform an action - not exactly lightweight. 

`scanput` is designed to be lightweight and requires zero third-party dependencies.

<sub>*Technically, Windows sets the `LLMHF_INJECTED` flag on synthesized input events to indicate they originated from `SendInput` rather than a physical device. It is rare for applications to check this flag, but it is possible. Full reference [here](https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-kbdllhookstruct)  </sub>

## Installation
```
pip install scanput
```
Alternatively, you could save `scanput.py` to your project's root directory and `import scanput`. It's literally a single file that needs importing:
```
cd my-project
curl https://raw.githubusercontent.com/sam-howle/scanput/refs/heads/main/scanput.py -o scanput.py
```

## Usage
A more detailed guide can be found in `demo.py`.

### Key Presses
All button pressing & mouse clicking functions require both a press and a release. Pressing a key without releasing it will result in the key being held down indefinately. 
It is recommended to add a short timing delay between presses. For example:
```python
HOLD = 0.08
key_down("k")
time.sleep(HOLD)
key_up("k")
```
The above example presses the `k` key and holds it for 0.08 seconds before releasing.

For a list of key aliases, run the following line:
```python
print(list(KEY_ALIASES.keys()))
```
Additionally, `key_down()` and `key_up()` accept either a key name string or a raw Windows Virtual Key code as an integer, allowing direct specification of any VK code not covered by the named aliases. The Windows Virtual Key code is converted to a hardware scan code before input is performed. A full list of Windows VK integers can be found at the following link: 

[https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes](https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes)

### Mouse Clicks
Similarly, mouse clicks also take a press & release approach:
```python
HOLD = 0.08
print("Left click.")
left_down()
time.sleep(HOLD)
left_up()

print("Right click.")
right_down()
time.sleep(HOLD)
right_up()
```

### Mouse Movement
There are only two mouse movement functions: `get_cursor_position()` and `set_cursor_position(x,y)`. The following snippet obtains the current mouse position, and offsets it by an `(x,y)` value of `(300,150)` pixels:
```python
x, y = get_cursor_position()
print(f"Current mouse position: ({x}, {y})")

dest_x, dest_y = x + 300, y - 150
print(f"Teleporting mouse to ({dest_x}, {dest_y})")
set_cursor_position(dest_x, dest_y)
```
Please note that the Y axis for monitor pixel coordinates starts with `0` being the top of the screen, rather than the bottom. That means the coordinate `(0,0)` is the top-left monitor pixel. While unintuitive, it is consistent with how monitor pixel coordinates have always worked starting with early CRTs. 

### Screen Resolution
The `get_screen_resolution()` function returns the primary monitor's resolution as `(width, height)`, which is handy for clamping cursor coordinates or calculating positions relative to the screen size:
```python
width, height = get_screen_resolution()
print(f"Screen resolution: {width}x{height}")

center_x, center_y = width // 2, height // 2
print(f"Teleporting mouse to center of screen: ({center_x}, {center_y})")
set_cursor_position(center_x, center_y)
```

### Toggle Key States
The `get_toggle_key_state()` function can be used to let your script know the current state of the `capslock`, `numlock`, and `scrolllock` keys:
```python
get_toggle_key_state("capslock")   # 1 or 0
get_toggle_key_state("numlock")    # 1 or 0
get_toggle_key_state("scrolllock") # 1 or 0
get_toggle_key_state(0x14)         # same as capslock by VK int
```
Be aware that this function is only meaningful for toggle keys. Passing modifier keys like `shift` or `alt` will always return `0`.

And that's really all there is to it. 

You can press buttons. And you can release buttons. You can move the mouse. And you can find out where the mouse is. You can click mice. And you can release mouse clicks. 
The world is your oyster. Crack it open and slurp out its innards.
