1/*
2 * tkMacOSXMouseEvent.c --
3 *
4 *	This file implements functions that decode & handle mouse events
5 *	on MacOS X.
6 *
7 * Copyright 2001, Apple Computer, Inc.
8 * Copyright (c) 2005-2007 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 *	The following terms apply to all files originating from Apple
14 *	Computer, Inc. ("Apple") and associated with the software
15 *	unless explicitly disclaimed in individual files.
16 *
17 *
18 *	Apple hereby grants permission to use, copy, modify,
19 *	distribute, and license this software and its documentation
20 *	for any purpose, provided that existing copyright notices are
21 *	retained in all copies and that this notice is included
22 *	verbatim in any distributions. No written agreement, license,
23 *	or royalty fee is required for any of the authorized
24 *	uses. Modifications to this software may be copyrighted by
25 *	their authors and need not follow the licensing terms
26 *	described here, provided that the new terms are clearly
27 *	indicated on the first page of each file where they apply.
28 *
29 *
30 *	IN NO EVENT SHALL APPLE, THE AUTHORS OR DISTRIBUTORS OF THE
31 *	SOFTWARE BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL,
32 *	INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF
33 *	THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
34 *	EVEN IF APPLE OR THE AUTHORS HAVE BEEN ADVISED OF THE
35 *	POSSIBILITY OF SUCH DAMAGE.  APPLE, THE AUTHORS AND
36 *	DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
37 *	BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
38 *	FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.	 THIS
39 *	SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND APPLE,THE
40 *	AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
41 *	MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
42 *
43 *	GOVERNMENT USE: If you are acquiring this software on behalf
44 *	of the U.S. government, the Government shall have only
45 *	"Restricted Rights" in the software and related documentation
46 *	as defined in the Federal Acquisition Regulations (FARs) in
47 *	Clause 52.227.19 (c) (2).  If you are acquiring the software
48 *	on behalf of the Department of Defense, the software shall be
49 *	classified as "Commercial Computer Software" and the
50 *	Government shall have only "Restricted Rights" as defined in
51 *	Clause 252.227-7013 (c) (1) of DFARs.  Notwithstanding the
52 *	foregoing, the authors grant the U.S. Government and others
53 *	acting in its behalf permission to use and distribute the
54 *	software in accordance with the terms specified in this
55 *	license.
56 *
57 * RCS: @(#) $Id: tkMacOSXMouseEvent.c,v 1.6.2.23 2007/12/18 18:21:30 das Exp $
58 */
59
60#include "tkMacOSXPrivate.h"
61#include "tkMacOSXWm.h"
62#include "tkMacOSXEvent.h"
63#include "tkMacOSXDebug.h"
64
65typedef struct {
66    WindowRef	   whichWin;
67    WindowRef	   activeNonFloating;
68    WindowPartCode windowPart;
69    unsigned int   state;
70    long	   delta;
71    Window	   window;
72    Point	   global;
73    Point	   local;
74} MouseEventData;
75
76/*
77 * Declarations of static variables used in this file.
78 */
79
80static int gEatButtonUp = 0;	   /* 1 if we need to eat the next up event */
81
82/*
83 * Declarations of functions used only in this file.
84 */
85
86static void BringWindowForward(WindowRef wRef, int isFrontProcess,
87	int frontWindowOnly);
88static int GeneratePollingEvents(MouseEventData * medPtr);
89static int GenerateMouseWheelEvent(MouseEventData * medPtr);
90static int GenerateButtonEvent(MouseEventData * medPtr);
91static int GenerateToolbarButtonEvent(MouseEventData * medPtr);
92static int HandleWindowTitlebarMouseDown(MouseEventData * medPtr, Tk_Window tkwin);
93static unsigned int ButtonModifiers2State(UInt32 buttonState, UInt32 keyModifiers);
94static Tk_Window GetGrabWindowForWindow(Tk_Window tkwin);
95
96static int TkMacOSXGetEatButtonUp(void);
97static void TkMacOSXSetEatButtonUp(int f);
98
99
100/*
101 *----------------------------------------------------------------------
102 *
103 * TkMacOSXProcessMouseEvent --
104 *
105 *	This routine processes the event in eventPtr, and
106 *	generates the appropriate Tk events from it.
107 *
108 * Results:
109 *	True if event(s) are generated - false otherwise.
110 *
111 * Side effects:
112 *	Additional events may be place on the Tk event queue.
113 *
114 *----------------------------------------------------------------------
115 */
116
117MODULE_SCOPE int
118TkMacOSXProcessMouseEvent(TkMacOSXEvent *eventPtr, MacEventStatus * statusPtr)
119{
120    Tk_Window tkwin;
121    Point where, where2;
122    int result;
123    TkDisplay * dispPtr;
124    OSStatus err;
125    MouseEventData mouseEventData, * medPtr = &mouseEventData;
126    int isFrontProcess;
127
128    switch (eventPtr->eKind) {
129	case kEventMouseDown:
130	case kEventMouseUp:
131	case kEventMouseMoved:
132	case kEventMouseDragged:
133	case kEventMouseWheelMoved:
134	    break;
135	default:
136	    return false;
137	    break;
138    }
139    err = ChkErr(GetEventParameter, eventPtr->eventRef,
140	    kEventParamMouseLocation,
141	    typeQDPoint, NULL,
142	    sizeof(where), NULL,
143	    &where);
144    if (err != noErr) {
145	GetGlobalMouse(&where);
146    }
147    err = ChkErr(GetEventParameter, eventPtr->eventRef,
148	    kEventParamWindowRef,
149	    typeWindowRef, NULL,
150	    sizeof(WindowRef), NULL,
151	    &medPtr->whichWin);
152    if (err == noErr) {
153	err = ChkErr(GetEventParameter, eventPtr->eventRef,
154		kEventParamWindowPartCode,
155		typeWindowPartCode, NULL,
156		sizeof(WindowPartCode), NULL,
157		&medPtr->windowPart);
158    }
159    if (err != noErr) {
160	medPtr->windowPart = FindWindow(where, &medPtr->whichWin);
161    }
162    medPtr->window = TkMacOSXGetXWindow(medPtr->whichWin);
163    if (medPtr->whichWin != NULL && medPtr->window == None) {
164	return false;
165    }
166    if (eventPtr->eKind == kEventMouseDown) {
167	if (IsWindowActive(medPtr->whichWin) && IsWindowPathSelectEvent(
168		medPtr->whichWin, eventPtr->eventRef)) {
169	    ChkErr(WindowPathSelect, medPtr->whichWin, NULL, NULL);
170	    return false;
171	}
172	if (medPtr->windowPart == inProxyIcon) {
173	    TkMacOSXTrackingLoop(1);
174	    err = ChkErr(TrackWindowProxyDrag, medPtr->whichWin, where);
175	    TkMacOSXTrackingLoop(0);
176	    if (err == errUserWantsToDragWindow) {
177		medPtr->windowPart = inDrag;
178	    } else {
179		return false;
180	    }
181	}
182    }
183    isFrontProcess = Tk_MacOSXIsAppInFront();
184    if (isFrontProcess) {
185	medPtr->state = ButtonModifiers2State(GetCurrentEventButtonState(),
186		GetCurrentEventKeyModifiers());
187    } else {
188	medPtr->state = ButtonModifiers2State(GetCurrentButtonState(),
189		GetCurrentKeyModifiers());
190    }
191    medPtr->global = where;
192    err = ChkErr(GetEventParameter, eventPtr->eventRef,
193	    kEventParamWindowMouseLocation,
194	    typeQDPoint, NULL,
195	    sizeof(Point), NULL,
196	    &medPtr->local);
197    if (err == noErr) {
198	if (medPtr->whichWin) {
199	    Rect widths;
200	    GetWindowStructureWidths(medPtr->whichWin, &widths);
201	    medPtr->local.h -=	widths.left;
202	    medPtr->local.v -=	widths.top;
203	}
204    } else {
205	medPtr->local = where;
206	if (medPtr->whichWin) {
207	    QDGlobalToLocalPoint(GetWindowPort(medPtr->whichWin),
208		    &medPtr->local);
209	}
210    }
211    medPtr->activeNonFloating = ActiveNonFloatingWindow();
212    dispPtr = TkGetDisplayList();
213    tkwin = Tk_IdToWindow(dispPtr->display, medPtr->window);
214
215    if (eventPtr->eKind != kEventMouseDown) {
216	int res = false;
217
218	switch (eventPtr->eKind) {
219	    case kEventMouseUp:
220		/*
221		 * The window manager only needs to know about mouse down
222		 * events and sometimes we need to "eat" the mouse up.
223		 * Otherwise, we just pass the event to Tk.
224		 */
225		if (TkMacOSXGetEatButtonUp()) {
226		    TkMacOSXSetEatButtonUp(false);
227		} else {
228		    res = GenerateButtonEvent(medPtr);
229		}
230		break;
231	    case kEventMouseWheelMoved:
232		err = ChkErr(GetEventParameter, eventPtr->eventRef,
233			kEventParamMouseWheelDelta, typeLongInteger, NULL,
234			sizeof(long), NULL, &medPtr->delta);
235		if (err != noErr ) {
236		    statusPtr->err = 1;
237		} else {
238		    EventMouseWheelAxis axis;
239		    err = ChkErr(GetEventParameter, eventPtr->eventRef,
240			    kEventParamMouseWheelAxis, typeMouseWheelAxis,
241			    NULL, sizeof(EventMouseWheelAxis), NULL, &axis);
242		    if (err == noErr && axis == kEventMouseWheelAxisX) {
243			 medPtr->state |= ShiftMask;
244		    }
245		    res = GenerateMouseWheelEvent(medPtr);
246		}
247		break;
248	    case kEventMouseMoved:
249	    case kEventMouseDragged:
250		res = GeneratePollingEvents(medPtr);
251		break;
252	    default:
253		Tcl_Panic("Unknown mouse event !");
254	}
255	if (res) {
256		statusPtr->stopProcessing = 1;
257	}
258	return res;
259    }
260    TkMacOSXSetEatButtonUp(false);
261    if (medPtr->whichWin) {
262	/*
263	 * We got a mouse down in a window
264	 * See if this is the activate click
265	 * This click moves the window forward. We don't want
266	 * the corresponding mouse-up to be reported to the application
267	 * or else it will mess up some Tk scripts.
268	 */
269
270	if (!(TkpIsWindowFloating(medPtr->whichWin))
271		&& (medPtr->whichWin != medPtr->activeNonFloating
272		|| !isFrontProcess)) {
273	    int frontWindowOnly = 1;
274	    int cmdDragGrow = ((medPtr->windowPart == inDrag ||
275		    medPtr->windowPart == inGrow) && medPtr->state & Mod1Mask);
276
277	    if (!cmdDragGrow) {
278		Tk_Window grabWin = GetGrabWindowForWindow(tkwin);
279
280		frontWindowOnly = !grabWin;
281		if (grabWin && grabWin != tkwin) {
282		    TkMacOSXSetEatButtonUp(true);
283		    BringWindowForward(TkMacOSXDrawableWindow(
284			    ((TkWindow*)grabWin)->window), isFrontProcess,
285			    frontWindowOnly);
286		    return false;
287		}
288	    }
289
290	    /*
291	     * Clicks in the titlebar widgets are handled without bringing the
292	     * window forward.
293	     */
294	    if ((result = HandleWindowTitlebarMouseDown(medPtr, tkwin)) != -1) {
295		statusPtr->stopProcessing = 1;
296		return result;
297	    } else {
298		/*
299		 * Only windows with the kWindowNoActivatesAttribute can
300		 * receive mouse events in the background.
301		 */
302		if (!(((TkWindow *)tkwin)->wmInfoPtr->attributes &
303			kWindowNoActivatesAttribute)) {
304		    /*
305		     * Allow background window dragging & growing with Command
306		     * down.
307		     */
308		    if (!cmdDragGrow) {
309			TkMacOSXSetEatButtonUp(true);
310			BringWindowForward(medPtr->whichWin, isFrontProcess,
311				frontWindowOnly);
312		    }
313		    /*
314		     * Allow dragging & growing of windows that were/are in the
315		     * background.
316		     */
317		    if (!(medPtr->windowPart == inDrag ||
318			    medPtr->windowPart == inGrow)) {
319			return false;
320		    }
321		}
322	    }
323	} else {
324	    if ((result = HandleWindowTitlebarMouseDown(medPtr, tkwin)) != -1) {
325		statusPtr->stopProcessing = 1;
326		return result;
327	    }
328	}
329	switch (medPtr->windowPart) {
330	    case inDrag: {
331		WindowAttributes attributes;
332
333		GetWindowAttributes(medPtr->whichWin, &attributes);
334		if (!(attributes & kWindowAsyncDragAttribute)) {
335		    TkMacOSXTrackingLoop(1);
336		    DragWindow(medPtr->whichWin, where, NULL);
337		    TkMacOSXTrackingLoop(0);
338		    where2.h = where2.v = 0;
339		    QDLocalToGlobalPoint(GetWindowPort(medPtr->whichWin),
340			    &where2);
341		    if (EqualPt(where, where2)) {
342			return false;
343		    }
344		    return true;
345		}
346		break;
347	    }
348	    case inGrow:
349		/*
350		 * Generally the content region is the domain of Tk
351		 * sub-windows. However, one exception is the grow
352		 * region. A button down in this area will be handled
353		 * by the window manager. Note: this means that Tk
354		 * may not get button down events in this area!
355		 */
356		if (TkMacOSXGrowToplevel(medPtr->whichWin, where) == true) {
357		    statusPtr->stopProcessing = 1;
358		    return true;
359		} else {
360		    return GenerateButtonEvent(medPtr);
361		}
362		break;
363	    case inContent:
364		return GenerateButtonEvent(medPtr);
365		break;
366	    default:
367		return false;
368		break;
369	}
370    }
371    return false;
372}
373
374/*
375 *----------------------------------------------------------------------
376 *
377 * HandleWindowTitlebarMouseDown --
378 *
379 *	Handle clicks in window titlebar.
380 *
381 * Results:
382 *	1 if event was handled, 0 if event was not handled,
383 *	-1 if MouseDown was not in window titlebar.
384 *
385 * Side effects:
386 *	Additional events may be place on the Tk event queue.
387 *
388 *----------------------------------------------------------------------
389 */
390
391int
392HandleWindowTitlebarMouseDown(MouseEventData * medPtr, Tk_Window tkwin)
393{
394    int result = INT_MAX;
395
396    switch (medPtr->windowPart) {
397	case inGoAway:
398	case inCollapseBox:
399	case inZoomIn:
400	case inZoomOut:
401	case inToolbarButton:
402	    if (!IsWindowActive(medPtr->whichWin)) {
403		WindowRef frontWindow = FrontNonFloatingWindow();
404		WindowModality frontWindowModality = kWindowModalityNone;
405
406		if (frontWindow && frontWindow != medPtr->whichWin) {
407		    ChkErr(GetWindowModality, frontWindow,
408			    &frontWindowModality, NULL);
409		}
410		if (frontWindowModality == kWindowModalityAppModal) {
411		    result  = 0;
412		}
413	    }
414	    break;
415	default:
416	    result = -1;
417	    break;
418    }
419
420    if (result == INT_MAX) {
421	result = 0;
422	TkMacOSXTrackingLoop(1);
423	switch (medPtr->windowPart) {
424	    case inGoAway:
425		if (TrackGoAway(medPtr->whichWin, medPtr->global) && tkwin) {
426		    TkGenWMDestroyEvent(tkwin);
427		    result = 1;
428		}
429		break;
430	    case inCollapseBox:
431		if (TrackBox(medPtr->whichWin, medPtr->global,
432			medPtr->windowPart) && tkwin) {
433		    TkpWmSetState((TkWindow *)tkwin, IconicState);
434		    result = 1;
435		}
436		break;
437	    case inZoomIn:
438	    case inZoomOut:
439		if (TrackBox(medPtr->whichWin, medPtr->global,
440			medPtr->windowPart)) {
441		    result = TkMacOSXZoomToplevel(medPtr->whichWin,
442			    medPtr->windowPart);
443		}
444		break;
445	    case inToolbarButton:
446		if (TrackBox(medPtr->whichWin, medPtr->global,
447			medPtr->windowPart)) {
448		    result = GenerateToolbarButtonEvent(medPtr);
449		}
450		break;
451	}
452	TkMacOSXTrackingLoop(0);
453    }
454
455    return result;
456}
457
458/*
459 *----------------------------------------------------------------------
460 *
461 * GeneratePollingEvents --
462 *
463 *	This function polls the mouse position and generates X Motion,
464 *	Enter & Leave events. The cursor is also updated at this
465 *	time.
466 *
467 * Results:
468 *	True if event(s) are generated - false otherwise.
469 *
470 * Side effects:
471 *	Additional events may be place on the Tk event queue.
472 *	The cursor may be changed.
473 *
474 *----------------------------------------------------------------------
475 */
476
477static int
478GeneratePollingEvents(MouseEventData * medPtr)
479{
480    Tk_Window tkwin, rootwin, grabWin;
481    int local_x, local_y;
482    TkDisplay *dispPtr;
483
484
485    grabWin = TkMacOSXGetCapture();
486
487    if ((!TkpIsWindowFloating(medPtr->whichWin)
488	    && (medPtr->activeNonFloating != medPtr->whichWin))) {
489	/*
490	 * If the window for this event is not floating, and is not the
491	 * active non-floating window, don't generate polling events.
492	 * We don't send events to backgrounded windows. So either send
493	 * it to the grabWin, or NULL if there is no grabWin.
494	 */
495
496	tkwin = grabWin;
497    } else {
498	/*
499	 * First check whether the toplevel containing this mouse
500	 * event is the grab window. If not, then send the event
501	 * to the grab window. Otherwise, set tkWin to the subwindow
502	 * which most closely contains the mouse event.
503	 */
504
505	dispPtr = TkGetDisplayList();
506	rootwin = Tk_IdToWindow(dispPtr->display, medPtr->window);
507	if ((rootwin == NULL)
508		|| ((grabWin != NULL) && (rootwin != grabWin))) {
509	    tkwin = grabWin;
510	} else {
511	    tkwin = Tk_TopCoordsToWindow(rootwin,
512		    medPtr->local.h, medPtr->local.v,
513		    &local_x, &local_y);
514	}
515    }
516
517    /*
518     * The following call will generate the appropiate X events and
519     * adjust any state that Tk must remember.
520     */
521
522    Tk_UpdatePointer(tkwin, medPtr->global.h, medPtr->global.v,
523	    medPtr->state);
524
525    return true;
526}
527
528/*
529 *----------------------------------------------------------------------
530 *
531 * BringWindowForward --
532 *
533 *	Bring this background window to the front.
534 *
535 * Results:
536 *	None.
537 *
538 * Side effects:
539 *	The window is brought forward.
540 *
541 *----------------------------------------------------------------------
542 */
543
544static void
545BringWindowForward(
546    WindowRef wRef,
547    int isFrontProcess,
548    int frontWindowOnly)
549{
550    if (wRef && !TkpIsWindowFloating(wRef) && IsValidWindowPtr(wRef)) {
551	WindowRef frontWindow = FrontNonFloatingWindow();
552	WindowModality frontWindowModality = kWindowModalityNone;
553
554	if (frontWindow && frontWindow != wRef) {
555	    ChkErr(GetWindowModality, frontWindow, &frontWindowModality, NULL);
556	}
557	if (frontWindowModality != kWindowModalityAppModal) {
558	    Window window = TkMacOSXGetXWindow(wRef);
559
560	    if (window != None) {
561		TkDisplay *dispPtr = TkGetDisplayList();
562		TkWindow * winPtr = (TkWindow *)Tk_IdToWindow(dispPtr->display,
563			window);
564
565		if (winPtr && winPtr->wmInfoPtr &&
566			winPtr->wmInfoPtr->master != None) {
567		    TkWindow *masterWinPtr = (TkWindow *)Tk_IdToWindow(
568			    dispPtr->display, winPtr->wmInfoPtr->master);
569
570		    if (masterWinPtr && masterWinPtr->window != None &&
571			    TkMacOSXHostToplevelExists(masterWinPtr)) {
572			WindowRef masterMacWin =
573				TkMacOSXDrawableWindow(masterWinPtr->window);
574
575			if (masterMacWin) {
576			    BringToFront(masterMacWin);
577			}
578		    }
579		}
580	    }
581	    SelectWindow(wRef);
582	} else {
583	    frontWindowOnly = 0;
584	}
585    }
586    if (!isFrontProcess) {
587	ProcessSerialNumber ourPsn = {0, kCurrentProcess};
588
589	ChkErr(SetFrontProcessWithOptions, &ourPsn, frontWindowOnly ?
590	    kSetFrontProcessFrontWindowOnly : 0);
591    }
592}
593
594/*
595 *----------------------------------------------------------------------
596 *
597 * TkMacOSXBringWindowForward --
598 *
599 *	Bring this window to the front in response to a mouse click. If
600 *	a grab is in effect, bring the grab window to the front instead.
601 *
602 * Results:
603 *	None.
604 *
605 * Side effects:
606 *	The window is brought forward.
607 *
608 *----------------------------------------------------------------------
609 */
610
611void
612TkMacOSXBringWindowForward(
613    WindowRef wRef)
614{
615    TkDisplay *dispPtr = TkGetDisplayList();
616    Tk_Window tkwin = Tk_IdToWindow(dispPtr->display,TkMacOSXGetXWindow(wRef));
617    Tk_Window grabWin = GetGrabWindowForWindow(tkwin);
618
619    if (grabWin && grabWin != tkwin) {
620	wRef = TkMacOSXDrawableWindow(((TkWindow*)grabWin)->window);
621    }
622    TkMacOSXSetEatButtonUp(true);
623    BringWindowForward(wRef, Tk_MacOSXIsAppInFront(), !grabWin);
624}
625
626/*
627 *----------------------------------------------------------------------
628 *
629 * GetGrabWindowForWindow --
630 *
631 *	Get the grab window for the given window, if any.
632 *
633 * Results:
634 *	Grab Tk_Window or None.
635 *
636 * Side effects:
637 *	None.
638 *
639 *----------------------------------------------------------------------
640 */
641
642Tk_Window
643GetGrabWindowForWindow(
644    Tk_Window tkwin)
645{
646    Tk_Window grabWin = TkMacOSXGetCapture();
647
648    if (!grabWin) {
649	int grabState = TkGrabState((TkWindow*)tkwin);
650
651	if (grabState != TK_GRAB_NONE && grabState != TK_GRAB_IN_TREE) {
652	    grabWin = (Tk_Window) (((TkWindow*)tkwin)->dispPtr->grabWinPtr);
653	}
654    }
655
656    return grabWin;
657}
658
659/*
660 *----------------------------------------------------------------------
661 *
662 * GenerateMouseWheelEvent --
663 *
664 *	Generates a "MouseWheel" Tk event.
665 *
666 * Results:
667 *	None.
668 *
669 * Side effects:
670 *	Places a mousewheel event on the event queue.
671 *
672 *----------------------------------------------------------------------
673 */
674
675static int
676GenerateMouseWheelEvent(MouseEventData * medPtr)
677{
678    Tk_Window tkwin, rootwin;
679    TkDisplay *dispPtr;
680    TkWindow  *winPtr;
681    XEvent xEvent;
682
683    dispPtr = TkGetDisplayList();
684    rootwin = Tk_IdToWindow(dispPtr->display, medPtr->window);
685    if (rootwin == NULL) {
686	tkwin = NULL;
687    } else {
688	tkwin = Tk_TopCoordsToWindow(rootwin,
689		medPtr->local.h, medPtr->local.v,
690		&xEvent.xbutton.x, &xEvent.xbutton.y);
691    }
692
693    /*
694     * The following call will generate the appropiate X events and
695     * adjust any state that Tk must remember.
696     */
697
698    if (!tkwin) {
699	tkwin = TkMacOSXGetCapture();
700    }
701    if (!tkwin) {
702	return false;
703    }
704    winPtr = (TkWindow *) tkwin;
705    xEvent.type = MouseWheelEvent;
706    xEvent.xkey.keycode = medPtr->delta;
707    xEvent.xbutton.x_root = medPtr->global.h;
708    xEvent.xbutton.y_root = medPtr->global.v;
709    xEvent.xbutton.state = medPtr->state;
710    xEvent.xany.serial = LastKnownRequestProcessed(winPtr->display);
711    xEvent.xany.send_event = false;
712    xEvent.xany.display = winPtr->display;
713    xEvent.xany.window = Tk_WindowId(winPtr);
714    Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL);
715
716    return true;
717}
718
719/*
720 *----------------------------------------------------------------------
721 *
722 * TkMacOSXGetEatButtonUp --
723 *
724 * Results:
725 *	Returns the flag indicating if we need to eat the
726 *	next mouse up event
727 *
728 * Side effects:
729 *	None.
730 *
731 *----------------------------------------------------------------------
732 */
733int
734TkMacOSXGetEatButtonUp(void)
735{
736    return gEatButtonUp;
737}
738
739/*
740 *----------------------------------------------------------------------
741 *
742 * TkMacOSXSetEatButtonUp --
743 *
744 * Results:
745 *	None.
746 *
747 * Side effects:
748 *	Sets the flag indicating if we need to eat the
749 *	next mouse up event
750 *
751 *----------------------------------------------------------------------
752 */
753void
754TkMacOSXSetEatButtonUp(int f)
755{
756    gEatButtonUp = f;
757}
758
759/*
760 *----------------------------------------------------------------------
761 *
762 * TkMacOSXKeyModifiers --
763 *
764 *	Returns the current state of the modifier keys.
765 *
766 * Results:
767 *	An OS Modifier state.
768 *
769 * Side effects:
770 *	None.
771 *
772 *----------------------------------------------------------------------
773 */
774
775EventModifiers
776TkMacOSXModifierState(void)
777{
778    UInt32 keyModifiers;
779    int isFrontProcess = (GetCurrentEvent() && Tk_MacOSXIsAppInFront());
780
781    keyModifiers = isFrontProcess ? GetCurrentEventKeyModifiers() :
782	    GetCurrentKeyModifiers();
783
784    return (EventModifiers)(keyModifiers & USHRT_MAX);
785}
786
787/*
788 *----------------------------------------------------------------------
789 *
790 * TkMacOSXButtonKeyState --
791 *
792 *	Returns the current state of the button & modifier keys.
793 *
794 * Results:
795 *	A bitwise inclusive OR of a subset of the following:
796 *	Button1Mask, ShiftMask, LockMask, ControlMask, Mod*Mask.
797 *
798 * Side effects:
799 *	None.
800 *
801 *----------------------------------------------------------------------
802 */
803
804unsigned int
805TkMacOSXButtonKeyState(void)
806{
807    UInt32 buttonState = 0, keyModifiers;
808    int isFrontProcess = (GetCurrentEvent() && Tk_MacOSXIsAppInFront());
809
810    if (!TkMacOSXGetEatButtonUp()) {
811	buttonState = isFrontProcess ? GetCurrentEventButtonState() :
812		GetCurrentButtonState();
813    }
814    keyModifiers = isFrontProcess ? GetCurrentEventKeyModifiers() :
815	    GetCurrentKeyModifiers();
816
817    return ButtonModifiers2State(buttonState, keyModifiers);
818}
819
820/*
821 *----------------------------------------------------------------------
822 *
823 * ButtonModifiers2State --
824 *
825 *	Converts Carbon mouse button state and modifier values into a Tk
826 *	button/modifier state.
827 *
828 * Results:
829 *	None.
830 *
831 * Side effects:
832 *	None.
833 *
834 *----------------------------------------------------------------------
835 */
836
837static unsigned int
838ButtonModifiers2State(UInt32 buttonState, UInt32 keyModifiers)
839{
840    unsigned int state;
841
842    /* Tk supports at most 5 buttons */
843    state = (buttonState & ((1<<5) - 1)) << 8;
844
845    if (keyModifiers & alphaLock) {
846	state |= LockMask;
847    }
848    if (keyModifiers & shiftKey) {
849	state |= ShiftMask;
850    }
851    if (keyModifiers & controlKey) {
852	state |= ControlMask;
853    }
854    if (keyModifiers & cmdKey) {
855	state |= Mod1Mask;		/* command key */
856    }
857    if (keyModifiers & optionKey) {
858	state |= Mod2Mask;		/* option key */
859    }
860    if (keyModifiers & kEventKeyModifierNumLockMask) {
861	state |= Mod3Mask;
862    }
863    if (keyModifiers & kEventKeyModifierFnMask) {
864	state |= Mod4Mask;
865    }
866
867    return state;
868}
869
870/*
871 *----------------------------------------------------------------------
872 *
873 * XQueryPointer --
874 *
875 *	Check the current state of the mouse. This is not a complete
876 *	implementation of this function. It only computes the root
877 *	coordinates and the current mask.
878 *
879 * Results:
880 *	Sets root_x_return, root_y_return, and mask_return. Returns
881 *	true on success.
882 *
883 * Side effects:
884 *	None.
885 *
886 *----------------------------------------------------------------------
887 */
888
889Bool
890XQueryPointer(
891    Display* display,
892    Window w,
893    Window* root_return,
894    Window* child_return,
895    int* root_x_return,
896    int* root_y_return,
897    int* win_x_return,
898    int* win_y_return,
899    unsigned int* mask_return)
900{
901    int getGlobal = (root_x_return && root_y_return);
902    int getLocal = (win_x_return && win_y_return);
903
904    if (getGlobal || getLocal) {
905	Point where, local;
906	OSStatus err = noErr;
907	int gotMouseLoc = 0;
908	EventRef ev = GetCurrentEvent();
909
910	if (ev && getLocal) {
911	    err = ChkErr(GetEventParameter, ev, kEventParamWindowMouseLocation,
912		    typeQDPoint, NULL, sizeof(Point), NULL, &local);
913	    gotMouseLoc = (err == noErr);
914	}
915	if (getGlobal || !gotMouseLoc) {
916	    if (ev) {
917		err = ChkErr(GetEventParameter, ev, kEventParamMouseLocation,
918			typeQDPoint, NULL, sizeof(Point), NULL, &where);
919	    }
920	    if (!ev || err != noErr) {
921		GetGlobalMouse(&where);
922	    }
923	}
924	if (getLocal) {
925	    WindowRef whichWin;
926	    if (ev) {
927		err = ChkErr(GetEventParameter, ev, kEventParamWindowRef,
928			typeWindowRef, NULL, sizeof(WindowRef), NULL,
929			&whichWin);
930	    }
931	    if (!ev || err != noErr) {
932		FindWindow(where, &whichWin);
933	    }
934	    if (gotMouseLoc) {
935		if (whichWin) {
936		    Rect widths;
937
938		    ChkErr(GetWindowStructureWidths, whichWin, &widths);
939		    local.h -= widths.left;
940		    local.v -= widths.top;
941		}
942	    } else {
943		local = where;
944		if (whichWin) {
945		    QDGlobalToLocalPoint(GetWindowPort(whichWin), &local);
946		}
947	    }
948	}
949	if (getGlobal) {
950	    *root_x_return = where.h;
951	    *root_y_return = where.v;
952	}
953	if (getLocal) {
954	    *win_x_return = local.h;
955	    *win_y_return = local.v;
956	}
957    }
958    if (mask_return) {
959	*mask_return = TkMacOSXButtonKeyState();
960    }
961    return True;
962}
963
964/*
965 *----------------------------------------------------------------------
966 *
967 * TkGenerateButtonEventForXPointer --
968 *
969 *	This procedure generates an X button event for the current
970 *	pointer state as reported by XQueryPointer().
971 *
972 * Results:
973 *	True if event(s) are generated - false otherwise.
974 *
975 * Side effects:
976 *	Additional events may be place on the Tk event queue.
977 *	Grab state may also change.
978 *
979 *----------------------------------------------------------------------
980 */
981
982MODULE_SCOPE int
983TkGenerateButtonEventForXPointer(
984    Window window)	  /* X Window containing button event. */
985{
986    MouseEventData med;
987    int global_x, global_y, local_x, local_y;
988
989    bzero(&med, sizeof(MouseEventData));
990    XQueryPointer(NULL, None, NULL, NULL, &global_x, &global_y,
991	    &local_x, &local_y, &med.state);
992    med.global.h = global_x;
993    med.global.v = global_y;
994    med.local.h = local_x;
995    med.local.v = local_y;
996    med.window = window;
997    med.activeNonFloating = ActiveNonFloatingWindow();
998
999    return GenerateButtonEvent(&med);
1000}
1001
1002/*
1003 *----------------------------------------------------------------------
1004 *
1005 * TkGenerateButtonEvent --
1006 *
1007 *	Given a global x & y position and the button key status this
1008 *	procedure generates the appropiate X button event. It also
1009 *	handles the state changes needed to implement implicit grabs.
1010 *
1011 * Results:
1012 *	True if event(s) are generated - false otherwise.
1013 *
1014 * Side effects:
1015 *	Additional events may be place on the Tk event queue.
1016 *	Grab state may also change.
1017 *
1018 *----------------------------------------------------------------------
1019 */
1020
1021int
1022TkGenerateButtonEvent(
1023    int x,		  /* X location of mouse */
1024    int y,		  /* Y location of mouse */
1025    Window window,	  /* X Window containing button event. */
1026    unsigned int state)	  /* Button Key state suitable for X event */
1027{
1028    MouseEventData med;
1029
1030    bzero(&med, sizeof(MouseEventData));
1031    med.state = state;
1032    med.window = window;
1033    med.global.h = x;
1034    med.global.v = y;
1035    FindWindow(med.global, &med.whichWin);
1036    med.activeNonFloating = ActiveNonFloatingWindow();
1037    med.local = med.global;
1038    QDGlobalToLocalPoint(GetWindowPort(med.whichWin), &med.local);
1039
1040    return GenerateButtonEvent(&med);
1041}
1042
1043/*
1044 *----------------------------------------------------------------------
1045 *
1046 * GenerateButtonEvent --
1047 *
1048 *	Generate an X button event from a MouseEventData structure.
1049 *	Handles the state changes needed to implement implicit grabs.
1050 *
1051 * Results:
1052 *	True if event(s) are generated - false otherwise.
1053 *
1054 * Side effects:
1055 *	Additional events may be place on the Tk event queue.
1056 *	Grab state may also change.
1057 *
1058 *----------------------------------------------------------------------
1059 */
1060
1061static int
1062GenerateButtonEvent(MouseEventData * medPtr)
1063{
1064    Tk_Window tkwin;
1065    int dummy;
1066    TkDisplay *dispPtr;
1067
1068#if UNUSED
1069    /*
1070     * ButtonDown events will always occur in the front
1071     * window. ButtonUp events, however, may occur anywhere
1072     * on the screen. ButtonUp events should only be sent
1073     * to Tk if in the front window or during an implicit grab.
1074     */
1075    if ((medPtr->activeNonFloating == NULL)
1076	    || ((!(TkpIsWindowFloating(medPtr->whichWin))
1077	    && (medPtr->activeNonFloating != medPtr->whichWin))
1078	    && TkMacOSXGetCapture() == NULL)) {
1079	return false;
1080    }
1081#endif
1082
1083    dispPtr = TkGetDisplayList();
1084    tkwin = Tk_IdToWindow(dispPtr->display, medPtr->window);
1085
1086    if (tkwin != NULL) {
1087	tkwin = Tk_TopCoordsToWindow(tkwin, medPtr->local.h, medPtr->local.v,
1088		&dummy, &dummy);
1089    }
1090
1091    Tk_UpdatePointer(tkwin, medPtr->global.h, medPtr->global.v, medPtr->state);
1092
1093    return true;
1094}
1095
1096/*
1097 *----------------------------------------------------------------------
1098 *
1099 * GenerateToolbarButtonEvent --
1100 *
1101 *	Generates a "ToolbarButton" virtual event.
1102 *	This can be used to manage disappearing toolbars.
1103 *
1104 * Results:
1105 *	None.
1106 *
1107 * Side effects:
1108 *	Places a virtual event on the event queue.
1109 *
1110 *----------------------------------------------------------------------
1111 */
1112
1113static int
1114GenerateToolbarButtonEvent(MouseEventData * medPtr)
1115{
1116    Tk_Window rootwin, tkwin = NULL;
1117    TkDisplay *dispPtr;
1118    TkWindow  *winPtr;
1119    XVirtualEvent event;
1120
1121    dispPtr = TkGetDisplayList();
1122    rootwin = Tk_IdToWindow(dispPtr->display, medPtr->window);
1123    if (rootwin) {
1124	tkwin = Tk_TopCoordsToWindow(rootwin,
1125		medPtr->local.h, medPtr->local.v, &event.x, &event.y);
1126    }
1127    if (!tkwin) {
1128	return true;
1129    }
1130    winPtr = (TkWindow *)tkwin;
1131
1132    bzero(&event, sizeof(XVirtualEvent));
1133    event.type = VirtualEvent;
1134    event.serial = LastKnownRequestProcessed(winPtr->display);
1135    event.send_event = false;
1136    event.display = winPtr->display;
1137    event.event = winPtr->window;
1138    event.root = XRootWindow(winPtr->display, 0);
1139    event.subwindow = None;
1140    event.time = TkpGetMS();
1141    event.x_root = medPtr->global.h;
1142    event.y_root = medPtr->global.v;
1143    event.state = medPtr->state;
1144    event.same_screen = true;
1145    event.name = Tk_GetUid("ToolbarButton");
1146
1147    Tk_QueueWindowEvent((XEvent *) &event, TCL_QUEUE_TAIL);
1148    return true;
1149}
1150