home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
tcltk805.zip
/
tcl805s.zip
/
tcl8.0.5
/
os2
/
tclOS2Notify.c
< prev
next >
Wrap
C/C++ Source or Header
|
2001-02-09
|
26KB
|
858 lines
/*
* tclOS2Notify.c --
*
* This file contains OS/2-specific procedures for the notifier,
* which is the lowest-level part of the Tcl event loop. This file
* works together with ../generic/tclNotify.c.
*
* Copyright (c) 1995-1996 Sun Microsystems, Inc.
* Copyright (c) 1996-2001 Illya Vaes
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
*/
#include "tclOS2Int.h"
/*
* This structure is used to keep track of the notifier info for a
* a registered file.
*/
typedef struct FileHandler {
int fd;
int mask; /* Mask of desired events: TCL_READABLE,
* etc. */
int readyMask; /* Mask of events that have been seen since the
* last time file handlers were invoked for
* this file. */
Tcl_FileProc *proc; /* Procedure to call, in the style of
* Tcl_CreateFileHandler. */
ClientData clientData; /* Argument to pass to proc. */
struct FileHandler *nextPtr;/* Next in list of all files we care about. */
} FileHandler;
/*
* The following structure is what is added to the Tcl event queue when
* file handlers are ready to fire.
*/
typedef struct FileHandlerEvent {
Tcl_Event header; /* Information that is standard for
* all events. */
int fd; /* File descriptor that is ready. Used
* to find the FileHandler structure for
* the file (can't point directly to the
* FileHandler structure because it could
* go away while the event is queued). */
} FileHandlerEvent;
/*
* The following static structure contains the state information for the
* select based implementation of the Tcl notifier.
*/
static struct {
FileHandler *firstFileHandlerPtr;
/* Pointer to head of file handler list. */
fd_set checkMasks[3];
/* This array is used to build up the masks
* to be used in the next call to select.
* Bits are set in response to calls to
* Tcl_CreateFileHandler. */
fd_set readyMasks[3];
/* This array reflects the readable/writable
* conditions that were found to exist by the
* last call to select. */
int numFdBits; /* Number of valid bits in checkMasks
* (one more than highest fd for which
* Tcl_WatchFile has been called). */
HWND hwnd; /* Messaging window. */
ULONG timeout; /* Current timeout value */
int timerActive; /* 1 if interval timer is running */
} notifier;
/*
* The following static indicates whether this module has been initialized.
*/
static int initialized = 0;
/*
* The following static holds the event semaphore handle for PM-less running.
*/
static HEV eventSem;
static HTIMER timerHandle = NULLHANDLE;
/*
* Static routines defined in this file:
*/
static void InitNotifier _ANSI_ARGS_((void));
static void NotifierExitHandler _ANSI_ARGS_((
ClientData clientData));
static MRESULT EXPENTRY NotifierProc(HWND hwnd, ULONG message,
MPARAM param1, MPARAM param2);
static int FileHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
int flags));
static void UpdateTimer(ULONG timeout);
/*
*----------------------------------------------------------------------
*
* InitNotifier --
*
* Initializes the notifier.
*
* Results:
* None.
*
* Side effects:
* Creates a new notifier.
*
*----------------------------------------------------------------------
*/
static void
InitNotifier(void)
{
#ifdef VERBOSE
printf("InitNotifier (Tcl) 1: initialized %d\n", initialized);
#endif
initialized = 1;
memset(¬ifier, 0, sizeof(notifier));
if (usePm) {
if (!WinRegisterClass(TclOS2GetHAB(), "TclNotifier", NotifierProc, 0L,
0L)) {
panic("Unable to register TclNotifier window class");
}
notifier.hwnd = WinCreateWindow(HWND_DESKTOP, "TclNotifier", NULL,
0L, 0, 0, 0, 0, HWND_DESKTOP, HWND_TOP,
0, NULL, NULL);
#ifdef VERBOSE
printf("InitNotifier (Tcl): hwnd %x\n", notifier.hwnd);
#endif
} else {
rc = DosCreateEventSem(NULL, &eventSem, DC_SEM_SHARED, FALSE);
#ifdef VERBOSE
if (rc != NO_ERROR) {
printf("InitNotifier (Tcl): DosCreateEventSem ERROR %d\n", rc);
} else {
printf("InitNotifier (Tcl): DosCreateEventSem OK %x\n", eventSem);
}
#endif
}
Tcl_CreateExitHandler(NotifierExitHandler, NULL);
}
/*
*----------------------------------------------------------------------
*
* NotifierExitHandler --
*
* This function is called to cleanup the notifier state before
* Tcl is unloaded.
*
* Results:
* None.
*
* Side effects:
* Destroys the notifier.
*
*----------------------------------------------------------------------
*/
static void
NotifierExitHandler(
ClientData clientData) /* Old window proc */
{
#ifdef VERBOSE
printf("NotifierExitHandler (Tcl): hwnd %x, initialized %d => 0\n",
notifier.hwnd, initialized);
#endif
initialized = 0;
Tcl_DeleteExitHandler(NotifierExitHandler, NULL);
if (usePm) {
if (notifier.hwnd != NULLHANDLE) {
WinStopTimer(TclOS2GetHAB(), notifier.hwnd, TID_USERMAX - 1);
WinDestroyWindow(notifier.hwnd);
notifier.hwnd = NULLHANDLE;
}
} else {
DosCloseEventSem(eventSem);
}
}
/*
*----------------------------------------------------------------------
*
* UpdateTimer --
*
* This function starts or stops the notifier interval timer.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
void
UpdateTimer(
ULONG timeout) /* ms timeout, 0 means cancel timer */
{
static ULONG timerId = 0L;
#ifdef VERBOSE
printf("UpdateTimer timeout %dms\n", timeout);
fflush(stdout);
#endif
notifier.timeout = timeout;
if (timeout != 0) {
notifier.timerActive = 1;
if (usePm) {
timerId = WinStartTimer(TclOS2GetHAB(), notifier.hwnd,
TID_USERMAX - 1, timeout);
#ifdef VERBOSE
if (timerId == 0) {
printf("WinStartTimer hwnd %x timeout %d ERROR %x\n",
notifier.hwnd, timeout, WinGetLastError(TclOS2GetHAB()));
} else {
printf("WinStartTimer hwnd %x timeout %d OK %x\n",
notifier.hwnd, timeout, timerId);
}
#endif
} else {
rc = DosAsyncTimer(timeout, (HSEM) eventSem, &timerHandle);
#ifdef VERBOSE
if (rc != NO_ERROR) {
printf("DosAsyncTimer %dms ERROR %d\n", timeout, rc);
} else {
printf("DosAsyncTimer %dms OK %x\n", timeout, timerHandle);
}
#endif
}
} else {
notifier.timerActive = 0;
if (usePm) {
if (timerId) {
rc = WinStopTimer(TclOS2GetHAB(), notifier.hwnd, TID_USERMAX-1);
#ifdef VERBOSE
if (rc == FALSE) {
printf("WinStopTimer hwnd %x ERROR/NOT EXIST %x\n",
notifier.hwnd, WinGetLastError(TclOS2GetHAB()));
} else {
printf("WinStopTimer hwnd %x OK\n", notifier.hwnd);
}
#endif
}
timerId = 0;
} else {
ULONG postCount;
#ifdef VERBOSE
printf("UpdateTimer: timerHandle %x\n", timerHandle);
#endif
if (timerHandle != NULLHANDLE) {
rc = DosStopTimer(timerHandle);
#ifdef VERBOSE
if (rc != NO_ERROR) {
printf("DosStopTimer %x ERROR %d\n", timerHandle, rc);
} else {
printf("DosStopTimer %x OK\n", timerHandle);
}
#endif
rc = DosResetEventSem(eventSem, &postCount);
#ifdef VERBOSE
if (rc != NO_ERROR) {
printf("DosResetEventSem %x ERROR %d\n", eventSem, rc);
} else {
printf("DosResetEventSem %x OK %d\n", eventSem, postCount);
}
#endif
timerHandle = NULLHANDLE;
}
}
}
}
/*
*----------------------------------------------------------------------
*
* Tcl_SetTimer --
*
* This procedure sets the current notifier timer value. The
* notifier will ensure that Tcl_ServiceAll() is called after
* the specified interval, even if no events have occurred.
*
* Results:
* None.
*
* Side effects:
* Replaces any previous timer.
*
*----------------------------------------------------------------------
*/
void
Tcl_SetTimer(timePtr)
Tcl_Time *timePtr; /* Timeout value, may be NULL. */
{
ULONG timeout;
#ifdef VERBOSE
printf("Tcl_SetTimer, timePtr %x\n", timePtr);
#endif
if (!initialized) {
#ifdef VERBOSE
printf("InitNotifier being called by Tcl_SetTimer\n");
#endif
InitNotifier();
}
if (!timePtr) {
timeout = 0;
} else {
/*
* For OS/2 2.1 and earlier, this must be an unsigned int range
* (0-65535), for higher version an unsigned long (0-4294967295).
*/
timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
#ifdef VERBOSE
printf("Tcl_SetTimer timeout %d (%d * 1000 + %d / 1000)\n", timeout,
timePtr->sec, timePtr->usec);
#endif
if ( timeout > 65535 && sysInfo[QSV_VERSION_MAJOR - 1] == 20
&& sysInfo[QSV_VERSION_MINOR - 1] < 30) {
timeout = 65535;
#ifdef VERBOSE
printf("Tcl_SetTimer major %d minor %d => timeout %d\n",
sysInfo[QSV_VERSION_MAJOR - 1],
sysInfo[QSV_VERSION_MINOR - 1], timeout);
#endif
}
}
UpdateTimer(timeout);
}
/*
*----------------------------------------------------------------------
*
* NotifierProc --
*
* This procedure is invoked by OS/2 to process the timer
* message whenever we are using an external dispatch loop.
*
* Results:
* A standard windows result.
*
* Side effects:
* Services any pending events.
*
*----------------------------------------------------------------------
*/
static MRESULT EXPENTRY
NotifierProc(
HWND hwnd,
ULONG message,
MPARAM param1,
MPARAM param2)
{
#ifdef VERBOSE
printf("NotifierProc hwnd %x, msg %x, p1 %x, p2 %x\n", hwnd, message,
param1, param2);
fflush(stdout);
#endif
if (message != WM_TIMER) {
return WinDefWindowProc(hwnd, message, param1, param2);
}
/*
* Process all of the runnable events.
*/
Tcl_ServiceAll();
return 0;
}
/*
*----------------------------------------------------------------------
*
* Tcl_CreateFileHandler --
*
* This procedure registers a file handler with the Xt notifier.
*
* Results:
* None.
*
* Side effects:
* Creates a new file handler structure and registers one or more
* input procedures with Xt.
*
*----------------------------------------------------------------------
*/
void
Tcl_CreateFileHandler(fd, mask, proc, clientData)
int fd; /* Handle of stream to watch. */
int mask; /* OR'ed combination of TCL_READABLE,
* TCL_WRITABLE and TCL_EXCEPTION:
* indicates conditions under which
* proc should be called. */
Tcl_FileProc *proc; /* Procedure to call for each
* selected event. */
ClientData clientData; /* Arbitrary data to pass to proc. */
{
FileHandler *filePtr;
#ifdef VERBOSE
printf("Tcl_CreateFileHandler\n");
#endif
if (!initialized) {
#ifdef VERBOSE
printf("InitNotifier being called by Tcl_CreateFileHandler\n");
#endif
InitNotifier();
}
for (filePtr = notifier.firstFileHandlerPtr; filePtr != NULL;
filePtr = filePtr->nextPtr) {
if (filePtr->fd == fd) {
break;
}
}
if (filePtr == NULL) {
filePtr = (FileHandler*) ckalloc(sizeof(FileHandler)); /* MLK */
filePtr->fd = fd;
filePtr->readyMask = 0;
filePtr->nextPtr = notifier.firstFileHandlerPtr;
notifier.firstFileHandlerPtr = filePtr;
}
filePtr->proc = proc;
filePtr->clientData = clientData;
filePtr->mask = mask;
/*
* Update the check masks for this file.
*/
if (mask & TCL_READABLE) {
FD_SET(fd, ¬ifier.checkMasks[0]);
} else {
FD_CLR(fd, ¬ifier.checkMasks[0]);
}
if (mask & TCL_WRITABLE) {
FD_SET(fd, ¬ifier.checkMasks[1]);
} else {
FD_CLR(fd, ¬ifier.checkMasks[1]);
}
if (mask & TCL_EXCEPTION) {
FD_SET(fd, ¬ifier.checkMasks[2]);
} else {
FD_CLR(fd, ¬ifier.checkMasks[2]);
}
if (notifier.numFdBits <= fd) {
notifier.numFdBits = fd+1;
}
}
/*
*----------------------------------------------------------------------
*
* Tcl_DeleteFileHandler --
*
* Cancel a previously-arranged callback arrangement for
* a file.
*
* Results:
* None.
*
* Side effects:
* If a callback was previously registered on file, remove it.
*
*----------------------------------------------------------------------
*/
void
Tcl_DeleteFileHandler(fd)
int fd; /* Stream id for which to remove callback procedure. */
{
FileHandler *filePtr, *prevPtr;
int index, i;
unsigned long flags;
#ifdef VERBOSE
printf("Tcl_DeleteFileHandler\n");
#endif
if (!initialized) {
#ifdef VERBOSE
printf("InitNotifier being called by Tcl_DeleteFileHandler\n");
#endif
InitNotifier();
}
/*
* Find the entry for the given file (and return if there
* isn't one).
*/
for (prevPtr = NULL, filePtr = notifier.firstFileHandlerPtr; ;
prevPtr = filePtr, filePtr = filePtr->nextPtr) {
if (filePtr == NULL) {
return;
}
if (filePtr->fd == fd) {
break;
}
}
/*
* Update the check masks for this file.
*/
if (filePtr->mask & TCL_READABLE) {
FD_CLR(fd, ¬ifier.checkMasks[0]);
}
if (filePtr->mask & TCL_WRITABLE) {
FD_CLR(fd, ¬ifier.checkMasks[1]);
}
if (filePtr->mask & TCL_EXCEPTION) {
FD_CLR(fd, ¬ifier.checkMasks[2]);
}
/*
* Find current max fd.
*/
if (fd+1 == notifier.numFdBits) {
for (index = fd/sizeof(fd_set), notifier.numFdBits = 0;
index >= 0; index--) {
flags = FD_ISSET(index, ¬ifier.checkMasks[0])
| FD_ISSET(index, ¬ifier.checkMasks[1])
| FD_ISSET(index, ¬ifier.checkMasks[2]);
if (flags) {
for (i = FD_SETSIZE; i > 0; i--) {
if (flags & (i << (i-1))) {
break;
}
}
notifier.numFdBits = index * (sizeof(fd_set)) + i;
return;
}
}
}
/*
* Clean up information in the callback record.
*/
if (prevPtr == NULL) {
notifier.firstFileHandlerPtr = filePtr->nextPtr;
} else {
prevPtr->nextPtr = filePtr->nextPtr;
}
ckfree((char *) filePtr);
}
/*
*----------------------------------------------------------------------
*
* FileHandlerEventProc --
*
* This procedure is called by Tcl_ServiceEvent when a file event
* reaches the front of the event queue. This procedure is
* responsible for actually handling the event by invoking the
* callback for the file handler.
*
* Results:
* Returns 1 if the event was handled, meaning it should be removed
* from the queue. Returns 0 if the event was not handled, meaning
* it should stay on the queue. The only time the event isn't
* handled is if the TCL_FILE_EVENTS flag bit isn't set.
*
* Side effects:
* Whatever the file handler's callback procedure does.
*
*----------------------------------------------------------------------
*/
static int
FileHandlerEventProc(evPtr, flags)
Tcl_Event *evPtr; /* Event to service. */
int flags; /* Flags that indicate what events to
* handle, such as TCL_FILE_EVENTS. */
{
FileHandler *filePtr;
FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr;
int mask;
#ifdef VERBOSE
printf("FileHandlerEventProc\n");
#endif
if (!(flags & TCL_FILE_EVENTS)) {
return 0;
}
/*
* Search through the file handlers to find the one whose handle matches
* the event. We do this rather than keeping a pointer to the file
* handler directly in the event, so that the handler can be deleted
* while the event is queued without leaving a dangling pointer.
*/
for (filePtr = notifier.firstFileHandlerPtr; filePtr != NULL;
filePtr = filePtr->nextPtr) {
if (filePtr->fd != fileEvPtr->fd) {
continue;
}
/*
* The code is tricky for two reasons:
* 1. The file handler's desired events could have changed
* since the time when the event was queued, so AND the
* ready mask with the desired mask.
* 2. The file could have been closed and re-opened since
* the time when the event was queued. This is why the
* ready mask is stored in the file handler rather than
* the queued event: it will be zeroed when a new
* file handler is created for the newly opened file.
*/
mask = filePtr->readyMask & filePtr->mask;
filePtr->readyMask = 0;
if (mask != 0) {
(*filePtr->proc)(filePtr->clientData, mask);
}
break;
}
return 1;
}
/*
*----------------------------------------------------------------------
*
* Tcl_WaitForEvent --
*
* This function is called by Tcl_DoOneEvent to wait for new
* events on the message queue. If the block time is 0, then
* Tcl_WaitForEvent just polls the event queue without blocking.
*
* Results:
* Returns -1 if the select would block forever, otherwise
* returns 0.
*
* Side effects:
* Queues file events that are detected by the select.
*
*----------------------------------------------------------------------
*/
int
Tcl_WaitForEvent(
Tcl_Time *timePtr) /* Maximum block time, or NULL. */
{
QMSG msg;
#ifdef 0
int foundEvent = 1;
#endif
FileHandler *filePtr;
FileHandlerEvent *fileEvPtr;
struct timeval selectTimeout;
ULONG timeout;
int mask, numFound;
#ifdef VERBOSE
printf("Tcl_WaitForEvent\n");
#endif
if (!initialized) {
#ifdef VERBOSE
printf("InitNotifier being called by Tcl_WaitForEvent\n");
#endif
InitNotifier();
}
#ifdef VERBOSE
printf("Tcl_WaitForEvent, timePtr %s\n", timePtr == NULL ? "NULL"
: "non-NULL");
fflush(stdout);
#endif
/*
* Only use the interval timer for non-zero timeouts. This avoids
* generating useless messages when we really just want to poll.
*/
if (timePtr) {
timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
selectTimeout.tv_sec = timePtr->sec;
selectTimeout.tv_usec = timePtr->usec;
} else {
timeout = 0;
selectTimeout.tv_sec = 0;
selectTimeout.tv_usec = 0;
}
UpdateTimer(timeout);
/*
* If we're not using PM, then select() will handle the timeout for us.
* If we're using PM then we have to check the message queue, timing out
* when nothing is there if Tcl specified so. In that case, after timing
* out we have to set the timeout for select() to 0 to prevent waiting
* twice.
*/
if (usePm) {
if (!timePtr || (timeout != 0) ||
WinPeekMsg(TclOS2GetHAB(), &msg, NULLHANDLE, 0, 0, PM_NOREMOVE)) {
if (!WinGetMsg(TclOS2GetHAB(), &msg, NULLHANDLE, 0, 0)) {
/* The application is exiting */
/*
if (msg.msg == WM_QUIT) {
#ifdef VERBOSE
printf("WinPostQueueMsg WM_QUIT (would be Tcl_Exit(0)\n");
fflush(stdout);
#endif
WinPostQueueMsg(msg.hwnd, WM_QUIT, 0, 0);
return -1;
*/
Tcl_Exit(0);
/*
}
*/
}
/*
* Handle timer expiration as a special case so we don't
* claim to be doing work when we aren't.
*/
#ifdef VERBOSE
if (msg.msg == WM_TIMER) {
printf("Tcl_WaitForEvent, WM_TIMER hwnd %x\n", msg.hwnd);
fflush(stdout);
}
#endif
if (msg.msg == WM_TIMER && msg.hwnd == notifier.hwnd) {
return 0;
}
WinDispatchMsg(TclOS2GetHAB(), &msg);
return 1;
}
/*
* We've had any timeout already, so if there's nothing to look for
* just return TCL_OK
*/
if (notifier.numFdBits == 0) {
#ifdef VERBOSE
printf("Tcl_WaitForEvent returning 0 after Win*Msg\n");
fflush(stdout);
#endif
return 0;
}
/* Set timeout to 0 to prevent waiting twice */
selectTimeout.tv_sec = 0;
selectTimeout.tv_usec = 0;
}
/*
if (notifier.numFdBits == 0) {
return 0;
}
*/
#ifdef VERBOSE
printf("Tcl_WaitForEvent: calling select with timeout %d:%d\n",
selectTimeout.tv_sec, selectTimeout.tv_usec);
fflush(stdout);
#endif
memcpy((VOID *) notifier.readyMasks, (VOID *) notifier.checkMasks,
3*sizeof(fd_set));
numFound = select(notifier.numFdBits, ¬ifier.readyMasks[0],
¬ifier.readyMasks[1], ¬ifier.readyMasks[2],
&selectTimeout);
#ifdef VERBOSE
printf("Tcl_WaitForEvent: select found %d with %d\n", numFound,
notifier.numFdBits);
fflush(stdout);
#endif
/*
* Some systems don't clear the masks after an error, so
* we have to do it here.
*/
if (numFound == -1) {
memset((VOID *) notifier.readyMasks, 0, 3*sizeof(fd_set));
}
/*
* Queue all detected file events before returning.
*/
for (filePtr = notifier.firstFileHandlerPtr;
(filePtr != NULL) && (numFound > 0);
filePtr = filePtr->nextPtr) {
mask = 0;
if (FD_ISSET(filePtr->fd, ¬ifier.readyMasks[0])) {
mask |= TCL_READABLE;
}
if (FD_ISSET(filePtr->fd, ¬ifier.readyMasks[1])) {
mask |= TCL_WRITABLE;
}
if (FD_ISSET(filePtr->fd, ¬ifier.readyMasks[2])) {
mask |= TCL_EXCEPTION;
}
if (!mask) {
continue;
} else {
numFound--;
}
/*
* Don't bother to queue an event if the mask was previously
* non-zero since an event must still be on the queue.
*/
if (filePtr->readyMask == 0) {
fileEvPtr = (FileHandlerEvent *) ckalloc(
sizeof(FileHandlerEvent));
fileEvPtr->header.proc = FileHandlerEventProc;
fileEvPtr->fd = filePtr->fd;
Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
}
filePtr->readyMask = mask;
}
return 0;
}
/*
*----------------------------------------------------------------------
*
* Tcl_Sleep --
*
* Delay execution for the specified number of milliseconds.
*
* Results:
* None.
*
* Side effects:
* Time passes.
*
*----------------------------------------------------------------------
*/
void
Tcl_Sleep(ms)
int ms; /* Number of milliseconds to sleep. */
{
#ifdef VERBOSE
printf("Tcl_Sleep %dms\n", ms);
#endif
DosSleep(ms);
}