Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions entry/src/main/ets/pages/SettingsPageV2.ets
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ struct SettingsPageV2 {
@State enableStartKeyMenu: boolean = true;
@State enableKeyInterceptor: boolean = false;
@State keyInterceptorAvailable: boolean = false; // 按键注入 API 是否可用(API 13+ 才支持反劫持)
@State gcButtonHomeMapping: string = 'guide'; // 2313键(Home键)动作映射
@State gcButtonHomeMapping: string = 'guide'; // 2313键(Guide/Mode 兼容键)动作映射
@State usbDriverEnabled: boolean = false; // 默认关闭 USB 手柄驱动
@State forceUsbDriverOnly: boolean = false; // 强制纯 USB 驱动模式
@State ddkHighSpeedPolling: boolean = false; // USB 驱动高速轮询
Expand Down Expand Up @@ -631,7 +631,7 @@ struct SettingsPageV2 {
}

/**
* 获取 Home 键动作描述
* 获取 Guide/Mode 键动作描述
*/
private getHomeButtonActionDescription(): string {
switch (this.gcButtonHomeMapping) {
Expand Down Expand Up @@ -1496,7 +1496,7 @@ struct SettingsPageV2 {
},
{
title: '按键反劫持',
subtitle: '防止蓝牙手柄 Home 键退出 App(需系统级签名,否则可能无效)',
subtitle: '拦截部分蓝牙手柄 Guide/Mode 键退出 App(需系统级签名,否则可能无效)',
type: 'toggle',
value: this.enableKeyInterceptor,
visible: this.keyInterceptorAvailable,
Expand All @@ -1506,7 +1506,7 @@ struct SettingsPageV2 {
}
},
{
title: 'Home 键动作',
title: 'Guide/Mode 键动作',
subtitle: this.getHomeButtonActionDescription(),
type: 'select',
value: this.gcButtonHomeMapping,
Expand Down Expand Up @@ -3303,11 +3303,11 @@ struct SettingsPageV2 {
}

/**
* 显示 Home 键动作选择器
* 显示 Guide/Mode 键动作选择器
*/
private showHomeButtonActionPicker(): void {
const options: PickerOption[] = [
{ title: '发送 Guide 键', subtitle: 'Xbox Guide / PS Home', value: 'guide' } as PickerOption,
{ title: '发送 Guide 键', subtitle: 'Xbox Guide / PS Home / Mode', value: 'guide' } as PickerOption,
{ title: '发送 Select 键', subtitle: '返回/选择键', value: 'select' } as PickerOption,
{ title: '切换鼠标模式', subtitle: '切换触摸屏鼠标模拟', value: 'toggle_mouse' } as PickerOption,
{ title: '切换性能图层', subtitle: '显示/隐藏性能监控', value: 'toggle_perf' } as PickerOption,
Expand All @@ -3316,8 +3316,8 @@ struct SettingsPageV2 {
];

const config: PickerConfig = {
title: 'Home 键动作',
subtitle: '设置蓝牙/系统手柄 Home 键的动作',
title: 'Guide/Mode 键动作',
subtitle: '设置 2313 兼容键的动作;部分手柄会把 Home/Guide/Mode 映射到这里',
selectedValue: this.gcButtonHomeMapping,
options: options,
onSelect: (option: PickerOption) => {
Expand Down
8 changes: 5 additions & 3 deletions entry/src/main/ets/service/input/GameControllerService.ets
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,9 @@ export class AxisType {
}

/**
* 按键码常量 (与 Game Controller Kit 一致)
* 按键码常量。
* 2301-2312/2314/2315 来自 GCK;2313 是 InputKit 兼容键,
* 部分手柄会用它上报 Guide/Home/Mode。
*/
export class ButtonCode {
static readonly BUTTON_A = 2301;
Expand All @@ -110,8 +112,8 @@ export class ButtonCode {
static readonly RIGHT_SHOULDER = 2308;
static readonly LEFT_TRIGGER = 2309;
static readonly RIGHT_TRIGGER = 2310;
static readonly BUTTON_HOME = 2311;
static readonly BUTTON_MENU = 2312;
static readonly BUTTON_HOME = 2311; // GCK ButtonHome,实测常对应 Select/Back
static readonly BUTTON_MENU = 2312; // GCK ButtonMenu,Start/Menu
static readonly LEFT_THUMBSTICK = 2314;
static readonly RIGHT_THUMBSTICK = 2315;
static readonly DPAD_UP = 2012;
Expand Down
53 changes: 38 additions & 15 deletions entry/src/main/ets/service/input/GamepadManager.ets
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ export class GamepadManager implements UsbDriverListener {

/**
* 加载 Game Controller Kit 按键映射设置
* 根据用户设置动态调整 2313 按键的动作
* 根据用户设置动态调整 2313 兼容键的动作
*/
private async loadGCButtonMappingSettings(): Promise<void> {
try {
Expand All @@ -276,15 +276,15 @@ export class GamepadManager implements UsbDriverListener {
}

/**
* 设置 2313 按键的自定义动作回调
* 设置 2313 兼容键的自定义动作回调
* @param callback 回调接口,包含各种自定义动作的处理函数
*/
setGCButton2313ActionCallback(callback: GCButton2313ActionCallback | null): void {
this.gcButton2313ActionCallback = callback;
}

/**
* 更新 GC Kit 2313 按键动作
* 更新 2313 兼容键动作
* @param action 动作类型
*/
updateGCButton2313Action(action: GCButton2313Action): void {
Expand All @@ -293,7 +293,7 @@ export class GamepadManager implements UsbDriverListener {
// 对于按键映射类型,更新 gcButtonMap
switch (action) {
case 'guide':
gcButtonMap.set(2313, MoonlightButton.SPECIAL); // 映射为 Guide/Home
gcButtonMap.set(2313, MoonlightButton.SPECIAL); // 映射为 Guide/Home/Mode
break;
case 'select':
gcButtonMap.set(2313, MoonlightButton.BACK); // 映射为 Select/Back
Expand All @@ -306,7 +306,7 @@ export class GamepadManager implements UsbDriverListener {
}

/**
* 处理 2313 自定义动作
* 处理 2313 兼容键自定义动作
* @returns true 如果动作已处理(不需要发送按键),false 如果需要正常发送按键
*/
private handleGCButton2313Action(): boolean {
Expand Down Expand Up @@ -1155,6 +1155,19 @@ export class GamepadManager implements UsbDriverListener {
console.info(`[${logPrefix}] 释放设备槽位: Key=${deviceKey}, Slot=${slot}`);
}
}

/**
* 释放已知槽位,并移除对应映射。
* 用于 GCK 多 HID 接口共槽场景:断开流程会先清理 deviceId 映射,
* 不能再依赖 releaseSlot() 从 map 中回查 slot。
*/
private releaseKnownSlot(deviceKey: string, slot: number, keyToSlotMap: Map<string, number>, logPrefix: string): void {
if (slot >= 0 && slot < GamepadManager.MAX_CONTROLLERS) {
this.slotOccupied[slot] = false;
console.info(`[${logPrefix}] 释放设备槽位: Key=${deviceKey}, Slot=${slot}`);
}
keyToSlotMap.delete(deviceKey);
}

/**
* 判断是否为特殊按键码(2311-2315)
Expand Down Expand Up @@ -1294,8 +1307,16 @@ export class GamepadManager implements UsbDriverListener {
console.info('[GAMEPAD] 检测到手柄连接,恢复输入监听');
}
} else {
// 获取槽位(在清理之前)
const slot = this.getSlotForGCDevice(deviceId);
// 获取槽位(在清理之前)。未知 deviceId 的 OFFLINE 事件直接忽略,
// 避免 getSlotForGCDevice() 的默认 0 误释放一号槽。
const slot = this.gcDeviceIdToSlot.get(deviceId);
if (slot === undefined) {
this.gcDeviceStates.delete(deviceId);
this.gcDeviceInfoCache.delete(deviceId);
this.gcDpadAxisActive.delete(deviceId);
console.info(`[GAMEPAD-GC] 收到未注册设备断开事件,忽略: deviceId=${deviceId}`);
return;
}

// 清理物理地址映射
const addrEntries = Array.from(this.gcPhysicalAddressToDeviceId.entries());
Expand All @@ -1321,7 +1342,8 @@ export class GamepadManager implements UsbDriverListener {
}
}

// 清理状态
// 清理当前 GCK deviceId 的状态和映射。副本设备共享主设备槽位时,
// 只移除副本映射;最后一个占用者断开时再释放 slotOccupied。
this.gcDeviceStates.delete(deviceId);
this.gcDeviceInfoCache.delete(deviceId);
this.gcDpadAxisActive.delete(deviceId);
Expand All @@ -1333,8 +1355,8 @@ export class GamepadManager implements UsbDriverListener {
return;
}

// 释放槽位
this.releaseSlotForGCDevice(deviceId);
// 释放槽位:此时 deviceId 映射已移除,使用断开前保存的 slot 显式释放。
this.releaseKnownSlot(deviceId, slot, this.gcDeviceIdToSlot, 'GAMEPAD-GC');
// 通知监听器
this.notifyGamepadDisconnectedByKey(`gc_${deviceId}`, slot);

Expand Down Expand Up @@ -1573,8 +1595,8 @@ export class GamepadManager implements UsbDriverListener {
* 处理按键事件 (InputKit KeyEvent)
*
* GCK 模式下:
* GCK 原生回调已覆盖所有手柄按键(除 2313)
* 此处仅处理 2313 和 Start 长按,其余静默丢弃。
* GCK 原生回调已覆盖大部分手柄按键
* 此处保留 2313(Guide/Mode 兼容键) 和 Start 长按兜底,其余静默丢弃。
*
* 传统模式下:
* 所有 KeyEvent 通过 buttonMap 映射并更新设备状态。
Expand All @@ -1585,13 +1607,14 @@ export class GamepadManager implements UsbDriverListener {

GamepadManager.DEBUG && console.info(`[GAMEPAD-KEY] handleKeyEvent: deviceId=${deviceId}, keyCode=${keyCode}, isPressed=${isPressed}`);

// 特殊处理 2313 按键(可自定义动作)- 无论是否使用 GC Kit 都处理
// 特殊处理 2313:部分手柄将 Guide/Mode/Home 键只通过 KeyEvent 上报。
// 无论是否使用 GC Kit 都处理,避免官方 GCK 通道识别不到时按键丢失。
if (keyCode === 2313) {
if (isPressed && this.handleGCButton2313Action()) {
return; // 自定义动作已处理(toggle_mouse, toggle_perf, show_menu, none)
}
// 'guide'/'select' 映射:通过映射表发送按钮状态
// 某些手柄的 Home/Guide 键仅通过 KeyEvent 到达(不经过 GCK Button 回调),
// 某些手柄的 Guide/Mode/Home 键仅通过 KeyEvent 到达(不经过 GCK Button 回调),
// 如果依赖 GCK Button 回调处理会导致按键丢失
if (this.useGameControllerKit) {
const mappedButton = gcButtonMap.get(2313);
Expand All @@ -1615,7 +1638,7 @@ export class GamepadManager implements UsbDriverListener {
}

// GCK 模式:KeyEvent 通道完全冗余
// GCK 原生回调(Axis + Button)已覆盖所有手柄按键(除 2313,已在上方处理)
// GCK 原生回调(Axis + Button)已覆盖大部分手柄按键;2313 兼容键已在上方处理
// 所有 KeyEvent 在此静默丢弃,避免与 GCK 回调双通道竞争导致双按。
if (this.useGameControllerKit) {
return;
Expand Down
20 changes: 11 additions & 9 deletions entry/src/main/ets/service/input/GamepadTypes.ets
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ export class MoonlightButton {
}

/**
* 2313 按键动作类型
* 2313 兼容按键动作类型
* 部分手柄会把 Guide/Home/Mode 键通过 InputKit KeyEvent 2313 上报,
* 不一定经过 GCK Button 回调,因此这里保留单独映射。
* 'guide' - 发送 Guide/Home 键
* 'select' - 发送 Select/Back 键
* 'toggle_mouse' - 切换鼠标模式
Expand Down Expand Up @@ -215,10 +217,10 @@ export const buttonMap = new Map<number, number>([
[109, MoonlightButton.BACK], // KEYCODE_BUTTON_SELECT
// Special
[110, MoonlightButton.SPECIAL], // KEYCODE_BUTTON_MODE
// GC Kit 按键码(某些蓝牙手柄可能通过 KeyEvent 发送这些码)
[2311, MoonlightButton.BACK], // KEYCODE_BUTTON_SELECT - Select
[2312, MoonlightButton.START], // KEYCODE_BUTTON_START - Start
[2313, MoonlightButton.SPECIAL], // KEYCODE_BUTTON_MODE - Home (可自定义)
// GC Kit/厂商兼容按键码(某些蓝牙手柄可能通过 KeyEvent 发送这些码)
[2311, MoonlightButton.BACK], // GCK ButtonHome,实测常对应 Select/Back
[2312, MoonlightButton.START], // GCK ButtonMenu,Start/Menu
[2313, MoonlightButton.SPECIAL], // InputKit Guide/Mode/Home 兼容键(可自定义)
[2314, MoonlightButton.LS], // KEYCODE_BUTTON_THUMBL - 左摇杆按下
[2315, MoonlightButton.RS], // KEYCODE_BUTTON_THUMBR - 右摇杆按下
]);
Expand All @@ -237,10 +239,10 @@ export const gcButtonMap = new Map<number, number>([
// Thumbstick buttons
[2314, MoonlightButton.LS], // LEFT_THUMBSTICK
[2315, MoonlightButton.RS], // RIGHT_THUMBSTICK
// Menu buttons - 根据测试调整
[2311, MoonlightButton.BACK], // 实测为 Select/Back
[2312, MoonlightButton.START], // BUTTON_MENU (Start)
[2313, MoonlightButton.SPECIAL],// 默认为 Guide/Home,可用户自定义
// Menu buttons - 根据 GCK 命名和实测兼容性调整
[2311, MoonlightButton.BACK], // GCK ButtonHome,实测常对应 Select/Back
[2312, MoonlightButton.START], // GCK ButtonMenu,Start/Menu
[2313, MoonlightButton.SPECIAL],// Guide/Mode/Home 兼容键,可用户自定义
// D-Pad
[2012, MoonlightButton.UP], // DPAD_UP
[2013, MoonlightButton.DOWN], // DPAD_DOWN
Expand Down
4 changes: 2 additions & 2 deletions nativelib/src/main/cpp/game_controller_native.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ typedef struct {
#define GC_KEYCODE_RIGHT_SHOULDER 2308
#define GC_KEYCODE_LEFT_TRIGGER 2309
#define GC_KEYCODE_RIGHT_TRIGGER 2310
#define GC_KEYCODE_BUTTON_HOME 2311 // GCK API 名为 "ButtonHome",实测对应 Select/Back
#define GC_KEYCODE_BUTTON_MENU 2312
#define GC_KEYCODE_BUTTON_HOME 2311 // GCK ButtonHome,实测常对应 Select/Back
#define GC_KEYCODE_BUTTON_MENU 2312 // GCK ButtonMenu,Start/Menu
#define GC_KEYCODE_LEFT_THUMBSTICK 2314
#define GC_KEYCODE_RIGHT_THUMBSTICK 2315
#define GC_KEYCODE_DPAD_UP 2012
Expand Down
Loading