From 7c577a8971ff7164f5fc9e2c0fd59b62db462983 Mon Sep 17 00:00:00 2001 From: Takashi Yano Date: Sun, 31 Mar 2019 07:41:56 +0900 Subject: [PATCH 1/3] Cygwin: console: support 24 bit color - Add 24 bit color support using xterm compatibility mode in Windows 10 build 14931 or later. - Add fake 24 bit color support for legacy console, which uses the nearest color from 16 system colors. --- winsup/cygwin/fhandler.h | 5 + winsup/cygwin/fhandler_console.cc | 237 ++++++++++++++++++++++++++---- winsup/cygwin/select.cc | 8 + 3 files changed, 218 insertions(+), 32 deletions(-) diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index b336eb63a..3f179a9f6 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -1778,6 +1778,8 @@ enum ansi_intensity #define eattitle 7 #define gotparen 8 #define gotrparen 9 +#define eatpalette 10 +#define endpalette 11 #define MAXARGS 10 enum cltype @@ -1791,6 +1793,9 @@ enum cltype class dev_console { + bool cap24bit_color; /* 24bit-color capability */ + pid_t owner; + WORD default_color, underline_color, dim_color; /* Used to determine if an input keystroke should be modified with META. */ diff --git a/winsup/cygwin/fhandler_console.cc b/winsup/cygwin/fhandler_console.cc index 281c2005c..12f450834 100644 --- a/winsup/cygwin/fhandler_console.cc +++ b/winsup/cygwin/fhandler_console.cc @@ -15,6 +15,7 @@ details. */ #include #include #include +#include #include "cygerrno.h" #include "security.h" #include "path.h" @@ -32,6 +33,16 @@ details. */ #include "child_info.h" #include "cygwait.h" +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING +#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 +#endif /* ENABLE_VIRTUAL_TERMINAL_PROCESSING */ +#ifndef DISABLE_NEWLINE_AUTO_RETURN +#define DISABLE_NEWLINE_AUTO_RETURN 0x0008 +#endif /* DISABLE_NEWLINE_AUTO_RETURN */ +#ifndef ENABLE_VIRTUAL_TERMINAL_INPUT +#define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200 +#endif /* ENABLE_VIRTUAL_TERMINAL_INPUT */ + /* Don't make this bigger than NT_MAX_PATH as long as the temporary buffer is allocated using tmp_pathbuf!!! */ #define CONVERT_LIMIT NT_MAX_PATH @@ -148,7 +159,11 @@ fhandler_console::set_unit () if (created) shared_console_info->tty_min_state.setntty (DEV_CONS_MAJOR, console_unit (me)); devset = (fh_devices) shared_console_info->tty_min_state.getntty (); + if (created) + con.owner = getpid (); } + if (!created && shared_console_info && kill (con.owner, 0) == -1) + con.owner = getpid (); dev ().parse (devset); if (devset != FH_ERROR) @@ -167,33 +182,44 @@ void fhandler_console::setup () { if (set_unit ()) - { - con.scroll_region.Bottom = -1; - con.dwLastCursorPosition.X = -1; - con.dwLastCursorPosition.Y = -1; - con.dwLastMousePosition.X = -1; - con.dwLastMousePosition.Y = -1; - con.dwLastButtonState = 0; /* none pressed */ - con.last_button_code = 3; /* released */ - con.underline_color = FOREGROUND_GREEN | FOREGROUND_BLUE; - con.dim_color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; - con.meta_mask = LEFT_ALT_PRESSED; - /* Set the mask that determines if an input keystroke is modified by - META. We set this based on the keyboard layout language loaded - for the current thread. The left key always generates - META, but the right key only generates META if we are using - an English keyboard because many "international" keyboards - replace common shell symbols ('[', '{', etc.) with accented - language-specific characters (umlaut, accent grave, etc.). On - these keyboards right (called AltGr) is used to produce the - shell symbols and should not be interpreted as META. */ - if (PRIMARYLANGID (LOWORD (GetKeyboardLayout (0))) == LANG_ENGLISH) - con.meta_mask |= RIGHT_ALT_PRESSED; - con.set_default_attr (); - con.backspace_keycode = CERASE; - con.cons_rapoi = NULL; - shared_console_info->tty_min_state.is_console = true; - } + { + con.scroll_region.Bottom = -1; + con.dwLastCursorPosition.X = -1; + con.dwLastCursorPosition.Y = -1; + con.dwLastMousePosition.X = -1; + con.dwLastMousePosition.Y = -1; + con.dwLastButtonState = 0; /* none pressed */ + con.last_button_code = 3; /* released */ + con.underline_color = FOREGROUND_GREEN | FOREGROUND_BLUE; + con.dim_color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; + con.meta_mask = LEFT_ALT_PRESSED; + /* Set the mask that determines if an input keystroke is modified by + META. We set this based on the keyboard layout language loaded + for the current thread. The left key always generates + META, but the right key only generates META if we are using + an English keyboard because many "international" keyboards + replace common shell symbols ('[', '{', etc.) with accented + language-specific characters (umlaut, accent grave, etc.). On + these keyboards right (called AltGr) is used to produce the + shell symbols and should not be interpreted as META. */ + if (PRIMARYLANGID (LOWORD (GetKeyboardLayout (0))) == LANG_ENGLISH) + con.meta_mask |= RIGHT_ALT_PRESSED; + con.set_default_attr (); + con.backspace_keycode = CERASE; + con.cons_rapoi = NULL; + shared_console_info->tty_min_state.is_console = true; + /* Check if 24bit color is available */ + DWORD dwVersion = GetVersion (); + dwVersion = (LOBYTE (LOWORD (dwVersion)) << 24) + | (HIBYTE (LOWORD (dwVersion)) << 16) | HIWORD (dwVersion); + if (dwVersion >= ((10 << 24) | (0 << 16) | 14931)) + { + con.cap24bit_color = true; + /* If system has 24bit color capability, + use xterm compatible mode. */ + setenv ("TERM", "xterm-256color", 1); + } + } } /* Return the tty structure associated with a given tty number. If the @@ -435,7 +461,8 @@ fhandler_console::read (void *pv, size_t& buflen) toadd = tmp; } /* Allow Ctrl-Space to emit ^@ */ - else if (input_rec.Event.KeyEvent.wVirtualKeyCode == VK_SPACE + else if (input_rec.Event.KeyEvent.wVirtualKeyCode + == (con.cap24bit_color ? '2' : VK_SPACE) && (ctrl_key_state & CTRL_PRESSED) && !(ctrl_key_state & ALT_PRESSED)) toadd = ""; @@ -855,10 +882,23 @@ fhandler_console::open (int flags, mode_t) get_ttyp ()->rstcons (false); set_open_status (); + if (getpid () == con.owner && con.cap24bit_color) + { + DWORD dwMode; + /* Enable xterm compatible mode in output */ + GetConsoleMode (get_output_handle (), &dwMode); + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode (get_output_handle (), dwMode); + /* Enable xterm compatible mode in input */ + GetConsoleMode (get_handle (), &dwMode); + dwMode |= ENABLE_VIRTUAL_TERMINAL_INPUT; + SetConsoleMode (get_handle (), dwMode); + } + DWORD cflags; if (GetConsoleMode (get_handle (), &cflags)) - SetConsoleMode (get_handle (), - ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT | cflags); + SetConsoleMode (get_handle (), ENABLE_WINDOW_INPUT + | (con.cap24bit_color ? 0 : ENABLE_MOUSE_INPUT) | cflags); debug_printf ("opened conin$ %p, conout$ %p", get_handle (), get_output_handle ()); @@ -878,6 +918,21 @@ fhandler_console::open_setup (int flags) int fhandler_console::close () { + debug_printf ("closing: %p, %p", get_handle (), get_output_handle ()); + + if (shared_console_info && getpid () == con.owner && con.cap24bit_color) + { + DWORD dwMode; + /* Disable xterm compatible mode in input */ + GetConsoleMode (get_handle (), &dwMode); + dwMode &= ~ENABLE_VIRTUAL_TERMINAL_INPUT; + SetConsoleMode (get_handle (), dwMode); + /* Disable xterm compatible mode in output */ + GetConsoleMode (get_output_handle (), &dwMode); + dwMode &= ~ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode (get_output_handle (), dwMode); + } + CloseHandle (get_handle ()); CloseHandle (get_output_handle ()); if (!have_execed) @@ -987,6 +1042,13 @@ fhandler_console::output_tcsetattr (int, struct termios const *t) /* All the output bits we can ignore */ DWORD flags = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT; + /* If system has 24bit color capability, use xterm compatible mode. */ + if (con.cap24bit_color) + { + flags |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + if (!(t->c_oflag & OPOST) || !(t->c_oflag & ONLCR)) + flags |= DISABLE_NEWLINE_AUTO_RETURN; + } int res = SetConsoleMode (get_output_handle (), flags) ? 0 : -1; if (res) @@ -1043,7 +1105,10 @@ fhandler_console::input_tcsetattr (int, struct termios const *t) flags |= ENABLE_PROCESSED_INPUT; } - flags |= ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT; + flags |= ENABLE_WINDOW_INPUT | (con.cap24bit_color ? 0 : ENABLE_MOUSE_INPUT); + /* if system has 24bit color capability, use xterm compatible mode. */ + if (con.cap24bit_color) + flags |= ENABLE_VIRTUAL_TERMINAL_INPUT; int res; if (flags == oflags) @@ -1602,11 +1667,32 @@ static const char base_chars[256] = /*F0 F1 F2 F3 F4 F5 F6 F7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*F8 F9 FA FB FC FD FE FF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR }; +static const char table256[256] = +{ + 0, 4, 2, 6, 1, 5, 3, 7, 8,12,10,14, 9,13,11,15, + 0, 1, 1, 1, 9, 9, 2, 3, 3, 3, 3, 9, 2, 3, 3, 3, + 3,11, 2, 3, 3, 3,11,11,10, 3, 3,11,11,11,10,10, + 11,11,11,11, 4, 5, 5, 5, 5, 9, 6, 8, 8, 8, 8, 9, + 6, 8, 8, 8, 8, 7, 6, 8, 8, 8, 7, 7, 6, 8, 8, 7, + 7,11,10,10, 7, 7,11,11, 4, 5, 5, 5, 5,13, 6, 8, + 8, 8, 8, 7, 6, 8, 8, 8, 7, 7, 6, 8, 8, 7, 7, 7, + 6, 8, 7, 7, 7, 7,14, 7, 7, 7, 7, 7, 4, 5, 5, 5, + 13,13, 6, 8, 8, 8, 7, 7, 6, 8, 8, 7, 7, 7, 6, 8, + 7, 7, 7, 7,14, 7, 7, 7, 7, 7,14, 7, 7, 7, 7,15, + 12, 5, 5,13,13,13, 6, 8, 8, 7, 7,13, 6, 8, 7, 7, + 7, 7,14, 7, 7, 7, 7, 7,14, 7, 7, 7, 7,15,14,14, + 7, 7,15,15,12,12,13,13,13,13,12,12, 7, 7,13,13, + 14, 7, 7, 7, 7, 7,14, 7, 7, 7, 7,15,14,14, 7, 7, + 15,15,14,14, 7,15,15,15, 0, 0, 0, 0, 0, 0, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7,15,15 +}; + void fhandler_console::char_command (char c) { int x, y, n; char buf[40]; + int r, g, b; switch (c) { @@ -1678,6 +1764,40 @@ fhandler_console::char_command (char c) case 37: /* WHITE foreg */ con.fg = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED; break; + case 38: + if (con.nargs < 1) + /* Sequence error (abort) */ + break; + switch (con.args[1]) + { + case 2: + if (con.nargs != 4) + /* Sequence error (abort) */ + break; + r = con.args[2]; + g = con.args[3]; + b = con.args[4]; + r = r < (95 + 1) / 2 ? 0 : r > 255 ? 5 : (r - 55 + 20) / 40; + g = g < (95 + 1) / 2 ? 0 : g > 255 ? 5 : (g - 55 + 20) / 40; + b = b < (95 + 1) / 2 ? 0 : b > 255 ? 5 : (b - 55 + 20) / 40; + con.fg = table256[16 + r*36 + g*6 + b]; + break; + case 5: + if (con.nargs != 2) + /* Sequence error (abort) */ + break; + { + int idx = con.args[2]; + if (idx < 0) + idx = 0; + if (idx > 255) + idx = 255; + con.fg = table256[idx]; + } + break; + } + i += con.nargs; + break; case 39: con.fg = con.default_color & FOREGROUND_ATTR_MASK; break; @@ -1705,6 +1825,40 @@ fhandler_console::char_command (char c) case 47: /* WHITE background */ con.bg = BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED; break; + case 48: + if (con.nargs < 1) + /* Sequence error (abort) */ + break; + switch (con.args[1]) + { + case 2: + if (con.nargs != 4) + /* Sequence error (abort) */ + break; + r = con.args[2]; + g = con.args[3]; + b = con.args[4]; + r = r < (95 + 1) / 2 ? 0 : r > 255 ? 5 : (r - 55 + 20) / 40; + g = g < (95 + 1) / 2 ? 0 : g > 255 ? 5 : (g - 55 + 20) / 40; + b = b < (95 + 1) / 2 ? 0 : b > 255 ? 5 : (b - 55 + 20) / 40; + con.bg = table256[16 + r*36 + g*6 + b] << 4; + break; + case 5: + if (con.nargs != 2) + /* Sequence error (abort) */ + break; + { + int idx = con.args[2]; + if (idx < 0) + idx = 0; + if (idx > 255) + idx = 255; + con.bg = table256[idx] << 4; + } + break; + } + i += con.nargs; + break; case 49: con.bg = con.default_color & BACKGROUND_ATTR_MASK; break; @@ -2143,10 +2297,12 @@ fhandler_console::write_normal (const unsigned char *src, /* Loop over src buffer as long as we have just simple characters. Stop as soon as we reach the conversion limit, or if we encounter a control character or a truncated or invalid mutibyte sequence. */ + /* If system has 24bit color capability, just write all control + sequences to console since xterm compatible mode is enabled. */ memset (&ps, 0, sizeof ps); while (found < end && found - src < CONVERT_LIMIT - && base_chars[*found] == NOR) + && (con.cap24bit_color || base_chars[*found] == NOR) ) { switch (ret = f_mbtowc (_REENT, NULL, (const char *) found, end - found, &ps)) @@ -2394,6 +2550,8 @@ fhandler_console::write (const void *vsrc, size_t len) con.rarg = con.rarg * 10 + (*src - '0'); else if (*src == ';' && (con.rarg == 2 || con.rarg == 0)) con.state = gettitle; + else if (*src == ';' && (con.rarg == 4 || con.rarg == 104)) + con.state = eatpalette; else con.state = eattitle; src++; @@ -2416,6 +2574,21 @@ fhandler_console::write (const void *vsrc, size_t len) src++; break; } + case eatpalette: + if (*src == '\033') + con.state = endpalette; + else if (*src == '\a') + con.state = normal; + src++; + break; + case endpalette: + if (*src == '\\') + con.state = normal; + else + /* Sequence error (abort) */ + con.state = normal; + src++; + break; case gotsquare: if (*src == ';') { diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc index 9b18e8f80..28adcf3e7 100644 --- a/winsup/cygwin/select.cc +++ b/winsup/cygwin/select.cc @@ -1053,6 +1053,14 @@ peek_console (select_record *me, bool) else if (irec.Event.KeyEvent.uChar.UnicodeChar || fhandler_console::get_nonascii_key (irec, tmpbuf)) return me->read_ready = true; + /* Allow Ctrl-Space for ^@ */ + else if ( (irec.Event.KeyEvent.wVirtualKeyCode == VK_SPACE + || irec.Event.KeyEvent.wVirtualKeyCode == '2') + && (irec.Event.KeyEvent.dwControlKeyState & + (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) + && !(irec.Event.KeyEvent.dwControlKeyState + & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) ) + return me->read_ready = true; } /* Ignore key up events, except for Alt+Numpad events. */ else if (is_alt_numpad_event (&irec)) -- 2.17.0