Made Keyboard API a lot more flexible

This commit is contained in:
NicoHood 2015-10-25 14:26:55 +01:00
parent 1f531ae9e9
commit eff788e540
7 changed files with 230 additions and 258 deletions

View file

@ -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

View file

@ -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"

View file

@ -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
}

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;