| /* |
| * BRLTTY - A background process providing access to the console screen (when in |
| * text mode) for a blind person using a refreshable braille display. |
| * |
| * Copyright (C) 1995-2023 by The BRLTTY Developers. |
| * |
| * BRLTTY comes with ABSOLUTELY NO WARRANTY. |
| * |
| * This is free software, placed under the terms of the |
| * GNU Lesser General Public License, as published by the Free Software |
| * Foundation; either version 2.1 of the License, or (at your option) any |
| * later version. Please see the file LICENSE-LGPL for details. |
| * |
| * Web Page: http://brltty.app/ |
| * |
| * This software is maintained by Dave Mielke <dave@mielke.cc>. |
| */ |
| |
| #include "prologue.h" |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <sys/stat.h> |
| #include <sys/ioctl.h> |
| #include <sys/mman.h> |
| #include <hurd/console.h> |
| |
| #include "log.h" |
| #include "brl_cmds.h" |
| #include "utf8.h" |
| |
| #include "scr_driver.h" |
| #include "screen.h" |
| #include "unicode.h" |
| |
| static char * |
| vtPath (const char *base, unsigned char vt) { |
| size_t length = strlen(base); |
| char buffer[length+1]; |
| snprintf(buffer, length, base, vt); |
| buffer[length] = 0; |
| return strdup(buffer); |
| } |
| |
| static int |
| currentVt(void) { |
| char link[]=HURD_VCSDIR "/00"; |
| char *c,*d; |
| int size,ret; |
| if ((size = readlink(HURD_CURVCSPATH,link,sizeof(link))) == -1) { |
| /* console may not be started yet or in X mode, don't flood */ |
| if (errno != ENOENT && errno != ENOTDIR && errno != ENODEV) |
| logSystemError("reading " HURD_CURVCSPATH " link"); |
| return 1; |
| } |
| link[size]='\0'; |
| if (!(c = strrchr(link,'/'))) |
| c = link; |
| else |
| c++; |
| if (!*c) |
| /* bad number */ |
| return 1; |
| ret = strtol(c,&d,10); |
| if (*d) |
| /* bad number */ |
| return 1; |
| return ret; |
| } |
| |
| static int |
| openDevice (const char *path, const char *description, int flags) { |
| int file; |
| logMessage(LOG_DEBUG, "Opening %s device: %s", description, path); |
| if ((file = open(path, flags)) == -1) { |
| logMessage(LOG_ERR, "Cannot open %s device: %s: %s", |
| description, path, strerror(errno)); |
| } |
| return file; |
| } |
| |
| static const char *const consolePath = HURD_INPUTPATH; |
| static int consoleDescriptor; |
| |
| static void |
| closeConsole (void) { |
| if (consoleDescriptor != -1) { |
| if (close(consoleDescriptor) == -1) { |
| logSystemError("Console close"); |
| } |
| logMessage(LOG_DEBUG, "Console closed: fd=%d", consoleDescriptor); |
| consoleDescriptor = -1; |
| } |
| } |
| |
| static int |
| openConsole (unsigned char vt) { |
| char *path = vtPath(consolePath, vt?vt:currentVt()); |
| if (path) { |
| int console = openDevice(path, "console", O_RDWR|O_NOCTTY); |
| if (console != -1) { |
| closeConsole(); |
| consoleDescriptor = console; |
| logMessage(LOG_DEBUG, "Console opened: %s: fd=%d", path, consoleDescriptor); |
| free(path); |
| return 1; |
| } |
| logSystemError("Console open"); |
| free(path); |
| } |
| return 0; |
| } |
| |
| static const char *const screenPath = HURD_DISPLAYPATH; |
| static int screenDescriptor; |
| static const struct cons_display *screenMap; |
| static size_t screenMapSize; |
| #define screenDisplay ((conchar_t *)((wchar_t *) screenMap + screenMap->screen.matrix)) |
| static unsigned char virtualTerminal; /* currently shown (0 means system's) */ |
| static unsigned char lastReadVt; /* last shown */ |
| |
| static void |
| closeScreen (void) { |
| if (screenDescriptor != -1) { |
| if (munmap((void *) screenMap, screenMapSize) == -1) { |
| logSystemError("Screen unmap"); |
| } |
| if (close(screenDescriptor) == -1) { |
| logSystemError("Screen close"); |
| } |
| logMessage(LOG_DEBUG, "Screen closed: fd=%d mmap=%p", screenDescriptor, screenMap); |
| screenDescriptor = -1; |
| screenMap = NULL; |
| } |
| } |
| |
| static int |
| openScreen (unsigned char vt) { |
| char *path = vtPath(screenPath, vt?vt:currentVt()); |
| if (path) { |
| int screen = openDevice(path, "screen", O_RDONLY); |
| if (screen != -1) { |
| struct stat st; |
| if (fstat(screen, &st) != -1) { |
| const struct cons_display *map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, screen, 0); |
| if (map != MAP_FAILED) { |
| if (map->magic == CONS_MAGIC && map->version >> CONS_VERSION_MAJ_SHIFT ==0 && openConsole(vt)) { |
| closeScreen(); |
| screenDescriptor = screen; |
| screenMap = map; |
| screenMapSize = st.st_size; |
| logMessage(LOG_DEBUG, "Screen opened: %s: fd=%d map=%p", path, screenDescriptor, screenMap); |
| free(path); |
| return 1; |
| } |
| munmap((void *) map, st.st_size); |
| } else logSystemError("Screen map"); |
| } else logSystemError("Getting size of Screen"); |
| close(screen); |
| } |
| free(path); |
| } |
| return 0; |
| } |
| |
| static int |
| processParameters_HurdScreen (char **parameters) { |
| return 1; |
| } |
| |
| static int |
| construct_HurdScreen (void) { |
| screenDescriptor = -1; |
| consoleDescriptor = -1; |
| return openScreen(0); |
| } |
| |
| static void |
| destruct_HurdScreen (void) { |
| closeConsole(); |
| closeScreen(); |
| } |
| |
| static void |
| getScreenDescription (ScreenDescription *description) { |
| description->rows = screenMap->screen.height; |
| description->cols = screenMap->screen.width; |
| description->posx = screenMap->cursor.col; |
| description->posy = screenMap->cursor.row; |
| } |
| |
| static void |
| getConsoleDescription (ScreenDescription *description) { |
| description->number = virtualTerminal ? virtualTerminal : currentVt(); |
| } |
| |
| static void |
| describe_HurdScreen (ScreenDescription *description) { |
| getScreenDescription(description); |
| getConsoleDescription(description); |
| } |
| |
| #ifndef offsetof |
| #define offsetof(type,field) ((size_t) &((type *) 0)->field) |
| #endif |
| static int |
| readCharacters_HurdScreen (const ScreenBox *box, ScreenCharacter *buffer) { |
| uint32_t lines, start, row, col; |
| ScreenDescription description; |
| describe_HurdScreen(&description); |
| if (lastReadVt != description.number) { |
| openScreen(description.number); |
| lastReadVt = description.number; |
| } |
| if (!validateScreenBox(box, description.cols, description.rows)) return 0; |
| |
| lines = screenMap->screen.lines; |
| start = screenMap->screen.cur_line; |
| for (row=start+box->top; row<start+box->top+box->height; ++row) |
| for (col=box->left; col<box->left+box->width; ++col) { |
| wchar_t text; |
| conchar_attr_t attr; |
| text = screenDisplay[(row%lines)*description.cols+col].chr; |
| #ifdef CONS_WCHAR_MASK |
| text &= CONS_WCHAR_MASK; |
| #endif |
| #ifdef CONS_WCHAR_CONTINUED |
| if (text & CONS_WCHAR_CONTINUED) |
| text = UNICODE_ZERO_WIDTH_SPACE; |
| #endif |
| buffer->text = text; |
| attr = screenDisplay[(row%lines)*description.cols+col].attr; |
| buffer->attributes = attr.fgcol | (attr.bgcol << 4) |
| | (attr.intensity == CONS_ATTR_INTENSITY_BOLD?SCR_ATTR_FG_BRIGHT:0) |
| | (attr.blinking?SCR_ATTR_BLINK:0); |
| buffer++; |
| } |
| return 1; |
| } |
| |
| static int |
| insertByte (unsigned char byte) { |
| if (write(consoleDescriptor, &byte, 1) != -1) |
| return 1; |
| logSystemError("Console write"); |
| return 0; |
| } |
| |
| static int |
| insertMapped (ScreenKey key, int (*insertCharacter)(wchar_t character)) { |
| wchar_t buffer[2]; |
| wchar_t *sequence; |
| wchar_t *end; |
| |
| setScreenKeyModifiers(&key, 0); |
| |
| if (isSpecialKey(key)) { |
| switch (key & SCR_KEY_CHAR_MASK) { |
| 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_FUNCTION + 0: |
| sequence = WS_C("\x1bOP"); |
| break; |
| case SCR_KEY_FUNCTION + 1: |
| sequence = WS_C("\x1bOQ"); |
| break; |
| case SCR_KEY_FUNCTION + 2: |
| sequence = WS_C("\x1bOR"); |
| break; |
| case SCR_KEY_FUNCTION + 3: |
| sequence = WS_C("\x1bOS"); |
| break; |
| case SCR_KEY_FUNCTION + 4: |
| sequence = WS_C("\x1b[15~"); |
| break; |
| case SCR_KEY_FUNCTION + 5: |
| sequence = WS_C("\x1b[17~"); |
| break; |
| case SCR_KEY_FUNCTION + 6: |
| sequence = WS_C("\x1b[18~"); |
| break; |
| case SCR_KEY_FUNCTION + 7: |
| sequence = WS_C("\x1b[19~"); |
| break; |
| case SCR_KEY_FUNCTION + 8: |
| sequence = WS_C("\x1b[20~"); |
| break; |
| case SCR_KEY_FUNCTION + 9: |
| sequence = WS_C("\x1b[21~"); |
| break; |
| case SCR_KEY_FUNCTION + 10: |
| sequence = WS_C("\x1b[23~"); |
| break; |
| case SCR_KEY_FUNCTION + 11: |
| sequence = WS_C("\x1b[24~"); |
| break; |
| case SCR_KEY_FUNCTION + 12: |
| sequence = WS_C("\x1b[25~"); |
| break; |
| case SCR_KEY_FUNCTION + 13: |
| sequence = WS_C("\x1b[26~"); |
| break; |
| case SCR_KEY_FUNCTION + 14: |
| sequence = WS_C("\x1b[28~"); |
| break; |
| case SCR_KEY_FUNCTION + 15: |
| sequence = WS_C("\x1b[29~"); |
| break; |
| case SCR_KEY_FUNCTION + 16: |
| sequence = WS_C("\x1b[31~"); |
| break; |
| case SCR_KEY_FUNCTION + 17: |
| sequence = WS_C("\x1b[32~"); |
| break; |
| case SCR_KEY_FUNCTION + 18: |
| sequence = WS_C("\x1b[33~"); |
| break; |
| case SCR_KEY_FUNCTION + 19: |
| sequence = WS_C("\x1b[34~"); |
| break; |
| default: |
| logMessage(LOG_WARNING, "Key %04X not suported in ANSI mode.", key); |
| return 0; |
| } |
| end = sequence + wcslen(sequence); |
| } else { |
| sequence = end = buffer + ARRAY_COUNT(buffer); |
| *--sequence = key & SCR_KEY_CHAR_MASK; |
| |
| if (key & SCR_KEY_ALT_LEFT) |
| *--sequence = 0X1B; |
| } |
| |
| while (sequence != end) |
| if (!insertCharacter(*sequence++)) |
| return 0; |
| return 1; |
| } |
| |
| static int |
| insertUtf8 (wchar_t character) { |
| Utf8Buffer utf8; |
| size_t utfs = convertWcharToUtf8(character, utf8); |
| int i; |
| for (i=0; i<utfs; ++i) |
| if (!insertByte(utf8[i])) |
| return 0; |
| return 1; |
| } |
| |
| static int |
| insertKey_HurdScreen (ScreenKey key) { |
| logMessage(LOG_DEBUG, "Insert key: %4.4X", key); |
| return insertMapped(key, insertUtf8); |
| } |
| |
| static int |
| validateVt (int vt) { |
| if ((vt >= 1) && (vt <= 99)) return 1; |
| logMessage(LOG_DEBUG, "Virtual terminal %d is out of range.", vt); |
| return 0; |
| } |
| |
| static int |
| selectVirtualTerminal_HurdScreen (int vt) { |
| if (vt == virtualTerminal) return 1; |
| if (vt && !validateVt(vt)) return 0; |
| return openScreen(vt); |
| } |
| |
| static int |
| switchVirtualTerminal_HurdScreen (int vt) { |
| if (validateVt(vt)) { |
| char link[]=HURD_VCSDIR "/00"; |
| snprintf(link, sizeof(link), HURD_VCSDIR "/%u", vt); |
| if (symlink(link, HURD_CURVCSPATH) != -1) { |
| logMessage(LOG_DEBUG, "Switched to virtual terminal %d.", vt); |
| return 1; |
| } else { |
| logSystemError("symlinking to switch vt"); |
| } |
| } |
| return 0; |
| } |
| |
| static int |
| currentVirtualTerminal_HurdScreen (void) { |
| ScreenDescription description; |
| getConsoleDescription(&description); |
| return description.number; |
| } |
| |
| static void |
| scr_initialize (MainScreen *main) { |
| initializeRealScreen(main); |
| main->base.describe = describe_HurdScreen; |
| main->base.readCharacters = readCharacters_HurdScreen; |
| main->base.insertKey = insertKey_HurdScreen; |
| main->base.selectVirtualTerminal = selectVirtualTerminal_HurdScreen; |
| main->base.switchVirtualTerminal = switchVirtualTerminal_HurdScreen; |
| main->base.currentVirtualTerminal = currentVirtualTerminal_HurdScreen; |
| main->processParameters = processParameters_HurdScreen; |
| main->construct = construct_HurdScreen; |
| main->destruct = destruct_HurdScreen; |
| } |