bce6fc14da687edf98597f061b2547dacde2593f
[NeonServV5.git] / src / IOEngine_win32.c
1 /* IOEngine_win32.c - IOMultiplexer
2  * Copyright (C) 2012  Philipp Kreil (pk910)
3  * 
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.
8  * 
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.
13  * 
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/>. 
16  */
17 #include "IOEngine.h"
18
19 #ifdef WIN32
20
21 #define _WIN32_WINNT 0x501
22 #include <windows.h>
23 #include <winsock2.h>
24
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
28  * messages.
29  *
30  * So ioset_win32_init() creates a hidden window and sets it up for
31  * asynchronous socket notifications.
32  */
33
34 #define IDT_TIMER1 1000
35 #define IDT_TIMER2 1001
36 #define IDT_SOCKET 1002
37
38 static HWND ioset_window;
39
40 static struct IODescriptor *engine_win32_get_iofd(int fd) {
41     struct IODescriptor *iofd;
42     for(iofd = first_descriptor; iofd; iofd = iofd->next) {
43         if(iofd->fd == fd)
44             return iofd;
45     }
46     return NULL;
47 }
48
49 static LRESULT CALLBACK engine_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
50     struct IODescriptor *iofd;
51     int events;
52     struct timeval now, tdiff;
53     
54     gettimeofday(&now, NULL);
55
56     if (hWnd == ioset_window) switch (uMsg)
57     {
58     case IDT_TIMER1:
59         return 0;
60     case IDT_TIMER2:
61         //User Timer
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
68                 continue;
69             }
70             break;
71         }
72         return 0;
73     case IDT_SOCKET:
74         iofd = engine_win32_get_iofd(wParam);
75         events = WSAGETSELECTEVENT(lParam);
76         
77         iohandler_events(iofd, (events & (FD_READ | FD_ACCEPT | FD_CLOSE)) != 0, (events & (FD_WRITE | FD_CONNECT)) != 0);
78         return 0;
79     case WM_QUIT:
80         return 0;
81     }
82     return DefWindowProc(hWnd, uMsg, wParam, lParam);
83 }
84
85 static int engine_win32_init() {
86     WNDCLASSEX wcx;
87     HINSTANCE hinst;
88     WSADATA wsadata;
89     
90     // Start Windows Sockets.
91     if (WSAStartup(MAKEWORD(2, 0), &wsadata)) {
92         iohandler_log(IOLOG_FATAL, "Unable to start Windows Sockets");
93         return 0;
94     }
95     
96     // Get Windows HINSTANCE.
97     hinst = GetModuleHandle(NULL);
98
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))
106         return 0;
107
108     ioset_window = CreateWindow("IOMultiplexerMainWindow", "IOMultiplexer", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
109     if (!ioset_window)
110         return 0;
111     return 1;
112 }
113
114 static long engine_win32_events(struct IODescriptor *iofd) {
115     switch (iofd->state) {
116     case IO_CLOSED:
117         return 0;
118     case IO_LISTENING:
119         return FD_ACCEPT;
120     case IO_CONNECTING:
121         return FD_CONNECT;
122     case IO_CONNECTED:
123     case IO_SSLWAIT:
124         return FD_READ | FD_CLOSE | (iohandler_wants_writes(iofd) ? FD_WRITE : 0);
125     }
126     return 0;
127 }
128
129 static void engine_win32_update(struct IODescriptor *iofd) {
130     long events;
131     
132     if(iofd->type == IOTYPE_STDIN)
133         return;
134     
135     events = engine_win32_events(iofd);
136     WSAAsyncSelect(iofd->fd, ioset_window, IDT_SOCKET, events);
137 }
138
139 static void engine_win32_add(struct IODescriptor *iofd) {
140     if(iofd->type == IOTYPE_STDIN)
141         return;
142     
143     engine_win32_update(iofd);
144 }
145
146 static void engine_win32_remove(struct IODescriptor *iofd) {
147     unsigned long ulong;
148     
149     if(iofd->type == IOTYPE_STDIN)
150         return;
151     
152     WSAAsyncSelect(iofd->fd, ioset_window, IDT_SOCKET, 0);
153     
154     ulong = 0;
155     ioctlsocket(iofd->fd, FIONBIO, &ulong);
156 }
157
158 static void engine_win32_loop(struct timeval *timeout) {
159     MSG msg;
160     BOOL not_really_bool;
161     int msec, cmsec, sett2;
162     struct timeval now, tdiff;
163     struct IODescriptor *iofd, *tmp_iofd;
164     
165     gettimeofday(&now, NULL);
166     
167     for(iofd = first_descriptor; iofd; iofd = tmp_iofd) {
168         tmp_iofd = iofd->next;
169         if(iofd->type == IOTYPE_STDIN) {
170             #ifdef WIN32
171             //WIN32 doesn't support stdin within select
172             //just try to read the single events from the console
173             DWORD dwRead;
174             INPUT_RECORD inRecords[128];
175             unsigned int i;
176             int read_bytes = 0;
177             GetNumberOfConsoleInputEvents(GetStdHandle(STD_INPUT_HANDLE), &dwRead);
178             if(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;
185                         read_bytes++;
186                     }
187                 }
188             }
189             if(read_bytes)
190                 iohandler_events(iofd, read_bytes, 0);
191             if(read_bytes >= 128) {
192                 timeout->tv_sec = 0;
193                 timeout->tv_usec = 1;
194                 //minimal timeout
195             } else {
196                 timeout->tv_sec = 0;
197                 timeout->tv_usec = 100000;
198             }
199             #else
200             if(iofd->fd > fds_size)
201                 fds_size = iofd->fd;
202             FD_SET(iofd->fd, &read_fds);
203             #endif
204         }
205     }
206     
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);
210     
211     //set additional User Timer (if ther's one)
212     sett2 = 0;
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
219             continue;
220         } else if(tdiff.tv_usec < 0) {
221             tdiff.tv_sec--;
222             tdiff.tv_usec += 1000000; //1 sec
223         }
224         cmsec = (tdiff.tv_sec * 1000) + (tdiff.tv_usec / 1000);
225         if(cmsec < msec) {
226             sett2 = 1;
227             msec = cmsec;
228         }
229         break;
230     }
231     if(sett2)
232         SetTimer(ioset_window, IDT_TIMER2, msec, NULL);
233     
234     // Do a blocking read of the message queue.
235     not_really_bool = GetMessage(&msg, NULL, 0, 0);
236     KillTimer(ioset_window, IDT_TIMER1);
237     if(sett2)
238         KillTimer(ioset_window, IDT_TIMER2);
239     if (not_really_bool <=0)
240         return;
241     else {
242         TranslateMessage(&msg);
243         DispatchMessage(&msg);
244     }
245 }
246
247 static void engine_win32_cleanup() {
248     DestroyWindow(ioset_window);
249     ioset_window = NULL;
250     WSACleanup();
251 }
252
253 struct IOEngine engine_win32 = {
254     .name = "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,
261 };
262
263 #else
264
265 struct IOEngine engine_win32 = {
266     .name = "win32",
267     .init = NULL,
268     .add = NULL,
269     .remove = NULL,
270     .update = NULL,
271     .loop = NULL,
272     .cleanup = NULL,
273 };
274
275 #endif