X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=cbits%2FinputReady.c;h=51f278feb15a001b2a4239050e0576a70116de3b;hb=7a97ec4b12e1fbec5505f82032cf4dc435b5a60c;hp=4cb9908617ce24200c1754a5bddbcb2c0abb2f82;hpb=7f1f4e7a695c402ddd3a1dc2cc7114e649a78ebc;p=ghc-base.git diff --git a/cbits/inputReady.c b/cbits/inputReady.c index 4cb9908..51f278f 100644 --- a/cbits/inputReady.c +++ b/cbits/inputReady.c @@ -1,17 +1,12 @@ /* - * (c) The GRASP/AQUA Project, Glasgow University, 1994-1998 + * (c) The GRASP/AQUA Project, Glasgow University, 1994-2002 * - * $Id: inputReady.c,v 1.1 2001/06/28 14:15:04 simonmar Exp $ - * - * hReady Runtime Support + * hWaitForInput Runtime Support */ -/* select and supporting types is not */ -#ifndef _AIX -#define NON_POSIX_SOURCE -#endif - -#include "HsCore.h" +/* select and supporting types is not Posix */ +/* #include "PosixSource.h" */ +#include "HsBase.h" /* * inputReady(fd) checks to see whether input is available on the file @@ -19,35 +14,155 @@ * *character* from this file object without blocking?' */ int -inputReady(int fd, int msecs) +fdReady(int fd, int write, int msecs, int isSock) { - int maxfd, ready; -#ifndef mingw32_TARGET_OS - fd_set rfd; - struct timeval tv; + if +#if defined(_MSC_VER) || defined(__MINGW32__) || defined(_WIN32) + ( isSock ) { +#else + ( 1 ) { #endif + int maxfd, ready; + fd_set rfd, wfd; + struct timeval tv; + + FD_ZERO(&rfd); + FD_ZERO(&wfd); + if (write) { + FD_SET(fd, &wfd); + } else { + FD_SET(fd, &rfd); + } + + /* select() will consider the descriptor set in the range of 0 to + * (maxfd-1) + */ + maxfd = fd + 1; + tv.tv_sec = msecs / 1000; + tv.tv_usec = (msecs % 1000) * 1000; + + while ((ready = select(maxfd, &rfd, &wfd, NULL, &tv)) < 0 ) { + if (errno != EINTR ) { + return -1; + } + } + + /* 1 => Input ready, 0 => not ready, -1 => error */ + return (ready); + } +#if defined(_MSC_VER) || defined(__MINGW32__) || defined(_WIN32) + else { + DWORD rc; + HANDLE hFile = (HANDLE)_get_osfhandle(fd); + DWORD avail; -#ifdef mingw32_TARGET_OS - return 1; -#else - FD_ZERO(&rfd); - FD_SET(fd, &rfd); + switch (GetFileType(hFile)) { + + case FILE_TYPE_CHAR: + { + INPUT_RECORD buf[1]; + DWORD count; + + // nightmare. A Console Handle will appear to be ready + // (WaitForSingleObject() returned WAIT_OBJECT_0) when + // it has events in its input buffer, but these events might + // not be keyboard events, so when we read from the Handle the + // read() will block. So here we try to discard non-keyboard + // events from a console handle's input buffer and then try + // the WaitForSingleObject() again. + + while (1) // keep trying until we find a real key event + { + rc = WaitForSingleObject( hFile, msecs ); + switch (rc) { + case WAIT_TIMEOUT: return 0; + case WAIT_OBJECT_0: break; + default: /* WAIT_FAILED */ maperrno(); return -1; + } + + while (1) // discard non-key events + { + rc = PeekConsoleInput(hFile, buf, 1, &count); + // printf("peek, rc=%d, count=%d, type=%d\n", rc, count, buf[0].EventType); + if (rc == 0) { + rc = GetLastError(); + if (rc == ERROR_INVALID_HANDLE || rc == ERROR_INVALID_FUNCTION) { + return 1; + } else { + maperrno(); + return -1; + } + } + + if (count == 0) break; // no more events => wait again + + // discard console events that are not "key down", because + // these will also be discarded by ReadFile(). + if (buf[0].EventType == KEY_EVENT && + buf[0].Event.KeyEvent.bKeyDown && + buf[0].Event.KeyEvent.uChar.AsciiChar != '\0') + { + // it's a proper keypress: + return 1; + } + else + { + // it's a non-key event, a key up event, or a + // non-character key (e.g. shift). discard it. + rc = ReadConsoleInput(hFile, buf, 1, &count); + if (rc == 0) { + rc = GetLastError(); + if (rc == ERROR_INVALID_HANDLE || rc == ERROR_INVALID_FUNCTION) { + return 1; + } else { + maperrno(); + return -1; + } + } + } + } + } + } - /* select() will consider the descriptor set in the range of 0 to - * (maxfd-1) - */ - maxfd = fd + 1; - tv.tv_sec = msecs / 1000; - tv.tv_usec = msecs % 1000; + case FILE_TYPE_DISK: + // assume that disk files are always ready: + return 1; - while ((ready = select(maxfd, &rfd, NULL, NULL, &tv)) < 0 ) { - if (errno != EINTR ) { - return -1; - } - } + case FILE_TYPE_PIPE: + // WaitForMultipleObjects() doesn't work for pipes (it + // always returns WAIT_OBJECT_0 even when no data is + // available). If the HANDLE is a pipe, therefore, we try + // PeekNamedPipe: + // + rc = PeekNamedPipe( hFile, NULL, 0, NULL, &avail, NULL ); + if (rc != 0) { + if (avail != 0) { + return 1; + } else { + return 0; + } + } else { + rc = GetLastError(); + if (rc == ERROR_BROKEN_PIPE) { + return 1; // this is probably what we want + } + if (rc != ERROR_INVALID_HANDLE && rc != ERROR_INVALID_FUNCTION) { + maperrno(); + return -1; + } + } + /* PeekNamedPipe didn't work - fall through to the general case */ - /* 1 => Input ready, 0 => not ready, -1 => error */ - return (ready); + default: + rc = WaitForSingleObject( hFile, msecs ); + /* 1 => Input ready, 0 => not ready, -1 => error */ + switch (rc) { + case WAIT_TIMEOUT: return 0; + case WAIT_OBJECT_0: return 1; + default: /* WAIT_FAILED */ maperrno(); return -1; + } + } + } #endif -} +}