Madeira 触屏与物理手柄输入合并优先级仲裁算法完整指南【免费下载链接】MadeiraRun x86-64 Windows PC games on jailed iOS via FEX-Emu Wine DXMT项目地址: https://gitcode.com/GitHub_Trending/mad/Madeira在 iOS 上运行 Windows PC 游戏的Madeira基于 FEX-Emu Wine DXMT有一个绕不开的问题你的手柄可能同时是手指点按的触屏虚拟按键和真实蓝牙手柄。两种输入必须合并成一份 XInput 快照交给游戏而本文要详解的正是 Madeira 中这套触屏与物理手柄输入合并的优先级仲裁算法——它决定了按钮叠加、扳机取大、摇杆死区优先三条核心规则。为什么需要仲裁而不是简单覆盖想象一个场景你用左手手指按住触屏上的A 键同时右手握着 Xbox 手柄推左摇杆。此时游戏读到的必须是——A 键按下且左摇杆有偏转两者缺一不可。如果采用后到覆盖先到的简单策略触屏每 4ms 上报一次全零摇杆就会把物理摇杆的偏转不断抹掉。所以 Madeira 选择按输入类型逐字段仲裁每个字段按钮、扳机、摇杆都有自己的合并规则。整体管线从手指到 XInput 快照整条链路分三层各自有明确职责层级文件职责触摸采集TouchGamepad.swiftUIKit 独立跟踪每根手指产出触摸采样仲裁合并GamepadInput.swift250Hz 定时轮询物理手柄与触摸采样合并快照发布WiniosGamepad.c加锁发布快照给 Wine 侧 XInput 查询物理手柄 (250Hz 轮询) ──┐ ├─→ merge() 逐字段仲裁 ─→ winios_gamepad_set_state() ─→ Wine XInput 触屏虚拟按键 (事件驱动) ─┘关键点物理手柄由一个4ms 周期定时器1ms 调度余量即 250Hz统一采样触屏则是事件驱动——手指按下/移动时直接更新状态并触发一次sample()不需要额外轮询见 GamepadInput.swift 的定时器实现。三条仲裁规则逐条拆解合并逻辑集中在 GamepadSample.merge只有十几个字句但每一行都有讲究1️⃣ 按钮按位或ORresult.buttons 物理按钮 | 触摸按钮按钮是 16 位掩码每一位独立。只要任意一侧按下该位就置 1。这意味着你完全可以手指按 A、手柄按 X同时生效松开任何一侧都会立刻清除对应位不会卡键。2️⃣ 扳机LT/RT取较大值lt max(物理LT, 触摸LT)扳机是 0–255 的模拟量。取最大值意味着轻推物理扳机、重压触屏扳机时游戏收到的是较重的那个。这对手指点 LT 开火 手柄控制其他的组合非常友好。3️⃣ 摇杆死区判定 物理优先这是最精妙的部分。规则是物理摇杆超出标准 XInput 死区 → 物理摇杆赢否则只要触摸摇杆有偏转 → 触摸摇杆赢。对应代码TouchGamepad.swiftif hypot(物理lx, 物理ly) 7849 且 触摸左摇杆有偏转 → 采用触摸左摇杆 if hypot(物理rx, 物理ry) 8689 且 触摸右摇杆有偏转 → 采用触摸右摇杆两个阈值分别对应左、右摇杆的判定半径源自标准 XInput 死区约定。设计意图有三层物理大动作永远优先你在手柄上用力推摇杆哪怕手指还搭在触屏摇杆上也不会被干扰触摸兜底物理摇杆只是轻微晃动死区内时视为无效抖动让位给触屏静止的触摸摇杆不抹零手指离开触屏摇杆后它报零此时保留物理摇杆的微小偏转——注释里写得很直白A resting touch stick must not erase a small physical deflection静止的触摸摇杆不得抹掉物理摇杆的微小偏转。多个触摸按键之间如何合并手指之间也可能冲突布局编辑允许把两个触屏控件映射到同一个按钮或同时放两个虚拟摇杆。合并逻辑在 TouchGamepadState.sample按钮同样按位或松开其中一根手指另一个按钮保持按住扳机取最大值重复摇杆更强向量胜出——比较两个向量的长度平方谁偏转大用谁长度相等时按坐标字典序打破平局保证结果确定、可复现。每根手指由 UIKit 独立分配身份ownerUUID所以松开左手、保留右手这种细粒度释放天然成立来电、切后台、旋转屏幕等中断会触发releaseAll()逐指上报松开杜绝卡键。250Hz 主循环合并发生在哪一步真正的合并在 GamepadInput.sample 中完成流程是逐槽位0–3读取物理手柄状态仅槽位 01 号玩家会混入触摸输入——触摸虚拟手柄固定挂到 1 号玩家这是 docs/CONTROLLERS.md 明确的设计约定调用merge得到最终状态通过winios_gamepad_set_state发布C 层用互斥锁保护包序号只在内容变化时递增避免 Wine 侧无谓地处理相同帧。还有一个细节当应用失去焦点退到后台、弹出系统窗口槽位保持已连接但所有按键归零——setActive(false)会清空触摸按住状态并停掉定时器延迟回调不可能再复活一个已按下的键。相关开关速查表所有行为都可用环境变量开关写入Documents/madeira.cfg的env.NAME便于排查输入问题开关默认作用MADEIRA_XINPUT开设为0关闭整个手柄桥接MADEIRA_TOUCH_XINPUT开设为0只关触屏虚拟手柄物理手柄保留MADEIRA_PAD_EARLY_SLOT关设为1在游戏启动前就预约1 号玩家槽位解决只扫描一次 XInput 的老游戏看不到手柄的问题调试日志也分两条线[xinput] ml1920记录物理手柄连接[touch-xinput] ml1930记录触屏启用状态。总结这套仲裁算法好在哪逐字段合并而非整包覆盖手指和手柄可以各管一摊同时按下互不干扰死区优先 触摸兜底物理大动作绝不被触屏噪声打断微小抖动又被正确忽略确定性平局规则重复映射的摇杆永远得到同一结果方便回归测试项目用 check-touch-gamepad.py 之类的宿主测试持续验证这些仲裁路径生命周期即释放切后台、旋转、布局变更、来电任何一条路径都会主动上报松开用户永远不会遇到卡住的虚拟按键。对普通用户来说你只需要知道在 Madeira 里同时使用触屏和蓝牙手柄是完全支持的而且游戏收到的是一份经过精心仲裁的、干净的手柄信号。【免费下载链接】MadeiraRun x86-64 Windows PC games on jailed iOS via FEX-Emu Wine DXMT项目地址: https://gitcode.com/GitHub_Trending/mad/Madeira创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考