os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/unix/tclXtNotify.c
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/unix/tclXtNotify.c Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,668 @@
1.4 +/*
1.5 + * tclXtNotify.c --
1.6 + *
1.7 + * This file contains the notifier driver implementation for the
1.8 + * Xt intrinsics.
1.9 + *
1.10 + * Copyright (c) 1997 by Sun Microsystems, Inc.
1.11 + *
1.12 + * See the file "license.terms" for information on usage and redistribution
1.13 + * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
1.14 + *
1.15 + * RCS: @(#) $Id: tclXtNotify.c,v 1.4 1999/07/02 06:05:34 welch Exp $
1.16 + */
1.17 +
1.18 +#include <X11/Intrinsic.h>
1.19 +#include <tclInt.h>
1.20 +
1.21 +/*
1.22 + * This structure is used to keep track of the notifier info for a
1.23 + * a registered file.
1.24 + */
1.25 +
1.26 +typedef struct FileHandler {
1.27 + int fd;
1.28 + int mask; /* Mask of desired events: TCL_READABLE, etc. */
1.29 + int readyMask; /* Events that have been seen since the
1.30 + last time FileHandlerEventProc was called
1.31 + for this file. */
1.32 + XtInputId read; /* Xt read callback handle. */
1.33 + XtInputId write; /* Xt write callback handle. */
1.34 + XtInputId except; /* Xt exception callback handle. */
1.35 + Tcl_FileProc *proc; /* Procedure to call, in the style of
1.36 + * Tcl_CreateFileHandler. */
1.37 + ClientData clientData; /* Argument to pass to proc. */
1.38 + struct FileHandler *nextPtr;/* Next in list of all files we care about. */
1.39 +} FileHandler;
1.40 +
1.41 +/*
1.42 + * The following structure is what is added to the Tcl event queue when
1.43 + * file handlers are ready to fire.
1.44 + */
1.45 +
1.46 +typedef struct FileHandlerEvent {
1.47 + Tcl_Event header; /* Information that is standard for
1.48 + * all events. */
1.49 + int fd; /* File descriptor that is ready. Used
1.50 + * to find the FileHandler structure for
1.51 + * the file (can't point directly to the
1.52 + * FileHandler structure because it could
1.53 + * go away while the event is queued). */
1.54 +} FileHandlerEvent;
1.55 +
1.56 +/*
1.57 + * The following static structure contains the state information for the
1.58 + * Xt based implementation of the Tcl notifier.
1.59 + */
1.60 +
1.61 +static struct NotifierState {
1.62 + XtAppContext appContext; /* The context used by the Xt
1.63 + * notifier. Can be set with
1.64 + * TclSetAppContext. */
1.65 + int appContextCreated; /* Was it created by us? */
1.66 + XtIntervalId currentTimeout; /* Handle of current timer. */
1.67 + FileHandler *firstFileHandlerPtr; /* Pointer to head of file handler
1.68 + * list. */
1.69 +} notifier;
1.70 +
1.71 +/*
1.72 + * The following static indicates whether this module has been initialized.
1.73 + */
1.74 +
1.75 +static int initialized = 0;
1.76 +
1.77 +/*
1.78 + * Static routines defined in this file.
1.79 + */
1.80 +
1.81 +static int FileHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
1.82 + int flags));
1.83 +static void FileProc _ANSI_ARGS_((caddr_t clientData,
1.84 + int *source, XtInputId *id));
1.85 +void InitNotifier _ANSI_ARGS_((void));
1.86 +static void NotifierExitHandler _ANSI_ARGS_((
1.87 + ClientData clientData));
1.88 +static void TimerProc _ANSI_ARGS_((caddr_t clientData,
1.89 + XtIntervalId *id));
1.90 +static void CreateFileHandler _ANSI_ARGS_((int fd, int mask,
1.91 + Tcl_FileProc * proc, ClientData clientData));
1.92 +static void DeleteFileHandler _ANSI_ARGS_((int fd));
1.93 +static void SetTimer _ANSI_ARGS_((Tcl_Time * timePtr));
1.94 +static int WaitForEvent _ANSI_ARGS_((Tcl_Time * timePtr));
1.95 +
1.96 +/*
1.97 + * Functions defined in this file for use by users of the Xt Notifier:
1.98 + */
1.99 +
1.100 +EXTERN XtAppContext TclSetAppContext _ANSI_ARGS_((XtAppContext ctx));
1.101 +
1.102 +/*
1.103 + *----------------------------------------------------------------------
1.104 + *
1.105 + * TclSetAppContext --
1.106 + *
1.107 + * Set the notifier application context.
1.108 + *
1.109 + * Results:
1.110 + * None.
1.111 + *
1.112 + * Side effects:
1.113 + * Sets the application context used by the notifier. Panics if
1.114 + * the context is already set when called.
1.115 + *
1.116 + *----------------------------------------------------------------------
1.117 + */
1.118 +
1.119 +XtAppContext
1.120 +TclSetAppContext(appContext)
1.121 + XtAppContext appContext;
1.122 +{
1.123 + if (!initialized) {
1.124 + InitNotifier();
1.125 + }
1.126 +
1.127 + /*
1.128 + * If we already have a context we check whether we were asked to set a
1.129 + * new context. If so, we panic because we try to prevent switching
1.130 + * contexts by mistake. Otherwise, we return the one we have.
1.131 + */
1.132 +
1.133 + if (notifier.appContext != NULL) {
1.134 + if (appContext != NULL) {
1.135 +
1.136 + /*
1.137 + * We already have a context. We do not allow switching contexts
1.138 + * after initialization, so we panic.
1.139 + */
1.140 +
1.141 + panic("TclSetAppContext: multiple application contexts");
1.142 +
1.143 + }
1.144 + } else {
1.145 +
1.146 + /*
1.147 + * If we get here we have not yet gotten a context, so either create
1.148 + * one or use the one supplied by our caller.
1.149 + */
1.150 +
1.151 + if (appContext == NULL) {
1.152 +
1.153 + /*
1.154 + * We must create a new context and tell our caller what it is, so
1.155 + * she can use it too.
1.156 + */
1.157 +
1.158 + notifier.appContext = XtCreateApplicationContext();
1.159 + notifier.appContextCreated = 1;
1.160 + } else {
1.161 +
1.162 + /*
1.163 + * Otherwise we remember the context that our caller gave us
1.164 + * and use it.
1.165 + */
1.166 +
1.167 + notifier.appContextCreated = 0;
1.168 + notifier.appContext = appContext;
1.169 + }
1.170 + }
1.171 +
1.172 + return notifier.appContext;
1.173 +}
1.174 +
1.175 +/*
1.176 + *----------------------------------------------------------------------
1.177 + *
1.178 + * InitNotifier --
1.179 + *
1.180 + * Initializes the notifier state.
1.181 + *
1.182 + * Results:
1.183 + * None.
1.184 + *
1.185 + * Side effects:
1.186 + * Creates a new exit handler.
1.187 + *
1.188 + *----------------------------------------------------------------------
1.189 + */
1.190 +
1.191 +void
1.192 +InitNotifier()
1.193 +{
1.194 + Tcl_NotifierProcs notifier;
1.195 + /*
1.196 + * Only reinitialize if we are not in exit handling. The notifier
1.197 + * can get reinitialized after its own exit handler has run, because
1.198 + * of exit handlers for the I/O and timer sub-systems (order dependency).
1.199 + */
1.200 +
1.201 + if (TclInExit()) {
1.202 + return;
1.203 + }
1.204 +
1.205 + notifier.createFileHandlerProc = CreateFileHandler;
1.206 + notifier.deleteFileHandlerProc = DeleteFileHandler;
1.207 + notifier.setTimerProc = SetTimer;
1.208 + notifier.waitForEventProc = WaitForEvent;
1.209 + Tcl_SetNotifier(¬ifier);
1.210 +
1.211 + /*
1.212 + * DO NOT create the application context yet; doing so would prevent
1.213 + * external applications from setting it for us to their own ones.
1.214 + */
1.215 +
1.216 + initialized = 1;
1.217 + memset(¬ifier, 0, sizeof(notifier));
1.218 + Tcl_CreateExitHandler(NotifierExitHandler, NULL);
1.219 +}
1.220 +
1.221 +/*
1.222 + *----------------------------------------------------------------------
1.223 + *
1.224 + * NotifierExitHandler --
1.225 + *
1.226 + * This function is called to cleanup the notifier state before
1.227 + * Tcl is unloaded.
1.228 + *
1.229 + * Results:
1.230 + * None.
1.231 + *
1.232 + * Side effects:
1.233 + * Destroys the notifier window.
1.234 + *
1.235 + *----------------------------------------------------------------------
1.236 + */
1.237 +
1.238 +static void
1.239 +NotifierExitHandler(
1.240 + ClientData clientData) /* Not used. */
1.241 +{
1.242 + if (notifier.currentTimeout != 0) {
1.243 + XtRemoveTimeOut(notifier.currentTimeout);
1.244 + }
1.245 + for (; notifier.firstFileHandlerPtr != NULL; ) {
1.246 + Tcl_DeleteFileHandler(notifier.firstFileHandlerPtr->fd);
1.247 + }
1.248 + if (notifier.appContextCreated) {
1.249 + XtDestroyApplicationContext(notifier.appContext);
1.250 + notifier.appContextCreated = 0;
1.251 + notifier.appContext = NULL;
1.252 + }
1.253 + initialized = 0;
1.254 +}
1.255 +
1.256 +/*
1.257 + *----------------------------------------------------------------------
1.258 + *
1.259 + * SetTimer --
1.260 + *
1.261 + * This procedure sets the current notifier timeout value.
1.262 + *
1.263 + * Results:
1.264 + * None.
1.265 + *
1.266 + * Side effects:
1.267 + * Replaces any previous timer.
1.268 + *
1.269 + *----------------------------------------------------------------------
1.270 + */
1.271 +
1.272 +static void
1.273 +SetTimer(timePtr)
1.274 + Tcl_Time *timePtr; /* Timeout value, may be NULL. */
1.275 +{
1.276 + long timeout;
1.277 +
1.278 + if (!initialized) {
1.279 + InitNotifier();
1.280 + }
1.281 +
1.282 + TclSetAppContext(NULL);
1.283 + if (notifier.currentTimeout != 0) {
1.284 + XtRemoveTimeOut(notifier.currentTimeout);
1.285 + }
1.286 + if (timePtr) {
1.287 + timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
1.288 + notifier.currentTimeout =
1.289 + XtAppAddTimeOut(notifier.appContext, (unsigned long) timeout,
1.290 + TimerProc, NULL);
1.291 + } else {
1.292 + notifier.currentTimeout = 0;
1.293 + }
1.294 +}
1.295 +
1.296 +/*
1.297 + *----------------------------------------------------------------------
1.298 + *
1.299 + * TimerProc --
1.300 + *
1.301 + * This procedure is the XtTimerCallbackProc used to handle
1.302 + * timeouts.
1.303 + *
1.304 + * Results:
1.305 + * None.
1.306 + *
1.307 + * Side effects:
1.308 + * Processes all queued events.
1.309 + *
1.310 + *----------------------------------------------------------------------
1.311 + */
1.312 +
1.313 +static void
1.314 +TimerProc(data, id)
1.315 + caddr_t data; /* Not used. */
1.316 + XtIntervalId *id;
1.317 +{
1.318 + if (*id != notifier.currentTimeout) {
1.319 + return;
1.320 + }
1.321 + notifier.currentTimeout = 0;
1.322 +
1.323 + Tcl_ServiceAll();
1.324 +}
1.325 +
1.326 +/*
1.327 + *----------------------------------------------------------------------
1.328 + *
1.329 + * CreateFileHandler --
1.330 + *
1.331 + * This procedure registers a file handler with the Xt notifier.
1.332 + *
1.333 + * Results:
1.334 + * None.
1.335 + *
1.336 + * Side effects:
1.337 + * Creates a new file handler structure and registers one or more
1.338 + * input procedures with Xt.
1.339 + *
1.340 + *----------------------------------------------------------------------
1.341 + */
1.342 +
1.343 +static void
1.344 +CreateFileHandler(fd, mask, proc, clientData)
1.345 + int fd; /* Handle of stream to watch. */
1.346 + int mask; /* OR'ed combination of TCL_READABLE,
1.347 + * TCL_WRITABLE, and TCL_EXCEPTION:
1.348 + * indicates conditions under which
1.349 + * proc should be called. */
1.350 + Tcl_FileProc *proc; /* Procedure to call for each
1.351 + * selected event. */
1.352 + ClientData clientData; /* Arbitrary data to pass to proc. */
1.353 +{
1.354 + FileHandler *filePtr;
1.355 +
1.356 + if (!initialized) {
1.357 + InitNotifier();
1.358 + }
1.359 +
1.360 + TclSetAppContext(NULL);
1.361 +
1.362 + for (filePtr = notifier.firstFileHandlerPtr; filePtr != NULL;
1.363 + filePtr = filePtr->nextPtr) {
1.364 + if (filePtr->fd == fd) {
1.365 + break;
1.366 + }
1.367 + }
1.368 + if (filePtr == NULL) {
1.369 + filePtr = (FileHandler*) ckalloc(sizeof(FileHandler));
1.370 + filePtr->fd = fd;
1.371 + filePtr->read = 0;
1.372 + filePtr->write = 0;
1.373 + filePtr->except = 0;
1.374 + filePtr->readyMask = 0;
1.375 + filePtr->mask = 0;
1.376 + filePtr->nextPtr = notifier.firstFileHandlerPtr;
1.377 + notifier.firstFileHandlerPtr = filePtr;
1.378 + }
1.379 + filePtr->proc = proc;
1.380 + filePtr->clientData = clientData;
1.381 +
1.382 + /*
1.383 + * Register the file with the Xt notifier, if it hasn't been done yet.
1.384 + */
1.385 +
1.386 + if (mask & TCL_READABLE) {
1.387 + if (!(filePtr->mask & TCL_READABLE)) {
1.388 + filePtr->read =
1.389 + XtAppAddInput(notifier.appContext, fd, XtInputReadMask,
1.390 + FileProc, filePtr);
1.391 + }
1.392 + } else {
1.393 + if (filePtr->mask & TCL_READABLE) {
1.394 + XtRemoveInput(filePtr->read);
1.395 + }
1.396 + }
1.397 + if (mask & TCL_WRITABLE) {
1.398 + if (!(filePtr->mask & TCL_WRITABLE)) {
1.399 + filePtr->write =
1.400 + XtAppAddInput(notifier.appContext, fd, XtInputWriteMask,
1.401 + FileProc, filePtr);
1.402 + }
1.403 + } else {
1.404 + if (filePtr->mask & TCL_WRITABLE) {
1.405 + XtRemoveInput(filePtr->write);
1.406 + }
1.407 + }
1.408 + if (mask & TCL_EXCEPTION) {
1.409 + if (!(filePtr->mask & TCL_EXCEPTION)) {
1.410 + filePtr->except =
1.411 + XtAppAddInput(notifier.appContext, fd, XtInputExceptMask,
1.412 + FileProc, filePtr);
1.413 + }
1.414 + } else {
1.415 + if (filePtr->mask & TCL_EXCEPTION) {
1.416 + XtRemoveInput(filePtr->except);
1.417 + }
1.418 + }
1.419 + filePtr->mask = mask;
1.420 +}
1.421 +
1.422 +/*
1.423 + *----------------------------------------------------------------------
1.424 + *
1.425 + * DeleteFileHandler --
1.426 + *
1.427 + * Cancel a previously-arranged callback arrangement for
1.428 + * a file.
1.429 + *
1.430 + * Results:
1.431 + * None.
1.432 + *
1.433 + * Side effects:
1.434 + * If a callback was previously registered on file, remove it.
1.435 + *
1.436 + *----------------------------------------------------------------------
1.437 + */
1.438 +
1.439 +static void
1.440 +DeleteFileHandler(fd)
1.441 + int fd; /* Stream id for which to remove
1.442 + * callback procedure. */
1.443 +{
1.444 + FileHandler *filePtr, *prevPtr;
1.445 +
1.446 + if (!initialized) {
1.447 + InitNotifier();
1.448 + }
1.449 +
1.450 + TclSetAppContext(NULL);
1.451 +
1.452 + /*
1.453 + * Find the entry for the given file (and return if there
1.454 + * isn't one).
1.455 + */
1.456 +
1.457 + for (prevPtr = NULL, filePtr = notifier.firstFileHandlerPtr; ;
1.458 + prevPtr = filePtr, filePtr = filePtr->nextPtr) {
1.459 + if (filePtr == NULL) {
1.460 + return;
1.461 + }
1.462 + if (filePtr->fd == fd) {
1.463 + break;
1.464 + }
1.465 + }
1.466 +
1.467 + /*
1.468 + * Clean up information in the callback record.
1.469 + */
1.470 +
1.471 + if (prevPtr == NULL) {
1.472 + notifier.firstFileHandlerPtr = filePtr->nextPtr;
1.473 + } else {
1.474 + prevPtr->nextPtr = filePtr->nextPtr;
1.475 + }
1.476 + if (filePtr->mask & TCL_READABLE) {
1.477 + XtRemoveInput(filePtr->read);
1.478 + }
1.479 + if (filePtr->mask & TCL_WRITABLE) {
1.480 + XtRemoveInput(filePtr->write);
1.481 + }
1.482 + if (filePtr->mask & TCL_EXCEPTION) {
1.483 + XtRemoveInput(filePtr->except);
1.484 + }
1.485 + ckfree((char *) filePtr);
1.486 +}
1.487 +
1.488 +/*
1.489 + *----------------------------------------------------------------------
1.490 + *
1.491 + * FileProc --
1.492 + *
1.493 + * These procedures are called by Xt when a file becomes readable,
1.494 + * writable, or has an exception.
1.495 + *
1.496 + * Results:
1.497 + * None.
1.498 + *
1.499 + * Side effects:
1.500 + * Makes an entry on the Tcl event queue if the event is
1.501 + * interesting.
1.502 + *
1.503 + *----------------------------------------------------------------------
1.504 + */
1.505 +
1.506 +static void
1.507 +FileProc(clientData, fd, id)
1.508 + caddr_t clientData;
1.509 + int *fd;
1.510 + XtInputId *id;
1.511 +{
1.512 + FileHandler *filePtr = (FileHandler *)clientData;
1.513 + FileHandlerEvent *fileEvPtr;
1.514 + int mask = 0;
1.515 +
1.516 + /*
1.517 + * Determine which event happened.
1.518 + */
1.519 +
1.520 + if (*id == filePtr->read) {
1.521 + mask = TCL_READABLE;
1.522 + } else if (*id == filePtr->write) {
1.523 + mask = TCL_WRITABLE;
1.524 + } else if (*id == filePtr->except) {
1.525 + mask = TCL_EXCEPTION;
1.526 + }
1.527 +
1.528 + /*
1.529 + * Ignore unwanted or duplicate events.
1.530 + */
1.531 +
1.532 + if (!(filePtr->mask & mask) || (filePtr->readyMask & mask)) {
1.533 + return;
1.534 + }
1.535 +
1.536 + /*
1.537 + * This is an interesting event, so put it onto the event queue.
1.538 + */
1.539 +
1.540 + filePtr->readyMask |= mask;
1.541 + fileEvPtr = (FileHandlerEvent *) ckalloc(sizeof(FileHandlerEvent));
1.542 + fileEvPtr->header.proc = FileHandlerEventProc;
1.543 + fileEvPtr->fd = filePtr->fd;
1.544 + Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
1.545 +
1.546 + /*
1.547 + * Process events on the Tcl event queue before returning to Xt.
1.548 + */
1.549 +
1.550 + Tcl_ServiceAll();
1.551 +}
1.552 +
1.553 +/*
1.554 + *----------------------------------------------------------------------
1.555 + *
1.556 + * FileHandlerEventProc --
1.557 + *
1.558 + * This procedure is called by Tcl_ServiceEvent when a file event
1.559 + * reaches the front of the event queue. This procedure is
1.560 + * responsible for actually handling the event by invoking the
1.561 + * callback for the file handler.
1.562 + *
1.563 + * Results:
1.564 + * Returns 1 if the event was handled, meaning it should be removed
1.565 + * from the queue. Returns 0 if the event was not handled, meaning
1.566 + * it should stay on the queue. The only time the event isn't
1.567 + * handled is if the TCL_FILE_EVENTS flag bit isn't set.
1.568 + *
1.569 + * Side effects:
1.570 + * Whatever the file handler's callback procedure does.
1.571 + *
1.572 + *----------------------------------------------------------------------
1.573 + */
1.574 +
1.575 +static int
1.576 +FileHandlerEventProc(evPtr, flags)
1.577 + Tcl_Event *evPtr; /* Event to service. */
1.578 + int flags; /* Flags that indicate what events to
1.579 + * handle, such as TCL_FILE_EVENTS. */
1.580 +{
1.581 + FileHandler *filePtr;
1.582 + FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr;
1.583 + int mask;
1.584 +
1.585 + if (!(flags & TCL_FILE_EVENTS)) {
1.586 + return 0;
1.587 + }
1.588 +
1.589 + /*
1.590 + * Search through the file handlers to find the one whose handle matches
1.591 + * the event. We do this rather than keeping a pointer to the file
1.592 + * handler directly in the event, so that the handler can be deleted
1.593 + * while the event is queued without leaving a dangling pointer.
1.594 + */
1.595 +
1.596 + for (filePtr = notifier.firstFileHandlerPtr; filePtr != NULL;
1.597 + filePtr = filePtr->nextPtr) {
1.598 + if (filePtr->fd != fileEvPtr->fd) {
1.599 + continue;
1.600 + }
1.601 +
1.602 + /*
1.603 + * The code is tricky for two reasons:
1.604 + * 1. The file handler's desired events could have changed
1.605 + * since the time when the event was queued, so AND the
1.606 + * ready mask with the desired mask.
1.607 + * 2. The file could have been closed and re-opened since
1.608 + * the time when the event was queued. This is why the
1.609 + * ready mask is stored in the file handler rather than
1.610 + * the queued event: it will be zeroed when a new
1.611 + * file handler is created for the newly opened file.
1.612 + */
1.613 +
1.614 + mask = filePtr->readyMask & filePtr->mask;
1.615 + filePtr->readyMask = 0;
1.616 + if (mask != 0) {
1.617 + (*filePtr->proc)(filePtr->clientData, mask);
1.618 + }
1.619 + break;
1.620 + }
1.621 + return 1;
1.622 +}
1.623 +
1.624 +/*
1.625 + *----------------------------------------------------------------------
1.626 + *
1.627 + * WaitForEvent --
1.628 + *
1.629 + * This function is called by Tcl_DoOneEvent to wait for new
1.630 + * events on the message queue. If the block time is 0, then
1.631 + * Tcl_WaitForEvent just polls without blocking.
1.632 + *
1.633 + * Results:
1.634 + * Returns 1 if an event was found, else 0. This ensures that
1.635 + * Tcl_DoOneEvent will return 1, even if the event is handled
1.636 + * by non-Tcl code.
1.637 + *
1.638 + * Side effects:
1.639 + * Queues file events that are detected by the select.
1.640 + *
1.641 + *----------------------------------------------------------------------
1.642 + */
1.643 +
1.644 +static int
1.645 +WaitForEvent(
1.646 + Tcl_Time *timePtr) /* Maximum block time, or NULL. */
1.647 +{
1.648 + int timeout;
1.649 +
1.650 + if (!initialized) {
1.651 + InitNotifier();
1.652 + }
1.653 +
1.654 + TclSetAppContext(NULL);
1.655 +
1.656 + if (timePtr) {
1.657 + timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
1.658 + if (timeout == 0) {
1.659 + if (XtAppPending(notifier.appContext)) {
1.660 + goto process;
1.661 + } else {
1.662 + return 0;
1.663 + }
1.664 + } else {
1.665 + Tcl_SetTimer(timePtr);
1.666 + }
1.667 + }
1.668 +process:
1.669 + XtAppProcessEvent(notifier.appContext, XtIMAll);
1.670 + return 1;
1.671 +}