1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
|
/*
* (c) The GRASP/AQUA Project, Glasgow University, 1994-2002
*
* hWaitForInput Runtime Support
*/
/* 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
* descriptor 'fd'. Input meaning 'can I safely read at least a
* *character* from this file object without blocking?'
*/
int
fdReady(int fd, int write, int msecs, int isSock)
{
if
#if defined(_WIN32)
( isSock ) {
#else
( 1 ) {
#endif
int maxfd, ready;
fd_set rfd, wfd;
struct timeval tv;
if ((fd >= (int)FD_SETSIZE) || (fd < 0)) {
/* avoid memory corruption on too large FDs */
errno = EINVAL;
return -1;
}
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(_WIN32)
else {
DWORD rc;
HANDLE hFile = (HANDLE)_get_osfhandle(fd);
DWORD avail;
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;
}
}
}
}
}
}
case FILE_TYPE_DISK:
// assume that disk files are always ready:
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 */
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
}
|