hw/xwin: More closely follow ICCCM for setting input focus
authorJon TURNEY <jon.turney@dronecode.org.uk>
Thu, 27 Jun 2013 22:56:23 +0000 (23:56 +0100)
committerJon TURNEY <jon.turney@dronecode.org.uk>
Mon, 3 Mar 2014 14:33:09 +0000 (14:33 +0000)
In multiwindow mode, more closely follow ICCCM section 4.1.7 when setting X
input focus to a window when the native Windows window acquires input focus:

- If InputHint is FALSE, don't use XSetInputFocus()
- If the window supports the WM_TAKE_FOCUS protocol, send a WM_TAKE_FOCUS message

This helps JDK 1.7 clients acquire the focus correctly.

Also, factor out checking client support for a given WM_PROTOCOLS protocol as a
separate function.

Signed-off-by: Jon TURNEY <jon.turney@dronecode.org.uk>
Reviewed-by: Colin Harrison <colin.harrison@virgin.net>
hw/xwin/winmultiwindowwm.c

index c6da777..9f12521 100644 (file)
@@ -111,6 +111,7 @@ typedef struct _WMInfo {
     WMMsgQueueRec wmMsgQueue;
     Atom atmWmProtos;
     Atom atmWmDelete;
+    Atom atmWmTakeFocus;
     Atom atmPrivMap;
     Bool fAllowOtherWM;
 } WMInfoRec, *WMInfoPtr;
@@ -453,6 +454,27 @@ GetWindowName(Display * pDisplay, Window iWin, char **ppWindowName)
 }
 
 /*
+ * Does the client support the specified WM_PROTOCOLS protocol?
+ */
+
+static Bool
+IsWmProtocolAvailable(Display * pDisplay, Window iWindow, Atom atmProtocol)
+{
+  int i, n, found = 0;
+  Atom *protocols;
+
+  if (XGetWMProtocols(pDisplay, iWindow, &protocols, &n)) {
+    for (i = 0; i < n; ++i)
+      if (protocols[i] == atmProtocol)
+        ++found;
+
+    XFree(protocols);
+  }
+
+  return found > 0;
+}
+
+/*
  * Send a message to the X server from the WM thread
  */
 
@@ -805,21 +827,10 @@ winMultiWindowWMProc(void *pArg)
             ErrorF("\tWM_WM_KILL\n");
 #endif
             {
-                int i, n, found = 0;
-                Atom *protocols;
-
-                /* --- */
-                if (XGetWMProtocols(pWMInfo->pDisplay,
-                                    pNode->msg.iWindow, &protocols, &n)) {
-                    for (i = 0; i < n; ++i)
-                        if (protocols[i] == pWMInfo->atmWmDelete)
-                            ++found;
-
-                    XFree(protocols);
-                }
-
                 /* --- */
-                if (found)
+                if (IsWmProtocolAvailable(pWMInfo->pDisplay,
+                                          pNode->msg.iWindow,
+                                          pWMInfo->atmWmDelete))
                     SendXMessage(pWMInfo->pDisplay,
                                  pNode->msg.iWindow,
                                  pWMInfo->atmWmProtos, pWMInfo->atmWmDelete);
@@ -832,11 +843,39 @@ winMultiWindowWMProc(void *pArg)
 #if CYGMULTIWINDOW_DEBUG
             ErrorF("\tWM_WM_ACTIVATE\n");
 #endif
-
             /* Set the input focus */
-            XSetInputFocus(pWMInfo->pDisplay,
-                           pNode->msg.iWindow,
-                           RevertToPointerRoot, CurrentTime);
+
+            /*
+               ICCCM 4.1.7 is pretty opaque, but it appears that the rules are
+               actually quite simple:
+               -- the WM_HINTS input field determines whether the WM should call
+               XSetInputFocus()
+               -- independently, the WM_TAKE_FOCUS protocol determines whether
+               the WM should send a WM_TAKE_FOCUS ClientMessage.
+            */
+            {
+              Bool neverFocus = FALSE;
+              XWMHints *hints = XGetWMHints(pWMInfo->pDisplay, pNode->msg.iWindow);
+
+              if (hints) {
+                if (hints->flags & InputHint)
+                  neverFocus = !hints->input;
+                XFree(hints);
+              }
+
+              if (!neverFocus)
+                XSetInputFocus(pWMInfo->pDisplay,
+                               pNode->msg.iWindow,
+                               RevertToPointerRoot, CurrentTime);
+
+              if (IsWmProtocolAvailable(pWMInfo->pDisplay,
+                                        pNode->msg.iWindow,
+                                        pWMInfo->atmWmTakeFocus))
+                SendXMessage(pWMInfo->pDisplay,
+                             pNode->msg.iWindow,
+                             pWMInfo->atmWmProtos, pWMInfo->atmWmTakeFocus);
+
+            }
             break;
 
         case WM_WM_NAME_EVENT:
@@ -1404,6 +1443,8 @@ winInitMultiWindowWM(WMInfoPtr pWMInfo, WMProcArgPtr pProcArg)
                                        "WM_PROTOCOLS", False);
     pWMInfo->atmWmDelete = XInternAtom(pWMInfo->pDisplay,
                                        "WM_DELETE_WINDOW", False);
+    pWMInfo->atmWmTakeFocus = XInternAtom(pWMInfo->pDisplay,
+                                       "WM_TAKE_FOCUS", False);
 
     pWMInfo->atmPrivMap = XInternAtom(pWMInfo->pDisplay,
                                       WINDOWSWM_NATIVE_HWND, False);