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