mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-13 07:23:39 +00:00
511 lines
11 KiB
Go
511 lines
11 KiB
Go
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
|
|
}
|