package remdeshid import "errors" const ( MOD_LCTRL = 0x01 MOD_LSHIFT = 0x02 MOD_LALT = 0x04 MOD_LGUI = 0x08 MOD_RCTRL = 0x10 MOD_RSHIFT = 0x20 MOD_RALT = 0x40 MOD_RGUI = 0x80 MOD_RENTER = 0x58 ) // IsModifierKey checks if the given JavaScript keycode corresponds to a modifier key func IsModifierKey(keycode uint8) bool { switch keycode { case 16: // Shift return true case 17: // Control return true case 18: // Alt return true case 91: // Meta (Windows/Command key) return true default: return false } } func (c *Controller) SetModifierKey(keycode uint8, isRight bool) ([]byte, error) { // Determine the modifier bit based on HID keycode var modifierBit uint8 switch keycode { case 17: if isRight { modifierBit = MOD_RCTRL } else { modifierBit = MOD_LCTRL } case 16: if isRight { modifierBit = MOD_RSHIFT } else { modifierBit = MOD_LSHIFT } case 18: if isRight { modifierBit = MOD_RALT } else { modifierBit = MOD_LALT } case 91: if isRight { modifierBit = MOD_RGUI } else { modifierBit = MOD_LGUI } default: // Not a modifier key return nil, errors.ErrUnsupported } c.hidState.Modkey |= modifierBit return keyboardSendKeyCombinations(c) } func (c *Controller) UnsetModifierKey(keycode uint8, isRight bool) ([]byte, error) { // Determine the modifier bit based on HID keycode var modifierBit uint8 switch keycode { case 17: if isRight { modifierBit = MOD_RCTRL } else { modifierBit = MOD_LCTRL } case 16: if isRight { modifierBit = MOD_RSHIFT } else { modifierBit = MOD_LSHIFT } case 18: if isRight { modifierBit = MOD_RALT } else { modifierBit = MOD_LALT } case 91: if isRight { modifierBit = MOD_RGUI } else { modifierBit = MOD_LGUI } default: // Not a modifier key return nil, errors.ErrUnsupported } c.hidState.Modkey &^= modifierBit return keyboardSendKeyCombinations(c) } // SendKeyboardPress sends a keyboard press by JavaScript keycode func (c *Controller) SendKeyboardPress(keycode uint8) ([]byte, error) { // Convert JavaScript keycode to HID keycode = javaScriptKeycodeToHIDOpcode(keycode) if keycode == 0x00 { // Not supported return nil, errors.New("Unsupported keycode: " + string(keycode)) } // Already pressed? Skip for i := 0; i < 6; i++ { if c.hidState.KeyboardButtons[i] == keycode { return nil, nil } } // Get the empty slot in the current HID list for i := 0; i < 6; i++ { if c.hidState.KeyboardButtons[i] == 0x00 { c.hidState.KeyboardButtons[i] = keycode return keyboardSendKeyCombinations(c) } } // No space left return nil, errors.New("No space left in keyboard state to press key: " + string(keycode)) } // SendKeyboardRelease sends a keyboard release by JavaScript keycode func (c *Controller) SendKeyboardRelease(keycode uint8) ([]byte, error) { // Convert JavaScript keycode to HID keycode = javaScriptKeycodeToHIDOpcode(keycode) if keycode == 0x00 { // Not supported return nil, errors.New("Unsupported keycode: " + string(keycode)) } // Find the position where the key is pressed for i := 0; i < 6; i++ { if c.hidState.KeyboardButtons[i] == keycode { c.hidState.KeyboardButtons[i] = 0x00 return keyboardSendKeyCombinations(c) } } // That key is not pressed return nil, nil } // keyboardSendKeyCombinations simulates sending the current key combinations func keyboardSendKeyCombinations(c *Controller) ([]byte, error) { // Prepare the packet packet := []uint8{ 0x57, 0xAB, 0x00, 0x02, 0x08, c.hidState.Modkey, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } // Populate the HID keycodes for i := 0; i < len(c.hidState.KeyboardButtons); i++ { packet[7+i] = c.hidState.KeyboardButtons[i] } // Calculate checksum packet[13] = calcChecksum(packet[:13]) err := c.Send(packet) if err != nil { return nil, errors.New("failed to send mouse move command: " + err.Error()) } // Wait for a reply from the device return c.WaitForReply(0x02) } // JavaScriptKeycodeToHIDOpcode converts JavaScript keycode into HID keycode func javaScriptKeycodeToHIDOpcode(keycode uint8) uint8 { // Letters A-Z if keycode >= 65 && keycode <= 90 { return (keycode - 65) + 0x04 // 'A' is 0x04 } // Numbers 1-9 (top row, not numpad) if keycode >= 49 && keycode <= 57 { return (keycode - 49) + 0x1E // '1' is 0x1E } // Numpad 1-9 if keycode >= 97 && keycode <= 105 { return (keycode - 97) + 0x59 // '1' (numpad) is 0x59 } // F1 to F12 if keycode >= 112 && keycode <= 123 { return (keycode - 112) + 0x3A // 'F1' is 0x3A } switch keycode { case 8: return 0x2A // Backspace case 9: return 0x2B // Tab case 13: return 0x28 // Enter case 16: return 0xE1 // Left shift case 17: return 0xE0 // Left Ctrl case 18: return 0xE6 // Left Alt case 19: return 0x48 // Pause case 20: return 0x39 // Caps Lock case 27: return 0x29 // Escape case 32: return 0x2C // Spacebar case 33: return 0x4B // Page Up case 34: return 0x4E // Page Down case 35: return 0x4D // End case 36: return 0x4A // Home case 37: return 0x50 // Left Arrow case 38: return 0x52 // Up Arrow case 39: return 0x4F // Right Arrow case 40: return 0x51 // Down Arrow case 44: return 0x46 // Print Screen or F13 (Firefox) case 45: return 0x49 // Insert case 46: return 0x4C // Delete case 48: return 0x27 // 0 (not Numpads) case 59: return 0x33 // ';' case 61: return 0x2E // '=' case 91: return 0xE3 // Left GUI (Windows) case 92: return 0xE7 // Right GUI case 93: return 0x65 // Menu key case 96: return 0x62 // 0 (Numpads) case 106: return 0x55 // * (Numpads) case 107: return 0x57 // + (Numpads) case 109: return 0x56 // - (Numpads) case 110: return 0x63 // dot (Numpads) case 111: return 0x54 // divide (Numpads) case 144: return 0x53 // Num Lock case 145: return 0x47 // Scroll Lock case 146: return 0x58 // Numpad enter case 173: return 0x2D // - case 186: return 0x33 // ';' case 187: return 0x2E // '=' case 188: return 0x36 // ',' case 189: return 0x2D // '-' case 190: return 0x37 // '.' case 191: return 0x38 // '/' case 192: return 0x35 // '`' case 219: return 0x2F // '[' case 220: return 0x31 // backslash case 221: return 0x30 // ']' case 222: return 0x34 // '\'' default: return 0x00 // Unknown / unsupported } }