win,tty: improve SIGWINCH support

Add SetWinEventHook for EVENT_CONSOLE_LAYOUT for better detection of
console resize events.

Ref: https://github.com/nodejs/node/issues/13197
PR-URL: https://github.com/libuv/libuv/pull/1408
Reviewed-By: Saúl Ibarra Corretgé <saghul@gmail.com>
This commit is contained in:
Bartosz Sosnowski 2017-06-22 12:07:30 +02:00
parent 832ab902cf
commit 6ad1e81547

View File

@ -112,14 +112,30 @@ static int uv_tty_virtual_offset = -1;
static int uv_tty_virtual_height = -1;
static int uv_tty_virtual_width = -1;
/* The console window size
* We keep this separate from uv_tty_virtual_*. We use those values to only
* handle signalling SIGWINCH
*/
static HANDLE uv__tty_console_handle = INVALID_HANDLE_VALUE;
static int uv__tty_console_height = -1;
static int uv__tty_console_width = -1;
static DWORD WINAPI uv__tty_console_resize_message_loop_thread(void* param);
static void CALLBACK uv__tty_console_resize_event(HWINEVENTHOOK hWinEventHook,
DWORD event,
HWND hwnd,
LONG idObject,
LONG idChild,
DWORD dwEventThread,
DWORD dwmsEventTime);
/* We use a semaphore rather than a mutex or critical section because in some
cases (uv__cancel_read_console) we need take the lock in the main thread and
release it in another thread. Using a semaphore ensures that in such
scenario the main thread will still block when trying to acquire the lock. */
static uv_sem_t uv_tty_output_lock;
static HANDLE uv_tty_output_handle = INVALID_HANDLE_VALUE;
static WORD uv_tty_default_text_attributes =
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
@ -141,6 +157,18 @@ static void uv__determine_vterm_state(HANDLE handle);
void uv_console_init(void) {
if (uv_sem_init(&uv_tty_output_lock, 1))
abort();
uv__tty_console_handle = CreateFileW(L"CONOUT$",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_WRITE,
0,
OPEN_EXISTING,
0,
0);
if (uv__tty_console_handle != NULL) {
QueueUserWorkItem(uv__tty_console_resize_message_loop_thread,
NULL,
WT_EXECUTELONGFUNCTION);
}
}
@ -184,11 +212,6 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int readable) {
if (uv__vterm_state == UV_UNCHECKED)
uv__determine_vterm_state(handle);
/* 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;
/* Remember the original console text attributes. */
uv_tty_capture_initial_style(&screen_buffer_info);
@ -705,25 +728,7 @@ void uv_process_tty_read_raw_req(uv_loop_t* loop, uv_tty_t* handle,
}
records_left--;
/* 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->tty.rd.last_input_record.EventType == WINDOW_BUFFER_SIZE_EVENT) {
CONSOLE_SCREEN_BUFFER_INFO info;
uv_sem_wait(&uv_tty_output_lock);
if (uv_tty_output_handle != INVALID_HANDLE_VALUE &&
GetConsoleScreenBufferInfo(uv_tty_output_handle, &info)) {
uv_tty_update_virtual_window(&info);
}
uv_sem_post(&uv_tty_output_lock);
continue;
}
/* Ignore other events that are not key or resize events. */
/* Ignore other events that are not key events. */
if (handle->tty.rd.last_input_record.EventType != KEY_EVENT) {
continue;
}
@ -1125,14 +1130,6 @@ 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);
}
}
@ -2280,3 +2277,52 @@ static void uv__determine_vterm_state(HANDLE handle) {
uv__vterm_state = UV_SUPPORTED;
}
static DWORD WINAPI uv__tty_console_resize_message_loop_thread(void* param) {
CONSOLE_SCREEN_BUFFER_INFO sb_info;
MSG msg;
if (!GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info))
return 0;
uv__tty_console_width = sb_info.dwSize.X;
uv__tty_console_height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1;
if (!SetWinEventHook(EVENT_CONSOLE_LAYOUT,
EVENT_CONSOLE_LAYOUT,
NULL,
uv__tty_console_resize_event,
0,
0,
WINEVENT_OUTOFCONTEXT))
return 0;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
static void CALLBACK uv__tty_console_resize_event(HWINEVENTHOOK hWinEventHook,
DWORD event,
HWND hwnd,
LONG idObject,
LONG idChild,
DWORD dwEventThread,
DWORD dwmsEventTime) {
CONSOLE_SCREEN_BUFFER_INFO sb_info;
int width, height;
if (!GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info))
return;
width = sb_info.dwSize.X;
height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1;
if (width != uv__tty_console_width || height != uv__tty_console_height) {
uv__tty_console_width = width;
uv__tty_console_height = height;
uv__signal_dispatch(SIGWINCH);
}
}