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