1 module ws.wm.win32.wm;
2 
3 version(Windows):
4 
5 import
6 	std.utf,
7 	std.string,
8 	std.conv,
9 	
10 	ws.list,
11 	ws.log,
12 	ws.gui.input,
13 	ws.gui.point,
14 	ws.wm.win32.api,
15 	ws.wm.win32.window,
16 	ws.wm.baseWindowManager,
17 	ws.wm;
18 
19 __gshared:
20 
21 
22 class Win32WindowManager: BaseWindowManager {
23 
24 	T_wglChoosePixelFormatARB wglChoosePixelFormatARB;
25 	T_wglCreateContextAttribsARB wglCreateContextAttribsARB;
26 
27 	HINSTANCE appInstance;
28 	WNDCLASSW windowClass = {0};
29 
30 	void load(string s)(){
31 		auto ptr = core.sys.windows.wingdi.wglGetProcAddress(s);
32 		if(!ptr)
33 			throw new Exception("failed to get function \"" ~ s ~ "\"");
34 		mixin(s ~ " = cast(typeof(" ~ s ~ "))ptr;");
35 	}
36 
37 	this(){
38 		super();
39 		DerelictGL3.load();
40 		appInstance = GetModuleHandleW(null);
41 		
42 		windowClass.lpfnWndProc = cast(WNDPROC)&internalEvents;
43 		windowClass.hInstance = appInstance;
44 		windowClass.hIcon = LoadIconW(null, IDI_APPLICATION);
45 		windowClass.hCursor = LoadCursorW(null, IDC_ARROW);
46 		windowClass.hbrBackground = cast(HBRUSH)GetStockObject(BLACK_BRUSH);
47 		windowClass.lpszClassName = "wm::windowClass".toUTF16z();
48 		windowClass.style = CS_OWNDC;
49 		RegisterClassW(&windowClass);
50 		// the following is solely to retrieve wglChoosePixelFormat && wglCreateContext
51 		HWND dummyWindow = CreateWindowExA(
52 				0, "STATIC", "", WS_POPUP | WS_DISABLED,
53 				0, 0, 1, 1, null, null, appInstance, null
54 		);
55 		PIXELFORMATDESCRIPTOR dummyFormatDescriptor = {
56 			(PIXELFORMATDESCRIPTOR).sizeof, 1, 4 | 32 | 1, 0, 8, 0,
57 			0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0
58 		};
59 		HDC dummyDeviceContext = GetDC(dummyWindow);
60 		int pixelFormat = ChoosePixelFormat(dummyDeviceContext, &dummyFormatDescriptor);
61 		SetPixelFormat(dummyDeviceContext, pixelFormat, &dummyFormatDescriptor);
62 		HGLRC dummyContext = wglCreateContext(dummyDeviceContext);
63 		wglMakeCurrent(dummyDeviceContext, dummyContext);
64 		try {
65 			load!"wglChoosePixelFormatARB"();
66 			load!"wglCreateContextAttribsARB"();
67 		} catch (Exception e)
68 			throw new Exception("OpenGL 3.3 not supported");
69 		wglMakeCurrent(null, null);
70 		wglDeleteContext(dummyContext);
71 		ReleaseDC(dummyWindow, dummyDeviceContext);
72 		DestroyWindow(dummyWindow);
73 	}
74 
75 	HINSTANCE getInstance(){
76 		return appInstance;
77 	}
78 
79 	Window[] systemWindows(){
80 		Window[] list;
81 		HWND h = GetTopWindow(null);
82 		while(h){
83 			list ~= new Win32Window(h);
84 			h = GetWindow(h, 2);
85 		}
86 		return list;
87 	}
88 
89 	void processEvents(){
90 		MSG msg;
91 		while(PeekMessageA(&msg, null, 0, 0, PM_REMOVE)){
92 			TranslateMessage(&msg);
93 			DispatchMessageA(&msg);
94 		}
95 		foreach(e; eventQueue){
96 			e();
97 		}
98 		eventQueue = [];
99 	}
100 
101 	long[2] getCursorPos(){
102 		POINT point;
103 		GetCursorPos(&point);
104 		return [point.x, point.y];
105 	}
106 
107 	Win32Window findWindow(string title){
108 		HWND window = FindWindowW(null, title.toUTF16z());
109 		if(!window)
110 			throw new WindowNotFound("Could not find window \"" ~ title ~ "\"");
111 		return new Win32Window(window);
112 	}
113 
114 	bool isKeyDown(Keyboard.key key){
115 		return GetKeyState(cast(int)key) < 0;
116 	}
117 
118 }
119 
120 
121 protected:
122 
123 	static HCURSOR getCursor(int i){
124 		return cast(LPWSTR)(cast(DWORD)(cast(WORD)i));
125 	}
126 
127 	HCURSOR[] MOUSE_CURSOR_TO_HCUR = [
128 		getCursor(32512), // IDC_ARROW
129 		getCursor(32516), // IDC_UPARROW
130 
131 		getCursor(32513), // IDC_BEAM
132 
133 		getCursor(32646), // IDC_SIZEALL
134 		getCursor(32645), // IDC_SIZENS
135 		getCursor(32644), // IDC_SIZEWE
136 		getCursor(32642), // IDC_SIZENWSE
137 		getCursor(32643), // IDC_SIZENESW
138 		getCursor(32643), // IDC_SIZENESW
139 		getCursor(32642), // IDC_SIZENWSE
140 
141 		getCursor(32649), // IDC_HAND
142 
143 		getCursor(32512), // IDC_ARROW
144 		null,
145 	];
146 
147 	void delegate()[] eventQueue;
148 
149 	void delegate() translateEvent(Win32Window window, UINT msg, WPARAM wpar, LPARAM lpar){
150 		switch(msg){
151 			case WM_INPUT:
152 				import ws.log;
153 				byte[] bytes;
154 				UINT bufferSize = RAWINPUT.sizeof;
155 
156 				GetRawInputData(cast(HRAWINPUT)lpar, RID_INPUT, NULL, &bufferSize, RAWINPUTHEADER.sizeof);
157 				bytes.length = bufferSize;
158 
159 				if(GetRawInputData(cast(HRAWINPUT)lpar, RID_INPUT, bytes.ptr, &bufferSize, RAWINPUTHEADER.sizeof) == cast(UINT)-1)
160 					return {};
161 				auto input = (cast(RAWINPUT[])bytes)[0];
162 				//import ws.io; writeln(cast(byte[RAWINPUT.sizeof])input);
163 				if(input.header.dwType == RIM_TYPEMOUSE){
164 					if(input.mouse.usFlags & MOUSE_MOVE_ABSOLUTE){
165 						Log.info("absolute");
166 					}else{
167 						if(input.mouse.lLastX || input.mouse.lLastY)
168 							return { window.onRawMouse(input.mouse.lLastX, input.mouse.lLastY); };
169 					}
170 				}
171 				break;
172 			//case WM_PAINT:
173 			case WM_SHOWWINDOW:
174 					return { window.onShow; };
175 			case WM_CLOSE:
176 					return { window.onDestroy; };
177 			case WM_SIZE:
178 				return {
179 					window.resized([LOWORD(lpar),HIWORD(lpar)]);
180 				};
181 			case WM_KEYDOWN:
182 				return {
183 					Keyboard.key c = cast(Keyboard.key)toLower(cast(char)wpar);
184 					Keyboard.set(c, true);
185 					window.onKeyboard(c, true);
186 				};
187 			case WM_KEYUP:
188 				return {
189 					auto c = cast(Keyboard.key)toLower(cast(char)wpar);
190 					Keyboard.set(c, false);
191 					window.onKeyboard(c, false);
192 				};
193 			case WM_CHAR:
194 				return {
195 					window.onKeyboard(cast(dchar)wpar);
196 				};
197 			case WM_ACTIVATE:
198 				return {
199 					window.onKeyboardFocus(LOWORD(wpar) > 0 ? true : false);
200 				};
201 			case WM_SETCURSOR:
202 				return {
203 					SetCursor(MOUSE_CURSOR_TO_HCUR[cast(int)window.cursor]);
204 				};
205 			case WM_MOUSEMOVE:
206 				int x = GET_X_LPARAM(lpar);
207 				int y = GET_Y_LPARAM(lpar);
208 				if(!window.hasMouse){
209 					TRACKMOUSEEVENT tme = {
210 						TRACKMOUSEEVENT.sizeof, 2, window.windowHandle, 0xFFFFFFFF
211 					};
212 					TrackMouseEvent(&tme);
213 					window.onMouseFocus(true);
214 					window.hasMouse = true;
215 				}
216 				return {
217 					window.onMouseMove(x, window.size.y-y);
218 				};
219 			case WM_MOUSELEAVE:
220 				return {
221 					window.hasMouse = false;
222 					window.onMouseFocus(false);
223 				};
224 			case WM_LBUTTONDOWN:
225 				return {
226 					window.onMouseButton(Mouse.buttonLeft, true, LOWORD(lpar), window.size.y-HIWORD(lpar));
227 				};
228 			case WM_LBUTTONUP:
229 				return {
230 					window.onMouseButton(Mouse.buttonLeft, false, LOWORD(lpar), window.size.y-HIWORD(lpar));
231 				};
232 			case WM_MBUTTONDOWN:
233 				return {
234 					window.onMouseButton(Mouse.buttonMiddle, true, LOWORD(lpar), window.size.y-HIWORD(lpar));
235 				};
236 			case WM_MBUTTONUP:
237 				return {
238 					window.onMouseButton(Mouse.buttonMiddle, false, LOWORD(lpar), window.size.y-HIWORD(lpar));
239 				};
240 			case WM_RBUTTONDOWN:
241 				return {
242 					window.onMouseButton(Mouse.buttonRight, true, LOWORD(lpar), window.size.y-HIWORD(lpar));
243 				};
244 			case WM_RBUTTONUP:
245 				return {
246 					window.onMouseButton(Mouse.buttonRight, false, LOWORD(lpar), window.size.y-HIWORD(lpar));
247 				};
248 			case WM_MOUSEWHEEL:
249 				return {
250 					window.onMouseButton(
251 						GET_WHEEL_DELTA_WPARAM(wpar) > 120 ? Mouse.wheelDown : Mouse.wheelUp,
252 						true, LOWORD(lpar), window.size.y-HIWORD(lpar)
253 				);
254 			};
255 			default:break;
256 		}
257 		return {};
258 	}
259 
260 	extern(Windows)
261 	static LRESULT internalEvents(HWND window, UINT msg, WPARAM wpar, LPARAM lpar) nothrow {
262 		try {
263 			foreach(w; cast(List!Win32Window)wm.windows){
264 				if(w.handle == window){
265 					eventQueue ~= translateEvent(w, msg, wpar, lpar);
266 					if(msg == WM_CLOSE)
267 						return 0;
268 				}
269 			}
270 		} catch(Throwable)
271 			assert(0);
272 		return DefWindowProcW(window, msg, wpar, lpar);
273 	}
274