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