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.
8 * The Mac notifier only polls for system and OS events, so it is process
9 * wide, rather than thread specific. However, this means that the convert
10 * event proc will have to arbitrate which events go to which threads.
12 * Copyright (c) 1995-1996 Sun Microsystems, Inc.
14 * See the file "license.terms" for information on usage and redistribution
15 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
23 #include "tclMacInt.h"
27 #include <Processes.h>
33 * This is necessary to work around a bug in Apple's Universal header files
34 * for the CFM68K libraries.
39 extern pascal QHdrPtr GetEventQueue(void)
40 THREEWORDINLINE(0x2EBC, 0x0000, 0x014A);
41 #pragma import list GetEventQueue
42 #define GetEvQHdr() GetEventQueue()
46 * Need this for replacing Tcl_SetTimer and Tcl_WaitForEvent defined
47 * in THIS file with ones defined in the stub table.
50 extern TclStubs tclStubs;
53 * The follwing static indicates whether this module has been initialized.
56 static int initialized = 0;
59 * The following structure contains the state information for the
64 int timerActive; /* 1 if timer is running. */
65 Tcl_Time timer; /* Time when next timer event is expected. */
66 int flags; /* OR'ed set of flags defined below. */
67 Point lastMousePosition; /* Last known mouse location. */
68 RgnHandle utilityRgn; /* Region used as the mouse region for
69 * WaitNextEvent and the update region when
70 * checking for events. */
71 Tcl_MacConvertEventPtr eventProcPtr;
72 /* This pointer holds the address of the
73 * function that will handle all incoming
74 * Macintosh events. */
78 * The following defines are used in the flags field of the notifier struct.
81 #define NOTIFY_IDLE (1<<1) /* Tcl_ServiceIdle should be called. */
82 #define NOTIFY_TIMER (1<<2) /* Tcl_ServiceTimer should be called. */
85 * Prototypes for procedures that are referenced only in this file:
88 static int HandleMacEvents _ANSI_ARGS_((void));
89 static void InitNotifier _ANSI_ARGS_((void));
90 static void NotifierExitHandler _ANSI_ARGS_((
91 ClientData clientData));
94 *----------------------------------------------------------------------
98 * Initializes the platform specific notifier state. There is no thread
99 * specific platform notifier on the Mac, so this really doesn't do
100 * anything. However, we need to return the ThreadID, since the generic
101 * notifier hands this back to us in AlertThread.
104 * Returns the threadID for this thread.
109 *----------------------------------------------------------------------
118 if (TclMacHaveThreads()) {
119 GetCurrentThread(&curThread);
120 return (ClientData) curThread;
131 *----------------------------------------------------------------------
133 * Tcl_FinalizeNotifier --
135 * This function is called to cleanup the notifier state before
136 * a thread is terminated. There is no platform thread specific
137 * notifier, so this does nothing.
145 *----------------------------------------------------------------------
149 Tcl_FinalizeNotifier(clientData)
150 ClientData clientData; /* Pointer to notifier data. */
152 /* Nothing to do on the Mac */
156 *----------------------------------------------------------------------
158 * Tcl_AlertNotifier --
160 * Wake up the specified notifier from any thread. This routine
161 * is called by the platform independent notifier code whenever
162 * the Tcl_ThreadAlert routine is called. This routine is
163 * guaranteed not to be called on a given notifier after
164 * Tcl_FinalizeNotifier is called for that notifier.
170 * Calls YieldToThread from this thread.
172 *----------------------------------------------------------------------
176 Tcl_AlertNotifier(clientData)
177 ClientData clientData; /* Pointer to thread data. */
181 if (TclMacHaveThreads()) {
182 YieldToThread((ThreadID) clientData);
189 *----------------------------------------------------------------------
193 * Initializes the notifier structure. Note - this function is never
200 * Creates a new exit handler.
202 *----------------------------------------------------------------------
209 memset(¬ifier, 0, sizeof(notifier));
210 Tcl_CreateExitHandler(NotifierExitHandler, NULL);
214 *----------------------------------------------------------------------
216 * NotifierExitHandler --
218 * This function is called to cleanup the notifier state before
219 * Tcl is unloaded. This function is never used, since InitNotifier
228 *----------------------------------------------------------------------
233 ClientData clientData) /* Not used. */
239 *----------------------------------------------------------------------
243 * This function checks for events from the Macintosh event queue.
246 * Returns 1 if event found, 0 otherwise.
249 * Pulls events off of the Mac event queue and then calls
252 *----------------------------------------------------------------------
256 HandleMacEvents(void)
258 EventRecord theEvent;
259 int eventFound = 0, needsUpdate = 0;
265 * Check for mouse moved events. These events aren't placed on the
266 * system event queue unless we call WaitNextEvent.
269 GetGlobalMouseTcl(¤tMouse);
270 if ((notifier.eventProcPtr != NULL) &&
271 !EqualPt(currentMouse, notifier.lastMousePosition)) {
272 notifier.lastMousePosition = currentMouse;
273 theEvent.what = nullEvent;
274 if ((*notifier.eventProcPtr)(&theEvent) == true) {
280 * Check for update events. Since update events aren't generated
281 * until we call GetNextEvent, we may need to force a call to
282 * GetNextEvent, even if the queue is empty.
285 for (windowRef = FrontWindow(); windowRef != NULL;
286 windowRef = GetNextWindow(windowRef)) {
287 GetWindowUpdateRgn(windowRef, notifier.utilityRgn);
288 if (!EmptyRgn(notifier.utilityRgn)) {
295 * Process events from the OS event queue.
298 while (needsUpdate || (GetEvQHdr()->qHead != NULL)) {
299 GetGlobalMouseTcl(¤tMouse);
300 SetRect(&mouseRect, currentMouse.h, currentMouse.v,
301 currentMouse.h + 1, currentMouse.v + 1);
302 RectRgn(notifier.utilityRgn, &mouseRect);
304 WaitNextEvent(everyEvent, &theEvent, 5, notifier.utilityRgn);
306 if ((notifier.eventProcPtr != NULL)
307 && ((*notifier.eventProcPtr)(&theEvent) == true)) {
316 *----------------------------------------------------------------------
320 * This procedure sets the current notifier timer value. The
321 * notifier will ensure that Tcl_ServiceAll() is called after
322 * the specified interval, even if no events have occurred.
328 * Replaces any previous timer.
330 *----------------------------------------------------------------------
335 Tcl_Time *timePtr) /* New value for interval timer. */
338 * Allow the notifier to be hooked. This may not make sense
339 * on the Mac, but mirrors the UNIX hook.
342 if (tclStubs.tcl_SetTimer != Tcl_SetTimer) {
343 tclStubs.tcl_SetTimer(timePtr);
348 notifier.timerActive = 0;
351 * Compute when the timer should fire.
354 Tcl_GetTime(¬ifier.timer);
355 notifier.timer.sec += timePtr->sec;
356 notifier.timer.usec += timePtr->usec;
357 if (notifier.timer.usec >= 1000000) {
358 notifier.timer.usec -= 1000000;
359 notifier.timer.sec += 1;
361 notifier.timerActive = 1;
366 *----------------------------------------------------------------------
368 * Tcl_ServiceModeHook --
370 * This function is invoked whenever the service mode changes.
378 *----------------------------------------------------------------------
382 Tcl_ServiceModeHook(mode)
383 int mode; /* Either TCL_SERVICE_ALL, or
384 * TCL_SERVICE_NONE. */
389 *----------------------------------------------------------------------
391 * Tcl_WaitForEvent --
393 * This function is called by Tcl_DoOneEvent to wait for new
394 * events on the message queue. If the block time is 0, then
395 * Tcl_WaitForEvent just polls the event queue without blocking.
403 *----------------------------------------------------------------------
408 Tcl_Time *timePtr) /* Maximum block time. */
411 EventRecord macEvent;
419 * Allow the notifier to be hooked. This may not make
420 * sense on the Mac, but mirrors the UNIX hook.
423 if (tclStubs.tcl_WaitForEvent != Tcl_WaitForEvent) {
424 return tclStubs.tcl_WaitForEvent(timePtr);
428 * Compute the next timeout value.
434 ms = (timePtr->sec * 1000) + (timePtr->usec / 1000);
436 timerToken = TclMacStartTimer((long) ms);
439 * Poll the Mac event sources. This loop repeats until something
440 * happens: a timeout, a socket event, mouse motion, or some other
441 * window event. Note that we don't call WaitNextEvent if another
442 * event is found to avoid context switches. This effectively gives
443 * events coming in via WaitNextEvent a slightly lower priority.
447 if (notifier.utilityRgn == NULL) {
448 notifier.utilityRgn = NewRgn();
453 * Check for generated and queued events.
456 if (HandleMacEvents()) {
461 * Check for time out.
464 if (!found && TclMacTimerExpired(timerToken)) {
469 * Check for window events. We may receive a NULL event for
470 * various reasons. 1) the timer has expired, 2) a mouse moved
471 * event is occuring or 3) the os is giving us time for idle
472 * events. Note that we aren't sharing the processor very
473 * well here. We really ought to do a better job of calling
474 * WaitNextEvent for time slicing purposes.
479 * Set up mouse region so we will wake if the mouse is moved.
480 * We do this by defining the smallest possible region around
481 * the current mouse position.
484 GetGlobalMouseTcl(¤tMouse);
485 SetRect(&mouseRect, currentMouse.h, currentMouse.v,
486 currentMouse.h + 1, currentMouse.v + 1);
487 RectRgn(notifier.utilityRgn, &mouseRect);
489 WaitNextEvent(everyEvent, &macEvent, sleepTime,
490 notifier.utilityRgn);
492 if (notifier.eventProcPtr != NULL) {
493 if ((*notifier.eventProcPtr)(&macEvent) == true) {
499 TclMacRemoveTimer(timerToken);
502 * Yield time to nay other thread at this point. If we find that the
503 * apps thrash too switching between threads, we can put a timer here,
504 * and only yield when the timer fires.
507 if (TclMacHaveThreads()) {
515 *----------------------------------------------------------------------
519 * Delay execution for the specified number of milliseconds. This
520 * is not a very good call to make. It will block the system -
521 * you will not even be able to switch applications.
529 *----------------------------------------------------------------------
534 int ms) /* Number of milliseconds to sleep. */
543 timerToken = TclMacStartTimer((long) ms);
545 WaitNextEvent(0, &dummy, (ms / 16.66) + 1, NULL);
546 if (TclMacHaveThreads()) {
549 if (TclMacTimerExpired(timerToken)) {
553 TclMacRemoveTimer(timerToken);
557 *----------------------------------------------------------------------
559 * Tcl_MacSetEventProc --
561 * This function sets the event handling procedure for the
562 * application. This function will be passed all incoming Mac
563 * events. This function usually controls the console or some
564 * other entity like Tk.
570 * Changes the event handling function.
572 *----------------------------------------------------------------------
577 Tcl_MacConvertEventPtr procPtr)
579 notifier.eventProcPtr = procPtr;