Merge branch 'development'
[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                 if(timer_priority->constant_timeout) {
67                     tdiff.tv_sec = 0;
68                     iohandler_set_timeout(timer_priority, &tdiff);
69                     iohandler_events(timer_priority, 0, 0);
70                 } else {
71                     iohandler_events(timer_priority, 0, 0);
72                     iohandler_close(timer_priority); //also sets timer_priority to the next timed element
73                 }
74                 continue;
75             }
76             break;
77         }
78         return 0;
79     case IDT_SOCKET:
80         iofd = engine_win32_get_iofd(wParam);
81         events = WSAGETSELECTEVENT(lParam);
82         
83         iohandler_events(iofd, (events & (FD_READ | FD_ACCEPT | FD_CLOSE)) != 0, (events & (FD_WRITE | FD_CONNECT)) != 0);
84         return 0;
85     case WM_QUIT:
86         return 0;
87     }
88     return DefWindowProc(hWnd, uMsg, wParam, lParam);
89 }
90
91 static int engine_win32_init() {
92     WNDCLASSEX wcx;
93     HINSTANCE hinst;
94     WSADATA wsadata;
95     
96     // Start Windows Sockets.
97     if (WSAStartup(MAKEWORD(2, 0), &wsadata)) {
98         iohandler_log(IOLOG_FATAL, "Unable to start Windows Sockets");
99         return 0;
100     }
101     
102     // Get Windows HINSTANCE.
103     hinst = GetModuleHandle(NULL);
104
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))
112         return 0;
113
114     ioset_window = CreateWindow("IOMultiplexerMainWindow", "IOMultiplexer", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
115     if (!ioset_window)
116         return 0;
117     return 1;
118 }
119
120 static long engine_win32_events(struct IODescriptor *iofd) {
121     switch (iofd->state) {
122     case IO_CLOSED:
123         return 0;
124     case IO_LISTENING:
125         return FD_ACCEPT;
126     case IO_CONNECTING:
127         return FD_CONNECT;
128     case IO_CONNECTED:
129     case IO_SSLWAIT:
130         return FD_READ | FD_CLOSE | (iohandler_wants_writes(iofd) ? FD_WRITE : 0);
131     }
132     return 0;
133 }
134
135 static void engine_win32_update(struct IODescriptor *iofd) {
136     long events;
137     
138     if(iofd->type == IOTYPE_STDIN)
139         return;
140     
141     events = engine_win32_events(iofd);
142     WSAAsyncSelect(iofd->fd, ioset_window, IDT_SOCKET, events);
143 }
144
145 static void engine_win32_add(struct IODescriptor *iofd) {
146     if(iofd->type == IOTYPE_STDIN)
147         return;
148     
149     engine_win32_update(iofd);
150 }
151
152 static void engine_win32_remove(struct IODescriptor *iofd) {
153     unsigned long ulong;
154     
155     if(iofd->type == IOTYPE_STDIN)
156         return;
157     
158     WSAAsyncSelect(iofd->fd, ioset_window, IDT_SOCKET, 0);
159     
160     ulong = 0;
161     ioctlsocket(iofd->fd, FIONBIO, &ulong);
162 }
163
164 static void engine_win32_loop(struct timeval *timeout) {
165     MSG msg;
166     BOOL not_really_bool;
167     int msec, cmsec, sett2;
168     struct timeval now, tdiff;
169     struct IODescriptor *iofd, *tmp_iofd;
170     
171     gettimeofday(&now, NULL);
172     
173     for(iofd = first_descriptor; iofd; iofd = tmp_iofd) {
174         tmp_iofd = iofd->next;
175         if(iofd->type == IOTYPE_STDIN) {
176             #ifdef WIN32
177             //WIN32 doesn't support stdin within select
178             //just try to read the single events from the console
179             DWORD dwRead;
180             INPUT_RECORD inRecords[128];
181             unsigned int i;
182             int read_bytes = 0;
183             GetNumberOfConsoleInputEvents(GetStdHandle(STD_INPUT_HANDLE), &dwRead);
184             if(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;
191                         read_bytes++;
192                     }
193                 }
194             }
195             if(read_bytes)
196                 iohandler_events(iofd, read_bytes, 0);
197             if(read_bytes >= 128) {
198                 timeout->tv_sec = 0;
199                 timeout->tv_usec = 1;
200                 //minimal timeout
201             } else {
202                 timeout->tv_sec = 0;
203                 timeout->tv_usec = 100000;
204             }
205             #else
206             if(iofd->fd > fds_size)
207                 fds_size = iofd->fd;
208             FD_SET(iofd->fd, &read_fds);
209             #endif
210         }
211     }
212     
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);
216     
217     //set additional User Timer (if ther's one)
218     sett2 = 0;
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) {
224                 tdiff.tv_sec = 0;
225                 iohandler_set_timeout(timer_priority, &tdiff);
226                 iohandler_events(timer_priority, 0, 0);
227             } else {
228                 iohandler_events(timer_priority, 0, 0);
229                 iohandler_close(timer_priority); //also sets timer_priority to the next timed element
230             }
231             continue;
232         } else if(tdiff.tv_usec < 0) {
233             tdiff.tv_sec--;
234             tdiff.tv_usec += 1000000; //1 sec
235         }
236         cmsec = (tdiff.tv_sec * 1000) + (tdiff.tv_usec / 1000);
237         if(cmsec < msec) {
238             sett2 = 1;
239             msec = cmsec;
240         }
241         break;
242     }
243     if(sett2)
244         SetTimer(ioset_window, IDT_TIMER2, msec, NULL);
245     
246     // Do a blocking read of the message queue.
247     not_really_bool = GetMessage(&msg, NULL, 0, 0);
248     KillTimer(ioset_window, IDT_TIMER1);
249     if(sett2)
250         KillTimer(ioset_window, IDT_TIMER2);
251     if (not_really_bool <=0)
252         return;
253     else {
254         TranslateMessage(&msg);
255         DispatchMessage(&msg);
256     }
257 }
258
259 static void engine_win32_cleanup() {
260     DestroyWindow(ioset_window);
261     ioset_window = NULL;
262     WSACleanup();
263 }
264
265 struct IOEngine engine_win32 = {
266     .name = "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,
273 };
274
275 #else
276
277 struct IOEngine engine_win32 = {
278     .name = "win32",
279     .init = NULL,
280     .add = NULL,
281     .remove = NULL,
282     .update = NULL,
283     .loop = NULL,
284     .cleanup = NULL,
285 };
286
287 #endif