-- v2: win32u: Sync cursor position on window position change.
From: Paul Gofman pgofman@codeweavers.com
--- dlls/user32/tests/msg.c | 51 +++++++++++++++++++++++++++++++++++- dlls/win32u/input.c | 13 +++++++++ dlls/win32u/win32u_private.h | 1 + dlls/win32u/window.c | 1 + server/protocol.def | 1 + server/queue.c | 24 ++++++++++------- 6 files changed, 81 insertions(+), 10 deletions(-)
diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c index 0347f0d73b0..590f50aa6b7 100644 --- a/dlls/user32/tests/msg.c +++ b/dlls/user32/tests/msg.c @@ -73,6 +73,8 @@ #define ARCH "none" #endif
+static void pump_msg_loop(HWND hwnd, HACCEL hAccel); + /* encoded DRAWITEMSTRUCT into an LPARAM */ typedef struct { @@ -108,6 +110,7 @@ typedef struct
static BOOL test_DestroyWindow_flag; static BOOL test_context_menu; +static BOOL ignore_mouse_messages = TRUE; static HWINEVENTHOOK hEvent_hook; static HHOOK hKBD_hook; static HHOOK hCBT_hook; @@ -6160,6 +6163,30 @@ static const struct message WmFrameChanged_move[] = { { 0 } };
+static const struct message WmMove_mouse[] = { + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOSIZE }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOCLIENTSIZE }, + { WM_MOVE, sent|defwinproc, 0 }, + { WM_GETTEXT, sent|optional }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam|optional, 0, 0 }, /* Win7 seems to send this twice. */ + { WM_SETCURSOR, sent|lparam, 0, HTCLIENT | (WM_MOUSEMOVE << 16) }, + { WM_MOUSEMOVE, sent }, + /* WM_MOUSEMOVE with accompanying WM_SETCURSOR may sometimes appear a few extra times on Windows 7 with the + * same parameters. */ + { WM_SETCURSOR, sent|lparam|optional, 0, HTCLIENT | (WM_MOUSEMOVE << 16) }, + { WM_MOUSEMOVE, sent|optional }, + { WM_SETCURSOR, sent|lparam|optional, 0, HTCLIENT | (WM_MOUSEMOVE << 16) }, + { WM_MOUSEMOVE, sent|optional }, + { WM_SETCURSOR, sent|lparam|optional, 0, HTCLIENT | (WM_MOUSEMOVE << 16) }, + { WM_MOUSEMOVE, sent|optional }, + { WM_SETCURSOR, sent|lparam|optional, 0, HTCLIENT | (WM_MOUSEMOVE << 16) }, + { WM_MOUSEMOVE, sent|optional }, + { WM_SETCURSOR, sent|lparam|optional, 0, HTCLIENT | (WM_MOUSEMOVE << 16) }, + { WM_MOUSEMOVE, sent|optional }, + { 0 } +}; + static void test_setwindowpos(void) { HWND hwnd; @@ -6198,6 +6225,7 @@ static void test_setwindowpos(void) expect(sysX + X, rc.right); expect(winY + Y, rc.bottom);
+ GetWindowRect(hwnd, &rc); res = SetWindowPos( hwnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); ok_sequence(WmFrameChanged_move, "FrameChanged", FALSE); @@ -6207,6 +6235,26 @@ static void test_setwindowpos(void) expect(sysX, rc.right); expect(winY, rc.bottom);
+ /* get away from possible menu bar to avoid spurious position changed induced by WM. */ + res = SetWindowPos( hwnd, HWND_TOPMOST, 200, 200, 200, 200, SWP_SHOWWINDOW ); + ok(res == TRUE, "SetWindowPos expected TRUE, got %Id.\n", res); + SetForegroundWindow( hwnd ); + SetActiveWindow( hwnd ); + flush_events(); + pump_msg_loop( hwnd, 0 ); + flush_sequence(); + GetWindowRect( hwnd, &rc ); + SetCursorPos( rc.left + 100, rc.top + 100 ); + flush_events(); + pump_msg_loop( hwnd, 0 ); + flush_sequence(); + ignore_mouse_messages = FALSE; + res = SetWindowPos( hwnd, 0, 205, 205, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE ); + ok(res == TRUE, "SetWindowPos expected TRUE, got %Id.\n", res); + flush_events(); + ok_sequence(WmMove_mouse, "MouseMove", FALSE); + ignore_mouse_messages = TRUE; + DestroyWindow(hwnd); }
@@ -10853,7 +10901,8 @@ static LRESULT MsgCheckProc (BOOL unicode, HWND hwnd, UINT message, case WM_NCMOUSEMOVE: case WM_SETCURSOR: case WM_IME_SELECT: - return 0; + if (ignore_mouse_messages) return 0; + break; }
msg.hwnd = hwnd; diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 0640b34ac2e..0577f460601 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -732,6 +732,19 @@ BOOL WINAPI NtUserSetCursorPos( INT x, INT y ) return ret; }
+/*********************************************************************** + * update_window_cursor_pos + */ +void update_window_cursor_pos(void) +{ + SERVER_START_REQ( set_cursor ) + { + req->flags = SET_CURSOR_UPDATE; + wine_server_call( req ); + } + SERVER_END_REQ; +} + /*********************************************************************** * get_cursor_pos */ diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index 8956f83af75..06f9919267d 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -94,6 +94,7 @@ extern BOOL destroy_caret(void); extern HWND get_active_window(void); extern HWND get_capture(void); extern BOOL get_cursor_pos( POINT *pt ); +extern void update_window_cursor_pos(void); extern HWND get_focus(void); extern DWORD get_input_state(void); extern BOOL get_async_keyboard_state( BYTE state[256] ); diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index 48a7304b6b0..928287b15ee 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -3795,6 +3795,7 @@ BOOL set_window_pos( WINDOWPOS *winpos, int parent_x, int parent_y ) winpos->cx = new_rects.window.right - new_rects.window.left; winpos->cy = new_rects.window.bottom - new_rects.window.top; send_message( winpos->hwnd, WM_WINDOWPOSCHANGED, 0, (LPARAM)winpos ); + update_window_cursor_pos(); }
if ((winpos->flags & (SWP_NOSIZE|SWP_NOMOVE|SWP_FRAMECHANGED)) != (SWP_NOSIZE|SWP_NOMOVE)) diff --git a/server/protocol.def b/server/protocol.def index a4f25e805f8..c0306113220 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3916,6 +3916,7 @@ typedef union #define SET_CURSOR_CLIP 0x08 #define SET_CURSOR_NOCLIP 0x10 #define SET_CURSOR_FSCLIP 0x20 +#define SET_CURSOR_UPDATE 0x40
/* Get the history of the 64 last cursor positions */ @REQ(get_cursor_history) diff --git a/server/queue.c b/server/queue.c index c99c8d8661b..2603c88f33b 100644 --- a/server/queue.c +++ b/server/queue.c @@ -563,19 +563,11 @@ static void update_desktop_cursor_handle( struct desktop *desktop, struct thread } }
-/* set the cursor position and queue the corresponding mouse message */ -static void set_cursor_pos( struct desktop *desktop, int x, int y ) +static void update_window_cursor_pos( struct desktop *desktop, int x, int y ) { static const struct hw_msg_source source = { IMDT_UNAVAILABLE, IMO_SYSTEM }; - const struct rawinput_device *device; struct message *msg;
- if ((device = current->process->rawinput_mouse) && (device->flags & RIDEV_NOLEGACY)) - { - update_desktop_cursor_pos( desktop, 0, x, y ); - return; - } - if (!(msg = alloc_hardware_message( 0, source, get_tick_count(), 0 ))) return;
msg->msg = WM_MOUSEMOVE; @@ -584,6 +576,19 @@ static void set_cursor_pos( struct desktop *desktop, int x, int y ) queue_hardware_message( desktop, msg, 1 ); }
+/* set the cursor position and queue the corresponding mouse message */ +static void set_cursor_pos( struct desktop *desktop, int x, int y ) +{ + const struct rawinput_device *device; + + if ((device = current->process->rawinput_mouse) && (device->flags & RIDEV_NOLEGACY)) + { + update_desktop_cursor_pos( desktop, 0, x, y ); + return; + } + update_window_cursor_pos( desktop, x, y ); +} + /* retrieve default position and time for synthesized messages */ static void get_message_defaults( struct msg_queue *queue, int *x, int *y, unsigned int *time ) { @@ -4009,6 +4014,7 @@ DECL_HANDLER(set_cursor) } SHARED_WRITE_END;
+ if (req->flags & SET_CURSOR_UPDATE) update_window_cursor_pos( desktop, reply->prev_x, reply->prev_y ); if (req->flags & SET_CURSOR_POS) set_cursor_pos( desktop, req->x, req->y ); if (req->flags & SET_CURSOR_CLIP) set_clip_rectangle( desktop, &req->clip, req->flags, 0 ); if (req->flags & SET_CURSOR_NOCLIP) set_clip_rectangle( desktop, NULL, SET_CURSOR_NOCLIP, 0 );
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=149235
Your paranoid android.
=== debian11b (64 bit WoW report) ===
user32: input.c:4305: Test succeeded inside todo block: button_down_hwnd_todo 1: got MSG_TEST_WIN hwnd 0000000001C300EA, msg WM_LBUTTONDOWN, wparam 0x1, lparam 0x320032
wsdapi: discovery.c:1077: Test failed: Hello message not received discovery.c:1078: Test failed: EndpointReference not received discovery.c:1079: Test failed: AppSequence not received discovery.c:1080: Test failed: MetadataVersion not received discovery.c:1081: Test failed: Hello message metadata not received discovery.c:1082: Test failed: Custom header not received discovery.c:1083: Test failed: Wine namespace not received discovery.c:1084: Test failed: Body and Hello elements not received discovery.c:1085: Test failed: Custom body element not received discovery.c:1086: Test failed: Types not received discovery.c:1087: Test failed: XML namespaces not received discovery.c:1088: Test failed: Scopes not received discovery.c:1089: Test failed: XAddrs not received
v2: - avoid touching host cursor state.
Rémi Bernon (@rbernon) commented about dlls/win32u/input.c:
return ret;
}
+/***********************************************************************
update_window_cursor_pos
- */
+void update_window_cursor_pos(void) +{
- SERVER_START_REQ( set_cursor )
Could this be done without a new server request? Every window position change goes through the `apply_window_pos` request already, wineserver should be able to update the cursor pos accordingly, or is there something I'm missing?