/* * BRLTTY - A background process providing access to the console screen (when in * text mode) for a blind person using a refreshable braille display. * * Copyright (C) 1995-2018 by The BRLTTY Developers. * * BRLTTY comes with ABSOLUTELY NO WARRANTY. * * This is free software, placed under the terms of the * GNU Lesser General Public License, as published by the Free Software * Foundation; either version 2.1 of the License, or (at your option) any * later version. Please see the file LICENSE-LGPL for details. * * Web Page: http://brltty.com/ * * This software is maintained by Dave Mielke . */ #include "prologue.h" #include #include "log.h" #include "parse.h" #include "bitmask.h" #include "kbd.h" #include "kbd_internal.h" const KeyboardProperties anyKeyboard = { .type = KBD_TYPE_Any, .vendor = 0, .product = 0 }; int parseKeyboardProperties (KeyboardProperties *properties, const char *string) { enum { KBD_PARM_TYPE, KBD_PARM_VENDOR, KBD_PARM_PRODUCT }; static const char *const names[] = {"type", "vendor", "product", NULL}; char **parameters = getParameters(names, NULL, string); int ok = 1; logParameters(names, parameters, "Keyboard Property"); *properties = anyKeyboard; if (*parameters[KBD_PARM_TYPE]) { static const KeyboardType types[] = {KBD_TYPE_Any, KBD_TYPE_PS2, KBD_TYPE_USB, KBD_TYPE_Bluetooth}; static const char *choices[] = {"any", "ps2", "usb", "bluetooth", NULL}; unsigned int choice; if (validateChoice(&choice, parameters[KBD_PARM_TYPE], choices)) { properties->type = types[choice]; } else { logMessage(LOG_WARNING, "invalid keyboard type: %s", parameters[KBD_PARM_TYPE]); ok = 0; } } if (*parameters[KBD_PARM_VENDOR]) { static const int minimum = 0; static const int maximum = 0XFFFF; int value; if (validateInteger(&value, parameters[KBD_PARM_VENDOR], &minimum, &maximum)) { properties->vendor = value; } else { logMessage(LOG_WARNING, "invalid keyboard vendor code: %s", parameters[KBD_PARM_VENDOR]); ok = 0; } } if (*parameters[KBD_PARM_PRODUCT]) { static const int minimum = 0; static const int maximum = 0XFFFF; int value; if (validateInteger(&value, parameters[KBD_PARM_PRODUCT], &minimum, &maximum)) { properties->product = value; } else { logMessage(LOG_WARNING, "invalid keyboard product code: %s", parameters[KBD_PARM_PRODUCT]); ok = 0; } } deallocateStrings(parameters); return ok; } int checkKeyboardProperties (const KeyboardProperties *actual, const KeyboardProperties *required) { if (!required) return 1; if (!actual) actual = &anyKeyboard; if (required->type != KBD_TYPE_Any) { if (required->type != actual->type) return 0; } if (required->vendor) { if (required->vendor != actual->vendor) return 0; } if (required->product) { if (required->product != actual->product) return 0; } return 1; } static void logKeyEvent (const char *action, int code, int press) { logMessage(LOG_CATEGORY(KEYBOARD_KEYS), "%s %d: %s", (press? "press": "release"), code, action); } static void flushKeyEvents (KeyboardInstanceObject *kio) { const KeyEventEntry *event = kio->events.buffer; while (kio->events.count) { logKeyEvent("flushing", event->code, event->press); forwardKeyEvent(kio, event->code, event->press); event += 1; kio->events.count -= 1; } memset(kio->deferred.mask, 0, kio->deferred.size); kio->deferred.modifiersOnly = 0; } KeyboardInstanceObject * newKeyboardInstanceObject (KeyboardMonitorObject *kmo) { KeyboardInstanceObject *kio; unsigned int count = BITMASK_ELEMENT_COUNT(keyCodeCount, BITMASK_ELEMENT_SIZE(unsigned char)); size_t size = sizeof(*kio) + count; if ((kio = malloc(size))) { memset(kio, 0, size); kio->kmo = kmo; kio->actualProperties = anyKeyboard; kio->events.buffer = NULL; kio->events.size = 0; kio->events.count = 0; kio->deferred.modifiersOnly = 0; kio->deferred.size = count; if (newKeyboardInstanceExtension(&kio->kix)) { if (enqueueItem(kmo->instanceQueue, kio)) { return kio; } destroyKeyboardInstanceExtension(kio->kix); } free(kio); } else { logMallocError(); } return NULL; } void destroyKeyboardInstanceObject (KeyboardInstanceObject *kio) { flushKeyEvents(kio); if (kio->events.buffer) free(kio->events.buffer); deleteItem(kio->kmo->instanceQueue, kio); if (kio->kix) destroyKeyboardInstanceExtension(kio->kix); free(kio); } void destroyKeyboardMonitorObject (KeyboardMonitorObject *kmo) { kmo->isActive = 0; while (getQueueSize(kmo->instanceQueue) > 0) { Element *element = getQueueHead(kmo->instanceQueue); KeyboardInstanceObject *kio = getElementItem(element); destroyKeyboardInstanceObject(kio); } if (kmo->instanceQueue) deallocateQueue(kmo->instanceQueue); if (kmo->kmx) destroyKeyboardMonitorExtension(kmo->kmx); free(kmo); } KeyboardMonitorObject * newKeyboardMonitorObject (const KeyboardProperties *properties, KeyEventHandler handleKeyEvent) { KeyboardMonitorObject *kmo; if ((kmo = malloc(sizeof(*kmo)))) { memset(kmo, 0, sizeof(*kmo)); kmo->requiredProperties = *properties; kmo->handleKeyEvent = handleKeyEvent; if (newKeyboardMonitorExtension(&kmo->kmx)) { if ((kmo->instanceQueue = newQueue(NULL, NULL))) { if (monitorKeyboards(kmo)) { kmo->isActive = 1; return kmo; } deallocateQueue(kmo->instanceQueue); } destroyKeyboardMonitorExtension(kmo->kmx); } free(kmo); } else { logMallocError(); } return NULL; } void handleKeyEvent (KeyboardInstanceObject *kio, int code, int press) { KeyTableState state = KTS_UNBOUND; logKeyEvent("received", code, press); if (kio->kmo->isActive) { if ((code >= 0) && (code < keyCodeCount)) { const KeyValue *kv = &keyCodeMap[code]; if ((kv->group != KBD_GROUP(SPECIAL)) || (kv->number != KBD_KEY(SPECIAL, Unmapped))) { if ((kv->group == KBD_GROUP(SPECIAL)) && (kv->number == KBD_KEY(SPECIAL, Ignore))) return; state = kio->kmo->handleKeyEvent(kv->group, kv->number, press); } } } if (state == KTS_HOTKEY) { logKeyEvent("ignoring", code, press); } else { typedef enum { WKA_NONE, WKA_CURRENT, WKA_ALL } WriteKeysAction; WriteKeysAction action = WKA_NONE; if (press) { kio->deferred.modifiersOnly = state == KTS_MODIFIERS; if (state == KTS_UNBOUND) { action = WKA_ALL; } else { if (kio->events.count == kio->events.size) { unsigned int newSize = kio->events.size? kio->events.size<<1: 0X1; KeyEventEntry *newBuffer = realloc(kio->events.buffer, (newSize * sizeof(*newBuffer))); if (newBuffer) { kio->events.buffer = newBuffer; kio->events.size = newSize; } else { logMallocError(); } } if (kio->events.count < kio->events.size) { KeyEventEntry *event = &kio->events.buffer[kio->events.count++]; event->code = code; event->press = press; BITMASK_SET(kio->deferred.mask, code); logKeyEvent("deferring", code, press); } else { logKeyEvent("discarding", code, press); } } } else if (kio->deferred.modifiersOnly) { kio->deferred.modifiersOnly = 0; action = WKA_ALL; } else if (BITMASK_TEST(kio->deferred.mask, code)) { KeyEventEntry *to = kio->events.buffer; const KeyEventEntry *from = to; unsigned int count = kio->events.count; while (count) { if (from->code == code) { logKeyEvent("dropping", from->code, from->press); } else if (to != from) { *to++ = *from; } else { to += 1; } from += 1, count -= 1; } kio->events.count = to - kio->events.buffer; BITMASK_CLEAR(kio->deferred.mask, code); } else { action = WKA_CURRENT; } switch (action) { case WKA_ALL: flushKeyEvents(kio); case WKA_CURRENT: logKeyEvent("forwarding", code, press); forwardKeyEvent(kio, code, press); case WKA_NONE: break; } } }