add GHC.HetMet.{hetmet_kappa,hetmet_kappa_app}
[ghc-base.git] / cbits / inputReady.c
1 /* 
2  * (c) The GRASP/AQUA Project, Glasgow University, 1994-2002
3  *
4  * hWaitForInput Runtime Support
5  */
6
7 /* select and supporting types is not Posix */
8 /* #include "PosixSource.h" */
9 #include "HsBase.h"
10
11 /*
12  * inputReady(fd) checks to see whether input is available on the file
13  * descriptor 'fd'.  Input meaning 'can I safely read at least a
14  * *character* from this file object without blocking?'
15  */
16 int
17 fdReady(int fd, int write, int msecs, int isSock)
18 {
19     if 
20 #if defined(_MSC_VER) || defined(__MINGW32__) || defined(_WIN32)
21     ( isSock ) {
22 #else
23     ( 1 ) {
24 #endif
25         int maxfd, ready;
26         fd_set rfd, wfd;
27         struct timeval tv;
28         
29         FD_ZERO(&rfd);
30         FD_ZERO(&wfd);
31         if (write) {
32             FD_SET(fd, &wfd);
33         } else {
34             FD_SET(fd, &rfd);
35         }
36         
37         /* select() will consider the descriptor set in the range of 0 to
38          * (maxfd-1) 
39          */
40         maxfd = fd + 1;
41         tv.tv_sec  = msecs / 1000;
42         tv.tv_usec = (msecs % 1000) * 1000;
43         
44         while ((ready = select(maxfd, &rfd, &wfd, NULL, &tv)) < 0 ) {
45             if (errno != EINTR ) {
46                 return -1;
47             }
48         }
49         
50         /* 1 => Input ready, 0 => not ready, -1 => error */
51         return (ready);
52     }
53 #if defined(_MSC_VER) || defined(__MINGW32__) || defined(_WIN32)
54     else {
55         DWORD rc;
56         HANDLE hFile = (HANDLE)_get_osfhandle(fd);
57         DWORD avail;
58
59         switch (GetFileType(hFile)) {
60
61         case FILE_TYPE_CHAR:
62         {
63             INPUT_RECORD buf[1];
64             DWORD count;
65
66             // nightmare.  A Console Handle will appear to be ready
67             // (WaitForSingleObject() returned WAIT_OBJECT_0) when
68             // it has events in its input buffer, but these events might
69             // not be keyboard events, so when we read from the Handle the
70             // read() will block.  So here we try to discard non-keyboard
71             // events from a console handle's input buffer and then try
72             // the WaitForSingleObject() again.
73
74             while (1) // keep trying until we find a real key event
75             {
76                 rc = WaitForSingleObject( hFile, msecs );
77                 switch (rc) {
78                 case WAIT_TIMEOUT: return 0;
79                 case WAIT_OBJECT_0: break;
80                 default: /* WAIT_FAILED */ maperrno(); return -1;
81                 }
82
83                 while (1) // discard non-key events
84                 {
85                     rc = PeekConsoleInput(hFile, buf, 1, &count);
86                     // printf("peek, rc=%d, count=%d, type=%d\n", rc, count, buf[0].EventType);
87                     if (rc == 0) {
88                         rc = GetLastError();
89                         if (rc == ERROR_INVALID_HANDLE || rc == ERROR_INVALID_FUNCTION) {
90                             return 1;
91                         } else {
92                             maperrno();
93                             return -1;
94                         }
95                     }
96
97                     if (count == 0) break; // no more events => wait again
98
99                     // discard console events that are not "key down", because
100                     // these will also be discarded by ReadFile().
101                     if (buf[0].EventType == KEY_EVENT &&
102                         buf[0].Event.KeyEvent.bKeyDown &&
103                         buf[0].Event.KeyEvent.uChar.AsciiChar != '\0')
104                     {
105                         // it's a proper keypress:
106                         return 1;
107                     }
108                     else
109                     {
110                         // it's a non-key event, a key up event, or a
111                         // non-character key (e.g. shift).  discard it.
112                         rc = ReadConsoleInput(hFile, buf, 1, &count);
113                         if (rc == 0) {
114                             rc = GetLastError();
115                             if (rc == ERROR_INVALID_HANDLE || rc == ERROR_INVALID_FUNCTION) {
116                                 return 1;
117                             } else {
118                                 maperrno();
119                                 return -1;
120                             }
121                         }
122                     }
123                 }
124             }
125         }
126
127         case FILE_TYPE_DISK:
128             // assume that disk files are always ready:
129             return 1;
130
131         case FILE_TYPE_PIPE:
132             // WaitForMultipleObjects() doesn't work for pipes (it
133             // always returns WAIT_OBJECT_0 even when no data is
134             // available).  If the HANDLE is a pipe, therefore, we try
135             // PeekNamedPipe:
136             //
137             rc = PeekNamedPipe( hFile, NULL, 0, NULL, &avail, NULL );
138             if (rc != 0) {
139                 if (avail != 0) {
140                     return 1;
141                 } else {
142                     return 0;
143                 }
144             } else {
145                 rc = GetLastError();
146                 if (rc == ERROR_BROKEN_PIPE) {
147                     return 1; // this is probably what we want
148                 }
149                 if (rc != ERROR_INVALID_HANDLE && rc != ERROR_INVALID_FUNCTION) {
150                     maperrno();
151                     return -1;
152                 }
153             }
154             /* PeekNamedPipe didn't work - fall through to the general case */
155
156         default:
157             rc = WaitForSingleObject( hFile, msecs );
158
159             /* 1 => Input ready, 0 => not ready, -1 => error */
160             switch (rc) {
161             case WAIT_TIMEOUT: return 0;
162             case WAIT_OBJECT_0: return 1;
163             default: /* WAIT_FAILED */ maperrno(); return -1;
164             }
165         }
166     }
167 #endif
168 }