1 module ws.wm.win32.window;
2 
3 version(Windows):
4 
5 import
6 	std.conv,
7 	std.string,
8 	std.utf,
9 
10 	ws.string,
11 	ws.list,
12 	ws.gui.base,
13 	ws.draw,
14 	ws.wm.win32.api,
15 	ws.wm.win32.wm,
16 	ws.wm;
17 
18 __gshared:
19 
20 
21 class Win32Window: Base {
22 
23 	package {
24 		Mouse.cursor cursor = Mouse.cursor.inherit;
25 		string title;
26 		WindowHandle windowHandle;
27 		GraphicsContext graphicsContext;
28 		List!Event eventQueue;
29 
30 		int antiAliasing = 1;
31 		HDC deviceContext;
32 
33 		alias CB = void delegate(Event);
34 		CB[int] eventHandlers;
35 
36 		int lastX, lastY, jumpX, jumpY;
37 
38 		bool hasMouse;
39 		bool _hasFocus;
40 		
41 	}
42 
43 	this(WindowHandle handle){
44 		windowHandle = handle;
45 	}
46 
47 	this(int w, int h, string t){
48 		initializeHandlers;
49 		title = t;
50 		size = [w, h];
51 		eventQueue = new List!Event;
52 		RECT targetSize = {0, 0, size.x, size.y};
53 		AdjustWindowRect(&targetSize, WS_OVERLAPPEDWINDOW | WS_VISIBLE, false);
54 		windowHandle = CreateWindowExW(
55 			0, wm.windowClass.lpszClassName, title.toUTF16z(),
56 			WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT,
57 			targetSize.right-targetSize.left, targetSize.bottom-targetSize.top,
58 			null, null, wm.getInstance, null
59 		);
60 		if(!windowHandle)
61 			throw new Exception("CreateWindowW failed");
62 		RECT r;
63 		GetWindowRect(windowHandle, &r);
64 		pos = [r.left, r.right];
65 
66 		shouldCreateGraphicsContext();
67 		drawInit;
68 		show();
69 
70 		RAWINPUTDEVICE rawMouseDevice;
71 		rawMouseDevice.usUsagePage = 0x01; 
72 		rawMouseDevice.usUsage = 0x02;
73 		rawMouseDevice.dwFlags = RIDEV_INPUTSINK;   
74 		rawMouseDevice.hwndTarget = windowHandle;
75 		if(!RegisterRawInputDevices(&rawMouseDevice, 1, RAWINPUTDEVICE.sizeof))
76 			throw new Exception("Failed to register RID");
77 		
78 	}
79 
80 	@property
81 	WindowHandle handle(){
82 		return windowHandle;
83 	}
84 
85 	override void show(){
86 		if(!hidden)
87 			return;
88 		ShowWindow(windowHandle, SW_SHOWNORMAL);
89 		UpdateWindow(windowHandle);
90 		activateGraphicsContext();
91 		onKeyboardFocus(true);
92 		super.show;
93 	}
94 
95 	override void hide(){
96 		if(hidden)
97 			return;
98 		DestroyWindow(windowHandle);
99 		super.hide;
100 	}
101 
102 	override void resize(int[2] size){
103 		super.resize(size);
104 		glViewport(0,0,size.w,size.h);
105 		if(draw)
106 			draw.resize(size);
107 	}
108 
109 	void setTitle(string title){
110 		this.title = title;
111 		if(!hidden)
112 			SetWindowTextW(windowHandle, title.toUTF16z());
113 	}
114 
115 	string getTitle(){
116 		wchar[512] str;
117 		int r = GetWindowTextW(windowHandle, str.ptr, str.length);
118 		return to!string(str[0..r]);
119 	}
120 
121 	long getPid(){
122 		DWORD pid;
123 		DWORD threadId = GetWindowThreadProcessId(windowHandle, &pid);
124 		return pid;
125 	}
126 	
127 	@property
128 	override bool hasFocus(){
129 		return _hasFocus;
130 	}
131 	
132 	override void onKeyboardFocus(bool focus){
133 		_hasFocus = focus;
134 	}
135 	
136 	void createGraphicsContext(){
137 		deviceContext = GetDC(windowHandle);
138 		if(!deviceContext)
139 			throw new Exception("window.Show failed: GetDC");
140 		uint formatCount = 0;
141 		int pixelFormat;
142 		int[] iAttribList = [
143 			0x2001, true,
144 			0x2010, true,
145 			0x2011, true,
146 			0x2003, 0x2027,
147 			0x2014, 0x202B,
148 			0x2014, 24,
149 			0x201B, 8,
150 			0x2022, 16,
151 			0x2023, 8,
152 			0x2011, true,
153 			0x2041, antiAliasing > 1 ? true : false,
154 			0x2042, antiAliasing,
155 			0
156 		];
157 		wm.wglChoosePixelFormatARB(deviceContext, iAttribList.ptr, null, 1, &pixelFormat, &formatCount);
158 		if(!formatCount)
159 			throw new Exception(tostring("wglChoosePixelFormatARB failed: ", glGetError()));
160 		SetPixelFormat(deviceContext, pixelFormat, null);
161 		int[] attribs = [
162 			0x2091, 3,
163 			0x2092, 2,
164 			0x9126, 0x00000001,
165 			0
166 		];
167 		graphicsContext = wm.wglCreateContextAttribsARB(deviceContext, null, attribs.ptr);
168 		if(!graphicsContext)
169 			throw new Exception(tostring("wglCreateContextAttribsARB() failed: ", glGetError()));
170 	}
171 
172 	void createGraphicsContextOld(){
173 		PIXELFORMATDESCRIPTOR pfd = {
174 			(PIXELFORMATDESCRIPTOR).sizeof, 1, 4 | 32 | 1, 0, 8, 0,
175 			0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0
176 		};
177 		int pixelFormat = ChoosePixelFormat(deviceContext, &pfd);
178 		SetPixelFormat(deviceContext, pixelFormat, &pfd);
179 		graphicsContext = core.sys.windows.wingdi.wglCreateContext(deviceContext);
180 		wglMakeCurrent(deviceContext, graphicsContext);
181 	}
182 
183 	void shouldCreateGraphicsContext(){
184 		try
185 			createGraphicsContext();
186 		catch
187 			createGraphicsContextOld();
188 		activateGraphicsContext();
189 		DerelictGL3.reload();
190 	}
191 
192 	void makeCurrent(Context context){
193 		if(!wglMakeCurrent(deviceContext, context))
194 			throw new Exception("Failed to activate context, " ~ getLastError());
195 	}
196 
197 	void activateGraphicsContext(){
198 		if(!wm.activeWindow)
199 			wm.activeWindow = this;
200 		makeCurrent(graphicsContext);
201 	}
202 
203 	Context gcShare(){
204 		auto c = wm.wglCreateContextAttribsARB(deviceContext, graphicsContext, null);
205 		if(!c)
206 			throw new Exception("Failed to create shared context, " ~ getLastError());
207 		return c;
208 	}
209 
210 	void swapBuffers(){
211 		if(!wm.activeWindow)
212 			return;
213 		SwapBuffers(deviceContext);
214 	}
215 
216 	void onRawMouse(int x, int y){}
217 
218 	void setActive(){
219 		wm.activeWindow = this;
220 	}
221 
222 	override void setCursor(Mouse.cursor cursor){
223 		version(Windows){
224 			HCURSOR hcur = null;
225 			if(cursor != Mouse.cursor.none)
226 				hcur = LoadCursorW(null, cast(const(wchar)*)MOUSE_CURSOR_TO_HCUR[cast(int)cursor]);
227 			this.cursor = cursor;
228 			SetCursor(hcur);
229 			SetClassLongW(windowHandle, -12, cast(LONG)cast(LONG_PTR)hcur);
230 		}
231 	}
232 
233 	void setCursorPos(int x, int y){
234 		lastX = x;
235 		lastY = y;
236 		POINT p = {cast(long)x, cast(long)y};
237 		ClientToScreen(windowHandle, &p);
238 		SetCursorPos(p.x, p.y);
239 	}
240 
241 
242 	void sendMessage(uint message, WPARAM wpar, LPARAM lpar){
243 		SendMessageA(windowHandle, message, wpar, lpar);
244 	}
245 
246 	/+
247 	void setTop(){
248 		SetForegroundWindow(windowHandle);
249 	}
250 	+/
251 
252 	void drawInit(){
253 		//_draw = new GlDraw;
254 	}
255 
256 	void initializeHandlers(){
257 		eventHandlers = [
258 			/+
259 			WM_INPUT: {
260 				PRAWINPUT pRawInput;
261 				UINT	  bufferSize;
262 				HANDLE	hHeap;
263 				GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, 
264 				&bufferSize, sizeof(RAWINPUTHEADER));
265 				hHeap	 = GetProcessHeap();
266 				pRawInput = (PRAWINPUT)HeapAlloc(hHeap, 0, bufferSize);
267 				if(!pRawInput)
268 					return 0;
269 				GetRawInputData((HRAWINPUT)lParam, RID_INPUT, 
270 				pRawInput, &bufferSize, sizeof(RAWINPUTHEADER));
271 				ParseRawInput(pRawInput);
272 				HeapFree(hHeap, 0, pRawInput);
273 			},
274 			+/
275 			WM_INPUT: (e){
276 				RAWINPUT input;
277 				UINT bufferSize = RAWINPUT.sizeof;
278 				GetRawInputData(cast(HRAWINPUT)e.lpar, RID_INPUT, &input, &bufferSize, RAWINPUTHEADER.sizeof);
279 				//import ws.io; writeln(cast(byte[RAWINPUT.sizeof])input);
280 				/+
281 				if(input.header.dwType == RIM_TYPEMOUSE){
282 					onRawMouse(input.mouse.lLastX, input.mouse.lLastY);
283 				}
284 				+/
285 			},
286 			//WM_PAINT: (e){},
287 			WM_SHOWWINDOW: (e){ onShow; },
288 			WM_CLOSE: (e){ hide; },
289 			WM_SIZE: (e){
290 				resize([LOWORD(e.lpar),HIWORD(e.lpar)]);
291 			},
292 			WM_KEYDOWN: (e){
293 				Keyboard.key c = cast(Keyboard.key)toLower(cast(char)e.wpar);
294 				Keyboard.set(c, true);
295 				onKeyboard(c, true);
296 			},
297 			WM_KEYUP: (e){
298 				auto c = cast(Keyboard.key)toLower(cast(char)e.wpar);
299 				Keyboard.set(c, false);
300 				onKeyboard(c, false);
301 			},
302 			WM_CHAR: (e){
303 				onKeyboard(cast(dchar)e.wpar);
304 			},
305 			WM_ACTIVATE: (e){
306 				onKeyboardFocus(LOWORD(e.wpar) > 0 ? true : false);
307 			},
308 			WM_SETCURSOR: (e){
309 				SetCursor(MOUSE_CURSOR_TO_HCUR[cast(int)cursor]);
310 			},
311 			WM_MOUSEMOVE: (e){
312 				int x = GET_X_LPARAM(e.lpar);
313 				int y = GET_Y_LPARAM(e.lpar);
314 				if(!hasMouse){
315 					TRACKMOUSEEVENT tme = {
316 						TRACKMOUSEEVENT.sizeof, 2, windowHandle, 0xFFFFFFFF
317 					};
318 					TrackMouseEvent(&tme);
319 					onMouseFocus(true);
320 					lastX = x;
321 					lastY = y;
322 					hasMouse = true;
323 				}
324 				onMouseMove(x, size.y-y);
325 				onRawMouse(x-lastX, y-lastY);
326 				lastX = x;
327 				lastY = y;
328 			},
329 			WM_MOUSELEAVE: (e){
330 				hasMouse = false;
331 				onMouseFocus(false);
332 			},
333 			WM_LBUTTONDOWN: (e){
334 				onMouseButton(Mouse.buttonLeft, true, LOWORD(e.lpar), size.y-HIWORD(e.lpar));
335 			},
336 			WM_LBUTTONUP: (e){
337 				onMouseButton(Mouse.buttonLeft, false, LOWORD(e.lpar), size.y-HIWORD(e.lpar));
338 			},
339 			WM_MBUTTONDOWN: (e){
340 				onMouseButton(Mouse.buttonMiddle, true, LOWORD(e.lpar), size.y-HIWORD(e.lpar));
341 			},
342 			WM_MBUTTONUP: (e){
343 				onMouseButton(Mouse.buttonMiddle, false, LOWORD(e.lpar), size.y-HIWORD(e.lpar));
344 			},
345 			WM_RBUTTONDOWN: (e){
346 				onMouseButton(Mouse.buttonRight, true, LOWORD(e.lpar), size.y-HIWORD(e.lpar));
347 			},
348 			WM_RBUTTONUP: (e){
349 				onMouseButton(Mouse.buttonRight, false, LOWORD(e.lpar), size.y-HIWORD(e.lpar));
350 			},
351 			WM_MOUSEWHEEL: (e){
352 				onMouseButton(
353 						GET_WHEEL_DELTA_WPARAM(e.wpar) > 120 ? Mouse.wheelDown : Mouse.wheelUp,
354 						true, LOWORD(e.lpar), size.y-HIWORD(e.lpar)
355 				);
356 			}
357 		];
358 	}
359 
360 	void addEvent(Event e){
361 		eventQueue ~= e;
362 	}
363 
364 	void processEvents(){
365 		foreach(e; eventQueue)
366 			if(e.msg in eventHandlers)
367 				eventHandlers[e.msg](e);
368 		eventQueue.clear;
369 	}
370 
371 }
372 
373 
374 string getLastError(){
375 	DWORD errcode = GetLastError();
376 	if(!errcode)
377 		return "No error";
378 	LPCSTR msgBuf;
379 	DWORD i = FormatMessageA(
380 		cast(uint)(
381 		FORMAT_MESSAGE_ALLOCATE_BUFFER |
382 		FORMAT_MESSAGE_FROM_SYSTEM |
383 		FORMAT_MESSAGE_IGNORE_INSERTS),
384 		null,
385 		errcode,
386 		cast(uint)MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
387 		cast(LPSTR)&msgBuf,
388 		0,
389 		null
390 	);
391 	string text = to!string(msgBuf);
392 	LocalFree(cast(HLOCAL)msgBuf);
393 	return text;
394 }