Some applications control their own position when they are being moved. Since events' x and y fields are reported relative to the window, this can apparently result in them being out of sync from the X server side and ours (hwnd). This makes applications like Winamp go nuts when they are being moved and move all over the place "randomly".
The solution is to use x_root and y_root instead, when the root is the same as our root_window, which solves this issue. The final coordinates will have to be relative to the virtual screen anyway so this actually simplifies most of the code in send_mouse_input.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46309 Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
I don't know if the root_window check is needed, honestly. I know little about the X server, this was just discovered by debugging and looking it up.
If it's not needed, then the code can be simplified considerably, and the map_to_root_coords helper function can be removed. Someone with more experience in the X11 code should know this.
I've been running this patch for a few weeks now in my day-to-day Wine usage, with no problems with any app I used (granted my usage is not all-encompassing), so I am actually wondering why it wasn't done like this in the first place. Hopefully I'm not missing anything.
dlls/winex11.drv/mouse.c | 100 ++++++++++++++++++++++++--------------- 1 file changed, 62 insertions(+), 38 deletions(-)
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index d678706..b12e214 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -554,6 +554,32 @@ static BOOL is_old_motion_event( unsigned long serial ) }
+/*********************************************************************** + * map_to_root_coords + * + * Map the window-relative coordinates so they're relative to the root. + */ +static POINT map_to_root_coords(HWND hwnd, Window window, int x, int y) +{ + struct x11drv_win_data *data; + POINT pt = { x, y }; + + if (!(data = get_win_data(hwnd))) return pt; + 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; + release_win_data(data); + MapWindowPoints(hwnd, 0, &pt, 1); + + return pt; +} + + /*********************************************************************** * send_mouse_input * @@ -562,7 +588,6 @@ static BOOL is_old_motion_event( unsigned long serial ) static void send_mouse_input( HWND hwnd, Window window, unsigned int state, INPUT *input ) { struct x11drv_win_data *data; - POINT pt;
input->type = INPUT_MOUSE;
@@ -585,32 +610,14 @@ static void send_mouse_input( HWND hwnd, Window window, unsigned int state, INPU return; }
- if (window != root_window) - { - pt.x = input->u.mi.dx; - pt.y = input->u.mi.dy; - } - else pt = root_to_virtual_screen( input->u.mi.dx, input->u.mi.dy ); - - if (!(data = get_win_data( hwnd ))) return; - - 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( data->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 (InterlockedExchangePointer( (void **)&cursor_window, hwnd ) != hwnd || - input->u.mi.time - last_cursor_change > 100) + if ((InterlockedExchangePointer( (void **)&cursor_window, hwnd ) != hwnd || + input->u.mi.time - last_cursor_change > 100) && + (data = get_win_data( hwnd ))) { sync_window_cursor( data->whole_window ); last_cursor_change = input->u.mi.time; + release_win_data( data ); } - release_win_data( data );
if (hwnd != GetDesktopWindow()) { @@ -625,8 +632,7 @@ static void send_mouse_input( HWND hwnd, Window window, unsigned int state, INPU /* ignore event if a button is pressed, since the mouse is then grabbed too */ !(state & (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask|Button6Mask|Button7Mask))) { - RECT rect; - SetRect( &rect, pt.x, pt.y, pt.x + 1, pt.y + 1 ); + RECT rect = { input->u.mi.dx, input->u.mi.dy, input->u.mi.dx + 1, input->u.mi.dy + 1 };
SERVER_START_REQ( update_window_zorder ) { @@ -640,8 +646,6 @@ static void send_mouse_input( HWND hwnd, Window window, unsigned int state, INPU SERVER_END_REQ; }
- input->u.mi.dx = pt.x; - input->u.mi.dy = pt.y; __wine_send_input( hwnd, input ); }
@@ -1598,13 +1602,18 @@ BOOL X11DRV_ButtonPress( HWND hwnd, XEvent *xev ) XButtonEvent *event = &xev->xbutton; int buttonNum = event->button - 1; INPUT input; + POINT pt;
if (buttonNum >= NB_BUTTONS) return FALSE;
- TRACE( "hwnd %p/%lx button %u pos %d,%d\n", hwnd, event->window, buttonNum, event->x, event->y ); + pt = root_to_virtual_screen(event->x_root, event->y_root); + if (event->root != root_window || !event->same_screen) + pt = map_to_root_coords(hwnd, event->window, event->x, event->y); + + TRACE( "hwnd %p/%lx button %u pos %d,%d\n", hwnd, event->window, buttonNum, pt.x, pt.y );
- input.u.mi.dx = event->x; - input.u.mi.dy = event->y; + input.u.mi.dx = pt.x; + input.u.mi.dy = pt.y; input.u.mi.mouseData = button_down_data[buttonNum]; input.u.mi.dwFlags = button_down_flags[buttonNum] | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE; input.u.mi.time = EVENT_x11_time_to_win32_time( event->time ); @@ -1624,13 +1633,18 @@ BOOL X11DRV_ButtonRelease( HWND hwnd, XEvent *xev ) XButtonEvent *event = &xev->xbutton; int buttonNum = event->button - 1; INPUT input; + POINT pt;
if (buttonNum >= NB_BUTTONS || !button_up_flags[buttonNum]) return FALSE;
- TRACE( "hwnd %p/%lx button %u pos %d,%d\n", hwnd, event->window, buttonNum, event->x, event->y ); + pt = root_to_virtual_screen(event->x_root, event->y_root); + if (event->root != root_window || !event->same_screen) + pt = map_to_root_coords(hwnd, event->window, event->x, event->y); + + TRACE( "hwnd %p/%lx button %u pos %d,%d\n", hwnd, event->window, buttonNum, pt.x, pt.y );
- input.u.mi.dx = event->x; - input.u.mi.dy = event->y; + input.u.mi.dx = pt.x; + input.u.mi.dy = pt.y; input.u.mi.mouseData = button_up_data[buttonNum]; input.u.mi.dwFlags = button_up_flags[buttonNum] | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE; input.u.mi.time = EVENT_x11_time_to_win32_time( event->time ); @@ -1648,12 +1662,17 @@ BOOL X11DRV_MotionNotify( HWND hwnd, XEvent *xev ) { XMotionEvent *event = &xev->xmotion; INPUT input; + POINT pt; + + pt = root_to_virtual_screen(event->x_root, event->y_root); + if (event->root != root_window || !event->same_screen) + pt = map_to_root_coords(hwnd, event->window, event->x, event->y);
TRACE( "hwnd %p/%lx pos %d,%d is_hint %d serial %lu\n", - hwnd, event->window, event->x, event->y, event->is_hint, event->serial ); + hwnd, event->window, pt.x, pt.y, event->is_hint, event->serial );
- input.u.mi.dx = event->x; - input.u.mi.dy = event->y; + input.u.mi.dx = pt.x; + input.u.mi.dy = pt.y; input.u.mi.mouseData = 0; input.u.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE; input.u.mi.time = EVENT_x11_time_to_win32_time( event->time ); @@ -1676,6 +1695,7 @@ BOOL X11DRV_EnterNotify( HWND hwnd, XEvent *xev ) { XCrossingEvent *event = &xev->xcrossing; INPUT input; + POINT pt;
TRACE( "hwnd %p/%lx pos %d,%d detail %d\n", hwnd, event->window, event->x, event->y, event->detail );
@@ -1683,8 +1703,12 @@ BOOL X11DRV_EnterNotify( HWND hwnd, XEvent *xev ) if (hwnd == x11drv_thread_data()->grab_hwnd) return FALSE;
/* simulate a mouse motion event */ - input.u.mi.dx = event->x; - input.u.mi.dy = event->y; + pt = root_to_virtual_screen(event->x_root, event->y_root); + if (event->root != root_window || !event->same_screen) + pt = map_to_root_coords(hwnd, event->window, event->x, event->y); + + input.u.mi.dx = pt.x; + input.u.mi.dy = pt.y; input.u.mi.mouseData = 0; input.u.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE; input.u.mi.time = EVENT_x11_time_to_win32_time( event->time );