mirror of
git://sourceware.org/git/newlib-cygwin.git
synced 2025-02-13 04:29:09 +08:00
Cygwin: console: Introduce new thread which handles input signal.
- Currently, Ctrl-Z, Ctrl-\ and SIGWINCH does not work in console if the process does not call read() or select(). This is because these are processed in process_input_message() which is called from read() or select(). This is a long standing issue of console. Addresses: https://cygwin.com/pipermail/cygwin/2020-May/244898.html https://cygwin.com/pipermail/cygwin/2021-February/247779.html With this patch, new thread which handles only input signals is introduced so that Crtl-Z, etc. work without calling read() or select(). Ctrl-S and Ctrl-Q are also handled in this thread.
This commit is contained in:
parent
a36bdd3b3e
commit
2bc370afba
@ -1163,6 +1163,7 @@ ctrl_c_handler (DWORD type)
|
|||||||
sig = SIGQUIT;
|
sig = SIGQUIT;
|
||||||
t->last_ctrl_c = GetTickCount64 ();
|
t->last_ctrl_c = GetTickCount64 ();
|
||||||
t->kill_pgrp (sig);
|
t->kill_pgrp (sig);
|
||||||
|
t->output_stopped = false;
|
||||||
t->last_ctrl_c = GetTickCount64 ();
|
t->last_ctrl_c = GetTickCount64 ();
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
@ -2293,6 +2293,7 @@ public:
|
|||||||
HANDLE input_mutex;
|
HANDLE input_mutex;
|
||||||
HANDLE output_mutex;
|
HANDLE output_mutex;
|
||||||
};
|
};
|
||||||
|
HANDLE thread_sync_event;
|
||||||
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;
|
||||||
@ -2355,7 +2356,7 @@ private:
|
|||||||
|
|
||||||
void __reg3 read (void *ptr, size_t& len);
|
void __reg3 read (void *ptr, size_t& len);
|
||||||
ssize_t __stdcall write (const void *ptr, size_t len);
|
ssize_t __stdcall write (const void *ptr, size_t len);
|
||||||
void doecho (const void *str, DWORD len) { (void) write (str, len); }
|
void doecho (const void *str, DWORD len);
|
||||||
int close ();
|
int close ();
|
||||||
static bool exists () {return !!GetConsoleCP ();}
|
static bool exists () {return !!GetConsoleCP ();}
|
||||||
|
|
||||||
@ -2437,6 +2438,8 @@ private:
|
|||||||
static void request_xterm_mode_input (bool, const 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);
|
static void request_xterm_mode_output (bool, const handle_set_t *p);
|
||||||
|
|
||||||
|
static void cons_master_thread (handle_set_t *p, tty *ttyp);
|
||||||
|
|
||||||
friend tty_min * tty_list::get_cttyp ();
|
friend tty_min * tty_list::get_cttyp ();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -47,6 +47,8 @@ details. */
|
|||||||
con.b.srWindow.Top + con.scroll_region.Bottom)
|
con.b.srWindow.Top + con.scroll_region.Bottom)
|
||||||
#define con_is_legacy (shared_console_info && con.is_legacy)
|
#define con_is_legacy (shared_console_info && con.is_legacy)
|
||||||
|
|
||||||
|
#define CONS_THREAD_SYNC "cygcons.thread_sync"
|
||||||
|
|
||||||
const unsigned fhandler_console::MAX_WRITE_CHARS = 16384;
|
const unsigned fhandler_console::MAX_WRITE_CHARS = 16384;
|
||||||
|
|
||||||
fhandler_console::console_state NO_COPY *fhandler_console::shared_console_info;
|
fhandler_console::console_state NO_COPY *fhandler_console::shared_console_info;
|
||||||
@ -170,6 +172,143 @@ console_unit::console_unit (HWND me0):
|
|||||||
api_fatal ("console device allocation failure - too many consoles in use, max consoles is 32");
|
api_fatal ("console device allocation failure - too many consoles in use, max consoles is 32");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static DWORD WINAPI
|
||||||
|
cons_master_thread (VOID *arg)
|
||||||
|
{
|
||||||
|
fhandler_console *fh = (fhandler_console *) arg;
|
||||||
|
tty *ttyp = (tty *) fh->tc ();
|
||||||
|
fhandler_console::handle_set_t handle_set;
|
||||||
|
fh->get_duplicated_handle_set (&handle_set);
|
||||||
|
HANDLE thread_sync_event;
|
||||||
|
DuplicateHandle (GetCurrentProcess (), fh->thread_sync_event,
|
||||||
|
GetCurrentProcess (), &thread_sync_event,
|
||||||
|
0, FALSE, DUPLICATE_SAME_ACCESS);
|
||||||
|
SetEvent (thread_sync_event);
|
||||||
|
/* Do not touch class members after here because the class instance
|
||||||
|
may have been destroyed. */
|
||||||
|
fhandler_console::cons_master_thread (&handle_set, ttyp);
|
||||||
|
fhandler_console::close_handle_set (&handle_set);
|
||||||
|
SetEvent (thread_sync_event);
|
||||||
|
CloseHandle (thread_sync_event);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This thread processes signals derived from input messages.
|
||||||
|
Without this thread, those signals can be handled only when
|
||||||
|
the process calls read() or select(). This thread reads input
|
||||||
|
records, processes signals and removes corresponding record.
|
||||||
|
The other input records are kept back for read() or select(). */
|
||||||
|
void
|
||||||
|
fhandler_console::cons_master_thread (handle_set_t *p, tty *ttyp)
|
||||||
|
{
|
||||||
|
DWORD output_stopped_at = 0;
|
||||||
|
while (con.owner == myself->pid)
|
||||||
|
{
|
||||||
|
DWORD total_read, n, i, j;
|
||||||
|
INPUT_RECORD input_rec[INREC_SIZE];
|
||||||
|
|
||||||
|
WaitForSingleObject (p->input_mutex, INFINITE);
|
||||||
|
total_read = 0;
|
||||||
|
switch (cygwait (p->input_handle, (DWORD) 0))
|
||||||
|
{
|
||||||
|
case WAIT_OBJECT_0:
|
||||||
|
ReadConsoleInputA (p->input_handle,
|
||||||
|
input_rec, INREC_SIZE, &total_read);
|
||||||
|
break;
|
||||||
|
case WAIT_TIMEOUT:
|
||||||
|
case WAIT_SIGNALED:
|
||||||
|
case WAIT_CANCELED:
|
||||||
|
break;
|
||||||
|
default: /* Error */
|
||||||
|
ReleaseMutex (p->input_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (i = 0; i < total_read; i++)
|
||||||
|
{
|
||||||
|
const char c = input_rec[i].Event.KeyEvent.uChar.AsciiChar;
|
||||||
|
bool processed = false;
|
||||||
|
termios &ti = ttyp->ti;
|
||||||
|
switch (input_rec[i].EventType)
|
||||||
|
{
|
||||||
|
case KEY_EVENT:
|
||||||
|
if (ti.c_lflag & ISIG)
|
||||||
|
{
|
||||||
|
int sig = 0;
|
||||||
|
if (CCEQ (ti.c_cc[VINTR], c))
|
||||||
|
sig = SIGINT;
|
||||||
|
else if (CCEQ (ti.c_cc[VQUIT], c))
|
||||||
|
sig = SIGQUIT;
|
||||||
|
else if (CCEQ (ti.c_cc[VSUSP], c))
|
||||||
|
sig = SIGTSTP;
|
||||||
|
if (sig && input_rec[i].Event.KeyEvent.bKeyDown)
|
||||||
|
{
|
||||||
|
ttyp->kill_pgrp (sig);
|
||||||
|
ttyp->output_stopped = false;
|
||||||
|
/* Discard type ahead input */
|
||||||
|
goto skip_writeback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ti.c_iflag & IXON)
|
||||||
|
{
|
||||||
|
if (CCEQ (ti.c_cc[VSTOP], c))
|
||||||
|
{
|
||||||
|
if (!ttyp->output_stopped
|
||||||
|
&& input_rec[i].Event.KeyEvent.bKeyDown)
|
||||||
|
{
|
||||||
|
ttyp->output_stopped = true;
|
||||||
|
output_stopped_at = i;
|
||||||
|
}
|
||||||
|
processed = true;
|
||||||
|
}
|
||||||
|
else if (CCEQ (ti.c_cc[VSTART], c))
|
||||||
|
{
|
||||||
|
restart_output:
|
||||||
|
if (input_rec[i].Event.KeyEvent.bKeyDown)
|
||||||
|
ttyp->output_stopped = false;
|
||||||
|
processed = true;
|
||||||
|
}
|
||||||
|
else if ((ti.c_iflag & IXANY) && ttyp->output_stopped
|
||||||
|
&& c && i >= output_stopped_at)
|
||||||
|
goto restart_output;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WINDOW_BUFFER_SIZE_EVENT:
|
||||||
|
SHORT y = con.dwWinSize.Y;
|
||||||
|
SHORT x = con.dwWinSize.X;
|
||||||
|
con.fillin (p->output_handle);
|
||||||
|
if (y != con.dwWinSize.Y || x != con.dwWinSize.X)
|
||||||
|
{
|
||||||
|
con.scroll_region.Top = 0;
|
||||||
|
con.scroll_region.Bottom = -1;
|
||||||
|
if (wincap.has_con_24bit_colors () && !con_is_legacy)
|
||||||
|
{ /* Fix tab position */
|
||||||
|
/* Re-setting ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||||
|
fixes the tab position. */
|
||||||
|
request_xterm_mode_output (false, p);
|
||||||
|
request_xterm_mode_output (true, p);
|
||||||
|
}
|
||||||
|
ttyp->kill_pgrp (SIGWINCH);
|
||||||
|
}
|
||||||
|
processed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (processed)
|
||||||
|
{ /* Remove corresponding record. */
|
||||||
|
for (j = i; j < total_read - 1; j++)
|
||||||
|
input_rec[j] = input_rec[j + 1];
|
||||||
|
total_read--;
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (total_read)
|
||||||
|
/* Write back input records other than interrupt. */
|
||||||
|
WriteConsoleInput (p->input_handle, input_rec, total_read, &n);
|
||||||
|
skip_writeback:
|
||||||
|
ReleaseMutex (p->input_mutex);
|
||||||
|
cygwait (40);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
fhandler_console::set_unit ()
|
fhandler_console::set_unit ()
|
||||||
{
|
{
|
||||||
@ -1253,6 +1392,15 @@ fhandler_console::open (int flags, mode_t)
|
|||||||
debug_printf ("opened conin$ %p, conout$ %p", get_handle (),
|
debug_printf ("opened conin$ %p, conout$ %p", get_handle (),
|
||||||
get_output_handle ());
|
get_output_handle ());
|
||||||
|
|
||||||
|
if (myself->pid == con.owner)
|
||||||
|
{
|
||||||
|
char name[MAX_PATH];
|
||||||
|
shared_name (name, CONS_THREAD_SYNC, get_minor ());
|
||||||
|
thread_sync_event = CreateEvent(NULL, FALSE, FALSE, name);
|
||||||
|
new cygthread (::cons_master_thread, this, "consm");
|
||||||
|
WaitForSingleObject (thread_sync_event, INFINITE);
|
||||||
|
CloseHandle (thread_sync_event);
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1289,6 +1437,16 @@ fhandler_console::close ()
|
|||||||
|
|
||||||
release_output_mutex ();
|
release_output_mutex ();
|
||||||
|
|
||||||
|
if (con.owner == myself->pid)
|
||||||
|
{
|
||||||
|
char name[MAX_PATH];
|
||||||
|
shared_name (name, CONS_THREAD_SYNC, get_minor ());
|
||||||
|
thread_sync_event = OpenEvent (MAXIMUM_ALLOWED, FALSE, name);
|
||||||
|
con.owner = 0;
|
||||||
|
WaitForSingleObject (thread_sync_event, INFINITE);
|
||||||
|
CloseHandle (thread_sync_event);
|
||||||
|
}
|
||||||
|
|
||||||
CloseHandle (input_mutex);
|
CloseHandle (input_mutex);
|
||||||
input_mutex = NULL;
|
input_mutex = NULL;
|
||||||
CloseHandle (output_mutex);
|
CloseHandle (output_mutex);
|
||||||
@ -1598,7 +1756,7 @@ fhandler_console::tcgetattr (struct termios *t)
|
|||||||
}
|
}
|
||||||
|
|
||||||
fhandler_console::fhandler_console (fh_devices unit) :
|
fhandler_console::fhandler_console (fh_devices unit) :
|
||||||
fhandler_termios (), input_ready (false),
|
fhandler_termios (), input_ready (false), thread_sync_event (NULL),
|
||||||
input_mutex (NULL), output_mutex (NULL)
|
input_mutex (NULL), output_mutex (NULL)
|
||||||
{
|
{
|
||||||
if (unit > 0)
|
if (unit > 0)
|
||||||
@ -3081,6 +3239,14 @@ fhandler_console::write (const void *vsrc, size_t len)
|
|||||||
if (bg <= bg_eof)
|
if (bg <= bg_eof)
|
||||||
return (ssize_t) bg;
|
return (ssize_t) bg;
|
||||||
|
|
||||||
|
if (get_ttyp ()->output_stopped && is_nonblocking ())
|
||||||
|
{
|
||||||
|
set_errno (EAGAIN);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
while (get_ttyp ()->output_stopped)
|
||||||
|
cygwait (10);
|
||||||
|
|
||||||
acquire_attach_mutex (INFINITE);
|
acquire_attach_mutex (INFINITE);
|
||||||
push_process_state process_state (PID_TTYOU);
|
push_process_state process_state (PID_TTYOU);
|
||||||
acquire_output_mutex (INFINITE);
|
acquire_output_mutex (INFINITE);
|
||||||
@ -3411,6 +3577,15 @@ fhandler_console::write (const void *vsrc, size_t len)
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
fhandler_console::doecho (const void *str, DWORD len)
|
||||||
|
{
|
||||||
|
bool stopped = get_ttyp ()->output_stopped;
|
||||||
|
get_ttyp ()->output_stopped = false;
|
||||||
|
write (str, len);
|
||||||
|
get_ttyp ()->output_stopped = stopped;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct {
|
static const struct {
|
||||||
int vk;
|
int vk;
|
||||||
const char *val[4];
|
const char *val[4];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user