1 |
/* $Header$ */ |
2 |
|
3 |
/* |
4 |
* tkGrab.c -- |
5 |
* |
6 |
* This file provides procedures that implement grabs for Tk. |
7 |
* |
8 |
* Copyright (c) 1992-1994 The Regents of the University of California. |
9 |
* Copyright (c) 1994-1997 Sun Microsystems, Inc. |
10 |
* |
11 |
* See the file "license.terms" for information on usage and redistribution |
12 |
* of this file, and for a DISCLAIMER OF ALL WARRANTIES. |
13 |
* |
14 |
* RCS: @(#) $Id: tkgrab.c,v 1.1.1.1 2001/06/13 05:01:40 dtashley Exp $ |
15 |
*/ |
16 |
|
17 |
#include "tkPort.h" |
18 |
#include "tkInt.h" |
19 |
|
20 |
#if !defined(__WIN32__) && !defined(MAC_TCL) |
21 |
#include "tkUnixInt.h" |
22 |
#endif |
23 |
|
24 |
/* |
25 |
* The grab state machine has four states: ungrabbed, button pressed, |
26 |
* grabbed, and button pressed while grabbed. In addition, there are |
27 |
* three pieces of grab state information: the current grab window, |
28 |
* the current restrict window, and whether the mouse is captured. |
29 |
* |
30 |
* The current grab window specifies the point in the Tk window |
31 |
* heirarchy above which pointer events will not be reported. Any |
32 |
* window within the subtree below the grab window will continue to |
33 |
* receive events as normal. Events outside of the grab tree will be |
34 |
* reported to the grab window. |
35 |
* |
36 |
* If the current restrict window is set, then all pointer events will |
37 |
* be reported only to the restrict window. The restrict window is |
38 |
* normally set during an automatic button grab. |
39 |
* |
40 |
* The mouse capture state specifies whether the window system will |
41 |
* report mouse events outside of any Tk toplevels. This is set |
42 |
* during a global grab or an automatic button grab. |
43 |
* |
44 |
* The transitions between different states is given in the following |
45 |
* table: |
46 |
* |
47 |
* Event\State U B G GB |
48 |
* ----------- -- -- -- -- |
49 |
* FirstPress B B GB GB |
50 |
* Press B B G GB |
51 |
* Release U B G GB |
52 |
* LastRelease U U G G |
53 |
* Grab G G G G |
54 |
* Ungrab U B U U |
55 |
* |
56 |
* Note: U=Ungrabbed, B=Button, G=Grabbed, GB=Grab and Button |
57 |
* |
58 |
* In addition, the following conditions are always true: |
59 |
* |
60 |
* State\Variable Grab Restrict Capture |
61 |
* -------------- ---- -------- ------- |
62 |
* Ungrabbed 0 0 0 |
63 |
* Button 0 1 1 |
64 |
* Grabbed 1 0 b/g |
65 |
* Grab and Button 1 1 1 |
66 |
* |
67 |
* Note: 0 means variable is set to NULL, 1 means variable is set to |
68 |
* some window, b/g means the variable is set to a window if a button |
69 |
* is currently down or a global grab is in effect. |
70 |
* |
71 |
* The final complication to all of this is enter and leave events. |
72 |
* In order to correctly handle all of the various cases, Tk cannot |
73 |
* rely on X enter/leave events in all situations. The following |
74 |
* describes the correct sequence of enter and leave events that |
75 |
* should be observed by Tk scripts: |
76 |
* |
77 |
* Event(state) Enter/Leave From -> To |
78 |
* ------------ ---------------------- |
79 |
* LastRelease(B | GB): restrict window -> anc(grab window, event window) |
80 |
* Grab(U | B): event window -> anc(grab window, event window) |
81 |
* Grab(G): anc(old grab window, event window) -> |
82 |
* anc(new grab window, event window) |
83 |
* Grab(GB): restrict window -> anc(new grab window, event window) |
84 |
* Ungrab(G): anc(grab window, event window) -> event window |
85 |
* Ungrab(GB): restrict window -> event window |
86 |
* |
87 |
* Note: anc(x,y) returns the least ancestor of y that is in the tree |
88 |
* of x, terminating at toplevels. |
89 |
*/ |
90 |
|
91 |
/* |
92 |
* The following structure is used to pass information to |
93 |
* GrabRestrictProc from EatGrabEvents. |
94 |
*/ |
95 |
|
96 |
typedef struct { |
97 |
Display *display; /* Display from which to discard events. */ |
98 |
unsigned int serial; /* Serial number with which to compare. */ |
99 |
} GrabInfo; |
100 |
|
101 |
/* |
102 |
* Bit definitions for grabFlags field of TkDisplay structures: |
103 |
* |
104 |
* GRAB_GLOBAL 1 means this is a global grab (we grabbed via |
105 |
* the server so all applications are locked out). |
106 |
* 0 means this is a local grab that affects |
107 |
* only this application. |
108 |
* GRAB_TEMP_GLOBAL 1 means we've temporarily grabbed via the |
109 |
* server because a button is down and we want |
110 |
* to make sure that we get the button-up |
111 |
* event. The grab will be released when the |
112 |
* last mouse button goes up. |
113 |
*/ |
114 |
|
115 |
#define GRAB_GLOBAL 1 |
116 |
#define GRAB_TEMP_GLOBAL 4 |
117 |
|
118 |
/* |
119 |
* The following structure is a Tcl_Event that triggers a change in |
120 |
* the grabWinPtr field of a display. This event guarantees that |
121 |
* the change occurs in the proper order relative to enter and leave |
122 |
* events. |
123 |
*/ |
124 |
|
125 |
typedef struct NewGrabWinEvent { |
126 |
Tcl_Event header; /* Standard information for all Tcl events. */ |
127 |
TkDisplay *dispPtr; /* Display whose grab window is to change. */ |
128 |
Window grabWindow; /* New grab window for display. This is |
129 |
* recorded instead of a (TkWindow *) because |
130 |
* it will allow us to detect cases where |
131 |
* the window is destroyed before this event |
132 |
* is processed. */ |
133 |
} NewGrabWinEvent; |
134 |
|
135 |
/* |
136 |
* The following magic value is stored in the "send_event" field of |
137 |
* EnterNotify and LeaveNotify events that are generated in this |
138 |
* file. This allows us to separate "real" events coming from the |
139 |
* server from those that we generated. |
140 |
*/ |
141 |
|
142 |
#define GENERATED_EVENT_MAGIC ((Bool) 0x147321ac) |
143 |
|
144 |
/* |
145 |
* Mask that selects any of the state bits corresponding to buttons, |
146 |
* plus masks that select individual buttons' bits: |
147 |
*/ |
148 |
|
149 |
#define ALL_BUTTONS \ |
150 |
(Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask) |
151 |
static unsigned int buttonStates[] = { |
152 |
Button1Mask, Button2Mask, Button3Mask, Button4Mask, Button5Mask |
153 |
}; |
154 |
|
155 |
/* |
156 |
* Forward declarations for procedures declared later in this file: |
157 |
*/ |
158 |
|
159 |
static void EatGrabEvents _ANSI_ARGS_((TkDisplay *dispPtr, |
160 |
unsigned int serial)); |
161 |
static TkWindow * FindCommonAncestor _ANSI_ARGS_((TkWindow *winPtr1, |
162 |
TkWindow *winPtr2, int *countPtr1, |
163 |
int *countPtr2)); |
164 |
static Tk_RestrictAction GrabRestrictProc _ANSI_ARGS_((ClientData arg, |
165 |
XEvent *eventPtr)); |
166 |
static int GrabWinEventProc _ANSI_ARGS_((Tcl_Event *evPtr, |
167 |
int flags)); |
168 |
static void MovePointer2 _ANSI_ARGS_((TkWindow *sourcePtr, |
169 |
TkWindow *destPtr, int mode, int leaveEvents, |
170 |
int EnterEvents)); |
171 |
static void QueueGrabWindowChange _ANSI_ARGS_((TkDisplay *dispPtr, |
172 |
TkWindow *grabWinPtr)); |
173 |
static void ReleaseButtonGrab _ANSI_ARGS_((TkDisplay *dispPtr)); |
174 |
|
175 |
/* |
176 |
*---------------------------------------------------------------------- |
177 |
* |
178 |
* Tk_GrabCmd -- |
179 |
* |
180 |
* This procedure is invoked to process the "grab" Tcl command. |
181 |
* See the user documentation for details on what it does. |
182 |
* |
183 |
* Results: |
184 |
* A standard Tcl result. |
185 |
* |
186 |
* Side effects: |
187 |
* See the user documentation. |
188 |
* |
189 |
*---------------------------------------------------------------------- |
190 |
*/ |
191 |
|
192 |
/* ARGSUSED */ |
193 |
int |
194 |
Tk_GrabCmd(clientData, interp, argc, argv) |
195 |
ClientData clientData; /* Main window associated with |
196 |
* interpreter. */ |
197 |
Tcl_Interp *interp; /* Current interpreter. */ |
198 |
int argc; /* Number of arguments. */ |
199 |
char **argv; /* Argument strings. */ |
200 |
{ |
201 |
int globalGrab, c; |
202 |
Tk_Window tkwin; |
203 |
TkDisplay *dispPtr; |
204 |
size_t length; |
205 |
|
206 |
if (argc < 2) { |
207 |
badArgs: |
208 |
Tcl_AppendResult(interp, "wrong # args: should be \"", |
209 |
argv[0], " ?-global? window\" or \"", argv[0], |
210 |
" option ?arg arg ...?\"", (char *) NULL); |
211 |
return TCL_ERROR; |
212 |
} |
213 |
c = argv[1][0]; |
214 |
length = strlen(argv[1]); |
215 |
if (c == '.') { |
216 |
if (argc != 2) { |
217 |
goto badArgs; |
218 |
} |
219 |
tkwin = Tk_NameToWindow(interp, argv[1], (Tk_Window) clientData); |
220 |
if (tkwin == NULL) { |
221 |
return TCL_ERROR; |
222 |
} |
223 |
return Tk_Grab(interp, tkwin, 0); |
224 |
} else if ((c == '-') && (strncmp(argv[1], "-global", length) == 0) |
225 |
&& (length >= 2)) { |
226 |
if (argc != 3) { |
227 |
goto badArgs; |
228 |
} |
229 |
tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData); |
230 |
if (tkwin == NULL) { |
231 |
return TCL_ERROR; |
232 |
} |
233 |
return Tk_Grab(interp, tkwin, 1); |
234 |
} else if ((c == 'c') && (strncmp(argv[1], "current", length) == 0)) { |
235 |
if (argc > 3) { |
236 |
Tcl_AppendResult(interp, "wrong # args: should be \"", |
237 |
argv[0], " current ?window?\"", (char *) NULL); |
238 |
return TCL_ERROR; |
239 |
} |
240 |
if (argc == 3) { |
241 |
tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData); |
242 |
if (tkwin == NULL) { |
243 |
return TCL_ERROR; |
244 |
} |
245 |
dispPtr = ((TkWindow *) tkwin)->dispPtr; |
246 |
if (dispPtr->eventualGrabWinPtr != NULL) { |
247 |
Tcl_SetResult(interp, dispPtr->eventualGrabWinPtr->pathName, |
248 |
TCL_STATIC); |
249 |
} |
250 |
} else { |
251 |
for (dispPtr = TkGetDisplayList(); dispPtr != NULL; |
252 |
dispPtr = dispPtr->nextPtr) { |
253 |
if (dispPtr->eventualGrabWinPtr != NULL) { |
254 |
Tcl_AppendElement(interp, |
255 |
dispPtr->eventualGrabWinPtr->pathName); |
256 |
} |
257 |
} |
258 |
} |
259 |
return TCL_OK; |
260 |
} else if ((c == 'r') && (strncmp(argv[1], "release", length) == 0)) { |
261 |
if (argc != 3) { |
262 |
Tcl_AppendResult(interp, "wrong # args: should be \"", |
263 |
argv[0], " release window\"", (char *) NULL); |
264 |
return TCL_ERROR; |
265 |
} |
266 |
tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData); |
267 |
if (tkwin == NULL) { |
268 |
Tcl_ResetResult(interp); |
269 |
} else { |
270 |
Tk_Ungrab(tkwin); |
271 |
} |
272 |
} else if ((c == 's') && (strncmp(argv[1], "set", length) == 0) |
273 |
&& (length >= 2)) { |
274 |
if ((argc != 3) && (argc != 4)) { |
275 |
Tcl_AppendResult(interp, "wrong # args: should be \"", |
276 |
argv[0], " set ?-global? window\"", (char *) NULL); |
277 |
return TCL_ERROR; |
278 |
} |
279 |
if (argc == 3) { |
280 |
globalGrab = 0; |
281 |
tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData); |
282 |
} else { |
283 |
globalGrab = 1; |
284 |
length = strlen(argv[2]); |
285 |
if ((strncmp(argv[2], "-global", length) != 0) || (length < 2)) { |
286 |
Tcl_AppendResult(interp, "bad argument \"", argv[2], |
287 |
"\": must be \"", argv[0], " set ?-global? window\"", |
288 |
(char *) NULL); |
289 |
return TCL_ERROR; |
290 |
} |
291 |
tkwin = Tk_NameToWindow(interp, argv[3], (Tk_Window) clientData); |
292 |
} |
293 |
if (tkwin == NULL) { |
294 |
return TCL_ERROR; |
295 |
} |
296 |
return Tk_Grab(interp, tkwin, globalGrab); |
297 |
} else if ((c == 's') && (strncmp(argv[1], "status", length) == 0) |
298 |
&& (length >= 2)) { |
299 |
TkWindow *winPtr; |
300 |
|
301 |
if (argc != 3) { |
302 |
Tcl_AppendResult(interp, "wrong # args: should be \"", |
303 |
argv[0], " status window\"", (char *) NULL); |
304 |
return TCL_ERROR; |
305 |
} |
306 |
winPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], |
307 |
(Tk_Window) clientData); |
308 |
if (winPtr == NULL) { |
309 |
return TCL_ERROR; |
310 |
} |
311 |
dispPtr = winPtr->dispPtr; |
312 |
if (dispPtr->eventualGrabWinPtr != winPtr) { |
313 |
Tcl_SetResult(interp, "none", TCL_STATIC); |
314 |
} else if (dispPtr->grabFlags & GRAB_GLOBAL) { |
315 |
Tcl_SetResult(interp, "global", TCL_STATIC); |
316 |
} else { |
317 |
Tcl_SetResult(interp, "local", TCL_STATIC); |
318 |
} |
319 |
} else { |
320 |
Tcl_AppendResult(interp, "unknown or ambiguous option \"", argv[1], |
321 |
"\": must be current, release, set, or status", |
322 |
(char *) NULL); |
323 |
return TCL_ERROR; |
324 |
} |
325 |
return TCL_OK; |
326 |
} |
327 |
|
328 |
/* |
329 |
*---------------------------------------------------------------------- |
330 |
* |
331 |
* Tk_Grab -- |
332 |
* |
333 |
* Grabs the pointer and keyboard, so that mouse-related events are |
334 |
* only reported relative to a given window and its descendants. |
335 |
* |
336 |
* Results: |
337 |
* A standard Tcl result is returned. TCL_OK is the normal return |
338 |
* value; if the grab could not be set then TCL_ERROR is returned |
339 |
* and the interp's result will hold an error message. |
340 |
* |
341 |
* Side effects: |
342 |
* Once this call completes successfully, no window outside the |
343 |
* tree rooted at tkwin will receive pointer- or keyboard-related |
344 |
* events until the next call to Tk_Ungrab. If a previous grab was |
345 |
* in effect within this application, then it is replaced with a new |
346 |
* one. |
347 |
* |
348 |
*---------------------------------------------------------------------- |
349 |
*/ |
350 |
|
351 |
int |
352 |
Tk_Grab(interp, tkwin, grabGlobal) |
353 |
Tcl_Interp *interp; /* Used for error reporting. */ |
354 |
Tk_Window tkwin; /* Window on whose behalf the pointer |
355 |
* is to be grabbed. */ |
356 |
int grabGlobal; /* Non-zero means issue a grab to the |
357 |
* server so that no other application |
358 |
* gets mouse or keyboard events. |
359 |
* Zero means the grab only applies |
360 |
* within this application. */ |
361 |
{ |
362 |
int grabResult, numTries; |
363 |
TkWindow *winPtr = (TkWindow *) tkwin; |
364 |
TkDisplay *dispPtr = winPtr->dispPtr; |
365 |
TkWindow *winPtr2; |
366 |
unsigned int serial; |
367 |
|
368 |
ReleaseButtonGrab(dispPtr); |
369 |
if (dispPtr->eventualGrabWinPtr != NULL) { |
370 |
if ((dispPtr->eventualGrabWinPtr == winPtr) |
371 |
&& (grabGlobal == ((dispPtr->grabFlags & GRAB_GLOBAL) != 0))) { |
372 |
return TCL_OK; |
373 |
} |
374 |
if (dispPtr->eventualGrabWinPtr->mainPtr != winPtr->mainPtr) { |
375 |
alreadyGrabbed: |
376 |
Tcl_SetResult(interp, "grab failed: another application has grab", |
377 |
TCL_STATIC); |
378 |
return TCL_ERROR; |
379 |
} |
380 |
Tk_Ungrab((Tk_Window) dispPtr->eventualGrabWinPtr); |
381 |
} |
382 |
|
383 |
Tk_MakeWindowExist(tkwin); |
384 |
if (!grabGlobal) { |
385 |
Window dummy1, dummy2; |
386 |
int dummy3, dummy4, dummy5, dummy6; |
387 |
unsigned int state; |
388 |
|
389 |
/* |
390 |
* Local grab. However, if any mouse buttons are down, turn |
391 |
* it into a global grab temporarily, until the last button |
392 |
* goes up. This does two things: (a) it makes sure that we |
393 |
* see the button-up event; and (b) it allows us to track mouse |
394 |
* motion among all of the windows of this application. |
395 |
*/ |
396 |
|
397 |
dispPtr->grabFlags &= ~(GRAB_GLOBAL|GRAB_TEMP_GLOBAL); |
398 |
XQueryPointer(dispPtr->display, winPtr->window, &dummy1, |
399 |
&dummy2, &dummy3, &dummy4, &dummy5, &dummy6, &state); |
400 |
if ((state & ALL_BUTTONS) != 0) { |
401 |
dispPtr->grabFlags |= GRAB_TEMP_GLOBAL; |
402 |
goto setGlobalGrab; |
403 |
} |
404 |
} else { |
405 |
dispPtr->grabFlags |= GRAB_GLOBAL; |
406 |
setGlobalGrab: |
407 |
|
408 |
/* |
409 |
* Tricky point: must ungrab before grabbing. This is needed |
410 |
* in case there is a button auto-grab already in effect. If |
411 |
* there is, and the mouse has moved to a different window, X |
412 |
* won't generate enter and leave events to move the mouse if |
413 |
* we grab without ungrabbing. |
414 |
*/ |
415 |
|
416 |
XUngrabPointer(dispPtr->display, CurrentTime); |
417 |
serial = NextRequest(dispPtr->display); |
418 |
|
419 |
/* |
420 |
* Another tricky point: there are races with some window |
421 |
* managers that can cause grabs to fail because the window |
422 |
* manager hasn't released its grab quickly enough. To work |
423 |
* around this problem, retry a few times after AlreadyGrabbed |
424 |
* errors to give the grab release enough time to register with |
425 |
* the server. |
426 |
*/ |
427 |
|
428 |
grabResult = 0; /* Needed only to prevent gcc |
429 |
* compiler warnings. */ |
430 |
for (numTries = 0; numTries < 10; numTries++) { |
431 |
grabResult = XGrabPointer(dispPtr->display, winPtr->window, |
432 |
True, ButtonPressMask|ButtonReleaseMask|ButtonMotionMask |
433 |
|PointerMotionMask, GrabModeAsync, GrabModeAsync, None, |
434 |
None, CurrentTime); |
435 |
if (grabResult != AlreadyGrabbed) { |
436 |
break; |
437 |
} |
438 |
Tcl_Sleep(100); |
439 |
} |
440 |
if (grabResult != 0) { |
441 |
grabError: |
442 |
if (grabResult == GrabNotViewable) { |
443 |
Tcl_SetResult(interp, "grab failed: window not viewable", |
444 |
TCL_STATIC); |
445 |
} else if (grabResult == AlreadyGrabbed) { |
446 |
goto alreadyGrabbed; |
447 |
} else if (grabResult == GrabFrozen) { |
448 |
Tcl_SetResult(interp, |
449 |
"grab failed: keyboard or pointer frozen", TCL_STATIC); |
450 |
} else if (grabResult == GrabInvalidTime) { |
451 |
Tcl_SetResult(interp, "grab failed: invalid time", |
452 |
TCL_STATIC); |
453 |
} else { |
454 |
char msg[64 + TCL_INTEGER_SPACE]; |
455 |
|
456 |
sprintf(msg, "grab failed for unknown reason (code %d)", |
457 |
grabResult); |
458 |
Tcl_AppendResult(interp, msg, (char *) NULL); |
459 |
} |
460 |
return TCL_ERROR; |
461 |
} |
462 |
grabResult = XGrabKeyboard(dispPtr->display, Tk_WindowId(tkwin), |
463 |
False, GrabModeAsync, GrabModeAsync, CurrentTime); |
464 |
if (grabResult != 0) { |
465 |
XUngrabPointer(dispPtr->display, CurrentTime); |
466 |
goto grabError; |
467 |
} |
468 |
|
469 |
/* |
470 |
* Eat up any grab-related events generated by the server for the |
471 |
* grab. There are several reasons for doing this: |
472 |
* |
473 |
* 1. We have to synthesize the events for local grabs anyway, since |
474 |
* the server doesn't participate in them. |
475 |
* 2. The server doesn't always generate the right events for global |
476 |
* grabs (e.g. it generates events even if the current window is |
477 |
* in the grab tree, which we don't want). |
478 |
* 3. We want all the grab-related events to be processed immediately |
479 |
* (before other events that are already queued); events coming |
480 |
* from the server will be in the wrong place, but events we |
481 |
* synthesize here will go to the front of the queue. |
482 |
*/ |
483 |
|
484 |
EatGrabEvents(dispPtr, serial); |
485 |
} |
486 |
|
487 |
/* |
488 |
* Synthesize leave events to move the pointer from its current window |
489 |
* up to the lowest ancestor that it has in common with the grab window. |
490 |
* However, only do this if the pointer is outside the grab window's |
491 |
* subtree but inside the grab window's application. |
492 |
*/ |
493 |
|
494 |
if ((dispPtr->serverWinPtr != NULL) |
495 |
&& (dispPtr->serverWinPtr->mainPtr == winPtr->mainPtr)) { |
496 |
for (winPtr2 = dispPtr->serverWinPtr; ; winPtr2 = winPtr2->parentPtr) { |
497 |
if (winPtr2 == winPtr) { |
498 |
break; |
499 |
} |
500 |
if (winPtr2 == NULL) { |
501 |
MovePointer2(dispPtr->serverWinPtr, winPtr, NotifyGrab, 1, 0); |
502 |
break; |
503 |
} |
504 |
} |
505 |
} |
506 |
QueueGrabWindowChange(dispPtr, winPtr); |
507 |
return TCL_OK; |
508 |
} |
509 |
|
510 |
/* |
511 |
*---------------------------------------------------------------------- |
512 |
* |
513 |
* Tk_Ungrab -- |
514 |
* |
515 |
* Releases a grab on the mouse pointer and keyboard, if there |
516 |
* is one set on the specified window. |
517 |
* |
518 |
* Results: |
519 |
* None. |
520 |
* |
521 |
* Side effects: |
522 |
* Pointer and keyboard events will start being delivered to other |
523 |
* windows again. |
524 |
* |
525 |
*---------------------------------------------------------------------- |
526 |
*/ |
527 |
|
528 |
void |
529 |
Tk_Ungrab(tkwin) |
530 |
Tk_Window tkwin; /* Window whose grab should be |
531 |
* released. */ |
532 |
{ |
533 |
TkDisplay *dispPtr; |
534 |
TkWindow *grabWinPtr, *winPtr; |
535 |
unsigned int serial; |
536 |
|
537 |
grabWinPtr = (TkWindow *) tkwin; |
538 |
dispPtr = grabWinPtr->dispPtr; |
539 |
if (grabWinPtr != dispPtr->eventualGrabWinPtr) { |
540 |
return; |
541 |
} |
542 |
ReleaseButtonGrab(dispPtr); |
543 |
QueueGrabWindowChange(dispPtr, (TkWindow *) NULL); |
544 |
if (dispPtr->grabFlags & (GRAB_GLOBAL|GRAB_TEMP_GLOBAL)) { |
545 |
dispPtr->grabFlags &= ~(GRAB_GLOBAL|GRAB_TEMP_GLOBAL); |
546 |
serial = NextRequest(dispPtr->display); |
547 |
XUngrabPointer(dispPtr->display, CurrentTime); |
548 |
XUngrabKeyboard(dispPtr->display, CurrentTime); |
549 |
EatGrabEvents(dispPtr, serial); |
550 |
} |
551 |
|
552 |
/* |
553 |
* Generate events to move the pointer back to the window where it |
554 |
* really is. Some notes: |
555 |
* 1. As with grabs, only do this if the "real" window is not a |
556 |
* descendant of the grab window, since in this case the pointer |
557 |
* is already where it's supposed to be. |
558 |
* 2. If the "real" window is in some other application then don't |
559 |
* generate any events at all, since everything's already been |
560 |
* reported correctly. |
561 |
* 3. Only generate enter events. Don't generate leave events, |
562 |
* because we never told the lower-level windows that they |
563 |
* had the pointer in the first place. |
564 |
*/ |
565 |
|
566 |
for (winPtr = dispPtr->serverWinPtr; ; winPtr = winPtr->parentPtr) { |
567 |
if (winPtr == grabWinPtr) { |
568 |
break; |
569 |
} |
570 |
if (winPtr == NULL) { |
571 |
if ((dispPtr->serverWinPtr == NULL) || |
572 |
(dispPtr->serverWinPtr->mainPtr == grabWinPtr->mainPtr)) { |
573 |
MovePointer2(grabWinPtr, dispPtr->serverWinPtr, |
574 |
NotifyUngrab, 0, 1); |
575 |
} |
576 |
break; |
577 |
} |
578 |
} |
579 |
} |
580 |
|
581 |
/* |
582 |
*---------------------------------------------------------------------- |
583 |
* |
584 |
* ReleaseButtonGrab -- |
585 |
* |
586 |
* This procedure is called to release a simulated button grab, if |
587 |
* there is one in effect. A button grab is present whenever |
588 |
* dispPtr->buttonWinPtr is non-NULL or when the GRAB_TEMP_GLOBAL |
589 |
* flag is set. |
590 |
* |
591 |
* Results: |
592 |
* None. |
593 |
* |
594 |
* Side effects: |
595 |
* DispPtr->buttonWinPtr is reset to NULL, and enter and leave |
596 |
* events are generated if necessary to move the pointer from |
597 |
* the button grab window to its current window. |
598 |
* |
599 |
*---------------------------------------------------------------------- |
600 |
*/ |
601 |
|
602 |
static void |
603 |
ReleaseButtonGrab(dispPtr) |
604 |
register TkDisplay *dispPtr; /* Display whose button grab is to be |
605 |
* released. */ |
606 |
{ |
607 |
unsigned int serial; |
608 |
|
609 |
if (dispPtr->buttonWinPtr != NULL) { |
610 |
if (dispPtr->buttonWinPtr != dispPtr->serverWinPtr) { |
611 |
MovePointer2(dispPtr->buttonWinPtr, dispPtr->serverWinPtr, |
612 |
NotifyUngrab, 1, 1); |
613 |
} |
614 |
dispPtr->buttonWinPtr = NULL; |
615 |
} |
616 |
if (dispPtr->grabFlags & GRAB_TEMP_GLOBAL) { |
617 |
dispPtr->grabFlags &= ~GRAB_TEMP_GLOBAL; |
618 |
serial = NextRequest(dispPtr->display); |
619 |
XUngrabPointer(dispPtr->display, CurrentTime); |
620 |
XUngrabKeyboard(dispPtr->display, CurrentTime); |
621 |
EatGrabEvents(dispPtr, serial); |
622 |
} |
623 |
} |
624 |
|
625 |
/* |
626 |
*---------------------------------------------------------------------- |
627 |
* |
628 |
* TkPointerEvent -- |
629 |
* |
630 |
* This procedure is called for each pointer-related event, before |
631 |
* the event has been processed. It does various things to make |
632 |
* grabs work correctly. |
633 |
* |
634 |
* Results: |
635 |
* If the return value is 1 it means the event should be processed |
636 |
* (event handlers should be invoked). If the return value is 0 |
637 |
* it means the event should be ignored in order to make grabs |
638 |
* work correctly. In some cases this procedure modifies the event. |
639 |
* |
640 |
* Side effects: |
641 |
* Grab state information may be updated. New events may also be |
642 |
* pushed back onto the event queue to replace or augment the |
643 |
* one passed in here. |
644 |
* |
645 |
*---------------------------------------------------------------------- |
646 |
*/ |
647 |
|
648 |
int |
649 |
TkPointerEvent(eventPtr, winPtr) |
650 |
register XEvent *eventPtr; /* Pointer to the event. */ |
651 |
TkWindow *winPtr; /* Tk's information for window |
652 |
* where event was reported. */ |
653 |
{ |
654 |
register TkWindow *winPtr2; |
655 |
TkDisplay *dispPtr = winPtr->dispPtr; |
656 |
unsigned int serial; |
657 |
int outsideGrabTree = 0; |
658 |
int ancestorOfGrab = 0; |
659 |
int appGrabbed = 0; /* Non-zero means event is being |
660 |
* reported to an application that is |
661 |
* affected by the grab. */ |
662 |
|
663 |
/* |
664 |
* Collect information about the grab (if any). |
665 |
*/ |
666 |
|
667 |
switch (TkGrabState(winPtr)) { |
668 |
case TK_GRAB_IN_TREE: |
669 |
appGrabbed = 1; |
670 |
break; |
671 |
case TK_GRAB_ANCESTOR: |
672 |
appGrabbed = 1; |
673 |
outsideGrabTree = 1; |
674 |
ancestorOfGrab = 1; |
675 |
break; |
676 |
case TK_GRAB_EXCLUDED: |
677 |
appGrabbed = 1; |
678 |
outsideGrabTree = 1; |
679 |
break; |
680 |
} |
681 |
|
682 |
if ((eventPtr->type == EnterNotify) || (eventPtr->type == LeaveNotify)) { |
683 |
/* |
684 |
* Keep track of what window the mouse is *really* over. |
685 |
* Any events that we generate have a special send_event value, |
686 |
* which is detected below and used to ignore the event for |
687 |
* purposes of setting serverWinPtr. |
688 |
*/ |
689 |
|
690 |
if (eventPtr->xcrossing.send_event != GENERATED_EVENT_MAGIC) { |
691 |
if ((eventPtr->type == LeaveNotify) && |
692 |
(winPtr->flags & TK_TOP_LEVEL)) { |
693 |
dispPtr->serverWinPtr = NULL; |
694 |
} else { |
695 |
dispPtr->serverWinPtr = winPtr; |
696 |
} |
697 |
} |
698 |
|
699 |
/* |
700 |
* When a grab is active, X continues to report enter and leave |
701 |
* events for windows outside the tree of the grab window: |
702 |
* 1. Detect these events and ignore them except for |
703 |
* windows above the grab window. |
704 |
* 2. Allow Enter and Leave events to pass through the |
705 |
* windows above the grab window, but never let them |
706 |
* end up with the pointer *in* one of those windows. |
707 |
*/ |
708 |
|
709 |
if (dispPtr->grabWinPtr != NULL) { |
710 |
if (outsideGrabTree && appGrabbed) { |
711 |
if (!ancestorOfGrab) { |
712 |
return 0; |
713 |
} |
714 |
switch (eventPtr->xcrossing.detail) { |
715 |
case NotifyInferior: |
716 |
return 0; |
717 |
case NotifyAncestor: |
718 |
eventPtr->xcrossing.detail = NotifyVirtual; |
719 |
break; |
720 |
case NotifyNonlinear: |
721 |
eventPtr->xcrossing.detail = NotifyNonlinearVirtual; |
722 |
break; |
723 |
} |
724 |
} |
725 |
|
726 |
/* |
727 |
* Make buttons have the same grab-like behavior inside a grab |
728 |
* as they do outside a grab: do this by ignoring enter and |
729 |
* leave events except for the window in which the button was |
730 |
* pressed. |
731 |
*/ |
732 |
|
733 |
if ((dispPtr->buttonWinPtr != NULL) |
734 |
&& (winPtr != dispPtr->buttonWinPtr)) { |
735 |
return 0; |
736 |
} |
737 |
} |
738 |
return 1; |
739 |
} |
740 |
|
741 |
if (!appGrabbed) { |
742 |
return 1; |
743 |
} |
744 |
|
745 |
if (eventPtr->type == MotionNotify) { |
746 |
/* |
747 |
* When grabs are active, X reports motion events relative to the |
748 |
* window under the pointer. Instead, it should report the events |
749 |
* relative to the window the button went down in, if there is a |
750 |
* button down. Otherwise, if the pointer window is outside the |
751 |
* subtree of the grab window, the events should be reported |
752 |
* relative to the grab window. Otherwise, the event should be |
753 |
* reported to the pointer window. |
754 |
*/ |
755 |
|
756 |
winPtr2 = winPtr; |
757 |
if (dispPtr->buttonWinPtr != NULL) { |
758 |
winPtr2 = dispPtr->buttonWinPtr; |
759 |
} else if (outsideGrabTree || (dispPtr->serverWinPtr == NULL)) { |
760 |
winPtr2 = dispPtr->grabWinPtr; |
761 |
} |
762 |
if (winPtr2 != winPtr) { |
763 |
TkChangeEventWindow(eventPtr, winPtr2); |
764 |
Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_HEAD); |
765 |
return 0; |
766 |
} |
767 |
return 1; |
768 |
} |
769 |
|
770 |
/* |
771 |
* Process ButtonPress and ButtonRelease events: |
772 |
* 1. Keep track of whether a button is down and what window it |
773 |
* went down in. |
774 |
* 2. If the first button goes down outside the grab tree, pretend |
775 |
* it went down in the grab window. Note: it's important to |
776 |
* redirect events to the grab window like this in order to make |
777 |
* things like menus work, where button presses outside the |
778 |
* grabbed menu need to be seen. An application can always |
779 |
* ignore the events if they occur outside its window. |
780 |
* 3. If a button press or release occurs outside the window where |
781 |
* the first button was pressed, retarget the event so it's reported |
782 |
* to the window where the first button was pressed. |
783 |
* 4. If the last button is released in a window different than where |
784 |
* the first button was pressed, generate Enter/Leave events to |
785 |
* move the mouse from the button window to its current window. |
786 |
* 5. If the grab is set at a time when a button is already down, or |
787 |
* if the window where the button was pressed was deleted, then |
788 |
* dispPtr->buttonWinPtr will stay NULL. Just forget about the |
789 |
* auto-grab for the button press; events will go to whatever |
790 |
* window contains the pointer. If this window isn't in the grab |
791 |
* tree then redirect events to the grab window. |
792 |
* 6. When a button is pressed during a local grab, the X server sets |
793 |
* a grab of its own, since it doesn't even know about our local |
794 |
* grab. This causes enter and leave events no longer to be |
795 |
* generated in the same way as for global grabs. To eliminate this |
796 |
* problem, set a temporary global grab when the first button goes |
797 |
* down and release it when the last button comes up. |
798 |
*/ |
799 |
|
800 |
if ((eventPtr->type == ButtonPress) || (eventPtr->type == ButtonRelease)) { |
801 |
winPtr2 = dispPtr->buttonWinPtr; |
802 |
if (winPtr2 == NULL) { |
803 |
if (outsideGrabTree) { |
804 |
winPtr2 = dispPtr->grabWinPtr; /* Note 5. */ |
805 |
} else { |
806 |
winPtr2 = winPtr; /* Note 5. */ |
807 |
} |
808 |
} |
809 |
if (eventPtr->type == ButtonPress) { |
810 |
if ((eventPtr->xbutton.state & ALL_BUTTONS) == 0) { |
811 |
if (outsideGrabTree) { |
812 |
TkChangeEventWindow(eventPtr, dispPtr->grabWinPtr); |
813 |
Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_HEAD); |
814 |
return 0; /* Note 2. */ |
815 |
} |
816 |
if (!(dispPtr->grabFlags & GRAB_GLOBAL)) { /* Note 6. */ |
817 |
serial = NextRequest(dispPtr->display); |
818 |
if (XGrabPointer(dispPtr->display, |
819 |
dispPtr->grabWinPtr->window, True, |
820 |
ButtonPressMask|ButtonReleaseMask|ButtonMotionMask, |
821 |
GrabModeAsync, GrabModeAsync, None, None, |
822 |
CurrentTime) == 0) { |
823 |
EatGrabEvents(dispPtr, serial); |
824 |
if (XGrabKeyboard(dispPtr->display, winPtr->window, |
825 |
False, GrabModeAsync, GrabModeAsync, |
826 |
CurrentTime) == 0) { |
827 |
dispPtr->grabFlags |= GRAB_TEMP_GLOBAL; |
828 |
} else { |
829 |
XUngrabPointer(dispPtr->display, CurrentTime); |
830 |
} |
831 |
} |
832 |
} |
833 |
dispPtr->buttonWinPtr = winPtr; |
834 |
return 1; |
835 |
} |
836 |
} else { |
837 |
if ((eventPtr->xbutton.state & ALL_BUTTONS) |
838 |
== buttonStates[eventPtr->xbutton.button - Button1]) { |
839 |
ReleaseButtonGrab(dispPtr); /* Note 4. */ |
840 |
} |
841 |
} |
842 |
if (winPtr2 != winPtr) { |
843 |
TkChangeEventWindow(eventPtr, winPtr2); |
844 |
Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_HEAD); |
845 |
return 0; /* Note 3. */ |
846 |
} |
847 |
} |
848 |
|
849 |
return 1; |
850 |
} |
851 |
|
852 |
/* |
853 |
*---------------------------------------------------------------------- |
854 |
* |
855 |
* TkChangeEventWindow -- |
856 |
* |
857 |
* Given an event and a new window to which the event should be |
858 |
* retargeted, modify fields of the event so that the event is |
859 |
* properly retargeted to the new window. |
860 |
* |
861 |
* Results: |
862 |
* The following fields of eventPtr are modified: window, |
863 |
* subwindow, x, y, same_screen. |
864 |
* |
865 |
* Side effects: |
866 |
* None. |
867 |
* |
868 |
*---------------------------------------------------------------------- |
869 |
*/ |
870 |
|
871 |
void |
872 |
TkChangeEventWindow(eventPtr, winPtr) |
873 |
register XEvent *eventPtr; /* Event to retarget. Must have |
874 |
* type ButtonPress, ButtonRelease, KeyPress, |
875 |
* KeyRelease, MotionNotify, EnterNotify, |
876 |
* or LeaveNotify. */ |
877 |
TkWindow *winPtr; /* New target window for event. */ |
878 |
{ |
879 |
int x, y, sameScreen, bd; |
880 |
register TkWindow *childPtr; |
881 |
|
882 |
eventPtr->xmotion.window = Tk_WindowId(winPtr); |
883 |
if (eventPtr->xmotion.root == |
884 |
RootWindow(winPtr->display, winPtr->screenNum)) { |
885 |
Tk_GetRootCoords((Tk_Window) winPtr, &x, &y); |
886 |
eventPtr->xmotion.x = eventPtr->xmotion.x_root - x; |
887 |
eventPtr->xmotion.y = eventPtr->xmotion.y_root - y; |
888 |
eventPtr->xmotion.subwindow = None; |
889 |
for (childPtr = winPtr->childList; childPtr != NULL; |
890 |
childPtr = childPtr->nextPtr) { |
891 |
if (childPtr->flags & TK_TOP_LEVEL) { |
892 |
continue; |
893 |
} |
894 |
x = eventPtr->xmotion.x - childPtr->changes.x; |
895 |
y = eventPtr->xmotion.y - childPtr->changes.y; |
896 |
bd = childPtr->changes.border_width; |
897 |
if ((x >= -bd) && (y >= -bd) |
898 |
&& (x < (childPtr->changes.width + bd)) |
899 |
&& (y < (childPtr->changes.height + bd))) { |
900 |
eventPtr->xmotion.subwindow = childPtr->window; |
901 |
} |
902 |
} |
903 |
sameScreen = 1; |
904 |
} else { |
905 |
eventPtr->xmotion.x = 0; |
906 |
eventPtr->xmotion.y = 0; |
907 |
eventPtr->xmotion.subwindow = None; |
908 |
sameScreen = 0; |
909 |
} |
910 |
if (eventPtr->type == MotionNotify) { |
911 |
eventPtr->xmotion.same_screen = sameScreen; |
912 |
} else { |
913 |
eventPtr->xbutton.same_screen = sameScreen; |
914 |
} |
915 |
} |
916 |
|
917 |
/* |
918 |
*---------------------------------------------------------------------- |
919 |
* |
920 |
* TkInOutEvents -- |
921 |
* |
922 |
* This procedure synthesizes EnterNotify and LeaveNotify events |
923 |
* to correctly transfer the pointer from one window to another. |
924 |
* It can also be used to generate FocusIn and FocusOut events |
925 |
* to move the input focus. |
926 |
* |
927 |
* Results: |
928 |
* None. |
929 |
* |
930 |
* Side effects: |
931 |
* Synthesized events may be pushed back onto the event queue. |
932 |
* The event pointed to by eventPtr is modified. |
933 |
* |
934 |
*---------------------------------------------------------------------- |
935 |
*/ |
936 |
|
937 |
void |
938 |
TkInOutEvents(eventPtr, sourcePtr, destPtr, leaveType, enterType, position) |
939 |
XEvent *eventPtr; /* A template X event. Must have all fields |
940 |
* properly set except for type, window, |
941 |
* subwindow, x, y, detail, and same_screen |
942 |
* (Not all of these fields are valid for |
943 |
* FocusIn/FocusOut events; x_root and y_root |
944 |
* must be valid for Enter/Leave events, even |
945 |
* though x and y needn't be valid). */ |
946 |
TkWindow *sourcePtr; /* Window that used to have the pointer or |
947 |
* focus (NULL means it was not in a window |
948 |
* managed by this process). */ |
949 |
TkWindow *destPtr; /* Window that is to end up with the pointer |
950 |
* or focus (NULL means it's not one managed |
951 |
* by this process). */ |
952 |
int leaveType; /* Type of events to generate for windows |
953 |
* being left (LeaveNotify or FocusOut). 0 |
954 |
* means don't generate leave events. */ |
955 |
int enterType; /* Type of events to generate for windows |
956 |
* being entered (EnterNotify or FocusIn). 0 |
957 |
* means don't generate enter events. */ |
958 |
Tcl_QueuePosition position; /* Position at which events are added to |
959 |
* the system event queue. */ |
960 |
{ |
961 |
register TkWindow *winPtr; |
962 |
int upLevels, downLevels, i, j, focus; |
963 |
|
964 |
/* |
965 |
* There are four possible cases to deal with: |
966 |
* |
967 |
* 1. SourcePtr and destPtr are the same. There's nothing to do in |
968 |
* this case. |
969 |
* 2. SourcePtr is an ancestor of destPtr in the same top-level |
970 |
* window. Must generate events down the window tree from source |
971 |
* to dest. |
972 |
* 3. DestPtr is an ancestor of sourcePtr in the same top-level |
973 |
* window. Must generate events up the window tree from sourcePtr |
974 |
* to destPtr. |
975 |
* 4. All other cases. Must first generate events up the window tree |
976 |
* from sourcePtr to its top-level, then down from destPtr's |
977 |
* top-level to destPtr. This form is called "non-linear." |
978 |
* |
979 |
* The call to FindCommonAncestor separates these four cases and decides |
980 |
* how many levels up and down events have to be generated for. |
981 |
*/ |
982 |
|
983 |
if (sourcePtr == destPtr) { |
984 |
return; |
985 |
} |
986 |
if ((leaveType == FocusOut) || (enterType == FocusIn)) { |
987 |
focus = 1; |
988 |
} else { |
989 |
focus = 0; |
990 |
} |
991 |
FindCommonAncestor(sourcePtr, destPtr, &upLevels, &downLevels); |
992 |
|
993 |
/* |
994 |
* Generate enter/leave events and add them to the grab event queue. |
995 |
*/ |
996 |
|
997 |
|
998 |
#define QUEUE(w, t, d) \ |
999 |
if (w->window != None) { \ |
1000 |
eventPtr->type = t; \ |
1001 |
if (focus) { \ |
1002 |
eventPtr->xfocus.window = w->window; \ |
1003 |
eventPtr->xfocus.detail = d; \ |
1004 |
} else { \ |
1005 |
eventPtr->xcrossing.detail = d; \ |
1006 |
TkChangeEventWindow(eventPtr, w); \ |
1007 |
} \ |
1008 |
Tk_QueueWindowEvent(eventPtr, position); \ |
1009 |
} |
1010 |
|
1011 |
if (downLevels == 0) { |
1012 |
|
1013 |
/* |
1014 |
* SourcePtr is an inferior of destPtr. |
1015 |
*/ |
1016 |
|
1017 |
if (leaveType != 0) { |
1018 |
QUEUE(sourcePtr, leaveType, NotifyAncestor); |
1019 |
for (winPtr = sourcePtr->parentPtr, i = upLevels-1; i > 0; |
1020 |
winPtr = winPtr->parentPtr, i--) { |
1021 |
QUEUE(winPtr, leaveType, NotifyVirtual); |
1022 |
} |
1023 |
} |
1024 |
if ((enterType != 0) && (destPtr != NULL)) { |
1025 |
QUEUE(destPtr, enterType, NotifyInferior); |
1026 |
} |
1027 |
} else if (upLevels == 0) { |
1028 |
|
1029 |
/* |
1030 |
* DestPtr is an inferior of sourcePtr. |
1031 |
*/ |
1032 |
|
1033 |
if ((leaveType != 0) && (sourcePtr != NULL)) { |
1034 |
QUEUE(sourcePtr, leaveType, NotifyInferior); |
1035 |
} |
1036 |
if (enterType != 0) { |
1037 |
for (i = downLevels-1; i > 0; i--) { |
1038 |
for (winPtr = destPtr->parentPtr, j = 1; j < i; |
1039 |
winPtr = winPtr->parentPtr, j++) { |
1040 |
} |
1041 |
QUEUE(winPtr, enterType, NotifyVirtual); |
1042 |
} |
1043 |
if (destPtr != NULL) { |
1044 |
QUEUE(destPtr, enterType, NotifyAncestor); |
1045 |
} |
1046 |
} |
1047 |
} else { |
1048 |
|
1049 |
/* |
1050 |
* Non-linear: neither window is an inferior of the other. |
1051 |
*/ |
1052 |
|
1053 |
if (leaveType != 0) { |
1054 |
QUEUE(sourcePtr, leaveType, NotifyNonlinear); |
1055 |
for (winPtr = sourcePtr->parentPtr, i = upLevels-1; i > 0; |
1056 |
winPtr = winPtr->parentPtr, i--) { |
1057 |
QUEUE(winPtr, leaveType, NotifyNonlinearVirtual); |
1058 |
} |
1059 |
} |
1060 |
if (enterType != 0) { |
1061 |
for (i = downLevels-1; i > 0; i--) { |
1062 |
for (winPtr = destPtr->parentPtr, j = 1; j < i; |
1063 |
winPtr = winPtr->parentPtr, j++) { |
1064 |
} |
1065 |
QUEUE(winPtr, enterType, NotifyNonlinearVirtual); |
1066 |
} |
1067 |
if (destPtr != NULL) { |
1068 |
QUEUE(destPtr, enterType, NotifyNonlinear); |
1069 |
} |
1070 |
} |
1071 |
} |
1072 |
} |
1073 |
|
1074 |
/* |
1075 |
*---------------------------------------------------------------------- |
1076 |
* |
1077 |
* MovePointer2 -- |
1078 |
* |
1079 |
* This procedure synthesizes EnterNotify and LeaveNotify events |
1080 |
* to correctly transfer the pointer from one window to another. |
1081 |
* It is different from TkInOutEvents in that no template X event |
1082 |
* needs to be supplied; this procedure generates the template |
1083 |
* event and calls TkInOutEvents. |
1084 |
* |
1085 |
* Results: |
1086 |
* None. |
1087 |
* |
1088 |
* Side effects: |
1089 |
* Synthesized events may be pushed back onto the event queue. |
1090 |
* |
1091 |
*---------------------------------------------------------------------- |
1092 |
*/ |
1093 |
|
1094 |
static void |
1095 |
MovePointer2(sourcePtr, destPtr, mode, leaveEvents, enterEvents) |
1096 |
TkWindow *sourcePtr; /* Window currently containing pointer (NULL |
1097 |
* means it's not one managed by this |
1098 |
* process). */ |
1099 |
TkWindow *destPtr; /* Window that is to end up containing the |
1100 |
* pointer (NULL means it's not one managed |
1101 |
* by this process). */ |
1102 |
int mode; /* Mode for enter/leave events, such as |
1103 |
* NotifyNormal or NotifyUngrab. */ |
1104 |
int leaveEvents; /* Non-zero means generate leave events for the |
1105 |
* windows being left. Zero means don't |
1106 |
* generate leave events. */ |
1107 |
int enterEvents; /* Non-zero means generate enter events for the |
1108 |
* windows being entered. Zero means don't |
1109 |
* generate enter events. */ |
1110 |
{ |
1111 |
XEvent event; |
1112 |
Window dummy1, dummy2; |
1113 |
int dummy3, dummy4; |
1114 |
TkWindow *winPtr; |
1115 |
|
1116 |
winPtr = sourcePtr; |
1117 |
if ((winPtr == NULL) || (winPtr->window == None)) { |
1118 |
winPtr = destPtr; |
1119 |
if ((winPtr == NULL) || (winPtr->window == None)) { |
1120 |
return; |
1121 |
} |
1122 |
} |
1123 |
|
1124 |
event.xcrossing.serial = LastKnownRequestProcessed( |
1125 |
winPtr->display); |
1126 |
event.xcrossing.send_event = GENERATED_EVENT_MAGIC; |
1127 |
event.xcrossing.display = winPtr->display; |
1128 |
event.xcrossing.root = RootWindow(winPtr->display, |
1129 |
winPtr->screenNum); |
1130 |
event.xcrossing.time = TkCurrentTime(winPtr->dispPtr); |
1131 |
XQueryPointer(winPtr->display, winPtr->window, &dummy1, &dummy2, |
1132 |
&event.xcrossing.x_root, &event.xcrossing.y_root, |
1133 |
&dummy3, &dummy4, &event.xcrossing.state); |
1134 |
event.xcrossing.mode = mode; |
1135 |
event.xcrossing.focus = False; |
1136 |
TkInOutEvents(&event, sourcePtr, destPtr, (leaveEvents) ? LeaveNotify : 0, |
1137 |
(enterEvents) ? EnterNotify : 0, TCL_QUEUE_MARK); |
1138 |
} |
1139 |
|
1140 |
/* |
1141 |
*---------------------------------------------------------------------- |
1142 |
* |
1143 |
* TkGrabDeadWindow -- |
1144 |
* |
1145 |
* This procedure is invoked whenever a window is deleted, so that |
1146 |
* grab-related cleanup can be performed. |
1147 |
* |
1148 |
* Results: |
1149 |
* None. |
1150 |
* |
1151 |
* Side effects: |
1152 |
* Various cleanups happen, such as generating events to move the |
1153 |
* pointer back to its "natural" window as if an ungrab had been |
1154 |
* done. See the code. |
1155 |
* |
1156 |
*---------------------------------------------------------------------- |
1157 |
*/ |
1158 |
|
1159 |
void |
1160 |
TkGrabDeadWindow(winPtr) |
1161 |
register TkWindow *winPtr; /* Window that is in the process |
1162 |
* of being deleted. */ |
1163 |
{ |
1164 |
TkDisplay *dispPtr = winPtr->dispPtr; |
1165 |
|
1166 |
if (dispPtr->eventualGrabWinPtr == winPtr) { |
1167 |
/* |
1168 |
* Grab window was deleted. Release the grab. |
1169 |
*/ |
1170 |
|
1171 |
Tk_Ungrab((Tk_Window) dispPtr->eventualGrabWinPtr); |
1172 |
} else if (dispPtr->buttonWinPtr == winPtr) { |
1173 |
ReleaseButtonGrab(dispPtr); |
1174 |
} |
1175 |
if (dispPtr->serverWinPtr == winPtr) { |
1176 |
if (winPtr->flags & TK_TOP_LEVEL) { |
1177 |
dispPtr->serverWinPtr = NULL; |
1178 |
} else { |
1179 |
dispPtr->serverWinPtr = winPtr->parentPtr; |
1180 |
} |
1181 |
} |
1182 |
if (dispPtr->grabWinPtr == winPtr) { |
1183 |
dispPtr->grabWinPtr = NULL; |
1184 |
} |
1185 |
} |
1186 |
|
1187 |
/* |
1188 |
*---------------------------------------------------------------------- |
1189 |
* |
1190 |
* EatGrabEvents -- |
1191 |
* |
1192 |
* This procedure is called to eliminate any Enter, Leave, |
1193 |
* FocusIn, or FocusOut events in the event queue for a |
1194 |
* display that have mode NotifyGrab or NotifyUngrab and |
1195 |
* have a serial number no less than a given value and are not |
1196 |
* generated by the grab module. |
1197 |
* |
1198 |
* Results: |
1199 |
* None. |
1200 |
* |
1201 |
* Side effects: |
1202 |
* DispPtr's display gets sync-ed, and some of the events get |
1203 |
* removed from the Tk event queue. |
1204 |
* |
1205 |
*---------------------------------------------------------------------- |
1206 |
*/ |
1207 |
|
1208 |
static void |
1209 |
EatGrabEvents(dispPtr, serial) |
1210 |
TkDisplay *dispPtr; /* Display from which to consume events. */ |
1211 |
unsigned int serial; /* Only discard events that have a serial |
1212 |
* number at least this great. */ |
1213 |
{ |
1214 |
Tk_RestrictProc *oldProc; |
1215 |
GrabInfo info; |
1216 |
ClientData oldArg, dummy; |
1217 |
|
1218 |
info.display = dispPtr->display; |
1219 |
info.serial = serial; |
1220 |
TkpSync(info.display); |
1221 |
oldProc = Tk_RestrictEvents(GrabRestrictProc, (ClientData)&info, &oldArg); |
1222 |
while (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) { |
1223 |
} |
1224 |
Tk_RestrictEvents(oldProc, oldArg, &dummy); |
1225 |
} |
1226 |
|
1227 |
/* |
1228 |
*---------------------------------------------------------------------- |
1229 |
* |
1230 |
* GrabRestrictProc -- |
1231 |
* |
1232 |
* A Tk_RestrictProc used by EatGrabEvents to eliminate any |
1233 |
* Enter, Leave, FocusIn, or FocusOut events in the event queue |
1234 |
* for a display that has mode NotifyGrab or NotifyUngrab and |
1235 |
* have a serial number no less than a given value. |
1236 |
* |
1237 |
* Results: |
1238 |
* Returns either TK_DISCARD_EVENT or TK_DEFER_EVENT. |
1239 |
* |
1240 |
* Side effects: |
1241 |
* None. |
1242 |
* |
1243 |
*---------------------------------------------------------------------- |
1244 |
*/ |
1245 |
|
1246 |
static Tk_RestrictAction |
1247 |
GrabRestrictProc(arg, eventPtr) |
1248 |
ClientData arg; |
1249 |
XEvent *eventPtr; |
1250 |
{ |
1251 |
GrabInfo *info = (GrabInfo *) arg; |
1252 |
int mode, diff; |
1253 |
|
1254 |
/* |
1255 |
* The diff caculation is trickier than it may seem. Don't forget |
1256 |
* that serial numbers can wrap around, so can't compare the two |
1257 |
* serial numbers directly. |
1258 |
*/ |
1259 |
|
1260 |
diff = eventPtr->xany.serial - info->serial; |
1261 |
if ((eventPtr->type == EnterNotify) |
1262 |
|| (eventPtr->type == LeaveNotify)) { |
1263 |
mode = eventPtr->xcrossing.mode; |
1264 |
} else if ((eventPtr->type == FocusIn) |
1265 |
|| (eventPtr->type == FocusOut)) { |
1266 |
mode = eventPtr->xfocus.mode; |
1267 |
} else { |
1268 |
mode = NotifyNormal; |
1269 |
} |
1270 |
if ((info->display != eventPtr->xany.display) || (mode == NotifyNormal) |
1271 |
|| (diff < 0)) { |
1272 |
return TK_DEFER_EVENT; |
1273 |
} else { |
1274 |
return TK_DISCARD_EVENT; |
1275 |
} |
1276 |
} |
1277 |
|
1278 |
/* |
1279 |
*---------------------------------------------------------------------- |
1280 |
* |
1281 |
* QueueGrabWindowChange -- |
1282 |
* |
1283 |
* This procedure queues a special event in the Tcl event queue, |
1284 |
* which will cause the "grabWinPtr" field for the display to get |
1285 |
* modified when the event is processed. This is needed to make |
1286 |
* sure that the grab window changes at the proper time relative |
1287 |
* to grab-related enter and leave events that are also in the |
1288 |
* queue. In particular, this approach works even when multiple |
1289 |
* grabs and ungrabs happen back-to-back. |
1290 |
* |
1291 |
* Results: |
1292 |
* None. |
1293 |
* |
1294 |
* Side effects: |
1295 |
* DispPtr->grabWinPtr will be modified later (by GrabWinEventProc) |
1296 |
* when the event is removed from the grab event queue. |
1297 |
* |
1298 |
*---------------------------------------------------------------------- |
1299 |
*/ |
1300 |
|
1301 |
static void |
1302 |
QueueGrabWindowChange(dispPtr, grabWinPtr) |
1303 |
TkDisplay *dispPtr; /* Display on which to change the grab |
1304 |
* window. */ |
1305 |
TkWindow *grabWinPtr; /* Window that is to become the new grab |
1306 |
* window (may be NULL). */ |
1307 |
{ |
1308 |
NewGrabWinEvent *grabEvPtr; |
1309 |
|
1310 |
grabEvPtr = (NewGrabWinEvent *) ckalloc(sizeof(NewGrabWinEvent)); |
1311 |
grabEvPtr->header.proc = GrabWinEventProc; |
1312 |
grabEvPtr->dispPtr = dispPtr; |
1313 |
if (grabWinPtr == NULL) { |
1314 |
grabEvPtr->grabWindow = None; |
1315 |
} else { |
1316 |
grabEvPtr->grabWindow = grabWinPtr->window; |
1317 |
} |
1318 |
Tcl_QueueEvent(&grabEvPtr->header, TCL_QUEUE_MARK); |
1319 |
dispPtr->eventualGrabWinPtr = grabWinPtr; |
1320 |
} |
1321 |
|
1322 |
/* |
1323 |
*---------------------------------------------------------------------- |
1324 |
* |
1325 |
* GrabWinEventProc -- |
1326 |
* |
1327 |
* This procedure is invoked as a handler for Tcl_Events of type |
1328 |
* NewGrabWinEvent. It updates the current grab window field in |
1329 |
* a display. |
1330 |
* |
1331 |
* Results: |
1332 |
* Returns 1 if the event was processed, 0 if it should be deferred |
1333 |
* for processing later. |
1334 |
* |
1335 |
* Side effects: |
1336 |
* The grabWinPtr field is modified in the display associated with |
1337 |
* the event. |
1338 |
* |
1339 |
*---------------------------------------------------------------------- |
1340 |
*/ |
1341 |
|
1342 |
static int |
1343 |
GrabWinEventProc(evPtr, flags) |
1344 |
Tcl_Event *evPtr; /* Event of type NewGrabWinEvent. */ |
1345 |
int flags; /* Flags argument to Tk_DoOneEvent: indicates |
1346 |
* what kinds of events are being processed |
1347 |
* right now. */ |
1348 |
{ |
1349 |
NewGrabWinEvent *grabEvPtr = (NewGrabWinEvent *) evPtr; |
1350 |
|
1351 |
grabEvPtr->dispPtr->grabWinPtr = (TkWindow *) Tk_IdToWindow( |
1352 |
grabEvPtr->dispPtr->display, grabEvPtr->grabWindow); |
1353 |
return 1; |
1354 |
} |
1355 |
|
1356 |
/* |
1357 |
*---------------------------------------------------------------------- |
1358 |
* |
1359 |
* FindCommonAncestor -- |
1360 |
* |
1361 |
* Given two windows, this procedure finds their least common |
1362 |
* ancestor and also computes how many levels up this ancestor |
1363 |
* is from each of the original windows. |
1364 |
* |
1365 |
* Results: |
1366 |
* If the windows are in different applications or top-level |
1367 |
* windows, then NULL is returned and *countPtr1 and *countPtr2 |
1368 |
* are set to the depths of the two windows in their respective |
1369 |
* top-level windows (1 means the window is a top-level, 2 means |
1370 |
* its parent is a top-level, and so on). Otherwise, the return |
1371 |
* value is a pointer to the common ancestor and the counts are |
1372 |
* set to the distance of winPtr1 and winPtr2 from this ancestor |
1373 |
* (1 means they're children, 2 means grand-children, etc.). |
1374 |
* |
1375 |
* Side effects: |
1376 |
* None. |
1377 |
* |
1378 |
*---------------------------------------------------------------------- |
1379 |
*/ |
1380 |
|
1381 |
static TkWindow * |
1382 |
FindCommonAncestor(winPtr1, winPtr2, countPtr1, countPtr2) |
1383 |
TkWindow *winPtr1; /* First window. May be NULL. */ |
1384 |
TkWindow *winPtr2; /* Second window. May be NULL. */ |
1385 |
int *countPtr1; /* Store nesting level of winPtr1 within |
1386 |
* common ancestor here. */ |
1387 |
int *countPtr2; /* Store nesting level of winPtr2 within |
1388 |
* common ancestor here. */ |
1389 |
{ |
1390 |
register TkWindow *winPtr; |
1391 |
TkWindow *ancestorPtr; |
1392 |
int count1, count2, i; |
1393 |
|
1394 |
/* |
1395 |
* Mark winPtr1 and all of its ancestors with a special flag bit. |
1396 |
*/ |
1397 |
|
1398 |
if (winPtr1 != NULL) { |
1399 |
for (winPtr = winPtr1; winPtr != NULL; winPtr = winPtr->parentPtr) { |
1400 |
winPtr->flags |= TK_GRAB_FLAG; |
1401 |
if (winPtr->flags & TK_TOP_LEVEL) { |
1402 |
break; |
1403 |
} |
1404 |
} |
1405 |
} |
1406 |
|
1407 |
/* |
1408 |
* Search upwards from winPtr2 until an ancestor of winPtr1 is |
1409 |
* found or a top-level window is reached. |
1410 |
*/ |
1411 |
|
1412 |
winPtr = winPtr2; |
1413 |
count2 = 0; |
1414 |
ancestorPtr = NULL; |
1415 |
if (winPtr2 != NULL) { |
1416 |
for (; winPtr != NULL; count2++, winPtr = winPtr->parentPtr) { |
1417 |
if (winPtr->flags & TK_GRAB_FLAG) { |
1418 |
ancestorPtr = winPtr; |
1419 |
break; |
1420 |
} |
1421 |
if (winPtr->flags & TK_TOP_LEVEL) { |
1422 |
count2++; |
1423 |
break; |
1424 |
} |
1425 |
} |
1426 |
} |
1427 |
|
1428 |
/* |
1429 |
* Search upwards from winPtr1 again, clearing the flag bits and |
1430 |
* remembering how many levels up we had to go. |
1431 |
*/ |
1432 |
|
1433 |
if (winPtr1 == NULL) { |
1434 |
count1 = 0; |
1435 |
} else { |
1436 |
count1 = -1; |
1437 |
for (i = 0, winPtr = winPtr1; winPtr != NULL; |
1438 |
i++, winPtr = winPtr->parentPtr) { |
1439 |
winPtr->flags &= ~TK_GRAB_FLAG; |
1440 |
if (winPtr == ancestorPtr) { |
1441 |
count1 = i; |
1442 |
} |
1443 |
if (winPtr->flags & TK_TOP_LEVEL) { |
1444 |
if (count1 == -1) { |
1445 |
count1 = i+1; |
1446 |
} |
1447 |
break; |
1448 |
} |
1449 |
} |
1450 |
} |
1451 |
|
1452 |
*countPtr1 = count1; |
1453 |
*countPtr2 = count2; |
1454 |
return ancestorPtr; |
1455 |
} |
1456 |
|
1457 |
/* |
1458 |
*---------------------------------------------------------------------- |
1459 |
* |
1460 |
* TkPositionInTree -- |
1461 |
* |
1462 |
* Compute where the given window is relative to a particular |
1463 |
* subtree of the window hierarchy. |
1464 |
* |
1465 |
* Results: |
1466 |
* |
1467 |
* Returns TK_GRAB_IN_TREE if the window is contained in the |
1468 |
* subtree. Returns TK_GRAB_ANCESTOR if the window is an |
1469 |
* ancestor of the subtree, in the same toplevel. Otherwise |
1470 |
* it returns TK_GRAB_EXCLUDED. |
1471 |
* |
1472 |
* Side effects: |
1473 |
* None. |
1474 |
* |
1475 |
*---------------------------------------------------------------------- |
1476 |
*/ |
1477 |
|
1478 |
int |
1479 |
TkPositionInTree(winPtr, treePtr) |
1480 |
TkWindow *winPtr; /* Window to be checked. */ |
1481 |
TkWindow *treePtr; /* Root of tree to compare against. */ |
1482 |
{ |
1483 |
TkWindow *winPtr2; |
1484 |
|
1485 |
for (winPtr2 = winPtr; winPtr2 != treePtr; |
1486 |
winPtr2 = winPtr2->parentPtr) { |
1487 |
if (winPtr2 == NULL) { |
1488 |
for (winPtr2 = treePtr; winPtr2 != NULL; |
1489 |
winPtr2 = winPtr2->parentPtr) { |
1490 |
if (winPtr2 == winPtr) { |
1491 |
return TK_GRAB_ANCESTOR; |
1492 |
} |
1493 |
if (winPtr2->flags & TK_TOP_LEVEL) { |
1494 |
break; |
1495 |
} |
1496 |
} |
1497 |
return TK_GRAB_EXCLUDED; |
1498 |
} |
1499 |
} |
1500 |
return TK_GRAB_IN_TREE; |
1501 |
} |
1502 |
|
1503 |
/* |
1504 |
*---------------------------------------------------------------------- |
1505 |
* |
1506 |
* TkGrabState -- |
1507 |
* |
1508 |
* Given a window, this procedure returns a value that indicates |
1509 |
* the grab state of the application relative to the window. |
1510 |
* |
1511 |
* Results: |
1512 |
* The return value is one of three things: |
1513 |
* TK_GRAB_NONE - no grab is in effect. |
1514 |
* TK_GRAB_IN_TREE - there is a grab in effect, and winPtr |
1515 |
* is in the grabbed subtree. |
1516 |
* TK_GRAB_ANCESTOR - there is a grab in effect; winPtr is |
1517 |
* an ancestor of the grabbed window, in |
1518 |
* the same toplevel. |
1519 |
* TK_GRAB_EXCLUDED - there is a grab in effect; winPtr is |
1520 |
* outside the tree of the grab and is not |
1521 |
* an ancestor of the grabbed window in the |
1522 |
* same toplevel. |
1523 |
* |
1524 |
* Side effects: |
1525 |
* None. |
1526 |
* |
1527 |
*---------------------------------------------------------------------- |
1528 |
*/ |
1529 |
|
1530 |
int |
1531 |
TkGrabState(winPtr) |
1532 |
TkWindow *winPtr; /* Window for which grab information is |
1533 |
* needed. */ |
1534 |
{ |
1535 |
TkWindow *grabWinPtr = winPtr->dispPtr->grabWinPtr; |
1536 |
|
1537 |
if (grabWinPtr == NULL) { |
1538 |
return TK_GRAB_NONE; |
1539 |
} |
1540 |
if ((winPtr->mainPtr != grabWinPtr->mainPtr) |
1541 |
&& !(winPtr->dispPtr->grabFlags & GRAB_GLOBAL)) { |
1542 |
return TK_GRAB_NONE; |
1543 |
} |
1544 |
|
1545 |
return TkPositionInTree(winPtr, grabWinPtr); |
1546 |
} |
1547 |
|
1548 |
/* End of tkgrab.c */ |