📄 tclwinconsole.c
字号:
*---------------------------------------------------------------------- * * ConsoleWatchProc -- * * Called by the notifier to set up to watch for events on this * channel. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */static voidConsoleWatchProc( ClientData instanceData, /* Console state. */ int mask) /* What events to watch for, OR-ed * combination of TCL_READABLE, * TCL_WRITABLE and TCL_EXCEPTION. */{ ConsoleInfo **nextPtrPtr, *ptr; ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData; int oldMask = infoPtr->watchMask; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); /* * Since most of the work is handled by the background threads, * we just need to update the watchMask and then force the notifier * to poll once. */ infoPtr->watchMask = mask & infoPtr->validMask; if (infoPtr->watchMask) { Tcl_Time blockTime = { 0, 0 }; if (!oldMask) { infoPtr->nextPtr = tsdPtr->firstConsolePtr; tsdPtr->firstConsolePtr = infoPtr; } Tcl_SetMaxBlockTime(&blockTime); } else { if (oldMask) { /* * Remove the console from the list of watched consoles. */ for (nextPtrPtr = &(tsdPtr->firstConsolePtr), ptr = *nextPtrPtr; ptr != NULL; nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) { if (infoPtr == ptr) { *nextPtrPtr = ptr->nextPtr; break; } } } }}/* *---------------------------------------------------------------------- * * ConsoleGetHandleProc -- * * Called from Tcl_GetChannelHandle to retrieve OS handles from * inside a command consoleline based channel. * * Results: * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if * there is no handle for the specified direction. * * Side effects: * None. * *---------------------------------------------------------------------- */static intConsoleGetHandleProc( ClientData instanceData, /* The console state. */ int direction, /* TCL_READABLE or TCL_WRITABLE */ ClientData *handlePtr) /* Where to store the handle. */{ ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData; *handlePtr = (ClientData) infoPtr->handle; return TCL_OK;}/* *---------------------------------------------------------------------- * * WaitForRead -- * * Wait until some data is available, the console is at * EOF or the reader thread is blocked waiting for data (if the * channel is in non-blocking mode). * * Results: * Returns 1 if console is readable. Returns 0 if there is no data * on the console, but there is buffered data. Returns -1 if an * error occurred. If an error occurred, the threads may not * be synchronized. * * Side effects: * Updates the shared state flags. If no error occurred, * the reader thread is blocked waiting for a signal from the * main thread. * *---------------------------------------------------------------------- */static intWaitForRead( ConsoleInfo *infoPtr, /* Console state. */ int blocking) /* Indicates whether call should be * blocking or not. */{ DWORD timeout, count; HANDLE *handle = infoPtr->handle; INPUT_RECORD input; while (1) { /* * Synchronize with the reader thread. */ timeout = blocking ? INFINITE : 0; if (WaitForSingleObject(infoPtr->readable, timeout) == WAIT_TIMEOUT) { /* * The reader thread is blocked waiting for data and the channel * is in non-blocking mode. */ errno = EAGAIN; return -1; } /* * At this point, the two threads are synchronized, so it is safe * to access shared state. */ /* * If the console has hit EOF, it is always readable. */ if (infoPtr->readFlags & CONSOLE_EOF) { return 1; } if (PeekConsoleInput(handle, &input, 1, &count) == FALSE) { /* * Check to see if the peek failed because of EOF. */ TclWinConvertError(GetLastError()); if (errno == EOF) { infoPtr->readFlags |= CONSOLE_EOF; return 1; } /* * Ignore errors if there is data in the buffer. */ if (infoPtr->readFlags & CONSOLE_BUFFERED) { return 0; } else { return -1; } } /* * If there is data in the buffer, the console must be * readable (since it is a line-oriented device). */ if (infoPtr->readFlags & CONSOLE_BUFFERED) { return 1; } /* * There wasn't any data available, so reset the thread and * try again. */ ResetEvent(infoPtr->readable); SetEvent(infoPtr->startReader); }}/* *---------------------------------------------------------------------- * * ConsoleReaderThread -- * * This function runs in a separate thread and waits for input * to become available on a console. * * Results: * None. * * Side effects: * Signals the main thread when input become available. May * cause the main thread to wake up by posting a message. May * one line from the console for each wait operation. * *---------------------------------------------------------------------- */static DWORD WINAPIConsoleReaderThread(LPVOID arg){ ConsoleInfo *infoPtr = (ConsoleInfo *)arg; HANDLE *handle = infoPtr->handle; DWORD count; for (;;) { /* * Wait for the main thread to signal before attempting to wait. */ WaitForSingleObject(infoPtr->startReader, INFINITE); count = 0; /* * Look for data on the console, but first ignore any events * that are not KEY_EVENTs */ if (ReadConsole(handle, infoPtr->buffer, CONSOLE_BUFFER_SIZE, (LPDWORD) &infoPtr->bytesRead, NULL) != FALSE) { /* * Data was stored in the buffer. */ infoPtr->readFlags |= CONSOLE_BUFFERED; } else { DWORD err; err = GetLastError(); if (err == EOF) { infoPtr->readFlags = CONSOLE_EOF; } } /* * Signal the main thread by signalling the readable event and * then waking up the notifier thread. */ SetEvent(infoPtr->readable); /* * Alert the foreground thread. Note that we need to treat this like * a critical section so the foreground thread does not terminate * this thread while we are holding a mutex in the notifier code. */ Tcl_MutexLock(&consoleMutex); Tcl_ThreadAlert(infoPtr->threadId); Tcl_MutexUnlock(&consoleMutex); } return 0; /* NOT REACHED */}/* *---------------------------------------------------------------------- * * ConsoleWriterThread -- * * This function runs in a separate thread and writes data * onto a console. * * Results: * Always returns 0. * * Side effects: * Signals the main thread when an output operation is completed. * May cause the main thread to wake up by posting a message. * *---------------------------------------------------------------------- */static DWORD WINAPIConsoleWriterThread(LPVOID arg){ ConsoleInfo *infoPtr = (ConsoleInfo *)arg; HANDLE *handle = infoPtr->handle; DWORD count, toWrite; char *buf; for (;;) { /* * Wait for the main thread to signal before attempting to write. */ WaitForSingleObject(infoPtr->startWriter, INFINITE); buf = infoPtr->writeBuf; toWrite = infoPtr->toWrite; /* * Loop until all of the bytes are written or an error occurs. */ while (toWrite > 0) { if (WriteFile(handle, buf, toWrite, &count, NULL) == FALSE) { infoPtr->writeError = GetLastError(); break; } else { toWrite -= count; buf += count; } } /* * Signal the main thread by signalling the writable event and * then waking up the notifier thread. */ SetEvent(infoPtr->writable); /* * Alert the foreground thread. Note that we need to treat this like * a critical section so the foreground thread does not terminate * this thread while we are holding a mutex in the notifier code. */ Tcl_MutexLock(&consoleMutex); Tcl_ThreadAlert(infoPtr->threadId); Tcl_MutexUnlock(&consoleMutex); } return 0; /* NOT REACHED */}/* *---------------------------------------------------------------------- * * TclWinOpenConsoleChannel -- * * Constructs a Console channel for the specified standard OS handle. * This is a helper function to break up the construction of * channels into File, Console, or Serial. * * Results: * Returns the new channel, or NULL. * * Side effects: * May open the channel * *---------------------------------------------------------------------- */Tcl_ChannelTclWinOpenConsoleChannel(handle, channelName, permissions) HANDLE handle; char *channelName; int permissions;{ char encoding[4 + TCL_INTEGER_SPACE]; ConsoleInfo *infoPtr; ThreadSpecificData *tsdPtr; DWORD id; tsdPtr = ConsoleInit(); /* * See if a channel with this handle already exists. */ infoPtr = (ConsoleInfo *) ckalloc((unsigned) sizeof(ConsoleInfo)); memset(infoPtr, 0, sizeof(ConsoleInfo)); infoPtr->validMask = permissions; infoPtr->handle = handle; wsprintfA(encoding, "cp%d", GetConsoleCP()); /* * Use the pointer for the name of the result channel. * This keeps the channel names unique, since some may share * handles (stdin/stdout/stderr for instance). */ wsprintfA(channelName, "file%lx", (int) infoPtr); infoPtr->channel = Tcl_CreateChannel(&consoleChannelType, channelName, (ClientData) infoPtr, permissions); infoPtr->threadId = Tcl_GetCurrentThread(); if (permissions & TCL_READABLE) { infoPtr->readable = CreateEvent(NULL, TRUE, TRUE, NULL); infoPtr->startReader = CreateEvent(NULL, FALSE, FALSE, NULL); infoPtr->readThread = CreateThread(NULL, 8000, ConsoleReaderThread, infoPtr, 0, &id); SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST); } if (permissions & TCL_WRITABLE) { infoPtr->writable = CreateEvent(NULL, TRUE, TRUE, NULL); infoPtr->startWriter = CreateEvent(NULL, FALSE, FALSE, NULL); infoPtr->writeThread = CreateThread(NULL, 8000, ConsoleWriterThread, infoPtr, 0, &id); } /* * Files have default translation of AUTO and ^Z eof char, which * means that a ^Z will be accepted as EOF when reading. */ Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto"); Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\032 {}"); Tcl_SetChannelOption(NULL, infoPtr->channel, "-encoding", encoding); return infoPtr->channel;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -