Last active
August 8, 2024 21:04
-
-
Save Hamayama/6add968870269f2426716fad79724b31 to your computer and use it in GitHub Desktop.
Windows Console Mouse Input Test
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
/* | |
Windows Console Mouse Input Test | |
2021-3-13 v1.28 | |
OS : Windows 10 (version 20H2) (64bit) | |
DevTools : MSYS2/MinGW-w64 (64bit) (gcc version 10.2.0 (Rev6, Built by MSYS2 project))) | |
Terminal : Windows Terminal 1.6.10571.0 | |
Compile : gcc -g -O2 -Wall -Wextra -o wincon_mouse.exe wincon_mouse.c | |
*/ | |
#include <windows.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#ifndef DISABLE_VT_MOUSE_INPUT | |
#define USE_VT_MOUSE_INPUT | |
#endif | |
#ifndef DISABLE_DBG_PRINT | |
#define USE_DBG_PRINT | |
#endif | |
#ifdef USE_DBG_PRINT | |
#define DBG_PRINT(...) fprintf(stderr, __VA_ARGS__); | |
#else | |
#define DBG_PRINT(...) do { } while (0) | |
#endif | |
#define MAX_INPUT_REC_LEN 20 | |
#define MAX_VT_INPUT_SEQ_LEN 6 | |
/* vt escape sequence input structure */ | |
struct vt_input | |
{ | |
char seq[MAX_VT_INPUT_SEQ_LEN + 1]; | |
int seq_len; | |
WORD vk; | |
WORD vs; | |
WCHAR uchar; | |
DWORD ctrl; | |
int modifier_index; | |
}; | |
/* vt escape sequence input table | |
( https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#input-sequences ) | |
*/ | |
static const struct vt_input vt_input_table[] = { | |
{ "[A", 2, 0x26, 0x48, 0, 0x0100, 0 }, /* Up Arrow */ | |
{ "[B", 2, 0x28, 0x50, 0, 0x0100, 0 }, /* Down Arrow */ | |
{ "[C", 2, 0x27, 0x4d, 0, 0x0100, 0 }, /* Right Arrow */ | |
{ "[D", 2, 0x25, 0x4b, 0, 0x0100, 0 }, /* Left Arrow */ | |
{ "[H", 2, 0x24, 0x47, 0, 0x0100, 0 }, /* Home */ | |
{ "[F", 2, 0x23, 0x4f, 0, 0x0100, 0 }, /* End */ | |
{ "[1;@A", 5, 0x26, 0x48, 0, 0x0100, 3 }, /* ModifierKeys + Up Arrow */ | |
{ "[1;@B", 5, 0x28, 0x50, 0, 0x0100, 3 }, /* ModifierKeys + Down Arrow */ | |
{ "[1;@C", 5, 0x27, 0x4d, 0, 0x0100, 3 }, /* ModifierKeys + Right Arrow */ | |
{ "[1;@D", 5, 0x25, 0x4b, 0, 0x0100, 3 }, /* ModifierKeys + Left Arrow */ | |
{ "[1;@H", 5, 0x24, 0x47, 0, 0x0100, 3 }, /* ModifierKeys + Home */ | |
{ "[1;@F", 5, 0x23, 0x4f, 0, 0x0100, 3 }, /* ModifierKeys + End */ | |
{ "[2~", 3, 0x2d, 0x52, 0, 0x0100, 0 }, /* Insert */ | |
{ "[3~", 3, 0x2e, 0x53, 0, 0x0100, 0 }, /* Delete */ | |
{ "[5~", 3, 0x21, 0x49, 0, 0x0100, 0 }, /* Page Up */ | |
{ "[6~", 3, 0x22, 0x51, 0, 0x0100, 0 }, /* Page Down */ | |
{ "[2;@~", 5, 0x2d, 0x52, 0, 0x0100, 3 }, /* ModifierKeys + Insert */ | |
{ "[3;@~", 5, 0x2e, 0x53, 0, 0x0100, 3 }, /* ModifierKeys + Delete */ | |
{ "[5;@~", 5, 0x21, 0x49, 0, 0x0100, 3 }, /* ModifierKeys + Page Up */ | |
{ "[6;@~", 5, 0x22, 0x51, 0, 0x0100, 3 }, /* ModifierKeys + Page Down */ | |
{ "OP", 2, 0x70, 0x3b, 0, 0x0000, 0 }, /* F1 */ | |
{ "OQ", 2, 0x71, 0x3c, 0, 0x0000, 0 }, /* F2 */ | |
{ "OR", 2, 0x72, 0x3d, 0, 0x0000, 0 }, /* F3 */ | |
{ "OS", 2, 0x73, 0x3e, 0, 0x0000, 0 }, /* F4 */ | |
{ "[15~", 4, 0x74, 0x3f, 0, 0x0000, 0 }, /* F5 */ | |
{ "[17~", 4, 0x75, 0x40, 0, 0x0000, 0 }, /* F6 */ | |
{ "[18~", 4, 0x76, 0x41, 0, 0x0000, 0 }, /* F7 */ | |
{ "[19~", 4, 0x77, 0x42, 0, 0x0000, 0 }, /* F8 */ | |
{ "[20~", 4, 0x78, 0x43, 0, 0x0000, 0 }, /* F9 */ | |
{ "[21~", 4, 0x79, 0x44, 0, 0x0000, 0 }, /* F10 */ | |
{ "[23~", 4, 0x7a, 0x57, 0, 0x0000, 0 }, /* F11 */ | |
{ "[24~", 4, 0x7b, 0x58, 0, 0x0000, 0 }, /* F12 */ | |
{ "[1;@P", 5, 0x70, 0x3b, 0, 0x0000, 3 }, /* ModifierKeys + F1 */ | |
{ "[1;@Q", 5, 0x71, 0x3c, 0, 0x0000, 3 }, /* ModifierKeys + F2 */ | |
{ "[1;@R", 5, 0x72, 0x3d, 0, 0x0000, 3 }, /* ModifierKeys + F3 */ | |
{ "[1;@S", 5, 0x73, 0x3e, 0, 0x0000, 3 }, /* ModifierKeys + F4 */ | |
{ "[15;@~", 6, 0x74, 0x3f, 0, 0x0000, 4 }, /* ModifierKeys + F5 */ | |
{ "[17;@~", 6, 0x75, 0x40, 0, 0x0000, 4 }, /* ModifierKeys + F6 */ | |
{ "[18;@~", 6, 0x76, 0x41, 0, 0x0000, 4 }, /* ModifierKeys + F7 */ | |
{ "[19;@~", 6, 0x77, 0x42, 0, 0x0000, 4 }, /* ModifierKeys + F8 */ | |
{ "[20;@~", 6, 0x78, 0x43, 0, 0x0000, 4 }, /* ModifierKeys + F9 */ | |
{ "[21;@~", 6, 0x79, 0x44, 0, 0x0000, 4 }, /* ModifierKeys + F10 */ | |
{ "[23;@~", 6, 0x7a, 0x57, 0, 0x0000, 4 }, /* ModifierKeys + F11 */ | |
{ "[24;@~", 6, 0x7b, 0x58, 0, 0x0000, 4 }, /* ModifierKeys + F12 */ | |
{ "[Z", 2, 0x9, 0xf, 0x9, 0x0000, 0 } /* Shift + Tab */ | |
}; | |
/* inner functions */ | |
static int is_vt_input(const INPUT_RECORD *input_rec_ptr); | |
static void set_key_event(PINPUT_RECORD input_rec_ptr, WORD vk, WORD vs, WCHAR uchar, DWORD ctrl); | |
static BOOL consume_vt_input(HANDLE hin, int input_seq_len); | |
static BOOL read_console_input_w_sub(HANDLE hin, PINPUT_RECORD input_rec_ptr, DWORD input_rec_len, LPDWORD read_event_num_ptr, int peek_flag); | |
/* api functions */ | |
BOOL PDC_peek_console_input_w(HANDLE hin, PINPUT_RECORD input_rec_ptr, DWORD input_rec_len, LPDWORD read_event_num_ptr); | |
BOOL PDC_read_console_input_w(HANDLE hin, PINPUT_RECORD input_rec_ptr, DWORD input_rec_len, LPDWORD read_event_num_ptr); | |
/* check vt escape sequence */ | |
static int is_vt_input(const INPUT_RECORD *input_rec_ptr) | |
{ | |
return (input_rec_ptr->EventType == KEY_EVENT && | |
input_rec_ptr->Event.KeyEvent.bKeyDown && | |
input_rec_ptr->Event.KeyEvent.wVirtualKeyCode == 0 && | |
input_rec_ptr->Event.KeyEvent.wVirtualScanCode == 0); | |
} | |
/* set key event */ | |
static void set_key_event(PINPUT_RECORD input_rec_ptr, WORD vk, WORD vs, WCHAR uchar, DWORD ctrl) | |
{ | |
input_rec_ptr->EventType = KEY_EVENT; | |
input_rec_ptr->Event.KeyEvent.bKeyDown = 1; | |
input_rec_ptr->Event.KeyEvent.wRepeatCount = 1; | |
input_rec_ptr->Event.KeyEvent.wVirtualKeyCode = vk; | |
input_rec_ptr->Event.KeyEvent.wVirtualScanCode = vs; | |
input_rec_ptr->Event.KeyEvent.uChar.UnicodeChar = uchar; | |
input_rec_ptr->Event.KeyEvent.dwControlKeyState = ctrl; | |
} | |
/* consume vt escape sequence */ | |
static BOOL consume_vt_input(HANDLE hin, int input_seq_len) { | |
INPUT_RECORD input_rec[MAX_INPUT_REC_LEN]; | |
DWORD read_event_num; | |
/* check arguments */ | |
if (input_seq_len > MAX_INPUT_REC_LEN) { | |
DBG_PRINT("internal error. (consume)\n"); | |
SetLastError(ERROR_INTERNAL_ERROR); | |
return FALSE; | |
} | |
/* consume vt escape sequence */ | |
if (!ReadConsoleInputW(hin, input_rec, input_seq_len, &read_event_num)) { | |
DBG_PRINT("ReadConsoleInputW failed. (consume)\n"); | |
return FALSE; | |
} | |
if ((int)read_event_num < input_seq_len) { | |
DBG_PRINT("ReadConsoleInputW returned before read. (consume)\n"); | |
SetLastError(ERROR_INTERNAL_ERROR); | |
return FALSE; | |
} | |
return TRUE; | |
} | |
/* peek/read console input | |
limitations: | |
- only one input record is returned. | |
- separated receive of vt escape sequence is not supported. | |
- double click event is not supported. | |
- horizontal mouse wheel is not supported. | |
- virtual key code and virtual scan code casually become zero. | |
- Alt + X key combination input is split into Esc and X key events. | |
- mouse coordinates origin is top left of screen (not top left of buffer). | |
*/ | |
BOOL PDC_peek_console_input_w(HANDLE hin, PINPUT_RECORD input_rec_ptr, DWORD input_rec_len, LPDWORD read_event_num_ptr) | |
{ | |
return read_console_input_w_sub(hin, input_rec_ptr, input_rec_len, read_event_num_ptr, 1); | |
} | |
BOOL PDC_read_console_input_w(HANDLE hin, PINPUT_RECORD input_rec_ptr, DWORD input_rec_len, LPDWORD read_event_num_ptr) | |
{ | |
return read_console_input_w_sub(hin, input_rec_ptr, input_rec_len, read_event_num_ptr, 0); | |
} | |
static BOOL read_console_input_w_sub(HANDLE hin, PINPUT_RECORD input_rec_ptr, DWORD input_rec_len, LPDWORD read_event_num_ptr, int peek_flag) | |
{ | |
static DWORD mouse_button_state = 0; | |
static DWORD ctrl_state = 0; | |
INPUT_RECORD input_rec2[MAX_INPUT_REC_LEN]; | |
DWORD read_event_num2; | |
char input_seq[MAX_INPUT_REC_LEN + 1]; | |
int input_seq_len; | |
int input_seq_len2; | |
int vt_input_table_len; | |
int mouse_cmd_read_state; | |
int mouse_button_param; | |
int mouse_x; | |
int mouse_y; | |
int mouse_button_press; | |
DWORD mouse_event_flags; | |
int ret_val; | |
int i; | |
/* initialize return data */ | |
*read_event_num_ptr = 0; | |
/* check arguments */ | |
if (input_rec_len == 0) { | |
/* DBG_PRINT("specified input record length is zero.\n"); */ | |
return TRUE; | |
} | |
/* check peek flag */ | |
if (peek_flag) { | |
/* peek all console input */ | |
if (!PeekConsoleInputW(hin, input_rec2, MAX_INPUT_REC_LEN, &read_event_num2)) { | |
DBG_PRINT("PeekConsoleInputW failed. (input_rec2)\n"); | |
return FALSE; | |
} | |
if (read_event_num2 == 0) { | |
/* DBG_PRINT("PeekConsoleInputW returned before read. (input_rec2)\n"); */ | |
return TRUE; | |
} | |
} else { | |
/* read one console input */ | |
if (!ReadConsoleInputW(hin, input_rec2, 1, &read_event_num2)) { | |
DBG_PRINT("ReadConsoleInputW failed. (input_rec2)\n"); | |
return FALSE; | |
} | |
if (read_event_num2 == 0) { | |
DBG_PRINT("ReadConsoleInputW returned before read. (input_rec2)\n"); | |
SetLastError(ERROR_INTERNAL_ERROR); | |
return FALSE; | |
} | |
/* peek all console input */ | |
if (!PeekConsoleInputW(hin, &input_rec2[1], MAX_INPUT_REC_LEN - 1, &read_event_num2)) { | |
DBG_PRINT("PeekConsoleInputW failed. (input_rec2) (after read)\n"); | |
return FALSE; | |
} | |
read_event_num2++; | |
} | |
/* check read event number */ | |
if (read_event_num2 > MAX_INPUT_REC_LEN) { | |
DBG_PRINT("internal error. (input_rec2)\n"); | |
SetLastError(ERROR_INTERNAL_ERROR); | |
return FALSE; | |
} | |
/* set return data (only one input record is returned) */ | |
*read_event_num_ptr = 1; | |
memcpy(input_rec_ptr, &input_rec2[0], sizeof(INPUT_RECORD)); | |
/* get/set modifier key state */ | |
if (input_rec2[0].EventType == KEY_EVENT) { | |
if (!is_vt_input(&input_rec2[0])) { | |
/* get modifier key state */ | |
ctrl_state = input_rec2[0].Event.KeyEvent.dwControlKeyState; | |
} else { | |
/* vt escape sequence doesn't have modifier key state. | |
so, we set the current state here. */ | |
/* (drop left alt key state to avoid unwanted character code | |
conversion on PDCurses) */ | |
input_rec_ptr->Event.KeyEvent.dwControlKeyState = ctrl_state & ~LEFT_ALT_PRESSED; | |
} | |
} | |
/* convert some keys */ | |
if (is_vt_input(&input_rec2[0]) && | |
input_rec2[0].Event.KeyEvent.uChar.UnicodeChar == 0x7f) { /* Backspace */ | |
set_key_event(input_rec_ptr, 0x8, 0xe, 0x8, ctrl_state); | |
return TRUE; | |
} | |
if (is_vt_input(&input_rec2[0]) && | |
input_rec2[0].Event.KeyEvent.uChar.UnicodeChar == 0x8) { /* Ctrl + Backspace */ | |
set_key_event(input_rec_ptr, 0x8, 0xe, 0x7f, ctrl_state); | |
return TRUE; | |
} | |
#if 0 | |
/* this breaks Ctrl + Z key input */ | |
if (is_vt_input(&input_rec2[0]) && | |
input_rec2[0].Event.KeyEvent.uChar.UnicodeChar == 0x1a) { /* Pause */ | |
set_key_event(input_rec_ptr, 0x13, 0x45, 0, ctrl_state); | |
return TRUE; | |
} | |
#endif | |
if (input_rec2[0].EventType == KEY_EVENT && | |
input_rec2[0].Event.KeyEvent.bKeyDown && | |
input_rec2[0].Event.KeyEvent.wVirtualKeyCode == 0x32 && | |
input_rec2[0].Event.KeyEvent.wVirtualScanCode == 0 && | |
input_rec2[0].Event.KeyEvent.uChar.UnicodeChar == 0) { /* Ctrl + Space */ | |
set_key_event(input_rec_ptr, 0x20, 0x39, 0x20, ctrl_state); | |
return TRUE; | |
} | |
if (is_vt_input(&input_rec2[0]) && | |
input_rec2[0].Event.KeyEvent.uChar.UnicodeChar == 0x9) { /* Ctrl + Tab */ | |
set_key_event(input_rec_ptr, 0x9, 0xf, 0x0, ctrl_state); | |
return TRUE; | |
} | |
/* process vt escape sequence */ | |
if (is_vt_input(&input_rec2[0]) && | |
input_rec2[0].Event.KeyEvent.uChar.UnicodeChar == 0x1b) { | |
/* read vt escape sequence */ | |
input_seq_len = 0; | |
for (i = 1; i < (int)read_event_num2; i++) { | |
if (is_vt_input(&input_rec2[i]) && | |
input_rec2[i].Event.KeyEvent.uChar.UnicodeChar <= 0x7f) { | |
input_seq[input_seq_len] = input_rec2[i].Event.KeyEvent.uChar.UnicodeChar; | |
input_seq_len++; | |
continue; | |
} | |
break; | |
} | |
input_seq[input_seq_len] = 0; | |
#if 0 | |
/* for debug */ | |
DBG_PRINT(" vt input seq : "); | |
for (i = 0; i < MAX_INPUT_REC_LEN; i++) { | |
DBG_PRINT("%02x ", (unsigned char)input_seq[i]); | |
} | |
DBG_PRINT("\n"); | |
#endif | |
/* process vt escape sequence of mouse input (sgr-1006) '[<' */ | |
if (input_seq_len >= 2 && | |
input_seq[0] == 0x5b && | |
input_seq[1] == 0x3c) { | |
input_seq_len2 = 2; | |
/* read parameters 'Db ; Dx ; Dy M/m' */ | |
mouse_cmd_read_state = 0; | |
mouse_button_param = 0; | |
mouse_x = 0; | |
mouse_y = 0; | |
mouse_button_press = 0; | |
mouse_event_flags = 0; | |
ret_val = FALSE; | |
for (i = 2; i < input_seq_len; i++) { | |
switch (mouse_cmd_read_state) { | |
case 0: | |
/* read mouse button parameter 'Db' */ | |
if (input_seq[i] >= 0x30 && input_seq[i] <= 0x39) { | |
input_seq_len2++; | |
mouse_button_param *= 10; | |
mouse_button_param += input_seq[i] - 0x30; | |
if (mouse_button_param < 10000) { | |
continue; | |
} | |
} | |
/* read delimiter ';' */ | |
if (input_seq[i] == 0x3b) { | |
input_seq_len2++; | |
mouse_cmd_read_state++; | |
continue; | |
} | |
break; | |
case 1: | |
/* read mouse position x 'Dx' */ | |
if (input_seq[i] >= 0x30 && input_seq[i] <= 0x39) { | |
input_seq_len2++; | |
mouse_x *= 10; | |
mouse_x += input_seq[i] - 0x30; | |
if (mouse_x < 10000) { | |
continue; | |
} | |
} | |
/* read delimiter ';' */ | |
if (input_seq[i] == 0x3b) { | |
input_seq_len2++; | |
mouse_cmd_read_state++; | |
continue; | |
} | |
break; | |
case 2: | |
/* read mouse position y 'Dy' */ | |
if (input_seq[i] >= 0x30 && input_seq[i] <= 0x39) { | |
input_seq_len2++; | |
mouse_y *= 10; | |
mouse_y += input_seq[i] - 0x30; | |
if (mouse_y < 10000) { | |
continue; | |
} | |
} | |
/* read button on/off 'M/m' */ | |
if ((input_seq[i] == 0x4d || input_seq[i] == 0x6d)) { | |
input_seq_len2++; | |
mouse_button_press = (input_seq[i] == 0x4d) ? 1 : 0; | |
/* make mouse event */ | |
input_rec_ptr->EventType = MOUSE_EVENT; | |
input_rec_ptr->Event.MouseEvent.dwMousePosition.X = mouse_x - 1; | |
input_rec_ptr->Event.MouseEvent.dwMousePosition.Y = mouse_y - 1; | |
input_rec_ptr->Event.MouseEvent.dwButtonState = 0; | |
input_rec_ptr->Event.MouseEvent.dwControlKeyState = ctrl_state; | |
input_rec_ptr->Event.MouseEvent.dwEventFlags = 0; | |
/* set mouse button state */ | |
mouse_button_state &= ~0xffff0000; /* clear wheel movement amount */ | |
switch (mouse_button_param & 0xc3) { /* extract button no */ | |
case 0: /* left button */ | |
if (mouse_button_press) { | |
mouse_button_state |= FROM_LEFT_1ST_BUTTON_PRESSED; | |
} else { | |
mouse_button_state &= ~FROM_LEFT_1ST_BUTTON_PRESSED; | |
} | |
break; | |
case 1: /* middle button */ | |
if (mouse_button_press) { | |
mouse_button_state |= FROM_LEFT_2ND_BUTTON_PRESSED; | |
} else { | |
mouse_button_state &= ~FROM_LEFT_2ND_BUTTON_PRESSED; | |
} | |
break; | |
case 2: /* right button */ | |
if (mouse_button_press) { | |
mouse_button_state |= RIGHTMOST_BUTTON_PRESSED; | |
} else { | |
mouse_button_state &= ~RIGHTMOST_BUTTON_PRESSED; | |
} | |
break; | |
case 64: /* vertical wheel up */ | |
mouse_event_flags |= MOUSE_WHEELED; | |
mouse_button_state |= 0x00780000; | |
break; | |
case 65: /* vertical wheel down */ | |
mouse_event_flags |= MOUSE_WHEELED; | |
mouse_button_state |= 0xff880000; | |
break; | |
} | |
input_rec_ptr->Event.MouseEvent.dwButtonState = mouse_button_state; | |
/* set mouse event flags */ | |
if (mouse_button_param & 0x20) { /* mouse moved */ | |
/* PDCurses uses MOUSE_MOVED with a different definition from Win32 API */ | |
/* mouse_event_flags |= MOUSE_MOVED; */ | |
mouse_event_flags |= 0x1; | |
} | |
input_rec_ptr->Event.MouseEvent.dwEventFlags = mouse_event_flags; | |
/* succeeded */ | |
ret_val = TRUE; | |
} | |
break; | |
} | |
break; | |
} | |
/* check failure */ | |
if (ret_val == FALSE) { | |
DBG_PRINT("mouse input failed.\n"); | |
} | |
/* consume vt escape sequence */ | |
if (!peek_flag && !consume_vt_input(hin, input_seq_len2)) { | |
return FALSE; | |
} | |
return ret_val; | |
} | |
/* process other vt escape sequence */ | |
if (input_seq_len > 0) { | |
/* search vt escape sequence input table */ | |
vt_input_table_len = sizeof(vt_input_table) / sizeof(struct vt_input); | |
for (i = 0; i < vt_input_table_len; i++) { | |
if (input_seq_len >= vt_input_table[i].seq_len && | |
((vt_input_table[i].modifier_index <= 0 && | |
!strncmp(vt_input_table[i].seq, input_seq, vt_input_table[i].seq_len)) || | |
(!strncmp(vt_input_table[i].seq, input_seq, vt_input_table[i].modifier_index) && | |
!strncmp(vt_input_table[i].seq + vt_input_table[i].modifier_index + 1, | |
input_seq + vt_input_table[i].modifier_index + 1, | |
vt_input_table[i].seq_len - vt_input_table[i].modifier_index - 1)))) { | |
/* set key event */ | |
set_key_event(input_rec_ptr, | |
vt_input_table[i].vk, | |
vt_input_table[i].vs, | |
vt_input_table[i].uchar, | |
ctrl_state | vt_input_table[i].ctrl); | |
/* consume vt escape sequence */ | |
if (!peek_flag && !consume_vt_input(hin, vt_input_table[i].seq_len)) { | |
return FALSE; | |
} | |
return TRUE; | |
} | |
} | |
} | |
} | |
return TRUE; | |
} | |
/* display last error message */ | |
void disp_last_err_msg(void) | |
{ | |
int err_code; | |
char *err_msg = NULL; | |
err_code = GetLastError(); | |
if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | | |
FORMAT_MESSAGE_FROM_SYSTEM | | |
FORMAT_MESSAGE_IGNORE_INSERTS, | |
NULL, | |
err_code, | |
MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), | |
(LPSTR)&err_msg, | |
0, | |
NULL)) { | |
DBG_PRINT("error code=%d : %s\n", err_code, err_msg); | |
} else { | |
DBG_PRINT("error code=%d : (no message)\n", err_code); | |
} | |
LocalFree(err_msg); | |
} | |
/* main function for test */ | |
int main(void) | |
{ | |
HANDLE hin = GetStdHandle(STD_INPUT_HANDLE); | |
HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE); | |
DWORD con_input_mode_orig; | |
DWORD con_output_mode_orig; | |
INPUT_RECORD input_rec; | |
DWORD read_event_num; | |
INPUT_RECORD input_rec_peek; | |
DWORD read_event_num_peek; | |
#ifdef USE_VT_MOUSE_INPUT | |
const char *vt_mouse_input_enable_cmd = "\x1b[?1003;1006h"; | |
const char *vt_mouse_input_disable_cmd = "\x1b[?1003;1006l"; | |
DWORD written; | |
#endif | |
int input_loop; | |
int ret_val; | |
int i; | |
/* get console mode */ | |
GetConsoleMode(hin, &con_input_mode_orig); | |
GetConsoleMode(hout, &con_output_mode_orig); | |
/* set console mode */ | |
#ifdef USE_VT_MOUSE_INPUT | |
if (!SetConsoleMode(hin, ENABLE_VIRTUAL_TERMINAL_INPUT | ENABLE_WINDOW_INPUT)) { | |
DBG_PRINT("SetConsoleMode for input failed. (VT escape sequence might not be supported)\n"); | |
return 1; | |
} | |
if (!SetConsoleMode(hout, ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) { | |
DBG_PRINT("SetConsoleMode for output failed. (VT escape sequence might not be supported)\n"); | |
SetConsoleMode(hin, con_input_mode_orig); | |
return 1; | |
} | |
#else | |
/* (To set ENABLE_MOUSE_INPUT to on, we must set ENABLE_QUICK_EDIT_MODE to off) */ | |
SetConsoleMode(hin, ENABLE_MOUSE_INPUT | ENABLE_EXTENDED_FLAGS | ENABLE_WINDOW_INPUT); | |
SetConsoleMode(hout, ENABLE_PROCESSED_OUTPUT); | |
#endif | |
#ifdef USE_VT_MOUSE_INPUT | |
/* enable vt escape sequence of mouse input (sgr-1006) */ | |
WriteConsoleA(hout, vt_mouse_input_enable_cmd, strlen(vt_mouse_input_enable_cmd), &written, NULL); | |
#endif | |
/* start-up message */ | |
DBG_PRINT("Please try key and mouse input.\n"); | |
DBG_PRINT("( [q] key to quit )\n"); | |
/* input loop */ | |
input_loop = 1; | |
ret_val = 0; | |
while (input_loop) { | |
/* peek console input */ | |
memset(&input_rec_peek, 0, sizeof(INPUT_RECORD)); | |
if (!PDC_peek_console_input_w(hin, &input_rec_peek, 1, &read_event_num_peek)) { | |
DBG_PRINT("PDC_peek_console_input_w failed.\n"); | |
disp_last_err_msg(); | |
ret_val = 1; | |
break; | |
} | |
if (read_event_num_peek == 0) { | |
Sleep(10); | |
continue; | |
} | |
/* read console input */ | |
memset(&input_rec, 0, sizeof(INPUT_RECORD)); | |
if (!PDC_read_console_input_w(hin, &input_rec, 1, &read_event_num)) { | |
DBG_PRINT("PDC_read_console_input_w failed.\n"); | |
disp_last_err_msg(); | |
ret_val = 1; | |
break; | |
} | |
if (read_event_num == 0) { | |
DBG_PRINT("PDC_read_console_input_w returned before read.\n"); | |
ret_val = 1; | |
break; | |
} | |
/* check input data */ | |
if (memcmp(&input_rec_peek, &input_rec, sizeof(INPUT_RECORD))) { | |
DBG_PRINT("read data corruption occurred.\n"); | |
DBG_PRINT(" size=%d\n", (int)sizeof(INPUT_RECORD)); | |
DBG_PRINT(" peek : "); | |
for (i = 0; i < (int)sizeof(INPUT_RECORD); i++) { | |
DBG_PRINT("%02x ", *((unsigned char*)&input_rec_peek + i)); | |
} | |
DBG_PRINT("\n"); | |
DBG_PRINT(" read : "); | |
for (i = 0; i < (int)sizeof(INPUT_RECORD); i++) { | |
DBG_PRINT("%02x ", *((unsigned char*)&input_rec + i)); | |
} | |
DBG_PRINT("\n"); | |
continue; | |
} | |
/* process event */ | |
switch (input_rec.EventType) { | |
case KEY_EVENT: | |
DBG_PRINT("key event : kdown=%d repeat=%d vkey=0x%x vscan=0x%x uchar=0x%x ctrl=0x%lx\n", | |
input_rec.Event.KeyEvent.bKeyDown, | |
input_rec.Event.KeyEvent.wRepeatCount, | |
input_rec.Event.KeyEvent.wVirtualKeyCode, | |
input_rec.Event.KeyEvent.wVirtualScanCode, | |
input_rec.Event.KeyEvent.uChar.UnicodeChar, | |
input_rec.Event.KeyEvent.dwControlKeyState); | |
/* process key event */ | |
if (!input_rec.Event.KeyEvent.bKeyDown) { | |
switch (input_rec.Event.KeyEvent.wVirtualKeyCode) { | |
/* [q] key to quit */ | |
case 0x51: | |
input_loop = 0; | |
break; | |
/* [l] key to clear screen */ | |
case 0x4c: | |
system("cls"); | |
break; | |
} | |
} | |
break; | |
case MOUSE_EVENT: | |
DBG_PRINT("mouse event : (%d,%d) btn=0x%lx ctrl=0x%lx evt=0x%lx\n", | |
input_rec.Event.MouseEvent.dwMousePosition.X, | |
input_rec.Event.MouseEvent.dwMousePosition.Y, | |
input_rec.Event.MouseEvent.dwButtonState, | |
input_rec.Event.MouseEvent.dwControlKeyState, | |
input_rec.Event.MouseEvent.dwEventFlags); | |
break; | |
case WINDOW_BUFFER_SIZE_EVENT: | |
DBG_PRINT("window buffer size event : width=%d height=%d\n", | |
input_rec.Event.WindowBufferSizeEvent.dwSize.X, | |
input_rec.Event.WindowBufferSizeEvent.dwSize.Y); | |
break; | |
} | |
} | |
#ifdef USE_VT_MOUSE_INPUT | |
/* disable vt escape sequence of mouse input (sgr-1006) */ | |
WriteConsoleA(hout, vt_mouse_input_disable_cmd, strlen(vt_mouse_input_disable_cmd), &written, NULL); | |
#endif | |
/* restore console mode */ | |
SetConsoleMode(hin, con_input_mode_orig); | |
SetConsoleMode(hout, con_output_mode_orig); | |
return ret_val; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment