blob: 140dadb8bbe25c42607368880d73081c5ee925b4 [file] [log] [blame]
Jack Jansenb0195ec1998-08-18 14:51:27 +00001/*
2 * tclMacNotify.c --
3 *
4 * This file contains Macintosh-specific procedures for the notifier,
5 * which is the lowest-level part of the Tcl event loop. This file
6 * works together with ../generic/tclNotify.c.
7 *
8 * Copyright (c) 1995-1996 Sun Microsystems, Inc.
9 *
10 * See the file "license.terms" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 *
13 * SCCS: @(#) tclMacNotify.c 1.36 97/05/07 19:09:29
14 */
15
16#include "tclInt.h"
17#include "tclPort.h"
18#include "tclMac.h"
19#include "tclMacInt.h"
20#include <signal.h>
21#include <Events.h>
22#include <LowMem.h>
23#include <Processes.h>
24#include <Timer.h>
25
26
27/*
28 * This is necessary to work around a bug in Apple's Universal header files
29 * for the CFM68K libraries.
30 */
31
32#ifdef __CFM68K__
33#undef GetEventQueue
34extern pascal QHdrPtr GetEventQueue(void)
35 THREEWORDINLINE(0x2EBC, 0x0000, 0x014A);
36#pragma import list GetEventQueue
37#define GetEvQHdr() GetEventQueue()
38#endif
39
40/*
41 * The follwing static indicates whether this module has been initialized.
42 */
43
44static int initialized = 0;
45
46/*
47 * The following structure contains the state information for the
48 * notifier module.
49 */
50
51static struct {
52 int timerActive; /* 1 if timer is running. */
53 Tcl_Time timer; /* Time when next timer event is expected. */
54 int flags; /* OR'ed set of flags defined below. */
55 Point lastMousePosition; /* Last known mouse location. */
56 RgnHandle utilityRgn; /* Region used as the mouse region for
57 * WaitNextEvent and the update region when
58 * checking for events. */
59 Tcl_MacConvertEventPtr eventProcPtr;
60 /* This pointer holds the address of the
61 * function that will handle all incoming
62 * Macintosh events. */
63} notifier;
64
65/*
66 * The following defines are used in the flags field of the notifier struct.
67 */
68
69#define NOTIFY_IDLE (1<<1) /* Tcl_ServiceIdle should be called. */
70#define NOTIFY_TIMER (1<<2) /* Tcl_ServiceTimer should be called. */
71
72/*
73 * Prototypes for procedures that are referenced only in this file:
74 */
75
76static int HandleMacEvents _ANSI_ARGS_((void));
77static void InitNotifier _ANSI_ARGS_((void));
78static void NotifierExitHandler _ANSI_ARGS_((
79 ClientData clientData));
80
81/*
82 *----------------------------------------------------------------------
83 *
84 * InitNotifier --
85 *
86 * Initializes the notifier structure.
87 *
88 * Results:
89 * None.
90 *
91 * Side effects:
92 * Creates a new exit handler.
93 *
94 *----------------------------------------------------------------------
95 */
96
97static void
98InitNotifier(void)
99{
100 initialized = 1;
101 memset(&notifier, 0, sizeof(notifier));
102 Tcl_CreateExitHandler(NotifierExitHandler, NULL);
103}
104
105/*
106 *----------------------------------------------------------------------
107 *
108 * NotifierExitHandler --
109 *
110 * This function is called to cleanup the notifier state before
111 * Tcl is unloaded.
112 *
113 * Results:
114 * None.
115 *
116 * Side effects:
117 * None.
118 *
119 *----------------------------------------------------------------------
120 */
121
122static void
123NotifierExitHandler(
124 ClientData clientData) /* Not used. */
125{
126 initialized = 0;
127}
128
129/*
130 *----------------------------------------------------------------------
131 *
132 * HandleMacEvents --
133 *
134 * This function checks for events from the Macintosh event queue.
135 *
136 * Results:
137 * Returns 1 if event found, 0 otherwise.
138 *
139 * Side effects:
140 * Pulls events off of the Mac event queue and then calls
141 * convertEventProc.
142 *
143 *----------------------------------------------------------------------
144 */
145
146static int
147HandleMacEvents(void)
148{
149 EventRecord theEvent;
150 int eventFound = 0, needsUpdate = 0;
151 Point currentMouse;
152 WindowRef windowRef;
153 Rect mouseRect;
154
155 /*
156 * Check for mouse moved events. These events aren't placed on the
157 * system event queue unless we call WaitNextEvent.
158 */
159
160 GetGlobalMouse(&currentMouse);
161 if ((notifier.eventProcPtr != NULL) &&
162 !EqualPt(currentMouse, notifier.lastMousePosition)) {
163 notifier.lastMousePosition = currentMouse;
164 theEvent.what = nullEvent;
165 if ((*notifier.eventProcPtr)(&theEvent) == true) {
166 eventFound = 1;
167 }
168 }
169
170 /*
171 * Check for update events. Since update events aren't generated
172 * until we call GetNextEvent, we may need to force a call to
173 * GetNextEvent, even if the queue is empty.
174 */
175
176 for (windowRef = FrontWindow(); windowRef != NULL;
177 windowRef = GetNextWindow(windowRef)) {
178 GetWindowUpdateRgn(windowRef, notifier.utilityRgn);
179 if (!EmptyRgn(notifier.utilityRgn)) {
180 needsUpdate = 1;
181 break;
182 }
183 }
184
185 /*
186 * Process events from the OS event queue.
187 */
188
189 while (needsUpdate || (GetEvQHdr()->qHead != NULL)) {
190 GetGlobalMouse(&currentMouse);
191 SetRect(&mouseRect, currentMouse.h, currentMouse.v,
192 currentMouse.h + 1, currentMouse.v + 1);
193 RectRgn(notifier.utilityRgn, &mouseRect);
194
195 WaitNextEvent(everyEvent, &theEvent, 5, notifier.utilityRgn);
196 needsUpdate = 0;
197 if ((notifier.eventProcPtr != NULL)
198 && ((*notifier.eventProcPtr)(&theEvent) == true)) {
199 eventFound = 1;
200 }
201 }
202
203 return eventFound;
204}
205
206/*
207 *----------------------------------------------------------------------
208 *
209 * Tcl_SetTimer --
210 *
211 * This procedure sets the current notifier timer value. The
212 * notifier will ensure that Tcl_ServiceAll() is called after
213 * the specified interval, even if no events have occurred.
214 *
215 * Results:
216 * None.
217 *
218 * Side effects:
219 * Replaces any previous timer.
220 *
221 *----------------------------------------------------------------------
222 */
223
224void
225Tcl_SetTimer(
226 Tcl_Time *timePtr) /* New value for interval timer. */
227{
228 if (!timePtr) {
229 notifier.timerActive = 0;
230 } else {
231 /*
232 * Compute when the timer should fire.
233 */
234
235 TclpGetTime(&notifier.timer);
236 notifier.timer.sec += timePtr->sec;
237 notifier.timer.usec += timePtr->usec;
238 if (notifier.timer.usec >= 1000000) {
239 notifier.timer.usec -= 1000000;
240 notifier.timer.sec += 1;
241 }
242 notifier.timerActive = 1;
243 }
244}
245
246/*
247 *----------------------------------------------------------------------
248 *
249 * Tcl_WaitForEvent --
250 *
251 * This function is called by Tcl_DoOneEvent to wait for new
252 * events on the message queue. If the block time is 0, then
253 * Tcl_WaitForEvent just polls the event queue without blocking.
254 *
255 * Results:
256 * Always returns 0.
257 *
258 * Side effects:
259 * None.
260 *
261 *----------------------------------------------------------------------
262 */
263
264int
265Tcl_WaitForEvent(
266 Tcl_Time *timePtr) /* Maximum block time. */
267{
268 int found;
269 EventRecord macEvent;
270 long sleepTime = 5;
271 long ms;
272 Point currentMouse;
273 void * timerToken;
274 Rect mouseRect;
275
276 /*
277 * Compute the next timeout value.
278 */
279
280 if (!timePtr) {
281 ms = INT_MAX;
282 } else {
283 ms = (timePtr->sec * 1000) + (timePtr->usec / 1000);
284 }
285 timerToken = TclMacStartTimer((long) ms);
286
287 /*
288 * Poll the Mac event sources. This loop repeats until something
289 * happens: a timeout, a socket event, mouse motion, or some other
290 * window event. Note that we don't call WaitNextEvent if another
291 * event is found to avoid context switches. This effectively gives
292 * events coming in via WaitNextEvent a slightly lower priority.
293 */
294
295 found = 0;
296 if (notifier.utilityRgn == NULL) {
297 notifier.utilityRgn = NewRgn();
298 }
299
300 while (!found) {
301 /*
302 * Check for generated and queued events.
303 */
304
305 if (HandleMacEvents()) {
306 found = 1;
307 }
308
309 /*
310 * Check for time out.
311 */
312
313 if (!found && TclMacTimerExpired(timerToken)) {
314 found = 1;
315 }
316
317 /*
318 * Mod by Jack: poll for select() events. Code is in TclSelectNotify.c
319 */
320 {
321 int Tcl_PollSelectEvent(void);
322 if (!found && Tcl_PollSelectEvent())
323 found = 1;
324 }
325
326 /*
327 * Check for window events. We may receive a NULL event for
328 * various reasons. 1) the timer has expired, 2) a mouse moved
329 * event is occuring or 3) the os is giving us time for idle
330 * events. Note that we aren't sharing the processor very
331 * well here. We really ought to do a better job of calling
332 * WaitNextEvent for time slicing purposes.
333 */
334
335 if (!found) {
336 /*
337 * Set up mouse region so we will wake if the mouse is moved.
338 * We do this by defining the smallest possible region around
339 * the current mouse position.
340 */
341
342 GetGlobalMouse(&currentMouse);
343 SetRect(&mouseRect, currentMouse.h, currentMouse.v,
344 currentMouse.h + 1, currentMouse.v + 1);
345 RectRgn(notifier.utilityRgn, &mouseRect);
346
347 WaitNextEvent(everyEvent, &macEvent, sleepTime,
348 notifier.utilityRgn);
349
350 if (notifier.eventProcPtr != NULL) {
351 if ((*notifier.eventProcPtr)(&macEvent) == true) {
352 found = 1;
353 }
354 }
355 }
356 }
357 TclMacRemoveTimer(timerToken);
358 return 0;
359}
360
361/*
362 *----------------------------------------------------------------------
363 *
364 * Tcl_Sleep --
365 *
366 * Delay execution for the specified number of milliseconds. This
367 * is not a very good call to make. It will block the system -
368 * you will not even be able to switch applications.
369 *
370 * Results:
371 * None.
372 *
373 * Side effects:
374 * Time passes.
375 *
376 *----------------------------------------------------------------------
377 */
378
379void
380Tcl_Sleep(
381 int ms) /* Number of milliseconds to sleep. */
382{
383 EventRecord dummy;
384 void *timerToken;
385
386 if (ms <= 0) {
387 return;
388 }
389
390 timerToken = TclMacStartTimer((long) ms);
391 while (1) {
392 WaitNextEvent(0, &dummy, (ms / 16.66) + 1, NULL);
393
394 if (TclMacTimerExpired(timerToken)) {
395 break;
396 }
397 }
398 TclMacRemoveTimer(timerToken);
399}
400
401/*
402 *----------------------------------------------------------------------
403 *
404 * Tcl_MacSetEventProc --
405 *
406 * This function sets the event handling procedure for the
407 * application. This function will be passed all incoming Mac
408 * events. This function usually controls the console or some
409 * other entity like Tk.
410 *
411 * Results:
412 * None.
413 *
414 * Side effects:
415 * Changes the event handling function.
416 *
417 *----------------------------------------------------------------------
418 */
419
420void
421Tcl_MacSetEventProc(
422 Tcl_MacConvertEventPtr procPtr)
423{
424 notifier.eventProcPtr = procPtr;
425}