Metadata-Version: 2.4
Name: windows-trackpad-helper
Version: 1.0.1
Summary: A native C++ and Python helper for Windows to detect trackpad scrolling with accurate physical touch state.
Author: Tony
License: MIT
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: Microsoft :: Windows
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: demo
Requires-Dist: PyQt6>=6.5.0; extra == "demo"
Dynamic: license-file

# Windows Trackpad Helper

A small Windows library to help python applications track finger state on precision trackpads, that may not be exposed otherwise.  This allows, for example, PyQt apps to know if the trackpad is providing events after the user has lifted their fingers.  This is needed because even though the device and the drivers provide this info, it may not trickle down to a framework like Qt.

It interacts directly with the Windows API via `ctypes` (included in the Python standard library) and can hook **any native Win32 window handle (`HWND`)**. While the demo uses `PyQt6`, the library should work with any GUI framework that exposes an HWND handle (such as `PySide6`, `wxPython`, `Tkinter`, `GLFW`, `SDL2`, or raw Win32 windows).

---

## Touch Tracking Algorithm & Heuristics

The library hooks Windows Raw Input (`WM_INPUT` messages for `Digitizer/Touchpad` usage pages) to track physical touchpad contact:

1. **`is_finger_down(system_id: int = 0)`**: 
   * **When `system_id` is non-zero (specific device):** Returns `1` if raw touchpad contact is active, `0` if inactive (and is a precision touchpad), or `-1` if the state is unknown (e.g. non-precision device).
   * **When `system_id` is `0` (global/default):** Employs the time heuristic and returns:
     * **`1` (Touching):** Raw touchpad touch contact is active.
     * **`0` (Lifted):** Raw contact is inactive, but a touch occurred within the lift threshold window (default `10000` ms).
     * **`-1` (Unknown):** Raw touch is inactive and occurred outside the window (or never touched / non-precision device).
2. **`get_time_since_last_touch(system_id: int = 0)`**: Returns the precise milliseconds since the last physical contact occurred (`-1` if never touched).
3. **`set_lift_threshold(threshold_ms: int)`**: Configures the time window (in milliseconds) used by global queries to distinguish between `Lifted` (`0`) and `Unknown/Idle` (`-1`) states.
4. **`get_lift_threshold()`**: Returns the currently configured threshold in milliseconds.

Your application can query `is_finger_down()` inside its own scroll/wheel event loop to determine the physical finger state cleanly and simply.

---

## Quick Start (Local Run)

To compile the DLL and run the dumper demo locally:

```cmd
# 1. Compile the C++ DLL locally (requires MSVC or gcc & CMake)
build.bat

# 2. Install package locally in editable mode with optional demo dependencies
py -m pip install -e ".[demo]"

# 3. Run the dumper demo
python -m windows_trackpad_helper.demo_pyqt6
```

---

## Direct Integration Example (Generic Win32)

```python
from windows_trackpad_helper import TrackpadHelper

# 1. Initialize the helper
trackpad = TrackpadHelper()

# Explicitly configure the lift threshold window to 10 seconds
trackpad.set_lift_threshold(10000)

# 2. Hook your window using its native HWND handle (from Tkinter, wx, GLFW, etc.)
hwnd = my_window.window_id()  # Replace with your framework's HWND getter
trackpad.init_hwnd(hwnd)

# ... inside your scroll/wheel event handler:
finger_state = trackpad.is_finger_down()

if finger_state == 1:
    state_str = "Touching"
elif finger_state == 0:
    state_str = "Lifted"
else:
    state_str = "Unknown"

print(f"Physical Touch State: {state_str}")

# 3. Cleanly unhook on window close
trackpad.shutdown()
```
