150 lines
4.5 KiB
C
150 lines
4.5 KiB
C
/*
|
||
* tkUnixFocus.c --
|
||
*
|
||
* This file contains platform specific procedures that manage
|
||
* focus for Tk.
|
||
*
|
||
* Copyright (c) 1997 Sun Microsystems, Inc.
|
||
*
|
||
* See the file "license.terms" for information on usage and redistribution
|
||
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||
*
|
||
* SCCS: @(#) tkUnixFocus.c 1.9 97/10/31 09:54:04
|
||
*/
|
||
|
||
#include "tkInt.h"
|
||
#include "tkPort.h"
|
||
#include "tkUnixInt.h"
|
||
|
||
extern int tclFocusDebug;
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkpChangeFocus --
|
||
*
|
||
* This procedure is invoked to move the official X focus from
|
||
* one window to another.
|
||
*
|
||
* Results:
|
||
* The return value is the serial number of the command that
|
||
* changed the focus. It may be needed by the caller to filter
|
||
* out focus change events that were queued before the command.
|
||
* If the procedure doesn't actually change the focus then
|
||
* it returns 0.
|
||
*
|
||
* Side effects:
|
||
* The official X focus window changes; the application's focus
|
||
* window isn't changed by this procedure.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
int
|
||
TkpChangeFocus(winPtr, force)
|
||
TkWindow *winPtr; /* Window that is to receive the X focus. */
|
||
int force; /* Non-zero means claim the focus even
|
||
* if it didn't originally belong to
|
||
* topLevelPtr's application. */
|
||
{
|
||
TkDisplay *dispPtr = winPtr->dispPtr;
|
||
Tk_ErrorHandler errHandler;
|
||
Window window, root, parent, *children;
|
||
unsigned int numChildren, serial;
|
||
TkWindow *winPtr2;
|
||
int dummy;
|
||
|
||
/*
|
||
* Don't set the X focus to a window that's marked
|
||
* override-redirect. This is a hack to avoid problems with menus
|
||
* under olvwm: if we move the focus then the focus can get lost
|
||
* during keyboard traversal. Fortunately, we don't really need to
|
||
* move the focus for menus: events will still find their way to the
|
||
* focus window, and menus aren't decorated anyway so the window
|
||
* manager doesn't need to hear about the focus change in order to
|
||
* redecorate the menu.
|
||
*/
|
||
|
||
serial = 0;
|
||
if (winPtr->atts.override_redirect) {
|
||
return serial;
|
||
}
|
||
|
||
/*
|
||
* Check to make sure that the focus is still in one of the windows
|
||
* of this application or one of their descendants. Furthermore,
|
||
* grab the server to make sure that the focus doesn't change in the
|
||
* middle of this operation.
|
||
*/
|
||
|
||
XGrabServer(dispPtr->display);
|
||
if (!force) {
|
||
/*
|
||
* Find the focus window, then see if it or one of its ancestors
|
||
* is a window in our application (it's possible that the focus
|
||
* window is in an embedded application, which may or may not be
|
||
* in the same process.
|
||
*/
|
||
|
||
XGetInputFocus(dispPtr->display, &window, &dummy);
|
||
while (1) {
|
||
winPtr2 = (TkWindow *) Tk_IdToWindow(dispPtr->display, window);
|
||
if ((winPtr2 != NULL) && (winPtr2->mainPtr == winPtr->mainPtr)) {
|
||
break;
|
||
}
|
||
if ((window == PointerRoot) || (window == None)) {
|
||
goto done;
|
||
}
|
||
XQueryTree(dispPtr->display, window, &root, &parent, &children,
|
||
&numChildren);
|
||
if (children != NULL) {
|
||
XFree((void *) children);
|
||
}
|
||
if (parent == root) {
|
||
goto done;
|
||
}
|
||
window = parent;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Tell X to change the focus. Ignore errors that occur when changing
|
||
* the focus: it is still possible that the window we're focussing
|
||
* to could have gotten unmapped, which will generate an error.
|
||
*/
|
||
|
||
errHandler = Tk_CreateErrorHandler(dispPtr->display, -1, -1, -1,
|
||
(Tk_ErrorProc *) NULL, (ClientData) NULL);
|
||
if (winPtr->window == None) {
|
||
panic("ChangeXFocus got null X window");
|
||
}
|
||
XSetInputFocus(dispPtr->display, winPtr->window, RevertToParent,
|
||
CurrentTime);
|
||
Tk_DeleteErrorHandler(errHandler);
|
||
|
||
/*
|
||
* Remember the current serial number for the X server and issue
|
||
* a dummy server request. This marks the position at which we
|
||
* changed the focus, so we can distinguish FocusIn and FocusOut
|
||
* events on either side of the mark.
|
||
*/
|
||
|
||
serial = NextRequest(winPtr->display);
|
||
XNoOp(winPtr->display);
|
||
|
||
done:
|
||
XUngrabServer(dispPtr->display);
|
||
|
||
/*
|
||
* After ungrabbing the server, it's important to flush the output
|
||
* immediately so that the server sees the ungrab command. Otherwise
|
||
* we might do something else that needs to communicate with the
|
||
* server (such as invoking a subprocess that needs to do I/O to
|
||
* the screen); if the ungrab command is still sitting in our
|
||
* output buffer, we could deadlock.
|
||
*/
|
||
|
||
XFlush(dispPtr->display);
|
||
return serial;
|
||
}
|