Skip to content

Commit

Permalink
Cygwin: console: support 24 bit color
Browse files Browse the repository at this point in the history
- 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.
  • Loading branch information
tyan0 authored and github-cygwin committed Mar 31, 2019
1 parent 7b8049f commit bd62786
Show file tree
Hide file tree
Showing 6 changed files with 227 additions and 34 deletions.
7 changes: 5 additions & 2 deletions winsup/cygwin/environ.cc
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions winsup/cygwin/fhandler.h
Expand Up @@ -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
Expand All @@ -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. */
Expand Down
230 changes: 198 additions & 32 deletions winsup/cygwin/fhandler_console.cc
Expand Up @@ -15,6 +15,7 @@ details. */
#include <sys/param.h>
#include <sys/cygwin.h>
#include <cygwin/kd.h>
#include <unistd.h>
#include "cygerrno.h"
#include "security.h"
#include "path.h"
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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 <ALT> key always generates
META, but the right <ALT> 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 <ALT> (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 <ALT> key always generates
META, but the right <ALT> 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 <ALT> (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
Expand Down Expand Up @@ -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 = "";
Expand Down Expand Up @@ -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 ());
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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++;
Expand All @@ -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 == ';')
{
Expand Down

0 comments on commit bd62786

Please sign in to comment.