hw/xwin: More closely follow ICCCM for setting input focus

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>
This commit is contained in:
Jon TURNEY 2013-06-27 23:56:23 +01:00
parent 0fc84a2bb6
commit b634e90989

View File

@ -111,6 +111,7 @@ typedef struct _WMInfo {
WMMsgQueueRec wmMsgQueue;
Atom atmWmProtos;
Atom atmWmDelete;
Atom atmWmTakeFocus;
Atom atmPrivMap;
Bool fAllowOtherWM;
} WMInfoRec, *WMInfoPtr;
@ -452,6 +453,27 @@ GetWindowName(Display * pDisplay, Window iWin, char **ppWindowName)
*ppWindowName = pszWindowName;
}
/*
* 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);