1/*
2 * tkMacOSXNotify.c --
3 *
4 *	This file contains the implementation of a tcl event source
5 *	for the Carbon event loop.
6 *
7 * Copyright (c) 1995-1997 Sun Microsystems, Inc.
8 * Copyright 2001, Apple Computer, Inc.
9 * Copyright (c) 2005-2007 Daniel A. Steffen <das@users.sourceforge.net>
10 *
11 * See the file "license.terms" for information on usage and redistribution
12 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13 *
14 * RCS: @(#) $Id: tkMacOSXNotify.c,v 1.5.2.12 2007/06/29 03:22:02 das Exp $
15 */
16
17#include "tkMacOSXPrivate.h"
18#include "tkMacOSXEvent.h"
19#include <pthread.h>
20
21/*
22 * The following static indicates whether this module has been initialized
23 * in the current thread.
24 */
25
26typedef struct ThreadSpecificData {
27    int initialized;
28} ThreadSpecificData;
29static Tcl_ThreadDataKey dataKey;
30
31static void TkMacOSXNotifyExitHandler(ClientData clientData);
32static void CarbonEventsSetupProc(ClientData clientData, int flags);
33static void CarbonEventsCheckProc(ClientData clientData, int flags);
34
35/*
36 *----------------------------------------------------------------------
37 *
38 * Tk_MacOSXSetupTkNotifier --
39 *
40 *	This procedure is called during Tk initialization to create
41 *	the event source for Carbon events.
42 *
43 * Results:
44 *	None.
45 *
46 * Side effects:
47 *	A new event source is created.
48 *
49 *----------------------------------------------------------------------
50 */
51
52void
53Tk_MacOSXSetupTkNotifier(void)
54{
55    ThreadSpecificData *tsdPtr = Tcl_GetThreadData(&dataKey,
56	    sizeof(ThreadSpecificData));
57
58    if (!tsdPtr->initialized) {
59	/* HACK ALERT: There is a bug in Jaguar where when it goes to make
60	 * the event queue for the Main Event Loop, it stores the Current
61	 * event loop rather than the Main Event Loop in the Queue structure.
62	 * So we have to make sure that the Main Event Queue gets set up on
63	 * the main thread. Calling GetMainEventQueue will force this to
64	 * happen.
65	 */
66	GetMainEventQueue();
67
68	tsdPtr->initialized = 1;
69	/* Install Carbon events event source in main event loop thread. */
70	if (GetCurrentEventLoop() == GetMainEventLoop()) {
71	    if (!pthread_main_np()) {
72		/*
73		 * Panic if the Carbon main event loop thread (i.e. the
74		 * thread  where HIToolbox was first loaded) is not the
75		 * main application thread, as Carbon does not support
76		 * this properly.
77		 */
78		Tcl_Panic("Tk_MacOSXSetupTkNotifier: %s",
79		    "first [load] of TkAqua has to occur in the main thread!");
80	    }
81	    Tcl_CreateEventSource(CarbonEventsSetupProc,
82		    CarbonEventsCheckProc, GetMainEventQueue());
83	    TkCreateExitHandler(TkMacOSXNotifyExitHandler, NULL);
84	}
85    }
86}
87
88/*
89 *----------------------------------------------------------------------
90 *
91 * TkMacOSXNotifyExitHandler --
92 *
93 *	This function is called during finalization to clean up the
94 *	TkMacOSXNotify module.
95 *
96 * Results:
97 *	None.
98 *
99 * Side effects:
100 *	None.
101 *
102 *----------------------------------------------------------------------
103 */
104
105static void
106TkMacOSXNotifyExitHandler(clientData)
107    ClientData clientData;	/* Not used. */
108{
109    ThreadSpecificData *tsdPtr = Tcl_GetThreadData(&dataKey,
110	    sizeof(ThreadSpecificData));
111
112    Tcl_DeleteEventSource(CarbonEventsSetupProc,
113	    CarbonEventsCheckProc, GetMainEventQueue());
114    tsdPtr->initialized = 0;
115}
116
117/*
118 *----------------------------------------------------------------------
119 *
120 * CarbonEventsSetupProc --
121 *
122 *	This procedure implements the setup part of the Carbon Events
123 *	event source. It is invoked by Tcl_DoOneEvent before entering
124 *	the notifier to check for events.
125 *
126 * Results:
127 *	None.
128 *
129 * Side effects:
130 *	If Carbon events are queued, then the maximum block time will be
131 *	set to 0 to ensure that the notifier returns control to Tcl.
132 *
133 *----------------------------------------------------------------------
134 */
135
136static void
137CarbonEventsSetupProc(clientData, flags)
138    ClientData clientData;
139    int flags;
140{
141    static Tcl_Time blockTime = { 0, 0 };
142
143    if (!(flags & TCL_WINDOW_EVENTS)) {
144	return;
145    }
146
147    if (GetNumEventsInQueue((EventQueueRef)clientData)) {
148	Tcl_SetMaxBlockTime(&blockTime);
149    }
150}
151
152/*
153 *----------------------------------------------------------------------
154 *
155 * CarbonEventsCheckProc --
156 *
157 *	This procedure processes events sitting in the Carbon event
158 *	queue.
159 *
160 * Results:
161 *	None.
162 *
163 * Side effects:
164 *	Moves applicable queued Carbon events onto the Tcl event queue.
165 *
166 *----------------------------------------------------------------------
167 */
168
169static void
170CarbonEventsCheckProc(clientData, flags)
171    ClientData clientData;
172    int flags;
173{
174    int numFound;
175    OSStatus err = noErr;
176
177    if (!(flags & TCL_WINDOW_EVENTS)) {
178	return;
179    }
180
181    numFound = GetNumEventsInQueue((EventQueueRef)clientData);
182
183    /* Avoid starving other event sources: */
184    if (numFound > 4) {
185	numFound = 4;
186    }
187    while (numFound > 0 && err == noErr) {
188	err = TkMacOSXReceiveAndDispatchEvent();
189	numFound--;
190    }
191}
192