/* * 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 #include "log.h" #include "queue.h" #include "async_alarm.h" #include "brl_base.h" #include "brl_utils.h" #include "brl_dots.h" #include "prefs.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); } void applyBrailleDisplayOrientation (unsigned char *cells, size_t count) { switch (prefs.brailleDisplayOrientation) { case BRL_ORIENTATION_ROTATED: { static TranslationTable rotateTable = {[1] = 0}; const unsigned char *source = cells; const unsigned char *end = source + count; unsigned char buffer[count]; unsigned char *target = &buffer[count]; if (!rotateTable[1]) { makeTranslationTable(dotsTable_rotated, rotateTable); } while (source < end) *--target = rotateTable[*source++]; memcpy(cells, buffer, count); break; } default: case BRL_ORIENTATION_NORMAL: break; } } 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 ) { unsigned char *bytes = packet; size_t count = 0; size_t length = 1; int started = 0; if (!endpoint) endpoint = brl->gioEndpoint; while (1) { unsigned char byte; if (!gioReadByte(endpoint, &byte, started)) { if (count > 0) logPartialPacket(bytes, count); return 0; } gotByte: started = 1; if (count < size) { bytes[count++] = byte; { BraillePacketVerifierResult result = verifyPacket(brl, bytes, count, &length, data); switch (result) { case BRL_PVR_EXCLUDE: count -= 1; case BRL_PVR_INCLUDE: break; default: logMessage(LOG_WARNING, "unimplemented braille packet verifier result: %u", result); /* fall through */ case BRL_PVR_INVALID: started = 0; if (--count) { logShortPacket(bytes, count); count = 0; length = 1; 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); 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; } 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 ) { if (brl->api) { if (brl->api->handleKeyEvent(group, number, press)) { return 1; } } if (brl->keyTable) { switch (prefs.brailleDisplayOrientation) { case BRL_ORIENTATION_ROTATED: if (brl->rotateInput) brl->rotateInput(brl, &group, &number); break; default: case BRL_ORIENTATION_NORMAL: break; } 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); }