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 }