1 /* IOEngine_win32.c - IOMultiplexer
2 * Copyright (C) 2012 Philipp Kreil (pk910)
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #define _WIN32_WINNT 0x501
25 /* This is massively kludgy. Unfortunately, the only performant I/O
26 * multiplexer with halfway decent semantics under Windows is
27 * WSAAsyncSelect() -- which requires a window that can receive
30 * So ioset_win32_init() creates a hidden window and sets it up for
31 * asynchronous socket notifications.
34 #define IDT_TIMER1 1000
35 #define IDT_TIMER2 1001
36 #define IDT_SOCKET 1002
38 static HWND ioset_window;
40 static struct IODescriptor *engine_win32_get_iofd(int fd) {
41 struct IODescriptor *iofd;
42 for(iofd = first_descriptor; iofd; iofd = iofd->next) {
49 static LRESULT CALLBACK engine_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
50 struct IODescriptor *iofd;
52 struct timeval now, tdiff;
54 gettimeofday(&now, NULL);
56 if (hWnd == ioset_window) switch (uMsg)
62 while(timer_priority) {
63 tdiff.tv_sec = timer_priority->timeout.tv_sec - now.tv_sec;
64 tdiff.tv_usec = timer_priority->timeout.tv_usec - now.tv_usec;
65 if(tdiff.tv_sec < 0 || (tdiff.tv_sec == 0 && tdiff.tv_usec <= 0)) {
66 if(timer_priority->constant_timeout) {
68 iohandler_set_timeout(timer_priority, &tdiff);
69 iohandler_events(timer_priority, 0, 0);
71 iohandler_events(timer_priority, 0, 0);
72 iohandler_close(timer_priority); //also sets timer_priority to the next timed element
80 iofd = engine_win32_get_iofd(wParam);
81 events = WSAGETSELECTEVENT(lParam);
83 iohandler_events(iofd, (events & (FD_READ | FD_ACCEPT | FD_CLOSE)) != 0, (events & (FD_WRITE | FD_CONNECT)) != 0);
88 return DefWindowProc(hWnd, uMsg, wParam, lParam);
91 static int engine_win32_init() {
96 // Start Windows Sockets.
97 if (WSAStartup(MAKEWORD(2, 0), &wsadata)) {
98 iohandler_log(IOLOG_FATAL, "Unable to start Windows Sockets");
102 // Get Windows HINSTANCE.
103 hinst = GetModuleHandle(NULL);
105 // Describe and register a window class.
106 memset(&wcx, 0, sizeof(wcx));
107 wcx.cbSize = sizeof(wcx);
108 wcx.lpfnWndProc = engine_win32_wndproc;
109 wcx.hInstance = hinst;
110 wcx.lpszClassName = "IOMultiplexerMainWindow";
111 if (!RegisterClassEx(&wcx))
114 ioset_window = CreateWindow("IOMultiplexerMainWindow", "IOMultiplexer", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
120 static long engine_win32_events(struct IODescriptor *iofd) {
121 switch (iofd->state) {
130 return FD_READ | FD_CLOSE | (iohandler_wants_writes(iofd) ? FD_WRITE : 0);
135 static void engine_win32_update(struct IODescriptor *iofd) {
138 if(iofd->type == IOTYPE_STDIN)
141 events = engine_win32_events(iofd);
142 WSAAsyncSelect(iofd->fd, ioset_window, IDT_SOCKET, events);
145 static void engine_win32_add(struct IODescriptor *iofd) {
146 if(iofd->type == IOTYPE_STDIN)
149 engine_win32_update(iofd);
152 static void engine_win32_remove(struct IODescriptor *iofd) {
155 if(iofd->type == IOTYPE_STDIN)
158 WSAAsyncSelect(iofd->fd, ioset_window, IDT_SOCKET, 0);
161 ioctlsocket(iofd->fd, FIONBIO, &ulong);
164 static void engine_win32_loop(struct timeval *timeout) {
166 BOOL not_really_bool;
167 int msec, cmsec, sett2;
168 struct timeval now, tdiff;
169 struct IODescriptor *iofd, *tmp_iofd;
171 gettimeofday(&now, NULL);
173 for(iofd = first_descriptor; iofd; iofd = tmp_iofd) {
174 tmp_iofd = iofd->next;
175 if(iofd->type == IOTYPE_STDIN) {
177 //WIN32 doesn't support stdin within select
178 //just try to read the single events from the console
180 INPUT_RECORD inRecords[128];
183 GetNumberOfConsoleInputEvents(GetStdHandle(STD_INPUT_HANDLE), &dwRead);
185 ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &inRecords[0], 128, &dwRead);
186 for (i = 0; i < dwRead; ++i) {
187 if (inRecords[i].EventType == KEY_EVENT) {
188 const char c = inRecords[i].Event.KeyEvent.uChar.AsciiChar;
189 if (inRecords[i].Event.KeyEvent.bKeyDown && c != 0) {
190 iofd->readbuf.buffer[iofd->readbuf.bufpos + read_bytes] = c;
196 iohandler_events(iofd, read_bytes, 0);
197 if(read_bytes >= 128) {
199 timeout->tv_usec = 1;
203 timeout->tv_usec = 100000;
206 if(iofd->fd > fds_size)
208 FD_SET(iofd->fd, &read_fds);
213 // Make sure we are woken up after the appropriate time.
214 msec = (timeout->tv_sec * 1000) + (timeout->tv_usec / 1000);
215 SetTimer(ioset_window, IDT_TIMER1, msec, NULL);
217 //set additional User Timer (if ther's one)
219 while(timer_priority) {
220 tdiff.tv_sec = timer_priority->timeout.tv_sec - now.tv_sec;
221 tdiff.tv_usec = timer_priority->timeout.tv_usec - now.tv_usec;
222 if(tdiff.tv_sec < 0 || (tdiff.tv_sec == 0 && tdiff.tv_usec < 100)) {
223 if(timer_priority->constant_timeout) {
225 iohandler_set_timeout(timer_priority, &tdiff);
226 iohandler_events(timer_priority, 0, 0);
228 iohandler_events(timer_priority, 0, 0);
229 iohandler_close(timer_priority); //also sets timer_priority to the next timed element
232 } else if(tdiff.tv_usec < 0) {
234 tdiff.tv_usec += 1000000; //1 sec
236 cmsec = (tdiff.tv_sec * 1000) + (tdiff.tv_usec / 1000);
244 SetTimer(ioset_window, IDT_TIMER2, msec, NULL);
246 // Do a blocking read of the message queue.
247 not_really_bool = GetMessage(&msg, NULL, 0, 0);
248 KillTimer(ioset_window, IDT_TIMER1);
250 KillTimer(ioset_window, IDT_TIMER2);
251 if (not_really_bool <=0)
254 TranslateMessage(&msg);
255 DispatchMessage(&msg);
259 static void engine_win32_cleanup() {
260 DestroyWindow(ioset_window);
265 struct IOEngine engine_win32 = {
267 .init = engine_win32_init,
268 .add = engine_win32_add,
269 .remove = engine_win32_remove,
270 .update = engine_win32_update,
271 .loop = engine_win32_loop,
272 .cleanup = engine_win32_cleanup,
277 struct IOEngine engine_win32 = {