diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ginput/driver_keyboard.h | 129 | ||||
-rw-r--r-- | src/ginput/ginput_ginput.c | 10 | ||||
-rw-r--r-- | src/ginput/ginput_keyboard.c | 575 | ||||
-rw-r--r-- | src/ginput/ginput_keyboard.h | 210 | ||||
-rw-r--r-- | src/ginput/keyboard_microcode.c | 73 | ||||
-rw-r--r-- | src/ginput/keyboard_microcode.h | 107 | ||||
-rw-r--r-- | src/ginput/sys_make.mk | 1 | ||||
-rw-r--r-- | src/ginput/sys_options.h | 46 |
8 files changed, 1081 insertions, 70 deletions
diff --git a/src/ginput/driver_keyboard.h b/src/ginput/driver_keyboard.h new file mode 100644 index 00000000..329df97a --- /dev/null +++ b/src/ginput/driver_keyboard.h @@ -0,0 +1,129 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/ginput/driver_keyboard.h + * @brief GINPUT LLD header file for keyboard drivers. + * + * @defgroup Keyboard Keyboard + * @ingroup GINPUT + * @{ + */ + +#ifndef _LLD_GINPUT_KEYBOARD_H +#define _LLD_GINPUT_KEYBOARD_H + +#if GINPUT_NEED_KEYBOARD //|| defined(__DOXYGEN__) + +// Include the GDRIVER infrastructure +#include "src/gdriver/sys_defs.h" + +typedef struct GKeyboard { + GDriver d; // The driver overheads and vmt + uint16_t cntc; // The byte count in c + uint16_t cntsc; // The byte count in sc + char c[8]; // The utf8 code for the current key + char sc[8]; // The scancode for the current key + uint32_t keystate; // The keyboard state. + uint16_t flags; + #define GKEYBOARD_FLG_NEEDREAD 0x0001 + uint16_t laystate; // The layout state. + const uint8_t * pLayout; // The current keyboard layout + // Other driver specific fields may follow. +} GKeyboard; + +typedef struct GKeyboardVMT { + GDriverVMT d; // Device flags are part of the general vmt + #define GKEYBOARD_VFLG_NOPOLL 0x0001 // Do not poll this device - it is purely interrupt driven + #define GKEYBOARD_VFLG_DYNAMICONLY 0x8000 // This keyboard driver should not be statically initialized eg Win32 + const uint8_t * defLayout; // The default keyboard layout + bool_t (*init)(GKeyboard *m, unsigned driverinstance); // Required + void (*deinit)(GKeyboard *m); // Optional + int (*getdata)(GKeyboard *k, uint8_t *pch, int sz); // Required. Get zero or more scancode bytes. Returns the number of scancode bytes returns + void (*putdata)(GKeyboard *k, char ch); // Optional. Send a single byte to the keyboard. +} GKeyboardVMT; + +#define gkvmt(m) ((const GKeyboardVMT const *)((m)->d.vmt)) + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +// If we are not using multiple keyboards then hard-code the VMT name +#if !defined(GINPUT_KEYBOARD_DRIVER_LIST) + #undef GKEYBOARD_DRIVER_VMT + #define GKEYBOARD_DRIVER_VMT GKEYBOARDVMT_OnlyOne +#endif + +#ifdef __cplusplus +extern "C" { +#endif + /** + * @brief Initialize a keyboard driver + * + * @param[in] g The keyboard driver + * @param[in] param Unused by keyboard + * @param[in] driverinstance The driver instance ToDo: Add some more details + * @param[in] systeminstance The mouse instance ToDo: Add some more details + * + * @return TRUE on success, FALSE otherwise + * @note This routine is provided by the high level code for + * use in the driver VMT's GMouseVMT.d structure. + * + * @notapi + */ + bool_t _gkeyboardInitDriver(GDriver *g, void *param, unsigned driverinstance, unsigned systeminstance); + + /** + * @brief Routine that is called after initialization + * + * @param[in] g The keyboard driver + * @note This routine is provided by the high level code for + * use in the driver VMT's GKeyboardVMT.d structure. + * + * @notapi + */ + void _gkeyboardPostInitDriver(GDriver *g); + + /** + * @brief Deinitialize a keyboard driver + * + * @param[in] g The kerboard driver + * @note This routine is provided by the high level code for + * use in the driver VMT's GKeyboardVMT.d structure. + * + * @notapi + */ + void _gkeyboardDeInitDriver(GDriver *g); + + /** + * @brief Wakeup the high level code so that it attempts another read + * + * @note This routine is provided to low level drivers by the high level code + * + * @notapi + */ + void _gkeyboardWakeup(GKeyboard *k); + + /** + * @brief Wakeup the high level code so that it attempts another read + * + * @note This routine is provided to low level drivers by the high level code + * + * @iclass + * @notapi + */ + void _gkeyboardWakeupI(GKeyboard *k); + +#ifdef __cplusplus +} +#endif + +#endif /* GINPUT_NEED_KEYBOARD */ + +#endif /* _LLD_GINPUT_KEYBOARD_H */ +/** @} */ diff --git a/src/ginput/ginput_ginput.c b/src/ginput/ginput_ginput.c index 191ae025..becefc19 100644 --- a/src/ginput/ginput_ginput.c +++ b/src/ginput/ginput_ginput.c @@ -20,12 +20,19 @@ extern void _gmouseInit(void); extern void _gmouseDeinit(void); #endif +#if GINPUT_NEED_KEYBOARD + extern void _gkeyboardInit(void); + extern void _gkeyboardDeinit(void); +#endif void _ginputInit(void) { #if GINPUT_NEED_MOUSE _gmouseInit(); #endif + #if GINPUT_NEED_KEYBOARD + _gkeyboardInit(); + #endif /** * This should really call an init routine for each ginput sub-system. * Maybe we'll do this later. @@ -34,6 +41,9 @@ void _ginputInit(void) void _ginputDeinit(void) { + #if GINPUT_NEED_KEYBOARD + _gkeyboardDeinit(); + #endif #if GINPUT_NEED_MOUSE _gmouseDeinit(); #endif diff --git a/src/ginput/ginput_keyboard.c b/src/ginput/ginput_keyboard.c index 5443572b..0d855622 100644 --- a/src/ginput/ginput_keyboard.c +++ b/src/ginput/ginput_keyboard.c @@ -8,16 +8,575 @@ /** * @file src/ginput/ginput_keyboard.c * @brief GINPUT keyboard code. - * - * @defgroup Keyboard Keyboard - * @ingroup GINPUT - * - * @{ */ #include "gfx.h" -#if (GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD) || defined(__DOXYGEN__) - #error "GINPUT: GINPUT_NEED_KEYBOARD - Not Implemented Yet" +#if GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD + +#define MICROCODE_DEBUG FALSE + +#if MICROCODE_DEBUG + #include <stdio.h> +#endif + +#if GKEYBOARD_LAYOUT_OFF + #error "Win32: The Win32 keyboard driver requires the layout engine. Please set GKEYBOARD_LAYOUT_OFF to FALSE." +#endif + +// Get the keyboard driver interface +#include "driver_keyboard.h" +#include "keyboard_microcode.h" + +// The keyboard poll timer +static GTIMER_DECL(KeyboardTimer); + +static void SendKeyboardEventToListener(GSourceListener *psl, GKeyboard *k) { + GEventKeyboard *pe; + int i; + + // If there is no event buffer just mark a missed event + if (!(pe = (GEventKeyboard *)geventGetEventBuffer(psl))) { + // This listener is missing - save the meta events that have happened + psl->srcflags |= GKEYSTATE_MISSED_EVENT; + return; + } + + if ((psl->listenflags & GLISTEN_KEYRAW)) { + pe->type = GEVENT_KEYBOARD; + pe->bytecount = k->cntsc; + for(i = 0; i < k->cntsc; i++) pe->c[i] = k->sc[i]; + for(; i < 8; i++) pe->c[i] = 0; + pe->keystate = k->keystate | psl->srcflags | GKEYSTATE_RAW; + psl->srcflags = 0; + return; + } + + if ((psl->listenflags & GLISTEN_KEYREPEATSOFF) && (k->keystate & GKEYSTATE_REPEAT)) + return; + + if ((psl->listenflags & GLISTEN_KEYNOSPECIALS) && (k->keystate & GKEYSTATE_SPECIAL)) + return; + + if (!(psl->listenflags & GLISTEN_KEYUP) && (k->keystate & GKEYSTATE_KEYUP)) + k->cntc = 0; + + if (!(psl->listenflags & GLISTEN_KEYTRANSITIONS) && !k->cntc) + return; + + pe->type = GEVENT_KEYBOARD; + pe->bytecount = k->cntc; + for(i = 0; i < k->cntc; i++) pe->c[i] = k->c[i]; + for(; i < 8; i++) pe->c[i] = 0; + pe->keystate = k->keystate | psl->srcflags; + psl->srcflags = 0; + geventSendEvent(psl); +} + +static void SendKeyboardEvent(GKeyboard *k) { + GSourceListener *psl; + + // Send to the "All Keyboards" source listeners + psl = 0; + while ((psl = geventGetSourceListener((GSourceHandle)&KeyboardTimer, psl))) + SendKeyboardEventToListener(psl, k); + + // Send to the keyboard specific source listeners + psl = 0; + while ((psl = geventGetSourceListener((GSourceHandle)k, psl))) + SendKeyboardEventToListener(psl, k); +} + +#define FLAG_ERROR 0x01 +#define FLAG_INIT 0x02 + +#if GKEYBOARD_LAYOUT_OFF + static void microengine(GKeyboard *k, uint8_t code, uint8_t flags) { + if (flags) + return; + + // Just send an event using the char + k->c[0] = k->sc[0] = code; + k->cntc = k->cntsc = 1; + SendKeyboardEvent(k); + k->cntc = k->cntsc = 0; + } +#else + static void microengine(GKeyboard *k, uint8_t code, uint8_t flags) { + const uint8_t *pc; + const uint8_t *nrec; + uint8_t ver, diff, p1, p2; + #if MICROCODE_DEBUG + unsigned cnt; + #endif + + pc = k->pLayout; + if (!pc) { + if (flags) + return; + + // Default is to just send an event using the char + k->c[0] = k->sc[0] = code; + k->cntc = k->cntsc = 1; + SendKeyboardEvent(k); + k->cntc = k->cntsc = 0; + return; + } + + // Check the layout header + if (*pc++ != KMC_HEADERSTART || *pc++ != KMC_HEADER_ID1 || *pc++ != KMC_HEADER_ID2) + return; + + // We only understand version 1 currently + ver = *pc++; + if (ver < KMC_HEADER_VER_MIN || ver > KMC_HEADER_VER_MAX) + return; + + // Setup + diff = code; + if (k->cntsc >= sizeof(k->sc)) + flags |= FLAG_ERROR; + else + k->sc[k->cntsc++] = code; + + #if MICROCODE_DEBUG + cnt = 0; + #endif + + while(*pc++ == KMC_RECORDSTART) { + // Get the record length + p1 = *pc++; + if (!p1) break; + nrec = pc + p1; + + #if MICROCODE_DEBUG + cnt++; + #endif + + while(pc < nrec) { + switch(*pc++) { + case KMC_TEST_INIT: + if (!(flags & FLAG_INIT)) goto nextrecord; + break; + case KMC_TEST_ERROR: + if (!(flags & FLAG_ERROR)) goto nextrecord; + break; + case KMC_TEST_CODE: + if (flags != 0) goto nextrecord; + p1 = *pc++; + if (p1 != code) goto nextrecord; + diff = 0; + break; + case KMC_TEST_CODERANGE: + if (flags != 0) goto nextrecord; + p1 = *pc++; + p2 = *pc++; + if (code < p1 || code > p2) goto nextrecord; + diff = code - p1; + break; + case KMC_TEST_CODETABLE: + if (flags != 0) goto nextrecord; + p1 = *pc++; + for(p2 = 0; ; p2++, p1--, pc++) { + if (!p1) goto nextrecord; + if (*pc == code) break; + } + pc += p1; + diff = p2; + break; + case KMC_TEST_STATEBIT: + p1 = *pc++; + if ((p1 & KMC_BIT_CLEAR)) { + if ((k->keystate & (1 << (p1 & 31)))) goto nextrecord; + } else { + if (!(k->keystate & (1 << (p1 & 31)))) goto nextrecord; + } + break; + case KMC_TEST_STATEOR: + p1 = *pc++; + if ((p1 & KMC_BIT_CLEAR)) { + if (!(k->keystate & (1 << (p1 & 31)))) break; + } else { + if ((k->keystate & (1 << (p1 & 31)))) break; + } + p2 = *pc++; + if ((p2 & KMC_BIT_CLEAR)) { + if (!(k->keystate & (1 << (p2 & 31)))) break; + } else { + if ((k->keystate & (1 << (p2 & 31)))) break; + } + goto nextrecord; + case KMC_TEST_STATEAND: + p1 = *pc++; + if ((p1 & KMC_BIT_CLEAR)) { + if ((k->keystate & (1 << (p1 & 31)))) goto nextrecord; + } else { + if (!(k->keystate & (1 << (p1 & 31)))) goto nextrecord; + } + p2 = *pc++; + if ((p2 & KMC_BIT_CLEAR)) { + if ((k->keystate & (1 << (p2 & 31)))) goto nextrecord; + } else { + if (!(k->keystate & (1 << (p2 & 31)))) goto nextrecord; + } + break; + case KMC_TEST_LAYOUTBIT: + p1 = *pc++; + if ((p1 & KMC_BIT_CLEAR)) { + if ((k->laystate & (1 << (p1 & 15)))) goto nextrecord; + } else { + if (!(k->laystate & (1 << (p1 & 15)))) goto nextrecord; + } + break; + case KMC_TEST_LAYOUTOR: + p1 = *pc++; + if ((p1 & KMC_BIT_CLEAR)) { + if (!(k->laystate & (1 << (p1 & 15)))) break; + } else { + if ((k->laystate & (1 << (p1 & 15)))) break; + } + p2 = *pc++; + if ((p2 & KMC_BIT_CLEAR)) { + if (!(k->laystate & (1 << (p2 & 15)))) break; + } else { + if ((k->laystate & (1 << (p2 & 15)))) break; + } + goto nextrecord; + case KMC_TEST_LAYOUTAND: + p1 = *pc++; + if ((p1 & KMC_BIT_CLEAR)) { + if ((k->laystate & (1 << (p1 & 15)))) goto nextrecord; + } else { + if (!(k->laystate & (1 << (p1 & 15)))) goto nextrecord; + } + p2 = *pc++; + if ((p2 & KMC_BIT_CLEAR)) { + if ((k->laystate & (1 << (p2 & 15)))) goto nextrecord; + } else { + if (!(k->laystate & (1 << (p2 & 15)))) goto nextrecord; + } + break; + case KMC_TEST_CODEBIT: + if (flags != 0) goto nextrecord; + p1 = *pc++; + if ((p1 & KMC_BIT_CLEAR)) { + if ((code & (1 << (p1 & 7)))) goto nextrecord; + } else { + if (!(code & (1 << (p1 & 7)))) goto nextrecord; + } + break; + case KMC_TEST_CODEOR: + if (flags != 0) goto nextrecord; + p1 = *pc++; + if ((p1 & KMC_BIT_CLEAR)) { + if (!(code & (1 << (p1 & 7)))) break; + } else { + if ((code & (1 << (p1 & 7)))) break; + } + p2 = *pc++; + if ((p2 & KMC_BIT_CLEAR)) { + if (!(code & (1 << (p2 & 7)))) break; + } else { + if ((code & (1 << (p2 & 7)))) break; + } + goto nextrecord; + case KMC_TEST_CODEAND: + if (flags != 0) goto nextrecord; + p1 = *pc++; + if ((p1 & KMC_BIT_CLEAR)) { + if ((code & (1 << (p1 & 7)))) goto nextrecord; + } else { + if (!(code & (1 << (p1 & 7)))) goto nextrecord; + } + p2 = *pc++; + if ((p2 & KMC_BIT_CLEAR)) { + if ((code & (1 << (p2 & 7)))) goto nextrecord; + } else { + if (!(code & (1 << (p2 & 7)))) goto nextrecord; + } + break; + case KMC_TEST_LASTCODE: + p1 = *pc++; + if (k->cntsc < 2) goto nextrecord; + if (p1 != k->sc[k->cntsc-2]) goto nextrecord; + break; + case KMC_TEST_SHIFT: + if ((k->keystate & (GKEYSTATE_SHIFT_L|GKEYSTATE_SHIFT_R))) break; + goto nextrecord; + case KMC_TEST_NOSHIFT: + if (!(k->keystate & (GKEYSTATE_SHIFT_L|GKEYSTATE_SHIFT_R))) break; + goto nextrecord; + case KMC_TEST_CTRL: + if ((k->keystate & (GKEYSTATE_CTRL_L|GKEYSTATE_CTRL_R))) break; + goto nextrecord; + case KMC_TEST_NOCTRL: + if (!(k->keystate & (GKEYSTATE_CTRL_L|GKEYSTATE_CTRL_R))) break; + goto nextrecord; + case KMC_TEST_ALT: + if ((k->keystate & (GKEYSTATE_ALT_L|GKEYSTATE_ALT_R))) break; + goto nextrecord; + case KMC_TEST_NOALT: + if (!(k->keystate & (GKEYSTATE_ALT_L|GKEYSTATE_ALT_R))) break; + goto nextrecord; + case KMC_TEST_CAPS: + if ((k->keystate & GKEYSTATE_CAPSLOCK)) { + if (!(k->keystate & (GKEYSTATE_SHIFT_L|GKEYSTATE_SHIFT_R))) break; + } else { + if ((k->keystate & (GKEYSTATE_SHIFT_L|GKEYSTATE_SHIFT_R))) break; + } + goto nextrecord; + case KMC_TEST_NOCAPS: + if ((k->keystate & GKEYSTATE_CAPSLOCK)) { + if ((k->keystate & (GKEYSTATE_SHIFT_L|GKEYSTATE_SHIFT_R))) break; + } else { + if (!(k->keystate & (GKEYSTATE_SHIFT_L|GKEYSTATE_SHIFT_R))) break; + } + goto nextrecord; + case KMC_TEST_NUMLOCK: + if ((k->keystate & GKEYSTATE_NUMLOCK)) break; + goto nextrecord; + case KMC_TEST_NONUMLOCK: + if (!(k->keystate & GKEYSTATE_NUMLOCK)) break; + goto nextrecord; + + case KMC_ACT_STOP: + #if MICROCODE_DEBUG + fprintf(stderr, "Executed STOP: Records=%2u Flags=0x%02X Code=0x%02X\n", cnt, flags, code); fflush(stderr); + #endif + return; + case KMC_ACT_DONE: + SendKeyboardEvent(k); + k->cntc = k->cntsc = 0; + k->keystate &= ~(GKEYSTATE_KEYUP|GKEYSTATE_SPECIAL); + #if MICROCODE_DEBUG + fprintf(stderr, "Executed DONE: Records=%2u Flags=0x%02X Code=0x%02X\n", cnt, flags, code); fflush(stderr); + #endif + return; + case KMC_ACT_RESET: + k->cntc = k->cntsc = 0; + k->keystate &= ~(GKEYSTATE_KEYUP|GKEYSTATE_SPECIAL); + break; + case KMC_ACT_STATEBIT: + p1 = *pc++; + if ((p1 & KMC_BIT_INVERT)) + k->keystate ^= (1 << (p1 & 31)); + else if ((p1 & KMC_BIT_CLEAR)) + k->keystate &= ~(1 << (p1 & 31)); + else + k->keystate |= (1 << (p1 & 31)); + break; + case KMC_ACT_LAYOUTBIT: + p1 = *pc++; + if ((p1 & KMC_BIT_INVERT)) + k->laystate ^= (1 << (p1 & 15)); + else if ((p1 & KMC_BIT_CLEAR)) + k->laystate &= ~(1 << (p1 & 15)); + else + k->laystate |= (1 << (p1 & 15)); + break; + case KMC_ACT_CODEBIT: + p1 = *pc++; + if ((p1 & KMC_BIT_INVERT)) + code ^= (1 << (p1 & 7)); + else if ((p1 & KMC_BIT_CLEAR)) + code &= ~(1 << (p1 & 7)); + else + code |= (1 << (p1 & 7)); + break; + case KMC_ACT_CHAR: + if (k->cntc >= sizeof(k->c)) goto codeerror; + k->c[k->cntc++] = *pc++; + break; + case KMC_ACT_CHARCODE: + if (k->cntc >= sizeof(k->c)) goto codeerror; + k->c[k->cntc++] = code; + break; + case KMC_ACT_CHARRANGE: + if (k->cntc >= sizeof(k->c)) goto codeerror; + k->c[k->cntc++] = diff + *pc++; + break; + case KMC_ACT_CHARTABLE: + p1 = *pc++; + if (diff < p1) { + if (k->cntc >= sizeof(k->c)) goto codeerror; + k->c[k->cntc++] = pc[diff]; + } + pc += p1; + break; + case KMC_ACT_CLEAR: + k->cntc = 0; + break; + case KMC_ACT_CHARADD: + p1 = *pc++; + if (!k->cntc) + k->c[k->cntc++] = 0; + k->c[k->cntc-1] = k->c[k->cntc-1] * p1 + diff; + break; + case KMC_ACT_DATA: + p1 = *pc++; + if (gkvmt(k)->putdata) + gkvmt(k)->putdata(k, p1); + break; + + default: + codeerror: + #if MICROCODE_DEBUG + fprintf(stderr, "Executed ERROR: Records=%2u Flags=0x%02X Code=0x%02X\n", cnt, flags, code); cnt = 0; fflush(stderr); + #endif + + // Prevent recursion + if (flags & FLAG_ERROR) + return; + + // Process as an error + flags |= FLAG_ERROR; + nrec = k->pLayout + 4; // Jump back to the end of the header to process the error + goto nextrecord; // Nothing left to do here. + } + } + + nextrecord: + pc = nrec; + } + + #if MICROCODE_DEBUG + fprintf(stderr, "Executed END: Records=%2u Flags=0x%02X Code=0x%02X\n", cnt, flags, code); fflush(stderr); + #endif + } +#endif + +static void KeyboardPoll(void *param) { + GKeyboard * k; + uint8_t scancodes[8]; + int sz, i; + (void) param; + + for(k = (GKeyboard *)gdriverGetNext(GDRIVER_TYPE_KEYBOARD, 0); k; k = (GKeyboard *)gdriverGetNext(GDRIVER_TYPE_KEYBOARD, (GDriver *)k)) { + if (!(gkvmt(k)->d.flags & GKEYBOARD_VFLG_NOPOLL) || (k->flags & GKEYBOARD_FLG_NEEDREAD)) { + k->flags &= ~GKEYBOARD_FLG_NEEDREAD; + sz = gkvmt(k)->getdata(k, scancodes, sizeof(scancodes)); + for(i = 0; i < sz; i++) + microengine(k, scancodes[i], 0); + } + } +} + +typedef const GKeyboardVMT const GKEYBOARDVMTLIST[]; + +void _gkeyboardInit(void) { + // GINPUT_KEYBOARD_DRIVER_LIST is defined - create each driver instance + #if defined(GINPUT_KEYBOARD_DRIVER_LIST) + { + int i; + + extern GKEYBOARDVMTLIST GINPUT_KEYBOARD_DRIVER_LIST; + static GKEYBOARDVMTLIST dclist[] = {GINPUT_KEYBOARD_DRIVER_LIST}; + + for(i = 0; i < sizeof(dclist)/sizeof(dclist[0]); i++) { + if (!(dclist[i]->d.flags & GKEYBOARD_VFLG_DYNAMICONLY)) + gdriverRegister(&dclist[i]->d, 0); + } + } + + // One and only one mouse + #else + { + extern GKEYBOARDVMTLIST GKEYBOARDVMT_OnlyOne; + + if (!(GKEYBOARDVMT_OnlyOne->d.flags & GKEYBOARD_VFLG_DYNAMICONLY)) + gdriverRegister(&GKEYBOARDVMT_OnlyOne->d, 0); + } + #endif + +} + +void _gkeyboardDeinit(void) { + gtimerDeinit(&KeyboardTimer); +} + +bool_t _gkeyboardInitDriver(GDriver *g, void *param, unsigned driverinstance, unsigned systeminstance) { + #define k ((GKeyboard *)g) + (void) param; + (void) systeminstance; + + // The initial keyboard layout comes from the VMT + k->pLayout = gkvmt(k)->defLayout; + + // Init the mouse + if (!gkvmt(k)->init((GKeyboard *)g, driverinstance)) + return FALSE; + + // Ensure the Poll timer is started + if (!gtimerIsActive(&KeyboardTimer)) + gtimerStart(&KeyboardTimer, KeyboardPoll, 0, TRUE, GINPUT_KEYBOARD_POLL_PERIOD); + + return TRUE; + + #undef k +} + +void _gkeyboardPostInitDriver(GDriver *g) { + #define k ((GKeyboard *)g) + + // Run the init sequence from the layout microcode. + microengine(k, 0, FLAG_INIT); + + #undef k +} + +void _gkeyboardDeInitDriver(GDriver *g) { + (void) g; +} + +GSourceHandle ginputGetKeyboard(unsigned instance) { + if (instance == GKEYBOARD_ALL_INSTANCES) + return (GSourceHandle)&KeyboardTimer; + return (GSourceHandle)gdriverGetInstance(GDRIVER_TYPE_KEYBOARD, instance); +} + +bool_t ginputGetKeyboardStatus(unsigned instance, GEventKeyboard *pe) { + GKeyboard *k; + + // Win32 threads don't seem to recognise priority and/or pre-emption + // so we add a sleep here to prevent 100% polled applications from locking up. + gfxSleepMilliseconds(1); + + if (!(k = (GKeyboard *)gdriverGetInstance(GDRIVER_TYPE_KEYBOARD, instance))) + return FALSE; + + pe->type = GEVENT_KEYBOARD; + // TODO + return TRUE; +} + +#if !GKEYBOARD_LAYOUT_OFF + bool_t ginputSetKeyboardLayout(unsigned instance, const void *pLayout) { + GKeyboard *k; + + if (!(k = (GKeyboard *)gdriverGetInstance(GDRIVER_TYPE_KEYBOARD, instance))) + return FALSE; + + if (pLayout) + k->pLayout = pLayout; + else + k->pLayout = gkvmt(k)->defLayout; + + return TRUE; + } +#endif + +/* Wake up the keyboard driver from an interrupt service routine (there may be new readings available) */ +void _gkeyboardWakeup(GKeyboard *k) { + if (k) + k->flags |= GKEYBOARD_FLG_NEEDREAD; + gtimerJab(&KeyboardTimer); +} + +/* Wake up the keyboard driver from an interrupt service routine (there may be new readings available) */ +void _gkeyboardWakeupI(GKeyboard *k) { + if (k) + k->flags |= GKEYBOARD_FLG_NEEDREAD; + gtimerJabI(&KeyboardTimer); +} + #endif /* GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD */ -/** @} */ diff --git a/src/ginput/ginput_keyboard.h b/src/ginput/ginput_keyboard.h index 40b83e72..ea218af7 100644 --- a/src/ginput/ginput_keyboard.h +++ b/src/ginput/ginput_keyboard.h @@ -23,72 +23,149 @@ /* Type definitions */ /*===========================================================================*/ -#define GINPUT_KEYBOARD_NUM_PORTS 1 // The total number of keyboard inputs - // Event types for various ginput sources #define GEVENT_KEYBOARD (GEVENT_GINPUT_FIRST+2) typedef struct GEventKeyboard_t { GEventType type; // The type of this event (GEVENT_KEYBOARD) - uint16_t instance; // The keyboard instance - char c; // The ASCII code for the current key press. - // The only possible values are 0(NUL), 8(BS), 9(TAB), 13(CR), 27(ESC), 32(SPACE) to 126(~), 127(DEL) - // 0 indicates an extended only key. - uint16_t code; // An extended keyboard code. Codes less than 128 match their ASCII equivalent. - #define GKEY_NULL 0 - #define GKEY_BACKSPACE 8 - #define GKEY_TAB 9 - #define GKEY_CR 13 - #define GKEY_ESC 27 - #define GKEY_SPACE 32 - #define GKEY_DEL 127 - #define GKEY_UP 0x0101 - #define GKEY_DOWN 0x0102 - #define GKEY_LEFT 0x0103 - #define GKEY_RIGHT 0x0104 - #define GKEY_HOME 0x0105 - #define GKEY_END 0x0106 - #define GKEY_PAGEUP 0x0107 - #define GKEY_PAGEDOWN 0x0108 - #define GKEY_INSERT 0x0109 - #define GKEY_DELETE 0x010A - #define GKEY_SHIFT 0x0201 - #define GKEY_CNTRL 0x0202 - #define GKEY_ALT 0x0203 - #define GKEY_WINKEY 0x0204 - #define GKEY_RCLKEY 0x0205 - #define GKEY_FNKEY 0x0206 - #define GKEY_FN1 0x0301 - #define GKEY_FN2 0x0302 - #define GKEY_FN3 0x0303 - #define GKEY_FN4 0x0304 - #define GKEY_FN5 0x0305 - #define GKEY_FN6 0x0306 - #define GKEY_FN7 0x0307 - #define GKEY_FN8 0x0308 - #define GKEY_FN9 0x0309 - #define GKEY_FN10 0x030A - #define GKEY_FN11 0x030B - #define GKEY_FN12 0x030C - uint16_t current_buttons; // A bit is set to indicate various meta status. - #define GMETA_KEY_DOWN 0x0001 - #define GMETA_KEY_SHIFT 0x0002 - #define GMETA_KEY_CNTRL 0x0004 - #define GMETA_KEY_ALT 0x0008 - #define GMETA_KEY_WINKEY 0x0010 - #define GMETA_KEY_RCLKKEY 0x0020 - #define GMETA_KEY_FN 0x0040 - #define GMETA_KEY_MISSED_EVENT 0x8000 - uint16_t last_buttons; // The value of current_buttons on the last event + uint16_t bytecount; // The number of bytes in c[]. Note this will only ever represent 0 or 1 characters. This is set to 0 for state transitions. + char c[8]; // The utf8 code for the key or a special key code + // Normal characters with special meaning. They are a maximum of 1 byte in length. + #define GKEY_NULL 0 + #define GKEY_BACKSPACE 8 + #define GKEY_TAB 9 + #define GKEY_LF 10 + #define GKEY_CR 13 + #define GKEY_ENTER 13 + #define GKEY_ESC 27 + #define GKEY_SPACE 32 + #define GKEY_DEL 127 + + // These are special keys - GKEYSTATE_SPECIAL will be set. They are a maximum of 1 byte in length. + #define GKEY_UP 0x81 + #define GKEY_DOWN 0x82 + #define GKEY_LEFT 0x83 + #define GKEY_RIGHT 0x84 + #define GKEY_HOME 0x85 + #define GKEY_END 0x86 + #define GKEY_PAGEUP 0x87 + #define GKEY_PAGEDOWN 0x88 + #define GKEY_INSERT 0x89 + #define GKEY_WINKEY 0x8A + #define GKEY_RIGHTCLICKKEY 0x8B + #define GKEY_FN1 0x91 + #define GKEY_FN2 0x92 + #define GKEY_FN3 0x93 + #define GKEY_FN4 0x94 + #define GKEY_FN5 0x95 + #define GKEY_FN6 0x96 + #define GKEY_FN7 0x97 + #define GKEY_FN8 0x98 + #define GKEY_FN9 0x99 + #define GKEY_FN10 0x9A + #define GKEY_FN11 0x9B + #define GKEY_FN12 0x9C + #define GKEY_FN13 0x9D + #define GKEY_FN14 0x9E + #define GKEY_FN15 0x9F + #define GKEY_CTRLBREAK 0xA0 + #define GKEY_CTRLPAUSE 0xA1 + #define GKEY_SYSREQ 0xA2 + #define GKEY_PRINTSCREEN 0xA3 + #define GKEY_POWER 0xA4 + #define GKEY_SLEEP 0xA5 + #define GKEY_SCREENSWITCH 0xA6 + #define GKEY_SCREENLOCK 0xA7 + #define GKEY_WIFIONOFF 0xA8 + #define GKEY_TRACKPADONOFF 0xA9 + #define GKEY_STARTMEDIA 0xAA + #define GKEY_STARTHOME 0xAB + #define GKEY_STARTEMAIL 0xAC + #define GKEY_STARTCOMPUTER 0xAD + #define GKEY_STARTAPP1 0xAE + #define GKEY_STARTAPP2 0xAF + #define GKEY_VOLUP 0xB0 + #define GKEY_VOLDOWN 0xB1 + #define GKEY_VOLMUTE 0xB2 + #define GKEY_EJECT 0xB3 + #define GKEY_MEDIAPLAY 0xB4 + #define GKEY_MEDIASTOP 0xB5 + #define GKEY_MEDIAPAUSE 0xB6 + #define GKEY_MEDIAFORWARD 0xB7 + #define GKEY_MEDIANEXT 0xB8 + #define GKEY_MEDIAREWIND 0xB9 + #define GKEY_MEDIAPREV 0xBA + #define GKEY_MEDIASLOW 0xBB + #define GKEY_LIGHTUP 0xBC + #define GKEY_LIGHTDOWN 0xBD + #define GKEY_LIGHTONOFF 0xBE + #define GKEY_LAYOUT_FIRST 0xC0 // Special characters the layout can return start here. + #define GKEY_DRIVER_FIRST 0xE0 // Special characters the driver can return start here. + + uint32_t keystate; // The keyboard state. + #define GKEYSTATE_KEYUP_BIT 0 + #define GKEYSTATE_REPEAT_BIT 1 + #define GKEYSTATE_SPECIAL_BIT 2 + #define GKEYSTATE_RAW_BIT 3 + #define GKEYSTATE_SHIFT_L_BIT 4 + #define GKEYSTATE_SHIFT_R_BIT 5 + #define GKEYSTATE_CTRL_L_BIT 6 + #define GKEYSTATE_CTRL_R_BIT 7 + #define GKEYSTATE_ALT_L_BIT 8 + #define GKEYSTATE_ALT_R_BIT 9 + #define GKEYSTATE_FN_BIT 10 + #define GKEYSTATE_COMPOSE_BIT 11 + #define GKEYSTATE_WINKEY_BIT 12 + #define GKEYSTATE_CAPSLOCK_BIT 13 + #define GKEYSTATE_NUMLOCK_BIT 14 + #define GKEYSTATE_SCROLLLOCK_BIT 15 + #define GKEYSTATE_LAYOUT_FIRST_BIT 16 + #define GKEYSTATE_SYSTEM_FIRST_BIT 20 + #define GKEYSTATE_DRIVER_FIRST_BIT 24 + #define GKEYSTATE_MISSED_EVENT_BIT 31 + + #define GKEYSTATE_KEYUP (1<<GKEYSTATE_KEYUP_BIT) // This is a keyup rather than a keydown event + #define GKEYSTATE_REPEAT (1<<GKEYSTATE_REPEAT_BIT) // This is an automatic repeat character + #define GKEYSTATE_SPECIAL (1<<GKEYSTATE_SPECIAL_BIT) // The character is a special character defined above and not a normal (utf8) character + #define GKEYSTATE_RAW (1<<GKEYSTATE_RAW_BIT) // The character is a raw scancode and not a normal character + #define GKEYSTATE_SHIFT_L (1<<GKEYSTATE_SHIFT_L_BIT) // Left Shift is down + #define GKEYSTATE_SHIFT_R (1<<GKEYSTATE_SHIFT_R_BIT) // Right Shift is down + #define GKEYSTATE_CTRL_L (1<<GKEYSTATE_CTRL_L_BIT) // Left Control is down + #define GKEYSTATE_CTRL_R (1<<GKEYSTATE_CTRL_R_BIT) // Right Control is down + #define GKEYSTATE_APPLE_CMD_L (1<<GKEYSTATE_CTRL_L_BIT) // Left Cmd (apple keyboard) is down + #define GKEYSTATE_APPLE_CMD_R (1<<GKEYSTATE_CTRL_R_BIT) // Right Cmd (apple keyboard) is down + #define GKEYSTATE_ALT_L (1<<GKEYSTATE_ALT_L_BIT) // Left Alt is down + #define GKEYSTATE_ALT_R (1<<GKEYSTATE_ALT_R_BIT) // Right Alt is down + #define GKEYSTATE_APPLE_OPTION_L (1<<GKEYSTATE_ALT_L_BIT) // Left Option (apple keyboard) is down + #define GKEYSTATE_APPLE_OPTION_R (1<<GKEYSTATE_ALT_R_BIT) // Right Option (apple keyboard) is down + #define GKEYSTATE_FN (1<<GKEYSTATE_FN_BIT) // Fn is down + #define GKEYSTATE_COMPOSE (1<<GKEYSTATE_COMPOSE_BIT) // Compose is down + #define GKEYSTATE_WINKEY (1<<GKEYSTATE_WINKEY_BIT) // WinKey is down + #define GKEYSTATE_APPLE_CTRL (1<<GKEYSTATE_WINKEY_BIT) // Control key on apple keyboard is down + #define GKEYSTATE_CAPSLOCK (1<<GKEYSTATE_CAPSLOCK_BIT) // CapsLock is on + #define GKEYSTATE_NUMLOCK (1<<GKEYSTATE_NUMLOCK_BIT) // NumLock is on + #define GKEYSTATE_SCROLLLOCK (1<<GKEYSTATE_SCROLLLOCK_BIT) // ScrollLock is on + #define GKEYSTATE_LAYOUT_FIRST (1<<GKEYSTATE_LAYOUT_FIRST_BIT) // 4 bits for extra states for the key Layout + #define GKEYSTATE_SYSTEM_FIRST (1<<GKEYSTATE_SYSTEM_FIRST_BIT) // 1st available flag for the system's use (maximum 4 bits) + #define GKEYSTATE_DRIVER_FIRST (1<<GKEYSTATE_DRIVER_FIRST_BIT) // 1st available flag for the driver's use (maximum 7 bits) + #define GKEYSTATE_MISSED_EVENT (1<<GKEYSTATE_MISSED_EVENT_BIT) // A keyboard event has been missed + + #define GKEYSTATE_SHIFT (GKEYSTATE_SHIFT_L|GKEYSTATE_SHIFT_R) // A shift key is down (left or right) + #define GKEYSTATE_CTRL (GKEYSTATE_CTRL_L|GKEYSTATE_CTRL_R) // A control key is down (left or right) + #define GKEYSTATE_ALT (GKEYSTATE_ALT_L|GKEYSTATE_ALT_R) // A alt key is down (left or right) + #define GKEYSTATE_APPLE_OPTION (GKEYSTATE_APPLE_OPTION_L|GKEYSTATE_APPLE_OPTION_R) // A apple option key is down (left or right) + #define GKEYSTATE_APPLE_CMD (GKEYSTATE_APPLE_CMD_L|GKEYSTATE_APPLE_CMD_R) // A apple cmd key is down (left or right) } GEventKeyboard; // Keyboard Listen Flags - passed to geventAddSourceToListener() -#define GLISTEN_KEYREPEATS 0x0001 // Return key repeats (where the key is held down to get a repeat character) -#define GLISTEN_KEYCODES 0x0002 // Return all key presses including extended code key presses (not just ascii codes) -#define GLISTEN_KEYALL 0x0004 // Return keyup's, keydown's and everything in between (but not repeats unless GLISTEN_KEYREPEATS is set). -#define GLISTEN_KEYSINGLE 0x8000 // Return only when one particular extended code key is pressed or released. The particular extended code is OR'd into this value - // eg. (GLISTEN_KEYSINGLE | GKEY_CR) - // No other flags may be set with this flag. +#define GLISTEN_KEYREPEATSOFF 0x0001 // Ignore key repeats (if possible) +#define GLISTEN_KEYNOSPECIALS 0x0002 // Ignore special keys +#define GLISTEN_KEYUP 0x0004 // Return keyup's as well as key down events +#define GLISTEN_KEYTRANSITIONS 0x0008 // Return transitions to the key state +#define GLISTEN_KEYRAW 0x0010 // Return raw scan-codes. This turns off normal character processing. + +// All keyboards +#define GKEYBOARD_ALL_INSTANCES ((unsigned)-1) /*===========================================================================*/ /* External declarations. */ @@ -105,7 +182,7 @@ extern "C" { * * @return The source handle of the created input instance */ - GSourceHandle ginputGetKeyboard(uint16_t instance); + GSourceHandle ginputGetKeyboard(unsigned instance); /** * @brief Get the current keyboard status @@ -115,7 +192,19 @@ extern "C" { * * @return Returns FALSE on an error (eg invalid instance) */ - bool_t ginputGetKeyboardStatus(uint16_t instance, GEventKeyboard *pkeyboard); + bool_t ginputGetKeyboardStatus(unsigned instance, GEventKeyboard *pkeyboard); + + #if !GKEYBOARD_LAYOUT_OFF || defined(__DOXYGEN__) + /** + * @brief Set the keyboard layout + * + * @param[in] instance The ID of the keyboard input instance + * @param[in] pLayout The keyboard layout micro-code. Passing NULL defaults to the driver's default layout. + * + * @return Returns FALSE on an error (eg invalid instance) + */ + bool_t ginputSetKeyboardLayout(unsigned instance, const void *pLayout); + #endif #ifdef __cplusplus } @@ -125,4 +214,3 @@ extern "C" { #endif /* _GINPUT_KEYBOARD_H */ /** @} */ - diff --git a/src/ginput/keyboard_microcode.c b/src/ginput/keyboard_microcode.c new file mode 100644 index 00000000..e3c04d5f --- /dev/null +++ b/src/ginput/keyboard_microcode.c @@ -0,0 +1,73 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/ginput/keyboard_microcode.c + * @brief GINPUT keyboard standard microcode definitions. + */ + +#include "gfx.h" + +#if GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD && !GKEYBOARD_LAYOUT_OFF + +#include "keyboard_microcode.h" + +#if GKEYBOARD_LAYOUT_SCANCODE2_US + + #error "Keyboard Layout SCANCODE2_US is not fully implemented yet" + + uint8_t KeyboardLayout_ScancodeSet2_US[] = { + KMC_HEADERSTART, KMC_HEADER_ID1, KMC_HEADER_ID2, KMC_HEADER_VER_1, + + KMC_RECORDSTART, 0x03, // Handle E0 codes (ignore for now assuming a single character) + KMC_TEST_LASTCODE, 0xE0, + KMC_ACT_DONE, + KMC_RECORDSTART, 0x03, + KMC_TEST_CODE, 0xE0, + KMC_ACT_STOP, + + KMC_RECORDSTART, 0x03, // Handle E1 codes (ignore for now assuming a single character) + KMC_TEST_LASTCODE, 0xE1, + KMC_ACT_DONE, + KMC_RECORDSTART, 0x03, + KMC_TEST_CODE, 0xE1, + KMC_ACT_STOP, + + KMC_RECORDSTART, 0x03, // Handle E2 codes (ignore for now assuming a single character) + KMC_TEST_LASTCODE, 0xE2, + KMC_ACT_DONE, + KMC_RECORDSTART, 0x03, + KMC_TEST_CODE, 0xE2, + KMC_ACT_STOP, + + KMC_RECORDSTART, 0x06, // KeyUp + KMC_TEST_CODEBIT, 0x80, + KMC_ACT_STATEBIT, GKEYSTATE_KEYUP_BIT, + KMC_ACT_CODEBIT, 0x80 | KMC_BIT_CLEAR, + + KMC_RECORDSTART, 0x05, // CapsLock (on keyup to avoid repeats) + KMC_TEST_CODE, 0x58, + KMC_TEST_STATEBIT, GKEYSTATE_KEYUP_BIT | KMC_BIT_CLEAR, + KMC_ACT_DONE, + KMC_RECORDSTART, 0x05, + KMC_TEST_CODE, 0x58, + KMC_ACT_STATEBIT, GKEYSTATE_CAPSLOCK_BIT | KMC_BIT_INVERT, + KMC_ACT_DONE, + + KMC_RECORDSTART, 0x05, // Detect Shift Keys + //KMC_ACT_LAYOUTBIT, SCANCODESET2_LAYOUT_E0_BIT | KMC_BIT_CLEAR, + KMC_ACT_STOP, + + KMC_RECORDSTART, 0x03, + KMC_ACT_CHARRANGE, 0x00, + KMC_ACT_DONE, + + KMC_RECORDSTART, 0x00, + }; +#endif // GKEYBOARD_LAYOUT_SCANCODE2_US + +#endif // GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD && !GKEYBOARD_LAYOUT_OFF diff --git a/src/ginput/keyboard_microcode.h b/src/ginput/keyboard_microcode.h new file mode 100644 index 00000000..c18e94e5 --- /dev/null +++ b/src/ginput/keyboard_microcode.h @@ -0,0 +1,107 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/ginput/keyboard_microcode.h + * @brief GINPUT keyboard layout microcode definition. + */ + +#ifndef _KEYBOARD_MICROCODE_H +#define _KEYBOARD_MICROCODE_H + +/* + * Keyboard Layout Microcode Definition + * + * Each time a byte is received from the keyboard it is processed through the layout microcode engine. This enables conversion from + * scancodes to ascii, internationalization, and various other tricky keyboard behavior. + * Note a "code" is defined as a single byte of data from the keyboard, a "scancode" is one or more "codes" that are sent in response to one keyboard press or release. + * + * The layout microcode can even be switched on the fly by the application to effect such changes as changing from US English to Russian layouts. + * They could conceivably even be loaded from disk at run-time. + * + * In the interest of efficiency there is very little error checking. Make sure your layout microcode has been debugged properly before releasing + * to production code. + * + * Layout microcode consists of a header followed by 1 or more records. + * + * The header consists of a KMC_HEADERSTART and associated bytes. The is used only to check it looks like it might be layout microcode and to specify the + * version of microcode. Future versions of layout microcode will always have the same header at least to the version number. + * + * Each record is delimited by a KMC_RECORDSTART. Each record can contain a maximum of 255 bytes. + * A record length of zero indicates the end of the layout microcode. + * + * A record consists a mixture of tests and actions (normally the tests are first). If a test fails the rest of this record is skipped and the next + * record is processed. This has the effect of AND'ing multiple tests that occur together. + * KMC_TEST_INIT and KMC_TEST_ERROR are special. These tests must be the first byte in their respective record as without this test being there, there + * is an implicit test that a code has actually been received. + * If no records have successful tests for this code then by extension no actions are executed. That is, the code is ignored. + * After fully processing a record, the next record is processed. This can be prevented by using the KMC_ACT_STOP action. When encountered all processing + * on this code stops. + * + * Some tests set a pseudo variable called "diff". This is then used by some actions. At the start of a new record "diff" is set to "code" (or 0 for the init and + * error conditions). + * Some tests and actions do bit operations on either the saved key-state or on the code itself. Bit numbers (which can range from 0 to 31) test or affect the + * "set" state of the bit. OR'ing KMC_BIT_CLEAR with the bit number test or affect the "clear" state of the bit. For example, KMC_ACT_STATEBIT with a parameter + * of 10 will set bit 10 of the key-state. KMC_ACT_STATEBIT with a parameter of (10 | KMC_BIT_CLEAR) will clear bit 10 of the key-state. + * + */ + +#define KMC_HEADERSTART 0x00 // Followed by: ID1 ID2 VER - This is the start of layout microcode. + #define KMC_HEADER_ID1 'L' + #define KMC_HEADER_ID2 'M' + #define KMC_HEADER_VER_1 0x01 + + #define KMC_HEADER_VER_CURRENT KMC_HEADER_VER_1 // The current version number + #define KMC_HEADER_VER_MIN KMC_HEADER_VER_1 // The minimum version number accepted + #define KMC_HEADER_VER_MAX KMC_HEADER_VER_1 // The maximum version number accepted + +#define KMC_RECORDSTART 0x01 // Followed by: nn b0 b1 ... b(nn-1) - nn bytes of test and action follow, nn = 00 means end of all tests + +#define KMC_TEST_INIT 0x10 // Followed by: nothing - The layout is initializing +#define KMC_TEST_ERROR 0x11 // Followed by: nothing - The keyboard has signaled an error +#define KMC_TEST_CODE 0x12 // Followed by: aa - Code must equal aa. Diff is set to 0 +#define KMC_TEST_CODERANGE 0x13 // Followed by: aa bb - Code must be between aa and bb (inclusive). Diff is set to (code - aa) +#define KMC_TEST_CODETABLE 0x14 // Followed by: n m1 m2 ... - Code must equal an m value. There are n possible m values. Diff is set to 0 to n-1 (the match position) +#define KMC_TEST_STATEBIT 0x15 // Followed by: b - Test if a key-state bit is set/clear. b = 0 to 31 or b = (0 | KMC_BIT_CLEAR) to (31 | KMC_BIT_CLEAR) +#define KMC_TEST_STATEOR 0x16 // Followed by: b1 b2 - Test two key-state bits and OR the result +#define KMC_TEST_STATEAND 0x17 // Followed by: b1 b2 - Test two key-state bits and AND the result +#define KMC_TEST_LAYOUTBIT 0x18 // Followed by: b - Test if a layout bit is set/clear. b = 0 to 15 or b = (0 | KMC_BIT_CLEAR) to (15 | KMC_BIT_CLEAR) +#define KMC_TEST_LAYOUTOR 0x19 // Followed by: b1 b2 - Test two layout bits and OR the result +#define KMC_TEST_LAYOUTAND 0x1A // Followed by: b1 b2 - Test two layout bits and AND the result +#define KMC_TEST_CODEBIT 0x1B // Followed by: b - Test if a code bit is set/clear. b = 0 to 7 or b = (0 | KMC_BIT_CLEAR) to (7 | KMC_BIT_CLEAR) +#define KMC_TEST_CODEOR 0x1C // Followed by: b1 b2 - Test two code bits and OR the result +#define KMC_TEST_CODEAND 0x1D // Followed by: b1 b2 - Test two code bits and AND the result +#define KMC_TEST_LASTCODE 0x1E // Followed by: aa - Test if the last scancode was aa +#define KMC_TEST_SHIFT 0x20 // Followed by: nothing - Test if a shift key is down +#define KMC_TEST_NOSHIFT 0x21 // Followed by: nothing - Test if a shift key is not down +#define KMC_TEST_CTRL 0x22 // Followed by: nothing - Test if a control key is down +#define KMC_TEST_NOCTRL 0x23 // Followed by: nothing - Test if a control key is not down +#define KMC_TEST_ALT 0x24 // Followed by: nothing - Test if an alt key is down +#define KMC_TEST_NOALT 0x25 // Followed by: nothing - Test if an alt key is not down +#define KMC_TEST_CAPS 0x26 // Followed by: nothing - Test if capslock as modified by shift is active +#define KMC_TEST_NOCAPS 0x27 // Followed by: nothing - Test if capslock as modified by shift is not active +#define KMC_TEST_NUMLOCK 0x28 // Followed by: nothing - Test if numlock is active +#define KMC_TEST_NONUMLOCK 0x29 // Followed by: nothing - Test if numlock is not active + +#define KMC_ACT_STOP 0xFF // Followed by: nothing - Stop processing this code +#define KMC_ACT_DONE 0xFE // Followed by: nothing - Finished processing this scancode sequence. (also implies STOP) +#define KMC_ACT_RESET 0xFD // Followed by: nothing - Empty all buffers +#define KMC_ACT_STATEBIT 0x80 // Followed by: b - Set or clear bit b in key-state. b = 0 to 31 or b = (0 | KMC_BIT_CLEAR) to (31 | KMC_BIT_CLEAR) +#define KMC_ACT_LAYOUTBIT 0x81 // Followed by: b - Set or clear bit b in layout bits. b = 0 to 15 or b = (0 | KMC_BIT_CLEAR) to (15 | KMC_BIT_CLEAR) +#define KMC_ACT_CODEBIT 0x82 // Followed by: b - Set or clear bit b in code. b = 0 to 7 or b = (0 | KMC_BIT_CLEAR) to (7 | KMC_BIT_CLEAR) +#define KMC_ACT_CHAR 0x83 // Followed by: nn - Append char nn to output buffer +#define KMC_ACT_CHARCODE 0x84 // Followed by: nothing - Append the code to output buffer +#define KMC_ACT_CHARRANGE 0x85 // Followed by: nn - Append char nn + Diff to output +#define KMC_ACT_CHARTABLE 0x86 // Followed by: n c1 c2 ... - Append char to output based on c[Diff]. If Diff is greater than n then nothing is appended. +#define KMC_ACT_CLEAR 0x87 // Followed by: nothing - Clear the output buffer +#define KMC_ACT_CHARADD 0x88 // Followed by: nn - Multiple the last char in output buffer by nn (assume 0 if none) and add Diff +#define KMC_ACT_DATA 0x89 // Followed by: nn - Send nn back to the keyboard + +#define KMC_BIT_CLEAR 0x80 // The bit number is being used for a bit clear operation rather than a bit set operation. +#define KMC_BIT_INVERT 0x40 // Invert the bit rather than setting or clearing it. + +#endif /* _KEYBOARD_MICROCODE_H */ diff --git a/src/ginput/sys_make.mk b/src/ginput/sys_make.mk index 9abe1645..6adb2e4c 100644 --- a/src/ginput/sys_make.mk +++ b/src/ginput/sys_make.mk @@ -1,5 +1,6 @@ GFXSRC += $(GFXLIB)/src/ginput/ginput_ginput.c \ $(GFXLIB)/src/ginput/ginput_mouse.c \ $(GFXLIB)/src/ginput/ginput_keyboard.c \ + $(GFXLIB)/src/ginput/keyboard_microcode.c \ $(GFXLIB)/src/ginput/ginput_toggle.c \ $(GFXLIB)/src/ginput/ginput_dial.c diff --git a/src/ginput/sys_options.h b/src/ginput/sys_options.h index 1841519c..9c1b0d30 100644 --- a/src/ginput/sys_options.h +++ b/src/ginput/sys_options.h @@ -170,7 +170,51 @@ #if defined(__DOXYGEN__) #define GMOUSE_DRIVER_LIST GMOUSEVMT_Win32, GMOUSEVMT_Win32 #endif - + /** + * @brief Milliseconds between keyboard polls. + * @details Defaults to 200 milliseconds + * @note How often keyboards should be polled. + */ + #ifndef GINPUT_KEYBOARD_POLL_PERIOD + #define GINPUT_KEYBOARD_POLL_PERIOD 200 + #endif + /** + * @brief Define multiple static keyboards + * @details When not defined the system automatically detects a single linked keyboard driver + * @note The references to GKEYBOARDVMT_Win32 in the definition would be replaced + * by the names of the VMT for each of the static keyboards you want to + * include. + * @note Dynamic keyboards associated automatically with a display eg Win32, X or GFXnet + * do not need to be specified in this list as the display driver will register + * them automatically as the display is created. + */ + #if defined(__DOXYGEN__) + #define GKEYBOARD_DRIVER_LIST GMOUSEVMT_Win32, GMOUSEVMT_Win32 + #endif + /** + * @brief Turn off the layout engine. + * @details When defined the layout engine is removed from the code and characters + * are passed directly from the keyboard driver to the application. + * @note Turning off the layout engine just saves code if it is not needed. + */ + #ifndef GKEYBOARD_LAYOUT_OFF + #define GKEYBOARD_LAYOUT_OFF FALSE + #endif + /** + * @brief Various Keyboard Layouts that can be included. + * @details A keyboard layout controls conversion of scancodes to characters + * and enables one keyboard to have multiple language mappings. + * @note Defining a layout does not make it active. The keyboard driver + * must have it active as the default or the application must + * use @p ginputSetKeyboardLayout() to set the active layout. + * @note Multiple layouts can be included but only one will be active + * at a time (per keyboard). + * @{ + */ + #ifndef GKEYBOARD_LAYOUT_SCANCODE2_US + #define GKEYBOARD_LAYOUT_SCANCODE2_US FALSE // US Keyboard using the ScanCode 2 set. + #endif + /** @} */ /** @} */ #endif /* _GINPUT_OPTIONS_H */ |