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