[IOMultiplexerV2] fixed compiling issue with newer MinGW builds...
[NextIRCd.git] / src / IOHandler / IOEngine_win32.c
1 /* IOEngine_win32.c - IOMultiplexer
2  * Copyright (C) 2014  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 #define _IOHandler_internals
18 #include "IOInternal.h"
19 #include "IOHandler.h"
20 #include "IOLog.h"
21 #include "IOSockets.h"
22 #include "IOTimer.h"
23
24 #ifdef WIN32
25 #ifdef _WIN32_WINNT
26 #undef _WIN32_WINNT
27 #endif
28 #define _WIN32_WINNT 0x501
29 #include <winsock2.h>
30 #include <windows.h>
31
32 /* This is massively kludgy.  Unfortunately, the only performant I/O
33  * multiplexer with halfway decent semantics under Windows is
34  * WSAAsyncSelect() -- which requires a window that can receive
35  * messages.
36  *
37  * So ioset_win32_init() creates a hidden window and sets it up for
38  * asynchronous socket notifications.
39  */
40
41 #define IDT_TIMER1 1000
42 #define IDT_TIMER2 1001
43 #define IDT_SOCKET 1002
44
45 static HWND ioset_window;
46
47 static struct _IOSocket *engine_win32_get_iosock(int fd) {
48         struct _IOSocket *iosock;
49         for(iosock = iosocket_first; iosock; iosock = iosock->next) {
50                 if(iosock->fd == fd)
51                         return iosock;
52         }
53         return NULL;
54 }
55
56 static LRESULT CALLBACK engine_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
57         struct _IOSocket *iosock;
58         int events;
59
60         if (hWnd == ioset_window) {
61                 switch (uMsg) {
62                 case IDT_TIMER1:
63                         return 0;
64                 case IDT_TIMER2:
65                         //check timers
66                         _trigger_timer();
67                         return 0;
68                 case IDT_SOCKET:
69                         iosock = engine_win32_get_iosock(wParam);
70                         events = WSAGETSELECTEVENT(lParam);
71                         
72                         if((events & FD_CONNECT)) {
73                                 int err;
74                                 if((err = WSAGETSELECTERROR(lParam)))
75                                         iosocket_events_callback(iosock, err, 0);
76                                 else
77                                         iosocket_events_callback(iosock, 0, 1);
78                         } else
79                                 iosocket_events_callback(iosock, (events & (FD_READ | FD_ACCEPT | FD_CLOSE)) != 0, (events & FD_WRITE) != 0);
80                         return 0;
81                 case WM_QUIT:
82                         return 0;
83                 }
84         }
85         return DefWindowProc(hWnd, uMsg, wParam, lParam);
86 }
87
88 static int engine_win32_init() {
89         WNDCLASSEX wcx;
90         HINSTANCE hinst;
91         WSADATA wsadata;
92         
93         // Start Windows Sockets.
94         if (WSAStartup(MAKEWORD(2, 0), &wsadata)) {
95                 iolog_trigger(IOLOG_FATAL, "Unable to start Windows Sockets");
96                 return 0;
97         }
98         
99         // Get Windows HINSTANCE.
100         hinst = GetModuleHandle(NULL);
101
102         // Describe and register a window class.
103         memset(&wcx, 0, sizeof(wcx));
104         wcx.cbSize = sizeof(wcx);
105         wcx.lpfnWndProc = engine_win32_wndproc;
106         wcx.hInstance = hinst;
107         wcx.lpszClassName = "IOMultiplexerMainWindow";
108         if (!RegisterClassEx(&wcx))
109                 return 0;
110
111         ioset_window = CreateWindow("IOMultiplexerMainWindow", "IOMultiplexer", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
112         if (!ioset_window)
113                 return 0;
114         return 1;
115 }
116
117 static long engine_win32_events(struct _IOSocket *iosock) {
118         if(iosock->socket_flags & IOSOCKETFLAG_LISTENING)
119                 return FD_ACCEPT;
120         if(iosock->socket_flags & IOSOCKETFLAG_CONNECTING)
121                 return FD_CONNECT;
122         
123         return FD_CLOSE | (iosocket_wants_reads(iosock) ? FD_READ : 0) | (iosocket_wants_writes(iosock) ? FD_WRITE : 0);
124 }
125
126 static void engine_win32_update(struct _IOSocket *iosock) {
127         long events;
128         events = engine_win32_events(iosock);
129         WSAAsyncSelect(iosock->fd, ioset_window, IDT_SOCKET, events);
130 }
131
132 static void engine_win32_add(struct _IOSocket *iosock) {
133         engine_win32_update(iosock);
134 }
135
136 static void engine_win32_remove(struct _IOSocket *iosock) {
137         unsigned long ulong = 0;
138         WSAAsyncSelect(iosock->fd, ioset_window, IDT_SOCKET, 0);
139         ioctlsocket(iosock->fd, FIONBIO, &ulong);
140 }
141
142 static void engine_win32_loop(struct timeval *timeout) {
143         MSG msg;
144         BOOL res;
145         int msec, msec2;
146         struct timeval now;
147         
148         //check timers
149         gettimeofday(&now, NULL);
150         if(iotimer_sorted_descriptors && timeval_is_bigger(now, iotimer_sorted_descriptors->timeout))
151                 _trigger_timer();
152         
153         //get timeout (timer or given timeout)
154         if(iotimer_sorted_descriptors) {
155                 msec = (iotimer_sorted_descriptors->timeout.tv_sec - now.tv_sec) * 1000;
156                 msec += (iotimer_sorted_descriptors->timeout.tv_usec - now.tv_usec) / 1000;
157         }
158         if(timeout) {
159                 msec2 = (timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
160                 if(!iotimer_sorted_descriptors || msec2 < msec)
161                         msec = msec2;
162         } else if(!iotimer_sorted_descriptors)
163                 msec = -1;
164         
165         //set TIMER
166         SetTimer(ioset_window, IDT_TIMER1, 1000, NULL);
167         if(msec > -1)
168                 SetTimer(ioset_window, IDT_TIMER2, msec, NULL);
169         
170         //GetMessage system call
171         res = GetMessage(&msg, NULL, 0, 0);
172         
173         //kill TIMER
174         KillTimer(ioset_window, IDT_TIMER1);
175         if(msec > -1)
176                 KillTimer(ioset_window, IDT_TIMER2);
177         
178         if (res <=0)
179                 return;
180         else {
181                 TranslateMessage(&msg);
182                 DispatchMessage(&msg);
183         }
184 }
185
186 static void engine_win32_cleanup() {
187         DestroyWindow(ioset_window);
188         ioset_window = NULL;
189         WSACleanup();
190 }
191
192 struct IOEngine engine_win32 = {
193         .name = "win32",
194         .init = engine_win32_init,
195         .add = engine_win32_add,
196         .remove = engine_win32_remove,
197         .update = engine_win32_update,
198         .loop = engine_win32_loop,
199         .cleanup = engine_win32_cleanup,
200 };
201
202 #else
203
204 struct IOEngine engine_win32 = {
205         .name = "win32",
206         .init = NULL,
207         .add = NULL,
208         .remove = NULL,
209         .update = NULL,
210         .loop = NULL,
211         .cleanup = NULL,
212 };
213
214 #endif