forked from aniani/vim
patch 9.0.1084: code handling low level MS-Windows events cannot be tested
Problem: Code handling low level MS-Windows events cannot be tested. Solution: Add test_mswin_event() and tests using it. (Christopher Plewright, closes #11622)
This commit is contained in:
committed by
Bram Moolenaar
parent
418b547881
commit
20b795e0eb
378
src/os_win32.c
378
src/os_win32.c
@@ -177,6 +177,25 @@ static void gotoxy(unsigned x, unsigned y);
|
||||
static void standout(void);
|
||||
static int s_cursor_visible = TRUE;
|
||||
static int did_create_conin = FALSE;
|
||||
// The 'input_record_buffer' is an internal dynamic fifo queue of MS-Windows
|
||||
// console INPUT_RECORD events that are normally read from the console input
|
||||
// buffer. This provides an injection point for testing the low-level handling
|
||||
// of INPUT_RECORDs.
|
||||
typedef struct input_record_buffer_node_S
|
||||
{
|
||||
INPUT_RECORD ir;
|
||||
struct input_record_buffer_node_S *next;
|
||||
} input_record_buffer_node_T;
|
||||
typedef struct input_record_buffer_S
|
||||
{
|
||||
input_record_buffer_node_T *head;
|
||||
input_record_buffer_node_T *tail;
|
||||
int length;
|
||||
} input_record_buffer_T;
|
||||
static input_record_buffer_T input_record_buffer;
|
||||
static int peek_input_record_buffer(INPUT_RECORD* irEvents, int nMaxLength);
|
||||
static int read_input_record_buffer(INPUT_RECORD* irEvents, int nMaxLength);
|
||||
static int write_input_record_buffer(INPUT_RECORD* irEvents, int nLength);
|
||||
#endif
|
||||
#ifdef FEAT_GUI_MSWIN
|
||||
static int s_dont_use_vimrun = TRUE;
|
||||
@@ -224,7 +243,7 @@ static int default_console_color_fg = 0xc0c0c0; // white
|
||||
static void set_console_color_rgb(void);
|
||||
static void reset_console_color_rgb(void);
|
||||
static void restore_console_color_rgb(void);
|
||||
#endif
|
||||
#endif // !FEAT_GUI_MSWIN || VIMDLL
|
||||
|
||||
// This flag is newly created from Windows 10
|
||||
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||
@@ -319,6 +338,13 @@ read_console_input(
|
||||
int i;
|
||||
static INPUT_RECORD s_irPseudo;
|
||||
|
||||
if (s_dwMax == 0 && input_record_buffer.length > 0)
|
||||
{
|
||||
dwEvents = read_input_record_buffer(s_irCache, IRSIZE);
|
||||
s_dwIndex = 0;
|
||||
s_dwMax = dwEvents;
|
||||
}
|
||||
|
||||
if (nLength == -2)
|
||||
return (s_dwMax > 0) ? TRUE : FALSE;
|
||||
|
||||
@@ -431,7 +457,7 @@ wait_for_single_object(
|
||||
return WaitForSingleObject(hHandle, dwMilliseconds);
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
#endif // !FEAT_GUI_MSWIN || VIMDLL
|
||||
|
||||
static void
|
||||
get_exe_name(void)
|
||||
@@ -1014,7 +1040,7 @@ win32_kbd_patch_key(
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (pker->uChar.UnicodeChar != 0)
|
||||
if (pker->uChar.UnicodeChar > 0 && pker->uChar.UnicodeChar < 0xfffd)
|
||||
return 1;
|
||||
|
||||
CLEAR_FIELD(abKeystate);
|
||||
@@ -1080,7 +1106,8 @@ decode_key_event(
|
||||
|
||||
// special cases
|
||||
if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0
|
||||
&& pker->uChar.UnicodeChar == NUL)
|
||||
&& (pker->uChar.UnicodeChar == NUL
|
||||
|| pker->uChar.UnicodeChar == 0xfffd))
|
||||
{
|
||||
// Ctrl-6 is Ctrl-^
|
||||
if (pker->wVirtualKeyCode == '6')
|
||||
@@ -1168,7 +1195,113 @@ decode_key_event(
|
||||
return (*pch != NUL);
|
||||
}
|
||||
|
||||
#endif // FEAT_GUI_MSWIN
|
||||
# if defined(FEAT_EVAL)
|
||||
static int
|
||||
encode_key_event(dict_T *args, INPUT_RECORD *ir)
|
||||
{
|
||||
static int s_dwMods = 0;
|
||||
|
||||
char_u *event = dict_get_string(args, "event", TRUE);
|
||||
if (event && (STRICMP(event, "keydown") == 0
|
||||
|| STRICMP(event, "keyup") == 0))
|
||||
{
|
||||
WORD vkCode = dict_get_number_def(args, "keycode", 0);
|
||||
if (vkCode <= 0 || vkCode >= 0xFF)
|
||||
{
|
||||
semsg(_(e_invalid_argument_nr), (long)vkCode);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ir->EventType = KEY_EVENT;
|
||||
KEY_EVENT_RECORD ker;
|
||||
ZeroMemory(&ker, sizeof(ker));
|
||||
ker.bKeyDown = STRICMP(event, "keydown") == 0;
|
||||
ker.wRepeatCount = 1;
|
||||
ker.wVirtualScanCode = 0;
|
||||
ker.dwControlKeyState = 0;
|
||||
int mods = (int)dict_get_number(args, "modifiers");
|
||||
// Encode the win32 console key modifiers from Vim keyboard modifiers.
|
||||
if (mods)
|
||||
{
|
||||
// If "modifiers" is explicitly set in the args, then we reset any
|
||||
// remembered modifer key state that may have been set from earlier
|
||||
// mod-key-down events, even if they are not yet unset by earlier
|
||||
// mod-key-up events.
|
||||
s_dwMods = 0;
|
||||
if (mods & MOD_MASK_SHIFT)
|
||||
ker.dwControlKeyState |= SHIFT_PRESSED;
|
||||
if (mods & MOD_MASK_CTRL)
|
||||
ker.dwControlKeyState |= LEFT_CTRL_PRESSED;
|
||||
if (mods & MOD_MASK_ALT)
|
||||
ker.dwControlKeyState |= LEFT_ALT_PRESSED;
|
||||
}
|
||||
|
||||
if (vkCode == VK_LSHIFT || vkCode == VK_RSHIFT || vkCode == VK_SHIFT)
|
||||
{
|
||||
if (STRICMP(event, "keydown") == 0)
|
||||
s_dwMods |= SHIFT_PRESSED;
|
||||
else
|
||||
s_dwMods &= ~SHIFT_PRESSED;
|
||||
}
|
||||
else if (vkCode == VK_LCONTROL || vkCode == VK_CONTROL)
|
||||
{
|
||||
if (STRICMP(event, "keydown") == 0)
|
||||
s_dwMods |= LEFT_CTRL_PRESSED;
|
||||
else
|
||||
s_dwMods &= ~LEFT_CTRL_PRESSED;
|
||||
}
|
||||
else if (vkCode == VK_RCONTROL)
|
||||
{
|
||||
if (STRICMP(event, "keydown") == 0)
|
||||
s_dwMods |= RIGHT_CTRL_PRESSED;
|
||||
else
|
||||
s_dwMods &= ~RIGHT_CTRL_PRESSED;
|
||||
}
|
||||
else if (vkCode == VK_LMENU || vkCode == VK_MENU)
|
||||
{
|
||||
if (STRICMP(event, "keydown") == 0)
|
||||
s_dwMods |= LEFT_ALT_PRESSED;
|
||||
else
|
||||
s_dwMods &= ~LEFT_ALT_PRESSED;
|
||||
}
|
||||
else if (vkCode == VK_RMENU)
|
||||
{
|
||||
if (STRICMP(event, "keydown") == 0)
|
||||
s_dwMods |= RIGHT_ALT_PRESSED;
|
||||
else
|
||||
s_dwMods &= ~RIGHT_ALT_PRESSED;
|
||||
}
|
||||
ker.dwControlKeyState |= s_dwMods;
|
||||
ker.wVirtualKeyCode = vkCode;
|
||||
win32_kbd_patch_key(&ker);
|
||||
|
||||
for (int i = ARRAY_LENGTH(VirtKeyMap);
|
||||
--i >= 0 && !ker.uChar.UnicodeChar; )
|
||||
{
|
||||
if (VirtKeyMap[i].wVirtKey == vkCode)
|
||||
ker.uChar.UnicodeChar = 0xfffd; // REPLACEMENT CHARACTER
|
||||
}
|
||||
|
||||
ir->Event.KeyEvent = ker;
|
||||
vim_free(event);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (event == NULL)
|
||||
{
|
||||
semsg(_(e_missing_argument_str), "event");
|
||||
}
|
||||
else
|
||||
{
|
||||
semsg(_(e_invalid_value_for_argument_str_str), "event", event);
|
||||
vim_free(event);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
# endif // FEAT_EVAL
|
||||
#endif // !FEAT_GUI_MSWIN || VIMDLL
|
||||
|
||||
|
||||
/*
|
||||
@@ -1179,7 +1312,7 @@ decode_key_event(
|
||||
mch_setmouse(int on UNUSED)
|
||||
{
|
||||
}
|
||||
#else
|
||||
#else // !FEAT_GUI_MSWIN || VIMDLL
|
||||
static int g_fMouseAvail = FALSE; // mouse present
|
||||
static int g_fMouseActive = FALSE; // mouse enabled
|
||||
static int g_nMouseClick = -1; // mouse status
|
||||
@@ -1234,21 +1367,21 @@ mch_bevalterm_changed(void)
|
||||
|
||||
/*
|
||||
* Win32 console mouse scroll event handler.
|
||||
* Loosely based on the _OnMouseWheel() function in gui_w32.c
|
||||
* Console version of the _OnMouseWheel() function in gui_w32.c
|
||||
*
|
||||
* This encodes the mouse scroll direction and keyboard modifiers into
|
||||
* g_nMouseClick, and the mouse position into g_xMouse and g_yMouse
|
||||
*
|
||||
* The direction of the scroll is decoded from two fields of the win32 console
|
||||
* mouse event record;
|
||||
* 1. The axis - vertical or horizontal flag - from dwEventFlags, and
|
||||
* 1. The orientation - vertical or horizontal flag - from dwEventFlags
|
||||
* 2. The sign - positive or negative (aka delta flag) - from dwButtonState
|
||||
*
|
||||
* When scroll axis is HORIZONTAL
|
||||
* When scroll orientation is HORIZONTAL
|
||||
* - If the high word of the dwButtonState member contains a positive
|
||||
* value, the wheel was rotated to the right.
|
||||
* - Otherwise, the wheel was rotated to the left.
|
||||
* When scroll axis is VERTICAL
|
||||
* When scroll orientation is VERTICAL
|
||||
* - If the high word of the dwButtonState member contains a positive value,
|
||||
* the wheel was rotated forward, away from the user.
|
||||
* - Otherwise, the wheel was rotated backward, toward the user.
|
||||
@@ -1594,8 +1727,231 @@ decode_mouse_event(
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#endif // FEAT_GUI_MSWIN
|
||||
# ifdef FEAT_EVAL
|
||||
static int
|
||||
encode_mouse_event(dict_T *args, INPUT_RECORD *ir)
|
||||
{
|
||||
int button;
|
||||
int row;
|
||||
int col;
|
||||
int repeated_click;
|
||||
int_u mods;
|
||||
int move;
|
||||
|
||||
if (!dict_has_key(args, "row") || !dict_has_key(args, "col"))
|
||||
return FALSE;
|
||||
|
||||
// Note: "move" is optional, requires fewer arguments
|
||||
move = (int)dict_get_bool(args, "move", FALSE);
|
||||
if (!move && (!dict_has_key(args, "button")
|
||||
|| !dict_has_key(args, "multiclick")
|
||||
|| !dict_has_key(args, "modifiers")))
|
||||
return FALSE;
|
||||
|
||||
row = (int)dict_get_number(args, "row") - 1;
|
||||
col = (int)dict_get_number(args, "col") - 1;
|
||||
|
||||
ir->EventType = MOUSE_EVENT;
|
||||
MOUSE_EVENT_RECORD mer;
|
||||
ZeroMemory(&mer, sizeof(mer));
|
||||
mer.dwMousePosition.X = col;
|
||||
mer.dwMousePosition.Y = row;
|
||||
|
||||
if (move)
|
||||
{
|
||||
mer.dwButtonState = 0;
|
||||
mer.dwEventFlags = MOUSE_MOVED;
|
||||
}
|
||||
else
|
||||
{
|
||||
button = (int)dict_get_number(args, "button");
|
||||
repeated_click = (int)dict_get_number(args, "multiclick");
|
||||
mods = (int)dict_get_number(args, "modifiers");
|
||||
// Reset the scroll values to known values.
|
||||
// XXX: Remove this when/if the scroll step is made configurable.
|
||||
mouse_set_hor_scroll_step(6);
|
||||
mouse_set_vert_scroll_step(3);
|
||||
|
||||
switch (button)
|
||||
{
|
||||
case MOUSE_LEFT:
|
||||
mer.dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED;
|
||||
mer.dwEventFlags = repeated_click == 1 ? DOUBLE_CLICK : 0;
|
||||
break;
|
||||
case MOUSE_MIDDLE:
|
||||
mer.dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED;
|
||||
mer.dwEventFlags = repeated_click == 1 ? DOUBLE_CLICK : 0;
|
||||
break;
|
||||
case MOUSE_RIGHT:
|
||||
mer.dwButtonState = RIGHTMOST_BUTTON_PRESSED;
|
||||
mer.dwEventFlags = repeated_click == 1 ? DOUBLE_CLICK : 0;
|
||||
break;
|
||||
case MOUSE_RELEASE:
|
||||
// umm? Assume Left Release?
|
||||
mer.dwEventFlags = 0;
|
||||
|
||||
case MOUSE_MOVE:
|
||||
mer.dwButtonState = 0;
|
||||
mer.dwEventFlags = MOUSE_MOVED;
|
||||
break;
|
||||
case MOUSE_X1:
|
||||
mer.dwButtonState = FROM_LEFT_3RD_BUTTON_PRESSED;
|
||||
break;
|
||||
case MOUSE_X2:
|
||||
mer.dwButtonState = FROM_LEFT_4TH_BUTTON_PRESSED;
|
||||
break;
|
||||
case MOUSE_4: // KE_MOUSEDOWN;
|
||||
mer.dwButtonState = -1;
|
||||
mer.dwEventFlags = MOUSE_WHEELED;
|
||||
break;
|
||||
case MOUSE_5: // KE_MOUSEUP;
|
||||
mer.dwButtonState = +1;
|
||||
mer.dwEventFlags = MOUSE_WHEELED;
|
||||
break;
|
||||
case MOUSE_6: // KE_MOUSELEFT;
|
||||
mer.dwButtonState = -1;
|
||||
mer.dwEventFlags = MOUSE_HWHEELED;
|
||||
break;
|
||||
case MOUSE_7: // KE_MOUSERIGHT;
|
||||
mer.dwButtonState = +1;
|
||||
mer.dwEventFlags = MOUSE_HWHEELED;
|
||||
break;
|
||||
default:
|
||||
semsg(_(e_invalid_argument_str), "button");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
mer.dwControlKeyState = 0;
|
||||
if (mods != 0)
|
||||
{
|
||||
// Encode the win32 console key modifiers from Vim MOUSE modifiers.
|
||||
if (mods & MOUSE_SHIFT)
|
||||
mer.dwControlKeyState |= SHIFT_PRESSED;
|
||||
if (mods & MOUSE_CTRL)
|
||||
mer.dwControlKeyState |= LEFT_CTRL_PRESSED;
|
||||
if (mods & MOUSE_ALT)
|
||||
mer.dwControlKeyState |= LEFT_ALT_PRESSED;
|
||||
}
|
||||
ir->Event.MouseEvent = mer;
|
||||
return TRUE;
|
||||
}
|
||||
# endif // FEAT_EVAL
|
||||
|
||||
static int
|
||||
write_input_record_buffer(INPUT_RECORD* irEvents, int nLength)
|
||||
{
|
||||
int nCount = 0;
|
||||
while (nCount < nLength)
|
||||
{
|
||||
input_record_buffer.length++;
|
||||
input_record_buffer_node_T *event_node =
|
||||
malloc(sizeof(input_record_buffer_node_T));
|
||||
event_node->ir = irEvents[nCount++];
|
||||
event_node->next = NULL;
|
||||
if (input_record_buffer.tail == NULL)
|
||||
{
|
||||
input_record_buffer.head = event_node;
|
||||
input_record_buffer.tail = event_node;
|
||||
}
|
||||
else
|
||||
{
|
||||
input_record_buffer.tail->next = event_node;
|
||||
input_record_buffer.tail = input_record_buffer.tail->next;
|
||||
}
|
||||
}
|
||||
return nCount;
|
||||
}
|
||||
|
||||
static int
|
||||
read_input_record_buffer(INPUT_RECORD* irEvents, int nMaxLength)
|
||||
{
|
||||
int nCount = 0;
|
||||
while (nCount < nMaxLength && input_record_buffer.head != NULL)
|
||||
{
|
||||
input_record_buffer.length--;
|
||||
input_record_buffer_node_T *pop_head = input_record_buffer.head;
|
||||
irEvents[nCount++] = pop_head->ir;
|
||||
input_record_buffer.head = pop_head->next;
|
||||
vim_free(pop_head);
|
||||
if (input_record_buffer.length == 0)
|
||||
input_record_buffer.tail = NULL;
|
||||
}
|
||||
return nCount;
|
||||
}
|
||||
static int
|
||||
peek_input_record_buffer(INPUT_RECORD* irEvents, int nMaxLength)
|
||||
{
|
||||
int nCount = 0;
|
||||
input_record_buffer_node_T *temp = input_record_buffer.head;
|
||||
while (nCount < nMaxLength && temp != NULL)
|
||||
{
|
||||
irEvents[nCount++] = temp->ir;
|
||||
temp = temp->next;
|
||||
}
|
||||
return nCount;
|
||||
}
|
||||
#endif // !FEAT_GUI_MSWIN || VIMDLL
|
||||
|
||||
#ifdef FEAT_EVAL
|
||||
/*
|
||||
* The 'test_mswin_event' function is for testing Vim's low-level handling of
|
||||
* user input events. ie, this manages the encoding of INPUT_RECORD events
|
||||
* so that we have a way to test how Vim decodes INPUT_RECORD events in Windows
|
||||
* consoles.
|
||||
*
|
||||
* The 'test_mswin_event' function is based on 'test_gui_event'. In fact, when
|
||||
* the Windows GUI is running, the arguments; 'event' and 'args', are the same.
|
||||
* So, it acts as an alias for 'test_gui_event' for the Windows GUI.
|
||||
*
|
||||
* When the Windows console is running, the arguments; 'event' and 'args', are
|
||||
* a subset of what 'test_gui_event' handles, ie, only "key" and "mouse"
|
||||
* events are encoded as INPUT_RECORD events.
|
||||
*
|
||||
* Note: INPUT_RECORDs are only used by the Windows console, not the GUI. The
|
||||
* GUI sends MSG structs instead.
|
||||
*/
|
||||
int
|
||||
test_mswin_event(char_u *event, dict_T *args)
|
||||
{
|
||||
int lpEventsWritten = 0;
|
||||
|
||||
# if defined(VIMDLL) || defined(FEAT_GUI_MSWIN)
|
||||
if (gui.in_use)
|
||||
return test_gui_w32_sendevent(event, args);
|
||||
# endif
|
||||
|
||||
# if defined(VIMDLL) || !defined(FEAT_GUI_MSWIN)
|
||||
|
||||
// Currently implemented event record types are; KEY_EVENT and MOUSE_EVENT
|
||||
// Potentially could also implement: FOCUS_EVENT and WINDOW_BUFFER_SIZE_EVENT
|
||||
// Maybe also: MENU_EVENT
|
||||
|
||||
INPUT_RECORD ir;
|
||||
BOOL input_encoded = FALSE;
|
||||
if (STRCMP(event, "key") == 0)
|
||||
input_encoded = encode_key_event(args, &ir);
|
||||
else if (STRCMP(event, "mouse") == 0)
|
||||
input_encoded = encode_mouse_event(args, &ir);
|
||||
else
|
||||
{
|
||||
semsg(_(e_invalid_value_for_argument_str_str), "event", event);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Ideally, WriteConsoleInput would be used to inject these low-level
|
||||
// events. But, this doesnt work well in the CI test environment. So
|
||||
// implementing an input_record_buffer instead.
|
||||
if (input_encoded)
|
||||
lpEventsWritten = write_input_record_buffer(&ir, 1);
|
||||
|
||||
if (STRCMP(event, "mouse") == 0)
|
||||
exec_normal(TRUE, TRUE, TRUE);
|
||||
|
||||
# endif
|
||||
return lpEventsWritten;
|
||||
}
|
||||
#endif // FEAT_EVAL
|
||||
|
||||
#ifdef MCH_CURSOR_SHAPE
|
||||
/*
|
||||
|
Reference in New Issue
Block a user