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(仅开发或源码构建时需要)
从源码构建
# 克隆仓库
git clone https://github.com/WindLX/fly_stick.git
cd fly_stick
# 安装构建依赖
pip install maturin
# 构建并安装到当前 Python 环境
maturin develop
使用 uv(推荐)
uv sync
uv run maturin develop
快速开始
单设备监控
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())
设备池异步监控
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())
设备池非阻塞读取
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:
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与aliasbuttons: 按钮配置列表,包含code与aliashats: 帽子开关配置列表,包含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)DeviceDescriptionDeviceDescription.from_toml(toml_file)DeviceDescription.build_state()DeviceItemDeviceButtonMode.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.tomldevices/Thrustmaster/twcs.tomldevices/Thrustmaster/t16000m.tomldevices/Thrustmaster/tca_qeng.tomldevices/Thrustmaster/twcs_with_tfrp.tomldevices/Microsoft X-Box 360/pad.tomldevices/Microsoft X-Box 360/series_sx.toml
设备映射图
项目提供了设备按键映射图:
figures/Thrustmaster_TA320_Copilot.drawio.pngfigures/Thrustmaster_TWCS_Throttle.drawio.pngfigures/Thrustmaster T.16000M.drawio.pngfigures/Thrustmaster T.Flight Rudder Pedals.drawio.pngfigures/Thrustmaster TCA Q-Eng 1&2.drawio.png
开发
项目结构
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
构建与测试
# Rust 单元测试
cargo test
# Python 测试(如项目中提供测试用例)
pytest
# 构建发布 wheel
maturin build --release
性能特性
- 低延迟: Rust 核心实现,输入处理延迟低
- 按钮模式控制: 支持 Trigger/Hold 两种按钮语义
- 非阻塞读取: 可使用
fetch_nowait()做高频轮询 - 内存安全: Rust 提供内存安全保障
TODO
-
alias支持 - 按钮触发逻辑优化
许可证
本项目采用 MIT 许可证,详见 LICENSE。
贡献
欢迎提交 Issue 和 Pull Request。
建议在提交前完成:
- 代码风格自检
- 必要测试补充
- 文档同步更新
作者
- windlx - 初始开发 - https://github.com/WindLX
注意:该库当前仅支持 Linux(evdev)。未来可按需扩展到其他平台。
Release files for fly-stick 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fly_stick-0.1.0.tar.gz | 22.5 MB | Details |
Built distributions (wheels)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fly_stick-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | CPython 3.9 | abi3 | Linux glibc 2.17+ x86-64 | Details |
| fly_stick-0.1.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl | CPython 3.9 | abi3 | Linux glibc 2.17+ ARM64 | Details |
Total release size: 24.1 MB
Release files / fly_stick-0.1.0.tar.gz
| Download URL | fly_stick-0.1.0.tar.gz |
|---|---|
| Size | 22.5 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d55bbac86b39221f49e85339fbe10db19324710880faf8f6dc53358c821afaf9
|
|
BLAKE2b-256 checksum How to use checksums |
562ee628e7264a27e18d420f7a418db4a9be8ded72661e28a6547885be9877b7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
maturin/1.14.1
|
Release files / fly_stick-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | fly_stick-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 833.8 kB |
| Tags | CPython 3.9 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
a8ab11242f8ca5102f7072458bd55191d5ab26ea4a666e1a16a2f82f0efa56f5
|
|
BLAKE2b-256 checksum How to use checksums |
ef31a474b8aae252d329e588914a39c05d778a987a91051681554132550e4527
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
maturin/1.14.1
|
Release files / fly_stick-0.1.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | fly_stick-0.1.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 836.3 kB |
| Tags | CPython 3.9 Linux glibc 2.17+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
5d28ba8d797b970f944d03153715f4812f1f4c71dbc284eaf6c5a0c8622bde20
|
|
BLAKE2b-256 checksum How to use checksums |
bcec52691c7c7ca3ab332b9bc6b118d1959f8ba5f7baff9493a0c3f09f171c08
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
maturin/1.14.1
|