diff --git a/include/uv-private/uv-win.h b/include/uv-private/uv-win.h index dbbecedc..ad27ee51 100644 --- a/include/uv-private/uv-win.h +++ b/include/uv-private/uv-win.h @@ -65,6 +65,7 @@ typedef intptr_t ssize_t; */ #define SIGHUP 1 #define SIGKILL 9 +#define SIGWINCH 28 /* * Guids and typedefs for winsock extension functions diff --git a/include/uv.h b/include/uv.h index 66ee41cb..0e90a392 100644 --- a/include/uv.h +++ b/include/uv.h @@ -1608,6 +1608,13 @@ UV_EXTERN int uv_fs_poll_stop(uv_fs_poll_t* handle); * program is given approximately 10 seconds to perform cleanup. After that * Windows will unconditionally terminate it. * + * SIGWINCH is raised whenever libuv detects that the console has been + * resized. SIGWINCH is emulated by libuv when the program uses an uv_tty_t + * handle to write to the console. SIGWINCH may not always be delivered in a + * timely manner; libuv will only detect size changes when the cursor is + * being moved. When a readable uv_tty_handle is used in raw mode, resizing + * the console buffer will also trigger a SIGWINCH signal. + * * Watchers for other signals can be successfully created, but these signals * are never generated. These signals are: SIGILL, SIGABRT, SIGFPE, SIGSEGV, * SIGTERM and SIGKILL. diff --git a/src/win/signal.c b/src/win/signal.c index 3407c6c6..e35e1ff9 100644 --- a/src/win/signal.c +++ b/src/win/signal.c @@ -169,6 +169,10 @@ static uv_err_t uv__signal_register(int signum) { case SIGHUP: return uv__signal_register_control_handler(); + case SIGWINCH: + /* SIGWINCH is generated in tty.c. No need to register anything. */ + return uv_ok_; + case SIGILL: case SIGABRT_COMPAT: case SIGFPE: @@ -193,6 +197,10 @@ static void uv__signal_unregister(int signum) { uv__signal_unregister_control_handler(); return; + case SIGWINCH: + /* SIGWINCH is generated in tty.c. No need to unregister anything. */ + return; + case SIGILL: case SIGABRT_COMPAT: case SIGFPE: diff --git a/src/win/tty.c b/src/win/tty.c index 8575f4f7..af828cf8 100644 --- a/src/win/tty.c +++ b/src/win/tty.c @@ -82,6 +82,8 @@ static int uv_tty_virtual_width = -1; static CRITICAL_SECTION uv_tty_output_lock; +static HANDLE uv_tty_output_handle = INVALID_HANDLE_VALUE; + void uv_console_init() { InitializeCriticalSection(&uv_tty_output_lock); @@ -113,10 +115,17 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int readable) { return -1; } - /* Update the virtual window. We must hold the tty_output_lock because the */ - /* virtual window state is shared between all uv_tty handles. */ + /* Obtain the the tty_output_lock because the virtual window state is */ + /* shared between all uv_tty_t handles. */ EnterCriticalSection(&uv_tty_output_lock); + + /* Store the global tty output handle. This handle is used by TTY read */ + /* streams to update the virtual window when a CONSOLE_BUFFER_SIZE_EVENT */ + /* is received. */ + uv_tty_output_handle = handle; + uv_tty_update_virtual_window(&screen_buffer_info); + LeaveCriticalSection(&uv_tty_output_lock); } @@ -513,7 +522,20 @@ void uv_process_tty_read_raw_req(uv_loop_t* loop, uv_tty_t* handle, } records_left--; - /* Ignore events that are not keyboard events */ + /* If the window was resized, recompute the virtual window size. This */ + /* will trigger a SIGWINCH signal if the window size changed in an */ + /* way that matters to libuv. */ + if (handle->last_input_record.EventType == WINDOW_BUFFER_SIZE_EVENT) { + CONSOLE_SCREEN_BUFFER_INFO info; + if (uv_tty_output_handle == INVALID_HANDLE_VALUE) + continue; + if (!GetConsoleScreenBufferInfo(uv_tty_output_handle, &info)) + continue; + uv_tty_update_virtual_window(&info); + continue; + } + + /* Ignore other events that are not key or resize events. */ if (handle->last_input_record.EventType != KEY_EVENT) { continue; } @@ -835,8 +857,11 @@ int uv_tty_read_stop(uv_tty_t* handle) { static void uv_tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info) { - uv_tty_virtual_height = info->srWindow.Bottom - info->srWindow.Top + 1; + int old_virtual_width = uv_tty_virtual_width; + int old_virtual_height = uv_tty_virtual_height; + uv_tty_virtual_width = info->dwSize.X; + uv_tty_virtual_height = info->srWindow.Bottom - info->srWindow.Top + 1; /* Recompute virtual window offset row. */ if (uv_tty_virtual_offset == -1) { @@ -854,6 +879,14 @@ static void uv_tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info) { if (uv_tty_virtual_offset < 0) { uv_tty_virtual_offset = 0; } + + /* If the virtual window size changed, emit a SIGWINCH signal. Don't emit */ + /* if this was the first time the virtual window size was computed. */ + if (old_virtual_width != -1 && old_virtual_height != -1 && + (uv_tty_virtual_width != old_virtual_width || + uv_tty_virtual_height != old_virtual_height)) { + uv__signal_dispatch(SIGWINCH); + } }