/* * 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/ginput_keyboard.c * @brief GINPUT keyboard code. */ // We need to include stdio.h below for MICROCODE_DEBUG. Turn off GFILE_NEED_STDIO just for this file to prevent conflicts #define GFILE_NEED_STDIO_MUST_BE_OFF #include "../../gfx.h" #if GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD #define MICROCODE_DEBUG FALSE #if MICROCODE_DEBUG #include #endif // Get the keyboard driver interface #include "ginput_driver_keyboard.h" #include "ginput_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); } } } void _gkeyboardInit(void) { // GINPUT_KEYBOARD_DRIVER_LIST is defined - create each driver instance #if defined(GINPUT_KEYBOARD_DRIVER_LIST) { int i; typedef const GKeyboardVMT const GKEYBOARDVMTLIST[1]; extern GKEYBOARDVMTLIST GINPUT_KEYBOARD_DRIVER_LIST; static const GKeyboardVMT * const 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 const GKeyboardVMT const GKEYBOARDVMT_OnlyOne[1]; 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 */