Made Keyboard API a lot more flexible
This commit is contained in:
parent
1f531ae9e9
commit
eff788e540
7 changed files with 230 additions and 258 deletions
|
|
@ -29,19 +29,6 @@ THE SOFTWARE.
|
|||
#endif
|
||||
|
||||
// Hut1_12v2.pdf
|
||||
enum KeyboardModifier : uint8_t {
|
||||
KEY_LEFT_CTRL = (1 << 0),
|
||||
KEY_LEFT_SHIFT = (1 << 1),
|
||||
KEY_LEFT_ALT = (1 << 2),
|
||||
KEY_LEFT_GUI = (1 << 3),
|
||||
KEY_LEFT_WINDOWS = (1 << 3), // Alias
|
||||
KEY_RIGHT_CTRL = (1 << 4),
|
||||
KEY_RIGHT_SHIFT = (1 << 5),
|
||||
KEY_RIGHT_ALT = (1 << 6),
|
||||
KEY_RIGHT_GUI = (1 << 7),
|
||||
KEY_RIGHT_WINDOWS = (1 << 7), // Alias
|
||||
};
|
||||
|
||||
enum KeyboardKeycode : uint8_t {
|
||||
KEY_RESERVED = 0,
|
||||
KEY_ERROR_ROLLOVER = 1,
|
||||
|
|
@ -267,15 +254,16 @@ enum KeyboardKeycode : uint8_t {
|
|||
KEYPAD_DECIMAL = 0xDC, // Disabled (Ubuntu)
|
||||
KEYPAD_HEXADECIMAL = 0xDD, // Disabled (Ubuntu)
|
||||
|
||||
// Used in KeyboardModifier enum directly
|
||||
//KEY_LEFT_CTRL = 0xE0,
|
||||
//KEY_LEFT_SHIFT = 0xE1,
|
||||
//KEY_LEFT_ALT = 0xE2,
|
||||
//KEY_LEFT_GUI = 0xE3,
|
||||
//KEY_RIGHT_CTRL = 0xE4,
|
||||
//KEY_RIGHT_SHIFT = 0xE5,
|
||||
//KEY_RIGHT_ALT = 0xE6,
|
||||
//KEY_RIGHT_GUI = 0xE7,
|
||||
KEY_LEFT_CTRL = 0xE0,
|
||||
KEY_LEFT_SHIFT = 0xE1,
|
||||
KEY_LEFT_ALT = 0xE2,
|
||||
KEY_LEFT_GUI = 0xE3,
|
||||
KEY_LEFT_WINDOWS = 0xE3, // Alias
|
||||
KEY_RIGHT_CTRL = 0xE4,
|
||||
KEY_RIGHT_SHIFT = 0xE5,
|
||||
KEY_RIGHT_ALT = 0xE6,
|
||||
KEY_RIGHT_GUI = 0xE7,
|
||||
KEY_RIGHT_WINDOWS = 0xE7, // Alias
|
||||
|
||||
// Keyboard HID mappings
|
||||
|
||||
|
|
|
|||
|
|
@ -37,52 +37,76 @@ typedef union{
|
|||
struct{
|
||||
uint8_t modifiers;
|
||||
uint8_t reserved;
|
||||
KeyboardKeycode keys[6];
|
||||
KeyboardKeycode keycodes[6];
|
||||
};
|
||||
uint8_t keys[8];
|
||||
} HID_KeyboardReport_Data_t;
|
||||
|
||||
|
||||
class KeyboardAPI : public Print
|
||||
{
|
||||
public:
|
||||
inline void begin(void);
|
||||
inline void end(void);
|
||||
|
||||
inline size_t write(uint8_t k);
|
||||
// Raw Keycode API functions
|
||||
inline size_t write(KeyboardKeycode k);
|
||||
inline size_t write(KeyboardModifier k);
|
||||
inline size_t write(ConsumerKeycode k);
|
||||
|
||||
inline size_t press(uint8_t k);
|
||||
inline size_t press(KeyboardKeycode k);
|
||||
inline size_t press(KeyboardModifier k);
|
||||
inline size_t press(ConsumerKeycode k);
|
||||
|
||||
inline size_t release(uint8_t k);
|
||||
inline size_t release(KeyboardKeycode k);
|
||||
inline size_t release(KeyboardModifier k);
|
||||
inline size_t release(ConsumerKeycode k);
|
||||
|
||||
inline size_t add(uint8_t k);
|
||||
inline size_t add(KeyboardKeycode k);
|
||||
inline size_t add(KeyboardModifier k);
|
||||
inline size_t add(ConsumerKeycode k);
|
||||
|
||||
inline size_t remove(uint8_t k);
|
||||
inline size_t remove(KeyboardKeycode k);
|
||||
inline size_t remove(KeyboardModifier k);
|
||||
inline size_t remove(ConsumerKeycode k);
|
||||
|
||||
inline size_t add(KeyboardKeycode k);
|
||||
inline size_t releaseAll(void);
|
||||
inline size_t removeAll(void);
|
||||
inline int send(void);
|
||||
inline void wakeupHost(void);
|
||||
|
||||
// Sending is public in the base class for advanced users.
|
||||
virtual int SendReport(void* data, int length) = 0;
|
||||
//press(uint8_t key, uint8_t modifier) TODO variadic template
|
||||
|
||||
// Print API functions
|
||||
inline virtual size_t write(uint8_t k) override;
|
||||
inline size_t press(uint8_t k);
|
||||
inline size_t release(uint8_t k);
|
||||
inline size_t add(uint8_t k);
|
||||
inline size_t remove(uint8_t k);
|
||||
|
||||
// Needs to be implemented in a lower level
|
||||
virtual size_t removeAll(void) = 0;
|
||||
virtual int send(void) = 0;
|
||||
|
||||
private:
|
||||
virtual size_t set(KeyboardKeycode k, bool s) = 0;
|
||||
inline size_t set(uint8_t k, bool s);
|
||||
};
|
||||
|
||||
class DefaultKeyboardAPI : public KeyboardAPI
|
||||
{
|
||||
public:
|
||||
// Implement adding/removing key functions
|
||||
inline virtual size_t removeAll(void) override;
|
||||
|
||||
// Add special consumer key API for the reserved byte
|
||||
inline size_t write(ConsumerKeycode k);
|
||||
inline size_t press(ConsumerKeycode k);
|
||||
inline size_t remove(ConsumerKeycode k);
|
||||
inline size_t add(ConsumerKeycode k);
|
||||
inline size_t release(ConsumerKeycode k);
|
||||
|
||||
// Also use the base class functions
|
||||
// http://en.cppreference.com/w/cpp/language/using_declaration
|
||||
using KeyboardAPI::write;
|
||||
using KeyboardAPI::press;
|
||||
using KeyboardAPI::remove;
|
||||
using KeyboardAPI::add;
|
||||
using KeyboardAPI::release;
|
||||
|
||||
// Needs to be implemented in a lower level
|
||||
virtual int send(void) = 0;
|
||||
|
||||
protected:
|
||||
HID_KeyboardReport_Data_t _keyReport;
|
||||
|
||||
private:
|
||||
inline virtual size_t set(KeyboardKeycode k, bool s) override;
|
||||
};
|
||||
|
||||
//TODO NKRO API
|
||||
|
||||
|
||||
// Implementation is inline
|
||||
#include "KeyboardAPI.hpp"
|
||||
|
|
|
|||
|
|
@ -24,9 +24,14 @@ THE SOFTWARE.
|
|||
// Include guard
|
||||
#pragma once
|
||||
|
||||
|
||||
void KeyboardAPI::begin(void)
|
||||
{
|
||||
releaseAll();
|
||||
// Force API to send a clean report.
|
||||
// This is important for and HID bridge where the receiver stays on,
|
||||
// while the sender is resetted.
|
||||
removeAll();
|
||||
send();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -36,17 +41,6 @@ void KeyboardAPI::end(void)
|
|||
}
|
||||
|
||||
|
||||
size_t KeyboardAPI::write(uint8_t k)
|
||||
{
|
||||
// Press and release key (if press was successfull)
|
||||
auto ret = press(k);
|
||||
if(ret){
|
||||
release(k);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
size_t KeyboardAPI::write(KeyboardKeycode k)
|
||||
{
|
||||
// Press and release key (if press was successfull)
|
||||
|
|
@ -58,19 +52,55 @@ size_t KeyboardAPI::write(KeyboardKeycode k)
|
|||
}
|
||||
|
||||
|
||||
size_t KeyboardAPI::write(KeyboardModifier k)
|
||||
{
|
||||
// Press and release key (if press was successfull)
|
||||
auto ret = press(k);
|
||||
size_t KeyboardAPI::press(KeyboardKeycode k)
|
||||
{
|
||||
// Press key and send report to host
|
||||
auto ret = add(k);
|
||||
if(ret){
|
||||
release(k);
|
||||
send();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
size_t KeyboardAPI::write(ConsumerKeycode k)
|
||||
size_t KeyboardAPI::release(KeyboardKeycode k)
|
||||
{
|
||||
// Release key and send report to host
|
||||
auto ret = remove(k);
|
||||
if(ret){
|
||||
send();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
size_t KeyboardAPI::add(KeyboardKeycode k)
|
||||
{
|
||||
// Add key to report
|
||||
return set(k, true);
|
||||
}
|
||||
|
||||
|
||||
size_t KeyboardAPI::remove(KeyboardKeycode k)
|
||||
{
|
||||
// Remove key from report
|
||||
return set(k, false);
|
||||
}
|
||||
|
||||
|
||||
size_t KeyboardAPI::releaseAll(void)
|
||||
{
|
||||
// Release all keys
|
||||
auto ret = removeAll();
|
||||
if(ret){
|
||||
send();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
size_t KeyboardAPI::write(uint8_t k)
|
||||
{
|
||||
// Press and release key (if press was successfull)
|
||||
auto ret = press(k);
|
||||
if(ret){
|
||||
|
|
@ -91,32 +121,10 @@ size_t KeyboardAPI::press(uint8_t k)
|
|||
}
|
||||
|
||||
|
||||
size_t KeyboardAPI::press(KeyboardKeycode k)
|
||||
size_t KeyboardAPI::release(uint8_t k)
|
||||
{
|
||||
// Press key and send report to host
|
||||
auto ret = add(k);
|
||||
if(ret){
|
||||
send();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
size_t KeyboardAPI::press(KeyboardModifier k)
|
||||
{
|
||||
// Press modifier key and send report to host
|
||||
auto ret = add(k);
|
||||
if(ret){
|
||||
send();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
size_t KeyboardAPI::press(ConsumerKeycode k)
|
||||
{
|
||||
// Press key and send report to host
|
||||
auto ret = add(k);
|
||||
// Release key and send report to host
|
||||
auto ret = remove(k);
|
||||
if(ret){
|
||||
send();
|
||||
}
|
||||
|
|
@ -126,6 +134,19 @@ size_t KeyboardAPI::press(ConsumerKeycode k)
|
|||
|
||||
size_t KeyboardAPI::add(uint8_t k)
|
||||
{
|
||||
// Add key to report
|
||||
return set(k, true);
|
||||
}
|
||||
|
||||
|
||||
size_t KeyboardAPI::remove(uint8_t k)
|
||||
{
|
||||
// Remove key from report
|
||||
return set(k, false);
|
||||
}
|
||||
|
||||
|
||||
size_t KeyboardAPI::set(uint8_t k, bool s){
|
||||
// Ignore invalid input
|
||||
if(k >= sizeof(_asciimap)){
|
||||
setWriteError();
|
||||
|
|
@ -134,45 +155,108 @@ size_t KeyboardAPI::add(uint8_t k)
|
|||
|
||||
// Read key from ascii lookup table
|
||||
k = pgm_read_byte(_asciimap + k);
|
||||
auto ret = add(KeyboardKeycode(k & ~SHIFT));
|
||||
auto ret = set(KeyboardKeycode(k & ~SHIFT), s);
|
||||
|
||||
// Only press shift and send if keycode was successfully added
|
||||
if(ret && k & SHIFT){
|
||||
add(KEY_LEFT_SHIFT);
|
||||
// Only add shift if keycode was successfully added before.
|
||||
// Always try to release shift (if used).
|
||||
if((k & SHIFT) && (ret || !s)){
|
||||
ret |= set(KEY_LEFT_SHIFT, s);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
size_t KeyboardAPI::add(KeyboardKeycode k)
|
||||
size_t DefaultKeyboardAPI::set(KeyboardKeycode k, bool s)
|
||||
{
|
||||
// Add k to the key report only if it's not already present
|
||||
// and if there is an empty slot.
|
||||
for (uint8_t i = 0; i < sizeof(_keyReport.keys); i++)
|
||||
// It's a modifier key
|
||||
if(k >= KEY_LEFT_CTRL && k <= KEY_RIGHT_GUI)
|
||||
{
|
||||
// Is key already in the list or did we found an empty slot?
|
||||
auto key = _keyReport.keys[i];
|
||||
if (key == uint8_t(k) || key == KEY_RESERVED) {
|
||||
_keyReport.keys[i] = k;
|
||||
return 1;
|
||||
// Convert key into bitfield (0 - 7)
|
||||
k = KeyboardKeycode(uint8_t(k) - uint8_t(KEY_LEFT_CTRL));
|
||||
if(s){
|
||||
_keyReport.modifiers = (1 << k);
|
||||
}
|
||||
else{
|
||||
_keyReport.modifiers &= ~(1 << k);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
// Its a normal key
|
||||
else{
|
||||
// Add k to the key report only if it's not already present
|
||||
// and if there is an empty slot. Remove the first available key.
|
||||
for (uint8_t i = 0; i < sizeof(_keyReport.keycodes); i++)
|
||||
{
|
||||
auto key = _keyReport.keycodes[i];
|
||||
|
||||
// Is key already in the list or did we found an empty slot?
|
||||
if (s && (key == uint8_t(k) || key == KEY_RESERVED)) {
|
||||
_keyReport.keycodes[i] = k;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Test the key report to see if k is present. Clear it if it exists.
|
||||
if (!s && (key == k)) {
|
||||
_keyReport.keycodes[i] = KEY_RESERVED;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No empty key position was found
|
||||
setWriteError();
|
||||
// No empty/pressed key was found
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
size_t KeyboardAPI::add(KeyboardModifier k)
|
||||
size_t DefaultKeyboardAPI::removeAll(void)
|
||||
{
|
||||
// Add modifier key
|
||||
_keyReport.modifiers |= k;
|
||||
return 1;
|
||||
// Release all keys
|
||||
uint8_t ret = 0;
|
||||
for (uint8_t i = 0; i < sizeof(_keyReport.keys); i++)
|
||||
{
|
||||
// Is a key in the list or did we found an empty slot?
|
||||
if(_keyReport.keys[i]){
|
||||
ret++;
|
||||
}
|
||||
_keyReport.keys[i] = 0x00;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
size_t KeyboardAPI::add(ConsumerKeycode k)
|
||||
size_t DefaultKeyboardAPI::write(ConsumerKeycode k)
|
||||
{
|
||||
// Press and release key (if press was successfull)
|
||||
auto ret = press(k);
|
||||
if(ret){
|
||||
release(k);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
size_t DefaultKeyboardAPI::press(ConsumerKeycode k)
|
||||
{
|
||||
// Press key and send report to host
|
||||
auto ret = add(k);
|
||||
if(ret){
|
||||
send();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
size_t DefaultKeyboardAPI::release(ConsumerKeycode k)
|
||||
{
|
||||
// Release key and send report to host
|
||||
auto ret = remove(k);
|
||||
if(ret){
|
||||
send();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
size_t DefaultKeyboardAPI::add(ConsumerKeycode k)
|
||||
{
|
||||
// No 2 byte keys are supported
|
||||
if(k > 0xFF){
|
||||
|
|
@ -187,99 +271,7 @@ size_t KeyboardAPI::add(ConsumerKeycode k)
|
|||
}
|
||||
|
||||
|
||||
size_t KeyboardAPI::release(uint8_t k)
|
||||
{
|
||||
// Release key and send report to host
|
||||
auto ret = remove(k);
|
||||
if(ret){
|
||||
send();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
size_t KeyboardAPI::release(KeyboardKeycode k)
|
||||
{
|
||||
// Release key and send report to host
|
||||
auto ret = remove(k);
|
||||
if(ret){
|
||||
send();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
size_t KeyboardAPI::release(KeyboardModifier k)
|
||||
{
|
||||
// Release modifier key and send report to host
|
||||
auto ret = remove(k);
|
||||
if(ret){
|
||||
send();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
size_t KeyboardAPI::release(ConsumerKeycode k)
|
||||
{
|
||||
// Release key and send report to host
|
||||
auto ret = remove(k);
|
||||
if(ret){
|
||||
send();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
size_t KeyboardAPI::remove(uint8_t k)
|
||||
{
|
||||
// Ignore invalid input
|
||||
if(k >= sizeof(_asciimap)){
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Read key from ascii lookup table
|
||||
k = pgm_read_byte(_asciimap + k);
|
||||
auto ret = remove(KeyboardKeycode(k & ~SHIFT));
|
||||
|
||||
// Always try to release shift (if used)
|
||||
if(k & SHIFT){
|
||||
ret |= remove(KEY_LEFT_SHIFT);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
size_t KeyboardAPI::remove(KeyboardKeycode k)
|
||||
{
|
||||
// Test the key report to see if k is present. Clear it if it exists.
|
||||
for (uint8_t i = 0; i < sizeof(_keyReport.keys); i++) {
|
||||
if (_keyReport.keys[i] == k) {
|
||||
_keyReport.keys[i] = KEY_RESERVED;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// No pressed key was found
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
size_t KeyboardAPI::remove(KeyboardModifier k)
|
||||
{
|
||||
// Release modifier key
|
||||
auto oldModifier = _keyReport.modifiers;
|
||||
_keyReport.modifiers &= ~k;
|
||||
|
||||
// Check if we actually released a key
|
||||
if(_keyReport.modifiers != oldModifier){
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
size_t KeyboardAPI::remove(ConsumerKeycode k)
|
||||
size_t DefaultKeyboardAPI::remove(ConsumerKeycode k)
|
||||
{
|
||||
// No 2 byte keys are supported
|
||||
if(k > 0xFF){
|
||||
|
|
@ -291,46 +283,3 @@ size_t KeyboardAPI::remove(ConsumerKeycode k)
|
|||
_keyReport.reserved = HID_CONSUMER_UNASSIGNED;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
size_t KeyboardAPI::releaseAll(void)
|
||||
{
|
||||
// Release all keys
|
||||
auto ret = removeAll();
|
||||
if(ret){
|
||||
send();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
size_t KeyboardAPI::removeAll(void)
|
||||
{
|
||||
// Release all keys
|
||||
uint8_t ret = 0;
|
||||
for (uint8_t i = 0; i < sizeof(_keyReport.keys); i++)
|
||||
{
|
||||
// Is a key in the list or did we found an empty slot?
|
||||
if(_keyReport.keys[i]){
|
||||
ret++;
|
||||
}
|
||||
|
||||
_keyReport.keys[i] = KEY_RESERVED;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int KeyboardAPI::send(void){
|
||||
// TODO set write error if usb send fails/disconnected?
|
||||
return SendReport(&_keyReport, sizeof(_keyReport));
|
||||
}
|
||||
|
||||
|
||||
void KeyboardAPI::wakeupHost(void){
|
||||
#ifdef USBCON
|
||||
USBDevice.wakeupHost();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -70,9 +70,13 @@ Keyboard_::Keyboard_(void)
|
|||
HID().AppendDescriptor(&node);
|
||||
}
|
||||
|
||||
int Keyboard_::SendReport(void* data, int length)
|
||||
int Keyboard_::send(void)
|
||||
{
|
||||
return HID().SendReport(HID_REPORTID_KEYBOARD, data, length);
|
||||
return HID().SendReport(HID_REPORTID_KEYBOARD, &_keyReport, sizeof(_keyReport));
|
||||
}
|
||||
|
||||
void Keyboard_::wakeupHost(void){
|
||||
USBDevice.wakeupHost();
|
||||
}
|
||||
|
||||
Keyboard_ Keyboard;
|
||||
|
|
|
|||
|
|
@ -31,13 +31,14 @@ THE SOFTWARE.
|
|||
#include "../HID-APIs/KeyboardAPI.h"
|
||||
|
||||
|
||||
class Keyboard_ : public KeyboardAPI
|
||||
class Keyboard_ : public DefaultKeyboardAPI
|
||||
{
|
||||
public:
|
||||
Keyboard_(void);
|
||||
void wakeupHost(void);
|
||||
|
||||
protected:
|
||||
virtual inline int SendReport(void* data, int length) override;
|
||||
virtual inline int send(void) override;
|
||||
};
|
||||
extern Keyboard_ Keyboard;
|
||||
|
||||
|
|
|
|||
|
|
@ -156,10 +156,15 @@ uint8_t BootKeyboard_::getProtocol(void){
|
|||
return protocol;
|
||||
}
|
||||
|
||||
int BootKeyboard_::SendReport(void* data, int length){
|
||||
return USB_Send(pluggedEndpoint | TRANSFER_RELEASE, data, length);
|
||||
int BootKeyboard_::send(void){
|
||||
return USB_Send(pluggedEndpoint | TRANSFER_RELEASE, &_keyReport, sizeof(_keyReport));
|
||||
}
|
||||
|
||||
void BootKeyboard_::wakeupHost(void){
|
||||
USBDevice.wakeupHost();
|
||||
}
|
||||
|
||||
|
||||
BootKeyboard_ BootKeyboard;
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -31,12 +31,13 @@ THE SOFTWARE.
|
|||
#include "../HID-APIs/KeyboardAPI.h"
|
||||
|
||||
|
||||
class BootKeyboard_ : public PluggableUSBModule, public KeyboardAPI
|
||||
class BootKeyboard_ : public PluggableUSBModule, public DefaultKeyboardAPI
|
||||
{
|
||||
public:
|
||||
BootKeyboard_(void);
|
||||
uint8_t getLeds(void);
|
||||
uint8_t getProtocol(void);
|
||||
void wakeupHost(void);
|
||||
|
||||
protected:
|
||||
// Implementation of the PUSBListNode
|
||||
|
|
@ -50,7 +51,7 @@ protected:
|
|||
|
||||
uint8_t leds;
|
||||
|
||||
virtual int SendReport(void* data, int length) override;
|
||||
virtual int send(void) override;
|
||||
};
|
||||
extern BootKeyboard_ BootKeyboard;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue