]> git.zerfleddert.de Git - micropolis/blob - src/tk/tkgrab.c
still more NumLock fixes, this time for scrollbars and sliders
[micropolis] / src / tk / tkgrab.c
1 /*
2 * tkGrab.c --
3 *
4 * This file provides procedures that implement grabs for Tk.
5 *
6 * Copyright 1992 Regents of the University of California.
7 * Permission to use, copy, modify, and distribute this
8 * software and its documentation for any purpose and without
9 * fee is hereby granted, provided that the above copyright
10 * notice appear in all copies. The University of California
11 * makes no representations about the suitability of this
12 * software for any purpose. It is provided "as is" without
13 * express or implied warranty.
14 */
15
16 #ifndef lint
17 static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkGrab.c,v 1.18 92/08/07 09:55:31 ouster Exp $ SPRITE (Berkeley)";
18 #endif
19
20 #include "tkconfig.h"
21 #include "tkint.h"
22
23 /*
24 *-------------------------------------------------------------------
25 * Problems with current grab implementation (8/7/92):
26 *
27 * 1. In a local grab the synthesized events are always placed at the
28 * front of the event queue. If there are several grabs and ungrabs
29 * in a row, the groups of events for the different grabs/ungrabs
30 * end up in backwards order.
31 * 2. The variables serverWinPtr and pointerWinPtr are hardly used at
32 * all and should probably be eliminated.
33 * 3. The fact that grabWinPtr is set at the time a grab is set or
34 * released, rather than when its events are processed, means that
35 * it can get out of sync with the event queue if there's a rapid
36 * sequence of grabs or ungrabs. The only solution I can think of
37 * is to keep a parallel queue to the event queue to update grabWinPtr
38 * (or, synthesize an event to change the pointer?).
39 *-------------------------------------------------------------------
40 */
41
42 /*
43 * Bit definitions for grabFlags field of TkDisplay structures:
44 *
45 * GRAB_GLOBAL 1 means this is a global grab (we grabbed via
46 * the server so all applications are locked out.
47 * 0 means this is a local grab that affects
48 * only this application.
49 * GRAB_BUTTON_RELEASE 1 means that a button-release event just
50 * occurred and we're in the middle of a sequence
51 * of Enter and Leave events with NotifyUngrab
52 * mode.
53 */
54
55 #define GRAB_GLOBAL 1
56 #define GRAB_BUTTON_RELEASE 2
57
58 /*
59 * Forward declarations for procedures declared later in this file:
60 */
61
62 static void ChangeEventWindow _ANSI_ARGS_((XEvent *eventPtr,
63 TkWindow *winPtr));
64 static void MovePointer _ANSI_ARGS_((XEvent *eventPtr,
65 TkWindow *sourcePtr, TkWindow *destPtr));
66 static void MovePointer2 _ANSI_ARGS_((TkWindow *sourcePtr,
67 TkWindow *destPtr, int mode));
68 \f
69 /*
70 *----------------------------------------------------------------------
71 *
72 * Tk_GrabCmd --
73 *
74 * This procedure is invoked to process the "grab" Tcl command.
75 * See the user documentation for details on what it does.
76 *
77 * Results:
78 * A standard Tcl result.
79 *
80 * Side effects:
81 * See the user documentation.
82 *
83 *----------------------------------------------------------------------
84 */
85
86 /* ARGSUSED */
87 int
88 Tk_GrabCmd(clientData, interp, argc, argv)
89 ClientData clientData; /* Main window associated with
90 * interpreter. */
91 Tcl_Interp *interp; /* Current interpreter. */
92 int argc; /* Number of arguments. */
93 char **argv; /* Argument strings. */
94 {
95 TkWindow *winPtr = (TkWindow *) clientData;
96 int length, lockScreen;
97 char *window;
98
99 if (argc > 3) {
100 badArgs:
101 Tcl_AppendResult(interp, "wrong # args: should be \"",
102 argv[0], " ?-global? ?window?\"", (char *) NULL);
103 return TCL_ERROR;
104 }
105 if (argc == 1) {
106 if ((winPtr->dispPtr->grabWinPtr != NULL)
107 && (winPtr->dispPtr->grabWinPtr->mainPtr
108 == winPtr->mainPtr)) {
109 interp->result = Tk_PathName(winPtr->dispPtr->grabWinPtr);
110 } else {
111 interp->result = "none";
112 }
113 return TCL_OK;
114 }
115 if (argc == 3) {
116 length = strlen(argv[1]);
117 if (strncmp(argv[1], "-off", length) == 0) {
118 lockScreen = -1;
119 } else {
120 if ((strncmp(argv[1], "-global", length) != 0) || (length < 2)) {
121 goto badArgs;
122 }
123 lockScreen = 1;
124 }
125 window = argv[2];
126 } else {
127 lockScreen = 0;
128 window = argv[1];
129 }
130 if ((window[0] == '\0')
131 || (strncmp(window, "none", strlen(window)) == 0)) {
132 Tk_Ungrab((Tk_Window) winPtr);
133 } else {
134 Tk_Window tkwin;
135
136 tkwin = Tk_NameToWindow(interp, window, (Tk_Window) winPtr);
137 if (tkwin == NULL) {
138 return TCL_ERROR;
139 }
140 if (lockScreen < 0) {
141 Tk_Ungrab(tkwin);
142 } else {
143 return Tk_Grab(interp, tkwin, lockScreen);
144 }
145 }
146 return TCL_OK;
147 }
148 \f
149 /*
150 *----------------------------------------------------------------------
151 *
152 * Tk_Grab --
153 *
154 * Grabs the pointer and keyboard, so that mouse-related events are
155 * only reported relative to a given window and its descendants.
156 *
157 * Results:
158 * A standard Tcl result is returned. TCL_OK is the normal return
159 * value; if the grab could not be set then TCL_ERROR is returned
160 * and interp->result will hold an error message.
161 *
162 * Side effects:
163 * Once this call completes successfully, no window outside the
164 * tree rooted at tkwin will receive pointer- or keyboard-related
165 * events until the next call to Tk_Ungrab. If a previous grab was
166 * in effect within this application, then it is replaced with a new
167 * one.
168 *
169 *----------------------------------------------------------------------
170 */
171
172 int
173 Tk_Grab(interp, tkwin, grabGlobal)
174 Tcl_Interp *interp; /* Used for error reporting. */
175 Tk_Window tkwin; /* Window on whose behalf the pointer
176 * is to be grabbed. */
177 int grabGlobal; /* Non-zero means issue a grab to the
178 * server so that no other application
179 * gets mouse or keyboard events.
180 * Zero means the grab only applies
181 * within this application. */
182 {
183 int grabResult;
184 TkWindow *winPtr = (TkWindow *) tkwin;
185 TkDisplay *dispPtr = winPtr->dispPtr;
186 int grabRequest, inSequence, ignoring, numEvents, i, diff;
187 XEvent *events, *eventPtr;
188 TkWindow *winPtr2;
189
190 if (dispPtr->grabWinPtr != NULL) {
191 if ((dispPtr->grabWinPtr == winPtr)
192 && (grabGlobal == ((dispPtr->grabFlags & GRAB_GLOBAL) != 0))) {
193 return TCL_OK;
194 }
195 if (dispPtr->grabWinPtr->mainPtr != winPtr->mainPtr) {
196 alreadyGrabbed:
197 interp->result = "grab failed: another application has grab";
198 return TCL_ERROR;
199 }
200 Tk_Ungrab(tkwin);
201 }
202
203 if (grabGlobal) {
204 grabRequest = NextRequest(dispPtr->display);
205 grabResult = XGrabPointer(dispPtr->display, Tk_WindowId(tkwin),
206 True, ButtonPressMask|ButtonReleaseMask|ButtonMotionMask|PointerMotionMask,
207 GrabModeAsync, GrabModeAsync, None, None,
208 TkCurrentTime(dispPtr));
209 if (grabResult != 0) {
210 grabError:
211 if (grabResult == GrabNotViewable) {
212 interp->result = "grab failed: window not viewable";
213 } else if (grabResult == AlreadyGrabbed) {
214 goto alreadyGrabbed;
215 } else if (grabResult == GrabFrozen) {
216 interp->result = "grab failed: keyboard or pointer frozen";
217 } else if (grabResult == GrabInvalidTime) {
218 interp->result = "grab failed: invalid time";
219 } else {
220 char msg[100];
221
222 sprintf(msg, "grab failed for unknown reason (code %d)",
223 grabResult);
224 Tcl_AppendResult(interp, msg, (char *) NULL);
225 }
226 return TCL_ERROR;
227 }
228 grabResult = XGrabKeyboard(dispPtr->display, Tk_WindowId(tkwin),
229 False, GrabModeAsync, GrabModeAsync, TkCurrentTime(dispPtr));
230 if (grabResult != 0) {
231 XUngrabPointer(dispPtr->display, TkCurrentTime(dispPtr));
232 goto grabError;
233 }
234 dispPtr->grabFlags |= GRAB_GLOBAL;
235 } else {
236 /*
237 * The call to XUngrabPointer below is needed to release any
238 * existing auto-grab due to a button press. This is needed
239 * so that local grabs behave the same as global grabs (the
240 * button grab is released by the X server in a global grab).
241 */
242
243 XUngrabPointer(dispPtr->display, TkCurrentTime(dispPtr));
244 grabRequest = LastKnownRequestProcessed(dispPtr->display);
245 dispPtr->grabFlags &= ~GRAB_GLOBAL;
246
247 /*
248 * Since we're not telling the server about the grab, we have
249 * to generate Leave and Enter events to move the pointer from
250 * its current window to the grab window.
251 */
252
253 MovePointer2(dispPtr->pointerWinPtr, winPtr, NotifyGrab);
254 }
255 dispPtr->grabWinPtr = winPtr;
256
257 /*
258 * When a grab occurs, X generates Enter and Leave events to move
259 * the pointer from its current window to the grab window, even if
260 * the current window is in the grab tree. We don't want these
261 * events getting through to the application if the current window
262 * is in the grab tree. In order to eliminate the bogus events,
263 * process all pending events and filter out the bogus ones.
264 *
265 * Also, filter out the final enter event into the grab window in
266 * any case: this event shouldn't be delivered until the mouse really
267 * moves into that window.
268 *
269 * The code below reads in all the pending events, filters out the bad
270 * ones, and then pushes back all the events that weren't filtered.
271 * Another alternative would be to simply process the events
272 * immediately rather than pushing them back again. However, this
273 * tends to interfere with scripts since it causes pending events
274 * to be processed during the "grab" command. The "grab" command
275 * might have been invoked in the middle of some computation where
276 * it's a bad idea to process new events.
277 */
278
279 XSync(dispPtr->display, False);
280 numEvents = QLength(dispPtr->display);
281 if (numEvents == 0) {
282 return TCL_OK;
283 }
284 events = (XEvent *) ckalloc((unsigned) (numEvents * sizeof(XEvent)));
285 for (i = 0; i < numEvents; i++) {
286 XNextEvent(dispPtr->display, &events[i]);
287 }
288 inSequence = ignoring = 0;
289 for (i = numEvents-1, eventPtr = events; i >= 0; i--, eventPtr++) {
290 if (((eventPtr->type != EnterNotify)
291 && (eventPtr->type != LeaveNotify))
292 || (eventPtr->xcrossing.mode != NotifyGrab)) {
293 continue;
294 }
295
296 /*
297 * The diff caculcation below is trickier than you might think,
298 * due to the fact that the event serial number is unsigned and
299 * serial numbers can wrap around.
300 */
301
302 diff = eventPtr->xcrossing.serial;
303 diff -= grabRequest;
304 if (!inSequence && (diff >= 0)) {
305 /*
306 * This is the first event of the grab sequence. See if its
307 * window is in the grab tree and ignore the sequence if it is.
308 */
309
310 inSequence = 1;
311 if (XFindContext(dispPtr->display, eventPtr->xcrossing.window,
312 tkWindowContext, (void *) &winPtr2) == 0) {
313 for ( ; winPtr2 != NULL; winPtr2 = winPtr2->parentPtr) {
314 if (winPtr2 == dispPtr->grabWinPtr) {
315 ignoring = 1;
316 break;
317 }
318 }
319 }
320 }
321 if (ignoring) {
322 eventPtr->type = 0;
323 }
324 if (inSequence && (eventPtr->type == EnterNotify)
325 && (dispPtr->grabWinPtr->window
326 == eventPtr->xcrossing.window)) {
327 eventPtr->type = 0;
328 break;
329 }
330 }
331 for (i = numEvents-1, eventPtr = &events[i]; i >= 0; i--, eventPtr--) {
332 if (eventPtr->type != 0) {
333 XPutBackEvent(dispPtr->display, eventPtr);
334 }
335 }
336 ckfree((char *) events);
337 return TCL_OK;
338 }
339 \f
340 /*
341 *----------------------------------------------------------------------
342 *
343 * Tk_Ungrab --
344 *
345 * Releases a grab on the mouse pointer and keyboard.
346 *
347 * Results:
348 * None.
349 *
350 * Side effects:
351 * Pointer and keyboard events will start being delivered to other
352 * windows again.
353 *
354 *----------------------------------------------------------------------
355 */
356
357 void
358 Tk_Ungrab(tkwin)
359 Tk_Window tkwin; /* Window that identifies display
360 * for grab to be released. */
361 {
362 TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
363 int inSequence, ignoring, ungrabRequest, numEvents, i, j, diff;
364 TkWindow *grabWinPtr, *winPtr;
365 XEvent *events, *eventPtr, *eventPtr2;
366
367 grabWinPtr = dispPtr->grabWinPtr;
368 if (grabWinPtr == NULL) {
369 return;
370 }
371 dispPtr->grabWinPtr = NULL;
372 dispPtr->buttonWinPtr = NULL;
373 if (dispPtr->grabFlags & GRAB_GLOBAL) {
374 ungrabRequest = NextRequest(dispPtr->display);
375 XUngrabPointer(dispPtr->display, TkCurrentTime(dispPtr));
376 XUngrabKeyboard(dispPtr->display, TkCurrentTime(dispPtr));
377 XSync(dispPtr->display, False);
378 } else {
379 ungrabRequest = LastKnownRequestProcessed(dispPtr->display);
380 if ((dispPtr->ungrabWinPtr != NULL)
381 && (dispPtr->ungrabWinPtr->mainPtr != grabWinPtr->mainPtr)) {
382
383 /*
384 * Don't report entries down into a window of a different
385 * application, since it's already seen those entries earlier.
386 */
387
388 dispPtr->ungrabWinPtr = NULL;
389 }
390 MovePointer2(grabWinPtr, dispPtr->ungrabWinPtr, NotifyUngrab);
391 }
392
393 /*
394 * We have to filter all the pending events in a fashion similar to
395 * Tk_Grab. As with grabs, the X server generates an Enter-Leave event
396 * sequence to move the pointer from the grab window back to its
397 * current window. We need to ignore this sequence if the pointer
398 * is being moved to a window that's already in the grab tree.
399 */
400
401 numEvents = QLength(dispPtr->display);
402 if (numEvents == 0) {
403 return;
404 }
405 events = (XEvent *) ckalloc((unsigned) (numEvents * sizeof(XEvent)));
406 for (i = 0; i < numEvents; i++) {
407 XNextEvent(dispPtr->display, &events[i]);
408 }
409 inSequence = ignoring = 0;
410 for (i = numEvents-1, eventPtr = events; i >= 0; i--, eventPtr++) {
411 if (((eventPtr->type != EnterNotify)
412 && (eventPtr->type != LeaveNotify))
413 || (eventPtr->xcrossing.mode != NotifyUngrab)) {
414 continue;
415 }
416 diff = eventPtr->xcrossing.serial;
417 diff -= ungrabRequest;
418 if (!inSequence && (diff >= 0)) {
419
420 /*
421 * This is the first event of the ungrab sequence. Scan forward
422 * looking for the final Enter event in the sequence. Then see
423 * if that event's window is in the grab tree.
424 */
425
426 inSequence = 1;
427 for (j = i, eventPtr2 = eventPtr; j >= 0; j--, eventPtr2++) {
428 if (eventPtr2->type == EnterNotify) {
429 if (eventPtr2->xcrossing.mode != NotifyUngrab) {
430 break;
431 }
432 if ((eventPtr2->xcrossing.detail != NotifyAncestor)
433 && (eventPtr2->xcrossing.detail != NotifyInferior)
434 && (eventPtr2->xcrossing.detail
435 != NotifyNonlinear)) {
436 continue;
437 }
438 if (XFindContext(dispPtr->display,
439 eventPtr2->xcrossing.window,
440 tkWindowContext, (void *) &winPtr) == 0) {
441 for ( ; winPtr != NULL; winPtr = winPtr->parentPtr) {
442 if (winPtr == grabWinPtr) {
443 ignoring = 1;
444 break;
445 }
446 }
447 }
448 break;
449 } else if ((eventPtr2->type != LeaveNotify)
450 || (eventPtr2->xcrossing.mode != NotifyUngrab)) {
451 break;
452 }
453 }
454 }
455 if (ignoring) {
456 eventPtr->type = 0;
457 }
458 }
459 for (i = numEvents-1, eventPtr = &events[i]; i >= 0; i--, eventPtr--) {
460 if (eventPtr->type != 0) {
461 XPutBackEvent(dispPtr->display, eventPtr);
462 }
463 }
464 ckfree((char *) events);
465 }
466 \f
467 /*
468 *----------------------------------------------------------------------
469 *
470 * TkPointerEvent --
471 *
472 * This procedure is called for each pointer-related event, before
473 * the event has been processed. It does various things to make
474 * grabs work correctly.
475 *
476 * Results:
477 * If the return value is 1 it means the event should be processed
478 * (event handlers should be invoked). If the return value is 0
479 * it means the event should be ignored in order to make grabs
480 * work correctly. Note: the event may be modified by this procedure.
481 *
482 * Side effects:
483 * Grab state information may be updated.
484 *
485 *----------------------------------------------------------------------
486 */
487
488 int
489 TkPointerEvent(eventPtr, winPtr)
490 register XEvent *eventPtr; /* Pointer to the event. */
491 TkWindow *winPtr; /* Tk's information for window
492 * where event was reported. */
493 {
494 register TkWindow *winPtr2;
495 TkDisplay *dispPtr = winPtr->dispPtr;
496 int outsideGrabTree = 0;
497 int originalFlags;
498 int appGrabbed = 0; /* Non-zero means event is being
499 * reported to an application that is
500 * affected by the grab. */
501 static unsigned int state[] = {
502 Button1Mask, Button2Mask, Button3Mask, Button4Mask, Button5Mask
503 };
504
505 /*
506 * Don't do any filtering on events generated by the event-sharing code.
507 */
508
509 if (eventPtr == tkShareEventPtr) {
510 return 1;
511 }
512
513 /*
514 * If a grab is in effect, see if the event is being reported to
515 * a window in the grab tree. Also see if the event is being reported
516 * to an application that is affected by the grab.
517 */
518
519 if (dispPtr->grabWinPtr != NULL) {
520 if ((winPtr->mainPtr == dispPtr->grabWinPtr->mainPtr)
521 || (dispPtr->grabFlags & GRAB_GLOBAL)) {
522 appGrabbed = 1;
523 }
524 for (winPtr2 = winPtr; winPtr2 != dispPtr->grabWinPtr;
525 winPtr2 = winPtr2->parentPtr) {
526 if (winPtr2 == NULL) {
527 outsideGrabTree = 1;
528 break;
529 }
530 }
531 }
532
533 originalFlags = dispPtr->grabFlags;
534 dispPtr->grabFlags &= ~GRAB_BUTTON_RELEASE;
535 if ((eventPtr->type == EnterNotify) || (eventPtr->type == LeaveNotify)) {
536 if ((eventPtr->type == EnterNotify)
537 && (eventPtr->xcrossing.detail != NotifyVirtual)
538 && (eventPtr->xcrossing.detail != NotifyNonlinearVirtual)) {
539 if ((dispPtr->grabWinPtr == NULL)
540 || (dispPtr->grabWinPtr->mainPtr == winPtr->mainPtr)) {
541 dispPtr->ungrabWinPtr = winPtr;
542 }
543 dispPtr->serverWinPtr = winPtr;
544 } else {
545 dispPtr->serverWinPtr = NULL;
546 }
547 if (dispPtr->grabWinPtr != NULL) {
548 if (eventPtr->xcrossing.mode == NotifyNormal) {
549 /*
550 * When a grab is active, X continues to report enter and
551 * leave events for windows outside the tree of the grab
552 * window. Detect these events and ignore them.
553 */
554
555 if (outsideGrabTree && appGrabbed) {
556 return 0;
557 }
558
559 /*
560 * Make buttons have the same grab-like behavior inside a grab
561 * as they do outside a grab: do this by ignoring enter and
562 * leave events except for the window in which the button was
563 * pressed.
564 */
565
566 if ((dispPtr->buttonWinPtr != NULL)
567 && (winPtr != dispPtr->buttonWinPtr)) {
568 return 0;
569 }
570 } else if (eventPtr->xcrossing.mode == NotifyUngrab) {
571 /*
572 * Keep the GRAB_BUTTON_RELEASE flag on if it used to be on.
573 */
574
575 dispPtr->grabFlags = originalFlags;
576 if (outsideGrabTree && appGrabbed
577 && (dispPtr->grabFlags & GRAB_BUTTON_RELEASE)) {
578 /*
579 * The only way we get here is if a button was pressed,
580 * then moved to a different window and released. Enter
581 * and leave events were deferred while the button was
582 * down, but now we're getting them to move the pointer
583 * back to the right window, and this particular event
584 * is for a window outside the grab tree. Ignore it.
585 */
586
587 return 0;
588 }
589 }
590 }
591
592 /*
593 * Keep track of the window containing the mouse, in order to
594 * detect various bogus event sequences.
595 */
596
597 dispPtr->pointerWinPtr = dispPtr->serverWinPtr;
598 return 1;
599 }
600 if ((dispPtr->grabWinPtr == NULL) || !appGrabbed) {
601 return 1;
602 }
603
604 if (eventPtr->type == MotionNotify) {
605 /*
606 * When grabs are active, X reports motion events relative to the
607 * window under the pointer. Instead, it should report the events
608 * relative to the window the button went down in, if there is a
609 * button down. Otherwise, if the pointer window is outside the
610 * subtree of the grab window, the events should be reported
611 * relative to the grab window. Otherwise, the event should be
612 * reported to the pointer window.
613 */
614
615 winPtr2 = winPtr;
616 if (dispPtr->buttonWinPtr != NULL) {
617 winPtr2 = dispPtr->buttonWinPtr;
618 } else if (outsideGrabTree || (dispPtr->serverWinPtr == NULL)) {
619 winPtr2 = dispPtr->grabWinPtr;
620 }
621 if (winPtr2 != winPtr) {
622 XEvent newEvent;
623
624 newEvent = *eventPtr;
625 ChangeEventWindow(&newEvent, winPtr2);
626 XPutBackEvent(winPtr2->display, &newEvent);
627 return 0;
628 }
629 return 1;
630 }
631
632 /*
633 * Process ButtonPress and ButtonRelease events:
634 * 1. Keep track of whether a button is down and what window it
635 * went down in.
636 * 2. If the first button goes down outside the grab tree, pretend
637 * it went down in the grab window. Note: it's important to
638 * redirect events to the grab window like this in order to make
639 * things like menus work, where button presses outside the
640 * grabbed menu need to be seen. An application can always
641 * ignore the events if they occur outside its window.
642 * 3. If a button press or release occurs outside the window where
643 * the first button was pressed, retarget the event so it's reported
644 * to the window where the first button was pressed.
645 * 4. If the last button is released in a window different than where
646 * the first button was pressed, generate Enter/Leave events to
647 * move the mouse from the button window to its current window.
648 * 5. If the grab is set at a time when a button is already down, or
649 * if the window where the button was pressed was deleted, then
650 * dispPtr->buttonWinPtr will stay NULL. Just forget about the
651 * auto-grab for the button press; events will go to whatever
652 * window contains the pointer. If this window isn't in the grab
653 * tree then redirect events to the grab window.
654 */
655
656 if ((eventPtr->type == ButtonPress) || (eventPtr->type == ButtonRelease)) {
657 winPtr2 = dispPtr->buttonWinPtr;
658 if (winPtr2 == NULL) {
659 if (outsideGrabTree) {
660 winPtr2 = dispPtr->grabWinPtr; /* Note 5. */
661 } else {
662 winPtr2 = winPtr; /* Note 5. */
663 }
664 }
665 if (eventPtr->type == ButtonPress) {
666 if ((eventPtr->xbutton.state & ALL_BUTTONS) == 0) {
667 if (outsideGrabTree) {
668 XEvent newEvent;
669
670 newEvent = *eventPtr;
671 ChangeEventWindow(&newEvent, dispPtr->grabWinPtr);
672 XPutBackEvent(dispPtr->display, &newEvent);
673 return 0; /* Note 2. */
674 }
675 dispPtr->buttonWinPtr = winPtr;
676 return 1;
677 }
678 } else {
679 if ((eventPtr->xbutton.state & ALL_BUTTONS)
680 == state[eventPtr->xbutton.button - Button1]) {
681 if ((dispPtr->buttonWinPtr != winPtr)
682 && (dispPtr->buttonWinPtr != NULL)) {
683 XEvent newEvent; /* Note 4. */
684
685 /*
686 * If the button release is made with pointer outside
687 * all applications, X reports it relative to the grab
688 * window. Change the current window to NULL to
689 * reflect that the pointer's outside everything. Do
690 * the same if the pointer's in a window that's not
691 * part of the grab tree.
692 */
693
694 if (outsideGrabTree || (dispPtr->serverWinPtr == NULL)) {
695 winPtr = NULL;
696 }
697 newEvent = *eventPtr;
698 newEvent.xcrossing.mode = NotifyUngrab;
699 newEvent.xcrossing.focus = False;
700 newEvent.xcrossing.state =
701 eventPtr->xbutton.state & ~ALL_BUTTONS;
702 MovePointer(&newEvent, dispPtr->buttonWinPtr, winPtr);
703 }
704 dispPtr->buttonWinPtr = NULL;
705 dispPtr->grabFlags |= GRAB_BUTTON_RELEASE;
706 }
707 }
708 if (winPtr2 != winPtr) {
709 XEvent newEvent;
710
711 newEvent = *eventPtr;
712 ChangeEventWindow(&newEvent, winPtr2);
713 XPutBackEvent(dispPtr->display, &newEvent);
714 return 0; /* Note 3. */
715 }
716 }
717
718 return 1;
719 }
720 \f
721 /*
722 *----------------------------------------------------------------------
723 *
724 * ChangeEventWindow --
725 *
726 * Given an event and a new window to which the event should be
727 * retargeted, modify fields of the event so that the event is
728 * properly retargeted to the new window.
729 *
730 * Results:
731 * The following fields of eventPtr are modified: window,
732 * subwindow, x, y, same_screen.
733 *
734 * Side effects:
735 * None.
736 *
737 *----------------------------------------------------------------------
738 */
739
740 static void
741 ChangeEventWindow(eventPtr, winPtr)
742 register XEvent *eventPtr; /* Event to retarget. Must have
743 * type ButtonPress, ButtonRelease, KeyPress,
744 * KeyRelease, MotionNotify, EnterNotify,
745 * or LeaveNotify. */
746 TkWindow *winPtr; /* New target window for event. */
747 {
748 int x, y, sameScreen, bd;
749 register TkWindow *childPtr;
750
751 eventPtr->xmotion.window = Tk_WindowId(winPtr);
752 if (eventPtr->xmotion.root ==
753 RootWindow(winPtr->display, winPtr->screenNum)) {
754 Tk_GetRootCoords((Tk_Window) winPtr, &x, &y);
755 eventPtr->xmotion.x = eventPtr->xmotion.x_root - x;
756 eventPtr->xmotion.y = eventPtr->xmotion.y_root - y;
757 eventPtr->xmotion.subwindow = None;
758 for (childPtr = winPtr->childList; childPtr != NULL;
759 childPtr = childPtr->nextPtr) {
760 if (childPtr->flags & TK_TOP_LEVEL) {
761 continue;
762 }
763 x = eventPtr->xmotion.x - childPtr->changes.x;
764 y = eventPtr->xmotion.y - childPtr->changes.y;
765 bd = childPtr->changes.border_width;
766 if ((x >= -bd) && (y >= -bd)
767 && (x < (childPtr->changes.width + bd))
768 && (y < (childPtr->changes.width + bd))) {
769 eventPtr->xmotion.subwindow = childPtr->window;
770 }
771 }
772 sameScreen = 1;
773 } else {
774 eventPtr->xmotion.x = 0;
775 eventPtr->xmotion.y = 0;
776 eventPtr->xmotion.subwindow = None;
777 sameScreen = 0;
778 }
779 if (eventPtr->type == MotionNotify) {
780 eventPtr->xmotion.same_screen = sameScreen;
781 } else {
782 eventPtr->xbutton.same_screen = sameScreen;
783 }
784 }
785 \f
786 /*
787 *----------------------------------------------------------------------
788 *
789 * MovePointer --
790 *
791 * This procedure synthesizes EnterNotify and LeaveNotify events
792 * to correctly transfer the pointer from one window to another.
793 *
794 * Results:
795 * None.
796 *
797 * Side effects:
798 * Synthesized events may be pushed back onto the event queue.
799 * The event pointed to by eventPtr is modified.
800 *
801 *----------------------------------------------------------------------
802 */
803
804 static void
805 MovePointer(eventPtr, sourcePtr, destPtr)
806 XEvent *eventPtr; /* A template X event. Must have all fields
807 * properly set for EnterNotify and LeaveNotify
808 * events except window, subwindow, x, y,
809 * detail, and same_screen. (x_root and y_root
810 * must be valid, even though x and y needn't
811 * be valid). */
812 TkWindow *sourcePtr; /* Window currently containing pointer (NULL
813 * means it's not one managed by this
814 * process). */
815 TkWindow *destPtr; /* Window that is to end up containing the
816 * pointer (NULL means it's not one managed
817 * by this process). */
818 {
819 TkDisplay *dispPtr;
820 register TkWindow *ancestorPtr; /* Lowest ancestor shared between
821 * sourcePtr and destPtr, or
822 * sourcePtr's top-level window if no
823 * shared ancestor. */
824 register TkWindow *winPtr;
825 int upLevels, downLevels, i, j;
826
827 /*
828 * There are four possible cases to deal with:
829 *
830 * 1. SourcePtr and destPtr are the same. There's nothing to do in
831 * this case.
832 * 2. SourcePtr is an ancestor of destPtr in the same top-level
833 * window. Must generate events down the window tree from source
834 * to dest.
835 * 3. DestPtr is an ancestor of sourcePtr in the same top-level
836 * window. Must generate events up the window tree from sourcePtr
837 * to destPtr.
838 * 4. All other cases. Must first generate events up the window tree
839 * from sourcePtr to its top-level, then down from destPtr's
840 * top-level to destPtr. This form is called "non-linear."
841 *
842 * The code below separates these four cases and decides how many levels
843 * up and down events have to be generated for.
844 */
845
846 if (sourcePtr == destPtr) {
847 return;
848 }
849
850 /*
851 * Mark destPtr and all of its ancestors with a special flag bit.
852 */
853
854 if (destPtr != NULL) {
855 dispPtr = destPtr->dispPtr;
856 for (winPtr = destPtr; ; winPtr = winPtr->parentPtr) {
857 winPtr->flags |= TK_GRAB_FLAG;
858 if (winPtr->flags & TK_TOP_LEVEL) {
859 break;
860 }
861 }
862 } else {
863 dispPtr = sourcePtr->dispPtr;
864 }
865
866 /*
867 * Search upwards from sourcePtr until an ancestor of destPtr is
868 * found or a top-level window is reached. Remember if we pass out
869 * of the grab tree along the way, since this means we'll have to
870 * skip some of the events that would otherwise be generated.
871 */
872
873 ancestorPtr = sourcePtr;
874 upLevels = 0;
875 if (sourcePtr != NULL) {
876 for (; ; upLevels++, ancestorPtr = ancestorPtr->parentPtr) {
877 if (ancestorPtr->flags & TK_GRAB_FLAG) {
878 break;
879 }
880 if (ancestorPtr->flags & TK_TOP_LEVEL) {
881 upLevels++;
882 break;
883 }
884 }
885 }
886
887 /*
888 * Search upwards from destPtr again, clearing the flag bits and
889 * remembering how many levels up we had to go.
890 */
891
892 if (destPtr == NULL) {
893 downLevels = 0;
894 } else {
895 downLevels = -1;
896 for (i = 0, winPtr = destPtr; ; i++, winPtr = winPtr->parentPtr) {
897 winPtr->flags &= ~TK_GRAB_FLAG;
898 if (winPtr == ancestorPtr) {
899 downLevels = i;
900 }
901 if (winPtr->flags & TK_TOP_LEVEL) {
902 if (downLevels == -1) {
903 downLevels = i+1;
904 }
905 break;
906 }
907 }
908 }
909
910 /*
911 * Generate enter/leave events and push them back onto the event
912 * queue. This has to be done backwards, since the last event
913 * pushed will be the first one processed.
914 */
915
916 #define PUSH_EVENT(w, t, d) \
917 if (w->window != None) { \
918 eventPtr->type = t; \
919 eventPtr->xcrossing.detail = d; \
920 ChangeEventWindow(eventPtr, w); \
921 XPutBackEvent(w->display, eventPtr); \
922 }
923
924 if (downLevels == 0) {
925
926 /*
927 * SourcePtr is an inferior of destPtr.
928 */
929
930 if (destPtr != NULL) {
931 PUSH_EVENT(destPtr, EnterNotify, NotifyInferior);
932 }
933 for (i = upLevels-1; i > 0; i--) {
934 for (winPtr = sourcePtr, j = 0; j < i;
935 winPtr = winPtr->parentPtr, j++) {
936 if (winPtr == dispPtr->grabWinPtr) {
937 goto nextIteration;
938 }
939 }
940 PUSH_EVENT(winPtr, LeaveNotify, NotifyVirtual);
941 nextIteration: continue;
942 }
943 PUSH_EVENT(sourcePtr, LeaveNotify, NotifyAncestor);
944 } else if (upLevels == 0) {
945
946 /*
947 * DestPtr is an inferior of sourcePtr.
948 */
949
950 if (destPtr != NULL) {
951 PUSH_EVENT(destPtr, EnterNotify, NotifyAncestor);
952 }
953 for (winPtr = destPtr->parentPtr, i = downLevels-1; i > 0;
954 winPtr = winPtr->parentPtr, i--) {
955 PUSH_EVENT(winPtr, EnterNotify, NotifyVirtual);
956 }
957 if (sourcePtr != NULL) {
958 PUSH_EVENT(sourcePtr, LeaveNotify, NotifyInferior);
959 }
960 } else {
961
962 /*
963 * Non-linear: neither window is an inferior of the other.
964 */
965
966 if (destPtr != NULL) {
967 PUSH_EVENT(destPtr, EnterNotify, NotifyNonlinear);
968 }
969 if (destPtr != dispPtr->grabWinPtr) {
970 for (winPtr = destPtr->parentPtr, i = downLevels-1; i > 0;
971 winPtr = winPtr->parentPtr, i--) {
972 PUSH_EVENT(winPtr, EnterNotify, NotifyNonlinearVirtual);
973 if (winPtr == dispPtr->grabWinPtr) {
974 break;
975 }
976 }
977 }
978 for (i = upLevels-1; i > 0; i--) {
979 for (winPtr = sourcePtr, j = 0; j < i;
980 winPtr = winPtr->parentPtr, j++) {
981 if (winPtr == dispPtr->grabWinPtr) {
982 goto nextWindow;
983 }
984 }
985 PUSH_EVENT(winPtr, LeaveNotify, NotifyNonlinearVirtual);
986 nextWindow: continue;
987 }
988 PUSH_EVENT(sourcePtr, LeaveNotify, NotifyNonlinear);
989 }
990 }
991 \f
992 /*
993 *----------------------------------------------------------------------
994 *
995 * MovePointer2 --
996 *
997 * This procedure synthesizes EnterNotify and LeaveNotify events
998 * to correctly transfer the pointer from one window to another.
999 * It is different from MovePointer in that no template X event
1000 * needs to be supplied; this procedure generates the template
1001 * event and calls MovePointer.
1002 *
1003 * Results:
1004 * None.
1005 *
1006 * Side effects:
1007 * Synthesized events may be pushed back onto the event queue.
1008 *
1009 *----------------------------------------------------------------------
1010 */
1011
1012 static void
1013 MovePointer2(sourcePtr, destPtr, mode)
1014 TkWindow *sourcePtr; /* Window currently containing pointer (NULL
1015 * means it's not one managed by this
1016 * process). */
1017 TkWindow *destPtr; /* Window that is to end up containing the
1018 * pointer (NULL means it's not one managed
1019 * by this process). */
1020 int mode; /* Mode for enter/leave events, such as
1021 * NotifyNormal or NotifyUngrab. */
1022 {
1023 XEvent event;
1024 Window dummy1, dummy2;
1025 int dummy3, dummy4;
1026 TkWindow *winPtr;
1027
1028 winPtr = sourcePtr;
1029 if ((winPtr == NULL) || (winPtr->window == None)) {
1030 winPtr = destPtr;
1031 if ((winPtr == NULL) || (winPtr->window == None)) {
1032 return;
1033 }
1034 }
1035
1036 event.xcrossing.serial = LastKnownRequestProcessed(winPtr->display);
1037 event.xcrossing.send_event = False;
1038 event.xcrossing.display = winPtr->display;
1039 event.xcrossing.root = RootWindow(winPtr->display, winPtr->screenNum);
1040 event.xcrossing.time = TkCurrentTime(winPtr->dispPtr);
1041 XQueryPointer(winPtr->display, winPtr->window, &dummy1, &dummy2,
1042 &event.xcrossing.x_root, &event.xcrossing.y_root,
1043 &dummy3, &dummy4, &event.xcrossing.state);
1044 event.xcrossing.mode = mode;
1045 event.xcrossing.focus = False;
1046 MovePointer(&event, sourcePtr, destPtr);
1047 }
1048 \f
1049 /*
1050 *----------------------------------------------------------------------
1051 *
1052 * TkGrabDeadWindow --
1053 *
1054 * This procedure is invoked whenever a window is deleted, so that
1055 * grab-related cleanup can be performed.
1056 *
1057 * Results:
1058 * None.
1059 *
1060 * Side effects:
1061 * Various cleanups happen, such as generating events to move the
1062 * pointer back to its "natural" window as if an ungrab had been
1063 * done. See the code.
1064 *
1065 *----------------------------------------------------------------------
1066 */
1067
1068 void
1069 TkGrabDeadWindow(winPtr)
1070 register TkWindow *winPtr; /* Window that is in the process
1071 * of being deleted. */
1072 {
1073 TkDisplay *dispPtr = winPtr->dispPtr;
1074
1075 if (dispPtr->grabWinPtr == winPtr) {
1076 dispPtr->grabWinPtr = NULL;
1077 if (!(dispPtr->grabFlags & GRAB_GLOBAL)) {
1078 /*
1079 * Must generate enter/leave events to move back to the window
1080 * that contains the mouse pointer. We needn't filter events
1081 * here like we do in Tk_Ungrab because there are no children
1082 * of the grab window left in existence.
1083 */
1084
1085 movePointerBack:
1086 if ((dispPtr->ungrabWinPtr != NULL)
1087 && (dispPtr->ungrabWinPtr->mainPtr != winPtr->mainPtr)) {
1088 dispPtr->ungrabWinPtr = NULL;
1089 }
1090 MovePointer2(winPtr, dispPtr->ungrabWinPtr, NotifyUngrab);
1091 }
1092 } else if (dispPtr->buttonWinPtr == winPtr) {
1093 /*
1094 * The window in which a button was pressed was deleted. Simulate
1095 * dropping the button auto-grab by generating Enter and Leave
1096 * events to move the pointer back to the window it's really on
1097 * top of.
1098 */
1099
1100 dispPtr->buttonWinPtr = NULL;
1101 goto movePointerBack;
1102 }
1103 if (dispPtr->ungrabWinPtr == winPtr) {
1104 dispPtr->ungrabWinPtr = NULL;
1105 }
1106 if (dispPtr->pointerWinPtr == winPtr) {
1107 dispPtr->pointerWinPtr = NULL;
1108 }
1109 if (dispPtr->serverWinPtr == winPtr) {
1110 dispPtr->serverWinPtr = NULL;
1111 }
1112 }
Impressum, Datenschutz