📄 tclwinconsole.c
字号:
/* * tclWinConsole.c -- * * This file implements the Windows-specific console functions, * and the "console" channel driver. * * Copyright (c) 1999 by Scriptics Corp. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * RCS: @(#) $Id: tclWinConsole.c,v 1.8 2002/09/02 19:27:02 hobbs Exp $ */#include "tclWinInt.h"#include <fcntl.h>#include <io.h>#include <sys/stat.h>/* * The following variable is used to tell whether this module has been * initialized. */static int initialized = 0;/* * The consoleMutex locks around access to the initialized variable, and it is * used to protect background threads from being terminated while they are * using APIs that hold locks. */TCL_DECLARE_MUTEX(consoleMutex)/* * Bit masks used in the flags field of the ConsoleInfo structure below. */#define CONSOLE_PENDING (1<<0) /* Message is pending in the queue. */#define CONSOLE_ASYNC (1<<1) /* Channel is non-blocking. *//* * Bit masks used in the sharedFlags field of the ConsoleInfo structure below. */#define CONSOLE_EOF (1<<2) /* Console has reached EOF. */#define CONSOLE_BUFFERED (1<<3) /* data was read into a buffer by the reader thread */#define CONSOLE_BUFFER_SIZE (8*1024)/* * This structure describes per-instance data for a console based channel. */typedef struct ConsoleInfo { HANDLE handle; int type; struct ConsoleInfo *nextPtr;/* Pointer to next registered console. */ Tcl_Channel channel; /* Pointer to channel structure. */ int validMask; /* OR'ed combination of TCL_READABLE, * TCL_WRITABLE, or TCL_EXCEPTION: indicates * which operations are valid on the file. */ int watchMask; /* OR'ed combination of TCL_READABLE, * TCL_WRITABLE, or TCL_EXCEPTION: indicates * which events should be reported. */ int flags; /* State flags, see above for a list. */ Tcl_ThreadId threadId; /* Thread to which events should be reported. * This value is used by the reader/writer * threads. */ HANDLE writeThread; /* Handle to writer thread. */ HANDLE readThread; /* Handle to reader thread. */ HANDLE writable; /* Manual-reset event to signal when the * writer thread has finished waiting for * the current buffer to be written. */ HANDLE readable; /* Manual-reset event to signal when the * reader thread has finished waiting for * input. */ HANDLE startWriter; /* Auto-reset event used by the main thread to * signal when the writer thread should attempt * to write to the console. */ HANDLE startReader; /* Auto-reset event used by the main thread to * signal when the reader thread should attempt * to read from the console. */ DWORD writeError; /* An error caused by the last background * write. Set to 0 if no error has been * detected. This word is shared with the * writer thread so access must be * synchronized with the writable object. */ char *writeBuf; /* Current background output buffer. * Access is synchronized with the writable * object. */ int writeBufLen; /* Size of write buffer. Access is * synchronized with the writable * object. */ int toWrite; /* Current amount to be written. Access is * synchronized with the writable object. */ int readFlags; /* Flags that are shared with the reader * thread. Access is synchronized with the * readable object. */ int bytesRead; /* number of bytes in the buffer */ int offset; /* number of bytes read out of the buffer */ char buffer[CONSOLE_BUFFER_SIZE]; /* Data consumed by reader thread. */} ConsoleInfo;typedef struct ThreadSpecificData { /* * The following pointer refers to the head of the list of consoles * that are being watched for file events. */ ConsoleInfo *firstConsolePtr;} ThreadSpecificData;static Tcl_ThreadDataKey dataKey;/* * The following structure is what is added to the Tcl event queue when * console events are generated. */typedef struct ConsoleEvent { Tcl_Event header; /* Information that is standard for * all events. */ ConsoleInfo *infoPtr; /* Pointer to console info structure. Note * that we still have to verify that the * console exists before dereferencing this * pointer. */} ConsoleEvent;/* * Declarations for functions used only in this file. */static int ConsoleBlockModeProc(ClientData instanceData, int mode);static void ConsoleCheckProc(ClientData clientData, int flags);static int ConsoleCloseProc(ClientData instanceData, Tcl_Interp *interp);static int ConsoleEventProc(Tcl_Event *evPtr, int flags);static void ConsoleExitHandler(ClientData clientData);static int ConsoleGetHandleProc(ClientData instanceData, int direction, ClientData *handlePtr);static ThreadSpecificData *ConsoleInit(void);static int ConsoleInputProc(ClientData instanceData, char *buf, int toRead, int *errorCode);static int ConsoleOutputProc(ClientData instanceData, CONST char *buf, int toWrite, int *errorCode);static DWORD WINAPI ConsoleReaderThread(LPVOID arg);static void ConsoleSetupProc(ClientData clientData, int flags);static void ConsoleWatchProc(ClientData instanceData, int mask);static DWORD WINAPI ConsoleWriterThread(LPVOID arg);static void ProcExitHandler(ClientData clientData);static int WaitForRead(ConsoleInfo *infoPtr, int blocking);/* * This structure describes the channel type structure for command console * based IO. */static Tcl_ChannelType consoleChannelType = { "console", /* Type name. */ TCL_CHANNEL_VERSION_2, /* v2 channel */ ConsoleCloseProc, /* Close proc. */ ConsoleInputProc, /* Input proc. */ ConsoleOutputProc, /* Output proc. */ NULL, /* Seek proc. */ NULL, /* Set option proc. */ NULL, /* Get option proc. */ ConsoleWatchProc, /* Set up notifier to watch the channel. */ ConsoleGetHandleProc, /* Get an OS handle from channel. */ NULL, /* close2proc. */ ConsoleBlockModeProc, /* Set blocking or non-blocking mode.*/ NULL, /* flush proc. */ NULL, /* handler proc. */};/* *---------------------------------------------------------------------- * * ConsoleInit -- * * This function initializes the static variables for this file. * * Results: * None. * * Side effects: * Creates a new event source. * *---------------------------------------------------------------------- */static ThreadSpecificData *ConsoleInit(){ ThreadSpecificData *tsdPtr; /* * Check the initialized flag first, then check again in the mutex. * This is a speed enhancement. */ if (!initialized) { Tcl_MutexLock(&consoleMutex); if (!initialized) { initialized = 1; Tcl_CreateExitHandler(ProcExitHandler, NULL); } Tcl_MutexUnlock(&consoleMutex); } tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); if (tsdPtr == NULL) { tsdPtr = TCL_TSD_INIT(&dataKey); tsdPtr->firstConsolePtr = NULL; Tcl_CreateEventSource(ConsoleSetupProc, ConsoleCheckProc, NULL); Tcl_CreateThreadExitHandler(ConsoleExitHandler, NULL); } return tsdPtr;}/* *---------------------------------------------------------------------- * * ConsoleExitHandler -- * * This function is called to cleanup the console module before * Tcl is unloaded. * * Results: * None. * * Side effects: * Removes the console event source. * *---------------------------------------------------------------------- */static voidConsoleExitHandler( ClientData clientData) /* Old window proc */{ Tcl_DeleteEventSource(ConsoleSetupProc, ConsoleCheckProc, NULL);}/* *---------------------------------------------------------------------- * * ProcExitHandler -- * * This function is called to cleanup the process list before * Tcl is unloaded. * * Results: * None. * * Side effects: * Resets the process list. * *---------------------------------------------------------------------- */static voidProcExitHandler( ClientData clientData) /* Old window proc */{ Tcl_MutexLock(&consoleMutex); initialized = 0; Tcl_MutexUnlock(&consoleMutex);}/* *---------------------------------------------------------------------- * * ConsoleSetupProc -- * * This procedure is invoked before Tcl_DoOneEvent blocks waiting * for an event. * * Results: * None. * * Side effects: * Adjusts the block time if needed. * *---------------------------------------------------------------------- */voidConsoleSetupProc( ClientData data, /* Not used. */ int flags) /* Event flags as passed to Tcl_DoOneEvent. */{ ConsoleInfo *infoPtr; Tcl_Time blockTime = { 0, 0 }; int block = 1; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); if (!(flags & TCL_FILE_EVENTS)) { return; } /* * Look to see if any events are already pending. If they are, poll. */ for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) { if (infoPtr->watchMask & TCL_WRITABLE) { if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) { block = 0; } } if (infoPtr->watchMask & TCL_READABLE) { if (WaitForRead(infoPtr, 0) >= 0) { block = 0; } } } if (!block) { Tcl_SetMaxBlockTime(&blockTime); }}/* *---------------------------------------------------------------------- * * ConsoleCheckProc -- * * This procedure is called by Tcl_DoOneEvent to check the console * event source for events. * * Results: * None. * * Side effects: * May queue an event. * *---------------------------------------------------------------------- */static voidConsoleCheckProc( ClientData data, /* Not used. */ int flags) /* Event flags as passed to Tcl_DoOneEvent. */{ ConsoleInfo *infoPtr; ConsoleEvent *evPtr; int needEvent; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); if (!(flags & TCL_FILE_EVENTS)) { return; } /* * Queue events for any ready consoles that don't already have events * queued. */ for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) { if (infoPtr->flags & CONSOLE_PENDING) { continue; } /* * Queue an event if the console is signaled for reading or writing. */ needEvent = 0; if (infoPtr->watchMask & TCL_WRITABLE) { if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) { needEvent = 1; } } if (infoPtr->watchMask & TCL_READABLE) { if (WaitForRead(infoPtr, 0) >= 0) { needEvent = 1; } } if (needEvent) { infoPtr->flags |= CONSOLE_PENDING; evPtr = (ConsoleEvent *) ckalloc(sizeof(ConsoleEvent)); evPtr->header.proc = ConsoleEventProc; evPtr->infoPtr = infoPtr; Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL); } }}/* *---------------------------------------------------------------------- * * ConsoleBlockModeProc -- * * Set blocking or non-blocking mode on channel. * * Results: * 0 if successful, errno when failed. * * Side effects: * Sets the device into blocking or non-blocking mode. * *---------------------------------------------------------------------- */static intConsoleBlockModeProc( ClientData instanceData, /* Instance data for channel. */ int mode) /* TCL_MODE_BLOCKING or * TCL_MODE_NONBLOCKING. */{ ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData; /* * Consoles on Windows can not be switched between blocking and nonblocking, * hence we have to emulate the behavior. This is done in the input * function by checking against a bit in the state. We set or unset the * bit here to cause the input function to emulate the correct behavior.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -