blob: d4e13e82cf43ec637142a7ceba3173c5c6fdec5d [file] [log] [blame] [edit]
/*
* 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-2023 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 <dave@mielke.cc>.
*/
#include "prologue.h"
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include "log.h"
#include "parameters.h"
#include "charset.h"
#include "unicode.h"
#include "brl.h"
#include "ttb.h"
#include "ktb.h"
#include "queue.h"
#include "async_handle.h"
void
constructBrailleDisplay (BrailleDisplay *brl) {
brl->data = NULL;
brl->refreshBrailleDisplay = NULL;
brl->refreshBrailleRow = NULL;
brl->setBrailleFirmness = NULL;
brl->setTouchSensitivity = NULL;
brl->setAutorepeatProperties = NULL;
brl->textColumns = 0;
brl->textRows = 1;
brl->statusColumns = 0;
brl->statusRows = 0;
brl->cellSize = 8;
brl->keyBindings = "all";
brl->keyNames = NULL;
brl->keyTable = NULL;
brl->gioEndpoint = NULL;
brl->writeDelay = 0;
brl->buffer = NULL;
brl->bufferResized = NULL;
brl->rowDescriptors.array = NULL;
brl->rowDescriptors.size = 0;
brl->cursor = BRL_NO_CURSOR;
brl->quality = 0;
brl->noDisplay = 0;
brl->hasFailed = 0;
brl->isOffline = 0;
brl->isSuspended = 0;
brl->isCoreBuffer = 0;
brl->resizeRequired = 0;
brl->hideCursor = 0;
brl->acknowledgements.messages = NULL;
brl->acknowledgements.alarm = NULL;
brl->acknowledgements.missing.timeout = BRAILLE_MESSAGE_ACKNOWLEDGEMENT_TIMEOUT;
brl->acknowledgements.missing.count = 0;
brl->acknowledgements.missing.limit = BRAILLE_MESSAGE_UNACKNOWLEDGEED_LIMIT;
}
static void
destructContractionCache (ContractionCache *cache) {
if (cache->input.characters) {
free(cache->input.characters);
cache->input.characters = NULL;
}
if (cache->output.cells) {
free(cache->output.cells);
cache->output.cells = NULL;
}
if (cache->offsets.array) {
free(cache->offsets.array);
cache->offsets.array = NULL;
}
}
static void
destructBrailleRowDescriptor (BrailleRowDescriptor *brd) {
destructContractionCache(&brd->contracted.cache);
if (brd->contracted.offsets.array) free(brd->contracted.offsets.array);
}
void
destructBrailleDisplay (BrailleDisplay *brl) {
if (brl->acknowledgements.alarm) {
asyncCancelRequest(brl->acknowledgements.alarm);
brl->acknowledgements.alarm = NULL;
}
if (brl->acknowledgements.messages) {
deallocateQueue(brl->acknowledgements.messages);
brl->acknowledgements.messages = NULL;
}
if (brl->keyTable) {
destroyKeyTable(brl->keyTable);
brl->keyTable = NULL;
}
if (brl->rowDescriptors.array) {
while (brl->rowDescriptors.size > 0) {
BrailleRowDescriptor *brd = &brl->rowDescriptors.array[--brl->rowDescriptors.size];
destructBrailleRowDescriptor(brd);
}
free(brl->rowDescriptors.array);
brl->rowDescriptors.array = NULL;
}
if (brl->buffer) {
if (brl->isCoreBuffer) free(brl->buffer);
brl->buffer = NULL;
}
}
static void
fillRegion (
wchar_t *text, unsigned char *dots,
unsigned int start, unsigned int count,
unsigned int columns, unsigned int rows,
void *data, unsigned int length,
void (*fill) (wchar_t *text, unsigned char *dots, void *data)
) {
text += start;
dots += start;
while (rows > 0) {
unsigned int index = 0;
size_t amount = length;
if (amount > count) amount = count;
while (index < amount) {
fill(&text[index], &dots[index], data);
index += 1;
}
length -= amount;
amount = count - index;
wmemset(&text[index], WC_C(' '), amount);
memset(&dots[index], 0, amount);
text += columns;
dots += columns;
rows -= 1;
}
}
static void
fillText (wchar_t *text, unsigned char *dots, void *data) {
wchar_t **character = data;
*dots = convertCharacterToDots(textTable, (*text = *(*character)++));
}
void
fillTextRegion (
wchar_t *text, unsigned char *dots,
unsigned int start, unsigned int count,
unsigned int columns, unsigned int rows,
const wchar_t *characters, size_t length
) {
fillRegion(text, dots, start, count, columns, rows, &characters, length, fillText);
}
static void
fillDots (wchar_t *text, unsigned char *dots, void *data) {
unsigned char **cell = data;
*text = UNICODE_BRAILLE_ROW | (*dots = *(*cell)++);
}
void
fillDotsRegion (
wchar_t *text, unsigned char *dots,
unsigned int start, unsigned int count,
unsigned int columns, unsigned int rows,
const unsigned char *cells, size_t length
) {
fillRegion(text, dots, start, count, columns, rows, &cells, length, fillDots);
}
int
setStatusText (BrailleDisplay *brl, const char *text) {
unsigned int length = brl->statusColumns * brl->statusRows;
if (braille->writeStatus && (length > 0)) {
unsigned char cells[length];
{
unsigned int index = 0;
while (index < length) {
char c;
wint_t wc;
if (!(c = text[index])) break;
if ((wc = convertCharToWchar(c)) == WEOF) wc = WC_C('?');
cells[index++] = convertCharacterToDots(textTable, wc);
}
memset(&cells[index], 0, length-index);
}
if (!braille->writeStatus(brl, cells)) return 0;
}
return 1;
}
int
clearStatusCells (BrailleDisplay *brl) {
return setStatusText(brl, "");
}
static void
brailleBufferResized (BrailleDisplay *brl, int infoLevel) {
logMessage(infoLevel,
"Braille Display Dimensions: %d %s, %d %s",
brl->textColumns, ((brl->textColumns == 1)? "column": "columns"),
brl->textRows, ((brl->textRows == 1)? "row": "rows")
);
memset(brl->buffer, 0, brl->textColumns*brl->textRows);
if (brl->bufferResized) brl->bufferResized(brl->textRows, brl->textColumns);
}
static int
resizeBrailleBuffer (BrailleDisplay *brl, int resized, int infoLevel) {
if (brl->resizeRequired) {
brl->resizeRequired = 0;
resized = 1;
if (brl->isCoreBuffer) {
size_t newSize = brl->textColumns * brl->textRows;
unsigned char *newAddress = malloc(newSize);
if (!newAddress) {
logMallocError();
return 0;
}
if (brl->buffer) free(brl->buffer);
brl->buffer = newAddress;
}
}
if (resized) brailleBufferResized(brl, infoLevel);
return 1;
}
int
ensureBrailleBuffer (BrailleDisplay *brl, int infoLevel) {
brl->resizeRequired = brl->isCoreBuffer = !brl->buffer;
if ((brl->noDisplay = !brl->textColumns)) brl->textColumns = 1;
return resizeBrailleBuffer(brl, 1, infoLevel);
}
int
readBrailleCommand (BrailleDisplay *brl, KeyTableCommandContext context) {
int command = braille->readCommand(brl, context);
resizeBrailleBuffer(brl, 0, LOG_INFO);
return command;
}
int
canRefreshBrailleDisplay (BrailleDisplay *brl) {
return brl->refreshBrailleDisplay != NULL;
}
int
refreshBrailleDisplay (BrailleDisplay *brl) {
if (!canRefreshBrailleDisplay(brl)) return 0;
logMessage(LOG_DEBUG, "refreshing braille display");
return brl->refreshBrailleDisplay(brl);
}
int
canRefreshBrailleRow (BrailleDisplay *brl) {
return brl->refreshBrailleRow != NULL;
}
int
refreshBrailleRow (BrailleDisplay *brl, int row) {
if (!canRefreshBrailleRow(brl)) return 0;
logMessage(LOG_DEBUG, "refreshing braille row: %d", row);
return brl->refreshBrailleRow(brl, row);
}
int
canSetBrailleFirmness (BrailleDisplay *brl) {
return brl->setBrailleFirmness != NULL;
}
int
setBrailleFirmness (BrailleDisplay *brl, BrailleFirmness setting) {
if (!canSetBrailleFirmness(brl)) return 0;
logMessage(LOG_DEBUG, "setting braille firmness: %d", setting);
return brl->setBrailleFirmness(brl, setting);
}
int
canSetTouchSensitivity (BrailleDisplay *brl) {
return brl->setTouchSensitivity != NULL;
}
int
setTouchSensitivity (BrailleDisplay *brl, TouchSensitivity setting) {
if (!canSetTouchSensitivity(brl)) return 0;
logMessage(LOG_DEBUG, "setting touch sensitivity: %d", setting);
return brl->setTouchSensitivity(brl, setting);
}
int
canSetAutorepeatProperties (BrailleDisplay *brl) {
return brl->setAutorepeatProperties != NULL;
}
int
setAutorepeatProperties (BrailleDisplay *brl, int on, int delay, int interval) {
if (!canSetAutorepeatProperties(brl)) return 0;
logMessage(LOG_DEBUG, "setting autorepeat properties: %s Delay:%d Interval:%d",
(on? "on": "off"), delay, interval);
return brl->setAutorepeatProperties(brl, on, delay, interval);
}