summaryrefslogtreecommitdiffstats
path: root/unix/tkUnixFocus.c
blob: 709354463a79b85c100d627a5cb658dc91364e17 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/* 
 * 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.
 *
 * RCS: @(#) $Id: tkUnixFocus.c,v 1.4 2004/01/13 02:06:01 davygrvy Exp $
 */

#include "tkInt.h"
#include "tkPort.h"
#include "tkUnixInt.h"


/*
 *----------------------------------------------------------------------
 *
 * 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) {
	Tcl_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;
}