os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/unix/tclXtNotify.c
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 /* 
     2  * tclXtNotify.c --
     3  *
     4  *	This file contains the notifier driver implementation for the
     5  *	Xt intrinsics.
     6  *
     7  * Copyright (c) 1997 by Sun Microsystems, Inc.
     8  *
     9  * See the file "license.terms" for information on usage and redistribution
    10  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    11  *
    12  * RCS: @(#) $Id: tclXtNotify.c,v 1.4 1999/07/02 06:05:34 welch Exp $
    13  */
    14 
    15 #include <X11/Intrinsic.h>
    16 #include <tclInt.h>
    17 
    18 /*
    19  * This structure is used to keep track of the notifier info for a 
    20  * a registered file.
    21  */
    22 
    23 typedef struct FileHandler {
    24     int fd;
    25     int mask;			/* Mask of desired events: TCL_READABLE, etc. */
    26     int readyMask;		/* Events that have been seen since the
    27 				   last time FileHandlerEventProc was called
    28 				   for this file. */
    29     XtInputId read;		/* Xt read callback handle. */
    30     XtInputId write;		/* Xt write callback handle. */
    31     XtInputId except;		/* Xt exception callback handle. */
    32     Tcl_FileProc *proc;		/* Procedure to call, in the style of
    33 				 * Tcl_CreateFileHandler. */
    34     ClientData clientData;	/* Argument to pass to proc. */
    35     struct FileHandler *nextPtr;/* Next in list of all files we care about. */
    36 } FileHandler;
    37 
    38 /*
    39  * The following structure is what is added to the Tcl event queue when
    40  * file handlers are ready to fire.
    41  */
    42 
    43 typedef struct FileHandlerEvent {
    44     Tcl_Event header;		/* Information that is standard for
    45 				 * all events. */
    46     int fd;			/* File descriptor that is ready.  Used
    47 				 * to find the FileHandler structure for
    48 				 * the file (can't point directly to the
    49 				 * FileHandler structure because it could
    50 				 * go away while the event is queued). */
    51 } FileHandlerEvent;
    52 
    53 /*
    54  * The following static structure contains the state information for the
    55  * Xt based implementation of the Tcl notifier.
    56  */
    57 
    58 static struct NotifierState {
    59     XtAppContext appContext;		/* The context used by the Xt
    60                                          * notifier. Can be set with
    61                                          * TclSetAppContext. */
    62     int appContextCreated;		/* Was it created by us? */
    63     XtIntervalId currentTimeout;	/* Handle of current timer. */
    64     FileHandler *firstFileHandlerPtr;	/* Pointer to head of file handler
    65 					 * list. */
    66 } notifier;
    67 
    68 /*
    69  * The following static indicates whether this module has been initialized.
    70  */
    71 
    72 static int initialized = 0;
    73 
    74 /*
    75  * Static routines defined in this file.
    76  */
    77 
    78 static int		FileHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
    79 			    int flags));
    80 static void		FileProc _ANSI_ARGS_((caddr_t clientData,
    81 			    int *source, XtInputId *id));
    82 void			InitNotifier _ANSI_ARGS_((void));
    83 static void		NotifierExitHandler _ANSI_ARGS_((
    84 			    ClientData clientData));
    85 static void		TimerProc _ANSI_ARGS_((caddr_t clientData,
    86 			    XtIntervalId *id));
    87 static void		CreateFileHandler _ANSI_ARGS_((int fd, int mask, 
    88 				Tcl_FileProc * proc, ClientData clientData));
    89 static void		DeleteFileHandler _ANSI_ARGS_((int fd));
    90 static void		SetTimer _ANSI_ARGS_((Tcl_Time * timePtr));
    91 static int		WaitForEvent _ANSI_ARGS_((Tcl_Time * timePtr));
    92 
    93 /*
    94  * Functions defined in this file for use by users of the Xt Notifier:
    95  */
    96 
    97 EXTERN XtAppContext	TclSetAppContext _ANSI_ARGS_((XtAppContext ctx));
    98 
    99 /*
   100  *----------------------------------------------------------------------
   101  *
   102  * TclSetAppContext --
   103  *
   104  *	Set the notifier application context.
   105  *
   106  * Results:
   107  *	None.
   108  *
   109  * Side effects:
   110  *	Sets the application context used by the notifier. Panics if
   111  *	the context is already set when called.
   112  *
   113  *----------------------------------------------------------------------
   114  */
   115 
   116 XtAppContext
   117 TclSetAppContext(appContext)
   118     XtAppContext	appContext;
   119 {
   120     if (!initialized) {
   121         InitNotifier();
   122     }
   123 
   124     /*
   125      * If we already have a context we check whether we were asked to set a
   126      * new context. If so, we panic because we try to prevent switching
   127      * contexts by mistake. Otherwise, we return the one we have.
   128      */
   129     
   130     if (notifier.appContext != NULL) {
   131         if (appContext != NULL) {
   132 
   133 	    /*
   134              * We already have a context. We do not allow switching contexts
   135              * after initialization, so we panic.
   136              */
   137         
   138             panic("TclSetAppContext:  multiple application contexts");
   139 
   140         }
   141     } else {
   142 
   143         /*
   144          * If we get here we have not yet gotten a context, so either create
   145          * one or use the one supplied by our caller.
   146          */
   147 
   148         if (appContext == NULL) {
   149 
   150 	    /*
   151              * We must create a new context and tell our caller what it is, so
   152              * she can use it too.
   153              */
   154     
   155             notifier.appContext = XtCreateApplicationContext();
   156             notifier.appContextCreated = 1;
   157         } else {
   158 
   159 	    /*
   160              * Otherwise we remember the context that our caller gave us
   161              * and use it.
   162              */
   163     
   164             notifier.appContextCreated = 0;
   165             notifier.appContext = appContext;
   166         }
   167     }
   168     
   169     return notifier.appContext;
   170 }
   171 
   172 /*
   173  *----------------------------------------------------------------------
   174  *
   175  * InitNotifier --
   176  *
   177  *	Initializes the notifier state.
   178  *
   179  * Results:
   180  *	None.
   181  *
   182  * Side effects:
   183  *	Creates a new exit handler.
   184  *
   185  *----------------------------------------------------------------------
   186  */
   187 
   188 void
   189 InitNotifier()
   190 {
   191     Tcl_NotifierProcs notifier;
   192     /*
   193      * Only reinitialize if we are not in exit handling. The notifier
   194      * can get reinitialized after its own exit handler has run, because
   195      * of exit handlers for the I/O and timer sub-systems (order dependency).
   196      */
   197 
   198     if (TclInExit()) {
   199         return;
   200     }
   201 
   202     notifier.createFileHandlerProc = CreateFileHandler;
   203     notifier.deleteFileHandlerProc = DeleteFileHandler;
   204     notifier.setTimerProc = SetTimer;
   205     notifier.waitForEventProc = WaitForEvent;
   206     Tcl_SetNotifier(&notifier);
   207 
   208     /*
   209      * DO NOT create the application context yet; doing so would prevent
   210      * external applications from setting it for us to their own ones.
   211      */
   212     
   213     initialized = 1;
   214     memset(&notifier, 0, sizeof(notifier));
   215     Tcl_CreateExitHandler(NotifierExitHandler, NULL);
   216 }
   217 
   218 /*
   219  *----------------------------------------------------------------------
   220  *
   221  * NotifierExitHandler --
   222  *
   223  *	This function is called to cleanup the notifier state before
   224  *	Tcl is unloaded.
   225  *
   226  * Results:
   227  *	None.
   228  *
   229  * Side effects:
   230  *	Destroys the notifier window.
   231  *
   232  *----------------------------------------------------------------------
   233  */
   234 
   235 static void
   236 NotifierExitHandler(
   237     ClientData clientData)	/* Not used. */
   238 {
   239     if (notifier.currentTimeout != 0) {
   240         XtRemoveTimeOut(notifier.currentTimeout);
   241     }
   242     for (; notifier.firstFileHandlerPtr != NULL; ) {
   243         Tcl_DeleteFileHandler(notifier.firstFileHandlerPtr->fd);
   244     }
   245     if (notifier.appContextCreated) {
   246         XtDestroyApplicationContext(notifier.appContext);
   247         notifier.appContextCreated = 0;
   248         notifier.appContext = NULL;
   249     }
   250     initialized = 0;
   251 }
   252 
   253 /*
   254  *----------------------------------------------------------------------
   255  *
   256  * SetTimer --
   257  *
   258  *	This procedure sets the current notifier timeout value.
   259  *
   260  * Results:
   261  *	None.
   262  *
   263  * Side effects:
   264  *	Replaces any previous timer.
   265  *
   266  *----------------------------------------------------------------------
   267  */
   268 
   269 static void
   270 SetTimer(timePtr)
   271     Tcl_Time *timePtr;		/* Timeout value, may be NULL. */
   272 {
   273     long timeout;
   274 
   275     if (!initialized) {
   276 	InitNotifier();
   277     }
   278 
   279     TclSetAppContext(NULL);
   280     if (notifier.currentTimeout != 0) {
   281 	XtRemoveTimeOut(notifier.currentTimeout);
   282     }
   283     if (timePtr) {
   284 	timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
   285 	notifier.currentTimeout =
   286             XtAppAddTimeOut(notifier.appContext, (unsigned long) timeout,
   287                     TimerProc, NULL);
   288     } else {
   289 	notifier.currentTimeout = 0;
   290     }
   291 }
   292 
   293 /*
   294  *----------------------------------------------------------------------
   295  *
   296  * TimerProc --
   297  *
   298  *	This procedure is the XtTimerCallbackProc used to handle
   299  *	timeouts.
   300  *
   301  * Results:
   302  *	None.
   303  *
   304  * Side effects:
   305  *      Processes all queued events.
   306  *
   307  *----------------------------------------------------------------------
   308  */
   309 
   310 static void
   311 TimerProc(data, id)
   312     caddr_t data;		/* Not used. */
   313     XtIntervalId *id;
   314 {
   315     if (*id != notifier.currentTimeout) {
   316 	return;
   317     }
   318     notifier.currentTimeout = 0;
   319 
   320     Tcl_ServiceAll();
   321 }
   322 
   323 /*
   324  *----------------------------------------------------------------------
   325  *
   326  * CreateFileHandler --
   327  *
   328  *	This procedure registers a file handler with the Xt notifier.
   329  *
   330  * Results:
   331  *	None.
   332  *
   333  * Side effects:
   334  *	Creates a new file handler structure and registers one or more
   335  *	input procedures with Xt.
   336  *
   337  *----------------------------------------------------------------------
   338  */
   339 
   340 static void
   341 CreateFileHandler(fd, mask, proc, clientData)
   342     int fd;			/* Handle of stream to watch. */
   343     int mask;			/* OR'ed combination of TCL_READABLE,
   344 				 * TCL_WRITABLE, and TCL_EXCEPTION:
   345 				 * indicates conditions under which
   346 				 * proc should be called. */
   347     Tcl_FileProc *proc;		/* Procedure to call for each
   348 				 * selected event. */
   349     ClientData clientData;	/* Arbitrary data to pass to proc. */
   350 {
   351     FileHandler *filePtr;
   352 
   353     if (!initialized) {
   354 	InitNotifier();
   355     }
   356 
   357     TclSetAppContext(NULL);
   358 
   359     for (filePtr = notifier.firstFileHandlerPtr; filePtr != NULL;
   360 	    filePtr = filePtr->nextPtr) {
   361 	if (filePtr->fd == fd) {
   362 	    break;
   363 	}
   364     }
   365     if (filePtr == NULL) {
   366 	filePtr = (FileHandler*) ckalloc(sizeof(FileHandler));
   367 	filePtr->fd = fd;
   368 	filePtr->read = 0;
   369 	filePtr->write = 0;
   370 	filePtr->except = 0;
   371 	filePtr->readyMask = 0;
   372 	filePtr->mask = 0;
   373 	filePtr->nextPtr = notifier.firstFileHandlerPtr;
   374 	notifier.firstFileHandlerPtr = filePtr;
   375     }
   376     filePtr->proc = proc;
   377     filePtr->clientData = clientData;
   378 
   379     /*
   380      * Register the file with the Xt notifier, if it hasn't been done yet.
   381      */
   382 
   383     if (mask & TCL_READABLE) {
   384 	if (!(filePtr->mask & TCL_READABLE)) {
   385 	    filePtr->read =
   386                 XtAppAddInput(notifier.appContext, fd, XtInputReadMask,
   387                         FileProc, filePtr);
   388 	}
   389     } else {
   390 	if (filePtr->mask & TCL_READABLE) {
   391 	    XtRemoveInput(filePtr->read);
   392 	}
   393     }
   394     if (mask & TCL_WRITABLE) {
   395 	if (!(filePtr->mask & TCL_WRITABLE)) {
   396 	    filePtr->write =
   397                 XtAppAddInput(notifier.appContext, fd, XtInputWriteMask,
   398                         FileProc, filePtr);
   399 	}
   400     } else {
   401 	if (filePtr->mask & TCL_WRITABLE) {
   402 	    XtRemoveInput(filePtr->write);
   403 	}
   404     }
   405     if (mask & TCL_EXCEPTION) {
   406 	if (!(filePtr->mask & TCL_EXCEPTION)) {
   407 	    filePtr->except =
   408                 XtAppAddInput(notifier.appContext, fd, XtInputExceptMask,
   409                         FileProc, filePtr);
   410 	}
   411     } else {
   412 	if (filePtr->mask & TCL_EXCEPTION) {
   413 	    XtRemoveInput(filePtr->except);
   414 	}
   415     }
   416     filePtr->mask = mask;
   417 }
   418 
   419 /*
   420  *----------------------------------------------------------------------
   421  *
   422  * DeleteFileHandler --
   423  *
   424  *	Cancel a previously-arranged callback arrangement for
   425  *	a file.
   426  *
   427  * Results:
   428  *	None.
   429  *
   430  * Side effects:
   431  *	If a callback was previously registered on file, remove it.
   432  *
   433  *----------------------------------------------------------------------
   434  */
   435 
   436 static void
   437 DeleteFileHandler(fd)
   438     int fd;			/* Stream id for which to remove
   439 				 * callback procedure. */
   440 {
   441     FileHandler *filePtr, *prevPtr;
   442 
   443     if (!initialized) {
   444 	InitNotifier();
   445     }
   446 
   447     TclSetAppContext(NULL);
   448 
   449     /*
   450      * Find the entry for the given file (and return if there
   451      * isn't one).
   452      */
   453 
   454     for (prevPtr = NULL, filePtr = notifier.firstFileHandlerPtr; ;
   455 	    prevPtr = filePtr, filePtr = filePtr->nextPtr) {
   456 	if (filePtr == NULL) {
   457 	    return;
   458 	}
   459 	if (filePtr->fd == fd) {
   460 	    break;
   461 	}
   462     }
   463 
   464     /*
   465      * Clean up information in the callback record.
   466      */
   467 
   468     if (prevPtr == NULL) {
   469 	notifier.firstFileHandlerPtr = filePtr->nextPtr;
   470     } else {
   471 	prevPtr->nextPtr = filePtr->nextPtr;
   472     }
   473     if (filePtr->mask & TCL_READABLE) {
   474 	XtRemoveInput(filePtr->read);
   475     }
   476     if (filePtr->mask & TCL_WRITABLE) {
   477 	XtRemoveInput(filePtr->write);
   478     }
   479     if (filePtr->mask & TCL_EXCEPTION) {
   480 	XtRemoveInput(filePtr->except);
   481     }
   482     ckfree((char *) filePtr);
   483 }
   484 
   485 /*
   486  *----------------------------------------------------------------------
   487  *
   488  * FileProc --
   489  *
   490  *	These procedures are called by Xt when a file becomes readable,
   491  *	writable, or has an exception.
   492  *
   493  * Results:
   494  *	None.
   495  *
   496  * Side effects:
   497  *	Makes an entry on the Tcl event queue if the event is
   498  *	interesting.
   499  *
   500  *----------------------------------------------------------------------
   501  */
   502 
   503 static void
   504 FileProc(clientData, fd, id)
   505     caddr_t clientData;
   506     int *fd;
   507     XtInputId *id;
   508 {
   509     FileHandler *filePtr = (FileHandler *)clientData;
   510     FileHandlerEvent *fileEvPtr;
   511     int mask = 0;
   512 
   513     /*
   514      * Determine which event happened.
   515      */
   516 
   517     if (*id == filePtr->read) {
   518 	mask = TCL_READABLE;
   519     } else if (*id == filePtr->write) {
   520 	mask = TCL_WRITABLE;
   521     } else if (*id == filePtr->except) {
   522 	mask = TCL_EXCEPTION;
   523     }
   524 
   525     /*
   526      * Ignore unwanted or duplicate events.
   527      */
   528 
   529     if (!(filePtr->mask & mask) || (filePtr->readyMask & mask)) {
   530 	return;
   531     }
   532     
   533     /*
   534      * This is an interesting event, so put it onto the event queue.
   535      */
   536 
   537     filePtr->readyMask |= mask;
   538     fileEvPtr = (FileHandlerEvent *) ckalloc(sizeof(FileHandlerEvent));
   539     fileEvPtr->header.proc = FileHandlerEventProc;
   540     fileEvPtr->fd = filePtr->fd;
   541     Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
   542 
   543     /*
   544      * Process events on the Tcl event queue before returning to Xt.
   545      */
   546 
   547     Tcl_ServiceAll();
   548 }
   549 
   550 /*
   551  *----------------------------------------------------------------------
   552  *
   553  * FileHandlerEventProc --
   554  *
   555  *	This procedure is called by Tcl_ServiceEvent when a file event
   556  *	reaches the front of the event queue.  This procedure is
   557  *	responsible for actually handling the event by invoking the
   558  *	callback for the file handler.
   559  *
   560  * Results:
   561  *	Returns 1 if the event was handled, meaning it should be removed
   562  *	from the queue.  Returns 0 if the event was not handled, meaning
   563  *	it should stay on the queue.  The only time the event isn't
   564  *	handled is if the TCL_FILE_EVENTS flag bit isn't set.
   565  *
   566  * Side effects:
   567  *	Whatever the file handler's callback procedure does.
   568  *
   569  *----------------------------------------------------------------------
   570  */
   571 
   572 static int
   573 FileHandlerEventProc(evPtr, flags)
   574     Tcl_Event *evPtr;		/* Event to service. */
   575     int flags;			/* Flags that indicate what events to
   576 				 * handle, such as TCL_FILE_EVENTS. */
   577 {
   578     FileHandler *filePtr;
   579     FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr;
   580     int mask;
   581 
   582     if (!(flags & TCL_FILE_EVENTS)) {
   583 	return 0;
   584     }
   585 
   586     /*
   587      * Search through the file handlers to find the one whose handle matches
   588      * the event.  We do this rather than keeping a pointer to the file
   589      * handler directly in the event, so that the handler can be deleted
   590      * while the event is queued without leaving a dangling pointer.
   591      */
   592 
   593     for (filePtr = notifier.firstFileHandlerPtr; filePtr != NULL;
   594 	    filePtr = filePtr->nextPtr) {
   595 	if (filePtr->fd != fileEvPtr->fd) {
   596 	    continue;
   597 	}
   598 
   599 	/*
   600 	 * The code is tricky for two reasons:
   601 	 * 1. The file handler's desired events could have changed
   602 	 *    since the time when the event was queued, so AND the
   603 	 *    ready mask with the desired mask.
   604 	 * 2. The file could have been closed and re-opened since
   605 	 *    the time when the event was queued.  This is why the
   606 	 *    ready mask is stored in the file handler rather than
   607 	 *    the queued event:  it will be zeroed when a new
   608 	 *    file handler is created for the newly opened file.
   609 	 */
   610 
   611 	mask = filePtr->readyMask & filePtr->mask;
   612 	filePtr->readyMask = 0;
   613 	if (mask != 0) {
   614 	    (*filePtr->proc)(filePtr->clientData, mask);
   615 	}
   616 	break;
   617     }
   618     return 1;
   619 }
   620 
   621 /*
   622  *----------------------------------------------------------------------
   623  *
   624  * WaitForEvent --
   625  *
   626  *	This function is called by Tcl_DoOneEvent to wait for new
   627  *	events on the message queue.  If the block time is 0, then
   628  *	Tcl_WaitForEvent just polls without blocking.
   629  *
   630  * Results:
   631  *	Returns 1 if an event was found, else 0.  This ensures that
   632  *	Tcl_DoOneEvent will return 1, even if the event is handled
   633  *	by non-Tcl code.
   634  *
   635  * Side effects:
   636  *	Queues file events that are detected by the select.
   637  *
   638  *----------------------------------------------------------------------
   639  */
   640 
   641 static int
   642 WaitForEvent(
   643     Tcl_Time *timePtr)		/* Maximum block time, or NULL. */
   644 {
   645     int timeout;
   646 
   647     if (!initialized) {
   648 	InitNotifier();
   649     }
   650 
   651     TclSetAppContext(NULL);
   652 
   653     if (timePtr) {
   654         timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
   655         if (timeout == 0) {
   656             if (XtAppPending(notifier.appContext)) {
   657                 goto process;
   658             } else {
   659                 return 0;
   660             }
   661         } else {
   662             Tcl_SetTimer(timePtr);
   663         }
   664     }
   665 process:
   666     XtAppProcessEvent(notifier.appContext, XtIMAll);
   667     return 1;
   668 }