1/*
2 * tkMacOSXCarbonEvents.c --
3 *
4 *	This file implements functions that register for and handle
5 *	various Carbon Events and Timers. Most carbon events of interest
6 *	to TkAqua are processed in a handler registered on the dispatcher
7 *	event target so that we get first crack at them before HIToolbox
8 *	dispatchers/processes them further.
9 *	As some events are sent directly to the focus or app event target
10 *	and not dispatched normally, we also register a handler on the
11 *	application event target.
12 *
13 * Copyright 2001, Apple Computer, Inc.
14 * Copyright (c) 2005-2008 Daniel A. Steffen <das@users.sourceforge.net>
15 *
16 * See the file "license.terms" for information on usage and redistribution of
17 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
18 *
19 *	The following terms apply to all files originating from Apple
20 *	Computer, Inc. ("Apple") and associated with the software
21 *	unless explicitly disclaimed in individual files.
22 *
23 *
24 *	Apple hereby grants permission to use, copy, modify,
25 *	distribute, and license this software and its documentation
26 *	for any purpose, provided that existing copyright notices are
27 *	retained in all copies and that this notice is included
28 *	verbatim in any distributions. No written agreement, license,
29 *	or royalty fee is required for any of the authorized
30 *	uses. Modifications to this software may be copyrighted by
31 *	their authors and need not follow the licensing terms
32 *	described here, provided that the new terms are clearly
33 *	indicated on the first page of each file where they apply.
34 *
35 *
36 *	IN NO EVENT SHALL APPLE, THE AUTHORS OR DISTRIBUTORS OF THE
37 *	SOFTWARE BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL,
38 *	INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF
39 *	THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
40 *	EVEN IF APPLE OR THE AUTHORS HAVE BEEN ADVISED OF THE
41 *	POSSIBILITY OF SUCH DAMAGE.  APPLE, THE AUTHORS AND
42 *	DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
43 *	BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
44 *	FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.	 THIS
45 *	SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND APPLE,THE
46 *	AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
47 *	MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
48 *
49 *	GOVERNMENT USE: If you are acquiring this software on behalf
50 *	of the U.S. government, the Government shall have only
51 *	"Restricted Rights" in the software and related documentation
52 *	as defined in the Federal Acquisition Regulations (FARs) in
53 *	Clause 52.227.19 (c) (2).  If you are acquiring the software
54 *	on behalf of the Department of Defense, the software shall be
55 *	classified as "Commercial Computer Software" and the
56 *	Government shall have only "Restricted Rights" as defined in
57 *	Clause 252.227-7013 (c) (1) of DFARs.  Notwithstanding the
58 *	foregoing, the authors grant the U.S. Government and others
59 *	acting in its behalf permission to use and distribute the
60 *	software in accordance with the terms specified in this
61 *	license.
62 *
63 * RCS: @(#) $Id: tkMacOSXCarbonEvents.c,v 1.3.2.18 2007/11/09 06:26:54 das Exp $
64 */
65
66#include "tkMacOSXPrivate.h"
67#include "tkMacOSXEvent.h"
68#include "tkMacOSXDebug.h"
69
70/*
71#ifdef TK_MAC_DEBUG
72#define TK_MAC_DEBUG_CARBON_EVENTS
73#endif
74*/
75
76/*
77 * Declarations of functions used only in this file:
78 */
79
80static OSStatus CarbonEventHandlerProc(EventHandlerCallRef callRef,
81	EventRef event, void *userData);
82static OSStatus InstallStandardApplicationEventHandler(void);
83static void CarbonTimerProc(EventLoopTimerRef timer, void *userData);
84
85/*
86 * Static data used by several functions in this file:
87 */
88
89static EventLoopTimerRef carbonTimer = NULL;
90static int carbonTimerEnabled = 0;
91static EventHandlerUPP carbonEventHandlerUPP = NULL;
92static Tcl_Interp *carbonEventInterp = NULL;
93static int inTrackingLoop = 0;
94
95#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
96/*
97 * For InstallStandardApplicationEventHandler():
98 */
99
100static jmp_buf exitRaelJmpBuf;
101static void ExitRaelEventHandlerProc(EventHandlerCallRef callRef,
102	EventRef event, void *userData) __attribute__ ((__noreturn__));
103#endif
104
105
106/*
107 *----------------------------------------------------------------------
108 *
109 * CarbonEventHandlerProc --
110 *
111 *	This procedure is the handler for all registered CarbonEvents.
112 *
113 * Results:
114 *	OS status code.
115 *
116 * Side effects:
117 *	Dispatches CarbonEvents.
118 *
119 *----------------------------------------------------------------------
120 */
121
122static OSStatus
123CarbonEventHandlerProc(
124    EventHandlerCallRef callRef,
125    EventRef event,
126    void *userData)
127{
128    OSStatus err = eventNotHandledErr;
129    TkMacOSXEvent macEvent;
130    MacEventStatus eventStatus;
131
132    macEvent.eventRef = event;
133    macEvent.eClass = GetEventClass(event);
134    macEvent.eKind = GetEventKind(event);
135    macEvent.interp = (Tcl_Interp *) userData;
136    macEvent.callRef = callRef;
137    bzero(&eventStatus, sizeof(eventStatus));
138
139#ifdef TK_MAC_DEBUG_CARBON_EVENTS
140    if (!(macEvent.eClass == kEventClassMouse && (
141	    macEvent.eKind == kEventMouseMoved ||
142	    macEvent.eKind == kEventMouseDragged))) {
143	TkMacOSXDbgMsg("Started handling %s",
144		TkMacOSXCarbonEventToAscii(event));
145	TkMacOSXInitNamedDebugSymbol(HIToolbox, void, _DebugPrintEvent,
146		EventRef inEvent);
147	if (_DebugPrintEvent) {
148	    /*
149	     * Carbon-internal event debugging (c.f. Technote 2124)
150	     */
151
152	    _DebugPrintEvent(event);
153	}
154    }
155#endif /* TK_MAC_DEBUG_CARBON_EVENTS */
156
157    TkMacOSXProcessEvent(&macEvent,&eventStatus);
158    if (eventStatus.stopProcessing) {
159	err = noErr;
160    }
161
162#ifdef TK_MAC_DEBUG_CARBON_EVENTS
163    if (macEvent.eKind != kEventMouseMoved &&
164	    macEvent.eKind != kEventMouseDragged) {
165	TkMacOSXDbgMsg("Finished handling %s: %s handled",
166		TkMacOSXCarbonEventToAscii(event),
167		eventStatus.stopProcessing ? "   " : "not");
168    }
169#endif /* TK_MAC_DEBUG_CARBON_EVENTS */
170    return err;
171}
172
173/*
174 *----------------------------------------------------------------------
175 *
176 * TkMacOSXInitCarbonEvents --
177 *
178 *	This procedure initializes all CarbonEvent handlers.
179 *
180 * Results:
181 *	None.
182 *
183 * Side effects:
184 *	Handlers for Carbon Events are registered.
185 *
186 *----------------------------------------------------------------------
187 */
188
189MODULE_SCOPE void
190TkMacOSXInitCarbonEvents(
191    Tcl_Interp *interp)
192{
193    const EventTypeSpec dispatcherEventTypes[] = {
194	{kEventClassKeyboard,	 kEventRawKeyDown},
195	{kEventClassKeyboard,	 kEventRawKeyRepeat},
196	{kEventClassKeyboard,	 kEventRawKeyUp},
197	{kEventClassKeyboard,	 kEventRawKeyModifiersChanged},
198	{kEventClassKeyboard,	 kEventRawKeyRepeat},
199    };
200    const EventTypeSpec applicationEventTypes[] = {
201	{kEventClassMenu,	 kEventMenuBeginTracking},
202	{kEventClassMenu,	 kEventMenuEndTracking},
203	{kEventClassMenu,	 kEventMenuOpening},
204	{kEventClassMenu,	 kEventMenuTargetItem},
205	{kEventClassCommand,	 kEventCommandProcess},
206	{kEventClassCommand,	 kEventCommandUpdateStatus},
207	{kEventClassApplication, kEventAppActivated},
208	{kEventClassApplication, kEventAppDeactivated},
209	{kEventClassApplication, kEventAppQuit},
210	{kEventClassApplication, kEventAppHidden},
211	{kEventClassApplication, kEventAppShown},
212	{kEventClassApplication, kEventAppAvailableWindowBoundsChanged},
213	{kEventClassAppearance,	 kEventAppearanceScrollBarVariantChanged},
214    };
215
216    carbonEventHandlerUPP = NewEventHandlerUPP(CarbonEventHandlerProc);
217    carbonEventInterp = interp;
218    ChkErr(InstallStandardApplicationEventHandler);
219    ChkErr(InstallEventHandler, GetEventDispatcherTarget(),
220	    carbonEventHandlerUPP, GetEventTypeCount(dispatcherEventTypes),
221	    dispatcherEventTypes, (void *) carbonEventInterp, NULL);
222    ChkErr(InstallEventHandler, GetApplicationEventTarget(),
223	    carbonEventHandlerUPP, GetEventTypeCount(applicationEventTypes),
224	    applicationEventTypes, (void *) carbonEventInterp, NULL);
225
226#ifdef TK_MAC_DEBUG_CARBON_EVENTS
227    TkMacOSXInitNamedSymbol(HIToolbox, void, DebugTraceEvent, OSType, UInt32,
228	    Boolean);
229    if (DebugTraceEvent) {
230	unsigned int i;
231	const EventTypeSpec *e;
232
233	for (i = 0, e = dispatcherEventTypes;
234		i < GetEventTypeCount(dispatcherEventTypes); i++, e++) {
235	    DebugTraceEvent(e->eventClass, e->eventKind, 1);
236	}
237	for (i = 0, e = applicationEventTypes;
238		i < GetEventTypeCount(applicationEventTypes); i++, e++) {
239	    DebugTraceEvent(e->eventClass, e->eventKind, 1);
240	}
241	DebugTraceEvent = NULL; /* Only enable tracing once. */
242    }
243#endif /* TK_MAC_DEBUG_CARBON_EVENTS */
244}
245
246/*
247 *----------------------------------------------------------------------
248 *
249 * TkMacOSXInstallWindowCarbonEventHandler --
250 *
251 *	This procedure installs our window CarbonEvent handler.
252 *
253 * Results:
254 *	None.
255 *
256 * Side effects:
257 *	Handler for Carbon Events is registered.
258 *
259 *----------------------------------------------------------------------
260 */
261
262MODULE_SCOPE void
263TkMacOSXInstallWindowCarbonEventHandler(
264	Tcl_Interp *interp, WindowRef window)
265{
266    const EventTypeSpec windowEventTypes[] = {
267	{kEventClassMouse,	 kEventMouseDown},
268	{kEventClassMouse,	 kEventMouseUp},
269	{kEventClassMouse,	 kEventMouseMoved},
270	{kEventClassMouse,	 kEventMouseDragged},
271	{kEventClassMouse,	 kEventMouseWheelMoved},
272	{kEventClassWindow,	 kEventWindowActivated},
273	{kEventClassWindow,	 kEventWindowDeactivated},
274	{kEventClassWindow,	 kEventWindowUpdate},
275	{kEventClassWindow,	 kEventWindowExpanding},
276	{kEventClassWindow,	 kEventWindowBoundsChanged},
277	{kEventClassWindow,	 kEventWindowDragStarted},
278	{kEventClassWindow,	 kEventWindowDragCompleted},
279	{kEventClassWindow,	 kEventWindowConstrain},
280	{kEventClassWindow,	 kEventWindowGetRegion},
281	{kEventClassWindow,	 kEventWindowDrawContent},
282    };
283
284    ChkErr(InstallEventHandler, GetWindowEventTarget(window),
285	    carbonEventHandlerUPP, GetEventTypeCount(windowEventTypes),
286	    windowEventTypes, (void *) (interp ? interp : carbonEventInterp),
287	    NULL);
288
289#ifdef TK_MAC_DEBUG_CARBON_EVENTS
290    TkMacOSXInitNamedSymbol(HIToolbox, void, DebugTraceEvent, OSType, UInt32,
291	    Boolean);
292    if (DebugTraceEvent) {
293	unsigned int i;
294	const EventTypeSpec *e;
295
296	for (i = 0, e = windowEventTypes;
297		i < GetEventTypeCount(windowEventTypes); i++, e++) {
298	    if (!(e->eventClass == kEventClassMouse && (
299		    e->eventKind == kEventMouseMoved ||
300		    e->eventKind == kEventMouseDragged))) {
301		DebugTraceEvent(e->eventClass, e->eventKind, 1);
302	    }
303	}
304    }
305#endif /* TK_MAC_DEBUG_CARBON_EVENTS */
306}
307
308/*
309 *----------------------------------------------------------------------
310 *
311 * InstallStandardApplicationEventHandler --
312 *
313 *	This procedure installs the carbon standard application event
314 *	handler.
315 *
316 * Results:
317 *	OS status code.
318 *
319 * Side effects:
320 *	Standard handlers for application Carbon Events are registered.
321 *
322 *----------------------------------------------------------------------
323 */
324
325static OSStatus
326InstallStandardApplicationEventHandler(void)
327{
328    OSStatus err = memFullErr;
329
330    TK_IF_HI_TOOLBOX(5,
331       /*
332	* The approach below does not work correctly in Leopard, it leads to
333	* crashes in [NSView unlockFocus] whenever HIToolbox uses Cocoa (Help
334	* menu, Nav Services, Color Picker). While it is now possible to
335	* install the standard app handler with InstallStandardEventHandler(),
336	* to fully replicate RAEL the standard menubar event handler also needs
337	* to be installed. Unfortunately there appears to be no public API to
338	* obtain the menubar event target. As a workaround, for now we resort
339	* to calling the HIToolbox-internal GetMenuBarEventTarget() directly
340	* (symbol acquired via TkMacOSXInitNamedSymbol() from HIToolbox
341	* version 343, may not exist in later versions).
342	*/
343	err = ChkErr(InstallStandardEventHandler, GetApplicationEventTarget());
344	TkMacOSXInitNamedSymbol(HIToolbox, EventTargetRef,
345		GetMenuBarEventTarget, void);
346	if (GetMenuBarEventTarget) {
347	    ChkErr(InstallStandardEventHandler, GetMenuBarEventTarget());
348	} else {
349	    TkMacOSXDbgMsg("Unable to install standard menubar event handler");
350	}
351    ) TK_ELSE_HI_TOOLBOX (5,
352       /*
353	* This is a hack to workaround missing Carbon API to install the
354	* standard application event handler (InstallStandardEventHandler()
355	* does not work on the application target). The only way to install the
356	* standard app handler is to call RunApplicationEventLoop(), but since
357	* we are running our own event loop, we'll immediately need to break
358	* out of RAEL again: we do this via longjmp out of the
359	* ExitRaelEventHandlerProc event handler called first off from RAEL by
360	* posting a high priority dummy event. This workaround is derived from
361	* a similar approach in Technical Q&A 1061.
362	*/
363	enum {
364	    kExitRaelEvent = 'ExiT'
365	};
366	const EventTypeSpec exitRaelEventType = {
367	    kExitRaelEvent, kExitRaelEvent
368	};
369	EventHandlerUPP exitRaelEventHandler;
370	EventHandlerRef exitRaelEventHandlerRef = NULL;
371	EventRef exitRaelEvent = NULL;
372
373	exitRaelEventHandler = NewEventHandlerUPP(
374		(EventHandlerProcPtr) ExitRaelEventHandlerProc);
375	if (exitRaelEventHandler) {
376	    err = ChkErr(InstallEventHandler, GetEventDispatcherTarget(),
377		    exitRaelEventHandler, 1, &exitRaelEventType, NULL,
378		    &exitRaelEventHandlerRef);
379	}
380	if (err == noErr) {
381	    err = ChkErr(CreateEvent, NULL, kExitRaelEvent, kExitRaelEvent,
382		    GetCurrentEventTime(), kEventAttributeNone,
383		    &exitRaelEvent);
384	}
385	if (err == noErr) {
386	    err = ChkErr(PostEventToQueue, GetMainEventQueue(), exitRaelEvent,
387		    kEventPriorityHigh);
388	}
389	if (err == noErr) {
390	    if (!setjmp(exitRaelJmpBuf)) {
391		RunApplicationEventLoop();
392
393		/*
394		 * This point should never be reached!
395		 */
396
397		Tcl_Panic("RunApplicationEventLoop exited !");
398	    }
399	}
400	if (exitRaelEvent) {
401	    ReleaseEvent(exitRaelEvent);
402	}
403	if (exitRaelEventHandlerRef) {
404	    RemoveEventHandler(exitRaelEventHandlerRef);
405	}
406	if (exitRaelEventHandler) {
407	    DisposeEventHandlerUPP(exitRaelEventHandler);
408	}
409    ) TK_ENDIF
410    return err;
411}
412
413#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
414/*
415 *----------------------------------------------------------------------
416 *
417 * ExitRaelEventHandlerProc --
418 *
419 *	This procedure is the dummy event handler used to break out of
420 *	RAEL via longjmp, it is called as the first ever event handler
421 *	in RAEL by posting a high priority dummy event.
422 *
423 * Results:
424 *	None. Never returns !
425 *
426 * Side effects:
427 *	longjmp back to InstallStandardApplicationEventHandler().
428 *
429 *----------------------------------------------------------------------
430 */
431
432static void
433ExitRaelEventHandlerProc(
434    EventHandlerCallRef callRef,
435    EventRef event,
436    void *userData)
437{
438    longjmp(exitRaelJmpBuf, 1);
439}
440#endif
441
442/*
443 *----------------------------------------------------------------------
444 *
445 * TkMacOSXRunTclEventLoop --
446 *
447 *	Process a limited number of tcl events.
448 *
449 * Results:
450 *	Returns 1 if events were handled and 0 otherwise.
451 *
452 * Side effects:
453 *	Runs the Tcl event loop.
454 *
455 *----------------------------------------------------------------------
456 */
457
458MODULE_SCOPE int
459TkMacOSXRunTclEventLoop(void)
460{
461    int i = 4, result = 0;
462
463    /* Avoid starving main event loop: process at most 4 events. */
464    while(--i && Tcl_ServiceAll()) {
465	result = 1;
466    }
467    return result;
468}
469
470/*
471 *----------------------------------------------------------------------
472 *
473 * CarbonTimerProc --
474 *
475 *	This procedure is the carbon timer handler that runs the tcl
476 *	event loop periodically.
477 *
478 * Results:
479 *	None.
480 *
481 * Side effects:
482 *	Runs the Tcl event loop.
483 *
484 *----------------------------------------------------------------------
485 */
486
487static void
488CarbonTimerProc(
489    EventLoopTimerRef timer,
490    void *userData)
491{
492    if(carbonTimerEnabled > 0 && TkMacOSXRunTclEventLoop()) {
493#ifdef TK_MAC_DEBUG_CARBON_EVENTS
494	TkMacOSXDbgMsg("Processed tcl events from carbon timer");
495#endif /* TK_MAC_DEBUG_CARBON_EVENTS */
496    }
497}
498
499/*
500 *----------------------------------------------------------------------
501 *
502 * TkMacOSXStartTclEventLoopCarbonTimer --
503 *
504 *	This procedure installs (if necessary) and starts a carbon
505 *	event timer that runs the tcl event loop periodically.
506 *	It should be called whenever a nested carbon event loop might
507 *	run by HIToolbox (e.g. during mouse tracking) to ensure that
508 *	tcl events continue to be processed.
509 *
510 * Results:
511 *	OS status code.
512 *
513 * Side effects:
514 *	Carbon event timer is installed and started.
515 *
516 *----------------------------------------------------------------------
517 */
518
519MODULE_SCOPE OSStatus
520TkMacOSXStartTclEventLoopCarbonTimer(void)
521{
522    OSStatus err = noErr;
523
524    if (++carbonTimerEnabled > 0) {
525	if(!carbonTimer) {
526	    EventLoopTimerUPP timerUPP = NewEventLoopTimerUPP(CarbonTimerProc);
527
528	    err = ChkErr(InstallEventLoopTimer, GetMainEventLoop(),
529		    5 * kEventDurationMillisecond,
530		    5 * kEventDurationMillisecond,
531		    timerUPP, NULL, &carbonTimer);
532	} else {
533	    err = ChkErr(SetEventLoopTimerNextFireTime, carbonTimer,
534		    5 * kEventDurationMillisecond);
535	}
536    }
537    return err;
538}
539
540/*
541 *----------------------------------------------------------------------
542 *
543 * TkMacOSXStopTclEventLoopCarbonTimer --
544 *
545 *	This procedure stops the carbon event timer started by
546 *	TkMacOSXStartTclEventLoopCarbonTimer().
547 *
548 * Results:
549 *	OS status code.
550 *
551 * Side effects:
552 *	Carbon event timer is stopped.
553 *
554 *----------------------------------------------------------------------
555 */
556
557MODULE_SCOPE OSStatus
558TkMacOSXStopTclEventLoopCarbonTimer(void)
559{
560    OSStatus err = noErr;
561
562    if (--carbonTimerEnabled == 0) {
563	if(carbonTimer) {
564	    err = ChkErr(SetEventLoopTimerNextFireTime, carbonTimer,
565		    kEventDurationForever);
566	}
567    }
568    return err;
569}
570
571/*
572 *----------------------------------------------------------------------
573 *
574 * TkMacOSXTrackingLoop --
575 *
576 *	Call with 1 before entering a mouse tracking loop (e.g. window
577 *	resizing or menu tracking) to enable tcl event processing but
578 *	disable  carbon event processing (except for update events)
579 *	during the loop, and with 0 after exiting the loop to reset.
580 *
581 * Results:
582 *	None.
583 *
584 * Side effects:
585 *	None.
586 *
587 *----------------------------------------------------------------------
588 */
589
590MODULE_SCOPE void
591TkMacOSXTrackingLoop(int tracking)
592{
593    static int previousServiceMode = TCL_SERVICE_NONE;
594
595    if (tracking) {
596	inTrackingLoop++;
597	previousServiceMode = Tcl_SetServiceMode(TCL_SERVICE_ALL);
598	TkMacOSXStartTclEventLoopCarbonTimer();
599#ifdef TK_MAC_DEBUG_CARBON_EVENTS
600	TkMacOSXDbgMsg("Entering tracking loop");
601#endif /* TK_MAC_DEBUG_CARBON_EVENTS */
602    } else {
603	TkMacOSXStopTclEventLoopCarbonTimer();
604	previousServiceMode = Tcl_SetServiceMode(previousServiceMode);
605	inTrackingLoop--;
606#ifdef TK_MAC_DEBUG_CARBON_EVENTS
607	TkMacOSXDbgMsg("Exiting tracking loop");
608#endif /* TK_MAC_DEBUG_CARBON_EVENTS */
609    }
610}
611
612/*
613 *----------------------------------------------------------------------
614 *
615 * TkMacOSXReceiveAndDispatchEvent --
616 *
617 *	This receives a carbon event and sends it to the carbon event
618 *	dispatcher.
619 *
620 * Results:
621 *	Mac OS status
622 *
623 * Side effects:
624 *	This receives and dispatches the next Carbon event.
625 *
626 *----------------------------------------------------------------------
627 */
628MODULE_SCOPE OSStatus
629TkMacOSXReceiveAndDispatchEvent(void)
630{
631    static EventTargetRef targetRef = NULL;
632    int numEventTypes = 0;
633    const EventTypeSpec *eventTypes = NULL;
634    EventRef eventRef;
635    OSStatus err;
636    const EventTypeSpec trackingEventTypes[] = {
637	{'dniw',		 kEventWindowUpdate},
638	{kEventClassWindow,	 kEventWindowUpdate},
639    };
640
641    if (inTrackingLoop > 0) {
642	eventTypes = trackingEventTypes;
643	numEventTypes = GetEventTypeCount(trackingEventTypes);
644    }
645
646    /*
647     * This is a poll, since we have already counted the events coming
648     * into this routine, and are guaranteed to have one waiting.
649     */
650
651    err = ReceiveNextEvent(numEventTypes, eventTypes,
652	    kEventDurationNoWait, true, &eventRef);
653    if (err == noErr) {
654#ifdef TK_MAC_DEBUG_CARBON_EVENTS
655	UInt32 kind = GetEventKind(eventRef);
656
657	if (kind != kEventMouseMoved && kind != kEventMouseDragged) {
658	    TkMacOSXDbgMsg("Dispatching %s", TkMacOSXCarbonEventToAscii(eventRef));
659	    TkMacOSXInitNamedDebugSymbol(HIToolbox, void, _DebugPrintEvent,
660		    EventRef inEvent);
661	    if (_DebugPrintEvent) {
662		/* Carbon-internal event debugging (c.f. Technote 2124) */
663		_DebugPrintEvent(eventRef);
664	    }
665	}
666#endif /* TK_MAC_DEBUG_CARBON_EVENTS */
667	if (!targetRef) {
668	    targetRef = GetEventDispatcherTarget();
669	}
670	TkMacOSXStartTclEventLoopCarbonTimer();
671	err = SendEventToEventTarget(eventRef, targetRef);
672	TkMacOSXStopTclEventLoopCarbonTimer();
673	if (err != noErr && err != eventLoopTimedOutErr
674		&& err != eventNotHandledErr) {
675	    TkMacOSXDbgMsg("SendEventToEventTarget(%s) failed: %ld",
676		    TkMacOSXCarbonEventToAscii(eventRef), err);
677	}
678	ReleaseEvent(eventRef);
679    } else if (err != eventLoopTimedOutErr) {
680	TkMacOSXDbgMsg("ReceiveNextEvent failed: %ld", err);
681    }
682    return err;
683}
684