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 result in them being out of sync from the WM. This makes applications like Winamp go nuts when they are being moved and end up all over the place.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46309 Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
Trying to upstream this wine-staging patch, which has been in staging for a long time, and also in Proton. I've also personally been using it for a year.
Since the coordinates we need are absolute screen coordinates anyway, it makes sense as well.
dlls/winex11.drv/mouse.c | 106 +++++++++++++++++++++++++-------------- 1 file changed, 68 insertions(+), 38 deletions(-)
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index dd25f8b..b18c913 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -587,6 +587,46 @@ static BOOL is_old_motion_event( unsigned long serial ) }
+/*********************************************************************** + * map_event_coords + * + * Map the input event coordinates so they're relative to the desktop. + */ +static POINT map_event_coords(const XButtonEvent *event, HWND hwnd) +{ + POINT pt = { event->x, event->y }; + struct x11drv_win_data *data; + + if (event->window == root_window) + pt = root_to_virtual_screen(event->x, event->y); + + if ((data = get_win_data(hwnd))) + { + if (event->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 (event->root == root_window && event->same_screen && data->managed) + { + /* Try to use root coordinates, unless the window is at the (0,0) + position on the desktop to workaround full-screen or apps like + vst-bridge which reparent the window, so they don't break. */ + if (pt.x != event->x || pt.y != event->y) + pt = root_to_virtual_screen(event->x_root, event->y_root); + } + release_win_data(data); + } + + return pt; +} + + /*********************************************************************** * send_mouse_input * @@ -595,7 +635,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;
@@ -618,32 +657,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()) { @@ -658,8 +679,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 ) { @@ -673,8 +693,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 ); }
@@ -1674,13 +1692,16 @@ 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 = map_event_coords(event, hwnd); + + 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 ); @@ -1700,13 +1721,16 @@ 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 = map_event_coords(event, hwnd); + + 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 ); @@ -1724,12 +1748,15 @@ BOOL X11DRV_MotionNotify( HWND hwnd, XEvent *xev ) { XMotionEvent *event = &xev->xmotion; INPUT input; + POINT pt; + + pt = map_event_coords((XButtonEvent*)event, hwnd);
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 ); @@ -1752,6 +1779,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 );
@@ -1759,8 +1787,10 @@ 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 = map_event_coords((XButtonEvent*)event, hwnd); + + 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 );