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