From bd627864ab4189984cdb0892c00f91e39c4e8243 Mon Sep 17 00:00:00 2001 From: Takashi Yano Date: Mon, 1 Apr 2019 00:47:46 +0900 Subject: [PATCH] Cygwin: console: support 24 bit color - Add 24 bit color support using xterm compatibility mode in Windows 10 1703 or later. - Add fake 24 bit color support for legacy console, which uses the nearest color from 16 system colors. --- winsup/cygwin/environ.cc | 7 +- winsup/cygwin/fhandler.h | 4 + winsup/cygwin/fhandler_console.cc | 230 +++++++++++++++++++++++++----- winsup/cygwin/select.cc | 8 ++ winsup/cygwin/wincap.cc | 10 ++ winsup/cygwin/wincap.h | 2 + 6 files changed, 227 insertions(+), 34 deletions(-) diff --git a/winsup/cygwin/environ.cc b/winsup/cygwin/environ.cc index 21f13734cb..a47ed72e77 100644 --- a/winsup/cygwin/environ.cc +++ b/winsup/cygwin/environ.cc @@ -869,7 +869,8 @@ win32env_to_cygenv (PWCHAR rawenv, bool posify) char *newp; int i; int sawTERM = 0; - static char NO_COPY cygterm[] = "TERM=cygwin"; + const char cygterm[] = "TERM=cygwin"; + const char xterm[] = "TERM=xterm-256color"; char *tmpbuf = tp.t_get (); PWCHAR w; @@ -899,8 +900,10 @@ win32env_to_cygenv (PWCHAR rawenv, bool posify) debug_printf ("%p: %s", envp[i], envp[i]); } + /* If console has 24 bit color capability, TERM=xterm-256color, + otherwise, TERM=cygwin */ if (!sawTERM) - envp[i++] = strdup (cygterm); + envp[i++] = strdup (wincap.has_con_24bit_colors () ? xterm : cygterm); envp[i] = NULL; return envp; diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index b336eb63a3..66e724bcbb 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,8 @@ enum cltype class dev_console { + 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 281c2005cc..6b14d4a258 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,17 @@ details. */ #include "child_info.h" #include "cygwait.h" +/* Not yet defined in Mingw-w64 */ +#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 +160,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 +183,33 @@ 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; + } } /* Return the tty structure associated with a given tty number. If the @@ -435,7 +451,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 + == (wincap.has_con_24bit_colors () ? '2' : VK_SPACE) && (ctrl_key_state & CTRL_PRESSED) && !(ctrl_key_state & ALT_PRESSED)) toadd = ""; @@ -855,10 +872,24 @@ fhandler_console::open (int flags, mode_t) get_ttyp ()->rstcons (false); set_open_status (); + if (getpid () == con.owner && wincap.has_con_24bit_colors ()) + { + 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 + | (wincap.has_con_24bit_colors () ? 0 : ENABLE_MOUSE_INPUT) + | cflags); debug_printf ("opened conin$ %p, conout$ %p", get_handle (), get_output_handle ()); @@ -878,6 +909,22 @@ 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 && + wincap.has_con_24bit_colors ()) + { + 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 +1034,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 24 bit color capability, use xterm compatible mode. */ + if (wincap.has_con_24bit_colors ()) + { + 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 +1097,11 @@ fhandler_console::input_tcsetattr (int, struct termios const *t) flags |= ENABLE_PROCESSED_INPUT; } - flags |= ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT; + flags |= ENABLE_WINDOW_INPUT | + (wincap.has_con_24bit_colors () ? 0 : ENABLE_MOUSE_INPUT); + /* if system has 24 bit color capability, use xterm compatible mode. */ + if (wincap.has_con_24bit_colors ()) + flags |= ENABLE_VIRTUAL_TERMINAL_INPUT; int res; if (flags == oflags) @@ -1602,11 +1660,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 +1757,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 +1818,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 +2290,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 24 bit 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) + && (wincap.has_con_24bit_colors () || base_chars[*found] == NOR) ) { switch (ret = f_mbtowc (_REENT, NULL, (const char *) found, end - found, &ps)) @@ -2394,6 +2543,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 +2567,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 9b18e8f803..28adcf3e7c 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)) diff --git a/winsup/cygwin/wincap.cc b/winsup/cygwin/wincap.cc index 5bc9c37786..86dc631ecf 100644 --- a/winsup/cygwin/wincap.cc +++ b/winsup/cygwin/wincap.cc @@ -40,6 +40,7 @@ wincaps wincap_vista __attribute__((section (".cygwin_dll_common"), shared)) = { has_case_sensitive_dirs:false, has_posix_rename_semantics:false, no_msv1_0_s4u_logon_in_wow64:true, + has_con_24bit_colors:false, }, }; @@ -65,6 +66,7 @@ wincaps wincap_7 __attribute__((section (".cygwin_dll_common"), shared)) = { has_case_sensitive_dirs:false, has_posix_rename_semantics:false, no_msv1_0_s4u_logon_in_wow64:true, + has_con_24bit_colors:false, }, }; @@ -90,6 +92,7 @@ wincaps wincap_8 __attribute__((section (".cygwin_dll_common"), shared)) = { has_case_sensitive_dirs:false, has_posix_rename_semantics:false, no_msv1_0_s4u_logon_in_wow64:false, + has_con_24bit_colors:false, }, }; @@ -115,6 +118,7 @@ wincaps wincap_8_1 __attribute__((section (".cygwin_dll_common"), shared)) = { has_case_sensitive_dirs:false, has_posix_rename_semantics:false, no_msv1_0_s4u_logon_in_wow64:false, + has_con_24bit_colors:false, }, }; @@ -140,6 +144,7 @@ wincaps wincap_10_1507 __attribute__((section (".cygwin_dll_common"), shared)) has_case_sensitive_dirs:false, has_posix_rename_semantics:false, no_msv1_0_s4u_logon_in_wow64:false, + has_con_24bit_colors:false, }, }; @@ -165,6 +170,7 @@ wincaps wincap_10_1511 __attribute__((section (".cygwin_dll_common"), shared)) = has_case_sensitive_dirs:false, has_posix_rename_semantics:false, no_msv1_0_s4u_logon_in_wow64:false, + has_con_24bit_colors:false, }, }; @@ -190,6 +196,7 @@ wincaps wincap_10_1703 __attribute__((section (".cygwin_dll_common"), shared)) = has_case_sensitive_dirs:false, has_posix_rename_semantics:false, no_msv1_0_s4u_logon_in_wow64:false, + has_con_24bit_colors:true, }, }; @@ -215,6 +222,7 @@ wincaps wincap_10_1709 __attribute__((section (".cygwin_dll_common"), shared)) = has_case_sensitive_dirs:false, has_posix_rename_semantics:false, no_msv1_0_s4u_logon_in_wow64:false, + has_con_24bit_colors:true, }, }; @@ -240,6 +248,7 @@ wincaps wincap_10_1803 __attribute__((section (".cygwin_dll_common"), shared)) = has_case_sensitive_dirs:true, has_posix_rename_semantics:false, no_msv1_0_s4u_logon_in_wow64:false, + has_con_24bit_colors:true, }, }; @@ -265,6 +274,7 @@ wincaps wincap_10_1809 __attribute__((section (".cygwin_dll_common"), shared)) = has_case_sensitive_dirs:true, has_posix_rename_semantics:true, no_msv1_0_s4u_logon_in_wow64:false, + has_con_24bit_colors:true, }, }; diff --git a/winsup/cygwin/wincap.h b/winsup/cygwin/wincap.h index 0e83f6794e..73b6f5ffc5 100644 --- a/winsup/cygwin/wincap.h +++ b/winsup/cygwin/wincap.h @@ -34,6 +34,7 @@ struct wincaps unsigned has_case_sensitive_dirs : 1; unsigned has_posix_rename_semantics : 1; unsigned no_msv1_0_s4u_logon_in_wow64 : 1; + unsigned has_con_24bit_colors : 1; }; }; @@ -89,6 +90,7 @@ class wincapc bool IMPLEMENT (has_case_sensitive_dirs) bool IMPLEMENT (has_posix_rename_semantics) bool IMPLEMENT (no_msv1_0_s4u_logon_in_wow64) + bool IMPLEMENT (has_con_24bit_colors) void disable_case_sensitive_dirs () {