blob: 3d2695cf01095425a856ef706292b60f163e73b6 [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/*
Jack Jansen216b8702001-04-03 14:36:35 +000086** Routine to determine whether Tk is the "main event handler" at the moment or
87** something else (MacPython, IDE) is.
88** Currently we only check that the frontmost window is Tk based, it may be better
89** to also check whether we're inside a Tk mainloop().
90*/
91static int
92TkIsTheBoss(void)
93{
94 WindowRef windowRef;
95
96 windowRef = FrontWindow();
97 if ( windowRef && !TkMacGetXWindow(windowRef) ) {
98 return 0;
99 }
100}
101/*
Jack Jansenb0195ec1998-08-18 14:51:27 +0000102 *----------------------------------------------------------------------
103 *
104 * InitNotifier --
105 *
106 * Initializes the notifier structure.
107 *
108 * Results:
109 * None.
110 *
111 * Side effects:
112 * Creates a new exit handler.
113 *
114 *----------------------------------------------------------------------
115 */
116
117static void
118InitNotifier(void)
119{
120 initialized = 1;
121 memset(&notifier, 0, sizeof(notifier));
122 Tcl_CreateExitHandler(NotifierExitHandler, NULL);
123}
124
125/*
126 *----------------------------------------------------------------------
127 *
128 * NotifierExitHandler --
129 *
130 * This function is called to cleanup the notifier state before
131 * Tcl is unloaded.
132 *
133 * Results:
134 * None.
135 *
136 * Side effects:
137 * None.
138 *
139 *----------------------------------------------------------------------
140 */
141
142static void
143NotifierExitHandler(
144 ClientData clientData) /* Not used. */
145{
146 initialized = 0;
147}
148
149/*
150 *----------------------------------------------------------------------
151 *
152 * HandleMacEvents --
153 *
154 * This function checks for events from the Macintosh event queue.
155 *
156 * Results:
157 * Returns 1 if event found, 0 otherwise.
158 *
159 * Side effects:
160 * Pulls events off of the Mac event queue and then calls
161 * convertEventProc.
162 *
163 *----------------------------------------------------------------------
164 */
165
166static int
167HandleMacEvents(void)
168{
169 EventRecord theEvent;
170 int eventFound = 0, needsUpdate = 0;
171 Point currentMouse;
172 WindowRef windowRef;
173 Rect mouseRect;
174
Jack Jansen216b8702001-04-03 14:36:35 +0000175 if ( !TkIsTheBoss() )
176 return 0;
Jack Jansenb0195ec1998-08-18 14:51:27 +0000177 /*
178 * Check for mouse moved events. These events aren't placed on the
179 * system event queue unless we call WaitNextEvent.
180 */
181
182 GetGlobalMouse(&currentMouse);
183 if ((notifier.eventProcPtr != NULL) &&
184 !EqualPt(currentMouse, notifier.lastMousePosition)) {
185 notifier.lastMousePosition = currentMouse;
186 theEvent.what = nullEvent;
187 if ((*notifier.eventProcPtr)(&theEvent) == true) {
188 eventFound = 1;
189 }
190 }
191
192 /*
193 * Check for update events. Since update events aren't generated
194 * until we call GetNextEvent, we may need to force a call to
195 * GetNextEvent, even if the queue is empty.
196 */
197
198 for (windowRef = FrontWindow(); windowRef != NULL;
199 windowRef = GetNextWindow(windowRef)) {
200 GetWindowUpdateRgn(windowRef, notifier.utilityRgn);
201 if (!EmptyRgn(notifier.utilityRgn)) {
202 needsUpdate = 1;
203 break;
204 }
205 }
206
207 /*
208 * Process events from the OS event queue.
209 */
210
211 while (needsUpdate || (GetEvQHdr()->qHead != NULL)) {
Jack Jansen216b8702001-04-03 14:36:35 +0000212 /* Give Python command-. handling a chance */
213 PyMac_DoYield(0, 0);
214
Jack Jansenb0195ec1998-08-18 14:51:27 +0000215 GetGlobalMouse(&currentMouse);
216 SetRect(&mouseRect, currentMouse.h, currentMouse.v,
217 currentMouse.h + 1, currentMouse.v + 1);
218 RectRgn(notifier.utilityRgn, &mouseRect);
Jack Jansenb0195ec1998-08-18 14:51:27 +0000219 WaitNextEvent(everyEvent, &theEvent, 5, notifier.utilityRgn);
220 needsUpdate = 0;
221 if ((notifier.eventProcPtr != NULL)
222 && ((*notifier.eventProcPtr)(&theEvent) == true)) {
223 eventFound = 1;
224 }
225 }
226
227 return eventFound;
228}
229
230/*
231 *----------------------------------------------------------------------
232 *
233 * Tcl_SetTimer --
234 *
235 * This procedure sets the current notifier timer value. The
236 * notifier will ensure that Tcl_ServiceAll() is called after
237 * the specified interval, even if no events have occurred.
238 *
239 * Results:
240 * None.
241 *
242 * Side effects:
243 * Replaces any previous timer.
244 *
245 *----------------------------------------------------------------------
246 */
247
248void
249Tcl_SetTimer(
250 Tcl_Time *timePtr) /* New value for interval timer. */
251{
252 if (!timePtr) {
253 notifier.timerActive = 0;
254 } else {
255 /*
256 * Compute when the timer should fire.
257 */
258
259 TclpGetTime(&notifier.timer);
260 notifier.timer.sec += timePtr->sec;
261 notifier.timer.usec += timePtr->usec;
262 if (notifier.timer.usec >= 1000000) {
263 notifier.timer.usec -= 1000000;
264 notifier.timer.sec += 1;
265 }
266 notifier.timerActive = 1;
267 }
268}
269
270/*
271 *----------------------------------------------------------------------
272 *
273 * Tcl_WaitForEvent --
274 *
275 * This function is called by Tcl_DoOneEvent to wait for new
276 * events on the message queue. If the block time is 0, then
277 * Tcl_WaitForEvent just polls the event queue without blocking.
278 *
279 * Results:
280 * Always returns 0.
281 *
282 * Side effects:
283 * None.
284 *
285 *----------------------------------------------------------------------
286 */
287
288int
289Tcl_WaitForEvent(
290 Tcl_Time *timePtr) /* Maximum block time. */
291{
292 int found;
293 EventRecord macEvent;
294 long sleepTime = 5;
295 long ms;
296 Point currentMouse;
297 void * timerToken;
298 Rect mouseRect;
299
300 /*
301 * Compute the next timeout value.
302 */
303
304 if (!timePtr) {
305 ms = INT_MAX;
306 } else {
307 ms = (timePtr->sec * 1000) + (timePtr->usec / 1000);
308 }
309 timerToken = TclMacStartTimer((long) ms);
310
311 /*
312 * Poll the Mac event sources. This loop repeats until something
313 * happens: a timeout, a socket event, mouse motion, or some other
314 * window event. Note that we don't call WaitNextEvent if another
315 * event is found to avoid context switches. This effectively gives
316 * events coming in via WaitNextEvent a slightly lower priority.
317 */
318
319 found = 0;
320 if (notifier.utilityRgn == NULL) {
321 notifier.utilityRgn = NewRgn();
322 }
323
324 while (!found) {
325 /*
326 * Check for generated and queued events.
327 */
328
329 if (HandleMacEvents()) {
330 found = 1;
331 }
332
333 /*
334 * Check for time out.
335 */
336
337 if (!found && TclMacTimerExpired(timerToken)) {
338 found = 1;
339 }
340
341 /*
342 * Mod by Jack: poll for select() events. Code is in TclSelectNotify.c
343 */
344 {
345 int Tcl_PollSelectEvent(void);
346 if (!found && Tcl_PollSelectEvent())
347 found = 1;
348 }
349
Jack Jansen216b8702001-04-03 14:36:35 +0000350 if ( !TkIsTheBoss() )
351 found = 1;
Jack Jansenb0195ec1998-08-18 14:51:27 +0000352 /*
353 * Check for window events. We may receive a NULL event for
354 * various reasons. 1) the timer has expired, 2) a mouse moved
355 * event is occuring or 3) the os is giving us time for idle
356 * events. Note that we aren't sharing the processor very
357 * well here. We really ought to do a better job of calling
358 * WaitNextEvent for time slicing purposes.
359 */
360
361 if (!found) {
362 /*
363 * Set up mouse region so we will wake if the mouse is moved.
364 * We do this by defining the smallest possible region around
365 * the current mouse position.
366 */
367
368 GetGlobalMouse(&currentMouse);
369 SetRect(&mouseRect, currentMouse.h, currentMouse.v,
370 currentMouse.h + 1, currentMouse.v + 1);
371 RectRgn(notifier.utilityRgn, &mouseRect);
372
373 WaitNextEvent(everyEvent, &macEvent, sleepTime,
374 notifier.utilityRgn);
375
376 if (notifier.eventProcPtr != NULL) {
377 if ((*notifier.eventProcPtr)(&macEvent) == true) {
378 found = 1;
379 }
380 }
381 }
382 }
383 TclMacRemoveTimer(timerToken);
384 return 0;
385}
386
387/*
388 *----------------------------------------------------------------------
389 *
390 * Tcl_Sleep --
391 *
392 * Delay execution for the specified number of milliseconds. This
393 * is not a very good call to make. It will block the system -
394 * you will not even be able to switch applications.
395 *
396 * Results:
397 * None.
398 *
399 * Side effects:
400 * Time passes.
401 *
402 *----------------------------------------------------------------------
403 */
404
405void
406Tcl_Sleep(
407 int ms) /* Number of milliseconds to sleep. */
408{
409 EventRecord dummy;
410 void *timerToken;
411
412 if (ms <= 0) {
413 return;
414 }
415
416 timerToken = TclMacStartTimer((long) ms);
417 while (1) {
418 WaitNextEvent(0, &dummy, (ms / 16.66) + 1, NULL);
419
420 if (TclMacTimerExpired(timerToken)) {
421 break;
422 }
423 }
424 TclMacRemoveTimer(timerToken);
425}
426
427/*
428 *----------------------------------------------------------------------
429 *
430 * Tcl_MacSetEventProc --
431 *
432 * This function sets the event handling procedure for the
433 * application. This function will be passed all incoming Mac
434 * events. This function usually controls the console or some
435 * other entity like Tk.
436 *
437 * Results:
438 * None.
439 *
440 * Side effects:
441 * Changes the event handling function.
442 *
443 *----------------------------------------------------------------------
444 */
445
446void
447Tcl_MacSetEventProc(
448 Tcl_MacConvertEventPtr procPtr)
449{
450 notifier.eventProcPtr = procPtr;
451}