tclXtNotify.cGo to the documentation of this file.00001 /* 00002 * tclXtNotify.c -- 00003 * 00004 * This file contains the notifier driver implementation for the Xt 00005 * intrinsics. 00006 * 00007 * Copyright (c) 1997 by Sun Microsystems, Inc. 00008 * 00009 * See the file "license.terms" for information on usage and redistribution of 00010 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 00011 * 00012 * RCS: @(#) $Id: tclXtNotify.c,v 1.9 2007/04/16 13:36:36 dkf Exp $ 00013 */ 00014 00015 #include <X11/Intrinsic.h> 00016 #include "tclInt.h" 00017 00018 /* 00019 * This structure is used to keep track of the notifier info for a a 00020 * registered file. 00021 */ 00022 00023 typedef struct FileHandler { 00024 int fd; 00025 int mask; /* Mask of desired events: TCL_READABLE, 00026 * etc. */ 00027 int readyMask; /* Events that have been seen since the last 00028 * time FileHandlerEventProc was called for 00029 * this file. */ 00030 XtInputId read; /* Xt read callback handle. */ 00031 XtInputId write; /* Xt write callback handle. */ 00032 XtInputId except; /* Xt exception callback handle. */ 00033 Tcl_FileProc *proc; /* Procedure to call, in the style of 00034 * Tcl_CreateFileHandler. */ 00035 ClientData clientData; /* Argument to pass to proc. */ 00036 struct FileHandler *nextPtr;/* Next in list of all files we care about. */ 00037 } FileHandler; 00038 00039 /* 00040 * The following structure is what is added to the Tcl event queue when file 00041 * handlers are ready to fire. 00042 */ 00043 00044 typedef struct FileHandlerEvent { 00045 Tcl_Event header; /* Information that is standard for all 00046 * events. */ 00047 int fd; /* File descriptor that is ready. Used to find 00048 * the FileHandler structure for the file 00049 * (can't point directly to the FileHandler 00050 * structure because it could go away while 00051 * the event is queued). */ 00052 } FileHandlerEvent; 00053 00054 /* 00055 * The following static structure contains the state information for the Xt 00056 * based implementation of the Tcl notifier. 00057 */ 00058 00059 static struct NotifierState { 00060 XtAppContext appContext; /* The context used by the Xt notifier. Can be 00061 * set with TclSetAppContext. */ 00062 int appContextCreated; /* Was it created by us? */ 00063 XtIntervalId currentTimeout;/* Handle of current timer. */ 00064 FileHandler *firstFileHandlerPtr; 00065 /* Pointer to head of file handler list. */ 00066 } notifier; 00067 00068 /* 00069 * The following static indicates whether this module has been initialized. 00070 */ 00071 00072 static int initialized = 0; 00073 00074 /* 00075 * Static routines defined in this file. 00076 */ 00077 00078 static int FileHandlerEventProc(Tcl_Event *evPtr, int flags); 00079 static void FileProc(caddr_t clientData, int *source, 00080 XtInputId *id); 00081 void InitNotifier(void); 00082 static void NotifierExitHandler(ClientData clientData); 00083 static void TimerProc(caddr_t clientData, XtIntervalId *id); 00084 static void CreateFileHandler(int fd, int mask, 00085 Tcl_FileProc * proc, ClientData clientData); 00086 static void DeleteFileHandler(int fd); 00087 static void SetTimer(Tcl_Time * timePtr); 00088 static int WaitForEvent(Tcl_Time * timePtr); 00089 00090 /* 00091 * Functions defined in this file for use by users of the Xt Notifier: 00092 */ 00093 00094 EXTERN XtAppContext TclSetAppContext(XtAppContext ctx); 00095 00096 /* 00097 *---------------------------------------------------------------------- 00098 * 00099 * TclSetAppContext -- 00100 * 00101 * Set the notifier application context. 00102 * 00103 * Results: 00104 * None. 00105 * 00106 * Side effects: 00107 * Sets the application context used by the notifier. Panics if the 00108 * context is already set when called. 00109 * 00110 *---------------------------------------------------------------------- 00111 */ 00112 00113 XtAppContext 00114 TclSetAppContext( 00115 XtAppContext appContext) 00116 { 00117 if (!initialized) { 00118 InitNotifier(); 00119 } 00120 00121 /* 00122 * If we already have a context we check whether we were asked to set a 00123 * new context. If so, we panic because we try to prevent switching 00124 * contexts by mistake. Otherwise, we return the one we have. 00125 */ 00126 00127 if (notifier.appContext != NULL) { 00128 if (appContext != NULL) { 00129 /* 00130 * We already have a context. We do not allow switching contexts 00131 * after initialization, so we panic. 00132 */ 00133 00134 Tcl_Panic("TclSetAppContext: multiple application contexts"); 00135 } 00136 } else { 00137 /* 00138 * If we get here we have not yet gotten a context, so either create 00139 * one or use the one supplied by our caller. 00140 */ 00141 00142 if (appContext == NULL) { 00143 /* 00144 * We must create a new context and tell our caller what it is, so 00145 * she can use it too. 00146 */ 00147 00148 notifier.appContext = XtCreateApplicationContext(); 00149 notifier.appContextCreated = 1; 00150 } else { 00151 /* 00152 * Otherwise we remember the context that our caller gave us and 00153 * use it. 00154 */ 00155 00156 notifier.appContextCreated = 0; 00157 notifier.appContext = appContext; 00158 } 00159 } 00160 00161 return notifier.appContext; 00162 } 00163 00164 /* 00165 *---------------------------------------------------------------------- 00166 * 00167 * InitNotifier -- 00168 * 00169 * Initializes the notifier state. 00170 * 00171 * Results: 00172 * None. 00173 * 00174 * Side effects: 00175 * Creates a new exit handler. 00176 * 00177 *---------------------------------------------------------------------- 00178 */ 00179 00180 void 00181 InitNotifier(void) 00182 { 00183 Tcl_NotifierProcs notifier; 00184 00185 /* 00186 * Only reinitialize if we are not in exit handling. The notifier can get 00187 * reinitialized after its own exit handler has run, because of exit 00188 * handlers for the I/O and timer sub-systems (order dependency). 00189 */ 00190 00191 if (TclInExit()) { 00192 return; 00193 } 00194 00195 notifier.createFileHandlerProc = CreateFileHandler; 00196 notifier.deleteFileHandlerProc = DeleteFileHandler; 00197 notifier.setTimerProc = SetTimer; 00198 notifier.waitForEventProc = WaitForEvent; 00199 Tcl_SetNotifier(¬ifier); 00200 00201 /* 00202 * DO NOT create the application context yet; doing so would prevent 00203 * external applications from setting it for us to their own ones. 00204 */ 00205 00206 initialized = 1; 00207 memset(¬ifier, 0, sizeof(notifier)); 00208 Tcl_CreateExitHandler(NotifierExitHandler, NULL); 00209 } 00210 00211 /* 00212 *---------------------------------------------------------------------- 00213 * 00214 * NotifierExitHandler -- 00215 * 00216 * This function is called to cleanup the notifier state before Tcl is 00217 * unloaded. 00218 * 00219 * Results: 00220 * None. 00221 * 00222 * Side effects: 00223 * Destroys the notifier window. 00224 * 00225 *---------------------------------------------------------------------- 00226 */ 00227 00228 static void 00229 NotifierExitHandler( 00230 ClientData clientData) /* Not used. */ 00231 { 00232 if (notifier.currentTimeout != 0) { 00233 XtRemoveTimeOut(notifier.currentTimeout); 00234 } 00235 for (; notifier.firstFileHandlerPtr != NULL; ) { 00236 Tcl_DeleteFileHandler(notifier.firstFileHandlerPtr->fd); 00237 } 00238 if (notifier.appContextCreated) { 00239 XtDestroyApplicationContext(notifier.appContext); 00240 notifier.appContextCreated = 0; 00241 notifier.appContext = NULL; 00242 } 00243 initialized = 0; 00244 } 00245 00246 /* 00247 *---------------------------------------------------------------------- 00248 * 00249 * SetTimer -- 00250 * 00251 * This procedure sets the current notifier timeout value. 00252 * 00253 * Results: 00254 * None. 00255 * 00256 * Side effects: 00257 * Replaces any previous timer. 00258 * 00259 *---------------------------------------------------------------------- 00260 */ 00261 00262 static void 00263 SetTimer( 00264 Tcl_Time *timePtr) /* Timeout value, may be NULL. */ 00265 { 00266 long timeout; 00267 00268 if (!initialized) { 00269 InitNotifier(); 00270 } 00271 00272 TclSetAppContext(NULL); 00273 if (notifier.currentTimeout != 0) { 00274 XtRemoveTimeOut(notifier.currentTimeout); 00275 } 00276 if (timePtr) { 00277 timeout = timePtr->sec * 1000 + timePtr->usec / 1000; 00278 notifier.currentTimeout = XtAppAddTimeOut(notifier.appContext, 00279 (unsigned long) timeout, TimerProc, NULL); 00280 } else { 00281 notifier.currentTimeout = 0; 00282 } 00283 } 00284 00285 /* 00286 *---------------------------------------------------------------------- 00287 * 00288 * TimerProc -- 00289 * 00290 * This procedure is the XtTimerCallbackProc used to handle timeouts. 00291 * 00292 * Results: 00293 * None. 00294 * 00295 * Side effects: 00296 * Processes all queued events. 00297 * 00298 *---------------------------------------------------------------------- 00299 */ 00300 00301 static void 00302 TimerProc( 00303 caddr_t data, /* Not used. */ 00304 XtIntervalId *id) 00305 { 00306 if (*id != notifier.currentTimeout) { 00307 return; 00308 } 00309 notifier.currentTimeout = 0; 00310 00311 Tcl_ServiceAll(); 00312 } 00313 00314 /* 00315 *---------------------------------------------------------------------- 00316 * 00317 * CreateFileHandler -- 00318 * 00319 * This procedure registers a file handler with the Xt notifier. 00320 * 00321 * Results: 00322 * None. 00323 * 00324 * Side effects: 00325 * Creates a new file handler structure and registers one or more input 00326 * procedures with Xt. 00327 * 00328 *---------------------------------------------------------------------- 00329 */ 00330 00331 static void 00332 CreateFileHandler( 00333 int fd, /* Handle of stream to watch. */ 00334 int mask, /* OR'ed combination of TCL_READABLE, 00335 * TCL_WRITABLE, and TCL_EXCEPTION: indicates 00336 * conditions under which proc should be 00337 * called. */ 00338 Tcl_FileProc *proc, /* Procedure to call for each selected 00339 * event. */ 00340 ClientData clientData) /* Arbitrary data to pass to proc. */ 00341 { 00342 FileHandler *filePtr; 00343 00344 if (!initialized) { 00345 InitNotifier(); 00346 } 00347 00348 TclSetAppContext(NULL); 00349 00350 for (filePtr = notifier.firstFileHandlerPtr; filePtr != NULL; 00351 filePtr = filePtr->nextPtr) { 00352 if (filePtr->fd == fd) { 00353 break; 00354 } 00355 } 00356 if (filePtr == NULL) { 00357 filePtr = (FileHandler*) ckalloc(sizeof(FileHandler)); 00358 filePtr->fd = fd; 00359 filePtr->read = 0; 00360 filePtr->write = 0; 00361 filePtr->except = 0; 00362 filePtr->readyMask = 0; 00363 filePtr->mask = 0; 00364 filePtr->nextPtr = notifier.firstFileHandlerPtr; 00365 notifier.firstFileHandlerPtr = filePtr; 00366 } 00367 filePtr->proc = proc; 00368 filePtr->clientData = clientData; 00369 00370 /* 00371 * Register the file with the Xt notifier, if it hasn't been done yet. 00372 */ 00373 00374 if (mask & TCL_READABLE) { 00375 if (!(filePtr->mask & TCL_READABLE)) { 00376 filePtr->read = XtAppAddInput(notifier.appContext, fd, 00377 XtInputReadMask, FileProc, filePtr); 00378 } 00379 } else { 00380 if (filePtr->mask & TCL_READABLE) { 00381 XtRemoveInput(filePtr->read); 00382 } 00383 } 00384 if (mask & TCL_WRITABLE) { 00385 if (!(filePtr->mask & TCL_WRITABLE)) { 00386 filePtr->write = XtAppAddInput(notifier.appContext, fd, 00387 XtInputWriteMask, FileProc, filePtr); 00388 } 00389 } else { 00390 if (filePtr->mask & TCL_WRITABLE) { 00391 XtRemoveInput(filePtr->write); 00392 } 00393 } 00394 if (mask & TCL_EXCEPTION) { 00395 if (!(filePtr->mask & TCL_EXCEPTION)) { 00396 filePtr->except = XtAppAddInput(notifier.appContext, fd, 00397 XtInputExceptMask, FileProc, filePtr); 00398 } 00399 } else { 00400 if (filePtr->mask & TCL_EXCEPTION) { 00401 XtRemoveInput(filePtr->except); 00402 } 00403 } 00404 filePtr->mask = mask; 00405 } 00406 00407 /* 00408 *---------------------------------------------------------------------- 00409 * 00410 * DeleteFileHandler -- 00411 * 00412 * Cancel a previously-arranged callback arrangement for a file. 00413 * 00414 * Results: 00415 * None. 00416 * 00417 * Side effects: 00418 * If a callback was previously registered on file, remove it. 00419 * 00420 *---------------------------------------------------------------------- 00421 */ 00422 00423 static void 00424 DeleteFileHandler( 00425 int fd) /* Stream id for which to remove callback 00426 * procedure. */ 00427 { 00428 FileHandler *filePtr, *prevPtr; 00429 00430 if (!initialized) { 00431 InitNotifier(); 00432 } 00433 00434 TclSetAppContext(NULL); 00435 00436 /* 00437 * Find the entry for the given file (and return if there isn't one). 00438 */ 00439 00440 for (prevPtr = NULL, filePtr = notifier.firstFileHandlerPtr; ; 00441 prevPtr = filePtr, filePtr = filePtr->nextPtr) { 00442 if (filePtr == NULL) { 00443 return; 00444 } 00445 if (filePtr->fd == fd) { 00446 break; 00447 } 00448 } 00449 00450 /* 00451 * Clean up information in the callback record. 00452 */ 00453 00454 if (prevPtr == NULL) { 00455 notifier.firstFileHandlerPtr = filePtr->nextPtr; 00456 } else { 00457 prevPtr->nextPtr = filePtr->nextPtr; 00458 } 00459 if (filePtr->mask & TCL_READABLE) { 00460 XtRemoveInput(filePtr->read); 00461 } 00462 if (filePtr->mask & TCL_WRITABLE) { 00463 XtRemoveInput(filePtr->write); 00464 } 00465 if (filePtr->mask & TCL_EXCEPTION) { 00466 XtRemoveInput(filePtr->except); 00467 } 00468 ckfree((char *) filePtr); 00469 } 00470 00471 /* 00472 *---------------------------------------------------------------------- 00473 * 00474 * FileProc -- 00475 * 00476 * These procedures are called by Xt when a file becomes readable, 00477 * writable, or has an exception. 00478 * 00479 * Results: 00480 * None. 00481 * 00482 * Side effects: 00483 * Makes an entry on the Tcl event queue if the event is interesting. 00484 * 00485 *---------------------------------------------------------------------- 00486 */ 00487 00488 static void 00489 FileProc( 00490 caddr_t clientData, 00491 int *fd, 00492 XtInputId *id) 00493 { 00494 FileHandler *filePtr = (FileHandler *)clientData; 00495 FileHandlerEvent *fileEvPtr; 00496 int mask = 0; 00497 00498 /* 00499 * Determine which event happened. 00500 */ 00501 00502 if (*id == filePtr->read) { 00503 mask = TCL_READABLE; 00504 } else if (*id == filePtr->write) { 00505 mask = TCL_WRITABLE; 00506 } else if (*id == filePtr->except) { 00507 mask = TCL_EXCEPTION; 00508 } 00509 00510 /* 00511 * Ignore unwanted or duplicate events. 00512 */ 00513 00514 if (!(filePtr->mask & mask) || (filePtr->readyMask & mask)) { 00515 return; 00516 } 00517 00518 /* 00519 * This is an interesting event, so put it onto the event queue. 00520 */ 00521 00522 filePtr->readyMask |= mask; 00523 fileEvPtr = (FileHandlerEvent *) ckalloc(sizeof(FileHandlerEvent)); 00524 fileEvPtr->header.proc = FileHandlerEventProc; 00525 fileEvPtr->fd = filePtr->fd; 00526 Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL); 00527 00528 /* 00529 * Process events on the Tcl event queue before returning to Xt. 00530 */ 00531 00532 Tcl_ServiceAll(); 00533 } 00534 00535 /* 00536 *---------------------------------------------------------------------- 00537 * 00538 * FileHandlerEventProc -- 00539 * 00540 * This procedure is called by Tcl_ServiceEvent when a file event reaches 00541 * the front of the event queue. This procedure is responsible for 00542 * actually handling the event by invoking the callback for the file 00543 * handler. 00544 * 00545 * Results: 00546 * Returns 1 if the event was handled, meaning it should be removed from 00547 * the queue. Returns 0 if the event was not handled, meaning it should 00548 * stay on the queue. The only time the event isn't handled is if the 00549 * TCL_FILE_EVENTS flag bit isn't set. 00550 * 00551 * Side effects: 00552 * Whatever the file handler's callback procedure does. 00553 * 00554 *---------------------------------------------------------------------- 00555 */ 00556 00557 static int 00558 FileHandlerEventProc( 00559 Tcl_Event *evPtr, /* Event to service. */ 00560 int flags) /* Flags that indicate what events to handle, 00561 * such as TCL_FILE_EVENTS. */ 00562 { 00563 FileHandler *filePtr; 00564 FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr; 00565 int mask; 00566 00567 if (!(flags & TCL_FILE_EVENTS)) { 00568 return 0; 00569 } 00570 00571 /* 00572 * Search through the file handlers to find the one whose handle matches 00573 * the event. We do this rather than keeping a pointer to the file handler 00574 * directly in the event, so that the handler can be deleted while the 00575 * event is queued without leaving a dangling pointer. 00576 */ 00577 00578 for (filePtr = notifier.firstFileHandlerPtr; filePtr != NULL; 00579 filePtr = filePtr->nextPtr) { 00580 if (filePtr->fd != fileEvPtr->fd) { 00581 continue; 00582 } 00583 00584 /* 00585 * The code is tricky for two reasons: 00586 * 1. The file handler's desired events could have changed since the 00587 * time when the event was queued, so AND the ready mask with the 00588 * desired mask. 00589 * 2. The file could have been closed and re-opened since the time 00590 * when the event was queued. This is why the ready mask is stored 00591 * in the file handler rather than the queued event: it will be 00592 * zeroed when a new file handler is created for the newly opened 00593 * file. 00594 */ 00595 00596 mask = filePtr->readyMask & filePtr->mask; 00597 filePtr->readyMask = 0; 00598 if (mask != 0) { 00599 (*filePtr->proc)(filePtr->clientData, mask); 00600 } 00601 break; 00602 } 00603 return 1; 00604 } 00605 00606 /* 00607 *---------------------------------------------------------------------- 00608 * 00609 * WaitForEvent -- 00610 * 00611 * This function is called by Tcl_DoOneEvent to wait for new events on 00612 * the message queue. If the block time is 0, then Tcl_WaitForEvent just 00613 * polls without blocking. 00614 * 00615 * Results: 00616 * Returns 1 if an event was found, else 0. This ensures that 00617 * Tcl_DoOneEvent will return 1, even if the event is handled by non-Tcl 00618 * code. 00619 * 00620 * Side effects: 00621 * Queues file events that are detected by the select. 00622 * 00623 *---------------------------------------------------------------------- 00624 */ 00625 00626 static int 00627 WaitForEvent( 00628 Tcl_Time *timePtr) /* Maximum block time, or NULL. */ 00629 { 00630 int timeout; 00631 00632 if (!initialized) { 00633 InitNotifier(); 00634 } 00635 00636 TclSetAppContext(NULL); 00637 00638 if (timePtr) { 00639 timeout = timePtr->sec * 1000 + timePtr->usec / 1000; 00640 if (timeout == 0) { 00641 if (XtAppPending(notifier.appContext)) { 00642 goto process; 00643 } else { 00644 return 0; 00645 } 00646 } else { 00647 Tcl_SetTimer(timePtr); 00648 } 00649 } 00650 00651 process: 00652 XtAppProcessEvent(notifier.appContext, XtIMAll); 00653 return 1; 00654 } 00655 00656 /* 00657 * Local Variables: 00658 * mode: c 00659 * c-basic-offset: 4 00660 * fill-column: 78 00661 * End: 00662 */
Generated on Wed Mar 12 12:18:27 2008 by 1.5.1 |