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 iohandler_events(timer_priority, 0, 0);
67 iohandler_close(timer_priority); //also sets timer_priority to the next timed element
74 iofd = engine_win32_get_iofd(wParam);
75 events = WSAGETSELECTEVENT(lParam);
77 iohandler_events(iofd, (events & (FD_READ | FD_ACCEPT | FD_CLOSE)) != 0, (events & (FD_WRITE | FD_CONNECT)) != 0);
82 return DefWindowProc(hWnd, uMsg, wParam, lParam);
85 static int engine_win32_init() {
90 // Start Windows Sockets.
91 if (WSAStartup(MAKEWORD(2, 0), &wsadata)) {
92 iohandler_log(IOLOG_FATAL, "Unable to start Windows Sockets");
96 // Get Windows HINSTANCE.
97 hinst = GetModuleHandle(NULL);
99 // Describe and register a window class.
100 memset(&wcx, 0, sizeof(wcx));
101 wcx.cbSize = sizeof(wcx);
102 wcx.lpfnWndProc = engine_win32_wndproc;
103 wcx.hInstance = hinst;
104 wcx.lpszClassName = "IOMultiplexerMainWindow";
105 if (!RegisterClassEx(&wcx))
108 ioset_window = CreateWindow("IOMultiplexerMainWindow", "IOMultiplexer", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
114 static long engine_win32_events(struct IODescriptor *iofd) {
115 switch (iofd->state) {
124 return FD_READ | FD_CLOSE | (iohandler_wants_writes(iofd) ? FD_WRITE : 0);
129 static void engine_win32_update(struct IODescriptor *iofd) {
132 if(iofd->type == IOTYPE_STDIN)
135 events = engine_win32_events(iofd);
136 WSAAsyncSelect(iofd->fd, ioset_window, IDT_SOCKET, events);
139 static void engine_win32_add(struct IODescriptor *iofd) {
140 if(iofd->type == IOTYPE_STDIN)
143 engine_win32_update(iofd);
146 static void engine_win32_remove(struct IODescriptor *iofd) {
149 if(iofd->type == IOTYPE_STDIN)
152 WSAAsyncSelect(iofd->fd, ioset_window, IDT_SOCKET, 0);
155 ioctlsocket(iofd->fd, FIONBIO, &ulong);
158 static void engine_win32_loop(struct timeval *timeout) {
160 BOOL not_really_bool;
161 int msec, cmsec, sett2;
162 struct timeval now, tdiff;
163 struct IODescriptor *iofd, *tmp_iofd;
165 gettimeofday(&now, NULL);
167 for(iofd = first_descriptor; iofd; iofd = tmp_iofd) {
168 tmp_iofd = iofd->next;
169 if(iofd->type == IOTYPE_STDIN) {
171 //WIN32 doesn't support stdin within select
172 //just try to read the single events from the console
174 INPUT_RECORD inRecords[128];
177 GetNumberOfConsoleInputEvents(GetStdHandle(STD_INPUT_HANDLE), &dwRead);
179 ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &inRecords[0], 128, &dwRead);
180 for (i = 0; i < dwRead; ++i) {
181 if (inRecords[i].EventType == KEY_EVENT) {
182 const char c = inRecords[i].Event.KeyEvent.uChar.AsciiChar;
183 if (inRecords[i].Event.KeyEvent.bKeyDown && c != 0) {
184 iofd->readbuf.buffer[iofd->readbuf.bufpos + read_bytes] = c;
190 iohandler_events(iofd, read_bytes, 0);
191 if(read_bytes >= 128) {
193 timeout->tv_usec = 1;
197 timeout->tv_usec = 100000;
200 if(iofd->fd > fds_size)
202 FD_SET(iofd->fd, &read_fds);
207 // Make sure we are woken up after the appropriate time.
208 msec = (timeout->tv_sec * 1000) + (timeout->tv_usec / 1000);
209 SetTimer(ioset_window, IDT_TIMER1, msec, NULL);
211 //set additional User Timer (if ther's one)
213 while(timer_priority) {
214 tdiff.tv_sec = timer_priority->timeout.tv_sec - now.tv_sec;
215 tdiff.tv_usec = timer_priority->timeout.tv_usec - now.tv_usec;
216 if(tdiff.tv_sec < 0 || (tdiff.tv_sec == 0 && tdiff.tv_usec < 1000)) {
217 iohandler_events(timer_priority, 0, 0);
218 iohandler_close(timer_priority); //also sets timer_priority to the next timed element
220 } else if(tdiff.tv_usec < 0) {
222 tdiff.tv_usec += 1000000; //1 sec
224 cmsec = (tdiff.tv_sec * 1000) + (tdiff.tv_usec / 1000);
232 SetTimer(ioset_window, IDT_TIMER2, msec, NULL);
234 // Do a blocking read of the message queue.
235 not_really_bool = GetMessage(&msg, NULL, 0, 0);
236 KillTimer(ioset_window, IDT_TIMER1);
238 KillTimer(ioset_window, IDT_TIMER2);
239 if (not_really_bool <=0)
242 TranslateMessage(&msg);
243 DispatchMessage(&msg);
247 static void engine_win32_cleanup() {
248 DestroyWindow(ioset_window);
253 struct IOEngine engine_win32 = {
255 .init = engine_win32_init,
256 .add = engine_win32_add,
257 .remove = engine_win32_remove,
258 .update = engine_win32_update,
259 .loop = engine_win32_loop,
260 .cleanup = engine_win32_cleanup,
265 struct IOEngine engine_win32 = {