@@ -94,6 +94,55 @@ pub fn cmd_button(slot: u8, category: u8, code: u8, modifier: u8) -> [u8; FRAME_
9494/// Mouse-button action category.
9595pub const ACTION_MOUSE : u8 = 0x01 ;
9696
97+ /// Keyboard-key action category (category `0x05` in the button-slot write).
98+ pub const ACTION_KEYBOARD : u8 = 0x05 ;
99+
100+ /// Macro-buffer block-write address used to stage a single key-press/release
101+ /// sequence before the button slot is pointed at category `ACTION_KEYBOARD`.
102+ const MACRO_BUFFER_ADDR : u8 = 0x20 ;
103+
104+ /// Build the macro-buffer write that stages a single keyboard key (press then
105+ /// release of `keycode`, no modifiers).
106+ ///
107+ /// Captured remap of the right button to keyboard `a` (0x04):
108+ /// `07 00 01 20 08 02 81 04 00 41 04 00 89 00 00 c8`
109+ ///
110+ /// Layout: byte[2]=0x01 (macro marker), byte[3]=0x20 (buffer addr),
111+ /// byte[4]=0x08 (data length), byte[5]=0x02 (event count: down+up),
112+ /// byte[6]=0x81/byte[9]=0x41 (key-down / key-up event tags),
113+ /// byte[7]/byte[10]=keycode, byte[8]/byte[11]=0x00 (inter-event delay),
114+ /// byte[12]=inner checksum `0x55 - sum(byte[5..=11])`, byte[15]=tail checksum
115+ /// `0xF1 - byte[2] - byte[3] - byte[4]`, byte[0]=global checksum.
116+ pub fn cmd_keyboard_macro ( keycode : u8 ) -> [ u8 ; FRAME_LEN ] {
117+ let mut f = [ 0u8 ; FRAME_LEN ] ;
118+ f[ 2 ] = 0x01 ;
119+ f[ 3 ] = MACRO_BUFFER_ADDR ;
120+ f[ 4 ] = 0x08 ;
121+ f[ 5 ] = 0x02 ; // two events: key down, key up
122+ f[ 6 ] = 0x81 ; // key-down event tag
123+ f[ 7 ] = keycode;
124+ f[ 8 ] = 0x00 ; // delay
125+ f[ 9 ] = 0x41 ; // key-up event tag
126+ f[ 10 ] = keycode;
127+ f[ 11 ] = 0x00 ; // delay
128+ let inner: u8 = f[ 5 ..=11 ] . iter ( ) . fold ( 0u8 , |a, & b| a. wrapping_add ( b) ) ;
129+ f[ 12 ] = 0x55u8 . wrapping_sub ( inner) ;
130+ // Tail rule generalised to include byte[2] (0 for ordinary config frames).
131+ f[ 15 ] = 0xF1u8 . wrapping_sub ( f[ 2 ] ) . wrapping_sub ( f[ 3 ] ) . wrapping_sub ( f[ 4 ] ) ;
132+ checksum ( & mut f) ;
133+ f
134+ }
135+
136+ /// The two frames that remap a physical button slot to a single keyboard key:
137+ /// first the macro-buffer stage, then the button-slot write pointing the slot
138+ /// at category `ACTION_KEYBOARD` with the keycode in the modifier byte.
139+ pub fn cmd_button_keyboard ( slot : u8 , keycode : u8 ) -> [ [ u8 ; FRAME_LEN ] ; 2 ] {
140+ [
141+ cmd_keyboard_macro ( keycode) ,
142+ cmd_button ( slot, ACTION_KEYBOARD , 0x00 , keycode) ,
143+ ]
144+ }
145+
97146// Mouse-button action codes (category `ACTION_MOUSE`). Confirmed from captures:
98147// these are a standard HID button bitmask.
99148pub const MOUSE_LEFT : u8 = 0x01 ;
@@ -522,6 +571,21 @@ mod tests {
522571 ) ;
523572 }
524573
574+ #[ test]
575+ fn test_button_right_slot_to_keyboard_a ( ) {
576+ // Captured: right button slot 0x64 remapped to keyboard 'a' (0x04).
577+ // The official software sends the macro-buffer stage then the slot write.
578+ let frames = cmd_button_keyboard ( SLOT_RIGHT , 0x04 ) ;
579+ assert_eq ! (
580+ frames[ 0 ] ,
581+ hex( "07 00 01 20 08 02 81 04 00 41 04 00 89 00 00 c8" )
582+ ) ;
583+ assert_eq ! (
584+ frames[ 1 ] ,
585+ hex( "07 00 00 64 04 05 00 04 4c 00 00 00 00 00 00 89" )
586+ ) ;
587+ }
588+
525589 #[ test]
526590 fn test_button_left_slot_to_left_click ( ) {
527591 // Captured: left button slot 0x60 set to Left Click.
0 commit comments