Created
September 24, 2011 04:07
-
-
Save piscisaureus/1238953 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Null uv_buf_t */ | |
static const uv_buf_t uv_null_buf_ = { 0, NULL }; | |
int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd) { | |
HANDLE input_event, win_handle; | |
win_handle = (HANDLE) _get_osfhandle(fd); | |
if (win_handle == INVALID_HANDLE_VALUE) { | |
uv_set_sys_error(loop, ERROR_INVALID_HANDLE); | |
return -1; | |
} | |
input_event = CreateEvent(NULL, 0, 0, NULL); | |
if (input_event == NULL) { | |
uv_set_sys_error(loop, GetLastError()); | |
return -1; | |
} | |
uv_stream_init(loop, (uv_stream_t*) tty); | |
tty->type = UV_TTY; | |
tty->handle = win_handle; | |
tty->reqs_pending = 0; | |
tty->handle = INVALID_HANDLE_VALUE; | |
tty->input_event = input_event; | |
tty->input_wait_handle = NULL; | |
tty->last_key_len = 0; | |
tty->last_key_offset = 0; | |
tty->last_utf16_high_surrogate = 0; | |
memset(&tty->last_input_record, 0, sizeof tty->last_input_record); | |
tty->flags |= UV_HANDLE_BOUND; | |
return 0; | |
} | |
#define BYTES_PER_KEY 7 | |
static const char* get_vt100_fn_key(DWORD code, char shift, char ctrl, | |
size_t* len) { | |
#define VK_CASE(vk, normal_str, shift_str, ctrl_str, shift_ctrl_str) \ | |
case (vk): \ | |
if (shift && ctrl) { \ | |
*len = sizeof shift_ctrl_str; \ | |
return "\033" shift_ctrl_str; \ | |
} else if (shift) { \ | |
*len = sizeof shift_str ; \ | |
return "\033" shift_str; \ | |
} else if (ctrl) { \ | |
*len = sizeof ctrl_str; \ | |
return "\033" ctrl_str; \ | |
} else { \ | |
*len = sizeof normal_str; \ | |
return "\033" normal_str; \ | |
} | |
switch (code) { | |
/* These mappings are the same as Cygwin's. Unmodified and alt-modified */ | |
/* keypad keys comply with linux console, modifiers comply with xterm */ | |
/* modifier usage. F1..f12 and shift-f1..f10 comply with linux console, */ | |
/* f6..f12 with and without modifiers comply with rxvt. */ | |
VK_CASE(VK_NUMPAD5, "[G", "[1;2G", "[1;5G", "[1;6G") | |
VK_CASE(VK_CLEAR, "[G", "[1;2G", "[1;5G", "[1;6G") | |
VK_CASE(VK_LEFT, "[D", "[1;2D", "[1;5D", "[1;6D") | |
VK_CASE(VK_RIGHT, "[C", "[1;2C", "[1;5C", "[1;6C") | |
VK_CASE(VK_UP, "[A", "[1;2A", "[1;5A", "[1;6A") | |
VK_CASE(VK_DOWN, "[B", "[1;2B", "[1;5B", "[1;6B") | |
VK_CASE(VK_PRIOR, "[5~", "[5;2~", "[5;5~", "[5;6~") | |
VK_CASE(VK_NEXT, "[6~", "[6;2~", "[6;5~", "[6;6~") | |
VK_CASE(VK_HOME, "[1~", "[1;2~", "[1;5~", "[1;6~") | |
VK_CASE(VK_END, "[4~", "[4;2~", "[4;5~", "[4;6~") | |
VK_CASE(VK_INSERT, "[2~", "[2;2~", "[2;5~", "[2;6~") | |
VK_CASE(VK_DELETE, "[3~", "[3;2~", "[3;5~", "[3;6~") | |
VK_CASE(VK_F1, "[[A", "[23~", "[11^", "[23^" ) | |
VK_CASE(VK_F2, "[[B", "[24~", "[12^", "[24^" ) | |
VK_CASE(VK_F3, "[[C", "[25~", "[13^", "[25^" ) | |
VK_CASE(VK_F4, "[[D", "[26~", "[14^", "[26^" ) | |
VK_CASE(VK_F5, "[[E", "[28~", "[15^", "[28^" ) | |
VK_CASE(VK_F6, "[17~", "[29~", "[17^", "[29^" ) | |
VK_CASE(VK_F7, "[18~", "[31~", "[18^", "[31^" ) | |
VK_CASE(VK_F8, "[19~", "[32~", "[19^", "[32^" ) | |
VK_CASE(VK_F9, "[20~", "[33~", "[20^", "[33^" ) | |
VK_CASE(VK_F10, "[21~", "[34~", "[21^", "[34^" ) | |
VK_CASE(VK_F11, "[23~", "[23$", "[23^", "[23@" ) | |
VK_CASE(VK_F12, "[24~", "[24$", "[24^", "[24@" ) | |
default: | |
*len = 0; | |
return NULL; | |
} | |
#undef VK_CASE | |
} | |
void uv_process_tty_read_req(uv_loop_t* loop, uv_tty_t* handle, | |
uv_req_t* req) { | |
/* Shortcut for handle->last_input_record.Event.KeyEvent. */ | |
#define KEV handle->last_input_record.Event.KeyEvent | |
uv_tty_t* handle; | |
DWORD i, records_left, records_read; | |
INPUT_RECORD record; | |
uv_buf_t buf; | |
off_t buf_used; | |
char esc_prefix; | |
assert(handle->type == UV_TTY); | |
handle->flags &= ~UV_HANDLE_READ_PENDING; | |
if (!(handle->flags & UV_HANDLE_READING)) { | |
goto out; | |
} | |
if (!REQ_SUCCESS(req)) { | |
/* An error occurred while waiting for the event. */ | |
if ((handle->flags & UV_HANDLE_READING)) { | |
handle->flags &= ~UV_HANDLE_READING; | |
loop->last_error = GET_REQ_UV_SOCK_ERROR(req); | |
handle->read_cb((uv_stream_t*)handle, -1, uv_null_buf_); | |
} | |
} else { | |
/* Fetch the number of events */ | |
if (!GetNumberOfConsoleInputEvents(handle->handle, &records_left)) { | |
handle->flags &= ~UV_HANDLE_READING; | |
uv_set_sys_error(loop, GetLastError()); | |
handle->read_cb((uv_stream_t*)handle, -1, uv_null_buf_); | |
goto out; | |
} | |
/* Windows sends a lot of events that we're not interested in, so buf */ | |
/* will be allocated on demand, when there's actually something to emit. */ | |
buf = uv_null_buf_; | |
buf_used = 0; | |
while (records_left > 0 && (handle->flags & UV_HANDLE_READING)) { | |
if (handle->last_key_len == 0) { | |
/* Read the next input record */ | |
if (!ReadConsoleInput(handle->handle, | |
&handle->last_input_record, | |
1, | |
&records_read)) { | |
uv_set_sys_error(loop, GetLastError()); | |
handle->flags &= ~UV_HANDLE_READING; | |
handle->read_cb((uv_stream_t*) handle, -1, buf); | |
goto out; | |
} | |
records_left--; | |
/* Ignore events that are not keyboard events */ | |
if (handle->last_input_record.EventType != KEY_EVENT) { | |
continue; | |
} | |
/* Ignore keyup events, unless the left alt key was held and a valid */ | |
/* unicode character was emitted; */ | |
if (!KEV.bKeyDown && !((KEV.dwControlKeyState & LEFT_ALT_PRESSED) && | |
KEV.uChar.UnicodeChar != 0)) { | |
continue; | |
} | |
if (KEV.uChar.UnicodeChar != 0) { | |
int prefix_len, char_len; | |
/* Character key pressed */ | |
if (KEV.uChar.UnicodeChar >= 0xD800 && | |
KEV.uChar.UnicodeChar < 0xDC00) { | |
/* UTF-16 high surrogate */ | |
handle->last_utf16_high_surrogate = KEV.uChar.UnicodeChar; | |
continue; | |
} | |
/* Prefix with \u033 if alt was held, but alt was not used as part */ | |
/* a compose sequence. */ | |
if ((KEV.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) | |
&& !(KEV.dwControlKeyState & (LEFT_CTRL_PRESSED | | |
RIGHT_CTRL_PRESSED)) && KEV.bKeyDown) { | |
handle->last_key[0] = '\x033'; | |
} else { | |
prefix_len = 0; | |
} | |
if (KEV.uChar.UnicodeChar >= 0xDC00 && | |
KEV.uChar.UnicodeChar < 0xE000) { | |
/* UTF-16 surrogate pair */ | |
WCHAR utf16_buffer[2] = { handle->last_utf16_high_surrogate, | |
KEV.uChar.UnicodeChar}; | |
char_len = WideCharToMultiByte(CP_UTF8, | |
0, | |
utf16_buffer, | |
2, | |
&handle->last_key[prefix_len], | |
sizeof handle->last_key, | |
NULL, | |
NULL); | |
} else { | |
/* Single UTF-16 character */ | |
char_len = WideCharToMultiByte(CP_UTF8, | |
0, | |
&KEV.uChar.UnicodeChar, | |
1, | |
&handle->last_key[prefix_len], | |
sizeof handle->last_key, | |
NULL, | |
NULL); | |
} | |
/* Whatever happened, the last character wasn't a high surrogate. */ | |
handle->last_utf16_high_surrogate = 0; | |
/* If the utf16 character(s) couldn't be converted something must */ | |
/* be wrong. */ | |
if (!char_len) { | |
uv_set_sys_error(loop, GetLastError()); | |
handle->flags &= ~UV_HANDLE_READING; | |
handle->read_cb((uv_stream_t*) handle, -1, buf); | |
goto out; | |
} | |
handle->last_key_len = (unsigned char) (prefix_len + char_len); | |
continue; | |
} else { | |
/* Function key pressed */ | |
const char* vt100; | |
size_t prefix_len, vt100_len; | |
vt100 = get_vt100_fn_key(KEV.wVirtualKeyCode, | |
!!(KEV.dwControlKeyState & SHIFT_PRESSED), | |
!!(KEV.dwControlKeyState & ( | |
LEFT_CTRL_PRESSED | | |
RIGHT_CTRL_PRESSED)), | |
&vt100_len); | |
/* If we were unable to map to a vt100 sequence, just ignore. */ | |
if (!vt100) { | |
continue; | |
} | |
/* Prefix with \x033 when the alt key was held. */ | |
if (KEV.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) { | |
handle->last_key[0] = '\x033'; | |
prefix_len = 1; | |
} else { | |
prefix_len = 0; | |
} | |
/* Copy the vt100 sequence to the handle buffer. */ | |
assert(prefix_len + vt100_len < sizeof handle->last_key); | |
memcpy(&handle->last_key[handle->last_key_len], vt100, vt100_len); | |
handle->last_key_len = (unsigned char) (prefix_len + vt100_len); | |
continue; | |
} | |
} else { | |
/* Copy any bytes left from the last keypress to the user buffer. */ | |
if (handle->last_key_offset < handle->last_key_len) { | |
/* Allocate a buffer if needed */ | |
if (buf_used == 0) { | |
buf = handle->alloc_cb((uv_handle_t*) handle, 1024); | |
} | |
buf.base[buf_used++] = handle->last_key[handle->last_key_offset++]; | |
/* If the buffer is full, emit it */ | |
if (buf_used == buf.len) { | |
handle->read_cb((uv_stream_t*) handle, buf_used, buf); | |
buf = uv_null_buf_; | |
buf_used = 0; | |
} | |
continue; | |
} | |
/* Apply dwRepeat from the last input record. */ | |
if (KEV.wRepeatCount > 0) { | |
KEV.wRepeatCount--; | |
handle->last_key_offset = 0; | |
continue; | |
} | |
handle->last_key_len = 0; | |
continue; | |
} | |
} | |
/* Send the buffer back to the user */ | |
if (buf_used > 0) { | |
handle->read_cb((uv_stream_t*) handle, buf_used, buf); | |
} | |
} | |
out: | |
/* Wait for more input events. */ | |
if ((handle->flags & UV_HANDLE_READING) && | |
!(handle->flags & UV_HANDLE_READ_PENDING)) { | |
uv_tty_queue_read(loop, handle); | |
} | |
DECREASE_PENDING_REQ_COUNT(handle); | |
#undef KEV | |
} | |
int uv_tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb) { | |
uv_loop_t* loop = handle->loop; | |
handle->flags |= UV_HANDLE_READING; | |
handle->read_cb = read_cb; | |
handle->alloc_cb = alloc_cb; | |
/* If reading was stopped and then started again, there could stell be a */ | |
/* read request pending. */ | |
if (handle->flags & UV_HANDLE_READ_PENDING) { | |
return; | |
} | |
/* Maybe the user stopped reading half-way while processing key events. */ | |
/* Short-circuit if this could be the case. */ | |
if (handle->last_key_len > 0) { | |
SET_REQ_SUCCESS(&handle->read_req); | |
POST_COMPLETION_FOR_REQ(handle->loop, &handle->read_req); | |
} | |
uv_tty_queue_read(loop, handle); | |
} | |
static void CALLBACK uv_tty_post_read(void* data, BOOLEAN didTimeout) { | |
uv_loop_t* loop; | |
uv_tty_t* handle; | |
uv_req_t* req; | |
assert(data); | |
assert(!didTimeout); | |
req = (uv_req_t*) data; | |
handle = (uv_tty_t*) req->data); | |
loop = handle->loop; | |
UnregisterWait(handle->input_wait_handle); | |
handle->input_wait_handle = NULL; | |
POST_COMPLETION_FOR_REQ(loop, req); | |
} | |
void uv_tty_queue_read(uv_loop_t* loop, uv_tty_t* handle) { | |
uv_req_t* req; | |
BOOL r; | |
assert(handle->flags & UV_HANDLE_READING); | |
assert(!(handle->flags & UV_HANDLE_READ_PENDING)); | |
assert(handle->handle && handle->handle != INVALID_HANDLE_VALUE); | |
assert(handle->input_event && handle->input_event != INVALID_HANDLE_VALUE); | |
req = &handle->read_req; | |
memset(&req->overlapped, 0, sizeof(req->overlapped)); | |
r = RegisterWaitForSingleObject(&handle->input_wait_handle, | |
handle->input_event, | |
uv_tty_post_read, | |
(void*) req, | |
INFINITE, | |
WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE); | |
if (!r) { | |
handle->input_wait_handle = NULL; | |
SET_REQ_ERROR(req, GetLastError()); | |
uv_insert_pending_req(loop, req); | |
} | |
handle->flags |= UV_HANDLE_READ_PENDING; | |
handle->reqs_pending++; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment