os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/mac/tclMacNotify.c
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 /* 
     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  *	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.
    11  *
    12  * Copyright (c) 1995-1996 Sun Microsystems, Inc.
    13  *
    14  * See the file "license.terms" for information on usage and redistribution
    15  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    16  *
    17  * RCS: @(#) $Id: tclMacNotify.c,v 1.8.4.1 2003/03/21 03:24:08 dgp Exp $
    18  */
    19 
    20 #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 #include <Threads.h>
    30 
    31 
    32 /* 
    33  * This is necessary to work around a bug in Apple's Universal header files
    34  * for the CFM68K libraries.
    35  */
    36 
    37 #ifdef __CFM68K__
    38 #undef GetEventQueue
    39 extern pascal QHdrPtr GetEventQueue(void)
    40  THREEWORDINLINE(0x2EBC, 0x0000, 0x014A);
    41 #pragma import list GetEventQueue
    42 #define GetEvQHdr() GetEventQueue()
    43 #endif
    44 
    45 /*
    46  * Need this for replacing Tcl_SetTimer and Tcl_WaitForEvent defined 
    47  * in THIS file with ones defined in the stub table.
    48  */
    49  
    50 extern TclStubs tclStubs;
    51 extern Tcl_NotifierProcs tclOriginalNotifier;
    52 
    53 /*
    54  * The follwing static indicates whether this module has been initialized.
    55  */
    56 
    57 static int initialized = 0;
    58 
    59 /*
    60  * The following structure contains the state information for the
    61  * notifier module.
    62  */
    63 
    64 static struct {
    65     int timerActive;		/* 1 if timer is running. */
    66     Tcl_Time timer;		/* Time when next timer event is expected. */
    67     int flags;			/* OR'ed set of flags defined below. */
    68     Point lastMousePosition;	/* Last known mouse location. */
    69     RgnHandle utilityRgn;	/* Region used as the mouse region for
    70 				 * WaitNextEvent and the update region when
    71 				 * checking for events. */   
    72     Tcl_MacConvertEventPtr eventProcPtr;
    73 				/* This pointer holds the address of the
    74 				 * function that will handle all incoming
    75 				 * Macintosh events. */
    76 } notifier;
    77 
    78 /*
    79  * The following defines are used in the flags field of the notifier struct.
    80  */
    81 
    82 #define NOTIFY_IDLE	(1<<1)	/* Tcl_ServiceIdle should be called. */
    83 #define NOTIFY_TIMER	(1<<2)	/* Tcl_ServiceTimer should be called. */
    84 
    85 /*
    86  * Prototypes for procedures that are referenced only in this file:
    87  */
    88 
    89 static int		HandleMacEvents _ANSI_ARGS_((void));
    90 static void		InitNotifier _ANSI_ARGS_((void));
    91 static void		NotifierExitHandler _ANSI_ARGS_((
    92 			    ClientData clientData));
    93 
    94 /*
    95  *----------------------------------------------------------------------
    96  *
    97  * Tcl_InitNotifier --
    98  *
    99  *	Initializes the platform specific notifier state.  There is no thread
   100  *	specific platform notifier on the Mac, so this really doesn't do 
   101  *	anything.  However, we need to return the ThreadID, since the generic
   102  *	notifier hands this back to us in AlertThread.
   103  *
   104  * Results:
   105  *	Returns the threadID for this thread.  
   106  *
   107  * Side effects:
   108  *	None.
   109  *
   110  *----------------------------------------------------------------------
   111  */
   112 
   113 ClientData
   114 Tcl_InitNotifier()
   115 {
   116     
   117 #ifdef TCL_THREADS
   118     ThreadID curThread;
   119     if (TclMacHaveThreads()) {
   120         GetCurrentThread(&curThread);
   121         return (ClientData) curThread;
   122     } else {
   123         return NULL;
   124     }
   125 #else
   126     return NULL;
   127 #endif
   128 
   129 }
   130 
   131 /*
   132  *----------------------------------------------------------------------
   133  *
   134  * Tcl_FinalizeNotifier --
   135  *
   136  *	This function is called to cleanup the notifier state before
   137  *	a thread is terminated.  There is no platform thread specific
   138  *	notifier, so this does nothing.
   139  *
   140  * Results:
   141  *	None.
   142  *
   143  * Side effects:
   144  *	None.
   145  *
   146  *----------------------------------------------------------------------
   147  */
   148 
   149 void
   150 Tcl_FinalizeNotifier(clientData)
   151     ClientData clientData;	/* Pointer to notifier data. */
   152 {
   153     /* Nothing to do on the Mac */
   154 }
   155 
   156 /*
   157  *----------------------------------------------------------------------
   158  *
   159  * Tcl_AlertNotifier --
   160  *
   161  *	Wake up the specified notifier from any thread. This routine
   162  *	is called by the platform independent notifier code whenever
   163  *	the Tcl_ThreadAlert routine is called.  This routine is
   164  *	guaranteed not to be called on a given notifier after
   165  *	Tcl_FinalizeNotifier is called for that notifier.
   166  *
   167  * Results:
   168  *	None.
   169  *
   170  * Side effects:
   171  *	Calls YieldToThread from this thread.
   172  *
   173  *----------------------------------------------------------------------
   174  */
   175 
   176 void
   177 Tcl_AlertNotifier(clientData)
   178     ClientData clientData;	/* Pointer to thread data. */
   179 {
   180 
   181 #ifdef TCL_THREADS
   182     if (TclMacHaveThreads()) {
   183         YieldToThread((ThreadID) clientData);
   184     }
   185 #endif
   186 
   187 }
   188 
   189 /*
   190  *----------------------------------------------------------------------
   191  *
   192  * InitNotifier --
   193  *
   194  *	Initializes the notifier structure.  Note - this function is never
   195  *	used.
   196  *
   197  * Results:
   198  *	None.
   199  *
   200  * Side effects:
   201  *	Creates a new exit handler.
   202  *
   203  *----------------------------------------------------------------------
   204  */
   205 
   206 static void
   207 InitNotifier(void)
   208 {
   209     initialized = 1;
   210     memset(&notifier, 0, sizeof(notifier));
   211     Tcl_CreateExitHandler(NotifierExitHandler, NULL);
   212 }
   213 
   214 /*
   215  *----------------------------------------------------------------------
   216  *
   217  * NotifierExitHandler --
   218  *
   219  *	This function is called to cleanup the notifier state before
   220  *	Tcl is unloaded.  This function is never used, since InitNotifier
   221  *	isn't either.
   222  *
   223  * Results:
   224  *	None.
   225  *
   226  * Side effects:
   227  *	None.
   228  *
   229  *----------------------------------------------------------------------
   230  */
   231 
   232 static void
   233 NotifierExitHandler(
   234     ClientData clientData)	/* Not used. */
   235 {
   236     initialized = 0;
   237 }
   238 
   239 /*
   240  *----------------------------------------------------------------------
   241  *
   242  * HandleMacEvents --
   243  *
   244  *	This function checks for events from the Macintosh event queue.
   245  *
   246  * Results:
   247  *	Returns 1 if event found, 0 otherwise.
   248  *
   249  * Side effects:
   250  *	Pulls events off of the Mac event queue and then calls
   251  *	convertEventProc.
   252  *
   253  *----------------------------------------------------------------------
   254  */
   255 
   256 static int
   257 HandleMacEvents(void)
   258 {
   259     EventRecord theEvent;
   260     int eventFound = 0, needsUpdate = 0;
   261     Point currentMouse;
   262     WindowRef windowRef;
   263     Rect mouseRect;
   264 
   265     /*
   266      * Check for mouse moved events.  These events aren't placed on the
   267      * system event queue unless we call WaitNextEvent.
   268      */
   269 
   270     GetGlobalMouseTcl(&currentMouse);
   271     if ((notifier.eventProcPtr != NULL) &&
   272 	    !EqualPt(currentMouse, notifier.lastMousePosition)) {
   273 	notifier.lastMousePosition = currentMouse;
   274 	theEvent.what = nullEvent;
   275 	if ((*notifier.eventProcPtr)(&theEvent) == true) {
   276 	    eventFound = 1;
   277 	}
   278     }
   279 
   280     /*
   281      * Check for update events.  Since update events aren't generated
   282      * until we call GetNextEvent, we may need to force a call to
   283      * GetNextEvent, even if the queue is empty.
   284      */
   285 
   286     for (windowRef = FrontWindow(); windowRef != NULL;
   287 	    windowRef = GetNextWindow(windowRef)) {
   288 	GetWindowUpdateRgn(windowRef, notifier.utilityRgn);
   289 	if (!EmptyRgn(notifier.utilityRgn)) {
   290 	    needsUpdate = 1;
   291 	    break;
   292 	}
   293     }
   294     
   295     /*
   296      * Process events from the OS event queue.
   297      */
   298 
   299     while (needsUpdate || (GetEvQHdr()->qHead != NULL)) {
   300 	GetGlobalMouseTcl(&currentMouse);
   301 	SetRect(&mouseRect, currentMouse.h, currentMouse.v,
   302 		currentMouse.h + 1, currentMouse.v + 1);
   303 	RectRgn(notifier.utilityRgn, &mouseRect);
   304 	
   305 	WaitNextEvent(everyEvent, &theEvent, 5, notifier.utilityRgn);
   306 	needsUpdate = 0;
   307 	if ((notifier.eventProcPtr != NULL)
   308 		&& ((*notifier.eventProcPtr)(&theEvent) == true)) {
   309 	    eventFound = 1;
   310 	}
   311     }
   312     
   313     return eventFound;
   314 }
   315 
   316 /*
   317  *----------------------------------------------------------------------
   318  *
   319  * Tcl_SetTimer --
   320  *
   321  *	This procedure sets the current notifier timer value.  The
   322  *	notifier will ensure that Tcl_ServiceAll() is called after
   323  *	the specified interval, even if no events have occurred.
   324  *
   325  * Results:
   326  *	None.
   327  *
   328  * Side effects:
   329  *	Replaces any previous timer.
   330  *
   331  *----------------------------------------------------------------------
   332  */
   333 
   334 void
   335 Tcl_SetTimer(
   336     Tcl_Time *timePtr)		/* New value for interval timer. */
   337 {
   338     /*
   339      * Allow the notifier to be hooked.  This may not make sense
   340      * on the Mac, but mirrors the UNIX hook.
   341      */
   342 
   343     if (tclStubs.tcl_SetTimer != tclOriginalNotifier.setTimerProc) {
   344 	tclStubs.tcl_SetTimer(timePtr);
   345 	return;
   346     }
   347 
   348     if (!timePtr) {
   349 	notifier.timerActive = 0;
   350     } else {
   351 	/*
   352 	 * Compute when the timer should fire.
   353 	 */
   354 	
   355 	Tcl_GetTime(&notifier.timer);
   356 	notifier.timer.sec += timePtr->sec;
   357 	notifier.timer.usec += timePtr->usec;
   358 	if (notifier.timer.usec >= 1000000) {
   359 	    notifier.timer.usec -= 1000000;
   360 	    notifier.timer.sec += 1;
   361 	}
   362 	notifier.timerActive = 1;
   363     }
   364 }
   365 
   366 /*
   367  *----------------------------------------------------------------------
   368  *
   369  * Tcl_ServiceModeHook --
   370  *
   371  *	This function is invoked whenever the service mode changes.
   372  *
   373  * Results:
   374  *	None.
   375  *
   376  * Side effects:
   377  *	None.
   378  *
   379  *----------------------------------------------------------------------
   380  */
   381 
   382 void
   383 Tcl_ServiceModeHook(mode)
   384     int mode;			/* Either TCL_SERVICE_ALL, or
   385 				 * TCL_SERVICE_NONE. */
   386 {
   387 }
   388 
   389 /*
   390  *----------------------------------------------------------------------
   391  *
   392  * Tcl_WaitForEvent --
   393  *
   394  *	This function is called by Tcl_DoOneEvent to wait for new
   395  *	events on the message queue.  If the block time is 0, then
   396  *	Tcl_WaitForEvent just polls the event queue without blocking.
   397  *
   398  * Results:
   399  *	Always returns 0.
   400  *
   401  * Side effects:
   402  *	None.
   403  *
   404  *----------------------------------------------------------------------
   405  */
   406 
   407 int
   408 Tcl_WaitForEvent(
   409     Tcl_Time *timePtr)		/* Maximum block time. */
   410 {
   411     int found;
   412     EventRecord macEvent;
   413     long sleepTime = 5;
   414     long ms;
   415     Point currentMouse;
   416     void * timerToken;
   417     Rect mouseRect;
   418 
   419     /*
   420      * Allow the notifier to be hooked.  This may not make
   421      * sense on the Mac, but mirrors the UNIX hook.
   422      */
   423 
   424     if (tclStubs.tcl_WaitForEvent != tclOriginalNotifier.waitForEventProc) {
   425 	return tclStubs.tcl_WaitForEvent(timePtr);
   426     }
   427 
   428     /*
   429      * Compute the next timeout value.
   430      */
   431 
   432     if (!timePtr) {
   433 	ms = INT_MAX;
   434     } else {
   435 	ms = (timePtr->sec * 1000) + (timePtr->usec / 1000);
   436     }
   437     timerToken = TclMacStartTimer((long) ms);
   438    
   439     /*
   440      * Poll the Mac event sources.  This loop repeats until something
   441      * happens: a timeout, a socket event, mouse motion, or some other
   442      * window event.  Note that we don't call WaitNextEvent if another
   443      * event is found to avoid context switches.  This effectively gives
   444      * events coming in via WaitNextEvent a slightly lower priority.
   445      */
   446 
   447     found = 0;
   448     if (notifier.utilityRgn == NULL) {
   449 	notifier.utilityRgn = NewRgn();
   450     }
   451 
   452     while (!found) {
   453 	/*
   454 	 * Check for generated and queued events.
   455 	 */
   456 
   457 	if (HandleMacEvents()) {
   458 	    found = 1;
   459 	}
   460 
   461 	/*
   462 	 * Check for time out.
   463 	 */
   464 
   465 	if (!found && TclMacTimerExpired(timerToken)) {
   466 	    found = 1;
   467 	}
   468 
   469 	/*
   470 	 * Check for window events.  We may receive a NULL event for
   471 	 * various reasons. 1) the timer has expired, 2) a mouse moved
   472 	 * event is occuring or 3) the os is giving us time for idle
   473 	 * events.  Note that we aren't sharing the processor very
   474 	 * well here.  We really ought to do a better job of calling
   475 	 * WaitNextEvent for time slicing purposes.
   476 	 */
   477 
   478 	if (!found) {
   479 	    /*
   480 	     * Set up mouse region so we will wake if the mouse is moved.
   481 	     * We do this by defining the smallest possible region around
   482 	     * the current mouse position.
   483 	     */
   484 
   485 	    GetGlobalMouseTcl(&currentMouse);
   486 	    SetRect(&mouseRect, currentMouse.h, currentMouse.v,
   487 		    currentMouse.h + 1, currentMouse.v + 1);
   488 	    RectRgn(notifier.utilityRgn, &mouseRect);
   489 	
   490 	    WaitNextEvent(everyEvent, &macEvent, sleepTime,
   491 		    notifier.utilityRgn);
   492 
   493 	    if (notifier.eventProcPtr != NULL) {
   494 		if ((*notifier.eventProcPtr)(&macEvent) == true) {
   495 		    found = 1;
   496 		}
   497 	    }
   498 	}
   499     }
   500     TclMacRemoveTimer(timerToken);
   501     
   502     /*
   503      * Yield time to nay other thread at this point.  If we find that the
   504      * apps thrash too switching between threads, we can put a timer here,
   505      * and only yield when the timer fires.
   506      */
   507      
   508     if (TclMacHaveThreads()) {
   509         YieldToAnyThread();
   510     }
   511     
   512     return 0;
   513 }
   514 
   515 /*
   516  *----------------------------------------------------------------------
   517  *
   518  * Tcl_Sleep --
   519  *
   520  *	Delay execution for the specified number of milliseconds.  This
   521  *	is not a very good call to make.  It will block the system -
   522  *	you will not even be able to switch applications.
   523  *
   524  * Results:
   525  *	None.
   526  *
   527  * Side effects:
   528  *	Time passes.
   529  *
   530  *----------------------------------------------------------------------
   531  */
   532 
   533 void
   534 Tcl_Sleep(
   535     int ms)			/* Number of milliseconds to sleep. */
   536 {
   537     EventRecord dummy;
   538     void *timerToken;
   539     
   540     if (ms <= 0) {
   541 	return;
   542     }
   543     
   544     timerToken = TclMacStartTimer((long) ms);
   545     while (1) {
   546 	WaitNextEvent(0, &dummy, (ms / 16.66) + 1, NULL);
   547         if (TclMacHaveThreads()) {
   548 	    YieldToAnyThread();
   549 	}
   550 	if (TclMacTimerExpired(timerToken)) {
   551 	    break;
   552 	}
   553     }
   554     TclMacRemoveTimer(timerToken);
   555 }
   556 
   557 /*
   558  *----------------------------------------------------------------------
   559  *
   560  * Tcl_MacSetEventProc --
   561  *
   562  *	This function sets the event handling procedure for the 
   563  *	application.  This function will be passed all incoming Mac
   564  *	events.  This function usually controls the console or some
   565  *	other entity like Tk.
   566  *
   567  * Results:
   568  *	None.
   569  *
   570  * Side effects:
   571  *	Changes the event handling function.
   572  *
   573  *----------------------------------------------------------------------
   574  */
   575 
   576 void
   577 Tcl_MacSetEventProc(
   578     Tcl_MacConvertEventPtr procPtr)
   579 {
   580     notifier.eventProcPtr = procPtr;
   581 }