/* * 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 #include #include #ifndef MB_LEN_MAX #define MB_LEN_MAX 16 #endif /* MB_LEN_MAX */ #ifdef HAVE_ICONV_H #include static iconv_t conversionDescriptor = NULL; #endif /* HAVE_ICONV_H */ #include "log.h" #include "parse.h" #include "charset.h" #include "unicode.h" #include "get_curses.h" #ifdef GOT_CURSES #define newLine() addch('\n') #else /* GOT_CURSES */ #define addstr(string) serialWriteData(ttyDevice, string, strlen(string)) #define addch(character) do { unsigned char __c = (character); serialWriteData(ttyDevice, &__c, 1); } while(0) #define getch() getch_noCurses() #define newLine() addstr("\r\n") #endif /* GOT_CURSES */ #ifdef GOT_CURSES #define BRLPARM_TERM "term", #else /* GOT_CURSES */ #define BRLPARM_TERM #endif /* GOT_CURSES */ #ifdef HAVE_ICONV_H #define BRLPARM_CHARSET "charset", #else /* HAVE_ICONV_H */ #define BRLPARM_CHARSET #endif /* HAVE_ICONV_H */ typedef enum { PARM_BAUD, #ifdef GOT_CURSES PARM_TERM, #endif /* GOT_CURSES */ PARM_LINES, PARM_COLUMNS, #ifdef HAVE_ICONV_H PARM_CHARSET, #endif /* HAVE_ICONV_H */ PARM_LOCALE } DriverParameter; #define BRLPARMS "baud", BRLPARM_TERM "lines", "columns", BRLPARM_CHARSET "locale" #include "brl_driver.h" #include "braille.h" #include "io_serial.h" #define MAX_WINDOW_LINES 3 #define MAX_WINDOW_COLUMNS 80 #define MAX_WINDOW_SIZE (MAX_WINDOW_LINES * MAX_WINDOW_COLUMNS) static SerialDevice *ttyDevice = NULL; static FILE *ttyStream = NULL; static char *classificationLocale = NULL; #ifdef GOT_CURSES static SCREEN *ttyScreen = NULL; #else /* GOT_CURSES */ static inline int getch_noCurses (void) { unsigned char c; if (serialReadData(ttyDevice, &c, 1, 0, 0) == 1) return c; return EOF; } #endif /* GOT_CURSES */ static int brl_construct (BrailleDisplay *brl, char **parameters, const char *device) { unsigned int ttyBaud = 9600; const char *ttyType = "vt100"; int windowLines = 1; int windowColumns = 40; #ifdef HAVE_ICONV_H const char *characterSet = getLocaleCharset(); #endif /* HAVE_ICONV_H */ if (!isSerialDeviceIdentifier(&device)) { unsupportedDeviceIdentifier(device); return 0; } { unsigned int baud = ttyBaud; if (serialValidateBaud(&baud, "TTY baud", parameters[PARM_BAUD], NULL)) { ttyBaud = baud; } } #ifdef GOT_CURSES if (*parameters[PARM_TERM]) { ttyType = parameters[PARM_TERM]; } #endif /* GOT_CURSES */ { static const int minimum = 1; static const int maximum = MAX_WINDOW_LINES; int lines = windowLines; if (validateInteger(&lines, parameters[PARM_LINES], &minimum, &maximum)) { windowLines = lines; } else { logMessage(LOG_WARNING, "%s: %s", "invalid line count", parameters[PARM_LINES]); } } { static const int minimum = 1; static const int maximum = MAX_WINDOW_COLUMNS; int columns = windowColumns; if (validateInteger(&columns, parameters[PARM_COLUMNS], &minimum, &maximum)) { windowColumns = columns; } else { logMessage(LOG_WARNING, "%s: %s", "invalid column count", parameters[PARM_COLUMNS]); } } #ifdef HAVE_ICONV_H if (*parameters[PARM_CHARSET]) { characterSet = parameters[PARM_CHARSET]; } #endif /* HAVE_ICONV_H */ if (*parameters[PARM_LOCALE]) { classificationLocale = parameters[PARM_LOCALE]; } #ifdef HAVE_ICONV_H if ((conversionDescriptor = iconv_open(characterSet, "WCHAR_T")) != (iconv_t)-1) { #endif /* HAVE_ICONV_H */ if ((ttyDevice = serialOpenDevice(device))) { if (serialRestartDevice(ttyDevice, ttyBaud)) { #ifdef GOT_CURSES if ((ttyStream = serialGetStream(ttyDevice))) { if ((ttyScreen = newterm(ttyType, ttyStream, ttyStream))) { cbreak(); noecho(); nonl(); nodelay(stdscr, TRUE); intrflush(stdscr, FALSE); keypad(stdscr, TRUE); clear(); refresh(); #endif /* GOT_CURSES */ brl->textColumns = windowColumns; brl->textRows = windowLines; logMessage(LOG_INFO, "TTY: type=%s baud=%u size=%dx%d", ttyType, ttyBaud, windowColumns, windowLines); return 1; #ifdef GOT_CURSES } else { logSystemError("newterm"); } ttyStream = NULL; } #endif /* GOT_CURSES */ } serialCloseDevice(ttyDevice); ttyDevice = NULL; } #ifdef HAVE_ICONV_H iconv_close(conversionDescriptor); } else { logSystemError("iconv_open"); } conversionDescriptor = NULL; #endif /* HAVE_ICONV_H */ return 0; } static void brl_destruct (BrailleDisplay *brl) { #ifdef GOT_CURSES if (ttyScreen) { endwin(); #ifndef __MINGW32__ delscreen(ttyScreen); #endif /* __MINGW32__ */ ttyScreen = NULL; } #endif /* GOT_CURSES */ if (ttyDevice) { ttyStream = NULL; serialCloseDevice(ttyDevice); ttyDevice = NULL; } #ifdef HAVE_ICONV_H if (conversionDescriptor) { iconv_close(conversionDescriptor); conversionDescriptor = NULL; } #endif /* HAVE_ICONV_H */ } static void writeText (const wchar_t *buffer, int columns) { int column; for (column=0; columnbuffer, brl->textColumns*brl->textRows, NULL, NULL, NULL) && (brl->cursor == previousCursor)) { return 1; } previousCursor = brl->cursor; if (classificationLocale) { previousLocale = setlocale(LC_CTYPE, NULL); setlocale(LC_CTYPE, classificationLocale); } else { previousLocale = NULL; } #ifdef GOT_CURSES clear(); #else /* GOT_CURSES */ newLine(); #endif /* GOT_CURSES */ { wchar_t braille[brl->textColumns]; for (unsigned int row=0; rowtextRows; row++) { unsigned int offset = row * brl->textColumns; writeText(&text[offset], brl->textColumns); for (unsigned int column=0; columntextColumns; column+=1) { unsigned char c = brl->buffer[offset + column]; braille[column] = UNICODE_BRAILLE_ROW | (!!(c & BRL_DOT1) << 0) | (!!(c & BRL_DOT2) << 1) | (!!(c & BRL_DOT3) << 2) | (!!(c & BRL_DOT4) << 3) | (!!(c & BRL_DOT5) << 4) | (!!(c & BRL_DOT6) << 5) | (!!(c & BRL_DOT7) << 6) | (!!(c & BRL_DOT8) << 7) ; } newLine(); writeText(braille, brl->textColumns); if (row < (brl->textRows - 1)) { newLine(); } } } #ifdef GOT_CURSES if ((brl->cursor != BRL_NO_CURSOR) && (brl->cursor < (brl->textColumns * brl->textRows))) { move(brl->cursor/brl->textColumns, brl->cursor%brl->textColumns); } else { move(brl->textRows, 0); } refresh(); #else /* GOT_CURSES */ if ((brl->textRows == 1) && (brl->cursor != BRL_NO_CURSOR) && (brl->cursor < brl->textColumns)) { addch('\r'); writeText(text, brl->cursor); } else { newLine(); } #endif /* GOT_CURSES */ if (previousLocale) setlocale(LC_CTYPE, previousLocale); return 1; } static int keyToCommand (BrailleDisplay *brl, KeyTableCommandContext context, int key) { switch (key) { case EOF: return EOF; default: if (key <= 0XFF) return BRL_CMD_CHAR(key); logMessage(LOG_WARNING, "unrecognized curses key: %d", key); return BRL_CMD_NOOP; #ifdef GOT_CURSES #define MAP(key,cmd) case KEY_##key: return BRL_CMD_##cmd MAP(BACKSPACE, KEY(BACKSPACE)); MAP(LEFT, FWINLT); MAP(RIGHT, FWINRT); MAP(UP, LNUP); MAP(DOWN, LNDN); MAP(PPAGE, PRDIFLN); MAP(NPAGE, NXDIFLN); MAP(A3, PRDIFLN); MAP(C3, NXDIFLN); MAP(HOME, TOP); MAP(END, BOT); MAP(A1, TOP); MAP(C1, BOT); MAP(IC, ATTRUP); MAP(DC, ATTRDN); MAP(B2, HOME); MAP(F(1), HELP); MAP(F(2), LEARN); MAP(F(3), INFO); MAP(F(4), PREFMENU); MAP(F(5), PRPROMPT); MAP(F(6), NXPROMPT); MAP(F(7), PRPGRPH); MAP(F(8), NXPGRPH); MAP(F(9), LNBEG); MAP(F(10), CHRLT); MAP(F(11), CHRRT); MAP(F(12), LNEND); #undef MAP #endif /* GOT_CURSES */ } } static int readKey (BrailleDisplay *brl) { int key = getch(); #ifdef GOT_CURSES if (key == ERR) return EOF; #endif /* GOT_CURSES */ if (key != EOF) { logMessage(LOG_CATEGORY(BRAILLE_DRIVER), "curses key: %d", key); } return key; } static int brl_readCommand (BrailleDisplay *brl, KeyTableCommandContext context) { int command = keyToCommand(brl, context, readKey(brl)); if (command != EOF) { logMessage(LOG_CATEGORY(BRAILLE_DRIVER), "command: 0X%04X", command); } return command; }