summaryrefslogtreecommitdiffstats
path: root/unix/tkUnixFocus.c
blob: 479ef61f2410dc3c4abbf0b284c9f38cee1dd8ff (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
149
150
151
152
153
/*
 * tkUnixFocus.c --
 *
 *	This file contains platform specific functions 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.5 2005/10/21 01:51:45 dkf Exp $
 */

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


/*
 *----------------------------------------------------------------------
 *
 * TkpChangeFocus --
 *
 *	This function 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 function 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 function.
 *
 *----------------------------------------------------------------------
 */

int
TkpChangeFocus(
    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, NULL,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;
}

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */