| /* |
| * 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 <limits.h> |
| #include <fcntl.h> |
| #include <sys/stat.h> |
| #include <sys/ioctl.h> |
| #include <linux/tty.h> |
| #include <linux/vt.h> |
| #include <linux/kd.h> |
| #include <linux/tiocl.h> |
| #include <linux/major.h> |
| |
| #ifndef VT_GETHIFONTMASK |
| #define VT_GETHIFONTMASK 0X560D |
| #endif /* VT_GETHIFONTMASK */ |
| |
| #include "log.h" |
| #include "report.h" |
| #include "message.h" |
| #include "async_handle.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_RPI_SPACES_BUG, |
| PARM_UNICODE, |
| PARM_VIRTUAL_TERMINAL_NUMBER, |
| PARM_WIDECHAR_PADDING, |
| } ScreenParameters; |
| #define SCRPARMS "charset", "fallbacktext", "hfb", "logsfm", "rpispacesbug", "unicode", "vt", "widecharpadding" |
| |
| #include "scr_driver.h" |
| #include "screen.h" |
| |
| static const char *problemText; |
| static const char *fallbackText; |
| |
| static unsigned int logScreenFontMap; |
| static unsigned int rpiSpacesBug; |
| static unsigned int unicodeEnabled; |
| static int virtualTerminalNumber; |
| static unsigned int widecharPadding; |
| |
| #define UNICODE_ROW_DIRECT 0XF000 |
| |
| typedef enum { |
| CONV_OK, |
| CONV_ILLEGAL, |
| CONV_SHORT, |
| CONV_OVERFLOW, |
| CONV_ERROR |
| } CharacterConversionResult; |
| |
| #if defined(HAVE_ICONV_H) |
| #include <iconv.h> |
| |
| 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) { |
| if (rpiSpacesBug) { |
| uint32_t *character = buffer; |
| const uint32_t *end = character + (count / sizeof(*character)); |
| |
| while (character < end) { |
| if (*character == 0X20202020) { |
| static unsigned char bugLogged = 0; |
| |
| if (!bugLogged) { |
| logMessage(LOG_WARNING, "Linux screen driver: RPI spaces bug detected"); |
| bugLogged = 1; |
| } |
| |
| *character = ' '; |
| } |
| |
| character += 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 <= unicodeCacheUsed) { |
| size_t left = unicodeCacheUsed - 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 <poll.h> |
| |
| 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; i<screenFontMapCount; i+=1) { |
| const struct unipair *map = &screenFontMapTable[i]; |
| |
| logMessage(LOG_CATEGORY(SCREEN_DRIVER), |
| "SFM[%03u]: U+%04X@%04X", |
| i, map->unicode, 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; index<screenFontMapCount; ++index) { |
| const struct unipair *map = &screenFontMapTable[index]; |
| |
| if (vgaCharacterCount <= map->fontpos) 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<count; index+=1) { |
| counts[(buffer[index] & 0X0F00) >> 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<count; i+=1) { |
| translationTable[i] = UNICODE_ROW_DIRECT | i; |
| } |
| |
| { |
| unsigned int screenFontMapIndex = screenFontMapCount; |
| |
| while (screenFontMapIndex > 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 if (widecharPadding) { |
| blanks = 0; |
| } 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 <linux/input.h> |
| |
| 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); |
| } |
| } |
| } |
| |
| rpiSpacesBug = 0; |
| { |
| const char *parameter = parameters[PARM_RPI_SPACES_BUG]; |
| |
| if (parameter && *parameter) { |
| if (!validateYesNo(&rpiSpacesBug, parameter)) { |
| logMessage(LOG_WARNING, "%s: %s", "invalid RPI spaces bug 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); |
| } |
| } |
| } |
| |
| widecharPadding = 0; |
| { |
| const char *parameter = parameters[PARM_WIDECHAR_PADDING]; |
| |
| if (parameter && *parameter) { |
| if (!validateYesNo(&widecharPadding, parameter)) { |
| logMessage(LOG_WARNING, "%s: %s", "invalid widechar padding setting", 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; row<box->height; 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) { |
| logMessage( |
| LOG_CATEGORY(SCREEN_DRIVER) | LOG_DEBUG, |
| "injecting key %s: %02X", |
| (press? "press": "release"), key |
| ); |
| |
| 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) { |
| logMessage( |
| LOG_CATEGORY(SCREEN_DRIVER) | LOG_DEBUG, |
| "inserting byte: %02X", byte |
| ); |
| |
| if (controlCurrentConsole(TIOCSTI, &byte) != -1) return 1; |
| logSystemError("ioctl[TIOCSTI]"); |
| logPossibleCause("BRLTTY is running without the CAP_SYS_ADMIN kernel capability (see man 7 capabilities)"); |
| logPossibleCause("the sysctl parameter dev.tty.legacy_tiocsti is off (see https://lore.kernel.org/linux-hardening/Y0m9l52AKmw6Yxi1@hostpad/)"); |
| |
| message( |
| NULL, "Linux character injection (TIOCSTI) is disabled on this system", |
| (MSG_SILENT) |
| ); |
| |
| 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 = ASCII_ESC; |
| break; |
| |
| case K_METABIT: |
| if (*character >= 0X80) { |
| logMessage(LOG_WARNING, "can't add meta bit to character: U+%04X", (uint32_t)*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; |
| } |