package keyboard import ( "errors" "machine" "machine/usb/hid" ) // from usb-hid-keyboard.go var ( ErrInvalidCodepoint = errors.New("invalid Unicode codepoint") ErrInvalidKeycode = errors.New("invalid keyboard keycode") ErrInvalidUTF8 = errors.New("invalid UTF-8 encoding") ErrKeypressMaximum = errors.New("maximum keypresses exceeded") ) var Keyboard *keyboard // Keyboard represents a USB HID keyboard device with support for international // layouts and various control, system, multimedia, and consumer keycodes. // // Keyboard implements the io.Writer interface that translates UTF-8 encoded // byte strings into sequences of keypress events. type keyboard struct { // led holds the current state of all keyboard LEDs: // 1=NumLock 2=CapsLock 4=ScrollLock 8=Compose 16=Kana led uint8 // mod holds the current state of all keyboard modifier keys: // 1=LeftCtrl 2=LeftShift 4=LeftAlt 8=LeftGUI // 16=RightCtrl 32=RightShift 64=RightAlt 128=RightGUI mod uint8 // key holds a list of all keyboard keys currently pressed. key [hidKeyboardKeyCount]uint8 con [hidKeyboardConCount]uint16 sys [hidKeyboardSysCount]uint8 // decode holds the current state of the UTF-8 decoder. decode decodeState // wideChar holds high bits for the UTF-8 decoder. wideChar uint16 buf *hid.RingBuffer waitTxc bool } // decodeState represents a state in the UTF-8 decode state machine. type decodeState uint8 // Constant enumerated values of type decodeState. const ( decodeReset decodeState = iota decodeByte1 decodeByte2 decodeByte3 ) func init() { if Keyboard == nil { Keyboard = newKeyboard() hid.SetHandler(Keyboard) } } // New returns the USB hid-keyboard port. // Deprecated, better to just use Port() func New() *keyboard { return Port() } // Port returns the USB hid-keyboard port. func Port() *keyboard { return Keyboard } func newKeyboard() *keyboard { return &keyboard{ buf: hid.NewRingBuffer(), } } func (kb *keyboard) Handler() bool { kb.waitTxc = false if b, ok := kb.buf.Get(); ok { kb.waitTxc = true hid.SendUSBPacket(b) return true } return false } func (kb *keyboard) RxHandler(b []byte) bool { return false } func (kb *keyboard) tx(b []byte) { if machine.USBDev.InitEndpointComplete { if kb.waitTxc { kb.buf.Put(b) } else { kb.waitTxc = true hid.SendUSBPacket(b) } } } func (kb *keyboard) ready() bool { return true } // Write transmits press-and-release key sequences for each Keycode translated // from the given UTF-8 byte string. Write implements the io.Writer interface // and conforms to all documented conventions for arguments and return values. func (kb *keyboard) Write(b []byte) (n int, err error) { for _, c := range b { if err = kb.WriteByte(c); nil != err { break } n += 1 } return } // WriteByte processes a single byte from a UTF-8 byte string. This method is a // stateful method with respect to the receiver Keyboard, meaning that its exact // behavior will depend on the current state of its UTF-8 decode state machine: // // 1. If the given byte is a valid ASCII encoding (0-127), then a keypress // sequence is immediately transmitted for the respective Keycode. // 2. If the given byte represents the final byte in a multi-byte codepoint, // then a keypress sequence is immediately transmitted by translating the // multi-byte codepoint to its respective Keycode. // 3. If the given byte appears to represent high bits for a multi-byte // codepoint, then the bits are copied to the receiver's internal state // machine buffer for use by a subsequent call to WriteByte() (or Write()) // that completes the codepoint. // 4. If the given byte is out of range, or contains illegal bits for the // current state of the UTF-8 decoder, then the UTF-8 decode state machine // is reset to its initial state. // // In cases 3 and 4, a keypress sequence is not generated and no data is // transmitted. In case 3, additional bytes must be received via WriteByte() // (or Write()) to complete or discard the current codepoint. func (kb *keyboard) WriteByte(b byte) error { switch { case b < 0x80: // 1-byte encoding (0x00-0x7F) kb.decode = decodeByte1 return kb.write(uint16(b)) case b < 0xC0: // 2nd, 3rd, or 4th byte (0x80-0xBF) b = Keycode(b).key() switch kb.decode { case decodeByte2: kb.decode = decodeByte1 return kb.write(kb.wideChar | uint16(b)) case decodeByte3: kb.decode = decodeByte2 kb.wideChar |= uint16(b) << 6 } case b < 0xE0: // 2-byte encoding (0xC2-0xDF), or illegal byte 2 (0xC0-0xC1) kb.decode = decodeByte2 kb.wideChar = uint16(b&0x1F) << 6 case b < 0xF0: // 3-byte encoding (0xE0-0xEF) kb.decode = decodeByte3 kb.wideChar = uint16(b&0x0F) << 12 default: // 4-byte encoding unsupported (0xF0-0xF4), or illegal byte 4 (0xF5-0xFF) kb.decode = decodeReset return ErrInvalidUTF8 } return nil } func (kb *keyboard) write(p uint16) error { c := keycode(p) if 0 == c { return ErrInvalidCodepoint } if d := deadkey(c); 0 != d { if err := kb.writeKeycode(d); nil != err { return err } } return kb.writeKeycode(c) } func (kb *keyboard) writeKeycode(c Keycode) error { var b [9]byte b[0] = 0x02 b[1] = c.mod() b[2] = 0 b[3] = c.key() b[4] = 0 b[5] = 0 b[6] = 0 b[7] = 0 b[8] = 0 if !kb.sendKey(false, b[:]) { return hid.ErrHIDReportTransfer } b[1] = 0 b[3] = 0 if !kb.sendKey(false, b[:]) { return hid.ErrHIDReportTransfer } return nil } // Press transmits a press-and-release sequence for the given Keycode, which // simulates a discrete keypress event. // // The following values of Keycode are supported: // // 0x0020 - 0x007F ASCII (U+0020 to U+007F) [USES LAYOUT] // 0x0080 - 0xC1FF Unicode (U+0080 to U+C1FF) [USES LAYOUT] // 0xC200 - 0xDFFF UTF-8 packed (U+0080 to U+07FF) [USES LAYOUT] // 0xE000 - 0xE0FF Modifier key (bitmap, 8 keys, Shift/Ctrl/Alt/GUI) // 0xE200 - 0xE2FF System key (HID usage code, page 1) // 0xE400 - 0xE7FF Media/Consumer key (HID usage code, page 12) // 0xF000 - 0xFFFF Normal key (HID usage code, page 7) func (kb *keyboard) Press(c Keycode) error { if err := kb.Down(c); nil != err { return err } return kb.Up(c) } func (kb *keyboard) sendKey(consumer bool, b []byte) bool { kb.tx(b) return true } func (kb *keyboard) keyboardSendKeys(consumer bool) bool { var b [9]byte if !consumer { b[0] = 0x02 // REPORT_ID b[1] = kb.mod b[2] = 0x02 b[3] = kb.key[0] b[4] = kb.key[1] b[5] = kb.key[2] b[6] = kb.key[3] b[7] = kb.key[4] b[8] = kb.key[5] return kb.sendKey(consumer, b[:]) } else { b[0] = 0x03 // REPORT_ID b[1] = uint8(kb.con[0]) b[2] = uint8((kb.con[0] & 0x0300) >> 8) return kb.sendKey(consumer, b[:3]) } } // Down transmits a key-down event for the given Keycode. // // The host will interpret the key as being held down continuously until a // corresponding key-up event is transmitted, e.g., via method Up(). // // See godoc comment on method Press() for details on what input is accepted and // how it is interpreted. func (kb *keyboard) Down(c Keycode) error { var res uint8 msb := c >> 8 if msb >= 0xC2 { if msb < 0xE0 { c = ((msb & 0x1F) << 6) | Keycode(c.key()) } else { switch msb { case 0xF0: return kb.down(uint8(c), 0) case 0xE0: return kb.down(0, uint8(c)) case 0xE2: return kb.downSys(uint8(c)) default: if 0xE4 <= msb && msb <= 0xE7 { return kb.downCon(uint16(c & 0x03FF)) } return ErrInvalidKeycode } } } c = keycode(uint16(c)) if 0 == c { return ErrInvalidCodepoint } if d := deadkey(c); 0 != d { res = kb.mod if 0 != res { kb.mod = 0 kb.keyboardSendKeys(false) } kb.down(d.key(), d.mod()) kb.up(d.key(), d.mod()) } return kb.down(c.key(), c.mod()|res) } func (kb *keyboard) down(key uint8, mod uint8) error { send := false if 0 != mod { if kb.mod&mod != mod { kb.mod |= mod send = true } } if 0 != key { for _, k := range kb.key { if k == key { goto end } } for i, k := range kb.key { if 0 == k { kb.key[i] = key send = true goto end } } return ErrKeypressMaximum } end: if send { if !kb.keyboardSendKeys(false) { return hid.ErrHIDReportTransfer } } return nil } func (kb *keyboard) downCon(key uint16) error { if 0 == key { return ErrInvalidKeycode } for _, k := range kb.con { if key == k { return nil // already pressed } } for i, k := range kb.con { if 0 == k { kb.con[i] = key if !kb.keyboardSendKeys(true) { return hid.ErrHIDReportTransfer } return nil } } return ErrKeypressMaximum } func (kb *keyboard) downSys(key uint8) error { if 0 == key { return ErrInvalidKeycode } for _, k := range kb.sys { if key == k { return nil // already pressed } } for i, k := range kb.sys { if 0 == k { kb.sys[i] = key if !kb.keyboardSendKeys(true) { return hid.ErrHIDReportTransfer } return nil } } return ErrKeypressMaximum } // Up transmits a key-up event for the given Keycode. // // See godoc comment on method Press() for details on what input is accepted and // how it is interpreted. func (kb *keyboard) Up(c Keycode) error { msb := c >> 8 if msb >= 0xC2 { if msb < 0xE0 { c = ((msb & 0x1F) << 6) | Keycode(c.key()) } else { switch msb { case 0xF0: return kb.up(uint8(c), 0) case 0xE0: return kb.up(0, uint8(c)) case 0xE2: return kb.upSys(uint8(c)) default: if 0xE4 <= msb && msb <= 0xE7 { return kb.upCon(uint16(c & 0x03FF)) } return ErrInvalidKeycode } } } c = keycode(uint16(c)) if 0 == c { return ErrInvalidCodepoint } return kb.up(c.key(), c.mod()) } // Release transmits a key-up event for all keyboard keys currently pressed as // if the user removed his/her hands from the keyboard entirely. func (kb *keyboard) Release() error { bits := uint16(kb.mod) kb.mod = 0 for i, k := range kb.key { bits |= uint16(k) kb.key[i] = 0 } if 0 != bits { if !kb.keyboardSendKeys(false) { return hid.ErrHIDReportTransfer } } bits = 0 for i, k := range kb.con { bits |= k kb.con[i] = 0 } for i, k := range kb.sys { bits |= uint16(k) kb.sys[i] = 0 } if 0 != bits { if !kb.keyboardSendKeys(true) { return hid.ErrHIDReportTransfer } } return nil } func (kb *keyboard) up(key uint8, mod uint8) error { send := false if 0 != mod { if kb.mod&mod != 0 { kb.mod &^= mod send = true } } if 0 != key { for i, k := range kb.key { if key == k { kb.key[i] = 0 send = true } } } if send { if !kb.keyboardSendKeys(false) { return hid.ErrHIDReportTransfer } } return nil } func (kb *keyboard) upCon(key uint16) error { if 0 == key { return ErrInvalidKeycode } for i, k := range kb.con { if key == k { kb.con[i] = 0 if !kb.keyboardSendKeys(true) { return hid.ErrHIDReportTransfer } return nil } } return nil } func (kb *keyboard) upSys(key uint8) error { if 0 == key { return ErrInvalidKeycode } for i, k := range kb.sys { if key == k { kb.sys[i] = 0 if !kb.keyboardSendKeys(true) { return hid.ErrHIDReportTransfer } return nil } } return nil }