Skip to content

Instantly share code, notes, and snippets.

@piscisaureus
Created September 24, 2011 04:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save piscisaureus/1238953 to your computer and use it in GitHub Desktop.
Save piscisaureus/1238953 to your computer and use it in GitHub Desktop.
/* 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