/* * 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 #include #include #include #include #include #include #include #ifndef VT_GETHIFONTMASK #define VT_GETHIFONTMASK 0X560D #endif /* VT_GETHIFONTMASK */ #include "log.h" #include "report.h" #include "async_io.h" #include "device.h" #include "io_misc.h" #include "timing.h" #include "parse.h" #include "brl_cmds.h" #include "kbd_keycodes.h" #include "ascii.h" #include "unicode.h" #include "charset.h" #include "scr_gpm.h" #include "system_linux.h" typedef enum { PARM_CHARSET, PARM_FALLBACK_TEXT, PARM_HIGH_FONT_BIT, PARM_LOG_SCREEN_FONT_MAP, PARM_UNICODE, PARM_VIRTUAL_TERMINAL_NUMBER, } ScreenParameters; #define SCRPARMS "charset", "fallbacktext", "hfb", "logsfm", "unicode", "vt" #include "scr_driver.h" #include "screen.h" static const char *problemText; static const char *fallbackText; static unsigned int logScreenFontMap; static unsigned int unicodeEnabled; static int virtualTerminalNumber; #define UNICODE_ROW_DIRECT 0XF000 typedef enum { CONV_OK, CONV_ILLEGAL, CONV_SHORT, CONV_OVERFLOW, CONV_ERROR } CharacterConversionResult; #if defined(HAVE_ICONV_H) #include typedef struct { iconv_t iconvHandle; } CharsetConverter; #define ICONV_NULL ((iconv_t)-1) #define CHARSET_CONVERTER_INITIALIZER {.iconvHandle = ICONV_NULL} static int allocateCharsetConverter (CharsetConverter *converter, const char *sourceCharset, const char *targetCharset) { if (converter->iconvHandle == ICONV_NULL) { if ((converter->iconvHandle = iconv_open(targetCharset, sourceCharset)) == ICONV_NULL) { logSystemError("iconv_open"); return 0; } } return 1; } static void deallocateCharsetConverter (CharsetConverter *converter) { if (converter->iconvHandle != ICONV_NULL) { iconv_close(converter->iconvHandle); converter->iconvHandle = ICONV_NULL; } } static CharacterConversionResult convertCharacters ( CharsetConverter *converter, const char **inputAddress, size_t *inputLength, char **outputAddress, size_t *outputLength ) { ssize_t result = iconv(converter->iconvHandle, (char **)inputAddress, inputLength, outputAddress, outputLength); if (result != -1) return CONV_OK; if (errno == EILSEQ) return CONV_ILLEGAL; if (errno == EINVAL) return CONV_SHORT; if (errno == E2BIG) return CONV_OVERFLOW; logSystemError("iconv"); return CONV_ERROR; } #else /* charset conversion definitions */ typedef struct { char aStructNeedsAtLeastOneField; } CharsetConverter; #define CHARSET_CONVERTER_INITIALIZER {0} static int allocateCharsetConverter (CharsetConverter *converter, const char *sourceCharset, const char *targetCharset) { return 1; } static void deallocateCharsetConverter (CharsetConverter *converter) { } static CharacterConversionResult convertCharacters ( CharsetConverter *converter, const char **inputAddress, size_t *inputLength, char **outputAddress, size_t *outputLength ) { *(*outputAddress)++ = *(*inputAddress)++; *inputLength -= 1; *outputLength -= 1; return CONV_OK; } #endif /* charset conversion definitions */ typedef struct { char *name; unsigned isMultiByte:1; CharsetConverter charsetToWchar; CharsetConverter wcharToCharset; } CharsetEntry; static CharsetEntry *charsetEntries = NULL; static unsigned int charsetCount = 0; static unsigned int charsetIndex = 0; static inline CharsetEntry * getCharsetEntry (void) { return &charsetEntries[charsetIndex]; } static void deallocateCharsetEntries (void) { if (charsetEntries) { while (charsetCount) { CharsetEntry *charset = &charsetEntries[--charsetCount]; free(charset->name); deallocateCharsetConverter(&charset->charsetToWchar); deallocateCharsetConverter(&charset->wcharToCharset); } free(charsetEntries); charsetEntries = NULL; } } static int allocateCharsetEntries (const char *names) { int ok = 0; int count; char **namesArray = splitString(names, '+', &count); if (namesArray) { CharsetEntry *entries = calloc(count, sizeof(*entries)); if (entries) { charsetEntries = entries; charsetCount = 0; charsetIndex = 0; ok = 1; while (charsetCount < count) { CharsetEntry *charset = &charsetEntries[charsetCount]; if (!(charset->name = strdup(namesArray[charsetCount]))) { logMallocError(); ok = 0; deallocateCharsetEntries(); break; } charset->isMultiByte = 0; { static const CharsetConverter nullCharsetConverter = CHARSET_CONVERTER_INITIALIZER; charset->charsetToWchar = nullCharsetConverter; charset->wcharToCharset = nullCharsetConverter; } charsetCount += 1; } } deallocateStrings(namesArray); } return ok; } static CharacterConversionResult convertCharsToWchar (const char *chars, size_t length, wchar_t *character, size_t *size) { unsigned int count = charsetCount; while (count--) { CharsetEntry *charset = getCharsetEntry(); CharsetConverter *converter = &charset->charsetToWchar; CharacterConversionResult result = CONV_ERROR; if (allocateCharsetConverter(converter, charset->name, getWcharCharset())) { const char *inptr = chars; size_t inlen = length; char *outptr = (char *)character; size_t outlen = sizeof(*character); if ((result = convertCharacters(converter, &inptr, &inlen, &outptr, &outlen)) == CONV_OK) if (size) *size = inptr - chars; } if (result == CONV_SHORT) charset->isMultiByte = 1; if (result != CONV_ILLEGAL) return result; if (++charsetIndex == charsetCount) charsetIndex = 0; } return CONV_ILLEGAL; } static CharacterConversionResult convertWcharToChars (wchar_t character, char *chars, size_t length, size_t *size) { CharsetEntry *charset = getCharsetEntry(); CharsetConverter *converter = &charset->wcharToCharset; CharacterConversionResult result = CONV_ERROR; if (allocateCharsetConverter(converter, getWcharCharset(), charset->name)) { const char *inptr = (char *)&character; size_t inlen = sizeof(character); char *outptr = chars; size_t outlen = length; if ((result = convertCharacters(converter, &inptr, &inlen, &outptr, &outlen)) == CONV_OK) { size_t count = outptr - chars; if (size) *size = count; if (count > 1) charset->isMultiByte = 1; } else if ((result == CONV_OVERFLOW) && length) { charset->isMultiByte = 1; } } return result; } static wint_t convertCharacter (const wchar_t *character) { static unsigned char spaces = 0; static unsigned char length = 0; static char buffer[MB_LEN_MAX]; const wchar_t cellMask = 0XFF; if (!character) { length = 0; if (!spaces) return WEOF; spaces -= 1; return WC_C(' '); } if ((*character & ~cellMask) != UNICODE_ROW_DIRECT) { length = 0; return *character; } if (length < sizeof(buffer)) { buffer[length++] = *character & cellMask; while (1) { wchar_t wc; CharacterConversionResult result = convertCharsToWchar(buffer, length, &wc, NULL); if (result == CONV_OK) { length = 0; return wc; } if (result == CONV_SHORT) break; if (result != CONV_ILLEGAL) break; if (!--length) break; memmove(buffer, buffer+1, length); } } spaces += 1; return WEOF; } static int setDeviceName (const char **name, const char *const *names, int strict, const char *description) { return (*name = resolveDeviceName(names, strict, description)) != NULL; } static char * vtName (const char *name, unsigned char vt) { char *string; if (vt) { int length = strlen(name); if (name[length-1] == '0') length -= 1; char buffer[length+4]; snprintf(buffer, sizeof(buffer), "%.*s%u", length, name, vt); string = strdup(buffer); } else { string = strdup(name); } if (!string) logMallocError(); return string; } static const char *consoleName = NULL; static int setConsoleName (void) { static const char *const names[] = {"tty0", "vc/0", NULL}; return setDeviceName(&consoleName, names, 0, "console"); } static void closeConsole (int *fd) { if (*fd != -1) { logMessage(LOG_CATEGORY(SCREEN_DRIVER), "closing console: fd=%d", *fd); if (close(*fd) == -1) logSystemError("close[console]"); *fd = -1; } } static int openConsole (int *fd, int vt) { int opened = 0; char *name = vtName(consoleName, vt); if (name) { int console = openCharacterDevice(name, O_WRONLY|O_NOCTTY, TTY_MAJOR, vt); if (console != -1) { logMessage(LOG_CATEGORY(SCREEN_DRIVER), "console opened: %s: fd=%d", name, console); closeConsole(fd); *fd = console; opened = 1; } free(name); } return opened; } static int controlConsole (int *fd, int vt, int operation, void *argument) { int result = ioctl(*fd, operation, argument); if (result == -1) { if (errno == EIO) { logMessage(LOG_ERR, "console control error %d: fd=%d vt=%d op=0X%04X: %s", errno, *fd, vt, operation, strerror(errno)); if (openConsole(fd, vt)) { result = ioctl(*fd, operation, argument); } } } return result; } static int consoleDescriptor; static void closeCurrentConsole (void) { closeConsole(&consoleDescriptor); } static int openCurrentConsole (void) { return openConsole(&consoleDescriptor, virtualTerminalNumber); } static int controlCurrentConsole (int operation, void *argument) { if (consoleDescriptor != -1) { return controlConsole(&consoleDescriptor, virtualTerminalNumber, operation, argument); } switch (operation) { case GIO_UNIMAP: { struct unimapdesc *sfm = argument; memset(sfm, 0, sizeof(*sfm)); sfm->entries = NULL; sfm->entry_ct = 0; return 0; } case KDFONTOP: { struct console_font_op *cfo = argument; if (cfo->op == KD_FONT_OP_GET) { cfo->charcount = 0; cfo->width = 8; cfo->height = 16; return 0; } break; } case VT_GETHIFONTMASK: { unsigned short *mask = argument; *mask = 0; return 0; } case KDGETMODE: { int *mode = argument; *mode = KD_TEXT; return 0; } default: break; } errno = EAGAIN; return -1; } static const int NO_CONSOLE = 0; static const int MAIN_CONSOLE = 0; static int mainConsoleDescriptor; static void closeMainConsole (void) { closeConsole(&mainConsoleDescriptor); } static int openMainConsole (void) { return openConsole(&mainConsoleDescriptor, MAIN_CONSOLE); } static int controlMainConsole (int operation, void *argument) { return controlConsole(&mainConsoleDescriptor, MAIN_CONSOLE, operation, argument); } static const char *unicodeName = NULL; static int setUnicodeName (void) { static const char *const names[] = {"vcsu", "vcsu0", NULL}; return setDeviceName(&unicodeName, names, 1, "unicode"); } static void closeUnicode (int *fd) { if (*fd != -1) { logMessage(LOG_CATEGORY(SCREEN_DRIVER), "closing unicode: fd=%d", *fd); if (close(*fd) == -1) logSystemError("close[unicode]"); *fd = -1; } } static int openUnicode (int *fd, int vt) { if (!unicodeName) return 0; if (*fd != -1) return 1; int opened = 0; char *name = vtName(unicodeName, vt); if (name) { int unicode = openCharacterDevice(name, O_RDWR, VCS_MAJOR, 0X40|vt); if (unicode != -1) { logMessage(LOG_CATEGORY(SCREEN_DRIVER), "unicode opened: %s: fd=%d", name, unicode); closeUnicode(fd); *fd = unicode; opened = 1; } else { unicodeName = NULL; } free(name); } return opened; } static int unicodeDescriptor; static void closeCurrentUnicode (void) { closeUnicode(&unicodeDescriptor); } static int openCurrentUnicode (void) { if (!unicodeEnabled) return 0; return openUnicode(&unicodeDescriptor, virtualTerminalNumber); } static size_t readUnicodeDevice (off_t offset, void *buffer, size_t size) { if (openCurrentUnicode()) { const ssize_t count = pread(unicodeDescriptor, buffer, size, offset); if (count != -1) return count; if (errno != ENODATA) logSystemError("unicode read"); } return 0; } static unsigned char *unicodeCacheBuffer; static size_t unicodeCacheSize; static size_t unicodeCacheUsed; static size_t readUnicodeCache (off_t offset, void *buffer, size_t size) { if (offset <= unicodeCacheSize) { size_t left = unicodeCacheSize - offset; if (size > left) size = left; memcpy(buffer, &unicodeCacheBuffer[offset], size); return size; } else { logMessage(LOG_ERR, "invalid unicode cache offset: %u", (unsigned int)offset); } return 0; } static int readUnicodeData (off_t offset, void *buffer, size_t size) { size_t count = (unicodeCacheBuffer? readUnicodeCache: readUnicodeDevice)(offset, buffer, size); if (count == size) return 1; logMessage(LOG_ERR, "truncated unicode data: expected %zu bytes but read %zu", size, count); return 0; } static int readUnicodeContent (off_t offset, uint32_t *buffer, size_t count) { count *= sizeof(*buffer); offset *= sizeof(*buffer); return readUnicodeData(offset, buffer, count); } static int refreshUnicodeCache (size_t size) { size *= 4; if (size > unicodeCacheSize) { const unsigned int bits = 10; const unsigned int mask = (1 << bits) - 1; size |= mask; size += 1; unsigned char *buffer = malloc(size); if (!buffer) { logMallocError(); return 0; } if (unicodeCacheBuffer) free(unicodeCacheBuffer); unicodeCacheBuffer = buffer; unicodeCacheSize = size; } unicodeCacheUsed = readUnicodeDevice(0, unicodeCacheBuffer, unicodeCacheSize); return 1; } static const char *screenName = NULL; static int screenDescriptor; static int isMonitorable; static THREAD_LOCAL AsyncHandle screenMonitor = NULL; static int screenUpdated; static int currentConsoleNumber; static int inTextMode; static TimePeriod mappingRecalculationTimer; typedef struct { unsigned char rows; unsigned char columns; } ScreenSize; typedef struct { unsigned char column; unsigned char row; } ScreenLocation; typedef struct { ScreenSize size; ScreenLocation location; } ScreenHeader; #ifdef HAVE_SYS_POLL_H #include static int canMonitorScreen (void) { struct pollfd pollDescriptor = { .fd = screenDescriptor, .events = POLLPRI }; return poll(&pollDescriptor, 1, 0) == 1; } #else /* can poll */ static int canMonitorScreen (void) { return 0; } #endif /* can poll */ static int setScreenName (void) { static const char *const names[] = {"vcsa", "vcsa0", "vcc/a", NULL}; return setDeviceName(&screenName, names, 0, "screen"); } static int openScreenDevice (int *fd, int vt) { int opened = 0; char *name = vtName(screenName, vt); if (name) { int screen = openCharacterDevice(name, O_RDWR, VCS_MAJOR, 0X80|vt); if (screen != -1) { logMessage(LOG_CATEGORY(SCREEN_DRIVER), "screen opened: %s: fd=%d", name, screen); *fd = screen; opened = 1; } free(name); } return opened; } static void closeCurrentScreen (void) { if (screenMonitor) { asyncCancelRequest(screenMonitor); screenMonitor = NULL; } if (screenDescriptor != -1) { logMessage(LOG_CATEGORY(SCREEN_DRIVER), "closing screen: fd=%d", screenDescriptor); if (close(screenDescriptor) == -1) logSystemError("close[screen]"); screenDescriptor = -1; } } static int setCurrentScreen (unsigned char vt) { int screen; if (!openScreenDevice(&screen, vt)) return 0; closeCurrentConsole(); closeCurrentUnicode(); closeCurrentScreen(); screenDescriptor = screen; virtualTerminalNumber = vt; isMonitorable = canMonitorScreen(); logMessage(LOG_CATEGORY(SCREEN_DRIVER), "screen is monitorable: %s", (isMonitorable? "yes": "no")); screenMonitor = NULL; screenUpdated = 1; return 1; } static size_t readScreenDevice (off_t offset, void *buffer, size_t size) { const ssize_t count = pread(screenDescriptor, buffer, size, offset); if (count != -1) return count; logSystemError("screen read"); return 0; } static unsigned char *screenCacheBuffer; static size_t screenCacheSize; static size_t readScreenCache (off_t offset, void *buffer, size_t size) { if (offset <= screenCacheSize) { size_t left = screenCacheSize - offset; if (size > left) size = left; memcpy(buffer, &screenCacheBuffer[offset], size); return size; } else { logMessage(LOG_ERR, "invalid screen cache offset: %u", (unsigned int)offset); } return 0; } static int readScreenData (off_t offset, void *buffer, size_t size) { size_t count = (screenCacheBuffer? readScreenCache: readScreenDevice)(offset, buffer, size); if (count == size) return 1; logMessage(LOG_ERR, "truncated screen data: expected %zu bytes but read %zu", size, count); return 0; } static int readScreenHeader (ScreenHeader *header) { return readScreenData(0, header, sizeof(*header)); } static int readScreenSize (ScreenSize *size) { return readScreenData(0, size, sizeof(*size)); } static int readScreenContent (off_t offset, uint16_t *buffer, size_t count) { count *= sizeof(*buffer); offset *= sizeof(*buffer); offset += sizeof(ScreenHeader); return readScreenData(offset, buffer, count); } static size_t getScreenBufferSize (const ScreenSize *screenSize) { return (screenSize->columns * screenSize->rows * 2) + sizeof(ScreenHeader); } static size_t refreshScreenBuffer (unsigned char **screenBuffer, size_t *screenSize) { if (!*screenBuffer) { ScreenHeader header; { size_t size = sizeof(header); size_t count = readScreenDevice(0, &header, size); if (!count) return 0; if (count < size) { logBytes(LOG_ERR, "truncated screen header", &header, count); return 0; } } { size_t size = getScreenBufferSize(&header.size); unsigned char *buffer = malloc(size); if (!buffer) { logMallocError(); return 0; } *screenBuffer = buffer; *screenSize = size; } } while (1) { size_t count = readScreenDevice(0, *screenBuffer, *screenSize); if (!count) return 0; if (count < sizeof(ScreenHeader)) { logBytes(LOG_ERR, "truncated screen header", *screenBuffer, count); return 0; } { ScreenHeader *header = (void *)*screenBuffer; size_t size = getScreenBufferSize(&header->size); if (count >= size) return header->size.columns * header->size.rows; { unsigned char *buffer = realloc(*screenBuffer, size); if (!buffer) { logMallocError(); return 0; } *screenBuffer = buffer; *screenSize = size; } } } } static struct unipair *screenFontMapTable = NULL; static unsigned short screenFontMapSize = 0; static unsigned short screenFontMapCount; static int setScreenFontMap (int force) { struct unimapdesc sfm; unsigned short size = force? 0: screenFontMapCount; if (!size) size = 0X100; while (1) { sfm.entry_ct = size; if (!(sfm.entries = malloc(sfm.entry_ct * sizeof(*sfm.entries)))) { logMallocError(); return 0; } if (controlCurrentConsole(GIO_UNIMAP, &sfm) != -1) break; free(sfm.entries); if (errno != ENOMEM) { logSystemError("ioctl[GIO_UNIMAP]"); return 0; } if (!(size <<= 1)) { logMessage(LOG_ERR, "screen font map too big"); return 0; } } if (!force) { if (sfm.entry_ct == screenFontMapCount) { if (memcmp(sfm.entries, screenFontMapTable, sfm.entry_ct*sizeof(sfm.entries[0])) == 0) { free(sfm.entries); return 0; } } } if (screenFontMapTable) free(screenFontMapTable); screenFontMapTable = sfm.entries; screenFontMapCount = sfm.entry_ct; screenFontMapSize = size; logMessage(LOG_CATEGORY(SCREEN_DRIVER), "Font Map Size: %d", screenFontMapCount); if (logScreenFontMap) { for (unsigned int i=0; iunicode, map->fontpos); } } return 1; } static int vgaCharacterCount; static int vgaLargeTable; static int setVgaCharacterCount (int force) { int oldCount = vgaCharacterCount; { struct console_font_op cfo = { .width = UINT_MAX, .height = UINT_MAX, .op = KD_FONT_OP_GET }; vgaCharacterCount = 0; { static unsigned char isNotImplemented = 0; if (!isNotImplemented) { if (controlCurrentConsole(KDFONTOP, &cfo) != -1) { logMessage(LOG_CATEGORY(SCREEN_DRIVER), "Font Properties: %ux%u*%u", cfo.width, cfo.height, cfo.charcount); vgaCharacterCount = cfo.charcount; } else { if (errno == ENOSYS) isNotImplemented = 1; if (errno != EINVAL) { logMessage(LOG_WARNING, "ioctl[KDFONTOP[GET]]: %s", strerror(errno)); } } } } } if (!vgaCharacterCount) { unsigned int index; for (index=0; indexfontpos) vgaCharacterCount = map->fontpos + 1; } } vgaCharacterCount = ((vgaCharacterCount - 1) | 0XFF) + 1; vgaLargeTable = vgaCharacterCount > 0X100; if (!force) { if (vgaCharacterCount == oldCount) { return 0; } } logMessage(LOG_CATEGORY(SCREEN_DRIVER), "VGA Character Count: %d(%s)", vgaCharacterCount, vgaLargeTable? "large": "small"); return 1; } static unsigned short highFontBit; static unsigned short fontAttributesMask; static unsigned short unshiftedAttributesMask; static unsigned short shiftedAttributesMask; static void setAttributesMasks (unsigned short bit) { fontAttributesMask = bit; unshiftedAttributesMask = bit - 1; shiftedAttributesMask = ~unshiftedAttributesMask & ~bit; unshiftedAttributesMask &= 0XFF00; logMessage(LOG_CATEGORY(SCREEN_DRIVER), "Attributes Masks: Font:%04X Unshifted:%04X Shifted:%04X", fontAttributesMask, unshiftedAttributesMask, shiftedAttributesMask); } static int determineAttributesMasks (void) { if (!vgaLargeTable) { setAttributesMasks(0); } else if (highFontBit) { setAttributesMasks(highFontBit); } else { { unsigned short mask; if (controlCurrentConsole(VT_GETHIFONTMASK, &mask) == -1) { if (errno != EINVAL) logSystemError("ioctl[VT_GETHIFONTMASK]"); } else if (mask & 0XFF) { logMessage(LOG_ERR, "high font mask has bit set in low-order byte: %04X", mask); } else { setAttributesMasks(mask); return 1; } } { ScreenSize size; if (readScreenSize(&size)) { const size_t count = size.columns * size.rows; unsigned short buffer[count]; if (readScreenContent(0, buffer, ARRAY_COUNT(buffer))) { unsigned int counts[0X10]; memset(counts, 0, sizeof(counts)); for (unsigned int index=0; index> 8] += 1; } setAttributesMasks((counts[0XE] > counts[0X7])? 0X0100: 0X0800); return 1; } } } } return 0; } static wchar_t translationTable[0X200]; static int setTranslationTable (int force) { int mappingChanged = 0; int sfmChanged = setScreenFontMap(force); int vccChanged = (sfmChanged || force)? setVgaCharacterCount(force): 0; if (vccChanged || force) determineAttributesMasks(); if (sfmChanged || vccChanged) { unsigned int count = ARRAY_COUNT(translationTable); for (unsigned int i=0; i 0) { const struct unipair *sfm = &screenFontMapTable[--screenFontMapIndex]; if (sfm->fontpos < count) { wchar_t *character = &translationTable[sfm->fontpos]; if (*character == 0X20) continue; *character = sfm->unicode; } } } mappingChanged = 1; } if (mappingChanged) { logMessage(LOG_CATEGORY(SCREEN_DRIVER), "character mapping changed"); } restartTimePeriod(&mappingRecalculationTimer); return mappingChanged; } static int readScreenRow (int row, size_t size, ScreenCharacter *characters, int *offsets) { off_t offset = row * size; uint16_t vgaBuffer[size]; if (!readScreenContent(offset, vgaBuffer, size)) return 0; uint32_t unicodeBuffer[size]; const uint32_t *unicode = NULL; if (unicodeEnabled) { if (readUnicodeContent(offset, unicodeBuffer, size)) { unicode = unicodeBuffer; } } ScreenCharacter *character = characters; int column = 0; { const uint16_t *vga = vgaBuffer; const uint16_t *end = vga + size; int blanks = 0; while (vga < end) { wint_t wc; if (unicode) { wc = *unicode++; if ((blanks > 0) && (wc == WC_C(' '))) { blanks -= 1; wc = WEOF; } else { blanks = getCharacterWidth(wc) - 1; } } else { uint16_t position = *vga & 0XFF; if (*vga & fontAttributesMask) position |= 0X100; wc = convertCharacter(&translationTable[position]); } if (wc != WEOF) { if (character) { character->attributes = ((*vga & unshiftedAttributesMask) | ((*vga & shiftedAttributesMask) >> 1)) >> 8; character->text = wc; character += 1; } if (offsets) offsets[column] = vga - vgaBuffer; column += 1; } vga += 1; } } if (!unicode) { wint_t wc; while ((wc = convertCharacter(NULL)) != WEOF) { if (column < size) { if (character) { character->text = wc; character->attributes = SCR_COLOUR_DEFAULT; character += 1; } if (offsets) offsets[column] = size - 1; column += 1; } } } while (column < size) { if (character) { static const ScreenCharacter pad = { .text = WC_C(' '), .attributes = SCR_COLOUR_DEFAULT }; *character++ = pad; } if (offsets) offsets[column] = size - 1; column += 1; } return 1; } static void adjustCursorColumn (short *column, short row, short columns) { int offsets[columns]; if (readScreenRow(row, columns, NULL, offsets)) { int first = 0; int last = columns - 1; while (first <= last) { int current = (first + last) / 2; if (offsets[current] < *column) { first = current + 1; } else { last = current - 1; } } if (first == columns) first -= 1; *column = first; } } #ifdef HAVE_LINUX_INPUT_H #include static const LinuxKeyCode *xtKeys; static const LinuxKeyCode *atKeys; static int atKeyPressed; static int ps2KeyPressed; #endif /* HAVE_LINUX_INPUT_H */ static UinputObject *uinputKeyboard = NULL; static ReportListenerInstance *brailleDeviceOfflineListener; static void closeKeyboard (void) { if (uinputKeyboard) { destroyUinputObject(uinputKeyboard); uinputKeyboard = NULL; } } static int openKeyboard (void) { if (!uinputKeyboard) { if (!(uinputKeyboard = newUinputKeyboard("Linux Screen Driver Keyboard"))) { return 0; } atexit(closeKeyboard); } return 1; } static void resetKeyboard (void) { if (uinputKeyboard) { releasePressedKeys(uinputKeyboard); } } static int processParameters_LinuxScreen (char **parameters) { fallbackText = parameters[PARM_FALLBACK_TEXT]; { const char *names = parameters[PARM_CHARSET]; if (!names || !*names) names = getLocaleCharset(); if (!allocateCharsetEntries(names)) return 0; } highFontBit = 0; { const char *parameter = parameters[PARM_HIGH_FONT_BIT]; if (parameter && *parameter) { int bit = 0; static const int minimum = 0; static const int maximum = 7; static const char *choices[] = {"auto", "vga", "fb", NULL}; unsigned int choice; if (validateInteger(&bit, parameter, &minimum, &maximum)) { highFontBit = 1 << (bit + 8); } else if (!validateChoice(&choice, parameter, choices)) { logMessage(LOG_WARNING, "%s: %s", "invalid high font bit", parameter); } else if (choice) { static const unsigned short bits[] = {0X0800, 0X0100}; highFontBit = bits[choice-1]; } } } logScreenFontMap = 0; { const char *parameter = parameters[PARM_LOG_SCREEN_FONT_MAP]; if (parameter && *parameter) { if (!validateYesNo(&logScreenFontMap, parameter)) { logMessage(LOG_WARNING, "%s: %s", "invalid log screen font map setting", parameter); } } } unicodeEnabled = 1; { const char *parameter = parameters[PARM_UNICODE]; if (parameter && *parameter) { if (!validateYesNo(&unicodeEnabled, parameter)) { logMessage(LOG_WARNING, "%s: %s", "invalid direct unicode setting", parameter); } } } virtualTerminalNumber = 0; { const char *parameter = parameters[PARM_VIRTUAL_TERMINAL_NUMBER]; if (parameter && *parameter) { static const int minimum = 0; static const int maximum = MAX_NR_CONSOLES; if (!validateInteger(&virtualTerminalNumber, parameter, &minimum, &maximum)) { logMessage(LOG_WARNING, "%s: %s", "invalid virtual terminal number", parameter); } } } return 1; } static void releaseParameters_LinuxScreen (void) { deallocateCharsetEntries(); } REPORT_LISTENER(lxBrailleDeviceOfflineListener) { resetKeyboard(); } static int construct_LinuxScreen (void) { mainConsoleDescriptor = -1; screenDescriptor = -1; consoleDescriptor = -1; unicodeDescriptor = -1; screenUpdated = 0; screenCacheBuffer = NULL; screenCacheSize = 0; unicodeCacheBuffer = NULL; unicodeCacheSize = 0; unicodeCacheUsed = 0; currentConsoleNumber = 0; inTextMode = 1; startTimePeriod(&mappingRecalculationTimer, 4000); brailleDeviceOfflineListener = NULL; #ifdef HAVE_LINUX_INPUT_H xtKeys = linuxKeyMap_xt00; atKeys = linuxKeyMap_at00; atKeyPressed = 1; ps2KeyPressed = 1; #endif /* HAVE_LINUX_INPUT_H */ if (setScreenName()) { if (setConsoleName()) { if (unicodeEnabled) { if (!setUnicodeName()) { unicodeEnabled = 0; } } if (openMainConsole()) { if (setCurrentScreen(virtualTerminalNumber)) { openKeyboard(); brailleDeviceOfflineListener = registerReportListener(REPORT_BRAILLE_DEVICE_OFFLINE, lxBrailleDeviceOfflineListener, NULL); return 1; } } } } closeCurrentConsole(); closeCurrentScreen(); closeMainConsole(); return 0; } static void destruct_LinuxScreen (void) { if (brailleDeviceOfflineListener) { unregisterReportListener(brailleDeviceOfflineListener); brailleDeviceOfflineListener = NULL; } closeCurrentConsole(); consoleName = NULL; closeCurrentScreen(); screenName = NULL; if (screenFontMapTable) { free(screenFontMapTable); screenFontMapTable = NULL; } screenFontMapSize = 0; screenFontMapCount = 0; if (screenCacheBuffer) { free(screenCacheBuffer); screenCacheBuffer = NULL; } screenCacheSize = 0; if (unicodeCacheBuffer) { free(unicodeCacheBuffer); unicodeCacheBuffer = NULL; } unicodeCacheSize = 0; unicodeCacheUsed = 0; closeMainConsole(); } ASYNC_MONITOR_CALLBACK(lxScreenUpdated) { asyncDiscardHandle(screenMonitor); screenMonitor = NULL; screenUpdated = 1; mainScreenUpdated(); return 0; } static int poll_LinuxScreen (void) { int poll = !isMonitorable? 1: screenMonitor? 0: !asyncMonitorFileAlert(&screenMonitor, screenDescriptor, lxScreenUpdated, NULL); if (poll) screenUpdated = 1; return poll; } static int getConsoleState (struct vt_stat *state) { if (controlMainConsole(VT_GETSTATE, state) != -1) return 1; logSystemError("ioctl[VT_GETSTATE]"); problemText = gettext("can't get console state"); return 0; } static int isUnusedConsole (int vt) { int isUnused = 1; unsigned char *buffer = NULL; size_t size = 0; if (refreshScreenBuffer(&buffer, &size)) { const ScreenHeader *header = (void *)buffer; const uint16_t *from = (void *)(buffer + sizeof(*header)); const uint16_t *to = (void *)(buffer + getScreenBufferSize(&header->size)); if (from < to) { const uint16_t vga = *from++; while (from < to) { if (*from++ != vga) { isUnused = 0; break; } } } } if (buffer) free(buffer); return isUnused; } static int canOpenCurrentConsole (void) { typedef uint16_t OpenableConsoles; static OpenableConsoles openableConsoles = 0; struct vt_stat state; if (!getConsoleState(&state)) return 0; int console = virtualTerminalNumber; if (!console) console = state.v_active; OpenableConsoles bit = 1 << console; if (bit && !(openableConsoles & bit)) { if (console != MAIN_CONSOLE) { if (!(state.v_state & bit)) { if (isUnusedConsole(console)) { return 0; } } } openableConsoles |= bit; } return 1; } static int getConsoleNumber (void) { int console; if (virtualTerminalNumber) { console = virtualTerminalNumber; } else { struct vt_stat state; if (!getConsoleState(&state)) return NO_CONSOLE; console = state.v_active; } if (console != currentConsoleNumber) { closeCurrentConsole(); } if (consoleDescriptor == -1) { if (!canOpenCurrentConsole()) { problemText = gettext("console not in use"); } else if (!openCurrentConsole()) { problemText = gettext("can't open console"); } setTranslationTable(1); } return console; } static int testTextMode (void) { if (problemText) return 0; int mode; if (controlCurrentConsole(KDGETMODE, &mode) == -1) { logSystemError("ioctl[KDGETMODE]"); } else if (mode == KD_TEXT) { if (afterTimePeriod(&mappingRecalculationTimer, NULL)) setTranslationTable(0); return 1; } problemText = gettext("screen not in text mode"); return 0; } static int refreshCache (void) { size_t size = refreshScreenBuffer(&screenCacheBuffer, &screenCacheSize); if (!size) return 0; if (unicodeEnabled) { if (!refreshUnicodeCache(size)) { return 0; } } return 1; } static int refresh_LinuxScreen (void) { if (screenUpdated) { while (1) { problemText = NULL; if (!refreshCache()) { problemText = gettext("can't read screen content"); goto done; } { int consoleNumber = getConsoleNumber(); if (consoleNumber == currentConsoleNumber) break; logMessage(LOG_CATEGORY(SCREEN_DRIVER), "console number changed: %u -> %u", currentConsoleNumber, consoleNumber); currentConsoleNumber = consoleNumber; } } inTextMode = testTextMode(); screenUpdated = 0; done: if (problemText) { if (*fallbackText) { problemText = gettext(fallbackText); } } } return 1; } static int getScreenDescription (ScreenDescription *description) { ScreenHeader header; if (readScreenHeader(&header)) { description->cols = header.size.columns; description->rows = header.size.rows; description->posx = header.location.column; description->posy = header.location.row; adjustCursorColumn(&description->posx, description->posy, description->cols); return 1; } problemText = gettext("can't read screen header"); return 0; } static void describe_LinuxScreen (ScreenDescription *description) { if (!screenCacheBuffer) { problemText = NULL; currentConsoleNumber = getConsoleNumber(); inTextMode = testTextMode(); } if ((description->number = currentConsoleNumber)) { if (inTextMode) { if (getScreenDescription(description)) { } } } if ((description->unreadable = problemText)) { description->cols = strlen(problemText); description->rows = 1; description->posx = 0; description->posy = 0; } } static int readCharacters_LinuxScreen (const ScreenBox *box, ScreenCharacter *buffer) { ScreenSize size; if (readScreenSize(&size)) { if (validateScreenBox(box, size.columns, size.rows)) { if (problemText) { setScreenMessage(box, buffer, problemText); return 1; } for (unsigned int row=0; rowheight; row+=1) { ScreenCharacter characters[size.columns]; if (!readScreenRow(box->top+row, size.columns, characters, NULL)) return 0; memcpy(buffer, &characters[box->left], (box->width * sizeof(characters[0]))); buffer += box->width; } return 1; } } return 0; } static int getCapsLockState (void) { char leds; if (controlCurrentConsole(KDGETLED, &leds) != -1) if (leds & LED_CAP) return 1; return 0; } static inline int hasModUpper (ScreenKey key) { return (key & SCR_KEY_UPPER) && !getCapsLockState(); } static inline int hasModShift (ScreenKey key) { return !!(key & SCR_KEY_SHIFT); } static inline int hasModControl (ScreenKey key) { return !!(key & SCR_KEY_CONTROL); } static inline int hasModAltLeft (ScreenKey key) { return !!(key & SCR_KEY_ALT_LEFT); } static inline int hasModAltRight (ScreenKey key) { return !!(key & SCR_KEY_ALT_RIGHT); } static inline int hasModGui (ScreenKey key) { return !!(key & SCR_KEY_GUI); } static int injectKeyEvent (int key, int press) { if (!openKeyboard()) return 0; return writeKeyEvent(uinputKeyboard, key, press); } static int insertUinput ( LinuxKeyCode code, int modUpper, int modShift, int modControl, int modAltLeft, int modAltRight ) { #ifdef HAVE_LINUX_INPUT_H if (code) { #define KEY_EVENT(KEY, PRESS) { if (!injectKeyEvent((KEY), (PRESS))) return 0; } if (modUpper) { KEY_EVENT(KEY_CAPSLOCK, 1); KEY_EVENT(KEY_CAPSLOCK, 0); } if (modShift) KEY_EVENT(KEY_LEFTSHIFT, 1); if (modControl) KEY_EVENT(KEY_LEFTCTRL, 1); if (modAltLeft) KEY_EVENT(KEY_LEFTALT, 1); if (modAltRight) KEY_EVENT(KEY_RIGHTALT, 1); KEY_EVENT(code, 1); KEY_EVENT(code, 0); if (modAltRight) KEY_EVENT(KEY_RIGHTALT, 0); if (modAltLeft) KEY_EVENT(KEY_LEFTALT, 0); if (modControl) KEY_EVENT(KEY_LEFTCTRL, 0); if (modShift) KEY_EVENT(KEY_LEFTSHIFT, 0); if (modUpper) { KEY_EVENT(KEY_CAPSLOCK, 1); KEY_EVENT(KEY_CAPSLOCK, 0); } #undef KEY_EVENT return 1; } #endif /* HAVE_LINUX_INPUT_H */ return 0; } static int insertByte (char byte) { if (controlCurrentConsole(TIOCSTI, &byte) != -1) return 1; logSystemError("ioctl[TIOCSTI]"); return 0; } static int insertBytes (const char *byte, size_t count) { while (count) { if (!insertByte(*byte++)) return 0; count -= 1; } return 1; } static int insertXlate (wchar_t character) { char bytes[MB_LEN_MAX]; size_t count; CharacterConversionResult result = convertWcharToChars(character, bytes, sizeof(bytes), &count); if (result != CONV_OK) { uint32_t value = character; logMessage(LOG_WARNING, "character not supported in xlate mode: 0X%02"PRIX32, value); return 0; } return insertBytes(bytes, count); } static int insertUnicode (wchar_t character) { { Utf8Buffer utf8; size_t utfs = convertWcharToUtf8(character, utf8); if (utfs) return insertBytes(utf8, utfs); } { uint32_t value = character; logMessage(LOG_WARNING, "character not supported in unicode keyboard mode: 0X%02"PRIX32, value); } return 0; } static int insertCode (ScreenKey key, int raw) { const LinuxKeyCode *map; unsigned char code; unsigned char escape; setScreenKeyModifiers(&key, SCR_KEY_SHIFT | SCR_KEY_CONTROL); #define KEY_TO_XT(KEY, ESCAPE, CODE) \ case (KEY): \ map = linuxKeyMap_xt ## ESCAPE; \ code = XT_KEY_ ## ESCAPE ## _ ## CODE; \ escape = XT_MOD_ ## ESCAPE; \ break; switch (key & SCR_KEY_CHAR_MASK) { KEY_TO_XT(SCR_KEY_ESCAPE, 00, Escape) KEY_TO_XT(SCR_KEY_F1, 00, F1) KEY_TO_XT(SCR_KEY_F2, 00, F2) KEY_TO_XT(SCR_KEY_F3, 00, F3) KEY_TO_XT(SCR_KEY_F4, 00, F4) KEY_TO_XT(SCR_KEY_F5, 00, F5) KEY_TO_XT(SCR_KEY_F6, 00, F6) KEY_TO_XT(SCR_KEY_F7, 00, F7) KEY_TO_XT(SCR_KEY_F8, 00, F8) KEY_TO_XT(SCR_KEY_F9, 00, F9) KEY_TO_XT(SCR_KEY_F10, 00, F10) KEY_TO_XT(SCR_KEY_F11, 00, F11) KEY_TO_XT(SCR_KEY_F12, 00, F12) KEY_TO_XT(SCR_KEY_F13, 00, F13) KEY_TO_XT(SCR_KEY_F14, 00, F14) KEY_TO_XT(SCR_KEY_F15, 00, F15) KEY_TO_XT(SCR_KEY_F16, 00, F16) KEY_TO_XT(SCR_KEY_F17, 00, F17) KEY_TO_XT(SCR_KEY_F18, 00, F18) KEY_TO_XT(SCR_KEY_F19, 00, F19) KEY_TO_XT(SCR_KEY_F20, 00, F20) KEY_TO_XT(SCR_KEY_F21, 00, F21) KEY_TO_XT(SCR_KEY_F22, 00, F22) KEY_TO_XT(SCR_KEY_F23, 00, F23) KEY_TO_XT(SCR_KEY_F24, 00, F24) KEY_TO_XT('`', 00, Grave) KEY_TO_XT('1', 00, 1) KEY_TO_XT('2', 00, 2) KEY_TO_XT('3', 00, 3) KEY_TO_XT('4', 00, 4) KEY_TO_XT('5', 00, 5) KEY_TO_XT('6', 00, 6) KEY_TO_XT('7', 00, 7) KEY_TO_XT('8', 00, 8) KEY_TO_XT('9', 00, 9) KEY_TO_XT('0', 00, 0) KEY_TO_XT('-', 00, Minus) KEY_TO_XT('=', 00, Equal) KEY_TO_XT(SCR_KEY_BACKSPACE, 00, Backspace) KEY_TO_XT(SCR_KEY_TAB, 00, Tab) KEY_TO_XT('q', 00, Q) KEY_TO_XT('w', 00, W) KEY_TO_XT('e', 00, E) KEY_TO_XT('r', 00, R) KEY_TO_XT('t', 00, T) KEY_TO_XT('y', 00, Y) KEY_TO_XT('u', 00, U) KEY_TO_XT('i', 00, I) KEY_TO_XT('o', 00, O) KEY_TO_XT('p', 00, P) KEY_TO_XT('[', 00, LeftBracket) KEY_TO_XT(']', 00, RightBracket) KEY_TO_XT('\\', 00, Backslash) KEY_TO_XT('a', 00, A) KEY_TO_XT('s', 00, S) KEY_TO_XT('d', 00, D) KEY_TO_XT('f', 00, F) KEY_TO_XT('g', 00, G) KEY_TO_XT('h', 00, H) KEY_TO_XT('j', 00, J) KEY_TO_XT('k', 00, K) KEY_TO_XT('l', 00, L) KEY_TO_XT(';', 00, Semicolon) KEY_TO_XT('\'', 00, Apostrophe) KEY_TO_XT(SCR_KEY_ENTER, 00, Enter) KEY_TO_XT('z', 00, Z) KEY_TO_XT('x', 00, X) KEY_TO_XT('c', 00, C) KEY_TO_XT('v', 00, V) KEY_TO_XT('b', 00, B) KEY_TO_XT('n', 00, N) KEY_TO_XT('m', 00, M) KEY_TO_XT(',', 00, Comma) KEY_TO_XT('.', 00, Period) KEY_TO_XT('/', 00, Slash) KEY_TO_XT(' ', 00, Space) KEY_TO_XT(SCR_KEY_INSERT, E0, Insert) KEY_TO_XT(SCR_KEY_DELETE, E0, Delete) KEY_TO_XT(SCR_KEY_HOME, E0, Home) KEY_TO_XT(SCR_KEY_END, E0, End) KEY_TO_XT(SCR_KEY_PAGE_UP, E0, PageUp) KEY_TO_XT(SCR_KEY_PAGE_DOWN, E0, PageDown) KEY_TO_XT(SCR_KEY_CURSOR_UP, E0, ArrowUp) KEY_TO_XT(SCR_KEY_CURSOR_LEFT, E0, ArrowLeft) KEY_TO_XT(SCR_KEY_CURSOR_DOWN, E0, ArrowDown) KEY_TO_XT(SCR_KEY_CURSOR_RIGHT, E0, ArrowRight) default: logMessage(LOG_WARNING, "key not supported in raw keyboard mode: %04X", key); return 0; } #undef KEY_TO_XT { const int modUpper = hasModUpper(key); const int modShift = hasModShift(key); const int modControl = hasModControl(key); const int modAltLeft = hasModAltLeft(key); const int modAltRight = hasModAltRight(key); const int modGui = hasModGui(key); if (raw) { char codes[22]; unsigned int count = 0; if (modUpper) { codes[count++] = XT_KEY_00_CapsLock; codes[count++] = XT_KEY_00_CapsLock | XT_BIT_RELEASE; } if (modShift) codes[count++] = XT_KEY_00_LeftShift; if (modControl) codes[count++] = XT_KEY_00_LeftControl; if (modAltLeft) codes[count++] = XT_KEY_00_LeftAlt; if (modAltRight) { codes[count++] = XT_MOD_E0; codes[count++] = XT_KEY_E0_RightAlt; } if (modGui) { codes[count++] = XT_MOD_E0; codes[count++] = XT_KEY_E0_LeftGUI; } if (escape) codes[count++] = escape; codes[count++] = code; if (escape) codes[count++] = escape; codes[count++] = code | XT_BIT_RELEASE; if (modGui) { codes[count++] = XT_MOD_E0; codes[count++] = XT_KEY_E0_LeftGUI | XT_BIT_RELEASE; } if (modAltRight) { codes[count++] = XT_MOD_E0; codes[count++] = XT_KEY_E0_RightAlt | XT_BIT_RELEASE; } if (modAltLeft) codes[count++] = XT_KEY_00_LeftAlt | XT_BIT_RELEASE; if (modControl) codes[count++] = XT_KEY_00_LeftControl | XT_BIT_RELEASE; if (modShift) codes[count++] = XT_KEY_00_LeftShift | XT_BIT_RELEASE; if (modUpper) { codes[count++] = XT_KEY_00_CapsLock; codes[count++] = XT_KEY_00_CapsLock | XT_BIT_RELEASE; } return insertBytes(codes, count); } else { LinuxKeyCode mapped = map[code]; if (!mapped) { logMessage(LOG_WARNING, "key not supported in medium raw keyboard mode: %04X", key); return 0; } return insertUinput(mapped, modUpper, modShift, modControl, modAltLeft, modAltRight); } } } static int insertTranslated (ScreenKey key, int (*insertCharacter)(wchar_t character)) { wchar_t buffer[2]; const wchar_t *sequence; const wchar_t *end; setScreenKeyModifiers(&key, 0); if (isSpecialKey(key)) { switch (key) { case SCR_KEY_ENTER: sequence = WS_C("\r"); break; case SCR_KEY_TAB: sequence = WS_C("\t"); break; case SCR_KEY_BACKSPACE: sequence = WS_C("\x7f"); break; case SCR_KEY_ESCAPE: sequence = WS_C("\x1b"); break; case SCR_KEY_CURSOR_LEFT: sequence = WS_C("\x1b[D"); break; case SCR_KEY_CURSOR_RIGHT: sequence = WS_C("\x1b[C"); break; case SCR_KEY_CURSOR_UP: sequence = WS_C("\x1b[A"); break; case SCR_KEY_CURSOR_DOWN: sequence = WS_C("\x1b[B"); break; case SCR_KEY_PAGE_UP: sequence = WS_C("\x1b[5~"); break; case SCR_KEY_PAGE_DOWN: sequence = WS_C("\x1b[6~"); break; case SCR_KEY_HOME: sequence = WS_C("\x1b[1~"); break; case SCR_KEY_END: sequence = WS_C("\x1b[4~"); break; case SCR_KEY_INSERT: sequence = WS_C("\x1b[2~"); break; case SCR_KEY_DELETE: sequence = WS_C("\x1b[3~"); break; case SCR_KEY_F1: sequence = WS_C("\x1b[[A"); break; case SCR_KEY_F2: sequence = WS_C("\x1b[[B"); break; case SCR_KEY_F3: sequence = WS_C("\x1b[[C"); break; case SCR_KEY_F4: sequence = WS_C("\x1b[[D"); break; case SCR_KEY_F5: sequence = WS_C("\x1b[[E"); break; case SCR_KEY_F6: sequence = WS_C("\x1b[17~"); break; case SCR_KEY_F7: sequence = WS_C("\x1b[18~"); break; case SCR_KEY_F8: sequence = WS_C("\x1b[19~"); break; case SCR_KEY_F9: sequence = WS_C("\x1b[20~"); break; case SCR_KEY_F10: sequence = WS_C("\x1b[21~"); break; case SCR_KEY_F11: sequence = WS_C("\x1b[23~"); break; case SCR_KEY_F12: sequence = WS_C("\x1b[24~"); break; case SCR_KEY_F13: sequence = WS_C("\x1b[25~"); break; case SCR_KEY_F14: sequence = WS_C("\x1b[26~"); break; case SCR_KEY_F15: sequence = WS_C("\x1b[28~"); break; case SCR_KEY_F16: sequence = WS_C("\x1b[29~"); break; case SCR_KEY_F17: sequence = WS_C("\x1b[31~"); break; case SCR_KEY_F18: sequence = WS_C("\x1b[32~"); break; case SCR_KEY_F19: sequence = WS_C("\x1b[33~"); break; case SCR_KEY_F20: sequence = WS_C("\x1b[34~"); break; default: if (insertCode(key, 0)) return 1; logMessage(LOG_WARNING, "key not supported in xlate keyboard mode: %04X", key); return 0; } end = sequence + wcslen(sequence); } else { wchar_t *character = buffer + ARRAY_COUNT(buffer); end = character; *--character = key & SCR_KEY_CHAR_MASK; if (hasModAltLeft(key)) { int meta; if (controlCurrentConsole(KDGKBMETA, &meta) == -1) return 0; switch (meta) { case K_ESCPREFIX: *--character = ESC; break; case K_METABIT: if (*character >= 0X80) { logMessage(LOG_WARNING, "can't add meta bit to character: U+%04X", *character); return 0; } *character |= 0X80; break; default: logMessage(LOG_WARNING, "unsupported keyboard meta mode: %d", meta); return 0; } } sequence = character; } while (sequence != end) { if (!insertCharacter(*sequence)) return 0; sequence += 1; } return 1; } static int insertKey_LinuxScreen (ScreenKey key) { int ok = 0; int mode; if (controlCurrentConsole(KDGKBMODE, &mode) != -1) { switch (mode) { case K_RAW: if (insertCode(key, 1)) ok = 1; break; case K_MEDIUMRAW: if (insertCode(key, 0)) ok = 1; break; case K_XLATE: if (insertTranslated(key, insertXlate)) ok = 1; break; case K_UNICODE: if (insertTranslated(key, insertUnicode)) ok = 1; break; #ifdef K_OFF case K_OFF: ok = 1; break; #endif /* K_OFF */ default: logMessage(LOG_WARNING, "unsupported keyboard mode: %d", mode); break; } } else { logSystemError("ioctl[KDGKBMODE]"); } return ok; } typedef struct { char subcode; struct tiocl_selection selection; } PACKED RegionSelectionArgument; static int selectRegion (RegionSelectionArgument *argument) { if (controlCurrentConsole(TIOCLINUX, argument) != -1) return 1; if (errno != EINVAL) logSystemError("ioctl[TIOCLINUX]"); return 0; } static int highlightRegion_LinuxScreen (int left, int right, int top, int bottom) { RegionSelectionArgument argument = { .subcode = TIOCL_SETSEL, .selection = { .xs = left + 1, .ys = top + 1, .xe = right + 1, .ye = bottom + 1, .sel_mode = TIOCL_SELCHAR } }; return selectRegion(&argument); } static int unhighlightRegion_LinuxScreen (void) { RegionSelectionArgument argument = { .subcode = TIOCL_SETSEL, .selection = { .xs = 0, .ys = 0, .xe = 0, .ye = 0, .sel_mode = TIOCL_SELCLEAR } }; return selectRegion(&argument); } static int validateVt (int vt) { if ((vt >= 1) && (vt <= MAX_NR_CONSOLES)) return 1; logMessage(LOG_WARNING, "virtual terminal out of range: %d", vt); return 0; } static int selectVirtualTerminal_LinuxScreen (int vt) { if (vt == virtualTerminalNumber) return 1; if (vt && !validateVt(vt)) return 0; return setCurrentScreen(vt); } static int switchVirtualTerminal_LinuxScreen (int vt) { if (validateVt(vt)) { if (selectVirtualTerminal_LinuxScreen(0)) { if (controlMainConsole(VT_ACTIVATE, (void *)(intptr_t)vt) != -1) { logMessage(LOG_CATEGORY(SCREEN_DRIVER), "switched to virtual tertminal %d", vt); return 1; } else { logSystemError("ioctl[VT_ACTIVATE]"); } } } return 0; } static int currentVirtualTerminal_LinuxScreen (void) { return currentConsoleNumber; } static int userVirtualTerminal_LinuxScreen (int number) { return MAX_NR_CONSOLES + 1 + number; } static int handleCommand_LinuxScreen (int command) { int blk = command & BRL_MSK_BLK; int arg UNUSED = command & BRL_MSK_ARG; int cmd = blk | arg; switch (cmd) { default: #ifdef HAVE_LINUX_INPUT_H switch (blk) { case BRL_CMD_BLK(PASSXT): if (command & BRL_FLG_KBD_RELEASE) arg |= XT_BIT_RELEASE; { int handled = 0; if (command & BRL_FLG_KBD_EMUL0) { xtKeys = linuxKeyMap_xtE0; } else if (arg == XT_MOD_E0) { xtKeys = linuxKeyMap_xtE0; handled = 1; } else if (command & BRL_FLG_KBD_EMUL1) { xtKeys = linuxKeyMap_xtE1; } else if (arg == XT_MOD_E1) { xtKeys = linuxKeyMap_xtE1; handled = 1; } if (handled) return 1; } { LinuxKeyCode key = xtKeys[arg & ~XT_BIT_RELEASE]; int press = !(arg & XT_BIT_RELEASE); xtKeys = linuxKeyMap_xt00; if (key) return injectKeyEvent(key, press); } break; case BRL_CMD_BLK(PASSAT): { int handled = 0; if (command & BRL_FLG_KBD_RELEASE) { atKeyPressed = 0; } else if (arg == AT_MOD_RELEASE) { atKeyPressed = 0; handled = 1; } if (command & BRL_FLG_KBD_EMUL0) { atKeys = linuxKeyMap_atE0; } else if (arg == AT_MOD_E0) { atKeys = linuxKeyMap_atE0; handled = 1; } else if (command & BRL_FLG_KBD_EMUL1) { atKeys = linuxKeyMap_atE1; } else if (arg == AT_MOD_E1) { atKeys = linuxKeyMap_atE1; handled = 1; } if (handled) return 1; } { LinuxKeyCode key = atKeys[arg]; int press = atKeyPressed; atKeys = linuxKeyMap_at00; atKeyPressed = 1; if (key) return injectKeyEvent(key, press); } break; case BRL_CMD_BLK(PASSPS2): { int handled = 0; if (command & BRL_FLG_KBD_RELEASE) { ps2KeyPressed = 0; } else if (arg == PS2_MOD_RELEASE) { ps2KeyPressed = 0; handled = 1; } if (handled) return 1; } { LinuxKeyCode key = linuxKeyMap_ps2[arg]; int press = ps2KeyPressed; ps2KeyPressed = 1; if (key) return injectKeyEvent(key, press); } break; default: break; } #endif /* HAVE_LINUX_INPUT_H */ break; } return 0; } static void scr_initialize (MainScreen *main) { initializeRealScreen(main); gpmIncludeScreenHandlers(main); main->base.poll = poll_LinuxScreen; main->base.refresh = refresh_LinuxScreen; main->base.describe = describe_LinuxScreen; main->base.readCharacters = readCharacters_LinuxScreen; main->base.insertKey = insertKey_LinuxScreen; main->base.highlightRegion = highlightRegion_LinuxScreen; main->base.unhighlightRegion = unhighlightRegion_LinuxScreen; main->base.selectVirtualTerminal = selectVirtualTerminal_LinuxScreen; main->base.switchVirtualTerminal = switchVirtualTerminal_LinuxScreen; main->base.currentVirtualTerminal = currentVirtualTerminal_LinuxScreen; main->base.handleCommand = handleCommand_LinuxScreen; main->processParameters = processParameters_LinuxScreen; main->releaseParameters = releaseParameters_LinuxScreen; main->construct = construct_LinuxScreen; main->destruct = destruct_LinuxScreen; main->userVirtualTerminal = userVirtualTerminal_LinuxScreen; }