To: vim_dev@googlegroups.com Subject: Patch 9.0.1084 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.1084 Problem: Code handling low level MS-Windows events cannot be tested. Solution: Add test_mswin_event() and tests using it. (Christopher Plewright, closes #11622) Files: runtime/doc/builtin.txt, runtime/doc/testing.txt, runtime/doc/usr_41.txt, src/evalfunc.c, src/gui_w32.c, src/os_win32.c, src/proto/gui_w32.pro, src/proto/os_win32.pro, src/proto/testing.pro, src/term.c, src/testing.c, src/testdir/Make_all.mak, src/testdir/mouse.vim, src/testdir/test_gui.vim, src/testdir/test_mswin_event.vim, src/testdir/test_termcodes.vim *** ../vim-9.0.1083/runtime/doc/builtin.txt 2022-12-05 13:50:49.718052362 +0000 --- runtime/doc/builtin.txt 2022-12-20 19:22:31.969188032 +0000 *************** *** 666,671 **** --- 666,673 ---- test_getvalue({string}) any get value of an internal variable test_gui_event({event}, {args}) bool generate a GUI event for testing test_ignore_error({expr}) none ignore a specific error + test_mswin_event({event}, {args}) + bool generate MS-Windows event for testing test_null_blob() Blob null value for testing test_null_channel() Channel null value for testing test_null_dict() Dict null value for testing *** ../vim-9.0.1083/runtime/doc/testing.txt 2022-07-23 05:04:07.580839529 +0100 --- runtime/doc/testing.txt 2022-12-20 19:27:27.353314009 +0000 *************** *** 94,100 **** "findrepl" search and replace text. "mouse" mouse button click event. "scrollbar" move or drag the scrollbar. ! "sendevent" send a low-level GUI event. "tabline" select a tab page by mouse click. "tabmenu" select a tabline menu entry. --- 94,100 ---- "findrepl" search and replace text. "mouse" mouse button click event. "scrollbar" move or drag the scrollbar. ! "key" send a low-level keyboard event. "tabline" select a tab page by mouse click. "tabmenu" select a tabline menu entry. *************** *** 135,143 **** Inject either a mouse button click, or a mouse move, event. The supported items in {args} are: button: mouse button. The supported values are: ! 0 right mouse button 1 middle mouse button ! 2 left mouse button 3 mouse button release 4 scroll wheel down 5 scroll wheel up --- 135,143 ---- Inject either a mouse button click, or a mouse move, event. The supported items in {args} are: button: mouse button. The supported values are: ! 0 left mouse button 1 middle mouse button ! 2 right mouse button 3 mouse button release 4 scroll wheel down 5 scroll wheel up *************** *** 178,191 **** dragging: 1 to drag the scrollbar and 0 to click in the scrollbar. ! "sendevent": ! Send a low-level GUI event (e.g. key-up or down). Currently only supported on MS-Windows. The supported items in {args} are: event: The supported string values are: keyup generate a keyup event keydown generate a keydown event keycode: Keycode to use for a keyup or a keydown event. "tabline": Inject a mouse click event on the tabline to select a --- 178,192 ---- dragging: 1 to drag the scrollbar and 0 to click in the scrollbar. ! "key": ! Send a low-level keyboard event (e.g. key-up or down). Currently only supported on MS-Windows. The supported items in {args} are: event: The supported string values are: keyup generate a keyup event keydown generate a keydown event keycode: Keycode to use for a keyup or a keydown event. + *E1291* "tabline": Inject a mouse click event on the tabline to select a *************** *** 222,227 **** --- 223,294 ---- Can also be used as a |method|: > GetErrorText()->test_ignore_error() + + test_mswin_event({event}, {args}) *test_mswin_event()* + Generate a low-level MS-Windows {event} with arguments {args} + for testing Vim functionality. It works for MS-Windows GUI + and for the console. + + {event} is a String and the supported values are: + "mouse" mouse event. + "key" keyboard event. + + "mouse": + Inject either a mouse button click, or a mouse move, event. + The supported items in {args} are: + button: mouse button. The supported values are: + 0 right mouse button + 1 middle mouse button + 2 left mouse button + 3 mouse button release + 4 scroll wheel down + 5 scroll wheel up + 6 scroll wheel left + 7 scroll wheel right + row: mouse click row number. The first row of the + Vim window is 1 and the last row is 'lines'. + col: mouse click column number. The maximum value + of {col} is 'columns'. + Note: row and col are always interpreted as + screen cells for the console application. + But, they may be interpreted as pixels + for the GUI, depending on "cell". + multiclick: set to 1 to inject a double-click mouse event. + modifiers: key modifiers. The supported values are: + 4 shift is pressed + 8 alt is pressed + 16 ctrl is pressed + move: Optional; if used and TRUE then a mouse move + event can be generated. + Only {args} row: and col: are used and + required. + Only results in an event when 'mousemoveevent' + is set or a popup uses mouse move events. + cell: Optional for the GUI: when present and TRUE + then "move" uses screen cells instead of pixel + positions. Not used by the console. + + "key": + Send a low-level keyboard event (e.g. keyup or keydown). + The supported items in {args} are: + event: The supported string values are: + keyup generate a keyup event + keydown generate a keydown event + keycode: Keycode to use for a keyup or a keydown event. + modifiers: Optional; key modifiers. + The supported values are: + 2 shift is pressed + 4 ctrl is pressed + 8 alt is pressed + Note: These values are different from the + mouse modifiers. + *E1291* + Returns TRUE if the event is successfully added, FALSE if + there is a failure. + + Can also be used as a |method|: > + GetEvent()->test_mswin_event({args}) + < test_null_blob() *test_null_blob()* Return a |Blob| that is null. Only useful for testing. *** ../vim-9.0.1083/runtime/doc/usr_41.txt 2022-12-05 13:50:49.718052362 +0000 --- runtime/doc/usr_41.txt 2022-12-20 19:27:44.801319311 +0000 *************** *** 1185,1190 **** --- 1186,1192 ---- test_getvalue() get value of an internal variable test_gui_event() generate a GUI event for testing test_ignore_error() ignore a specific error message + test_mswin_event() generate an MS-Windows event test_null_blob() return a null Blob test_null_channel() return a null Channel test_null_dict() return a null Dict *** ../vim-9.0.1083/src/evalfunc.c 2022-12-08 15:32:11.079034172 +0000 --- src/evalfunc.c 2022-12-20 19:28:32.093332756 +0000 *************** *** 2694,2699 **** --- 2694,2701 ---- ret_bool, f_test_gui_event}, {"test_ignore_error", 1, 1, FEARG_1, arg1_string, ret_void, f_test_ignore_error}, + {"test_mswin_event", 2, 2, FEARG_1, arg2_string_dict, + ret_number, f_test_mswin_event}, {"test_null_blob", 0, 0, 0, NULL, ret_blob, f_test_null_blob}, {"test_null_channel", 0, 0, 0, NULL, *************** *** 4387,4393 **** if (*keys != NUL || execute) { ! if (lowlevel) { #ifdef USE_INPUT_BUF ch_log(NULL, "feedkeys() lowlevel: %s", keys); --- 4389,4400 ---- if (*keys != NUL || execute) { ! if (lowlevel ! #ifdef FEAT_VTP ! && (!is_term_win32() ! || (keys[0] == 3 && ctrl_c_interrupts && typed)) ! #endif ! ) { #ifdef USE_INPUT_BUF ch_log(NULL, "feedkeys() lowlevel: %s", keys); *** ../vim-9.0.1083/src/gui_w32.c 2022-11-16 12:02:24.861138121 +0000 --- src/gui_w32.c 2022-12-20 19:45:48.112743098 +0000 *************** *** 8643,8683 **** #endif #if defined(FEAT_EVAL) || defined(PROTO) ! int ! test_gui_w32_sendevent(dict_T *args) { ! char_u *event; ! INPUT inputs[1]; ! event = dict_get_string(args, "event", TRUE); ! if (event == NULL) return FALSE; ! ZeroMemory(inputs, sizeof(inputs)); ! if (STRICMP(event, "keydown") == 0 || STRICMP(event, "keyup") == 0) { ! WORD vkCode; ! vkCode = dict_get_number_def(args, "keycode", 0); if (vkCode <= 0 || vkCode >= 0xFF) { semsg(_(e_invalid_argument_nr), (long)vkCode); return FALSE; } inputs[0].type = INPUT_KEYBOARD; inputs[0].ki.wVk = vkCode; if (STRICMP(event, "keyup") == 0) inputs[0].ki.dwFlags = KEYEVENTF_KEYUP; (void)SetForegroundWindow(s_hwnd); SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT)); ! } ! else ! semsg(_(e_invalid_argument_str), event); ! vim_free(event); return TRUE; } #endif --- 8643,8818 ---- #endif #if defined(FEAT_EVAL) || defined(PROTO) ! ! // TODO: at the moment, this is just a copy of test_gui_mouse_event. ! // But, we could instead generate actual Win32 mouse event messages, ! // ie. to make it consistent wih test_gui_w32_sendevent_keyboard. ! static int ! test_gui_w32_sendevent_mouse(dict_T *args) { ! if (!dict_has_key(args, "row") || !dict_has_key(args, "col")) ! return FALSE; ! // Note: "move" is optional, requires fewer arguments ! int 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; ! int row = (int)dict_get_number(args, "row"); ! int col = (int)dict_get_number(args, "col"); ! if (move) { ! // the "move" argument expects row and col coordnates to be in pixels, ! // unless "cell" is specified and is TRUE. ! if (dict_get_bool(args, "cell", FALSE)) ! { ! // calculate the middle of the character cell ! // Note: Cell coordinates are 1-based from vimscript ! int pY = (row - 1) * gui.char_height + gui.char_height / 2; ! int pX = (col - 1) * gui.char_width + gui.char_width / 2; ! gui_mouse_moved(pX, pY); ! } ! else ! gui_mouse_moved(col, row); ! } ! else ! { ! int button = (int)dict_get_number(args, "button"); ! int repeated_click = (int)dict_get_number(args, "multiclick"); ! int_u 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); ! ! gui_send_mouse_event(button, TEXT_X(col - 1), TEXT_Y(row - 1), ! repeated_click, mods); ! } ! return TRUE; ! } ! ! static int ! test_gui_w32_sendevent_keyboard(dict_T *args) ! { ! INPUT inputs[1]; ! INPUT modkeys[3]; ! SecureZeroMemory(inputs, sizeof(INPUT)); ! SecureZeroMemory(modkeys, 3 * sizeof(INPUT)); ! ! 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; } + BOOL isModKey = (vkCode == VK_SHIFT || vkCode == VK_CONTROL + || vkCode == VK_MENU || vkCode == VK_LSHIFT || vkCode == VK_RSHIFT + || vkCode == VK_LCONTROL || vkCode == VK_RCONTROL + || vkCode == VK_LMENU || vkCode == VK_RMENU ); + + BOOL unwrapMods = FALSE; + int mods = (int)dict_get_number(args, "modifiers"); + + // If there are modifiers in the args, and it is not a keyup event and + // vkCode is not a modifier key, then we generate virtual modifier key + // messages before sending the actual key message. + if(mods && STRICMP(event, "keydown") == 0 && !isModKey) + { + int n = 0; + if (mods & MOD_MASK_SHIFT) + { + modkeys[n].type = INPUT_KEYBOARD; + modkeys[n].ki.wVk = VK_LSHIFT; + n++; + } + if (mods & MOD_MASK_CTRL) + { + modkeys[n].type = INPUT_KEYBOARD; + modkeys[n].ki.wVk = VK_LCONTROL; + n++; + } + if (mods & MOD_MASK_ALT) + { + modkeys[n].type = INPUT_KEYBOARD; + modkeys[n].ki.wVk = VK_LMENU; + n++; + } + if (n) + { + (void)SetForegroundWindow(s_hwnd); + SendInput(n, modkeys, sizeof(INPUT)); + } + } + inputs[0].type = INPUT_KEYBOARD; inputs[0].ki.wVk = vkCode; if (STRICMP(event, "keyup") == 0) + { inputs[0].ki.dwFlags = KEYEVENTF_KEYUP; + if(!isModKey) + unwrapMods = TRUE; + } + (void)SetForegroundWindow(s_hwnd); SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT)); ! vim_free(event); ! if (unwrapMods) ! { ! modkeys[0].type = INPUT_KEYBOARD; ! modkeys[0].ki.wVk = VK_LSHIFT; ! modkeys[0].ki.dwFlags = KEYEVENTF_KEYUP; ! ! modkeys[1].type = INPUT_KEYBOARD; ! modkeys[1].ki.wVk = VK_LCONTROL; ! modkeys[1].ki.dwFlags = KEYEVENTF_KEYUP; ! ! modkeys[2].type = INPUT_KEYBOARD; ! modkeys[2].ki.wVk = VK_LMENU; ! modkeys[2].ki.dwFlags = KEYEVENTF_KEYUP; + (void)SetForegroundWindow(s_hwnd); + SendInput(3, modkeys, sizeof(INPUT)); + } + } + 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; } + + int + test_gui_w32_sendevent(char_u *event, dict_T *args) + { + if (STRICMP(event, "key") == 0) + return test_gui_w32_sendevent_keyboard(args); + else if (STRICMP(event, "mouse") == 0) + return test_gui_w32_sendevent_mouse(args); + else + { + semsg(_(e_invalid_value_for_argument_str_str), "event", event); + return FALSE; + } + } #endif *** ../vim-9.0.1083/src/os_win32.c 2022-11-30 18:11:52.690904297 +0000 --- src/os_win32.c 2022-12-20 19:43:38.056916734 +0000 *************** *** 177,182 **** --- 177,201 ---- 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,230 **** static void set_console_color_rgb(void); static void reset_console_color_rgb(void); static void restore_console_color_rgb(void); ! #endif // This flag is newly created from Windows 10 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING --- 243,249 ---- static void set_console_color_rgb(void); static void reset_console_color_rgb(void); static void restore_console_color_rgb(void); ! #endif // !FEAT_GUI_MSWIN || VIMDLL // This flag is newly created from Windows 10 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING *************** *** 319,324 **** --- 338,350 ---- 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,437 **** return WaitForSingleObject(hHandle, dwMilliseconds); } # endif ! #endif static void get_exe_name(void) --- 457,463 ---- return WaitForSingleObject(hHandle, dwMilliseconds); } # endif ! #endif // !FEAT_GUI_MSWIN || VIMDLL static void get_exe_name(void) *************** *** 1014,1020 **** return 1; } ! if (pker->uChar.UnicodeChar != 0) return 1; CLEAR_FIELD(abKeystate); --- 1040,1046 ---- return 1; } ! if (pker->uChar.UnicodeChar > 0 && pker->uChar.UnicodeChar < 0xfffd) return 1; CLEAR_FIELD(abKeystate); *************** *** 1080,1086 **** // special cases if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0 ! && pker->uChar.UnicodeChar == NUL) { // Ctrl-6 is Ctrl-^ if (pker->wVirtualKeyCode == '6') --- 1106,1113 ---- // special cases if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0 ! && (pker->uChar.UnicodeChar == NUL ! || pker->uChar.UnicodeChar == 0xfffd)) { // Ctrl-6 is Ctrl-^ if (pker->wVirtualKeyCode == '6') *************** *** 1168,1174 **** return (*pch != NUL); } ! #endif // FEAT_GUI_MSWIN /* --- 1195,1307 ---- return (*pch != NUL); } ! # 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,1185 **** mch_setmouse(int on UNUSED) { } ! #else static int g_fMouseAvail = FALSE; // mouse present static int g_fMouseActive = FALSE; // mouse enabled static int g_nMouseClick = -1; // mouse status --- 1312,1318 ---- mch_setmouse(int on UNUSED) { } ! #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,1254 **** /* * Win32 console mouse scroll event handler. ! * Loosely based on 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 * 2. The sign - positive or negative (aka delta flag) - from dwButtonState * ! * When scroll axis 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 * - 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. --- 1367,1387 ---- /* * Win32 console mouse scroll event handler. ! * 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 orientation - vertical or horizontal flag - from dwEventFlags * 2. The sign - positive or negative (aka delta flag) - from dwButtonState * ! * 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 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,1601 **** return TRUE; } ! #endif // FEAT_GUI_MSWIN #ifdef MCH_CURSOR_SHAPE /* --- 1727,1957 ---- return TRUE; } ! # 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 /* *** ../vim-9.0.1083/src/proto/gui_w32.pro 2022-07-23 05:04:07.580839529 +0100 --- src/proto/gui_w32.pro 2022-12-20 19:45:50.336740416 +0000 *************** *** 96,100 **** BalloonEval *gui_mch_create_beval_area(void *target, char_u *mesg, void (*mesgCB)(BalloonEval *, int), void *clientData); void gui_mch_destroy_beval_area(BalloonEval *beval); void netbeans_draw_multisign_indicator(int row); ! int test_gui_w32_sendevent(dict_T *args); /* vim: set ft=c : */ --- 96,100 ---- BalloonEval *gui_mch_create_beval_area(void *target, char_u *mesg, void (*mesgCB)(BalloonEval *, int), void *clientData); void gui_mch_destroy_beval_area(BalloonEval *beval); void netbeans_draw_multisign_indicator(int row); ! int test_gui_w32_sendevent(char_u *event, dict_T *args); /* vim: set ft=c : */ *** ../vim-9.0.1083/src/proto/os_win32.pro 2022-11-12 18:45:59.818782978 +0000 --- src/proto/os_win32.pro 2022-12-20 19:46:12.692713979 +0000 *************** *** 9,14 **** --- 9,15 ---- void PlatformId(void); void mch_setmouse(int on); void mch_bevalterm_changed(void); + int test_mswin_event(char_u *event, dict_T *args); void mch_update_cursor(void); int mch_char_avail(void); int mch_check_messages(void); *** ../vim-9.0.1083/src/proto/testing.pro 2022-06-27 23:15:25.000000000 +0100 --- src/proto/testing.pro 2022-12-20 19:47:23.100636236 +0000 *************** *** 33,38 **** --- 33,39 ---- void f_test_unknown(typval_T *argvars, typval_T *rettv); void f_test_void(typval_T *argvars, typval_T *rettv); void f_test_setmouse(typval_T *argvars, typval_T *rettv); + void f_test_mswin_event(typval_T *argvars, typval_T *rettv); void f_test_gui_event(typval_T *argvars, typval_T *rettv); void f_test_settime(typval_T *argvars, typval_T *rettv); /* vim: set ft=c : */ *** ../vim-9.0.1083/src/term.c 2022-12-19 18:56:44.169594372 +0000 --- src/term.c 2022-12-20 19:22:31.977188037 +0000 *************** *** 827,833 **** }; /* ! * These codes are valid for the Win32 Console . The entries that start with * ESC | are translated into console calls in os_win32.c. The function keys * are also translated in os_win32.c. */ --- 827,833 ---- }; /* ! * These codes are valid for the Win32 Console. The entries that start with * ESC | are translated into console calls in os_win32.c. The function keys * are also translated in os_win32.c. */ *** ../vim-9.0.1083/src/testing.c 2022-12-08 15:32:11.083034191 +0000 --- src/testing.c 2022-12-20 19:22:31.977188037 +0000 *************** *** 1388,1400 **** if (move) { if (dict_get_bool(args, "cell", FALSE)) { ! // click in the middle of the character cell ! row = row * gui.char_height + gui.char_height / 2; ! col = col * gui.char_width + gui.char_width / 2; } ! gui_mouse_moved(col, row); } else { --- 1388,1405 ---- if (move) { + int pY = row; + int pX = col; + // the "move" argument expects row and col coordnates to be in pixels, + // unless "cell" is specified and is TRUE. if (dict_get_bool(args, "cell", FALSE)) { ! // calculate the middle of the character cell ! // Note: Cell coordinates are 1-based from vimscript ! pY = (row - 1) * gui.char_height + gui.char_height / 2; ! pX = (col - 1) * gui.char_width + gui.char_width / 2; } ! gui_mouse_moved(pX, pY); } else { *************** *** 1489,1494 **** --- 1494,1523 ---- # endif void + f_test_mswin_event(typval_T *argvars UNUSED, typval_T *rettv UNUSED) + { + # ifdef MSWIN + rettv->v_type = VAR_BOOL; + rettv->vval.v_number = FALSE; + + if (sandbox != 0) + { + emsg(_(e_not_allowed_in_sandbox)); + return; + } + + if (check_for_string_arg(argvars, 0) == FAIL + || check_for_dict_arg(argvars, 1) == FAIL + || argvars[1].vval.v_dict == NULL) + return; + + char_u *event = tv_get_string(&argvars[0]); + rettv->vval.v_number = test_mswin_event(event, argvars[1].vval.v_dict); + + # endif + } + + void f_test_gui_event(typval_T *argvars UNUSED, typval_T *rettv UNUSED) { # ifdef FEAT_GUI *************** *** 1515,1520 **** --- 1544,1553 ---- else if (STRCMP(event, "findrepl") == 0) rettv->vval.v_number = test_gui_find_repl(argvars[1].vval.v_dict); # endif + # ifdef MSWIN + else if (STRCMP(event, "key") == 0 || STRCMP(event, "mouse") == 0) + rettv->vval.v_number = test_mswin_event(event, argvars[1].vval.v_dict); + # endif else if (STRCMP(event, "mouse") == 0) rettv->vval.v_number = test_gui_mouse_event(argvars[1].vval.v_dict); else if (STRCMP(event, "scrollbar") == 0) *************** *** 1523,1532 **** rettv->vval.v_number = test_gui_tabline_event(argvars[1].vval.v_dict); else if (STRCMP(event, "tabmenu") == 0) rettv->vval.v_number = test_gui_tabmenu_event(argvars[1].vval.v_dict); - # ifdef FEAT_GUI_MSWIN - else if (STRCMP(event, "sendevent") == 0) - rettv->vval.v_number = test_gui_w32_sendevent(argvars[1].vval.v_dict); - # endif else { semsg(_(e_invalid_argument_str), event); --- 1556,1561 ---- *** ../vim-9.0.1083/src/testdir/Make_all.mak 2022-12-08 15:32:11.087034211 +0000 --- src/testdir/Make_all.mak 2022-12-20 19:22:31.977188037 +0000 *************** *** 210,215 **** --- 210,216 ---- test_modeless \ test_modeline \ test_move \ + test_mswin_event \ test_mzscheme \ test_nested_function \ test_netbeans \ *************** *** 454,459 **** --- 455,461 ---- test_mksession.res \ test_modeless.res \ test_modeline.res \ + test_mswin_event.res \ test_mzscheme.res \ test_nested_function.res \ test_netbeans.res \ *** ../vim-9.0.1083/src/testdir/mouse.vim 2020-07-18 11:57:17.000000000 +0100 --- src/testdir/mouse.vim 2022-12-20 19:49:05.844535922 +0000 *************** *** 20,25 **** --- 20,46 ---- let g:Ttymouse_netterm = [] endif + " Vim Mouse Codes. + " Used by the GUI and by MS-Windows Consoles. + " Keep these in sync with vim.h + let s:MOUSE_CODE = { + \ 'BTN_LEFT' : 0x00, + \ 'BTN_MIDDLE' : 0x01, + \ 'BTN_RIGHT' : 0x02, + \ 'BTN_RELEASE' : 0x03, + \ 'BTN_X1' : 0x300, + \ 'BTN_X2' : 0x400, + \ 'SCRL_DOWN' : 0x100, + \ 'SCRL_UP' : 0x200, + \ 'SCRL_LEFT' : 0x500, + \ 'SCRL_RIGHT' : 0x600, + \ 'MOVE' : 0x700, + \ 'MOD_SHIFT' : 0x04, + \ 'MOD_ALT' : 0x08, + \ 'MOD_CTRL' : 0x10, + \ } + + " Helper function to emit a terminal escape code. func TerminalEscapeCode(code, row, col, m) if &ttymouse ==# 'xterm2' *************** *** 47,52 **** --- 68,98 ---- return printf("\}%d,%d\r", a:row, a:col) endfunc + " Send low level mouse event to MS-Windows consoles or GUI + func MSWinMouseEvent(button, row, col, move, multiclick, modifiers) + let args = { } + let args.button = a:button + " Scroll directions are inverted in the GUI, no idea why. + if has('gui_running') + if a:button == s:MOUSE_CODE.SCRL_UP + let args.button = s:MOUSE_CODE.SCRL_DOWN + elseif a:button == s:MOUSE_CODE.SCRL_DOWN + let args.button = s:MOUSE_CODE.SCRL_UP + elseif a:button == s:MOUSE_CODE.SCRL_LEFT + let args.button = s:MOUSE_CODE.SCRL_RIGHT + elseif a:button == s:MOUSE_CODE.SCRL_RIGHT + let args.button = s:MOUSE_CODE.SCRL_LEFT + endif + endif + let args.row = a:row + let args.col = a:col + let args.move = a:move + let args.multiclick = a:multiclick + let args.modifiers = a:modifiers + call test_mswin_event("mouse", args) + unlet args + endfunc + func MouseLeftClickCode(row, col) if &ttymouse ==# 'dec' return DecEscapeCode(2, 4, a:row, a:col) *************** *** 58,64 **** endfunc func MouseLeftClick(row, col) ! call feedkeys(MouseLeftClickCode(a:row, a:col), 'Lx!') endfunc func MouseMiddleClickCode(row, col) --- 104,114 ---- endfunc func MouseLeftClick(row, col) ! if has('win32') ! call MSWinMouseEvent(s:MOUSE_CODE.BTN_LEFT, a:row, a:col, 0, 0, 0) ! else ! call feedkeys(MouseLeftClickCode(a:row, a:col), 'Lx!') ! endif endfunc func MouseMiddleClickCode(row, col) *************** *** 70,76 **** endfunc func MouseMiddleClick(row, col) ! call feedkeys(MouseMiddleClickCode(a:row, a:col), 'Lx!') endfunc func MouseRightClickCode(row, col) --- 120,130 ---- endfunc func MouseMiddleClick(row, col) ! if has('win32') ! call MSWinMouseEvent(s:MOUSE_CODE.BTN_MIDDLE, a:row, a:col, 0, 0, 0) ! else ! call feedkeys(MouseMiddleClickCode(a:row, a:col), 'Lx!') ! endif endfunc func MouseRightClickCode(row, col) *************** *** 82,88 **** endfunc func MouseRightClick(row, col) ! call feedkeys(MouseRightClickCode(a:row, a:col), 'Lx!') endfunc func MouseCtrlLeftClickCode(row, col) --- 136,146 ---- endfunc func MouseRightClick(row, col) ! if has('win32') ! call MSWinMouseEvent(s:MOUSE_CODE.BTN_RIGHT, a:row, a:col, 0, 0, 0) ! else ! call feedkeys(MouseRightClickCode(a:row, a:col), 'Lx!') ! endif endfunc func MouseCtrlLeftClickCode(row, col) *************** *** 91,97 **** endfunc func MouseCtrlLeftClick(row, col) ! call feedkeys(MouseCtrlLeftClickCode(a:row, a:col), 'Lx!') endfunc func MouseCtrlRightClickCode(row, col) --- 149,160 ---- endfunc func MouseCtrlLeftClick(row, col) ! if has('win32') ! call MSWinMouseEvent(s:MOUSE_CODE.BTN_LEFT, a:row, a:col, 0, 0, ! \ s:MOUSE_CODE.MOD_CTRL) ! else ! call feedkeys(MouseCtrlLeftClickCode(a:row, a:col), 'Lx!') ! endif endfunc func MouseCtrlRightClickCode(row, col) *************** *** 100,106 **** endfunc func MouseCtrlRightClick(row, col) ! call feedkeys(MouseCtrlRightClickCode(a:row, a:col), 'Lx!') endfunc func MouseAltLeftClickCode(row, col) --- 163,174 ---- endfunc func MouseCtrlRightClick(row, col) ! if has('win32') ! call MSWinMouseEvent(s:MOUSE_CODE.BTN_RIGHT, a:row, a:col, 0, 0, ! \ s:MOUSE_CODE.MOD_CTRL) ! else ! call feedkeys(MouseCtrlRightClickCode(a:row, a:col), 'Lx!') ! endif endfunc func MouseAltLeftClickCode(row, col) *************** *** 109,115 **** endfunc func MouseAltLeftClick(row, col) ! call feedkeys(MouseAltLeftClickCode(a:row, a:col), 'Lx!') endfunc func MouseAltRightClickCode(row, col) --- 177,188 ---- endfunc func MouseAltLeftClick(row, col) ! if has('win32') ! call MSWinMouseEvent(s:MOUSE_CODE.BTN_LEFT, a:row, a:col, 0, 0, ! \ s:MOUSE_CODE.MOD_ALT) ! else ! call feedkeys(MouseAltLeftClickCode(a:row, a:col), 'Lx!') ! endif endfunc func MouseAltRightClickCode(row, col) *************** *** 118,124 **** endfunc func MouseAltRightClick(row, col) ! call feedkeys(MouseAltRightClickCode(a:row, a:col), 'Lx!') endfunc func MouseLeftReleaseCode(row, col) --- 191,202 ---- endfunc func MouseAltRightClick(row, col) ! if has('win32') ! call MSWinMouseEvent(s:MOUSE_CODE.BTN_RIGHT, a:row, a:col, 0, 0, ! \ s:MOUSE_CODE.MOD_ALT) ! else ! call feedkeys(MouseAltRightClickCode(a:row, a:col), 'Lx!') ! endif endfunc func MouseLeftReleaseCode(row, col) *************** *** 132,138 **** endfunc func MouseLeftRelease(row, col) ! call feedkeys(MouseLeftReleaseCode(a:row, a:col), 'Lx!') endfunc func MouseMiddleReleaseCode(row, col) --- 210,220 ---- endfunc func MouseLeftRelease(row, col) ! if has('win32') ! call MSWinMouseEvent(s:MOUSE_CODE.BTN_RELEASE, a:row, a:col, 0, 0, 0) ! else ! call feedkeys(MouseLeftReleaseCode(a:row, a:col), 'Lx!') ! endif endfunc func MouseMiddleReleaseCode(row, col) *************** *** 144,150 **** endfunc func MouseMiddleRelease(row, col) ! call feedkeys(MouseMiddleReleaseCode(a:row, a:col), 'Lx!') endfunc func MouseRightReleaseCode(row, col) --- 226,236 ---- endfunc func MouseMiddleRelease(row, col) ! if has('win32') ! call MSWinMouseEvent(s:MOUSE_CODE.BTN_RELEASE, a:row, a:col, 0, 0, 0) ! else ! call feedkeys(MouseMiddleReleaseCode(a:row, a:col), 'Lx!') ! endif endfunc func MouseRightReleaseCode(row, col) *************** *** 156,162 **** endfunc func MouseRightRelease(row, col) ! call feedkeys(MouseRightReleaseCode(a:row, a:col), 'Lx!') endfunc func MouseLeftDragCode(row, col) --- 242,252 ---- endfunc func MouseRightRelease(row, col) ! if has('win32') ! call MSWinMouseEvent(s:MOUSE_CODE.BTN_RELEASE, a:row, a:col, 0, 0, 0) ! else ! call feedkeys(MouseRightReleaseCode(a:row, a:col), 'Lx!') ! endif endfunc func MouseLeftDragCode(row, col) *************** *** 168,174 **** endfunc func MouseLeftDrag(row, col) ! call feedkeys(MouseLeftDragCode(a:row, a:col), 'Lx!') endfunc func MouseWheelUpCode(row, col) --- 258,268 ---- endfunc func MouseLeftDrag(row, col) ! if has('win32') ! call MSWinMouseEvent(s:MOUSE_CODE.BTN_LEFT, a:row, a:col, 1, 0, 0) ! else ! call feedkeys(MouseLeftDragCode(a:row, a:col), 'Lx!') ! endif endfunc func MouseWheelUpCode(row, col) *************** *** 176,182 **** endfunc func MouseWheelUp(row, col) ! call feedkeys(MouseWheelUpCode(a:row, a:col), 'Lx!') endfunc func MouseWheelDownCode(row, col) --- 270,280 ---- endfunc func MouseWheelUp(row, col) ! if has('win32') ! call MSWinMouseEvent(s:MOUSE_CODE.SCRL_UP, a:row, a:col, 0, 0, 0) ! else ! call feedkeys(MouseWheelUpCode(a:row, a:col), 'Lx!') ! endif endfunc func MouseWheelDownCode(row, col) *************** *** 184,190 **** endfunc func MouseWheelDown(row, col) ! call feedkeys(MouseWheelDownCode(a:row, a:col), 'Lx!') endfunc func MouseWheelLeftCode(row, col) --- 282,292 ---- endfunc func MouseWheelDown(row, col) ! if has('win32') ! call MSWinMouseEvent(s:MOUSE_CODE.SCRL_DOWN, a:row, a:col, 0, 0, 0) ! else ! call feedkeys(MouseWheelDownCode(a:row, a:col), 'Lx!') ! endif endfunc func MouseWheelLeftCode(row, col) *************** *** 192,198 **** endfunc func MouseWheelLeft(row, col) ! call feedkeys(MouseWheelLeftCode(a:row, a:col), 'Lx!') endfunc func MouseWheelRightCode(row, col) --- 294,304 ---- endfunc func MouseWheelLeft(row, col) ! if has('win32') ! call MSWinMouseEvent(s:MOUSE_CODE.SCRL_LEFT, a:row, a:col, 0, 0, 0) ! else ! call feedkeys(MouseWheelLeftCode(a:row, a:col), 'Lx!') ! endif endfunc func MouseWheelRightCode(row, col) *************** *** 200,206 **** endfunc func MouseWheelRight(row, col) ! call feedkeys(MouseWheelRightCode(a:row, a:col), 'Lx!') endfunc " vim: shiftwidth=2 sts=2 expandtab --- 306,372 ---- endfunc func MouseWheelRight(row, col) ! if has('win32') ! call MSWinMouseEvent(s:MOUSE_CODE.SCRL_RIGHT, a:row, a:col, 0, 0, 0) ! else ! call feedkeys(MouseWheelRightCode(a:row, a:col), 'Lx!') ! endif ! endfunc ! ! func MouseShiftWheelUpCode(row, col) ! " todo feed shift mod. ! return TerminalEscapeCode(0x40, a:row, a:col, 'M') ! endfunc ! ! func MouseShiftWheelUp(row, col) ! if has('win32') ! call MSWinMouseEvent(s:MOUSE_CODE.SCRL_UP, a:row, a:col, 0, 0, ! \ s:MOUSE_CODE.MOD_SHIFT) ! else ! call feedkeys(MouseShiftWheelUpCode(a:row, a:col), 'Lx!') ! endif ! endfunc ! ! func MouseShiftWheelDownCode(row, col) ! " todo feed shift mod. ! return TerminalEscapeCode(0x41, a:row, a:col, 'M') ! endfunc ! ! func MouseShiftWheelDown(row, col) ! if has('win32') ! call MSWinMouseEvent(s:MOUSE_CODE.SCRL_DOWN, a:row, a:col, 0, 0, ! \ s:MOUSE_CODE.MOD_SHIFT) ! else ! call feedkeys(MouseShiftWheelDownCode(a:row, a:col), 'Lx!') ! endif ! endfunc ! ! func MouseShiftWheelLeftCode(row, col) ! " todo feed shift mod. ! return TerminalEscapeCode(0x42, a:row, a:col, 'M') ! endfunc ! ! func MouseShiftWheelLeft(row, col) ! if has('win32') ! call MSWinMouseEvent(s:MOUSE_CODE.SCRL_LEFT, a:row, a:col, 0, 0, ! \ s:MOUSE_CODE.MOD_SHIFT) ! else ! call feedkeys(MouseShiftWheelLeftCode(a:row, a:col), 'Lx!') ! endif ! endfunc ! ! func MouseShiftWheelRightCode(row, col) ! " todo feed shift mod. ! return TerminalEscapeCode(0x43, a:row, a:col, 'M') ! endfunc ! ! func MouseShiftWheelRight(row, col) ! if has('win32') ! call MSWinMouseEvent(s:MOUSE_CODE.SCRL_RIGHT, a:row, a:col, 0, 0, ! \ s:MOUSE_CODE.MOD_SHIFT) ! else ! call feedkeys(MouseShiftWheelRightCode(a:row, a:col), 'Lx!') ! endif endfunc " vim: shiftwidth=2 sts=2 expandtab *** ../vim-9.0.1083/src/testdir/test_gui.vim 2022-11-28 21:20:44.416909580 +0000 --- src/testdir/test_gui.vim 2022-12-20 19:22:31.977188037 +0000 *************** *** 1281,1287 **** let g:eventlist = g:eventlist[1 : ] endif ! call assert_equal([#{row: 4, col: 31}, #{row: 11, col: 31}], g:eventlist) " wiggle the mouse around within a screen cell, shouldn't trigger events call extend(args, #{cell: v:false}) --- 1281,1287 ---- let g:eventlist = g:eventlist[1 : ] endif ! call assert_equal([#{row: 3, col: 30}, #{row: 10, col: 30}], g:eventlist) " wiggle the mouse around within a screen cell, shouldn't trigger events call extend(args, #{cell: v:false}) *************** *** 1638,1647 **** " Test for sending low level key presses func SendKeys(keylist) for k in a:keylist ! call test_gui_event("sendevent", #{event: "keydown", keycode: k}) endfor for k in reverse(a:keylist) ! call test_gui_event("sendevent", #{event: "keyup", keycode: k}) endfor endfunc --- 1638,1647 ---- " Test for sending low level key presses func SendKeys(keylist) for k in a:keylist ! call test_gui_event("key", #{event: "keydown", keycode: k}) endfor for k in reverse(a:keylist) ! call test_gui_event("key", #{event: "keyup", keycode: k}) endfor endfunc *** ../vim-9.0.1083/src/testdir/test_mswin_event.vim 2022-12-20 19:59:53.400124626 +0000 --- src/testdir/test_mswin_event.vim 2022-12-20 19:57:01.584208587 +0000 *************** *** 0 **** --- 1,651 ---- + " Test MS-Windows console event handling. + + source check.vim + CheckMSWindows + " The mswin events should also work in gui + + source mouse.vim + + " Helper function for sending a sequence of low level key presses + " The modifer key(s) can be included as normal key presses in the sequence + func SendKeys(keylist) + for k in a:keylist + call test_mswin_event("key", #{event: "keydown", keycode: k}) + endfor + for k in reverse(copy(a:keylist)) + call test_mswin_event("key", #{event: "keyup", keycode: k}) + endfor + endfunc + + " Send an individual key press + " the modifers for the key press can be specified in the modifiers arg. + func SendKey(key, modifiers) + let args = { } + let args.keycode = a:key + let args.modifiers = a:modifiers + let args.event = "keydown" + call test_mswin_event("key", args) + let args.event = "keyup" + call test_mswin_event("key", args) + unlet args + endfunc + + " Test MS-Windows console key events + func Test_mswin_key_event() + CheckMSWindows + new + + " flush out any garbage left in the buffer + while getchar(0) + endwhile + + let VK = #{ + \ SPACE : 0x20, + \ SHIFT : 0x10, + \ LSHIFT : 0xA0, + \ RSHIFT : 0xA1, + \ CONTROL : 0x11, + \ LCONTROL : 0xA2, + \ RCONTROL : 0xA3, + \ MENU : 0x12, + \ ALT : 0x12, + \ LMENU : 0xA4, + \ LALT : 0xA4, + \ RMENU : 0xA5, + \ RALT : 0xA5, + \ OEM_1 : 0xBA, + \ OEM_2 : 0xBF, + \ OEM_3 : 0xC0, + \ OEM_4 : 0xDB, + \ OEM_5 : 0xDC, + \ OEM_6 : 0xDD, + \ OEM_7 : 0xDE, + \ OEM_PLUS : 0xBB, + \ OEM_COMMA : 0xBC, + \ OEM_MINUS : 0xBD, + \ OEM_PERIOD : 0xBE, + \ PRIOR : 0x21, + \ NEXT : 0x22, + \ END : 0x23, + \ HOME : 0x24, + \ LEFT : 0x25, + \ UP : 0x26, + \ RIGHT : 0x27, + \ DOWN : 0x28, + \ KEY_0 : 0x30, + \ KEY_1 : 0x31, + \ KEY_2 : 0x32, + \ KEY_3 : 0x33, + \ KEY_4 : 0x34, + \ KEY_5 : 0x35, + \ KEY_6 : 0x36, + \ KEY_7 : 0x37, + \ KEY_8 : 0x38, + \ KEY_9 : 0x39, + \ NUMPAD0 : 0x60, + \ NUMPAD1 : 0x61, + \ NUMPAD2 : 0x62, + \ NUMPAD3 : 0x63, + \ NUMPAD4 : 0x64, + \ NUMPAD5 : 0x65, + \ NUMPAD6 : 0x66, + \ NUMPAD7 : 0x67, + \ NUMPAD8 : 0x68, + \ NUMPAD9 : 0x69, + \ MULTIPLY : 0x6A, + \ ADD : 0x6B, + \ SUBTRACT : 0x6D, + \ F1 : 0x70, + \ F2 : 0x71, + \ F3 : 0x72, + \ F4 : 0x73, + \ F5 : 0x74, + \ F6 : 0x75, + \ F7 : 0x76, + \ F8 : 0x77, + \ F9 : 0x78, + \ F10 : 0x79, + \ F11 : 0x7A, + \ F12 : 0x7B, + \ KEY_A : 0x41, + \ KEY_B : 0x42, + \ KEY_C : 0x43, + \ KEY_D : 0x44, + \ KEY_E : 0x45, + \ KEY_F : 0x46, + \ KEY_G : 0x47, + \ KEY_H : 0x48, + \ KEY_I : 0x49, + \ KEY_J : 0x4A, + \ KEY_K : 0x4B, + \ KEY_L : 0x4C, + \ KEY_M : 0x4D, + \ KEY_N : 0x4E, + \ KEY_O : 0x4F, + \ KEY_P : 0x50, + \ KEY_Q : 0x51, + \ KEY_R : 0x52, + \ KEY_S : 0x53, + \ KEY_T : 0x54, + \ KEY_U : 0x55, + \ KEY_V : 0x56, + \ KEY_W : 0x57, + \ KEY_X : 0x58, + \ KEY_Y : 0x59, + \ KEY_Z : 0x5A + \ } + + let vim_MOD_MASK_SHIFT = 0x02 + let vim_MOD_MASK_CTRL = 0x04 + let vim_MOD_MASK_ALT = 0x08 + + let vim_key_modifiers = [ + \ ["", 0, []], + \ ["S-", 2, [VK.SHIFT]], + \ ["C-", 4, [VK.CONTROL]], + \ ["C-S-", 6, [VK.CONTROL, VK.SHIFT]], + \ ["A-", 8, [VK.MENU]], + \ ["A-S-", 10, [VK.MENU, VK.SHIFT]], + \ ["A-C-", 12, [VK.MENU, VK.CONTROL]], + \ ["A-C-S-", 14, [VK.MENU, VK.CONTROL, VK.SHIFT]], + \] + + " Some punctuation characters + " Assuming Standard US PC Keyboard layout + let test_punctuation_keys = [ + \ [[VK.SPACE], ' '], + \ [[VK.OEM_1], ';'], + \ [[VK.OEM_2], '/'], + \ [[VK.OEM_3], '`'], + \ [[VK.OEM_4], '['], + \ [[VK.OEM_5], '\'], + \ [[VK.OEM_6], ']'], + \ [[VK.OEM_7], ''''], + \ [[VK.OEM_PLUS], '='], + \ [[VK.OEM_COMMA], ','], + \ [[VK.OEM_MINUS], '-'], + \ [[VK.OEM_PERIOD], '.'], + \ [[VK.SHIFT, VK.OEM_1], ':'], + \ [[VK.SHIFT, VK.OEM_2], '?'], + \ [[VK.SHIFT, VK.OEM_3], '~'], + \ [[VK.SHIFT, VK.OEM_4], '{'], + \ [[VK.SHIFT, VK.OEM_5], '|'], + \ [[VK.SHIFT, VK.OEM_6], '}'], + \ [[VK.SHIFT, VK.OEM_7], '"'], + \ [[VK.SHIFT, VK.OEM_PLUS], '+'], + \ [[VK.SHIFT, VK.OEM_COMMA], '<'], + \ [[VK.SHIFT, VK.OEM_MINUS], '_'], + \ [[VK.SHIFT, VK.OEM_PERIOD], '>'], + \ [[VK.SHIFT, VK.KEY_1], '!'], + \ [[VK.SHIFT, VK.KEY_2], '@'], + \ [[VK.SHIFT, VK.KEY_3], '#'], + \ [[VK.SHIFT, VK.KEY_4], '$'], + \ [[VK.SHIFT, VK.KEY_5], '%'], + \ [[VK.SHIFT, VK.KEY_6], '^'], + \ [[VK.SHIFT, VK.KEY_7], '&'], + \ [[VK.SHIFT, VK.KEY_8], '*'], + \ [[VK.SHIFT, VK.KEY_9], '('], + \ [[VK.SHIFT, VK.KEY_0], ')'], + \ [[VK.LSHIFT, VK.KEY_9], '('], + \ [[VK.RSHIFT, VK.KEY_0], ')'] + \ ] + + for [kcodes, kstr] in test_punctuation_keys + call SendKeys(kcodes) + let ch = getcharstr(0) + call assert_equal($"{kstr}", $"{ch}") + let mod_mask = getcharmod() + " the mod_mask is zero when no modifiers are used + " and when the virtual termcap maps shift the character + call assert_equal(0, mod_mask, $"key = {kstr}") + endfor + + " flush out any garbage left in the buffer + while getchar(0) + endwhile + + for [kcodes, kstr] in test_punctuation_keys + let modifiers = 0 + let key = kcodes[0] + + for key in kcodes + if index([VK.SHIFT, VK.LSHIFT, VK.RSHIFT], key) >= 0 + let modifiers = modifiers + vim_MOD_MASK_SHIFT + endif + if index([VK.CONTROL, VK.LCONTROL, VK.RCONTROL], key) >= 0 + let modifiers = modifiers + vim_MOD_MASK_CTRL + endif + if index([VK.ALT, VK.LALT, VK.RALT], key) >= 0 + let modifiers = modifiers + vim_MOD_MASK_ALT + endif + endfor + + call SendKey(key, modifiers) + let ch = getcharstr(0) + call assert_equal($"{kstr}", $"{ch}") + let mod_mask = getcharmod() + " workaround for the virtual termcap maps changing the character instead + " of sending Shift + if index([VK.SHIFT, VK.LSHIFT, VK.RSHIFT], kcodes[0]) >= 0 + let modifiers = modifiers - vim_MOD_MASK_SHIFT + endif + call assert_equal(modifiers, mod_mask, $"key = {kstr}") + endfor + + " flush out any garbage left in the buffer + while getchar(0) + endwhile + + " Test keyboard codes for digits + " (0x30 - 0x39) : VK_0 - VK_9 are the same as ASCII '0' - '9' + for kc in range(48, 57) + call SendKeys([kc]) + let ch = getcharstr(0) + call assert_equal(nr2char(kc), ch) + call SendKey(kc, 0) + let ch = getcharstr(0) + call assert_equal(nr2char(kc), ch) + endfor + + " Test keyboard codes for Alt-0 to Alt-9 + " Expect +128 from the digit char codes + for modkey in [VK.ALT, VK.LALT, VK.RALT] + for kc in range(48, 57) + call SendKeys([modkey, kc]) + let ch = getchar(0) + call assert_equal(kc+128, ch) + call SendKey(kc, vim_MOD_MASK_ALT) + let ch = getchar(0) + call assert_equal(kc+128, ch) + endfor + endfor + + " Test for lowercase 'a' to 'z', VK codes 65(0x41) - 90(0x5A) + " Note: VK_A-VK_Z virtual key codes coincide with uppercase ASCII codes A-Z. + " eg VK_A is 65, and the ASCII character code for uppercase 'A' is also 65. + " Caution: these are interpreted as lowercase when Shift is NOT pressed. + " eg, sending VK_A (65) 'A' Key code without shift modifier, will produce ASCII + " char 'a' (91) as the output. The ASCII codes for the lowercase letters are + " numbered 32 higher than their uppercase versions. + for kc in range(65, 90) + call SendKeys([kc]) + let ch = getcharstr(0) + call assert_equal(nr2char(kc + 32), ch) + call SendKey(kc, 0) + let ch = getcharstr(0) + call assert_equal(nr2char(kc + 32), ch) + endfor + + " Test for Uppercase 'A' - 'Z' keys + " ie. with VK_SHIFT, expect the keycode = character code. + for kc in range(65, 90) + call SendKeys([VK.SHIFT, kc]) + let ch = getcharstr(0) + call assert_equal(nr2char(kc), ch) + call SendKey(kc, vim_MOD_MASK_SHIFT) + let ch = getcharstr(0) + call assert_equal(nr2char(kc), ch) + endfor + + " Test for to keys + " Same as for lowercase, except with Ctrl Key + " Expect the unicode characters 0x01 to 0x1A + for modkey in [VK.CONTROL, VK.LCONTROL, VK.RCONTROL] + for kc in range(65, 90) + call SendKeys([modkey, kc]) + let ch = getcharstr(0) + call assert_equal(nr2char(kc - 64), ch) + call SendKey(kc, vim_MOD_MASK_CTRL) + let ch = getcharstr(0) + call assert_equal(nr2char(kc - 64), ch) + endfor + endfor + + if !has("gui_running") + " Test for to keys + " Expect the unicode characters 0xE1 to 0xFA + " ie. 160 higher than the lowercase equivalent + for kc in range(65, 90) + call SendKeys([VK.LMENU, kc]) + let ch = getchar(0) + call assert_equal(kc+160, ch) + call SendKey(kc, vim_MOD_MASK_ALT) + let ch = getchar(0) + call assert_equal(kc+160, ch) + endfor + endif + + if !has("gui_running") + " Test for Function Keys 'F1' to 'F12' + for n in range(1, 12) + let kstr = $"F{n}" + let keycode = eval('"\<' .. kstr .. '>"') + call SendKeys([111+n]) + let ch = getcharstr(0) + call assert_equal(keycode, $"{ch}", $"key = <{kstr}>") + endfor + endif + + bw! + endfunc + + " Test MS-Windows console mouse events + func Test_mswin_mouse_event() + CheckMSWindows + new + + set mousemodel=extend + call test_override('no_query_mouse', 1) + call WaitForResponses() + + let msg = '' + + call setline(1, ['one two three', 'four five six']) + + " Test mouse movement + " by default, no mouse move events are generated + " this setting enables it to generate move events + set mousemev + + if !has('gui_running') + " console version needs a button pressed, + " otherwise it ignores mouse movements. + call MouseLeftClick(2, 3) + endif + call MSWinMouseEvent(0x700, 8, 13, 0, 0, 0) + if has('gui_running') + call feedkeys("\", 'Lx!') + endif + let pos = getmousepos() + call assert_equal(8, pos.screenrow) + call assert_equal(13, pos.screencol) + + if !has('gui_running') + call MouseLeftClick(2, 3) + call MSWinMouseEvent(0x700, 6, 4, 1, 0, 0) + let pos = getmousepos() + call assert_equal(6, pos.screenrow) + call assert_equal(4, pos.screencol) + endif + + " test cells vs pixels + if has('gui_running') + let args = { } + let args.row = 9 + let args.col = 7 + let args.move = 1 + let args.cell = 1 + call test_mswin_event("mouse", args) + call feedkeys("\", 'Lx!') + let pos = getmousepos() + call assert_equal(9, pos.screenrow) + call assert_equal(7, pos.screencol) + + let args.cell = 0 + call test_mswin_event("mouse", args) + call feedkeys("\", 'Lx!') + let pos = getmousepos() + call assert_equal(1, pos.screenrow) + call assert_equal(1, pos.screencol) + + unlet args + endif + + " finish testing mouse movement + set mousemev& + + " place the cursor using left click and release in normal mode + call MouseLeftClick(2, 4) + call MouseLeftRelease(2, 4) + if has('gui_running') + call feedkeys("\", 'Lx!') + endif + call assert_equal([0, 2, 4, 0], getpos('.')) + + " select and yank a word + let @" = '' + call MouseLeftClick(1, 9) + let args = #{button: 0, row: 1, col: 9, multiclick: 1, modifiers: 0} + call test_mswin_event('mouse', args) + call MouseLeftRelease(1, 9) + call feedkeys("y", 'Lx!') + call assert_equal('three', @") + + " create visual selection using right click + let @" = '' + + call MouseLeftClick(2 ,6) + call MouseLeftRelease(2, 6) + call MouseRightClick(2, 13) + call MouseRightRelease(2, 13) + call feedkeys("y", 'Lx!') + call assert_equal('five six', @") + + " paste using middle mouse button + let @* = 'abc ' + call feedkeys('""', 'Lx!') + call MouseMiddleClick(1, 9) + call MouseMiddleRelease(1, 9) + if has('gui_running') + call feedkeys("\", 'Lx!') + endif + call assert_equal(['one two abc three', 'four five six'], getline(1, '$')) + + " test mouse scrolling (aka touchpad scrolling.) + %d _ + set scrolloff=0 + call setline(1, range(1, 100)) + + " Scroll Down + call MouseWheelDown(2, 1) + call MouseWheelDown(2, 1) + call MouseWheelDown(2, 1) + call feedkeys("H", 'Lx!') + call assert_equal(10, line('.')) + + " Scroll Up + call MouseWheelUp(2, 1) + call MouseWheelUp(2, 1) + call feedkeys("H", 'Lx!') + call assert_equal(4, line('.')) + + " Shift Scroll Down + call MouseShiftWheelDown(2, 1) + call feedkeys("H", 'Lx!') + " should scroll from where it is (4) + visible buffer height - cmdheight + let shift_scroll_height = line('w$') - line('w0') - &cmdheight + call assert_equal(4 + shift_scroll_height, line('.')) + + " Shift Scroll Up + call MouseShiftWheelUp(2, 1) + call feedkeys("H", 'Lx!') + call assert_equal(4, line('.')) + + if !has('gui_running') + " Shift Scroll Down (using MOD) + call MSWinMouseEvent(0x100, 2, 1, 0, 0, 0x04) + call feedkeys("H", 'Lx!') + " should scroll from where it is (4) + visible buffer height - cmdheight + let shift_scroll_height = line('w$') - line('w0') - &cmdheight + call assert_equal(4 + shift_scroll_height, line('.')) + + " Shift Scroll Up (using MOD) + call MSWinMouseEvent(0x200, 2, 1, 0, 0, 0x04) + call feedkeys("H", 'Lx!') + call assert_equal(4, line('.')) + endif + + set scrolloff& + + %d _ + set nowrap + " make the buffer 500 wide. + call setline(1, range(10)->join('')->repeat(50)) + " Scroll Right + call MouseWheelRight(1, 5) + call MouseWheelRight(1, 10) + call MouseWheelRight(1, 15) + call feedkeys('g0', 'Lx!') + call assert_equal(19, col('.')) + + " Scroll Left + call MouseWheelLeft(1, 15) + call MouseWheelLeft(1, 10) + call feedkeys('g0', 'Lx!') + call assert_equal(7, col('.')) + + " Shift Scroll Right + call MouseShiftWheelRight(1, 10) + call feedkeys('g0', 'Lx!') + " should scroll from where it is (7) + window width + call assert_equal(7 + winwidth(0), col('.')) + + " Shift Scroll Left + call MouseShiftWheelLeft(1, 50) + call feedkeys('g0', 'Lx!') + call assert_equal(7, col('.')) + set wrap& + + %d _ + call setline(1, repeat([repeat('a', 60)], 10)) + + " record various mouse events + let mouseEventNames = [ + \ 'LeftMouse', 'LeftRelease', '2-LeftMouse', '3-LeftMouse', + \ 'S-LeftMouse', 'A-LeftMouse', 'C-LeftMouse', 'MiddleMouse', + \ 'MiddleRelease', '2-MiddleMouse', '3-MiddleMouse', + \ 'S-MiddleMouse', 'A-MiddleMouse', 'C-MiddleMouse', + \ 'RightMouse', 'RightRelease', '2-RightMouse', + \ '3-RightMouse', 'S-RightMouse', 'A-RightMouse', 'C-RightMouse', + \ ] + let mouseEventCodes = map(copy(mouseEventNames), "'<' .. v:val .. '>'") + let g:events = [] + for e in mouseEventCodes + exe 'nnoremap ' .. e .. ' call add(g:events, "' .. + \ substitute(e, '[<>]', '', 'g') .. '")' + endfor + + " Test various mouse buttons + "(0 - Left, 1 - Middle, 2 - Right, + " 0x300 - MOUSE_X1/FROM_LEFT_3RD_BUTTON, + " 0x400 - MOUSE_X2/FROM_LEFT_4TH_BUTTON) + for button in [0, 1, 2, 0x300, 0x400] + " Single click + let args = #{button: button, row: 2, col: 5, multiclick: 0, modifiers: 0} + call test_mswin_event('mouse', args) + let args.button = 3 + call test_mswin_event('mouse', args) + + " Double Click + let args.button = button + call test_mswin_event('mouse', args) + let args.multiclick = 1 + call test_mswin_event('mouse', args) + let args.button = 3 + let args.multiclick = 0 + call test_mswin_event('mouse', args) + + " Triple Click + let args.button = button + call test_mswin_event('mouse', args) + let args.multiclick = 1 + call test_mswin_event('mouse', args) + call test_mswin_event('mouse', args) + let args.button = 3 + let args.multiclick = 0 + call test_mswin_event('mouse', args) + + " Shift click + let args = #{button: button, row: 3, col: 7, multiclick: 0, modifiers: 4} + call test_mswin_event('mouse', args) + let args.button = 3 + call test_mswin_event('mouse', args) + + " Alt click + let args.button = button + let args.modifiers = 8 + call test_mswin_event('mouse', args) + let args.button = 3 + call test_mswin_event('mouse', args) + + " Ctrl click + let args.button = button + let args.modifiers = 16 + call test_mswin_event('mouse', args) + let args.button = 3 + call test_mswin_event('mouse', args) + + call feedkeys("\", 'Lx!') + endfor + + if has('gui_running') + call assert_equal(['LeftMouse', 'LeftRelease', 'LeftMouse', + \ '2-LeftMouse', 'LeftMouse', '2-LeftMouse', '3-LeftMouse', + \ 'S-LeftMouse', 'A-LeftMouse', 'C-LeftMouse', 'MiddleMouse', + \ 'MiddleRelease', 'MiddleMouse', '2-MiddleMouse', 'MiddleMouse', + \ '2-MiddleMouse', '3-MiddleMouse', 'S-MiddleMouse', 'A-MiddleMouse', + \ 'C-MiddleMouse', 'RightMouse', 'RightRelease', 'RightMouse', + \ '2-RightMouse', 'RightMouse', '2-RightMouse', '3-RightMouse', + \ 'S-RightMouse', 'A-RightMouse', 'C-RightMouse'], + \ g:events) + else + call assert_equal(['MiddleRelease', 'LeftMouse', '2-LeftMouse', + \ '3-LeftMouse', 'S-LeftMouse', 'MiddleMouse', '2-MiddleMouse', + \ '3-MiddleMouse', 'MiddleMouse', 'S-MiddleMouse', 'RightMouse', + \ '2-RightMouse', '3-RightMouse'], + \ g:events) + endif + + for e in mouseEventCodes + exe 'nunmap ' .. e + endfor + + bw! + call test_override('no_query_mouse', 0) + set mousemodel& + endfunc + + + " Test MS-Windows test_mswin_event error handling + func Test_mswin_event_error_handling() + + let args = #{button: 0xfff, row: 2, col: 4, move: 0, multiclick: 0, modifiers: 0} + if !has('gui_running') + call assert_fails("call test_mswin_event('mouse', args)",'E475:') + endif + let args = #{button: 0, row: 2, col: 4, move: 0, multiclick: 0, modifiers: 0} + call assert_fails("call test_mswin_event('a1b2c3', args)", 'E475:') + call assert_fails("call test_mswin_event(test_null_string(), {})", 'E475:') + + call assert_fails("call test_mswin_event([], args)", 'E1174:') + call assert_fails("call test_mswin_event('abc', [])", 'E1206:') + + call assert_false(test_mswin_event('mouse', test_null_dict())) + let args = #{row: 2, col: 4, multiclick: 0, modifiers: 0} + call assert_false(test_mswin_event('mouse', args)) + let args = #{button: 0, col: 4, multiclick: 0, modifiers: 0} + call assert_false(test_mswin_event('mouse', args)) + let args = #{button: 0, row: 2, multiclick: 0, modifiers: 0} + call assert_false(test_mswin_event('mouse', args)) + let args = #{button: 0, row: 2, col: 4, modifiers: 0} + call assert_false(test_mswin_event('mouse', args)) + let args = #{button: 0, row: 2, col: 4, multiclick: 0} + call assert_false(test_mswin_event('mouse', args)) + + call assert_false(test_mswin_event('key', test_null_dict())) + call assert_fails("call test_mswin_event('key', [])", 'E1206:') + call assert_fails("call test_mswin_event('key', {'event': 'keydown', 'keycode': 0x0})", 'E1291:') + call assert_fails("call test_mswin_event('key', {'event': 'keydown', 'keycode': [15]})", 'E745:') + call assert_fails("call test_mswin_event('key', {'event': 'keys', 'keycode': 0x41})", 'E475:') + call assert_fails("call test_mswin_event('key', {'keycode': 0x41})", 'E417:') + call assert_fails("call test_mswin_event('key', {'event': 'keydown'})", 'E1291:') + + call assert_fails("sandbox call test_mswin_event('key', {'event': 'keydown', 'keycode': 61 })", 'E48:') + + " flush out any garbage left in the buffer. + while getchar(0) + endwhile + endfunc + + + " vim: shiftwidth=2 sts=2 expandtab *** ../vim-9.0.1083/src/testdir/test_termcodes.vim 2022-12-05 13:50:49.722052362 +0000 --- src/testdir/test_termcodes.vim 2022-12-20 19:22:31.977188037 +0000 *************** *** 437,461 **** call assert_equal(1, line('w0'), msg) call assert_equal([0, 7, 1, 0], getpos('.'), msg) ! if has('gui') ! " Horizontal wheel scrolling currently only works when vim is ! " compiled with gui enabled. ! call MouseWheelRight(1, 1) ! call assert_equal(7, 1 + virtcol(".") - wincol(), msg) ! call assert_equal([0, 7, 7, 0], getpos('.'), msg) ! ! call MouseWheelRight(1, 1) ! call assert_equal(13, 1 + virtcol(".") - wincol(), msg) ! call assert_equal([0, 7, 13, 0], getpos('.'), msg) ! ! call MouseWheelLeft(1, 1) ! call assert_equal(7, 1 + virtcol(".") - wincol(), msg) ! call assert_equal([0, 7, 13, 0], getpos('.'), msg) ! ! call MouseWheelLeft(1, 1) ! call assert_equal(1, 1 + virtcol(".") - wincol(), msg) ! call assert_equal([0, 7, 13, 0], getpos('.'), msg) ! endif endfor let &mouse = save_mouse --- 437,458 ---- call assert_equal(1, line('w0'), msg) call assert_equal([0, 7, 1, 0], getpos('.'), msg) ! call MouseWheelRight(1, 1) ! call assert_equal(7, 1 + virtcol(".") - wincol(), msg) ! call assert_equal([0, 7, 7, 0], getpos('.'), msg) ! ! call MouseWheelRight(1, 1) ! call assert_equal(13, 1 + virtcol(".") - wincol(), msg) ! call assert_equal([0, 7, 13, 0], getpos('.'), msg) ! ! call MouseWheelLeft(1, 1) ! call assert_equal(7, 1 + virtcol(".") - wincol(), msg) ! call assert_equal([0, 7, 13, 0], getpos('.'), msg) ! ! call MouseWheelLeft(1, 1) ! call assert_equal(1, 1 + virtcol(".") - wincol(), msg) ! call assert_equal([0, 7, 13, 0], getpos('.'), msg) ! endfor let &mouse = save_mouse *** ../vim-9.0.1083/src/version.c 2022-12-20 13:38:18.284419451 +0000 --- src/version.c 2022-12-20 19:23:31.093219669 +0000 *************** *** 697,698 **** --- 697,700 ---- { /* Add new patch number below this line */ + /**/ + 1084, /**/ -- A poem: read aloud: <> !*''# Waka waka bang splat tick tick hash, ^"`$$- Caret quote back-tick dollar dollar dash, !*=@$_ Bang splat equal at dollar under-score, %*<> ~#4 Percent splat waka waka tilde number four, &[]../ Ampersand bracket bracket dot dot slash, |{,,SYSTEM HALTED Vertical-bar curly-bracket comma comma CRASH. Fred Bremmer and Steve Kroese (Calvin College & Seminary of Grand Rapids, MI.) /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///