Improving Wine focus handling

Jukka Heinonen jhei at iki.fi
Sun Oct 21 09:28:32 CDT 2001


In order to make Wine ICCCM compliant and to fix focus
livelock problems, it is necessary for Wine to use
WM_TAKE_FOCUS protocol. This makes it necessary to 
stop using override-redirect for non-transient windows.
I have included below a patch that does both of these things.
The patch seems to work fine even with full-screen games
and it does make Wine focus handling to cooperate a lot better
with window managers. However, I feel reluctant to submit this 
patch to wine-patches, at least before I do get some feedback
about the patch.

Here are some notes about the patch:
- Done against the latest Wine cvs tree,
  file wine/dlls/ntdll/debugtools.c has some bugs
  that might need to be fixed first, though.
- There have recently been lots of changes in directory
  wine/windows/x11drv and I'm not sure whether the changes 
  are over yet.
- Alexandre did mention that this fix is in his todo list,
  perhaps he has already planned a better way to do this fix.
- Menus do work but activating them makes parent window lose focus.
  This does not look particularly nice.
- It is highly likely that every single case of Wine
  using XSetInputFocus breaks ICCCM compliance. These need fixing.
- It might be a good idea to make X11DRV_SetFocus call EVENT_TakeFocus,
  since there seems to be much shared code.
- Mouse clicks over non-active windows should not automatically
  result into focus transfer. In these cases, Windows sends a special
  message that makes certain that the window really wants to gain focus.
  However, I don't think this is very important thing to do.

Index: wine/windows/x11drv/event.c
===================================================================
RCS file: /home/wine/wine/windows/x11drv/event.c,v
retrieving revision 1.112
diff -u -r1.112 event.c
--- wine/windows/x11drv/event.c 2001/10/18 21:38:59     1.112
+++ wine/windows/x11drv/event.c 2001/10/21 12:26:07
@@ -42,6 +42,7 @@
 
 extern Atom wmProtocols;
 extern Atom wmDeleteWindow;
+extern Atom wmTakeFocus;
 extern Atom dndProtocol;
 extern Atom dndSelection;
 
@@ -116,7 +117,59 @@
 static INPUT_TYPE current_input_type = X11DRV_INPUT_ABSOLUTE;
 static BOOL in_transition = FALSE; /* This is not used as for today */
 
+/***********************************************************************
+ *          EVENT_TakeFocus 
+ */
+static void EVENT_TakeFocus( HWND hWnd, XAnyEvent *event, Time time )
+{
+    XWindowAttributes win_attr;
+    BOOL              bIsDisabled;
+    Window            focusWindow;
+    HWND              focusHandle;
+
+    TRACE("called, X11 window is %08lx, window handle is %04x.\n",
+         event->window, hWnd);
+
+    if (!hWnd) return;
+
+    bIsDisabled = GetWindowLongA( hWnd, GWL_STYLE ) & WS_DISABLED;
+
+    /* If the window has been disabled,
+     * revert the X focus back to the last focus window. This is to disallow
+     * the window manager from switching focus away while the app is
+     * in a modal state.
+     */
+    if (bIsDisabled)
+        focusWindow = glastXFocusWin;
+    else
+        focusWindow = event->window;
+
+    wine_tsx11_lock();
+
+    /* Change focus only if focus window is registered and viewable. */
+    if (XFindContext( event->display, focusWindow, winContext, (char **)&focusHandle ) ||
+        !XGetWindowAttributes( event->display, focusWindow, &win_attr ) ||
+        (win_attr.map_state != IsViewable))
+    {
+        wine_tsx11_unlock();
+        return;
+    }
+
+    TRACE("Set X11 focus to: %08lx\n", focusWindow);
+    XSetInputFocus( event->display, focusWindow, RevertToParent, time );
+
+    wine_tsx11_unlock();
+
+    glastXFocusWin = focusWindow;
 
+    if (focusHandle != GetForegroundWindow())
+    {
+        TRACE("Set foreground window to: %04x\n", focusHandle);
+        SetForegroundWindow( focusHandle );
+    }
+}
+
+
 /***********************************************************************
  *           process_events
  */
@@ -274,6 +327,7 @@
       break;
 
     case ButtonPress:
+      EVENT_TakeFocus(hWnd, (XAnyEvent*)event, ((XButtonEvent*)event)->time);
       X11DRV_ButtonPress( hWnd, (XButtonEvent*)event );
       break;
 
@@ -354,39 +408,6 @@
  */
 static void EVENT_FocusIn( HWND hWnd, XFocusChangeEvent *event )
 {
-    WND *pWndLastFocus;
-    XWindowAttributes win_attr;
-    BOOL bIsDisabled;
-
-    if (!hWnd) return;
-
-    bIsDisabled = GetWindowLongA( hWnd, GWL_STYLE ) & WS_DISABLED;
-
-    /* If the window has been disabled and we are in managed mode,
-       * revert the X focus back to the last focus window. This is to disallow
-       * the window manager from switching focus away while the app is
-       * in a modal state.
-       */
-    if ( Options.managed && bIsDisabled && glastXFocusWin)
-    {
-        /* Change focus only if saved focus window is registered and viewable */
-        wine_tsx11_lock();
-        if (XFindContext( event->display, glastXFocusWin, winContext,
-                           (char **)&pWndLastFocus ) == 0 )
-        {
-            if (XGetWindowAttributes( event->display, glastXFocusWin, &win_attr ) &&
-                (win_attr.map_state == IsViewable) )
-            {
-                XSetInputFocus( event->display, glastXFocusWin, RevertToParent, CurrentTime );
-                wine_tsx11_unlock();
-                return;
-            }
-        }
-        wine_tsx11_unlock();
-    }
-
-    if (event->detail != NotifyPointer && hWnd != GetForegroundWindow())
-        SetForegroundWindow( hWnd );
 }
 
 
@@ -397,10 +418,7 @@
  */
 static void EVENT_FocusOut( HWND hWnd, XFocusChangeEvent *event )
 {
-    /* Save the last window which had the focus */
-    glastXFocusWin = event->window;
     if (!hWnd) return;
-    if (GetWindowLongA( hWnd, GWL_STYLE ) & WS_DISABLED) glastXFocusWin = 0;
 
     if (event->detail != NotifyPointer && hWnd == GetForegroundWindow())
     {
@@ -1252,6 +1270,10 @@
             EVENT_DropFromOffiX(hWnd, event);
         else if (event->data.l[0] == DndURL)
             EVENT_DropURLs(hWnd, event);
+    }
+    else if (event->message_type == wmProtocols && ((Atom)event->data.l[0]) == wmTakeFocus)
+    {
+        EVENT_TakeFocus(hWnd, (XAnyEvent*)event, (Time)event->data.l[1]);
     }
     else {
 #if 0


Index: wine/dlls/x11drv/window.c
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/window.c,v
retrieving revision 1.27
diff -u -r1.27 window.c
--- wine/dlls/x11drv/window.c   2001/10/18 21:38:59     1.27
+++ wine/dlls/x11drv/window.c   2001/10/21 12:27:57
@@ -44,6 +44,7 @@
 Atom wmChangeState = None;
 Atom kwmDockWindow = None;
 Atom _kde_net_wm_system_tray_window_for = None; /* KDE 2 Final */
+Atom wmMotifHints = None;
 
 static LPCSTR whole_window_atom;
 static LPCSTR client_window_atom;
@@ -109,7 +110,7 @@
     if (managed) win->dwExStyle |= WS_EX_MANAGED;
     else win->dwExStyle &= ~WS_EX_MANAGED;
 
-    attr->override_redirect = !managed;
+    attr->override_redirect = 0; // FIXME: true for transient windows (menus)
     attr->colormap          = X11DRV_PALETTE_PaletteXColormap;
     attr->save_under        = ((win->clsStyle & CS_SAVEBITS) != 0);
     attr->cursor            = None;
@@ -332,6 +333,16 @@
 
     wine_tsx11_lock();
 
+    /* motif hints */
+    if(!(win->dwExStyle & WS_EX_MANAGED)) 
+    {
+        INT32 hints[5] = {2,0,0,0,0};
+        XChangeProperty( display, data->whole_window,
+                         wmMotifHints, wmMotifHints,
+                         32, PropModeReplace,
+                         (char*)hints, 5 );
+    }
+
     /* wm protocols */
     i = 0;
     protocols[i++] = wmDeleteWindow;
@@ -594,8 +605,8 @@
     winContext     = XUniqueContext();
     wmProtocols    = XInternAtom( display, "WM_PROTOCOLS", False );
     wmDeleteWindow = XInternAtom( display, "WM_DELETE_WINDOW", False );
-/*    wmTakeFocus    = XInternAtom( display, "WM_TAKE_FOCUS", False );*/
-    wmTakeFocus = 0;  /* not yet */
+    wmTakeFocus    = XInternAtom( display, "WM_TAKE_FOCUS", False );
+    wmMotifHints   =  XInternAtom( display, "_MOTIF_WM_HINTS", False );
     dndProtocol = XInternAtom( display, "DndProtocol" , False );
     dndSelection = XInternAtom( display, "DndSelection" , False );
     wmChangeState = XInternAtom (display, "WM_CHANGE_STATE", False);



-- 
Jukka Heinonen <http://www.iki.fi/jhei/>




More information about the wine-devel mailing list