1 /*
2  * tkMacOSXMouseEvent.c --
3  *
4  *	This file implements functions that decode & handle mouse events on
5  *	MacOS X.
6  *
7  * Copyright 2001-2009, Apple Inc.
8  * Copyright (c) 2005-2009 Daniel A. Steffen <das@users.sourceforge.net>
9  *
10  * See the file "license.terms" for information on usage and redistribution of
11  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
12  */
13 
14 #include "tkMacOSXPrivate.h"
15 #include "tkMacOSXWm.h"
16 #include "tkMacOSXEvent.h"
17 #include "tkMacOSXDebug.h"
18 #include "tkMacOSXConstants.h"
19 
20 typedef struct {
21     unsigned int state;
22     long delta;
23     Window window;
24     Point global;
25     Point local;
26 } MouseEventData;
27 
28 static Tk_Window captureWinPtr = NULL;	/* Current capture window; may be
29 					 * NULL. */
30 
31 static int		GenerateButtonEvent(MouseEventData *medPtr);
32 static unsigned int	ButtonModifiers2State(UInt32 buttonState,
33 			    UInt32 keyModifiers);
34 
35 #pragma mark TKApplication(TKMouseEvent)
36 
37 enum {
38     NSWindowWillMoveEventType = 20
39 };
40 
41 /*
42  * In OS X 10.6 an NSEvent of type NSMouseMoved would always have a non-Nil
43  * window attribute pointing to the active window.  As of 10.8 this behavior
44  * had changed.  The new behavior was that if the mouse were ever moved outside
45  * of a window, all subsequent NSMouseMoved NSEvents would have a Nil window
46  * attribute until the mouse returned to the window.  In 11.1 it changed again.
47  * The window attribute can be non-nil, but referencing a window which does not
48  * belong to the application.
49  */
50 
51 @implementation TKApplication(TKMouseEvent)
52 - (NSEvent *) tkProcessMouseEvent: (NSEvent *) theEvent
53 {
54     NSWindow *eventWindow = [theEvent window];
55     NSEventType eventType = [theEvent type];
56     NSRect viewFrame = [[eventWindow contentView] frame];
57     NSPoint location = [theEvent locationInWindow];
58     TkWindow *winPtr = NULL, *grabWinPtr;
59     Tk_Window tkwin = None, capture, target;
60     NSPoint local, global;
61     NSInteger button;
62     Bool inTitleBar = NO;
63     int win_x, win_y;
64     unsigned int buttonState = 0;
65     static int validPresses = 0, ignoredPresses = 0;
66 
67 #ifdef TK_MAC_DEBUG_EVENTS
68     TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, theEvent);
69 #endif
70 
71     /*
72      * If this event is not for a Tk toplevel, it should just be passed up the
73      * responder chain.  However, there is an exception for synthesized events,
74      * which are used in testing.  Those events are recognized by having their
75      * both the windowNumber and the eventNumber set to -1.
76      */
77 
78     if (eventWindow && ![eventWindow isMemberOfClass:[TKWindow class]]) {
79 	if ([theEvent windowNumber] != -1 || [theEvent eventNumber] != -1)
80 	    return theEvent;
81     }
82 
83     /*
84      * Check if the event is located in the titlebar.
85      */
86 
87     if (eventWindow) {
88 	inTitleBar = viewFrame.size.height < location.y;
89     }
90 
91     button = [theEvent buttonNumber] + Button1;
92     switch (eventType) {
93     case NSRightMouseUp:
94     case NSOtherMouseUp:
95 	buttonState &= ~TkGetButtonMask(button);
96 	break;
97     case NSLeftMouseDragged:
98     case NSRightMouseDragged:
99     case NSOtherMouseDragged:
100     case NSRightMouseDown:
101     case NSOtherMouseDown:
102 	buttonState |= TkGetButtonMask(button);
103 	break;
104     case NSMouseEntered:
105 	if ([eventWindow respondsToSelector:@selector(mouseInResizeArea)] &&
106 	    !inTitleBar) {
107 	    [(TKWindow *)eventWindow setMouseInResizeArea:YES];
108 	}
109 	break;
110     case NSMouseExited:
111 	if ([eventWindow respondsToSelector:@selector(mouseInResizeArea)]) {
112 	    [(TKWindow *)eventWindow setMouseInResizeArea:NO];
113 	    break;
114 	}
115     case NSLeftMouseUp:
116     case NSLeftMouseDown:
117 
118 	/*
119 	 * Ignore mouse button events which arrive while the app is inactive.
120 	 * These events will be resent after activation, causing duplicate
121 	 * actions when an app is activated by a bound mouse event. See ticket
122 	 * [7bda9882cb].
123 	 */
124 
125 	if (! [NSApp isActive]) {
126 	    return theEvent;
127 	}
128     case NSMouseMoved:
129     case NSScrollWheel:
130 #if 0
131     case NSCursorUpdate:
132     case NSTabletPoint:
133     case NSTabletProximity:
134 #endif
135 	break;
136     default: /* This type of event is ignored. */
137 	return theEvent;
138     }
139 
140     /*
141      * Update the button state.  We ignore left button presses that start a
142      * resize or occur in the title bar.  See tickets [d72abe6b54] and
143      * [39cbacb9e8].
144      */
145 
146     if (eventType == NSLeftMouseDown) {
147 	if ([eventWindow respondsToSelector:@selector(mouseInResizeArea)] &&
148 	    [(TKWindow *) eventWindow mouseInResizeArea]) {
149 
150 	    /*
151 	     * When the left button is pressed in the resize area, we receive
152 	     * NSMouseDown, but when it is released we do not receive
153 	     * NSMouseUp.  So ignore the event and clear the button state but
154 	     * do not change the ignoredPresses count.
155 	     */
156 
157 	    buttonState &= ~TkGetButtonMask(Button1);
158 	    return theEvent;
159 	}
160 	if (inTitleBar) {
161 	    ignoredPresses++;
162 	    return theEvent;
163 	}
164 	validPresses++;
165 	buttonState |= TkGetButtonMask(Button1);
166     }
167     if (eventType == NSLeftMouseUp) {
168 	if (ignoredPresses > 0) {
169 	    ignoredPresses--;
170 	} else if (validPresses > 0) {
171 	    validPresses--;
172 	}
173 	if (validPresses == 0) {
174 	    buttonState &= ~TkGetButtonMask(Button1);
175 	}
176     }
177 
178     /*
179      * Find an appropriate NSWindow to attach to this event, and its
180      * associated Tk window.
181      */
182 
183     capture = TkMacOSXGetCapture();
184     if (eventWindow) {
185 	    winPtr = TkMacOSXGetTkWindow(eventWindow);
186     } else if (capture) {
187 	winPtr = (TkWindow *) capture;
188 	eventWindow = TkMacOSXGetNSWindowForDrawable(winPtr->window);
189 	if (!eventWindow) {
190 	    return theEvent;
191 	}
192     }
193     if (!winPtr) {
194 	eventWindow = [NSApp mainWindow];
195 	winPtr = TkMacOSXGetTkWindow(eventWindow);
196     }
197     if (!winPtr) {
198 
199 	/*
200 	 * We couldn't find a Tk window for this event.  We have to ignore it.
201 	 */
202 
203 #ifdef TK_MAC_DEBUG_EVENTS
204 	TkMacOSXDbgMsg("Event received with no Tk window.");
205 #endif
206 	return theEvent;
207     }
208     tkwin = (Tk_Window) winPtr;
209 
210     /*
211      * Compute the mouse position in local (window) and global (screen)
212      * coordinates.  These are Tk coordinates, meaning that the local origin is
213      * at the top left corner of the containing toplevel and the global origin
214      * is at top left corner of the primary screen.
215      */
216 
217     global = [NSEvent mouseLocation];
218     local = [eventWindow tkConvertPointFromScreen: global];
219     global.x = floor(global.x);
220     global.y = floor(TkMacOSXZeroScreenHeight() - global.y);
221     local.x = floor(local.x);
222     local.y = floor([eventWindow frame].size.height - local.y);
223     if (Tk_IsEmbedded(winPtr)) {
224 	TkWindow *contPtr = TkpGetOtherWindow(winPtr);
225 	if (Tk_IsTopLevel(contPtr)) {
226 	    local.x -= contPtr->wmInfoPtr->xInParent;
227 	    local.y -= contPtr->wmInfoPtr->yInParent;
228 	} else {
229 	    TkWindow *topPtr = TkMacOSXGetHostToplevel(winPtr)->winPtr;
230 	    local.x -= (topPtr->wmInfoPtr->xInParent + contPtr->changes.x);
231 	    local.y -= (topPtr->wmInfoPtr->yInParent + contPtr->changes.y);
232 	}
233     } else {
234 	local.x -= winPtr->wmInfoPtr->xInParent;
235 	local.y -= winPtr->wmInfoPtr->yInParent;
236     }
237 
238     /*
239      * Use the local coordinates to find the Tk window which should receive
240      * this event.  Also convert local into the coordinates of that window.
241      * (The converted local coordinates are only needed for scrollwheel
242      * events.)
243      */
244 
245     target = Tk_TopCoordsToWindow(tkwin, local.x, local.y, &win_x, &win_y);
246 
247     /*
248      * Ignore the event if a local grab is in effect and the Tk window is
249      * not in the grabber's subtree.
250      */
251 
252     grabWinPtr = winPtr->dispPtr->grabWinPtr;
253     if (grabWinPtr && /* There is a grab in effect ... */
254 	!winPtr->dispPtr->grabFlags && /* and it is a local grab ... */
255 	grabWinPtr->mainPtr == winPtr->mainPtr){ /* in the same application. */
256 	Tk_Window tkwin2;
257 	if (!target) {
258 	    return theEvent;
259 	}
260 	for (tkwin2 = target;
261 	     !Tk_IsTopLevel(tkwin2);
262 	     tkwin2 = Tk_Parent(tkwin2)) {
263 	    if (tkwin2 == (Tk_Window)grabWinPtr) {
264 		break;
265 	    }
266 	}
267 	if (tkwin2 != (Tk_Window)grabWinPtr) {
268 	    return theEvent;
269 	}
270     }
271 
272     /*
273      *  Generate an XEvent for this mouse event.
274      */
275 
276     unsigned int state = buttonState;
277     NSUInteger modifiers = [theEvent modifierFlags];
278 
279     if (modifiers & NSAlphaShiftKeyMask) {
280 	state |= LockMask;
281     }
282     if (modifiers & NSShiftKeyMask) {
283 	state |= ShiftMask;
284     }
285     if (modifiers & NSControlKeyMask) {
286 	state |= ControlMask;
287     }
288     if (modifiers & NSCommandKeyMask) {
289 	state |= Mod1Mask;		/* command key */
290     }
291     if (modifiers & NSAlternateKeyMask) {
292 	state |= Mod2Mask;		/* option key */
293     }
294     if (modifiers & NSNumericPadKeyMask) {
295 	state |= Mod3Mask;
296     }
297     if (modifiers & NSFunctionKeyMask) {
298 	state |= Mod4Mask;
299     }
300 
301     if (eventType != NSScrollWheel) {
302 
303 	/*
304 	 * For normal mouse events, Tk_UpdatePointer will send the appropriate
305 	 * XEvents using its cached state information.  Unfortunately, it will
306 	 * also recompute the local coordinates.
307 	 */
308 
309 #ifdef TK_MAC_DEBUG_EVENTS
310 	TKLog(@"UpdatePointer %p x %.1f y %.1f %d",
311 		target, global.x, global.y, state);
312 #endif
313 
314 	Tk_UpdatePointer(target, global.x, global.y, state);
315     } else {
316 	CGFloat delta;
317 	int coarseDelta;
318 	XEvent xEvent;
319 
320 	/*
321 	 * For scroll wheel events we need to send the XEvent here.
322 	 */
323 
324 	xEvent.type = MouseWheelEvent;
325 	xEvent.xbutton.x = win_x;
326 	xEvent.xbutton.y = win_y;
327 	xEvent.xbutton.x_root = global.x;
328 	xEvent.xbutton.y_root = global.y;
329 	xEvent.xany.send_event = false;
330 	xEvent.xany.display = Tk_Display(target);
331 	xEvent.xany.window = Tk_WindowId(target);
332 
333 	delta = [theEvent deltaY];
334 	if (delta != 0.0) {
335 	    coarseDelta = (delta > -1.0 && delta < 1.0) ?
336 		    (signbit(delta) ? -1 : 1) : lround(delta);
337 	    xEvent.xbutton.state = state;
338 	    xEvent.xkey.keycode = coarseDelta;
339 	    xEvent.xany.serial = LastKnownRequestProcessed(Tk_Display(tkwin));
340 	    Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL);
341 	}
342 	delta = [theEvent deltaX];
343 	if (delta != 0.0) {
344 	    coarseDelta = (delta > -1.0 && delta < 1.0) ?
345 		    (signbit(delta) ? -1 : 1) : lround(delta);
346 	    xEvent.xbutton.state = state | ShiftMask;
347 	    xEvent.xkey.keycode = coarseDelta;
348 	    xEvent.xany.serial = LastKnownRequestProcessed(Tk_Display(tkwin));
349 	    Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL);
350 	}
351     }
352     return theEvent;
353 }
354 @end
355 
356 #pragma mark -
357 
358 /*
359  *----------------------------------------------------------------------
360  *
361  * TkMacOSXButtonKeyState --
362  *
363  *	Returns the current state of the button & modifier keys.
364  *
365  * Results:
366  *	A bitwise inclusive OR of a subset of the following: Button1Mask,
367  *	ShiftMask, LockMask, ControlMask, Mod*Mask.
368  *
369  * Side effects:
370  *	None.
371  *
372  *----------------------------------------------------------------------
373  */
374 
375 unsigned int
TkMacOSXButtonKeyState(void)376 TkMacOSXButtonKeyState(void)
377 {
378     UInt32 buttonState = 0, keyModifiers;
379     int isFrontProcess = (GetCurrentEvent() && Tk_MacOSXIsAppInFront());
380 
381     buttonState = isFrontProcess ? GetCurrentEventButtonState() :
382 	    GetCurrentButtonState();
383     keyModifiers = isFrontProcess ? GetCurrentEventKeyModifiers() :
384 	    GetCurrentKeyModifiers();
385 
386     return ButtonModifiers2State(buttonState, keyModifiers);
387 }
388 
389 /*
390  *----------------------------------------------------------------------
391  *
392  * ButtonModifiers2State --
393  *
394  *	Converts Carbon mouse button state and modifier values into a Tk
395  *	button/modifier state.
396  *
397  * Results:
398  *	None.
399  *
400  * Side effects:
401  *	None.
402  *
403  *----------------------------------------------------------------------
404  */
405 
406 static unsigned int
ButtonModifiers2State(UInt32 buttonState,UInt32 keyModifiers)407 ButtonModifiers2State(
408     UInt32 buttonState,
409     UInt32 keyModifiers)
410 {
411     unsigned int state;
412 
413     /*
414      * Tk on OSX supports at most 5 buttons.
415      */
416 
417     state = (buttonState & 0x1F) * Button1Mask;
418 
419     if (keyModifiers & alphaLock) {
420 	state |= LockMask;
421     }
422     if (keyModifiers & shiftKey) {
423 	state |= ShiftMask;
424     }
425     if (keyModifiers & controlKey) {
426 	state |= ControlMask;
427     }
428     if (keyModifiers & cmdKey) {
429 	state |= Mod1Mask;		/* command key */
430     }
431     if (keyModifiers & optionKey) {
432 	state |= Mod2Mask;		/* option key */
433     }
434     if (keyModifiers & kEventKeyModifierNumLockMask) {
435 	state |= Mod3Mask;
436     }
437     if (keyModifiers & kEventKeyModifierFnMask) {
438 	state |= Mod4Mask;
439     }
440 
441     return state;
442 }
443 
444 /*
445  *----------------------------------------------------------------------
446  *
447  * XQueryPointer --
448  *
449  *	Check the current state of the mouse. This is not a complete
450  *	implementation of this function. It only computes the root coordinates
451  *	and the current mask.
452  *
453  * Results:
454  *	Sets root_x_return, root_y_return, and mask_return. Returns true on
455  *	success.
456  *
457  * Side effects:
458  *	None.
459  *
460  *----------------------------------------------------------------------
461  */
462 
463 Bool
XQueryPointer(Display * display,Window w,Window * root_return,Window * child_return,int * root_x_return,int * root_y_return,int * win_x_return,int * win_y_return,unsigned int * mask_return)464 XQueryPointer(
465     Display *display,
466     Window w,
467     Window *root_return,
468     Window *child_return,
469     int *root_x_return,
470     int *root_y_return,
471     int *win_x_return,
472     int *win_y_return,
473     unsigned int *mask_return)
474 {
475     int getGlobal = (root_x_return && root_y_return);
476     int getLocal = (win_x_return && win_y_return && w != None);
477 
478     if (getGlobal || getLocal) {
479 	NSPoint global = [NSEvent mouseLocation];
480 
481 	if (getLocal) {
482 	    MacDrawable *macWin = (MacDrawable *)w;
483 	    NSWindow *win = TkMacOSXGetNSWindowForDrawable(w);
484 
485 	    if (win) {
486 		NSPoint local;
487 
488 		local = [win tkConvertPointFromScreen:global];
489 		local.y = [win frame].size.height - local.y;
490 		if (macWin->winPtr && macWin->winPtr->wmInfoPtr) {
491 		    local.x -= macWin->winPtr->wmInfoPtr->xInParent;
492 		    local.y -= macWin->winPtr->wmInfoPtr->yInParent;
493 		}
494 		*win_x_return = local.x;
495 		*win_y_return = local.y;
496 	    }
497 	}
498 	if (getGlobal) {
499 	    *root_x_return = global.x;
500 	    *root_y_return = TkMacOSXZeroScreenHeight() - global.y;
501 	}
502     }
503     if (mask_return) {
504 	*mask_return = TkMacOSXButtonKeyState();
505     }
506     return True;
507 }
508 
509 /*
510  *----------------------------------------------------------------------
511  *
512  * TkGenerateButtonEventForXPointer --
513  *
514  *	This procedure generates an X button event for the current pointer
515  *	state as reported by XQueryPointer().
516  *
517  * Results:
518  *	True if event(s) are generated - false otherwise.
519  *
520  * Side effects:
521  *	Additional events may be placed on the Tk event queue. Grab state may
522  *	also change.
523  *
524  *----------------------------------------------------------------------
525  */
526 
527 MODULE_SCOPE int
TkGenerateButtonEventForXPointer(Window window)528 TkGenerateButtonEventForXPointer(
529     Window window)		/* X Window containing button event. */
530 {
531     MouseEventData med;
532     int global_x, global_y, local_x, local_y;
533 
534     bzero(&med, sizeof(MouseEventData));
535     XQueryPointer(NULL, window, NULL, NULL, &global_x, &global_y,
536 	    &local_x, &local_y, &med.state);
537     med.global.h = global_x;
538     med.global.v = global_y;
539     med.local.h = local_x;
540     med.local.v = local_y;
541     med.window = window;
542 
543     return GenerateButtonEvent(&med);
544 }
545 
546 /*
547  *----------------------------------------------------------------------
548  *
549  * TkGenerateButtonEvent --
550  *
551  *	Given a global x & y position and the button key status this procedure
552  *	generates the appropriate X button event. It also handles the state
553  *	changes needed to implement implicit grabs.
554  *
555  * Results:
556  *	True if event(s) are generated, false otherwise.
557  *
558  * Side effects:
559  *	Additional events may be placed on the Tk event queue. Grab state may
560  *	also change.
561  *
562  *----------------------------------------------------------------------
563  */
564 
565 int
TkGenerateButtonEvent(int x,int y,Window window,unsigned int state)566 TkGenerateButtonEvent(
567     int x,			/* X location of mouse, */
568     int y,			/* Y location of mouse. */
569     Window window,		/* X Window containing button event. */
570     unsigned int state)		/* Button Key state suitable for X event. */
571 {
572     MacDrawable *macWin = (MacDrawable *)window;
573     NSWindow *win = TkMacOSXGetNSWindowForDrawable(window);
574     MouseEventData med;
575 
576     bzero(&med, sizeof(MouseEventData));
577     med.state = state;
578     med.window = window;
579     med.global.h = x;
580     med.global.v = y;
581     med.local = med.global;
582 
583     if (win) {
584 	NSPoint local = NSMakePoint(x, TkMacOSXZeroScreenHeight() - y);
585 
586 	local = [win tkConvertPointFromScreen:local];
587 	local.y = [win frame].size.height - local.y;
588 	if (macWin->winPtr && macWin->winPtr->wmInfoPtr) {
589 	    local.x -= macWin->winPtr->wmInfoPtr->xInParent;
590 	    local.y -= macWin->winPtr->wmInfoPtr->yInParent;
591 	}
592 	med.local.h = local.x;
593 	med.local.v = TkMacOSXZeroScreenHeight() - local.y;
594     }
595 
596     return GenerateButtonEvent(&med);
597 }
598 
599 /*
600  *----------------------------------------------------------------------
601  *
602  * GenerateButtonEvent --
603  *
604  *	Generate an X button event from a MouseEventData structure. Handles
605  *	the state changes needed to implement implicit grabs.
606  *
607  * Results:
608  *	True if event(s) are generated - false otherwise.
609  *
610  * Side effects:
611  *	Additional events may be placed on the Tk event queue. Grab state may
612  *	also change.
613  *
614  *----------------------------------------------------------------------
615  */
616 
617 static int
GenerateButtonEvent(MouseEventData * medPtr)618 GenerateButtonEvent(
619     MouseEventData *medPtr)
620 {
621     Tk_Window tkwin;
622     int dummy;
623     TkDisplay *dispPtr;
624 
625 #if UNUSED
626 
627     /*
628      * ButtonDown events will always occur in the front window. ButtonUp
629      * events, however, may occur anywhere on the screen. ButtonUp events
630      * should only be sent to Tk if in the front window or during an implicit
631      * grab.
632      */
633 
634     if ((medPtr->activeNonFloating == NULL)
635 	    || ((!(TkpIsWindowFloating(medPtr->whichWin))
636 	    && (medPtr->activeNonFloating != medPtr->whichWin))
637 	    && TkMacOSXGetCapture() == NULL)) {
638 	return false;
639     }
640 #endif
641 
642     dispPtr = TkGetDisplayList();
643     tkwin = Tk_IdToWindow(dispPtr->display, medPtr->window);
644 
645     if (tkwin != NULL) {
646 	tkwin = Tk_TopCoordsToWindow(tkwin, medPtr->local.h, medPtr->local.v,
647 		&dummy, &dummy);
648     }
649 
650     Tk_UpdatePointer(tkwin, medPtr->global.h, medPtr->global.v, medPtr->state);
651     return true;
652 }
653 
654 /*
655  *----------------------------------------------------------------------
656  *
657  * TkpWarpPointer --
658  *
659  *	Move the mouse cursor to the screen location specified by the warpX and
660  *	warpY fields of a TkDisplay.
661  *
662  * Results:
663  *	None
664  *
665  * Side effects:
666  *	The mouse cursor is moved.
667  *
668  *----------------------------------------------------------------------
669  */
670 
671 void
TkpWarpPointer(TkDisplay * dispPtr)672 TkpWarpPointer(
673     TkDisplay *dispPtr)
674 {
675     CGPoint pt;
676 
677     if (dispPtr->warpWindow) {
678 	int x, y;
679 	Tk_GetRootCoords(dispPtr->warpWindow, &x, &y);
680 	pt.x = x + dispPtr->warpX;
681 	pt.y = y + dispPtr->warpY;
682     } else {
683 	pt.x = dispPtr->warpX;
684 	pt.y = dispPtr->warpY;
685     }
686 
687     CGWarpMouseCursorPosition(pt);
688 
689     if (dispPtr->warpWindow) {
690         TkGenerateButtonEventForXPointer(Tk_WindowId(dispPtr->warpWindow));
691     } else {
692         TkGenerateButtonEventForXPointer(None);
693     }
694 }
695 
696 /*
697  *----------------------------------------------------------------------
698  *
699  * TkpSetCapture --
700  *
701  *	This function captures the mouse so that all future events will be
702  *	reported to this window, even if the mouse is outside the window. If
703  *	the specified window is NULL, then the mouse is released.
704  *
705  * Results:
706  *	None.
707  *
708  * Side effects:
709  *	Sets the capture flag and captures the mouse.
710  *
711  *----------------------------------------------------------------------
712  */
713 
714 void
TkpSetCapture(TkWindow * winPtr)715 TkpSetCapture(
716     TkWindow *winPtr)		/* Capture window, or NULL. */
717 {
718     while (winPtr && !Tk_IsTopLevel(winPtr)) {
719 	winPtr = winPtr->parentPtr;
720     }
721     captureWinPtr = (Tk_Window)winPtr;
722 }
723 
724 /*
725  *----------------------------------------------------------------------
726  *
727  * TkMacOSXGetCapture --
728  *
729  * Results:
730  *	Returns the current grab window
731  *
732  * Side effects:
733  *	None.
734  *
735  *----------------------------------------------------------------------
736  */
737 
738 Tk_Window
TkMacOSXGetCapture(void)739 TkMacOSXGetCapture(void)
740 {
741     return captureWinPtr;
742 }
743 
744 /*
745  * Local Variables:
746  * mode: objc
747  * c-basic-offset: 4
748  * fill-column: 79
749  * coding: utf-8
750  * End:
751  */
752