Skip to content

Commit

Permalink
win,tty: improve SIGWINCH support
Browse files Browse the repository at this point in the history
Add SetWinEventHook for EVENT_CONSOLE_LAYOUT for better detection of
console resize events.

Ref: nodejs/node#13197
  • Loading branch information
bzoz committed Jul 7, 2017
1 parent d63030b commit a805728
Showing 1 changed file with 77 additions and 34 deletions.
111 changes: 77 additions & 34 deletions src/win/tty.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -176,18 +192,22 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int readable) {
return uv_translate_sys_error(GetLastError());
}

/* 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_console_handle = handle;
if (!QueueUserWorkItem(uv__tty_console_resize_message_loop_thread,
NULL,
WT_EXECUTELONGFUNCTION))
return uv_translate_sys_error(GetLastError());

/* Obtain the the tty_output_lock because the virtual window state is */
/* shared between all uv_tty_t handles. */
uv_sem_wait(&uv_tty_output_lock);

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);

Expand Down Expand Up @@ -704,25 +724,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;
}
Expand Down Expand Up @@ -1124,14 +1126,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);
}
}


Expand Down Expand Up @@ -2279,3 +2273,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 screen_buffer_info;
MSG msg;

if (!GetConsoleScreenBufferInfo(uv_tty_console_handle, &screen_buffer_info))
uv_fatal_error(GetLastError(), "GetConsoleScreenBufferInfo");

uv_tty_console_width = screen_buffer_info.dwSize.X;
uv_tty_console_height = screen_buffer_info.srWindow.Bottom - screen_buffer_info.srWindow.Top + 1;

if (!SetWinEventHook(EVENT_CONSOLE_LAYOUT,
EVENT_CONSOLE_LAYOUT,
NULL,
uv__tty_console_resize_event,
0,
0,
WINEVENT_OUTOFCONTEXT))
uv_fatal_error(GetLastError(), "SetWinEventHook");

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 screen_buffer_info;
int width, height;

if (!GetConsoleScreenBufferInfo(uv_tty_console_handle, &screen_buffer_info))
uv_fatal_error(GetLastError(), "GetConsoleScreenBufferInfo");

width = screen_buffer_info.dwSize.X;
height = screen_buffer_info.srWindow.Bottom - screen_buffer_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);
}
}

0 comments on commit a805728

Please sign in to comment.