From 5243731cda201116f2c3a5576c6ee0047c3e6b39 Mon Sep 17 00:00:00 2001 From: honjow Date: Sun, 1 Mar 2026 22:38:56 +0800 Subject: [PATCH 1/3] add extra button support for xinput mode --- src/hhd/device/claw/base.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/hhd/device/claw/base.py b/src/hhd/device/claw/base.py index f61a03a7..58a3b5c6 100644 --- a/src/hhd/device/claw/base.py +++ b/src/hhd/device/claw/base.py @@ -33,6 +33,9 @@ CLAW_SET_M1M2 = lambda a, btn: bytes( [0x0F, 0x00, 0x00, 0x3C, 0x21, 0x01, *a[btn], 0x05, 0x01, 0x00, 0x00, 0x12, 0x00] ) +CLAW_SET_XINPUT_M1M2 = lambda a, btn, b: bytes( + [0x0F, 0x00, 0x00, 0x3C, 0x21, 0x01, *a[btn], 0x01, b] +) CLAW_SYNC_ROM = bytes([0x0F, 0x00, 0x00, 0x3C, 0x22]) CLAW_SET_XINPUT = bytes([0x0F, 0x00, 0x00, 0x3C, 0x24, 0x01, 0x00]) @@ -61,6 +64,15 @@ BACK_BUTTON_DELAY = 0.1 BUTTON_MIN_DELAY = 0.13 +XINPUT_MAP = { + "btn_a": 0x09, + "btn_b": 0x0A, + "key_inst": 0x7A, + "key_del": 0x7D, + "key_1": 0x40, + "key_esc": 0x32, +} + # 0211 ADDR_0163 = { "rgb": [0x01, 0xFA], @@ -74,6 +86,8 @@ "rgb": [0x02, 0x4A], "m1": [0x00, 0xBA], "m2": [0x01, 0x63], + "m1_xinput": [0x00, 0xBD], + "m2_xinput": [0x01, 0x66], } ADDR_DEFAULT = ADDR_0166 @@ -247,6 +261,17 @@ def set_controller_mode(self, dinput: bool = False, init: bool = False) -> None: time.sleep(0.5) self.write(CLAW_SET_MSI) time.sleep(2) + elif init: + logger.info(">>>>>>>> Setting controller to xinput mode.") + time.sleep(0.3) + self.write(CLAW_SET_XINPUT_M1M2(self.addr or ADDR_DEFAULT, "m1_xinput", XINPUT_MAP["key_inst"])) + time.sleep(0.5) + self.write(CLAW_SET_XINPUT_M1M2(self.addr or ADDR_DEFAULT, "m2_xinput", XINPUT_MAP["key_del"])) + time.sleep(0.5) + self.write(CLAW_SYNC_ROM) + time.sleep(0.5) + self.write(CLAW_SET_MSI) + time.sleep(2) if dinput: # Set the device to dinput mode @@ -477,6 +502,10 @@ def controller_loop( required=False, grab=True, capabilities={EC("EV_KEY"): [EC("KEY_ESC")]}, + btn_map={ + EC("KEY_DELETE"): "extra_l1", # M2 + EC("KEY_INSERT"): "extra_r1", # M1 + }, ) d_mouse = DesktopDetectorEvdev( vid=[MSI_CLAW_VID], From 907d629087ab6ba3928fed0c58c415f1bfd3cb24 Mon Sep 17 00:00:00 2001 From: honjow Date: Sun, 1 Mar 2026 22:38:56 +0800 Subject: [PATCH 2/3] refactor: update button mappings and improve xinput mode handling --- src/hhd/device/claw/base.py | 134 +++++++++++++++++++++++++++++++++--- 1 file changed, 123 insertions(+), 11 deletions(-) diff --git a/src/hhd/device/claw/base.py b/src/hhd/device/claw/base.py index 58a3b5c6..051206f4 100644 --- a/src/hhd/device/claw/base.py +++ b/src/hhd/device/claw/base.py @@ -30,11 +30,20 @@ logger = logging.getLogger(__name__) +# CLAW_SET_M1M2 = lambda a, btn: bytes( +# [0x0F, 0x00, 0x00, 0x3C, 0x21, 0x01, *a[btn], 0x05, 0x01, 0x00, 0x00, 0x12, 0x00] +# ) CLAW_SET_M1M2 = lambda a, btn: bytes( - [0x0F, 0x00, 0x00, 0x3C, 0x21, 0x01, *a[btn], 0x05, 0x01, 0x00, 0x00, 0x12, 0x00] + [0x0F, 0x00, 0x00, 0x3C, 0x21, 0x01, *a[btn], 0x02, 0x01, 0x00] ) CLAW_SET_XINPUT_M1M2 = lambda a, btn, b: bytes( - [0x0F, 0x00, 0x00, 0x3C, 0x21, 0x01, *a[btn], 0x01, b] + [ + 0x0F, 0x00, 0x00, 0x3C, 0x21, 0x01, + *a[btn], # offset + 0x07, # data length + 0x04, 0x00, # reset mode + b, 0xFF, 0xFF, 0xFF, 0xFF, # key code, max 5 keys + ] ) CLAW_SYNC_ROM = bytes([0x0F, 0x00, 0x00, 0x3C, 0x22]) @@ -65,12 +74,115 @@ BUTTON_MIN_DELAY = 0.13 XINPUT_MAP = { - "btn_a": 0x09, - "btn_b": 0x0A, - "key_inst": 0x7A, - "key_del": 0x7D, - "key_1": 0x40, + # gamepad codes + "abs_hat0y-1": 0x01, + "abs_hat0y+1": 0x02, + "abs_hat0x-1": 0x03, + "abs_hat0x+1": 0x04, + "btn_tl": 0x05, + "btn_tr": 0x06, + "btn_thumbl": 0x07, + "btn_thumbr": 0x08, + + "btn_south": 0x09, # A + "btn_east": 0x0A, # B + "btn_north": 0x0B, # X + "btn_west": 0x0C, # Y + + "btn_mode": 0x0D, + "btn_select": 0x0E, + "btn_start": 0x0F, + + # keyboard codes "key_esc": 0x32, + "key_f1": 0x33, + "key_f2": 0x34, + "key_f3": 0x35, + "key_f4": 0x36, + "key_f5": 0x37, + "key_f6": 0x38, + "key_f7": 0x39, + "key_f8": 0x3A, + "key_f9": 0x3B, + "key_f10": 0x3C, + "key_f11": 0x3D, + "key_f12": 0x3E, + "key_grave": 0x3F, + "key_1": 0x40, + "key_2": 0x41, + "key_3": 0x42, + "key_4": 0x43, + "key_5": 0x44, + "key_6": 0x45, + "key_7": 0x46, + "key_8": 0x47, + "key_9": 0x48, + "key_0": 0x49, + "key_minus": 0x4A, + "key_equal": 0x4B, + "key_backspace": 0x4C, + "key_tab": 0x4D, + "key_q": 0x4E, + "key_w": 0x4F, + "key_e": 0x50, + "key_r": 0x51, + "key_t": 0x52, + "key_y": 0x53, + "key_u": 0x54, + "key_i": 0x55, + "key_o": 0x56, + "key_p": 0x57, + "key_leftbrace": 0x58, + "key_rightbrace": 0x59, + "key_backslash": 0x5A, + "key_capslock": 0x5B, + "key_a": 0x5C, + "key_s": 0x5D, + "key_d": 0x5E, + "key_f": 0x5F, + "key_g": 0x60, + "key_h": 0x61, + "key_j": 0x62, + "key_k": 0x63, + "key_l": 0x64, + "key_semicolon": 0x65, + "key_leftshift": 0x66, + "key_apostrophe": 0x67, + "key_enter": 0x68, + "key_z": 0x69, + "key_x": 0x6A, + "key_c": 0x6B, + "key_v": 0x6C, + "key_b": 0x6D, + "key_n": 0x6E, + "key_m": 0x6F, + "key_leftctrl": 0x70, + "key_rightshift": 0x71, + "key_comma": 0x72, + "key_dot": 0x73, + "key_slash": 0x74, + "key_leftalt": 0x75, + "key_leftmata": 0x76, + "key_rightctrl": 0x77, + "key_rightalt": 0x78, + "key_space": 0x79, + "key_insert": 0x7A, + "key_home": 0x7B, + "key_pageup": 0x7C, + "key_delete": 0x7D, + "key_end": 0x7E, + "key_pagedown": 0x7f, + "key_kp_enter": 0x8A, + "key_kp_0": 0x8B, + "key_kp_1": 0x8C, + "key_kp_2": 0x8D, + "key_kp_3": 0x8E, + "key_kp_4": 0x8F, + "key_kp_5": 0x90, + "key_kp_6": 0x91, + "key_kp_7": 0x92, + "key_kp_8": 0x93, + "key_kp_9": 0x94, } # 0211 @@ -86,8 +198,8 @@ "rgb": [0x02, 0x4A], "m1": [0x00, 0xBA], "m2": [0x01, 0x63], - "m1_xinput": [0x00, 0xBD], - "m2_xinput": [0x01, 0x66], + "m1_xinput": [0x00, 0xBB], + "m2_xinput": [0x01, 0x64], } ADDR_DEFAULT = ADDR_0166 @@ -264,9 +376,9 @@ def set_controller_mode(self, dinput: bool = False, init: bool = False) -> None: elif init: logger.info(">>>>>>>> Setting controller to xinput mode.") time.sleep(0.3) - self.write(CLAW_SET_XINPUT_M1M2(self.addr or ADDR_DEFAULT, "m1_xinput", XINPUT_MAP["key_inst"])) + self.write(CLAW_SET_XINPUT_M1M2(self.addr or ADDR_DEFAULT, "m1_xinput", XINPUT_MAP["key_insert"])) time.sleep(0.5) - self.write(CLAW_SET_XINPUT_M1M2(self.addr or ADDR_DEFAULT, "m2_xinput", XINPUT_MAP["key_del"])) + self.write(CLAW_SET_XINPUT_M1M2(self.addr or ADDR_DEFAULT, "m2_xinput", XINPUT_MAP["key_delete"])) time.sleep(0.5) self.write(CLAW_SYNC_ROM) time.sleep(0.5) From e071d4c0a4e3046f6b7c4ec2a9c3959ee14c6215 Mon Sep 17 00:00:00 2001 From: honjow Date: Sun, 1 Mar 2026 22:38:56 +0800 Subject: [PATCH 3/3] claw: change d_kbd_2 to allow M1/M2 event output Change d_kbd_2 from DesktopDetectorEvdev to GenericGamepadEvdev to allow M1/M2 buttons (Insert/Delete keys) to be used as extra_l1/extra_r1 game buttons. Desktop mode detection now relies solely on d_mouse. --- src/hhd/device/claw/base.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/hhd/device/claw/base.py b/src/hhd/device/claw/base.py index 051206f4..e2cd5e98 100644 --- a/src/hhd/device/claw/base.py +++ b/src/hhd/device/claw/base.py @@ -607,8 +607,7 @@ def controller_loop( btn_map=dconf.get("btn_mapping", MSI_CLAW_MAPPINGS), ) - # Mute these so after suspend we do not get stray keypresses - d_kbd_2 = DesktopDetectorEvdev( + d_kbd_2 = GenericGamepadEvdev( vid=[MSI_CLAW_VID], pid=[MSI_CLAW_XINPUT_PID, MSI_CLAW_DINPUT_PID], required=False, @@ -717,9 +716,8 @@ def prepare(m): evs.extend(d.produce(r)) # Detect if we are in desktop mode through events - desktop_mode = d_mouse.desktop or d_kbd_2.desktop + desktop_mode = d_mouse.desktop d_mouse.desktop = False - d_kbd_2.desktop = False if desktop_mode or (switch_to_dinput and start > switch_to_dinput): logger.info(