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