Signed-off-by: Rémi Bernon rbernon@codeweavers.com ---
Supersedes: 203001-203003
v3: No functional changes, add a test case and reword commits.
dlls/user32/tests/input.c | 48 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 63163b7ed01..1fd9383e80a 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -3186,6 +3186,24 @@ static DWORD WINAPI create_static_win(void *arg) return 0; }
+static LRESULT CALLBACK mouse_move_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + static DWORD last_x = 200, expect_x = 210; + + if (msg == WM_MOUSEMOVE) + { + POINT pt = {LOWORD(lparam), HIWORD(lparam)}; + MapWindowPoints(hwnd, NULL, &pt, 1); + + if (pt.x != last_x) todo_wine ok( pt.x == expect_x, "got unexpected WM_MOUSEMOVE x %d, expected %d\n", pt.x, expect_x ); + + expect_x = pt.x == 200 ? 210 : 200; + last_x = pt.x; + } + + return DefWindowProcW(hwnd, msg, wparam, lparam); +} + static void test_Input_mouse(void) { BOOL got_button_down, got_button_up; @@ -3412,6 +3430,36 @@ static void test_Input_mouse(void) CloseHandle(thread_data.start_event); CloseHandle(thread_data.end_event); DestroyWindow(button_win); + + SetCursorPos(200, 200); + hwnd = CreateWindowA("static", "Title", WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 200, 200, NULL, NULL, NULL, NULL); + ok(hwnd != NULL, "CreateWindowA failed %u\n", GetLastError()); + + /* warm up test case by moving cursor and window a bit first */ + SetCursorPos(210, 200); + SetWindowPos(hwnd, NULL, 110, 100, 0, 0, SWP_NOSIZE); + empty_message_queue(); + SetCursorPos(200, 200); + SetWindowPos(hwnd, NULL, 100, 100, 0, 0, SWP_NOSIZE); + empty_message_queue(); + SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)mouse_move_wndproc); + + SetCursorPos(210, 200); + SetWindowPos(hwnd, NULL, 110, 100, 0, 0, SWP_NOSIZE); + empty_message_queue(); + GetCursorPos(&pt); + todo_wine ok(pt.x == 210 && pt.y == 200, "GetCursorPos returned %dx%d, expected 210x200\n", pt.x, pt.y); + + SetCursorPos(200, 200); + SetWindowPos(hwnd, NULL, 100, 100, 0, 0, SWP_NOSIZE); + empty_message_queue(); + GetCursorPos(&pt); + todo_wine ok(pt.x == 200 && pt.y == 200, "GetCursorPos returned %dx%d, expected 200x200\n", pt.x, pt.y); + + SetCursorPos(pt_org.x, pt_org.y); + empty_message_queue(); + DestroyWindow(hwnd); }
We already converted coordinates from root to virtual screen, we shouldn't need to remap them.
This is actually no-op because when window == root_window, hwnd is the desktop window. In which case it isn't RTL, and MapWindowPoints is no-op too.
It makes things less confusing and it will help later cases when we get root-relative coordinates for other windows.
Based on a patch from Gabriel Ivăncescu gabrielopcode@gmail.com. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46309
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winex11.drv/mouse.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 8b39458fd37..e567a03c61c 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -599,15 +599,18 @@ static void map_event_coords( HWND hwnd, Window window, struct x11drv_win_data * TRACE( "hwnd %p, window %lx, data %p, input %p\n", hwnd, window, data, input );
if (window == root_window) pt = root_to_virtual_screen( pt.x, pt.y ); - if (window == data->whole_window) + else { - pt.x += data->whole_rect.left - data->client_rect.left; - pt.y += data->whole_rect.top - data->client_rect.top; - } + if (window == data->whole_window) + { + pt.x += data->whole_rect.left - data->client_rect.left; + pt.y += data->whole_rect.top - data->client_rect.top; + }
- if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) - pt.x = data->client_rect.right - data->client_rect.left - 1 - pt.x; - MapWindowPoints( hwnd, 0, &pt, 1 ); + if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) + pt.x = data->client_rect.right - data->client_rect.left - 1 - pt.x; + MapWindowPoints( hwnd, 0, &pt, 1 ); + }
TRACE( "mapped %s to %s\n", wine_dbgstr_point( (POINT *)&input->u.mi.dx ), wine_dbgstr_point( &pt ) );
Based on a patch from Gabriel Ivăncescu gabrielopcode@gmail.com. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46309
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winex11.drv/mouse.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-)
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index e567a03c61c..8110070c2bf 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -592,13 +592,24 @@ static BOOL is_old_motion_event( unsigned long serial ) * * Map the input event coordinates so they're relative to the desktop. */ -static void map_event_coords( HWND hwnd, Window window, struct x11drv_win_data *data, INPUT *input ) +static void map_event_coords( HWND hwnd, Window window, INPUT *input ) { + struct x11drv_thread_data *thread_data; + struct x11drv_win_data *data = NULL; POINT pt = { input->u.mi.dx, input->u.mi.dy };
- TRACE( "hwnd %p, window %lx, data %p, input %p\n", hwnd, window, data, input ); + TRACE( "hwnd %p, window %lx, input %p\n", hwnd, window, input );
- if (window == root_window) pt = root_to_virtual_screen( pt.x, pt.y ); + if (!hwnd) + { + thread_data = x11drv_thread_data(); + if (!thread_data->clip_hwnd) return; + if (thread_data->clip_window != window) return; + pt.x += clip_rect.left; + pt.y += clip_rect.top; + } + else if (!(data = get_win_data( hwnd ))) return; + else if (window == root_window) pt = root_to_virtual_screen( pt.x, pt.y ); else { if (window == data->whole_window) @@ -611,6 +622,7 @@ static void map_event_coords( HWND hwnd, Window window, struct x11drv_win_data * pt.x = data->client_rect.right - data->client_rect.left - 1 - pt.x; MapWindowPoints( hwnd, 0, &pt, 1 ); } + if (data) release_win_data( data );
TRACE( "mapped %s to %s\n", wine_dbgstr_point( (POINT *)&input->u.mi.dx ), wine_dbgstr_point( &pt ) );
@@ -643,15 +655,11 @@ static void send_mouse_input( HWND hwnd, Window window, unsigned int state, INPU sync_window_cursor( window ); last_cursor_change = input->u.mi.time; } - input->u.mi.dx += clip_rect.left; - input->u.mi.dy += clip_rect.top; __wine_send_input( hwnd, input ); return; }
if (!(data = get_win_data( hwnd ))) return; - map_event_coords( hwnd, window, data, input ); - if (InterlockedExchangePointer( (void **)&cursor_window, hwnd ) != hwnd || input->u.mi.time - last_cursor_change > 100) { @@ -1699,6 +1707,7 @@ BOOL X11DRV_ButtonPress( HWND hwnd, XEvent *xev ) input.u.mi.dwExtraInfo = 0;
update_user_time( event->time ); + map_event_coords( hwnd, event->window, &input ); send_mouse_input( hwnd, event->window, event->state, &input ); return TRUE; } @@ -1724,6 +1733,7 @@ BOOL X11DRV_ButtonRelease( HWND hwnd, XEvent *xev ) input.u.mi.time = EVENT_x11_time_to_win32_time( event->time ); input.u.mi.dwExtraInfo = 0;
+ map_event_coords( hwnd, event->window, &input ); send_mouse_input( hwnd, event->window, event->state, &input ); return TRUE; } @@ -1752,6 +1762,7 @@ BOOL X11DRV_MotionNotify( HWND hwnd, XEvent *xev ) TRACE( "pos %d,%d old serial %lu, ignoring\n", input.u.mi.dx, input.u.mi.dy, event->serial ); return FALSE; } + map_event_coords( hwnd, event->window, &input ); send_mouse_input( hwnd, event->window, event->state, &input ); return TRUE; } @@ -1783,6 +1794,7 @@ BOOL X11DRV_EnterNotify( HWND hwnd, XEvent *xev ) TRACE( "pos %d,%d old serial %lu, ignoring\n", input.u.mi.dx, input.u.mi.dy, event->serial ); return FALSE; } + map_event_coords( hwnd, event->window, &input ); send_mouse_input( hwnd, event->window, event->state, &input ); return TRUE; }
Since whole_rect / client_rect are updated asynchronously, there may be a small lag between X11 and Wine regarding the expected window position.
Then, as events' x and y fields are reported relative to the X11 window position, this lag can cause inconsistencies when we compute absolute mouse positions.
Also, applications that control their own position while being moved cause additional whole_rect / client_rect updates, before X11 knows about it.
This can make applications like Winamp go nuts when they are being moved and move all over the place "randomly".
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46309 Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/user32/tests/input.c | 6 +++--- dlls/winex11.drv/mouse.c | 14 ++++++++------ 2 files changed, 11 insertions(+), 9 deletions(-)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 1fd9383e80a..646a9a66eb5 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -3195,7 +3195,7 @@ static LRESULT CALLBACK mouse_move_wndproc(HWND hwnd, UINT msg, WPARAM wparam, L POINT pt = {LOWORD(lparam), HIWORD(lparam)}; MapWindowPoints(hwnd, NULL, &pt, 1);
- if (pt.x != last_x) todo_wine ok( pt.x == expect_x, "got unexpected WM_MOUSEMOVE x %d, expected %d\n", pt.x, expect_x ); + if (pt.x != last_x) ok( pt.x == expect_x, "got unexpected WM_MOUSEMOVE x %d, expected %d\n", pt.x, expect_x );
expect_x = pt.x == 200 ? 210 : 200; last_x = pt.x; @@ -3449,13 +3449,13 @@ static void test_Input_mouse(void) SetWindowPos(hwnd, NULL, 110, 100, 0, 0, SWP_NOSIZE); empty_message_queue(); GetCursorPos(&pt); - todo_wine ok(pt.x == 210 && pt.y == 200, "GetCursorPos returned %dx%d, expected 210x200\n", pt.x, pt.y); + ok(pt.x == 210 && pt.y == 200, "GetCursorPos returned %dx%d, expected 210x200\n", pt.x, pt.y);
SetCursorPos(200, 200); SetWindowPos(hwnd, NULL, 100, 100, 0, 0, SWP_NOSIZE); empty_message_queue(); GetCursorPos(&pt); - todo_wine ok(pt.x == 200 && pt.y == 200, "GetCursorPos returned %dx%d, expected 200x200\n", pt.x, pt.y); + ok(pt.x == 200 && pt.y == 200, "GetCursorPos returned %dx%d, expected 200x200\n", pt.x, pt.y);
SetCursorPos(pt_org.x, pt_org.y); empty_message_queue(); diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 8110070c2bf..a87c147cbd4 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -592,13 +592,14 @@ static BOOL is_old_motion_event( unsigned long serial ) * * Map the input event coordinates so they're relative to the desktop. */ -static void map_event_coords( HWND hwnd, Window window, INPUT *input ) +static void map_event_coords( HWND hwnd, Window window, Window event_root, int x_root, int y_root, INPUT *input ) { struct x11drv_thread_data *thread_data; struct x11drv_win_data *data = NULL; POINT pt = { input->u.mi.dx, input->u.mi.dy };
- TRACE( "hwnd %p, window %lx, input %p\n", hwnd, window, input ); + TRACE( "hwnd %p, window %lx, event_root %lx, x_root %d, y_root %d, input %p\n", hwnd, window, event_root, + x_root, y_root, input );
if (!hwnd) { @@ -610,6 +611,7 @@ static void map_event_coords( HWND hwnd, Window window, INPUT *input ) } else if (!(data = get_win_data( hwnd ))) return; else if (window == root_window) pt = root_to_virtual_screen( pt.x, pt.y ); + else if (event_root == root_window) pt = root_to_virtual_screen( x_root, y_root ); else { if (window == data->whole_window) @@ -1707,7 +1709,7 @@ BOOL X11DRV_ButtonPress( HWND hwnd, XEvent *xev ) input.u.mi.dwExtraInfo = 0;
update_user_time( event->time ); - map_event_coords( hwnd, event->window, &input ); + map_event_coords( hwnd, event->window, event->root, event->x_root, event->y_root, &input ); send_mouse_input( hwnd, event->window, event->state, &input ); return TRUE; } @@ -1733,7 +1735,7 @@ BOOL X11DRV_ButtonRelease( HWND hwnd, XEvent *xev ) input.u.mi.time = EVENT_x11_time_to_win32_time( event->time ); input.u.mi.dwExtraInfo = 0;
- map_event_coords( hwnd, event->window, &input ); + map_event_coords( hwnd, event->window, event->root, event->x_root, event->y_root, &input ); send_mouse_input( hwnd, event->window, event->state, &input ); return TRUE; } @@ -1762,7 +1764,7 @@ BOOL X11DRV_MotionNotify( HWND hwnd, XEvent *xev ) TRACE( "pos %d,%d old serial %lu, ignoring\n", input.u.mi.dx, input.u.mi.dy, event->serial ); return FALSE; } - map_event_coords( hwnd, event->window, &input ); + map_event_coords( hwnd, event->window, event->root, event->x_root, event->y_root, &input ); send_mouse_input( hwnd, event->window, event->state, &input ); return TRUE; } @@ -1794,7 +1796,7 @@ BOOL X11DRV_EnterNotify( HWND hwnd, XEvent *xev ) TRACE( "pos %d,%d old serial %lu, ignoring\n", input.u.mi.dx, input.u.mi.dy, event->serial ); return FALSE; } - map_event_coords( hwnd, event->window, &input ); + map_event_coords( hwnd, event->window, event->root, event->x_root, event->y_root, &input ); send_mouse_input( hwnd, event->window, event->state, &input ); return TRUE; }
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=88342
Your paranoid android.
=== w7u_el (32 bit report) ===
user32: input.c:2185: Test failed: GetRawInputData succeeded input.c:2186: Test failed: GetRawInputData returned deadbeef input.c:2299: Test failed: Spurious WM_INPUT messages
=== w10pro64_ar (64 bit report) ===
user32: input.c:3258: Test failed: expected WM_LBUTTONDOWN message input.c:3259: Test failed: expected WM_LBUTTONUP message input.c:3286: Test failed: expected WM_NCHITTEST message input.c:3287: Test failed: expected WM_RBUTTONDOWN message input.c:3288: Test failed: expected WM_RBUTTONUP message input.c:3317: Test failed: expected WM_LBUTTONDOWN message input.c:3318: Test failed: expected WM_LBUTTONUP message input.c:3371: Test failed: expected loop with WM_NCHITTEST messages input.c:3424: Test failed: expected WM_LBUTTONDOWN message input.c:3425: Test failed: expected WM_LBUTTONUP message
=== w10pro64_he (64 bit report) ===
user32: input.c:1322: Test failed: GetCursorPos: (99,100)
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=88339
Your paranoid android.
=== w1064_2qxl (64 bit report) ===
user32: input.c:3286: Test failed: expected WM_NCHITTEST message input.c:3287: Test failed: expected WM_RBUTTONDOWN message input.c:3288: Test failed: expected WM_RBUTTONUP message input.c:3317: Test failed: expected WM_LBUTTONDOWN message input.c:3318: Test failed: expected WM_LBUTTONUP message input.c:3371: Test failed: expected loop with WM_NCHITTEST messages input.c:3424: Test failed: expected WM_LBUTTONDOWN message input.c:3425: Test failed: expected WM_LBUTTONUP message