diff options
author | inmarket <andrewh@inmarket.com.au> | 2015-01-07 13:20:23 +1000 |
---|---|---|
committer | inmarket <andrewh@inmarket.com.au> | 2015-01-07 13:20:23 +1000 |
commit | 87cca4f7a5d3e79f3e0d3a0d669da22081f7b024 (patch) | |
tree | 190408d9dda3ca72bad7eba2feb6cba96b30eedc /src/ginput/ginput_keyboard.c | |
parent | 128a3b972cc50b084415b5fb69fc355fe5db9520 (diff) | |
download | uGFX-87cca4f7a5d3e79f3e0d3a0d669da22081f7b024.tar.gz uGFX-87cca4f7a5d3e79f3e0d3a0d669da22081f7b024.tar.bz2 uGFX-87cca4f7a5d3e79f3e0d3a0d669da22081f7b024.zip |
New keyboard driver interface with international keyboard support.
Diffstat (limited to 'src/ginput/ginput_keyboard.c')
-rw-r--r-- | src/ginput/ginput_keyboard.c | 575 |
1 files changed, 567 insertions, 8 deletions
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 */ -/** @} */ |