Metadata-Version: 2.4
Name: fly-stick
Version: 0.1.0
Requires-Dist: toml>=0.10.2
License-File: LICENSE
Summary: A Python library for game controller input handling
Home-Page: https://github.com/WindLX/fly_stick
Author-email: windlx <1418043337@qq.com>
Requires-Python: >=3.10
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM

# fly-stick

一个基于 Rust 和 PyO3 构建的高性能 Python 库，专门用于处理游戏控制器（操纵杆/手柄）输入设备。该库使用 Linux evdev 接口提供低延迟的设备监控和状态读取能力，特别适用于飞行模拟器等实时输入场景。

## 特性

- 🎮 **多设备支持** - 同时监控多个游戏控制器设备
- ⚡ **高性能核心** - Rust 底层实现，提供毫秒级响应
- 🔄 **异步/非阻塞双模式** - 支持 `await fetch()` 与 `fetch_nowait()`
- 📊 **设备池管理** - 统一管理多设备，读取逻辑设备状态
- 🛠️ **TOML 配置** - 基于 TOML 的设备描述文件
- 🐍 **完整 Python API** - 直接从 `fly_stick` 包导入核心类型
- 🎯 **按钮模式控制** - 支持 `trigger` 与 `hold` 两种按钮处理模式
- 📈 **实时状态监控** - 轴、按钮、帽子开关状态实时更新

## 安装

### 系统要求

- Linux 系统（依赖 evdev）
- Python 3.10+
- Rust（仅开发或源码构建时需要）

### 从源码构建

```bash
# 克隆仓库
git clone https://github.com/WindLX/fly_stick.git
cd fly_stick

# 安装构建依赖
pip install maturin

# 构建并安装到当前 Python 环境
maturin develop
```

### 使用 uv（推荐）

```bash
uv sync
uv run maturin develop
```

## 快速开始

### 单设备监控

```python
import asyncio
import fly_stick


async def monitor_single_device():
    devices = fly_stick.fetch_connected_joysticks()
    if not devices:
        print("未找到设备")
        return

    dev = devices[0]
    joystick = fly_stick.PyJoystick(dev.path)

    print(f"监控设备: {dev.name} @ {dev.path}")

    while True:
        try:
            state = joystick.get_state()
            print(state.to_dict())
            await asyncio.sleep(0.01)
        except KeyboardInterrupt:
            break


asyncio.run(monitor_single_device())
```

### 设备池异步监控

```python
import asyncio
from fly_stick import PyDevicePool, DeviceDescription, DeviceButtonMode


async def monitor_device_pool():
    pool = PyDevicePool(
        device_descs={
            "ta320": DeviceDescription.from_toml("devices/Thrustmaster/ta320.toml"),
            "twcs": DeviceDescription.from_toml("devices/Thrustmaster/twcs.toml"),
        },
        debounce_seconds=0.1,
        btn_mode=DeviceButtonMode.hold(),
    )

    # 使用设备池前先初始化监控
    await pool.reset()

    print("开始监控设备池...")

    while True:
        try:
            states = await pool.fetch(timeout_seconds=1.0)
            for device_name, state in states.items():
                print(f"{device_name}: {state.to_dict()}")
        except KeyboardInterrupt:
            print("停止监控")
            await pool.stop()
            break


asyncio.run(monitor_device_pool())
```

### 设备池非阻塞读取

```python
import asyncio
from fly_stick import PyDevicePool, DeviceDescription


async def monitor_nowait():
    pool = PyDevicePool(
        device_descs={
            "ta320": DeviceDescription.from_toml("devices/Thrustmaster/ta320.toml"),
            "twcs": DeviceDescription.from_toml("devices/Thrustmaster/twcs.toml"),
        },
        debounce_seconds=0.1,
    )

    await pool.reset()

    try:
        while True:
            states = pool.fetch_nowait()
            for name, state in states.items():
                print(f"{name}: 轴={state.axes}, 按钮={state.buttons}, 帽子={state.hats}")
            await asyncio.sleep(0.01)
    except KeyboardInterrupt:
        await pool.stop()


asyncio.run(monitor_nowait())
```

## 设备配置

DevicePool 的设备配置使用 TOML 格式描述，此描述文件约束了哪些按键才会被监控和记录数据。例如 `devices/Thrustmaster/ta320.toml`：

```toml
device_name = "Thrustmaster T.A320 Copilot"
author = "WindLX"
created = "2025-01-14"
description = "Thrustmaster T.A320 Copilot Device Description File"

[[axes]]
code = 0
alias = "ABS_X"

[[axes]]
code = 1
alias = "ABS_Y"

[[buttons]]
code = 288
alias = "BTN_TRIGGER"

[[hats]]
code = 16
alias = "ABS_HAT0X"
```

### 配置文件说明

- `device_name`: 设备显示名称（用于匹配系统设备名）
- `author`: 配置文件作者（可选）
- `created`: 创建日期（可选）
- `description`: 设备描述（可选）
- `axes`: 轴配置列表，包含 `code` 与 `alias`
- `buttons`: 按钮配置列表，包含 `code` 与 `alias`
- `hats`: 帽子开关配置列表，包含 `code` 与 `alias`

## API 参考

### 核心函数

- `fetch_connected_joysticks()` - 获取当前连接的输入设备列表

### 核心类

- `PyJoystick(device_path)`
- `PyJoystick.get_state()`
- `PyDevicePool(device_descs, debounce_seconds=0.1, btn_mode=DeviceButtonMode.hold())`
- `PyDevicePool.reset()`
- `PyDevicePool.fetch(timeout_seconds=None)`
- `PyDevicePool.fetch_nowait()`
- `PyDevicePool.stop()`
- `PyDevicePool.devices`（属性）
- `PyDevicePool.debounce_time`（属性）
- `PyDevicePool.button_mode`（属性，可读写）

### 数据结构

- `JoystickInfo`（`path`, `name`）
- `JoystickState`（`axes`, `buttons`, `hats`）
- `JoystickState.to_dict()`
- `JoystickState.to_alias_dict(desc)`
- `JoystickState.get_alias_axes(desc)`
- `JoystickState.get_alias_buttons(desc)`
- `JoystickState.get_alias_hats(desc)`
- `DeviceDescription`
- `DeviceDescription.from_toml(toml_file)`
- `DeviceDescription.build_state()`
- `DeviceItem`
- `DeviceButtonMode.trigger()`
- `DeviceButtonMode.hold()`

> 注意：使用 `PyDevicePool.fetch()` 或 `fetch_nowait()` 前，需先调用 `await reset()`。

## 示例

项目包含多个示例文件：

- `examples/single_device.py` - 单设备异步监控
- `examples/multi_device.py` - 多设备异步监控
- `examples/device_pool.py` - 设备池非阻塞读取
- `examples/device_pool_block.py` - 设备池阻塞读取
- `examples/alias.py` - 按别名读取轴状态
- `examples/btn_mode.py` - 按钮触发模式示例

## 支持的设备

目前仓库内提供了以下设备描述文件：

- `devices/Thrustmaster/ta320.toml`
- `devices/Thrustmaster/twcs.toml`
- `devices/Thrustmaster/t16000m.toml`
- `devices/Thrustmaster/tca_qeng.toml`
- `devices/Thrustmaster/twcs_with_tfrp.toml`
- `devices/Microsoft X-Box 360/pad.toml`
- `devices/Microsoft X-Box 360/series_sx.toml`

### 设备映射图

项目提供了设备按键映射图：

- `figures/Thrustmaster_TA320_Copilot.drawio.png`
- `figures/Thrustmaster_TWCS_Throttle.drawio.png`
- `figures/Thrustmaster T.16000M.drawio.png`
- `figures/Thrustmaster T.Flight Rudder Pedals.drawio.png`
- `figures/Thrustmaster TCA Q-Eng 1&2.drawio.png`

## 开发

### 项目结构

```text
fly_stick/
├── src/
│   ├── lib.rs
│   ├── utils.rs
│   ├── inner/
│   │   ├── description.rs
│   │   ├── device_pool.rs
│   │   ├── joystick.rs
│   │   └── mod.rs
│   ├── wrapper/
│   │   ├── device_pool_wrapper.rs
│   │   ├── joystick_wrapper.rs
│   │   └── mod.rs
│   └── fly_stick/
│       ├── __init__.py
│       └── _core.pyi
├── examples/
├── devices/
├── figures/
├── Cargo.toml
└── pyproject.toml
```

### 构建与测试

```bash
# Rust 单元测试
cargo test

# Python 测试（如项目中提供测试用例）
pytest

# 构建发布 wheel
maturin build --release
```

## 性能特性

- **低延迟**: Rust 核心实现，输入处理延迟低
- **按钮模式控制**: 支持 Trigger/Hold 两种按钮语义
- **非阻塞读取**: 可使用 `fetch_nowait()` 做高频轮询
- **内存安全**: Rust 提供内存安全保障

## TODO

- [x] `alias` 支持
- [x] 按钮触发逻辑优化

## 许可证

本项目采用 MIT 许可证，详见 `LICENSE`。

## 贡献

欢迎提交 Issue 和 Pull Request。

建议在提交前完成：

1. 代码风格自检
2. 必要测试补充
3. 文档同步更新

## 作者

- **windlx** - 初始开发 - https://github.com/WindLX

---

*注意：该库当前仅支持 Linux（evdev）。未来可按需扩展到其他平台。*

