os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/unix/tclXtNotify.c
Update contrib.
4 * This file contains the notifier driver implementation for the
7 * Copyright (c) 1997 by Sun Microsystems, Inc.
9 * See the file "license.terms" for information on usage and redistribution
10 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 * RCS: @(#) $Id: tclXtNotify.c,v 1.4 1999/07/02 06:05:34 welch Exp $
15 #include <X11/Intrinsic.h>
19 * This structure is used to keep track of the notifier info for a
23 typedef struct FileHandler {
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
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. */
39 * The following structure is what is added to the Tcl event queue when
40 * file handlers are ready to fire.
43 typedef struct FileHandlerEvent {
44 Tcl_Event header; /* Information that is standard for
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). */
54 * The following static structure contains the state information for the
55 * Xt based implementation of the Tcl notifier.
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
69 * The following static indicates whether this module has been initialized.
72 static int initialized = 0;
75 * Static routines defined in this file.
78 static int FileHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
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,
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));
94 * Functions defined in this file for use by users of the Xt Notifier:
97 EXTERN XtAppContext TclSetAppContext _ANSI_ARGS_((XtAppContext ctx));
100 *----------------------------------------------------------------------
102 * TclSetAppContext --
104 * Set the notifier application context.
110 * Sets the application context used by the notifier. Panics if
111 * the context is already set when called.
113 *----------------------------------------------------------------------
117 TclSetAppContext(appContext)
118 XtAppContext appContext;
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.
130 if (notifier.appContext != NULL) {
131 if (appContext != NULL) {
134 * We already have a context. We do not allow switching contexts
135 * after initialization, so we panic.
138 panic("TclSetAppContext: multiple application contexts");
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.
148 if (appContext == NULL) {
151 * We must create a new context and tell our caller what it is, so
152 * she can use it too.
155 notifier.appContext = XtCreateApplicationContext();
156 notifier.appContextCreated = 1;
160 * Otherwise we remember the context that our caller gave us
164 notifier.appContextCreated = 0;
165 notifier.appContext = appContext;
169 return notifier.appContext;
173 *----------------------------------------------------------------------
177 * Initializes the notifier state.
183 * Creates a new exit handler.
185 *----------------------------------------------------------------------
191 Tcl_NotifierProcs notifier;
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).
202 notifier.createFileHandlerProc = CreateFileHandler;
203 notifier.deleteFileHandlerProc = DeleteFileHandler;
204 notifier.setTimerProc = SetTimer;
205 notifier.waitForEventProc = WaitForEvent;
206 Tcl_SetNotifier(¬ifier);
209 * DO NOT create the application context yet; doing so would prevent
210 * external applications from setting it for us to their own ones.
214 memset(¬ifier, 0, sizeof(notifier));
215 Tcl_CreateExitHandler(NotifierExitHandler, NULL);
219 *----------------------------------------------------------------------
221 * NotifierExitHandler --
223 * This function is called to cleanup the notifier state before
230 * Destroys the notifier window.
232 *----------------------------------------------------------------------
237 ClientData clientData) /* Not used. */
239 if (notifier.currentTimeout != 0) {
240 XtRemoveTimeOut(notifier.currentTimeout);
242 for (; notifier.firstFileHandlerPtr != NULL; ) {
243 Tcl_DeleteFileHandler(notifier.firstFileHandlerPtr->fd);
245 if (notifier.appContextCreated) {
246 XtDestroyApplicationContext(notifier.appContext);
247 notifier.appContextCreated = 0;
248 notifier.appContext = NULL;
254 *----------------------------------------------------------------------
258 * This procedure sets the current notifier timeout value.
264 * Replaces any previous timer.
266 *----------------------------------------------------------------------
271 Tcl_Time *timePtr; /* Timeout value, may be NULL. */
279 TclSetAppContext(NULL);
280 if (notifier.currentTimeout != 0) {
281 XtRemoveTimeOut(notifier.currentTimeout);
284 timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
285 notifier.currentTimeout =
286 XtAppAddTimeOut(notifier.appContext, (unsigned long) timeout,
289 notifier.currentTimeout = 0;
294 *----------------------------------------------------------------------
298 * This procedure is the XtTimerCallbackProc used to handle
305 * Processes all queued events.
307 *----------------------------------------------------------------------
312 caddr_t data; /* Not used. */
315 if (*id != notifier.currentTimeout) {
318 notifier.currentTimeout = 0;
324 *----------------------------------------------------------------------
326 * CreateFileHandler --
328 * This procedure registers a file handler with the Xt notifier.
334 * Creates a new file handler structure and registers one or more
335 * input procedures with Xt.
337 *----------------------------------------------------------------------
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
349 ClientData clientData; /* Arbitrary data to pass to proc. */
351 FileHandler *filePtr;
357 TclSetAppContext(NULL);
359 for (filePtr = notifier.firstFileHandlerPtr; filePtr != NULL;
360 filePtr = filePtr->nextPtr) {
361 if (filePtr->fd == fd) {
365 if (filePtr == NULL) {
366 filePtr = (FileHandler*) ckalloc(sizeof(FileHandler));
371 filePtr->readyMask = 0;
373 filePtr->nextPtr = notifier.firstFileHandlerPtr;
374 notifier.firstFileHandlerPtr = filePtr;
376 filePtr->proc = proc;
377 filePtr->clientData = clientData;
380 * Register the file with the Xt notifier, if it hasn't been done yet.
383 if (mask & TCL_READABLE) {
384 if (!(filePtr->mask & TCL_READABLE)) {
386 XtAppAddInput(notifier.appContext, fd, XtInputReadMask,
390 if (filePtr->mask & TCL_READABLE) {
391 XtRemoveInput(filePtr->read);
394 if (mask & TCL_WRITABLE) {
395 if (!(filePtr->mask & TCL_WRITABLE)) {
397 XtAppAddInput(notifier.appContext, fd, XtInputWriteMask,
401 if (filePtr->mask & TCL_WRITABLE) {
402 XtRemoveInput(filePtr->write);
405 if (mask & TCL_EXCEPTION) {
406 if (!(filePtr->mask & TCL_EXCEPTION)) {
408 XtAppAddInput(notifier.appContext, fd, XtInputExceptMask,
412 if (filePtr->mask & TCL_EXCEPTION) {
413 XtRemoveInput(filePtr->except);
416 filePtr->mask = mask;
420 *----------------------------------------------------------------------
422 * DeleteFileHandler --
424 * Cancel a previously-arranged callback arrangement for
431 * If a callback was previously registered on file, remove it.
433 *----------------------------------------------------------------------
437 DeleteFileHandler(fd)
438 int fd; /* Stream id for which to remove
439 * callback procedure. */
441 FileHandler *filePtr, *prevPtr;
447 TclSetAppContext(NULL);
450 * Find the entry for the given file (and return if there
454 for (prevPtr = NULL, filePtr = notifier.firstFileHandlerPtr; ;
455 prevPtr = filePtr, filePtr = filePtr->nextPtr) {
456 if (filePtr == NULL) {
459 if (filePtr->fd == fd) {
465 * Clean up information in the callback record.
468 if (prevPtr == NULL) {
469 notifier.firstFileHandlerPtr = filePtr->nextPtr;
471 prevPtr->nextPtr = filePtr->nextPtr;
473 if (filePtr->mask & TCL_READABLE) {
474 XtRemoveInput(filePtr->read);
476 if (filePtr->mask & TCL_WRITABLE) {
477 XtRemoveInput(filePtr->write);
479 if (filePtr->mask & TCL_EXCEPTION) {
480 XtRemoveInput(filePtr->except);
482 ckfree((char *) filePtr);
486 *----------------------------------------------------------------------
490 * These procedures are called by Xt when a file becomes readable,
491 * writable, or has an exception.
497 * Makes an entry on the Tcl event queue if the event is
500 *----------------------------------------------------------------------
504 FileProc(clientData, fd, id)
509 FileHandler *filePtr = (FileHandler *)clientData;
510 FileHandlerEvent *fileEvPtr;
514 * Determine which event happened.
517 if (*id == filePtr->read) {
519 } else if (*id == filePtr->write) {
521 } else if (*id == filePtr->except) {
522 mask = TCL_EXCEPTION;
526 * Ignore unwanted or duplicate events.
529 if (!(filePtr->mask & mask) || (filePtr->readyMask & mask)) {
534 * This is an interesting event, so put it onto the event queue.
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);
544 * Process events on the Tcl event queue before returning to Xt.
551 *----------------------------------------------------------------------
553 * FileHandlerEventProc --
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.
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.
567 * Whatever the file handler's callback procedure does.
569 *----------------------------------------------------------------------
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. */
578 FileHandler *filePtr;
579 FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr;
582 if (!(flags & TCL_FILE_EVENTS)) {
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.
593 for (filePtr = notifier.firstFileHandlerPtr; filePtr != NULL;
594 filePtr = filePtr->nextPtr) {
595 if (filePtr->fd != fileEvPtr->fd) {
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.
611 mask = filePtr->readyMask & filePtr->mask;
612 filePtr->readyMask = 0;
614 (*filePtr->proc)(filePtr->clientData, mask);
622 *----------------------------------------------------------------------
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.
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
636 * Queues file events that are detected by the select.
638 *----------------------------------------------------------------------
643 Tcl_Time *timePtr) /* Maximum block time, or NULL. */
651 TclSetAppContext(NULL);
654 timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
656 if (XtAppPending(notifier.appContext)) {
662 Tcl_SetTimer(timePtr);
666 XtAppProcessEvent(notifier.appContext, XtIMAll);