1/*
2 * tkUnixFocus.c --
3 *
4 *	This file contains platform specific procedures that manage
5 *	focus for Tk.
6 *
7 * Copyright (c) 1997 Sun Microsystems, Inc.
8 *
9 * See the file "license.terms" for information on usage and redistribution
10 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 *
12 * RCS: @(#) $Id: tkUnixFocus.c,v 1.3 1999/04/16 01:51:46 stanton Exp $
13 */
14
15#include "tkInt.h"
16#include "tkPort.h"
17#include "tkUnixInt.h"
18
19
20/*
21 *----------------------------------------------------------------------
22 *
23 * TkpChangeFocus --
24 *
25 *	This procedure is invoked to move the official X focus from
26 *	one window to another.
27 *
28 * Results:
29 *	The return value is the serial number of the command that
30 *	changed the focus.  It may be needed by the caller to filter
31 *	out focus change events that were queued before the command.
32 *	If the procedure doesn't actually change the focus then
33 *	it returns 0.
34 *
35 * Side effects:
36 *	The official X focus window changes;  the application's focus
37 *	window isn't changed by this procedure.
38 *
39 *----------------------------------------------------------------------
40 */
41
42int
43TkpChangeFocus(winPtr, force)
44    TkWindow *winPtr;		/* Window that is to receive the X focus. */
45    int force;			/* Non-zero means claim the focus even
46				 * if it didn't originally belong to
47				 * topLevelPtr's application. */
48{
49    TkDisplay *dispPtr = winPtr->dispPtr;
50    Tk_ErrorHandler errHandler;
51    Window window, root, parent, *children;
52    unsigned int numChildren, serial;
53    TkWindow *winPtr2;
54    int dummy;
55
56    /*
57     * Don't set the X focus to a window that's marked
58     * override-redirect.  This is a hack to avoid problems with menus
59     * under olvwm: if we move the focus then the focus can get lost
60     * during keyboard traversal.  Fortunately, we don't really need to
61     * move the focus for menus: events will still find their way to the
62     * focus window, and menus aren't decorated anyway so the window
63     * manager doesn't need to hear about the focus change in order to
64     * redecorate the menu.
65     */
66
67    serial = 0;
68    if (winPtr->atts.override_redirect) {
69	return serial;
70    }
71
72    /*
73     * Check to make sure that the focus is still in one of the windows
74     * of this application or one of their descendants.  Furthermore,
75     * grab the server to make sure that the focus doesn't change in the
76     * middle of this operation.
77     */
78
79    XGrabServer(dispPtr->display);
80    if (!force) {
81	/*
82	 * Find the focus window, then see if it or one of its ancestors
83	 * is a window in our application (it's possible that the focus
84	 * window is in an embedded application, which may or may not be
85	 * in the same process.
86	 */
87
88	XGetInputFocus(dispPtr->display, &window, &dummy);
89	while (1) {
90	    winPtr2 = (TkWindow *) Tk_IdToWindow(dispPtr->display, window);
91	    if ((winPtr2 != NULL) && (winPtr2->mainPtr == winPtr->mainPtr)) {
92		break;
93	    }
94	    if ((window == PointerRoot) || (window == None)) {
95		goto done;
96	    }
97	    XQueryTree(dispPtr->display, window, &root, &parent, &children,
98		    &numChildren);
99	    if (children != NULL) {
100		XFree((void *) children);
101	    }
102	    if (parent == root) {
103		goto done;
104	    }
105	    window = parent;
106	}
107    }
108
109    /*
110     * Tell X to change the focus.  Ignore errors that occur when changing
111     * the focus:  it is still possible that the window we're focussing
112     * to could have gotten unmapped, which will generate an error.
113     */
114
115    errHandler = Tk_CreateErrorHandler(dispPtr->display, -1, -1, -1,
116	    (Tk_ErrorProc *) NULL, (ClientData) NULL);
117    if (winPtr->window == None) {
118	panic("ChangeXFocus got null X window");
119    }
120    XSetInputFocus(dispPtr->display, winPtr->window, RevertToParent,
121	    CurrentTime);
122    Tk_DeleteErrorHandler(errHandler);
123
124    /*
125     * Remember the current serial number for the X server and issue
126     * a dummy server request.  This marks the position at which we
127     * changed the focus, so we can distinguish FocusIn and FocusOut
128     * events on either side of the mark.
129     */
130
131    serial = NextRequest(winPtr->display);
132    XNoOp(winPtr->display);
133
134    done:
135    XUngrabServer(dispPtr->display);
136
137    /*
138     * After ungrabbing the server, it's important to flush the output
139     * immediately so that the server sees the ungrab command.  Otherwise
140     * we might do something else that needs to communicate with the
141     * server (such as invoking a subprocess that needs to do I/O to
142     * the screen); if the ungrab command is still sitting in our
143     * output buffer, we could deadlock.
144     */
145
146    XFlush(dispPtr->display);
147    return serial;
148}
149