Cygwin: console: Revise the code to switch xterm mode.

- If application changes the console mode, mode management introduced
  by commit 10d8c278 will be corrupted. For example, stdout of jansi
  v2.0.1 or later is piped to less, jansi resets the xterm mode flag
  ENABLE_VIRTUAL_TERMINA_PROCESSING when jansi is terminated. This
  causes garbled output in less because less needs this flag enabled.
  This patch fixes the issue.
This commit is contained in:
Takashi Yano via Cygwin-patches 2021-01-15 17:32:10 +09:00 committed by Corinna Vinschen
parent fa22eea29d
commit 974e6d76d8
4 changed files with 127 additions and 62 deletions

View File

@ -2048,8 +2048,6 @@ class dev_console
bool raw_win32_keyboard_mode; bool raw_win32_keyboard_mode;
char cons_rabuf[40]; // cannot get longer than char buf[40] in char_command char cons_rabuf[40]; // cannot get longer than char buf[40] in char_command
char *cons_rapoi; char *cons_rapoi;
LONG xterm_mode_input;
LONG xterm_mode_output;
bool cursor_key_app_mode; bool cursor_key_app_mode;
inline UINT get_console_cp (); inline UINT get_console_cp ();
@ -2086,11 +2084,19 @@ public:
input_signalled = 2, input_signalled = 2,
input_winch = 3 input_winch = 3
}; };
struct handle_set_t
{
HANDLE input_handle;
HANDLE output_handle;
HANDLE input_mutex;
HANDLE output_mutex;
};
private: private:
static const unsigned MAX_WRITE_CHARS; static const unsigned MAX_WRITE_CHARS;
static console_state *shared_console_info; static console_state *shared_console_info;
static bool invisible_console; static bool invisible_console;
HANDLE input_mutex, output_mutex; HANDLE input_mutex, output_mutex;
handle_set_t handle_set;
/* Used when we encounter a truncated multi-byte sequence. The /* Used when we encounter a truncated multi-byte sequence. The
lead bytes are stored here and revisited in the next write call. */ lead bytes are stored here and revisited in the next write call. */
@ -2212,8 +2218,12 @@ private:
size_t &raixput (); size_t &raixput ();
size_t &rabuflen (); size_t &rabuflen ();
void request_xterm_mode_input (bool); const handle_set_t *get_handle_set (void) {return &handle_set;}
void request_xterm_mode_output (bool); void get_duplicated_handle_set (handle_set_t *p);
static void close_handle_set (handle_set_t *p);
static void request_xterm_mode_input (bool, const handle_set_t *p);
static void request_xterm_mode_output (bool, const handle_set_t *p);
friend tty_min * tty_list::get_cttyp (); friend tty_min * tty_list::get_cttyp ();
}; };

View File

@ -187,11 +187,7 @@ fhandler_console::set_unit ()
tty_min_state.setntty (DEV_CONS_MAJOR, console_unit (me)); tty_min_state.setntty (DEV_CONS_MAJOR, console_unit (me));
devset = (fh_devices) shared_console_info->tty_min_state.getntty (); devset = (fh_devices) shared_console_info->tty_min_state.getntty ();
if (created) if (created)
{ con.owner = myself->pid;
con.owner = myself->pid;
con.xterm_mode_input = 0;
con.xterm_mode_output = 0;
}
} }
if (!created && shared_console_info) if (!created && shared_console_info)
{ {
@ -279,60 +275,65 @@ fhandler_console::rabuflen ()
return con_ra.rabuflen; return con_ra.rabuflen;
} }
/* The function request_xterm_mode_{in,out}put() should be static so that
they can be called even after the fhandler_console instance is deleted. */
void void
fhandler_console::request_xterm_mode_input (bool req) fhandler_console::request_xterm_mode_input (bool req, const handle_set_t *p)
{ {
if (con_is_legacy) if (con_is_legacy)
return; return;
WaitForSingleObject (p->input_mutex, INFINITE);
DWORD dwMode;
GetConsoleMode (p->input_handle, &dwMode);
if (req) if (req)
{ {
if (InterlockedExchange (&con.xterm_mode_input, 1) == 0) if (!(dwMode & ENABLE_VIRTUAL_TERMINAL_INPUT))
{ {
DWORD dwMode;
GetConsoleMode (get_handle (), &dwMode);
dwMode |= ENABLE_VIRTUAL_TERMINAL_INPUT; dwMode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
SetConsoleMode (get_handle (), dwMode); SetConsoleMode (p->input_handle, dwMode);
if (con.cursor_key_app_mode) /* Restore DECCKM */ if (con.cursor_key_app_mode) /* Restore DECCKM */
WriteConsoleW (get_output_handle (), L"\033[?1h", 5, NULL, 0); {
request_xterm_mode_output (true, p);
WriteConsoleW (p->output_handle, L"\033[?1h", 5, NULL, 0);
}
} }
} }
else else
{ {
if (InterlockedExchange (&con.xterm_mode_input, 0) == 1) if (dwMode & ENABLE_VIRTUAL_TERMINAL_INPUT)
{ {
DWORD dwMode;
GetConsoleMode (get_handle (), &dwMode);
dwMode &= ~ENABLE_VIRTUAL_TERMINAL_INPUT; dwMode &= ~ENABLE_VIRTUAL_TERMINAL_INPUT;
SetConsoleMode (get_handle (), dwMode); SetConsoleMode (p->input_handle, dwMode);
} }
} }
ReleaseMutex (p->input_mutex);
} }
void void
fhandler_console::request_xterm_mode_output (bool req) fhandler_console::request_xterm_mode_output (bool req, const handle_set_t *p)
{ {
if (con_is_legacy) if (con_is_legacy)
return; return;
WaitForSingleObject (p->output_mutex, INFINITE);
DWORD dwMode;
GetConsoleMode (p->output_handle, &dwMode);
if (req) if (req)
{ {
if (InterlockedExchange (&con.xterm_mode_output, 1) == 0) if (!(dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING))
{ {
DWORD dwMode;
GetConsoleMode (get_output_handle (), &dwMode);
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode (get_output_handle (), dwMode); SetConsoleMode (p->output_handle, dwMode);
} }
} }
else else
{ {
if (InterlockedExchange (&con.xterm_mode_output, 0) == 1) if (dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)
{ {
DWORD dwMode;
GetConsoleMode (get_output_handle (), &dwMode);
dwMode &= ~ENABLE_VIRTUAL_TERMINAL_PROCESSING; dwMode &= ~ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode (get_output_handle (), dwMode); SetConsoleMode (p->output_handle, dwMode);
} }
} }
ReleaseMutex (p->output_mutex);
} }
/* Return the tty structure associated with a given tty number. If the /* Return the tty structure associated with a given tty number. If the
@ -440,8 +441,8 @@ fhandler_console::fix_tab_position (void)
{ {
/* Re-setting ENABLE_VIRTUAL_TERMINAL_PROCESSING /* Re-setting ENABLE_VIRTUAL_TERMINAL_PROCESSING
fixes the tab position. */ fixes the tab position. */
request_xterm_mode_output (false); request_xterm_mode_output (false, &handle_set);
request_xterm_mode_output (true); request_xterm_mode_output (true, &handle_set);
} }
bool bool
@ -506,7 +507,7 @@ fhandler_console::read (void *pv, size_t& buflen)
/* if system has 24 bit color capability, use xterm compatible mode. */ /* if system has 24 bit color capability, use xterm compatible mode. */
if (wincap.has_con_24bit_colors ()) if (wincap.has_con_24bit_colors ())
request_xterm_mode_input (true); request_xterm_mode_input (true, &handle_set);
while (!input_ready && !get_cons_readahead_valid ()) while (!input_ready && !get_cons_readahead_valid ())
{ {
@ -514,8 +515,6 @@ fhandler_console::read (void *pv, size_t& buflen)
if ((bgres = bg_check (SIGTTIN)) <= bg_eof) if ((bgres = bg_check (SIGTTIN)) <= bg_eof)
{ {
buflen = bgres; buflen = bgres;
if (wincap.has_con_24bit_colors ())
request_xterm_mode_input (false);
return; return;
} }
@ -533,8 +532,6 @@ fhandler_console::read (void *pv, size_t& buflen)
case WAIT_TIMEOUT: case WAIT_TIMEOUT:
set_sig_errno (EAGAIN); set_sig_errno (EAGAIN);
buflen = (size_t) -1; buflen = (size_t) -1;
if (wincap.has_con_24bit_colors ())
request_xterm_mode_input (false);
return; return;
default: default:
goto err; goto err;
@ -582,22 +579,16 @@ fhandler_console::read (void *pv, size_t& buflen)
#undef buf #undef buf
buflen = copied_chars; buflen = copied_chars;
if (wincap.has_con_24bit_colors ())
request_xterm_mode_input (false);
return; return;
err: err:
__seterrno (); __seterrno ();
buflen = (size_t) -1; buflen = (size_t) -1;
if (wincap.has_con_24bit_colors ())
request_xterm_mode_input (false);
return; return;
sig_exit: sig_exit:
set_sig_errno (EINTR); set_sig_errno (EINTR);
buflen = (size_t) -1; buflen = (size_t) -1;
if (wincap.has_con_24bit_colors ())
request_xterm_mode_input (false);
} }
fhandler_console::input_states fhandler_console::input_states
@ -1106,6 +1097,7 @@ fhandler_console::open (int flags, mode_t)
return 0; return 0;
} }
set_handle (h); set_handle (h);
handle_set.input_handle = h;
h = CreateFileW (L"CONOUT$", GENERIC_READ | GENERIC_WRITE, h = CreateFileW (L"CONOUT$", GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, &sec_none, FILE_SHARE_READ | FILE_SHARE_WRITE, &sec_none,
@ -1117,8 +1109,11 @@ fhandler_console::open (int flags, mode_t)
return 0; return 0;
} }
set_output_handle (h); set_output_handle (h);
handle_set.output_handle = h;
setup_io_mutex (); setup_io_mutex ();
handle_set.input_mutex = input_mutex;
handle_set.output_mutex = output_mutex;
if (con.fillin (get_output_handle ())) if (con.fillin (get_output_handle ()))
{ {
@ -1191,8 +1186,10 @@ fhandler_console::close ()
&obi, sizeof obi, NULL); &obi, sizeof obi, NULL);
if ((NT_SUCCESS (status) && obi.HandleCount == 1) if ((NT_SUCCESS (status) && obi.HandleCount == 1)
|| myself->pid == con.owner) || myself->pid == con.owner)
request_xterm_mode_output (false); {
request_xterm_mode_input (false); request_xterm_mode_output (false, &handle_set);
request_xterm_mode_input (false, &handle_set);
}
} }
release_output_mutex (); release_output_mutex ();
@ -1354,10 +1351,14 @@ fhandler_console::output_tcsetattr (int, struct termios const *t)
/* All the output bits we can ignore */ /* All the output bits we can ignore */
acquire_output_mutex (INFINITE); acquire_output_mutex (INFINITE);
if (wincap.has_con_24bit_colors ())
request_xterm_mode_output (false);
DWORD flags = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT; DWORD flags = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT;
DWORD oflags;
GetConsoleMode (get_output_handle (), &oflags);
if (wincap.has_con_24bit_colors () && !con_is_legacy
&& (oflags & ENABLE_VIRTUAL_TERMINAL_PROCESSING))
flags |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
int res = SetConsoleMode (get_output_handle (), flags) ? 0 : -1; int res = SetConsoleMode (get_output_handle (), flags) ? 0 : -1;
if (res) if (res)
__seterrno_from_win_error (GetLastError ()); __seterrno_from_win_error (GetLastError ());
@ -1419,6 +1420,10 @@ fhandler_console::input_tcsetattr (int, struct termios const *t)
((wincap.has_con_24bit_colors () && !con_is_legacy) ? ((wincap.has_con_24bit_colors () && !con_is_legacy) ?
0 : ENABLE_MOUSE_INPUT); 0 : ENABLE_MOUSE_INPUT);
if (wincap.has_con_24bit_colors () && !con_is_legacy
&& (oflags & ENABLE_VIRTUAL_TERMINAL_INPUT))
flags |= ENABLE_VIRTUAL_TERMINAL_INPUT;
int res; int res;
if (flags == oflags) if (flags == oflags)
res = 0; res = 0;
@ -2973,7 +2978,7 @@ fhandler_console::write (const void *vsrc, size_t len)
/* If system has 24 bit color capability, use xterm compatible mode. */ /* If system has 24 bit color capability, use xterm compatible mode. */
if (wincap.has_con_24bit_colors ()) if (wincap.has_con_24bit_colors ())
request_xterm_mode_output (true); request_xterm_mode_output (true, &handle_set);
if (wincap.has_con_24bit_colors () && !con_is_legacy) if (wincap.has_con_24bit_colors () && !con_is_legacy)
{ {
DWORD dwMode; DWORD dwMode;
@ -3657,3 +3662,35 @@ fhandler_console::__release_output_mutex (const char *fn, int ln)
strace.prntf (_STRACE_TERMIOS, fn, "(%d): release output_mutex", ln); strace.prntf (_STRACE_TERMIOS, fn, "(%d): release output_mutex", ln);
#endif #endif
} }
void
fhandler_console::get_duplicated_handle_set (handle_set_t *p)
{
DuplicateHandle (GetCurrentProcess (), get_handle (),
GetCurrentProcess (), &p->input_handle,
0, FALSE, DUPLICATE_SAME_ACCESS);
DuplicateHandle (GetCurrentProcess (), get_output_handle (),
GetCurrentProcess (), &p->output_handle,
0, FALSE, DUPLICATE_SAME_ACCESS);
DuplicateHandle (GetCurrentProcess (), input_mutex,
GetCurrentProcess (), &p->input_mutex,
0, FALSE, DUPLICATE_SAME_ACCESS);
DuplicateHandle (GetCurrentProcess (), output_mutex,
GetCurrentProcess (), &p->output_mutex,
0, FALSE, DUPLICATE_SAME_ACCESS);
}
/* The function close_handle_set() should be static so that they can
be called even after the fhandler_console instance is deleted. */
void
fhandler_console::close_handle_set (handle_set_t *p)
{
CloseHandle (p->input_handle);
p->input_handle = NULL;
CloseHandle (p->output_handle);
p->output_handle = NULL;
CloseHandle (p->input_mutex);
p->input_mutex = NULL;
CloseHandle (p->output_mutex);
p->output_mutex = NULL;
}

View File

@ -1087,28 +1087,15 @@ verify_console (select_record *me, fd_set *rfds, fd_set *wfds,
return peek_console (me, true); return peek_console (me, true);
} }
static void console_cleanup (select_record *, select_stuff *);
static int static int
console_startup (select_record *me, select_stuff *stuff) console_startup (select_record *me, select_stuff *stuff)
{ {
fhandler_console *fh = (fhandler_console *) me->fh; fhandler_console *fh = (fhandler_console *) me->fh;
if (wincap.has_con_24bit_colors ()) if (wincap.has_con_24bit_colors ())
{ fhandler_console::request_xterm_mode_input (true, fh->get_handle_set ());
fh->request_xterm_mode_input (true);
me->cleanup = console_cleanup;
}
return 1; return 1;
} }
static void
console_cleanup (select_record *me, select_stuff *stuff)
{
fhandler_console *fh = (fhandler_console *) me->fh;
if (wincap.has_con_24bit_colors ())
fh->request_xterm_mode_input (false);
}
select_record * select_record *
fhandler_console::select_read (select_stuff *ss) fhandler_console::select_read (select_stuff *ss)
{ {

View File

@ -606,6 +606,7 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
sa = &sec_none_nih; sa = &sec_none_nih;
fhandler_pty_slave *ptys_primary = NULL; fhandler_pty_slave *ptys_primary = NULL;
fhandler_console *cons_native = NULL;
for (int i = 0; i < 3; i ++) for (int i = 0; i < 3; i ++)
{ {
const int chk_order[] = {1, 0, 2}; const int chk_order[] = {1, 0, 2};
@ -621,10 +622,23 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
{ {
fhandler_console *cons = (fhandler_console *) fh; fhandler_console *cons = (fhandler_console *) fh;
if (wincap.has_con_24bit_colors () && !iscygwin ()) if (wincap.has_con_24bit_colors () && !iscygwin ())
if (fd == 1 || fd == 2) {
cons->request_xterm_mode_output (false); if (cons_native == NULL)
cons_native = cons;
if (fd == 0)
fhandler_console::request_xterm_mode_input (false,
cons->get_handle_set ());
else if (fd == 1 || fd == 2)
fhandler_console::request_xterm_mode_output (false,
cons->get_handle_set ());
}
} }
} }
struct fhandler_console::handle_set_t cons_handle_set = { 0, };
if (cons_native)
/* Console handles will be closed by close_all_handle(),
therefore, duplicate them here */
cons_native->get_duplicated_handle_set (&cons_handle_set);
if (!iscygwin ()) if (!iscygwin ())
{ {
@ -942,6 +956,15 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
WaitForSingleObject (pi.hProcess, INFINITE); WaitForSingleObject (pi.hProcess, INFINITE);
ptys_primary->close_pseudoconsole (); ptys_primary->close_pseudoconsole ();
} }
else if (cons_native)
{
WaitForSingleObject (pi.hProcess, INFINITE);
fhandler_console::request_xterm_mode_output (true,
&cons_handle_set);
fhandler_console::request_xterm_mode_input (true,
&cons_handle_set);
fhandler_console::close_handle_set (&cons_handle_set);
}
myself.exit (EXITCODE_NOSET); myself.exit (EXITCODE_NOSET);
break; break;
case _P_WAIT: case _P_WAIT:
@ -951,6 +974,14 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
res = -1; res = -1;
if (enable_pcon) if (enable_pcon)
ptys_primary->close_pseudoconsole (); ptys_primary->close_pseudoconsole ();
else if (cons_native)
{
fhandler_console::request_xterm_mode_output (true,
&cons_handle_set);
fhandler_console::request_xterm_mode_input (true,
&cons_handle_set);
fhandler_console::close_handle_set (&cons_handle_set);
}
break; break;
case _P_DETACH: case _P_DETACH:
res = 0; /* Lost all memory of this child. */ res = 0; /* Lost all memory of this child. */