Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winex11.drv/event.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 6054f645c47..61dd56dcb4c 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -749,6 +749,14 @@ static const char * const focus_details[] = "NotifyDetailNone" };
+static const char * const focus_modes[] = +{ + "NotifyNormal", + "NotifyGrab", + "NotifyUngrab", + "NotifyWhileGrabbed" +}; + /********************************************************************** * X11DRV_FocusIn */ @@ -759,7 +767,7 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev )
if (!hwnd) return FALSE;
- TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] ); + TRACE( "win %p xwin %lx detail=%s mode=%s\n", hwnd, event->window, focus_details[event->detail], focus_modes[event->mode] );
if (event->detail == NotifyPointer) return FALSE; if (hwnd == GetDesktopWindow()) return FALSE; @@ -839,7 +847,7 @@ static BOOL X11DRV_FocusOut( HWND hwnd, XEvent *xev ) { XFocusChangeEvent *event = &xev->xfocus;
- TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] ); + TRACE( "win %p xwin %lx detail=%s mode=%s\n", hwnd, event->window, focus_details[event->detail], focus_modes[event->mode] );
if (event->detail == NotifyPointer) {
Several of the most used WMs are sending FocusOut events with NotifyGrab mode whenever they take a keyboard grab, which is the case for example when window is being moved from a title bar click, when screen is locked or when activity view is brought up. When releasing the grab they then send FocusIn events with NotifyUngrabbed mode.
However, XGetInputFocus call still returns that the current window still has the input focus, so Wine was not changing the foregound window.
This was causing various number of focus and mouse grab issues when locking screen, moving windows with their title bar, or using the super key to bring up application overview.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winex11.drv/event.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 61dd56dcb4c..42b74b0a973 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -794,9 +794,9 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) /********************************************************************** * focus_out */ -static void focus_out( Display *display , HWND hwnd ) +static void focus_out( Display *display , HWND hwnd, BOOL grabbed ) { - HWND hwnd_tmp; + HWND focus_hwnd; Window focus_win; int revert; XIC xic; @@ -820,11 +820,11 @@ static void focus_out( Display *display , HWND hwnd ) XGetInputFocus( display, &focus_win, &revert ); if (focus_win) { - if (XFindContext( display, focus_win, winContext, (char **)&hwnd_tmp ) != 0) + if (XFindContext( display, focus_win, winContext, (char **)&focus_hwnd ) != 0) focus_win = 0; }
- if (!focus_win) + if (!focus_win || (grabbed && focus_hwnd == hwnd)) { /* Abey : 6-Oct-99. Check again if the focus out window is the Foreground window, because in most cases the messages sent @@ -855,7 +855,7 @@ static BOOL X11DRV_FocusOut( HWND hwnd, XEvent *xev ) return TRUE; } if (!hwnd) return FALSE; - focus_out( event->display, hwnd ); + focus_out( event->display, hwnd, event->mode == NotifyGrab ); return TRUE; }
@@ -1665,12 +1665,12 @@ static void handle_xembed_protocol( HWND hwnd, XClientMessageEvent *event )
case XEMBED_WINDOW_DEACTIVATE: TRACE( "win %p/%lx XEMBED_WINDOW_DEACTIVATE message\n", hwnd, event->window ); - focus_out( event->display, GetAncestor( hwnd, GA_ROOT ) ); + focus_out( event->display, GetAncestor( hwnd, GA_ROOT ), FALSE ); break;
case XEMBED_FOCUS_OUT: TRACE( "win %p/%lx XEMBED_FOCUS_OUT message\n", hwnd, event->window ); - focus_out( event->display, GetAncestor( hwnd, GA_ROOT ) ); + focus_out( event->display, GetAncestor( hwnd, GA_ROOT ), FALSE ); break;
case XEMBED_MODALITY_ON:
I'll update this one, to properly handle NotifyWhileGrabbed modes as well.
On Mon, 2019-06-10 at 13:43 +0200, Rémi Bernon wrote:
Several of the most used WMs are sending FocusOut events with NotifyGrab mode whenever they take a keyboard grab, which is the case for example when window is being moved from a title bar click, when screen is locked or when activity view is brought up. When releasing the grab they then send FocusIn events with NotifyUngrabbed mode.
However, XGetInputFocus call still returns that the current window still has the input focus, so Wine was not changing the foregound window.
This was causing various number of focus and mouse grab issues when locking screen, moving windows with their title bar, or using the super key to bring up application overview.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com
dlls/winex11.drv/event.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 61dd56dcb4c..42b74b0a973 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -794,9 +794,9 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) /*******************************************************************
focus_out
*/ -static void focus_out( Display *display , HWND hwnd ) +static void focus_out( Display *display , HWND hwnd, BOOL grabbed ) {
- HWND hwnd_tmp;
- HWND focus_hwnd; Window focus_win; int revert; XIC xic;
@@ -820,11 +820,11 @@ static void focus_out( Display *display , HWND hwnd ) XGetInputFocus( display, &focus_win, &revert ); if (focus_win) {
if (XFindContext( display, focus_win, winContext, (char
**)&hwnd_tmp ) != 0)
if (XFindContext( display, focus_win, winContext, (char
**)&focus_hwnd ) != 0) focus_win = 0; }
- if (!focus_win)
- if (!focus_win || (grabbed && focus_hwnd == hwnd)) { /* Abey : 6-Oct-99. Check again if the focus out window is
the Foreground window, because in most cases the messages sent @@ -855,7 +855,7 @@ static BOOL X11DRV_FocusOut( HWND hwnd, XEvent *xev ) return TRUE; } if (!hwnd) return FALSE;
- focus_out( event->display, hwnd );
- focus_out( event->display, hwnd, event->mode == NotifyGrab ); return TRUE;
}
@@ -1665,12 +1665,12 @@ static void handle_xembed_protocol( HWND hwnd, XClientMessageEvent *event )
case XEMBED_WINDOW_DEACTIVATE: TRACE( "win %p/%lx XEMBED_WINDOW_DEACTIVATE message\n",
hwnd, event->window );
focus_out( event->display, GetAncestor( hwnd, GA_ROOT ) );
focus_out( event->display, GetAncestor( hwnd, GA_ROOT ),
FALSE ); break;
case XEMBED_FOCUS_OUT: TRACE( "win %p/%lx XEMBED_FOCUS_OUT message\n", hwnd, event-
window );
focus_out( event->display, GetAncestor( hwnd, GA_ROOT ) );
focus_out( event->display, GetAncestor( hwnd, GA_ROOT ),
FALSE ); break;
case XEMBED_MODALITY_ON:
Unfortunately, this will make some games entering minimized state at startup. I am guessing the extra FocusOut event with NotifyGrab make it lost foreground, and then losing foreground makes the game minimized automatically.
I tested it on GNOME 3.32.1 with Project CARS with this patch applied to Proton tip.
On 6/10/19 7:43 PM, Rémi Bernon wrote:
Several of the most used WMs are sending FocusOut events with NotifyGrab mode whenever they take a keyboard grab, which is the case for example when window is being moved from a title bar click, when screen is locked or when activity view is brought up. When releasing the grab they then send FocusIn events with NotifyUngrabbed mode.
However, XGetInputFocus call still returns that the current window still has the input focus, so Wine was not changing the foregound window.
This was causing various number of focus and mouse grab issues when locking screen, moving windows with their title bar, or using the super key to bring up application overview.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com
dlls/winex11.drv/event.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 61dd56dcb4c..42b74b0a973 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -794,9 +794,9 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) /**********************************************************************
focus_out
*/ -static void focus_out( Display *display , HWND hwnd ) +static void focus_out( Display *display , HWND hwnd, BOOL grabbed ) {
- HWND hwnd_tmp;
- HWND focus_hwnd; Window focus_win; int revert; XIC xic;
@@ -820,11 +820,11 @@ static void focus_out( Display *display , HWND hwnd ) XGetInputFocus( display, &focus_win, &revert ); if (focus_win) {
if (XFindContext( display, focus_win, winContext, (char **)&hwnd_tmp ) != 0)
}if (XFindContext( display, focus_win, winContext, (char **)&focus_hwnd ) != 0) focus_win = 0;
- if (!focus_win)
- if (!focus_win || (grabbed && focus_hwnd == hwnd)) { /* Abey : 6-Oct-99. Check again if the focus out window is the Foreground window, because in most cases the messages sent
@@ -855,7 +855,7 @@ static BOOL X11DRV_FocusOut( HWND hwnd, XEvent *xev ) return TRUE; } if (!hwnd) return FALSE;
- focus_out( event->display, hwnd );
- focus_out( event->display, hwnd, event->mode == NotifyGrab ); return TRUE;
}
@@ -1665,12 +1665,12 @@ static void handle_xembed_protocol( HWND hwnd, XClientMessageEvent *event )
case XEMBED_WINDOW_DEACTIVATE: TRACE( "win %p/%lx XEMBED_WINDOW_DEACTIVATE message\n", hwnd, event->window );
focus_out( event->display, GetAncestor( hwnd, GA_ROOT ) );
focus_out( event->display, GetAncestor( hwnd, GA_ROOT ), FALSE ); break;
case XEMBED_FOCUS_OUT: TRACE( "win %p/%lx XEMBED_FOCUS_OUT message\n", hwnd, event->window );
focus_out( event->display, GetAncestor( hwnd, GA_ROOT ) );
focus_out( event->display, GetAncestor( hwnd, GA_ROOT ), FALSE ); break;
case XEMBED_MODALITY_ON:
This could be a situation where the user has clicked on a window title bar (eventually the current window). Grabbing the cursor then will warp it and move the window with it.
Also, as described in the previous commit, FocusOut events with NotifyGrab mode is not sent by all WMs, and there is also a small delay between FocusIn with NotifyNormal mode event that is sent as soon as the window is focused, and FocusOut with NotifyGrab mode event that is sent when window is moved by the WM, where the cursor could still be moved by the application.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winex11.drv/mouse.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+)
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index f737a306a56..478adcf26a9 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -368,6 +368,31 @@ static void disable_xinput2(void) #endif }
+static BOOL is_safe_to_grab_or_warp_pointer(void) +{ + struct x11drv_thread_data *data = x11drv_thread_data(); + Window xfocus, xroot, xparent, xchild, *xchildren = NULL; + int revert, root_x, root_y, win_x, win_y; + unsigned int nchildren, xstate; + BOOL ret = TRUE; + + /* If we aren't clipping the mouse already and if a mouse button is + * pressed outside of the focused window, then it isn't safe to warp + * or grab the mouse as it could be moving a window. + */ + XGetInputFocus( data->display, &xfocus, &revert ); + if (!clipping_cursor && + XQueryTree( data->display, xfocus, &xroot, &xparent, &xchildren, &nchildren ) && + XQueryPointer( data->display, xparent, &xroot, &xchild, &root_x, &root_y, &win_x, &win_y, &xstate ) && + xchild != xfocus && + (xstate & (Button1Mask | Button2Mask | Button3Mask))) + ret = FALSE; + + if (xchildren) + XFree(xchildren); + + return ret; +}
/*********************************************************************** * grab_clipping_window @@ -403,6 +428,12 @@ static BOOL grab_clipping_window( const RECT *clip ) return TRUE; }
+ if (!is_safe_to_grab_or_warp_pointer()) + { + WARN( "Button is pressed outside of focused window, refusing to clip to %s\n", wine_dbgstr_rect(clip) ); + return FALSE; + } + TRACE( "clipping to %s win %lx\n", wine_dbgstr_rect(clip), clip_window );
if (!data->clip_hwnd) XUnmapWindow( data->display, clip_window ); @@ -1430,6 +1461,12 @@ BOOL CDECL X11DRV_SetCursorPos( INT x, INT y ) struct x11drv_thread_data *data = x11drv_init_thread_data(); POINT pos = virtual_screen_to_root( x, y );
+ if (!is_safe_to_grab_or_warp_pointer()) + { + WARN( "Button is pressed outside of focused window, refusing to warp to %u, %u\n", pos.x, pos.y ); + return FALSE; + } + XWarpPointer( data->display, root_window, root_window, 0, 0, 0, 0, pos.x, pos.y ); data->warp_serial = NextRequest( data->display ); XNoOp( data->display );