/* * 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-2021 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.app/ * * This software is maintained by Dave Mielke . */ #include "prologue.h" #include #include #include "log.h" #include "report.h" #include "api_control.h" #include "queue.h" #include "async_alarm.h" #include "brl_base.h" #include "brl_utils.h" #include "brl_dots.h" #include "kbd_keycodes.h" #include "io_generic.h" #include "cmd_queue.h" #include "ktb.h" const DotsTable dotsTable_ISO11548_1 = { BRL_DOT_1, BRL_DOT_2, BRL_DOT_3, BRL_DOT_4, BRL_DOT_5, BRL_DOT_6, BRL_DOT_7, BRL_DOT_8 }; const DotsTable dotsTable_rotated = { BRL_DOT_8, BRL_DOT_6, BRL_DOT_5, BRL_DOT_7, BRL_DOT_3, BRL_DOT_2, BRL_DOT_4, BRL_DOT_1 }; void makeTranslationTable (const DotsTable dots, TranslationTable table) { int byte; for (byte=0; byte=0; byte--) to[from[byte]] = byte; } static inline void * translateCells ( const TranslationTable table, unsigned char *target, const unsigned char *source, size_t count ) { if (table) { while (count--) *target++ = table[*source++]; return target; } if (target == source) return target + count; return mempcpy(target, source, count); } static inline unsigned char translateCell (const TranslationTable table, unsigned char cell) { return table? table[cell]: cell; } static TranslationTable internalOutputTable; static const unsigned char *outputTable; void setOutputTable (const TranslationTable table) { outputTable = table; } void makeOutputTable (const DotsTable dots) { if (memcmp(dots, dotsTable_ISO11548_1, DOTS_TABLE_SIZE) == 0) { outputTable = NULL; } else { makeTranslationTable(dots, internalOutputTable); outputTable = internalOutputTable; } } void * translateOutputCells (unsigned char *target, const unsigned char *source, size_t count) { return translateCells(outputTable, target, source, count); } unsigned char translateOutputCell (unsigned char cell) { return translateCell(outputTable, cell); } static TranslationTable internalInputTable; static const unsigned char *inputTable; void makeInputTable (void) { if (outputTable) { reverseTranslationTable(outputTable, internalInputTable); inputTable = internalInputTable; } else { inputTable = NULL; } } void * translateInputCells (unsigned char *target, const unsigned char *source, size_t count) { return translateCells(inputTable, target, source, count); } unsigned char translateInputCell (unsigned char cell) { return translateCell(inputTable, cell); } int awaitBrailleInput (BrailleDisplay *brl, int timeout) { return gioAwaitInput(brl->gioEndpoint, timeout); } int connectBrailleResource ( BrailleDisplay *brl, const char *identifier, const GioDescriptor *descriptor, BrailleSessionInitializer *initializeSession ) { if ((brl->gioEndpoint = gioConnectResource(identifier, descriptor))) { if (!initializeSession || initializeSession(brl)) { if (gioDiscardInput(brl->gioEndpoint)) { return 1; } } gioDisconnectResource(brl->gioEndpoint); brl->gioEndpoint = NULL; } return 0; } void disconnectBrailleResource ( BrailleDisplay *brl, BrailleSessionEnder *endSession ) { if (brl->gioEndpoint) { if (endSession) endSession(brl); drainBrailleOutput(brl, 0); gioDisconnectResource(brl->gioEndpoint); brl->gioEndpoint = NULL; } } size_t readBraillePacket ( BrailleDisplay *brl, GioEndpoint *endpoint, void *packet, size_t size, BraillePacketVerifier *verifyPacket, void *data ) { if (!endpoint) endpoint = brl->gioEndpoint; unsigned char *bytes = packet; size_t count = 0; size_t length = 1; int started = 0; while (1) { unsigned char byte; if (!gioReadByte(endpoint, &byte, started)) { if (count > 0) logPartialPacket(bytes, count); return 0; } gotByte: if (count < size) { bytes[count++] = byte; BraillePacketVerifierResult result = verifyPacket(brl, bytes, count, &length, data); switch (result) { case BRL_PVR_EXCLUDE: count -= 1; /* fall through */ case BRL_PVR_INCLUDE: started = 1; break; case BRL_PVR_IGNORE: count -= 1; continue; default: logMessage(LOG_WARNING, "unimplemented braille packet verifier result: %u", result); /* fall through */ case BRL_PVR_INVALID: started = 0; length = 1; if (--count) { logShortPacket(bytes, count); count = 0; goto gotByte; } logIgnoredByte(byte); continue; } if (count >= length) { logInputPacket(bytes, length); return length; } } else { if (count++ == size) logTruncatedPacket(bytes, size); logDiscardedByte(byte); } } } int writeBraillePacket ( BrailleDisplay *brl, GioEndpoint *endpoint, const void *packet, size_t size ) { if (!endpoint) endpoint = brl->gioEndpoint; logOutputPacket(packet, size); if (gioWriteData(endpoint, packet, size) == -1) return 0; if (endpoint == brl->gioEndpoint) { brl->writeDelay += gioGetMillisecondsToTransfer(endpoint, size); } return 1; } typedef struct { GioEndpoint *endpoint; int type; size_t size; unsigned char packet[0]; } BrailleMessage; static void logBrailleMessage (BrailleMessage *msg, const char *action) { logBytes(LOG_CATEGORY(OUTPUT_PACKETS), "%s", msg->packet, msg->size, action); } static void deallocateBrailleMessage (BrailleMessage *msg) { free(msg); } static void setBrailleMessageAlarm (BrailleDisplay *brl); static int writeNextBrailleMessage (BrailleDisplay *brl) { int ok = 1; if (brl->acknowledgements.messages) { BrailleMessage *msg = dequeueItem(brl->acknowledgements.messages); if (msg) { int written; logBrailleMessage(msg, "dequeued"); written = writeBraillePacket(brl, msg->endpoint, msg->packet, msg->size); deallocateBrailleMessage(msg); msg = NULL; if (written) { setBrailleMessageAlarm(brl); return 1; } ok = 0; } } if (brl->acknowledgements.alarm) { asyncCancelRequest(brl->acknowledgements.alarm); brl->acknowledgements.alarm = NULL; } return ok; } int acknowledgeBrailleMessage (BrailleDisplay *brl) { logMessage(LOG_CATEGORY(OUTPUT_PACKETS), "acknowledged"); brl->acknowledgements.missing.count = 0; return writeNextBrailleMessage(brl); } ASYNC_ALARM_CALLBACK(handleBrailleMessageTimeout) { BrailleDisplay *brl = parameters->data; asyncDiscardHandle(brl->acknowledgements.alarm); brl->acknowledgements.alarm = NULL; if ((brl->acknowledgements.missing.count += 1) < brl->acknowledgements.missing.limit) { logMessage(LOG_WARNING, "missing braille message acknowledgement"); writeNextBrailleMessage(brl); } else { logMessage(LOG_WARNING, "too many missing braille message acknowledgements"); brl->hasFailed = 1; } } static void setBrailleMessageAlarm (BrailleDisplay *brl) { if (brl->acknowledgements.alarm) { asyncResetAlarmIn(brl->acknowledgements.alarm, brl->acknowledgements.missing.timeout); } else { asyncNewRelativeAlarm(&brl->acknowledgements.alarm, brl->acknowledgements.missing.timeout, handleBrailleMessageTimeout, brl); } } static int findOldBrailleMessage (const void *item, void *data) { const BrailleMessage *old = item; const BrailleMessage *new = data; return old->type == new->type; } static void deallocateBrailleMessageItem (void *item, void *data) { BrailleMessage *msg = item; deallocateBrailleMessage(msg); } int writeBrailleMessage ( BrailleDisplay *brl, GioEndpoint *endpoint, int type, const void *packet, size_t size ) { if (brl->acknowledgements.alarm) { BrailleMessage *msg; if (!brl->acknowledgements.messages) { if (!(brl->acknowledgements.messages = newQueue(deallocateBrailleMessageItem, NULL))) { return 0; } } if ((msg = malloc(sizeof(*msg) + size))) { memset(msg, 0, sizeof(*msg)); msg->endpoint = endpoint; msg->type = type; msg->size = size; memcpy(msg->packet, packet, size); { Element *element = findElement(brl->acknowledgements.messages, findOldBrailleMessage, msg); if (element) { logBrailleMessage(getElementItem(element), "unqueued"); deleteElement(element); } } if (enqueueItem(brl->acknowledgements.messages, msg)) { logBrailleMessage(msg, "enqueued"); return 1; } logBrailleMessage(msg, "discarded"); free(msg); } else { logMallocError(); } } else if (writeBraillePacket(brl, endpoint, packet, size)) { setBrailleMessageAlarm(brl); return 1; } return 0; } int probeBrailleDisplay ( BrailleDisplay *brl, unsigned int retryLimit, GioEndpoint *endpoint, int inputTimeout, BrailleRequestWriter *writeRequest, BraillePacketReader *readPacket, void *responsePacket, size_t responseSize, BrailleResponseHandler *handleResponse ) { unsigned int retryCount = 0; if (!endpoint) endpoint = brl->gioEndpoint; while (writeRequest(brl)) { drainBrailleOutput(brl, 0); while (gioAwaitInput(endpoint, inputTimeout)) { size_t size = readPacket(brl, responsePacket, responseSize); if (!size) break; { BrailleResponseResult result = handleResponse(brl, responsePacket, size); switch (result) { case BRL_RSP_DONE: return 1; case BRL_RSP_UNEXPECTED: logUnexpectedPacket(responsePacket, size); case BRL_RSP_CONTINUE: break; default: logMessage(LOG_WARNING, "unimplemented braille response result: %u", result); /* fall through */ case BRL_RSP_FAIL: return 0; } } } if (errno != EAGAIN) #ifdef ETIMEDOUT if (errno != ETIMEDOUT) #endif /* ETIMEDOUT */ break; if (retryCount == retryLimit) break; retryCount += 1; } return 0; } void releaseBrailleKeys (BrailleDisplay *brl) { releaseAllKeys(brl->keyTable); } KeyNumberSet mapKeyNumbers (KeyNumberSet fromKeys, const KeyNumberMapEntry *map, size_t count) { KeyNumberSet toKeys = fromKeys; const KeyNumberMapEntry *end = map + count; while (map < end) { KeyNumber key = map->from; int yes = (key == KTB_KEY_ANY)? 0: isKeyNumberIncluded(fromKeys, key); setKeyNumberIncluded(&toKeys, map->to, yes); map += 1; } return toKeys; } void remapKeyNumbers (KeyNumberSet *keys, const KeyNumberMapEntry *map, size_t count) { *keys = mapKeyNumbers(*keys, map, count); } struct KeyNumberSetMapStruct { const KeyNumberSetMapEntry *linear; const KeyNumberSetMapEntry **sorted; size_t count; }; static int compareKeyNumberSets (KeyNumberSet keys1, KeyNumberSet keys2) { if (keys1 < keys2) return -1; if (keys1 > keys2) return 1; return 0; } static int sortKeyNumberSets (const void *element1, const void *element2) { const KeyNumberSetMapEntry *const *entry1 = element1; const KeyNumberSetMapEntry *const *entry2 = element2; return compareKeyNumberSets((*entry1)->from, (*entry2)->from); } static int searchKeyNumberSetMapEntry (const void *target, const void *element) { const KeyNumberSet *reference = target; const KeyNumberSetMapEntry *const *entry = element; return compareKeyNumberSets(*reference, (*entry)->from); } KeyNumberSetMap * newKeyNumberSetMap (const KeyNumberSetMapEntry *entries, size_t count) { KeyNumberSetMap *map; if ((map = malloc(sizeof(*map)))) { memset(map, 0, sizeof(*map)); map->linear = entries; map->count = count; if (count < 4) { map->sorted = NULL; return map; } if ((map->sorted = malloc(ARRAY_SIZE(map->sorted, count)))) { for (size_t i=0; isorted[i] = &entries[i]; } qsort(map->sorted, map->count, sizeof(*map->sorted), sortKeyNumberSets); return map; } free(map); } logMallocError(); return NULL; } void destroyKeyNumberSetMap (KeyNumberSetMap *map) { if (map->sorted) free(map->sorted); free(map); } KeyNumberSet mapKeyNumberSet (KeyNumberSet keys, const KeyNumberSetMap *map) { if (map) { if (map->sorted) { const KeyNumberSetMapEntry *const *entry = bsearch( &keys, map->sorted, map->count, sizeof(*map->sorted), searchKeyNumberSetMapEntry ); if (entry) return (*entry)->to; } else { const KeyNumberSetMapEntry *entry = map->linear; const KeyNumberSetMapEntry *end = entry + map->count; while (entry < end) { if (keys == entry->from) return entry->to; entry += 1; } } } return keys; } void remapKeyNumberSet (KeyNumberSet *keys, const KeyNumberSetMap *map) { *keys = mapKeyNumberSet(*keys, map); } typedef struct { const KeyGroup group; KeyNumberSet set; } IncludeKeyNumberData; static int includeKeyNumber (const KeyNameEntry *kne, void *data) { if (kne) { IncludeKeyNumberData *ikn = data; if (kne->value.group == ikn->group) ikn->set |= KEY_NUMBER_BIT(kne->value.number); } return 1; } KeyNumberSet makeKeyNumberSet (KEY_NAME_TABLES_REFERENCE keys, KeyGroup group) { IncludeKeyNumberData ikn = { .group = group, .set = 0 }; forEachKeyName(keys, includeKeyNumber, &ikn); return ikn.set; } int enqueueKeyEvent ( BrailleDisplay *brl, KeyGroup group, KeyNumber number, int press ) { report(REPORT_BRAILLE_KEY_EVENT, NULL); if (api.handleKeyEvent(group, number, press)) return 1; if (brl->keyTable) { processKeyEvent(brl->keyTable, getCurrentCommandContext(), group, number, press); return 1; } return 0; } int enqueueKeyEvents ( BrailleDisplay *brl, KeyNumberSet set, KeyGroup group, KeyNumber number, int press ) { while (set) { if (set & 0X1) { if (!enqueueKeyEvent(brl, group, number, press)) { return 0; } } set >>= 1; number += 1; } return 1; } int enqueueKey ( BrailleDisplay *brl, KeyGroup group, KeyNumber number ) { if (enqueueKeyEvent(brl, group, number, 1)) { if (enqueueKeyEvent(brl, group, number, 0)) { return 1; } } return 0; } int enqueueKeys ( BrailleDisplay *brl, KeyNumberSet set, KeyGroup group, KeyNumber number ) { KeyNumber stack[UINT8_MAX + 1]; unsigned char count = 0; while (set) { if (set & 0X1) { if (!enqueueKeyEvent(brl, group, number, 1)) return 0; stack[count++] = number; } set >>= 1; number += 1; } while (count) { if (!enqueueKeyEvent(brl, group, stack[--count], 0)) { return 0; } } return 1; } int enqueueUpdatedKeys ( BrailleDisplay *brl, KeyNumberSet new, KeyNumberSet *old, KeyGroup group, KeyNumber number ) { KeyNumberSet bit = KEY_NUMBER_BIT(0); KeyNumber stack[UINT8_MAX + 1]; unsigned char count = 0; while (*old != new) { if ((new & bit) && !(*old & bit)) { stack[count++] = number; *old |= bit; } else if (!(new & bit) && (*old & bit)) { if (!enqueueKeyEvent(brl, group, number, 0)) return 0; *old &= ~bit; } number += 1; bit <<= 1; } while (count) { if (!enqueueKeyEvent(brl, group, stack[--count], 1)) { return 0; } } return 1; } int enqueueUpdatedKeyGroup ( BrailleDisplay *brl, unsigned int count, const unsigned char *new, unsigned char *old, KeyGroup group ) { KeyNumber pressStack[count]; unsigned char pressCount = 0; KeyNumber base = 0; while (base < count) { KeyNumber number = base; unsigned char bit = 1; while (*old != *new) { unsigned char isPressed = *new & bit; unsigned char wasPressed = *old & bit; if (isPressed && !wasPressed) { *old |= bit; pressStack[pressCount++] = number; } else if (wasPressed && !isPressed) { *old &= ~bit; enqueueKeyEvent(brl, group, number, 0); } if (++number == count) break; bit <<= 1; } old += 1; new += 1; base += 8; } while (pressCount > 0) { enqueueKeyEvent(brl, group, pressStack[--pressCount], 1); } return 1; } int enqueueXtScanCode ( BrailleDisplay *brl, unsigned char key, unsigned char escape, KeyGroup group00, KeyGroup groupE0, KeyGroup groupE1 ) { KeyGroup group; switch (escape) { case XT_MOD_00: group = group00; break; case XT_MOD_E0: group = groupE0; break; case XT_MOD_E1: group = groupE1; break; default: logMessage(LOG_WARNING, "unsupported XT scan code: %02X %02X", escape, key); return 0; } return enqueueKey(brl, group, key); }