1 // Written in the D programming language. 2 3 /** 4 5 This module contains implementation of Win32 platform support 6 7 Provides Win32Window and Win32Platform classes. 8 9 Usually you don't need to use this module directly. 10 11 12 Synopsis: 13 14 ---- 15 import dlangui.platforms.windows.winapp; 16 ---- 17 18 Copyright: Vadim Lopatin, 2014 19 License: Boost License 1.0 20 Authors: Vadim Lopatin, coolreader.org@gmail.com 21 */ 22 module dlangui.platforms.windows.winapp; 23 24 public import dlangui.core.config; 25 26 static if (BACKEND_WIN32): 27 28 import core.runtime; 29 import core.sys.windows.windows; 30 import std.string; 31 import std.utf; 32 import std.stdio; 33 import std.algorithm; 34 import std.file; 35 import dlangui.platforms.common.platform; 36 import dlangui.platforms.windows.win32fonts; 37 import dlangui.platforms.windows.win32drawbuf; 38 import dlangui.widgets.styles; 39 import dlangui.widgets.widget; 40 import dlangui.graphics.drawbuf; 41 import dlangui.graphics.images; 42 import dlangui.graphics.fonts; 43 import dlangui.core.logger; 44 import dlangui.core.files; 45 46 static if (ENABLE_OPENGL) { 47 import dlangui.graphics.glsupport; 48 } 49 50 // specify debug=DebugMouseEvents for logging mouse handling 51 // specify debug=DebugRedraw for logging drawing and layouts handling 52 // specify debug=DebugKeys for logging of key events 53 54 pragma(lib, "gdi32.lib"); 55 pragma(lib, "user32.lib"); 56 57 /// this function should be defined in user application! 58 extern (C) int UIAppMain(string[] args); 59 60 immutable WIN_CLASS_NAME = "DLANGUI_APP"; 61 62 /* This is a pretty dirty hack to get multisampling to work */ 63 private __gshared bool isInitialized = false; 64 65 __gshared HINSTANCE _hInstance; 66 __gshared int _cmdShow; 67 68 // TODO: Encapsulate this better 69 private string GetErrorMessage(DWORD error) @trusted 70 { 71 char[] err = new char[256]; 72 err[0 .. $] = 0; 73 int chars = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, 74 MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), err.ptr, 255, NULL); 75 return cast(string) err[0 .. chars]; 76 } 77 78 static if (ENABLE_OPENGL) { 79 80 // WGL stuff 81 82 // WGL_ARB_pixel_format 83 enum WGL_DRAW_TO_WINDOW_ARB = 0x2001; 84 enum WGL_DRAW_TO_BITMAP_ARB = 0x2002; 85 enum WGL_ACCELERATION_ARB = 0x2003; 86 enum WGL_SUPPORT_GDI_ARB = 0x200F; 87 enum WGL_SUPPORT_OPENGL_ARB = 0x2010; 88 enum WGL_DOUBLE_BUFFER_ARB = 0x2011; 89 enum WGL_STEREO_ARB = 0x2012; 90 enum WGL_PIXEL_TYPE_ARB = 0x2013; 91 enum WGL_COLOR_BITS_ARB = 0x2014; 92 enum WGL_DEPTH_BITS_ARB = 0x2022; 93 enum WGL_STENCIL_BITS_ARB = 0x2023; 94 95 enum WGL_NO_ACCELERATION_ARB = 0x2025; 96 enum WGL_GENERIC_ACCELERATION_ARB = 0x2026; 97 enum WGL_FULL_ACCELERATION_ARB = 0x2027; 98 99 enum WGL_TYPE_RGBA_ARB = 0x202B; 100 enum WGL_TYPE_COLORINDEX_ARB = 0x202C; 101 102 // WGL_ARB_create_context_profile 103 enum WGL_CONTEXT_MAJOR_VERSION_ARB = 0x2091; 104 enum WGL_CONTEXT_MINOR_VERSION_ARB = 0x2092; 105 enum WGL_CONTEXT_FLAGS_ARB = 0x2094; 106 enum WGL_CONTEXT_PROFILE_MASK_ARB = 0x9126; 107 108 // WGL_CONTEXT_FLAGS bits 109 enum WGL_CONTEXT_DEBUG_BIT_ARB = 0x0001; 110 enum WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB = 0x0002; 111 112 // WGL_CONTEXT_PROFILE_MASK_ARB bits 113 enum WGL_CONTEXT_CORE_PROFILE_BIT_ARB = 0x00000001; 114 enum WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB = 0x00000002; 115 116 enum GL_NUM_EXTENSIONS = 0x821D; 117 118 enum WGL_ALPHA_BITS_ARB = 0x201B; 119 120 enum WGL_SAMPLE_BUFFERS_ARB = 0x2041; 121 enum WGL_SAMPLES_ARB = 0x2042; 122 123 bool setupPixelFormat(HDC hDC, int multisamples = 0) 124 { 125 PIXELFORMATDESCRIPTOR pfd = { 126 PIXELFORMATDESCRIPTOR.sizeof, /* size */ 127 1, /* version */ 128 PFD_SUPPORT_OPENGL | 129 PFD_DRAW_TO_WINDOW | 130 PFD_DOUBLEBUFFER, /* support double-buffering */ 131 PFD_TYPE_RGBA, /* color type */ 132 24, /* prefered color depth */ 133 0, 0, 0, 0, 0, 0, /* color bits (ignored) */ 134 0, /* no alpha buffer */ 135 0, /* alpha bits (ignored) */ 136 0, /* no accumulation buffer */ 137 0, 0, 0, 0, /* accum bits (ignored) */ 138 16, /* depth buffer */ 139 0, /* no stencil buffer */ 140 0, /* no auxiliary buffers */ 141 0, /* main layer PFD_MAIN_PLANE */ 142 0, /* reserved */ 143 0, 0, 0, /* no layer, visible, damage masks */ 144 }; 145 int pixelFormat; 146 147 pixelFormat = multisamples > 0 ? sharedGLContext.multisampleFormat(hDC, multisamples) : ChoosePixelFormat(hDC, &pfd); 148 if (pixelFormat == 0) { 149 Log.e("ChoosePixelFormat failed."); 150 return false; 151 } 152 153 if (SetPixelFormat(hDC, pixelFormat, &pfd) != TRUE) { 154 Log.e("SetPixelFormat failed."); 155 return false; 156 } 157 return true; 158 } 159 160 HPALETTE setupPalette(HDC hDC) 161 { 162 import core.stdc.stdlib; 163 HPALETTE hPalette = NULL; 164 int pixelFormat = GetPixelFormat(hDC); 165 PIXELFORMATDESCRIPTOR pfd; 166 LOGPALETTE* pPal; 167 int paletteSize; 168 169 DescribePixelFormat(hDC, pixelFormat, PIXELFORMATDESCRIPTOR.sizeof, &pfd); 170 171 if (pfd.dwFlags & PFD_NEED_PALETTE) { 172 paletteSize = 1 << pfd.cColorBits; 173 } else { 174 return null; 175 } 176 177 pPal = cast(LOGPALETTE*) 178 malloc(LOGPALETTE.sizeof + paletteSize * PALETTEENTRY.sizeof); 179 pPal.palVersion = 0x300; 180 pPal.palNumEntries = cast(ushort)paletteSize; 181 182 /* build a simple RGB color palette */ 183 { 184 int redMask = (1 << pfd.cRedBits) - 1; 185 int greenMask = (1 << pfd.cGreenBits) - 1; 186 int blueMask = (1 << pfd.cBlueBits) - 1; 187 int i; 188 189 for (i=0; i<paletteSize; ++i) { 190 pPal.palPalEntry[i].peRed = cast(ubyte)( 191 (((i >> pfd.cRedShift) & redMask) * 255) / redMask); 192 pPal.palPalEntry[i].peGreen = cast(ubyte)( 193 (((i >> pfd.cGreenShift) & greenMask) * 255) / greenMask); 194 pPal.palPalEntry[i].peBlue = cast(ubyte)( 195 (((i >> pfd.cBlueShift) & blueMask) * 255) / blueMask); 196 pPal.palPalEntry[i].peFlags = 0; 197 } 198 } 199 200 hPalette = CreatePalette(pPal); 201 free(pPal); 202 203 if (hPalette) { 204 SelectPalette(hDC, hPalette, FALSE); 205 RealizePalette(hDC); 206 } 207 208 return hPalette; 209 } 210 211 private __gshared bool BINDBC_GL3_RELOADED = false; // is this even used? 212 } 213 214 const uint CUSTOM_MESSAGE_ID = WM_USER + 1; 215 216 static if (ENABLE_OPENGL) { 217 218 alias PFNWGLCHOOSEPIXELFORMATARBPROC = extern(C) BOOL function(HDC hdc, const(int)* attributes, const(FLOAT)* fAttributes, UINT maxFormats, int* pixelFormat, UINT *numFormats); 219 PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB; 220 221 alias PFNWGLCREATECONTEXTATTRIBSARBPROC = extern(C) HGLRC function(HDC hdc, HGLRC hShareContext, const int *attribList); 222 PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB; 223 224 /// Shared opengl context helper 225 struct SharedGLContext { 226 import bindbc.opengl; 227 228 HGLRC _hGLRC; // opengl context 229 HPALETTE _hPalette; 230 bool _error; 231 /// Init OpenGL context, if not yet initialized 232 bool init(HDC hDC) { 233 if (_hGLRC) { 234 // just setup pixel format 235 if (setupPixelFormat(hDC)) { 236 Log.i("OpenGL context already exists. Setting pixel format."); 237 } else { 238 Log.e("Cannot setup pixel format"); 239 } 240 return true; 241 } 242 if (_error) 243 return false; 244 if (setupPixelFormat(hDC)) { 245 _hPalette = setupPalette(hDC); 246 _hGLRC = wglCreateContext(hDC); 247 if (_hGLRC) { 248 bind(hDC); 249 wglChoosePixelFormatARB = cast(PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB"); 250 unbind(hDC); 251 return true; 252 } else { 253 _error = true; 254 return false; 255 } 256 } else { 257 Log.e("Cannot setup pixel format"); 258 _error = true; 259 return false; 260 } 261 } 262 263 bool initGLBindings(HDC hDC) 264 { 265 bind(hDC); 266 scope(exit) unbind(hDC); 267 bool initialized = initGLSupport(Platform.instance.GLVersionMajor < 3); 268 if (!initialized) { 269 uninit(); 270 Log.e("Failed to init OpenGL shaders"); 271 _error = true; 272 return false; 273 } 274 return true; 275 } 276 277 /// A helper function to reinit a context to use multisampling 278 bool reinit(HDC hDC, int samples) 279 { 280 if(setupPixelFormat(hDC, samples)) 281 { 282 _hPalette = setupPalette(hDC); 283 _hGLRC = wglCreateContext(hDC); 284 if (_hGLRC) { 285 return true; 286 } 287 else 288 { 289 _error = true; 290 return false; 291 } 292 } 293 else 294 { 295 Log.e("Cannot reinit pixel format"); 296 _error = true; 297 return false; 298 } 299 } 300 301 void uninit() { 302 if (_hGLRC) { 303 wglDeleteContext(_hGLRC); 304 _hGLRC = null; 305 } 306 } 307 /// make this context current for DC 308 void bind(HDC hDC) { 309 if (!wglMakeCurrent(hDC, _hGLRC)) { 310 import std.string : format; 311 Log.e("wglMakeCurrent is failed. ", GetErrorMessage(GetLastError())); 312 } 313 } 314 /// make null context current for DC 315 void unbind(HDC hDC) { 316 //wglMakeCurrent(hDC, null); 317 wglMakeCurrent(null, null); 318 } 319 void swapBuffers(HDC hDC) { 320 SwapBuffers(hDC); 321 } 322 323 int multisampleFormat(HDC hdc, int samples) 324 { 325 GLint pixelFormat; 326 BOOL valid; 327 GLuint numFormats; 328 329 float[] fattribs = [0.0f, 0.0f]; 330 331 int[] attribs = 332 [ 333 WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, 334 WGL_SUPPORT_OPENGL_ARB, GL_TRUE, 335 WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, 336 WGL_COLOR_BITS_ARB, 24, 337 WGL_ALPHA_BITS_ARB, 8, 338 WGL_DEPTH_BITS_ARB, 24, 339 WGL_STENCIL_BITS_ARB, 0, 340 WGL_DOUBLE_BUFFER_ARB, GL_TRUE, 341 WGL_SAMPLE_BUFFERS_ARB, GL_TRUE, 342 WGL_SAMPLES_ARB, Platform.instance.multisamples, 343 0 344 ]; 345 346 valid = wglChoosePixelFormatARB(hdc, attribs.ptr, fattribs.ptr, 1, &pixelFormat, &numFormats); 347 if(!valid) 348 { 349 Log.e("wglChoosePixelFormatARB failed. GetLastError=%x".format(GetLastError())); 350 return 0; 351 } 352 353 return pixelFormat; 354 } 355 356 bool createCoreRC(HDC hDC) 357 { 358 int[] attribs = 359 [ 360 WGL_CONTEXT_MAJOR_VERSION_ARB, Platform.instance.GLVersionMajor, 361 WGL_CONTEXT_MINOR_VERSION_ARB, Platform.instance.GLVersionMinor, 362 WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, 363 0 364 ]; 365 bind(hDC); 366 wglCreateContextAttribsARB = cast(PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB"); 367 HGLRC tmpRC = wglCreateContextAttribsARB(hDC, null, attribs.ptr); 368 if(!tmpRC) 369 return false; 370 wglMakeCurrent(hDC, null); 371 wglDeleteContext(_hGLRC); 372 _hGLRC = tmpRC; 373 wglMakeCurrent(hDC, tmpRC); 374 unbind(hDC); 375 return true; 376 } 377 } 378 379 /// OpenGL context to share between windows 380 __gshared SharedGLContext sharedGLContext; 381 } 382 383 interface UnknownWindowMessageHandler { 384 /// return true if message is handled, put return value into result 385 bool onUnknownWindowMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, ref LRESULT result); 386 } 387 388 class Win32Window : Window { 389 Win32Platform _platform; 390 391 HWND _hwnd; 392 dstring _caption; 393 Win32ColorDrawBuf _drawbuf; 394 private Win32Window _w32parent; 395 bool useOpengl; 396 397 /// win32 only - return window handle 398 @property HWND windowHandle() { 399 return _hwnd; 400 } 401 402 this(Win32Platform platform, dstring windowCaption, Window parent, uint flags, uint width = 0, uint height = 0) { 403 _w32parent = cast(Win32Window)parent; 404 HWND parenthwnd = _w32parent ? _w32parent._hwnd : null; 405 _dx = width; 406 _dy = height; 407 if (!_dx) 408 _dx = 600; 409 if (!_dy) 410 _dy = 400; 411 _platform = platform; 412 _caption = windowCaption; 413 _windowState = WindowState.hidden; 414 _flags = flags; 415 uint ws = WS_CLIPCHILDREN | WS_CLIPSIBLINGS; 416 if (flags & WindowFlag.Resizable) 417 ws |= WS_OVERLAPPEDWINDOW; 418 else 419 ws |= WS_OVERLAPPED | WS_CAPTION | WS_CAPTION | WS_BORDER | WS_SYSMENU; 420 //if (flags & WindowFlag.Fullscreen) 421 // ws |= SDL_WINDOW_FULLSCREEN; 422 Rect screenRc = getScreenDimensions(); 423 Log.d("Screen dimensions: ", screenRc); 424 425 int x = CW_USEDEFAULT; 426 int y = CW_USEDEFAULT; 427 428 if (flags & WindowFlag.Fullscreen) { 429 // fullscreen 430 x = screenRc.left; 431 y = screenRc.top; 432 _dx = screenRc.width; 433 _dy = screenRc.height; 434 ws = WS_POPUP; 435 } 436 if (flags & WindowFlag.Borderless) { 437 ws = WS_POPUP | WS_SYSMENU; 438 } 439 440 441 _hwnd = CreateWindowW(toUTF16z(WIN_CLASS_NAME), // window class name 442 toUTF16z(windowCaption), // window caption 443 ws, // window style 444 x, // initial x position 445 y, // initial y position 446 _dx, // initial x size 447 _dy, // initial y size 448 parenthwnd, // parent window handle 449 null, // window menu handle 450 _hInstance, // program instance handle 451 platform.getMultisamples ? cast(void*)null : cast(void*)this); // creation parameters 452 static if (ENABLE_OPENGL) { 453 /* initialize OpenGL rendering */ 454 HDC hDC = GetDC(_hwnd); 455 456 if (openglEnabled) { 457 useOpengl = sharedGLContext.init(hDC); 458 459 if(platform.multisamples != 0) 460 { 461 sharedGLContext.uninit(); 462 ReleaseDC(_hwnd, hDC); 463 464 // Recreate window with multisampling (copy-paste from above) 465 DestroyWindow(_hwnd); 466 _hwnd = CreateWindowW(toUTF16z(WIN_CLASS_NAME), // window class name 467 toUTF16z(windowCaption), // window caption 468 ws, // window style 469 x, // initial x position 470 y, // initial y position 471 _dx, // initial x size 472 _dy, // initial y size 473 parenthwnd, // parent window handle 474 null, // window menu handle 475 _hInstance, // program instance handle 476 cast(void*)this); // creation parameters 477 478 hDC = GetDC(_hwnd); 479 useOpengl = sharedGLContext.reinit(hDC, platform.multisamples); 480 481 if(!sharedGLContext.createCoreRC(hDC)) 482 { 483 Log.d("Unable to create Core OpenGL"); 484 throw new Exception("Unable to create Core OpenGL"); 485 } 486 } 487 } 488 sharedGLContext.initGLBindings(hDC); 489 } 490 491 isInitialized = true; 492 493 RECT rect; 494 GetWindowRect(_hwnd, &rect); 495 handleWindowStateChange(WindowState.unspecified, Rect(rect.left, rect.top, _dx, _dy)); 496 497 if (platform.defaultWindowIcon.length != 0) 498 windowIcon = drawableCache.getImage(platform.defaultWindowIcon); 499 } 500 501 static if (ENABLE_OPENGL) { 502 private void paintUsingOpenGL() { 503 // hack to stop infinite WM_PAINT loop 504 PAINTSTRUCT ps; 505 HDC hdc2 = BeginPaint(_hwnd, &ps); 506 EndPaint(_hwnd, &ps); 507 508 import bindbc.opengl; //3.gl3; 509 import bindbc.opengl; //3.wgl; 510 import dlangui.graphics.gldrawbuf; 511 //Log.d("onPaint() start drawing opengl viewport: ", _dx, "x", _dy); 512 //PAINTSTRUCT ps; 513 //HDC hdc = BeginPaint(_hwnd, &ps); 514 //scope(exit) EndPaint(_hwnd, &ps); 515 HDC hdc = GetDC(_hwnd); 516 sharedGLContext.bind(hdc); 517 //_glSupport = _gl; 518 glDisable(GL_DEPTH_TEST); 519 glViewport(0, 0, _dx, _dy); 520 float a = 1.0f; 521 float r = ((_backgroundColor >> 16) & 255) / 255.0f; 522 float g = ((_backgroundColor >> 8) & 255) / 255.0f; 523 float b = ((_backgroundColor >> 0) & 255) / 255.0f; 524 glClearColor(r, g, b, a); 525 glClear(GL_COLOR_BUFFER_BIT); 526 527 GLDrawBuf buf = new GLDrawBuf(_dx, _dy, false); 528 buf.beforeDrawing(); 529 static if (false) { 530 // for testing for render 531 buf.fillRect(Rect(100, 100, 200, 200), 0x704020); 532 buf.fillRect(Rect(40, 70, 100, 120), 0x000000); 533 buf.fillRect(Rect(80, 80, 150, 150), 0x80008000); // green 534 drawableCache.get("exit").drawTo(buf, Rect(300, 100, 364, 164)); 535 drawableCache.get("btn_default_pressed").drawTo(buf, Rect(300, 200, 564, 264)); 536 drawableCache.get("btn_default_normal").drawTo(buf, Rect(300, 0, 400, 50)); 537 drawableCache.get("btn_default_selected").drawTo(buf, Rect(0, 0, 100, 50)); 538 FontRef fnt = currentTheme.font; 539 fnt.drawText(buf, 40, 40, "Some Text 1234567890 !@#$^*", 0x80FF0000); 540 } else { 541 onDraw(buf); 542 } 543 buf.afterDrawing(); 544 sharedGLContext.swapBuffers(hdc); 545 sharedGLContext.unbind(hdc); 546 destroy(buf); 547 } 548 } 549 550 protected Rect getScreenDimensions() { 551 MONITORINFO monitor_info; 552 monitor_info.cbSize = monitor_info.sizeof; 553 HMONITOR hMonitor; 554 if (_hwnd) { 555 hMonitor = MonitorFromWindow(_hwnd, MONITOR_DEFAULTTONEAREST); 556 } else { 557 hMonitor = MonitorFromPoint(POINT(0,0), MONITOR_DEFAULTTOPRIMARY); 558 } 559 GetMonitorInfo(hMonitor, 560 &monitor_info); 561 Rect res; 562 res.left = monitor_info.rcMonitor.left; 563 res.top = monitor_info.rcMonitor.top; 564 res.right = monitor_info.rcMonitor.right; 565 res.bottom = monitor_info.rcMonitor.bottom; 566 return res; 567 } 568 569 protected bool _destroying; 570 ~this() { 571 debug Log.d("Window destructor"); 572 _destroying = true; 573 if (_drawbuf) { 574 destroy(_drawbuf); 575 _drawbuf = null; 576 } 577 578 /* 579 static if (ENABLE_OPENGL) { 580 import derelict.opengl3.wgl; 581 if (_hGLRC) { 582 //glSupport.uninitShaders(); 583 //destroy(_glSupport); 584 //_glSupport = null; 585 //_gl = null; 586 wglMakeCurrent (null, null) ; 587 wglDeleteContext(_hGLRC); 588 _hGLRC = null; 589 } 590 } 591 */ 592 if (_hwnd) 593 DestroyWindow(_hwnd); 594 _hwnd = null; 595 } 596 597 /// post event to handle in UI thread (this method can be used from background thread) 598 override void postEvent(CustomEvent event) { 599 super.postEvent(event); 600 PostMessageW(_hwnd, CUSTOM_MESSAGE_ID, 0, event.uniqueId); 601 } 602 603 /// set handler for files dropped to app window 604 override @property Window onFilesDropped(void delegate(string[]) handler) { 605 super.onFilesDropped(handler); 606 DragAcceptFiles(_hwnd, handler ? TRUE : FALSE); 607 return this; 608 } 609 610 private long _nextExpectedTimerTs; 611 private UINT_PTR _timerId = 1; 612 613 /// schedule timer for interval in milliseconds - call window.onTimer when finished 614 override protected void scheduleSystemTimer(long intervalMillis) { 615 if (intervalMillis < 10) 616 intervalMillis = 10; 617 long nextts = currentTimeMillis + intervalMillis; 618 if (_timerId && _nextExpectedTimerTs && _nextExpectedTimerTs < nextts + 10) 619 return; // don't reschedule timer, timer event will be received soon 620 if (_hwnd) { 621 //_timerId = 622 SetTimer(_hwnd, _timerId, cast(uint)intervalMillis, null); 623 _nextExpectedTimerTs = nextts; 624 } 625 } 626 627 void handleTimer(UINT_PTR timerId) { 628 //Log.d("handleTimer id=", timerId); 629 if (timerId == _timerId) { 630 KillTimer(_hwnd, timerId); 631 //_timerId = 0; 632 _nextExpectedTimerTs = 0; 633 onTimer(); 634 } 635 } 636 637 /// custom window message handler 638 Signal!UnknownWindowMessageHandler onUnknownWindowMessage; 639 private LRESULT handleUnknownWindowMessage(UINT message, WPARAM wParam, LPARAM lParam) { 640 if (onUnknownWindowMessage.assigned) { 641 LRESULT res; 642 if (onUnknownWindowMessage(_hwnd, message, wParam, lParam, res)) 643 return res; 644 } 645 return DefWindowProc(_hwnd, message, wParam, lParam); 646 } 647 648 Win32ColorDrawBuf getDrawBuf() { 649 //RECT rect; 650 //GetClientRect(_hwnd, &rect); 651 //int dx = rect.right - rect.left; 652 //int dy = rect.bottom - rect.top; 653 if (_drawbuf is null) 654 _drawbuf = new Win32ColorDrawBuf(_dx, _dy); 655 else 656 _drawbuf.resize(_dx, _dy); 657 _drawbuf.resetClipping(); 658 return _drawbuf; 659 } 660 override void show() { 661 if (!_mainWidget) { 662 Log.e("Window is shown without main widget"); 663 _mainWidget = new Widget(); 664 } 665 ReleaseCapture(); 666 if (_mainWidget) { 667 _mainWidget.measure(SIZE_UNSPECIFIED, SIZE_UNSPECIFIED); 668 if (flags & WindowFlag.MeasureSize) 669 resizeWindow(Point(_mainWidget.measuredWidth, _mainWidget.measuredHeight)); 670 else 671 adjustWindowOrContentSize(_mainWidget.measuredWidth, _mainWidget.measuredHeight); 672 } 673 674 adjustPositionDuringShow(); 675 676 if (_flags & WindowFlag.Fullscreen) { 677 Rect rc = getScreenDimensions(); 678 SetWindowPos(_hwnd, HWND_TOPMOST, 0, 0, rc.width, rc.height, SWP_SHOWWINDOW); 679 _windowState = WindowState.fullscreen; 680 } else { 681 ShowWindow(_hwnd, SW_SHOWNORMAL); 682 _windowState = WindowState.normal; 683 } 684 if (_mainWidget) 685 _mainWidget.setFocus(); 686 SetFocus(_hwnd); 687 //UpdateWindow(_hwnd); 688 } 689 690 override @property Window parentWindow() { 691 return _w32parent; 692 } 693 694 override protected void handleWindowActivityChange(bool isWindowActive) { 695 super.handleWindowActivityChange(isWindowActive); 696 } 697 698 override @property bool isActive() { 699 return _hwnd == GetForegroundWindow(); 700 } 701 702 override @property dstring windowCaption() const { 703 return _caption; 704 } 705 706 override @property void windowCaption(dstring caption) { 707 _caption = caption; 708 if (_hwnd) { 709 Log.d("windowCaption ", caption); 710 SetWindowTextW(_hwnd, toUTF16z(_caption)); 711 } 712 } 713 714 /// change window state, position, or size; returns true if successful, false if not supported by platform 715 override bool setWindowState(WindowState newState, bool activate = false, Rect newWindowRect = RECT_VALUE_IS_NOT_SET) { 716 if (!_hwnd) 717 return false; 718 bool res = false; 719 // change state and activate support 720 switch(newState) { 721 case WindowState.unspecified: 722 if (activate) { 723 switch (_windowState) { 724 case WindowState.hidden: 725 // show hidden window 726 ShowWindow(_hwnd, SW_SHOW); 727 res = true; 728 break; 729 case WindowState.normal: 730 ShowWindow(_hwnd, SW_SHOWNORMAL); 731 res = true; 732 break; 733 case WindowState.fullscreen: 734 ShowWindow(_hwnd, SW_SHOWNORMAL); 735 res = true; 736 break; 737 case WindowState.minimized: 738 ShowWindow(_hwnd, SW_SHOWMINIMIZED); 739 res = true; 740 break; 741 case WindowState.maximized: 742 ShowWindow(_hwnd, SW_SHOWMAXIMIZED); 743 res = true; 744 break; 745 default: 746 break; 747 } 748 res = true; 749 } 750 break; 751 case WindowState.maximized: 752 if (_windowState != WindowState.maximized || activate) { 753 ShowWindow(_hwnd, activate ? SW_SHOWMAXIMIZED : SW_MAXIMIZE); 754 res = true; 755 } 756 break; 757 case WindowState.minimized: 758 if (_windowState != WindowState.minimized || activate) { 759 ShowWindow(_hwnd, activate ? SW_SHOWMINIMIZED : SW_MINIMIZE); 760 res = true; 761 } 762 break; 763 case WindowState.hidden: 764 if (_windowState != WindowState.hidden) { 765 ShowWindow(_hwnd, SW_HIDE); 766 res = true; 767 } 768 break; 769 case WindowState.normal: 770 if (_windowState != WindowState.normal || activate) { 771 ShowWindow(_hwnd, activate ? SW_SHOWNORMAL : SW_SHOWNA); // SW_RESTORE 772 res = true; 773 } 774 break; 775 776 default: 777 break; 778 } 779 // change size and/or position 780 bool rectChanged = false; 781 if (newWindowRect != RECT_VALUE_IS_NOT_SET && (newState == WindowState.normal || newState == WindowState.unspecified)) { 782 UINT flags = SWP_NOOWNERZORDER | SWP_NOZORDER; 783 if (!activate) 784 flags |= SWP_NOACTIVATE; 785 if (newWindowRect.top == int.min || newWindowRect.left == int.min) { 786 // no position specified 787 if (newWindowRect.bottom != int.min && newWindowRect.right != int.min) { 788 // change size only 789 SetWindowPos(_hwnd, NULL, 0, 0, newWindowRect.right + 2 * GetSystemMetrics(SM_CXDLGFRAME), newWindowRect.bottom + GetSystemMetrics(SM_CYCAPTION) + 2 * GetSystemMetrics(SM_CYDLGFRAME), flags | SWP_NOMOVE); 790 rectChanged = true; 791 res = true; 792 } 793 } else { 794 if (newWindowRect.bottom != int.min && newWindowRect.right != int.min) { 795 // change size and position 796 SetWindowPos(_hwnd, NULL, newWindowRect.left, newWindowRect.top, newWindowRect.right + 2 * GetSystemMetrics(SM_CXDLGFRAME), newWindowRect.bottom + GetSystemMetrics(SM_CYCAPTION) + 2 * GetSystemMetrics(SM_CYDLGFRAME), flags); 797 rectChanged = true; 798 res = true; 799 } else { 800 // change position only 801 SetWindowPos(_hwnd, NULL, newWindowRect.left, newWindowRect.top, 0, 0, flags | SWP_NOSIZE); 802 rectChanged = true; 803 res = true; 804 } 805 } 806 } 807 808 if (rectChanged) { 809 handleWindowStateChange(newState, Rect(newWindowRect.left == int.min ? _windowRect.left : newWindowRect.left, 810 newWindowRect.top == int.min ? _windowRect.top : newWindowRect.top, newWindowRect.right == int.min ? _windowRect.right : newWindowRect.right, 811 newWindowRect.bottom == int.min ? _windowRect.bottom : newWindowRect.bottom)); 812 } 813 else 814 handleWindowStateChange(newState, RECT_VALUE_IS_NOT_SET); 815 816 return res; 817 } 818 819 void onCreate() { 820 Log.d("Window onCreate"); 821 _platform.onWindowCreated(_hwnd, this); 822 } 823 void onDestroy() { 824 Log.d("Window onDestroy"); 825 _platform.onWindowDestroyed(_hwnd, this); 826 } 827 828 protected bool _closeCalled; 829 /// close window 830 override void close() { 831 if (_closeCalled) 832 return; 833 _closeCalled = true; 834 Log.d("Window.close()"); 835 _platform.closeWindow(this); 836 } 837 838 override protected void handleWindowStateChange(WindowState newState, Rect newWindowRect = RECT_VALUE_IS_NOT_SET) { 839 if (_destroying) 840 return; 841 super.handleWindowStateChange(newState, newWindowRect); 842 } 843 844 HICON _icon; 845 846 uint _cursorType; 847 848 HANDLE[ushort] _cursorCache; 849 850 HANDLE loadCursor(ushort id) { 851 if (id in _cursorCache) 852 return _cursorCache[id]; 853 HANDLE h = LoadCursor(null, MAKEINTRESOURCE(id)); 854 _cursorCache[id] = h; 855 return h; 856 } 857 858 void onSetCursorType() { 859 HANDLE winCursor = null; 860 switch (_cursorType) with(CursorType) 861 { 862 case None: 863 winCursor = null; 864 break; 865 case NotSet: 866 break; 867 case Arrow: 868 winCursor = loadCursor(IDC_ARROW); 869 break; 870 case IBeam: 871 winCursor = loadCursor(IDC_IBEAM); 872 break; 873 case Wait: 874 winCursor = loadCursor(IDC_WAIT); 875 break; 876 case Crosshair: 877 winCursor = loadCursor(IDC_CROSS); 878 break; 879 case WaitArrow: 880 winCursor = loadCursor(IDC_APPSTARTING); 881 break; 882 case SizeNWSE: 883 winCursor = loadCursor(IDC_SIZENWSE); 884 break; 885 case SizeNESW: 886 winCursor = loadCursor(IDC_SIZENESW); 887 break; 888 case SizeWE: 889 winCursor = loadCursor(IDC_SIZEWE); 890 break; 891 case SizeNS: 892 winCursor = loadCursor(IDC_SIZENS); 893 break; 894 case SizeAll: 895 winCursor = loadCursor(IDC_SIZEALL); 896 break; 897 case No: 898 winCursor = loadCursor(IDC_NO); 899 break; 900 case Hand: 901 winCursor = loadCursor(IDC_HAND); 902 break; 903 default: 904 break; 905 } 906 SetCursor(winCursor); 907 } 908 909 /// sets cursor type for window 910 override protected void setCursorType(uint cursorType) { 911 // override to support different mouse cursors 912 _cursorType = cursorType; 913 onSetCursorType(); 914 } 915 916 /// sets window icon 917 @property override void windowIcon(DrawBufRef buf) { 918 if (_icon) 919 DestroyIcon(_icon); 920 _icon = null; 921 ColorDrawBuf icon = cast(ColorDrawBuf)buf.get; 922 if (!icon) { 923 Log.e("Trying to set null icon for window"); 924 return; 925 } 926 Win32ColorDrawBuf resizedicon = new Win32ColorDrawBuf(icon, 32, 32); 927 resizedicon.invertAlpha(); 928 ICONINFO ii; 929 HBITMAP mask = resizedicon.createTransparencyBitmap(); 930 HBITMAP color = resizedicon.destroyLeavingBitmap(); 931 ii.fIcon = TRUE; 932 ii.xHotspot = 0; 933 ii.yHotspot = 0; 934 ii.hbmMask = mask; 935 ii.hbmColor = color; 936 _icon = CreateIconIndirect(&ii); 937 if (_icon) { 938 SendMessageW(_hwnd, WM_SETICON, ICON_SMALL, cast(LPARAM)_icon); 939 SendMessageW(_hwnd, WM_SETICON, ICON_BIG, cast(LPARAM)_icon); 940 } else { 941 Log.e("failed to create icon"); 942 } 943 if (mask) 944 DeleteObject(mask); 945 DeleteObject(color); 946 } 947 948 private void paintUsingGDI() { 949 PAINTSTRUCT ps; 950 HDC hdc = BeginPaint(_hwnd, &ps); 951 scope(exit) EndPaint(_hwnd, &ps); 952 953 Win32ColorDrawBuf buf = getDrawBuf(); 954 buf.fill(_backgroundColor); 955 onDraw(buf); 956 buf.drawTo(hdc, 0, 0); 957 } 958 959 void onPaint() { 960 debug(DebugRedraw) Log.d("onPaint()"); 961 long paintStart = currentTimeMillis; 962 static if (ENABLE_OPENGL) { 963 if (useOpengl && sharedGLContext._hGLRC) { 964 paintUsingOpenGL(); 965 } else { 966 paintUsingGDI(); 967 } 968 } else { 969 paintUsingGDI(); 970 } 971 long paintEnd = currentTimeMillis; 972 debug(DebugRedraw) Log.d("WM_PAINT handling took ", paintEnd - paintStart, " ms"); 973 } 974 975 protected ButtonDetails _lbutton; 976 protected ButtonDetails _mbutton; 977 protected ButtonDetails _rbutton; 978 979 private void updateButtonsState(uint flags) { 980 if (!(flags & MK_LBUTTON) && _lbutton.isDown) 981 _lbutton.reset(); 982 if (!(flags & MK_MBUTTON) && _mbutton.isDown) 983 _mbutton.reset(); 984 if (!(flags & MK_RBUTTON) && _rbutton.isDown) 985 _rbutton.reset(); 986 } 987 988 private bool _mouseTracking; 989 private bool onMouse(uint message, uint flags, short x, short y) { 990 debug(DebugMouseEvents) Log.d("Win32 Mouse Message ", message, " flags=", flags, " x=", x, " y=", y); 991 MouseButton button = MouseButton.None; 992 MouseAction action = MouseAction.ButtonDown; 993 ButtonDetails * pbuttonDetails = null; 994 short wheelDelta = 0; 995 switch (message) { 996 case WM_MOUSEMOVE: 997 action = MouseAction.Move; 998 updateButtonsState(flags); 999 break; 1000 case WM_LBUTTONDOWN: 1001 action = MouseAction.ButtonDown; 1002 button = MouseButton.Left; 1003 pbuttonDetails = &_lbutton; 1004 SetFocus(_hwnd); 1005 break; 1006 case WM_RBUTTONDOWN: 1007 action = MouseAction.ButtonDown; 1008 button = MouseButton.Right; 1009 pbuttonDetails = &_rbutton; 1010 SetFocus(_hwnd); 1011 break; 1012 case WM_MBUTTONDOWN: 1013 action = MouseAction.ButtonDown; 1014 button = MouseButton.Middle; 1015 pbuttonDetails = &_mbutton; 1016 SetFocus(_hwnd); 1017 break; 1018 case WM_LBUTTONUP: 1019 action = MouseAction.ButtonUp; 1020 button = MouseButton.Left; 1021 pbuttonDetails = &_lbutton; 1022 break; 1023 case WM_RBUTTONUP: 1024 action = MouseAction.ButtonUp; 1025 button = MouseButton.Right; 1026 pbuttonDetails = &_rbutton; 1027 break; 1028 case WM_MBUTTONUP: 1029 action = MouseAction.ButtonUp; 1030 button = MouseButton.Middle; 1031 pbuttonDetails = &_mbutton; 1032 break; 1033 case WM_MOUSELEAVE: 1034 debug(DebugMouseEvents) Log.d("WM_MOUSELEAVE"); 1035 action = MouseAction.Leave; 1036 break; 1037 case WM_MOUSEWHEEL: 1038 { 1039 action = MouseAction.Wheel; 1040 wheelDelta = (cast(short)(flags >> 16)) / 120; 1041 POINT pt; 1042 pt.x = x; 1043 pt.y = y; 1044 ScreenToClient(_hwnd, &pt); 1045 x = cast(short)pt.x; 1046 y = cast(short)pt.y; 1047 } 1048 break; 1049 default: 1050 // unsupported event 1051 return false; 1052 } 1053 if (action == MouseAction.ButtonDown) { 1054 pbuttonDetails.down(x, y, cast(ushort)flags); 1055 } else if (action == MouseAction.ButtonUp) { 1056 pbuttonDetails.up(x, y, cast(ushort)flags); 1057 } 1058 if (((message == WM_MOUSELEAVE) || (x < 0 || y < 0 || x >= _dx || y >= _dy)) && _mouseTracking) { 1059 if (!isMouseCaptured() || (!_lbutton.isDown && !_rbutton.isDown && !_mbutton.isDown)) { 1060 action = MouseAction.Leave; 1061 debug(DebugMouseEvents) Log.d("Win32Window.onMouse releasing capture"); 1062 _mouseTracking = false; 1063 ReleaseCapture(); 1064 } 1065 } 1066 if (message != WM_MOUSELEAVE && !_mouseTracking) { 1067 if (x >=0 && y >= 0 && x < _dx && y < _dy) { 1068 debug(DebugMouseEvents) Log.d("Win32Window.onMouse Setting capture"); 1069 _mouseTracking = true; 1070 SetCapture(_hwnd); 1071 } 1072 } 1073 MouseEvent event = new MouseEvent(action, button, cast(ushort)flags, x, y, wheelDelta); 1074 event.lbutton = _lbutton; 1075 event.rbutton = _rbutton; 1076 event.mbutton = _mbutton; 1077 bool res = dispatchMouseEvent(event); 1078 if (res) { 1079 //Log.v("Calling update() after mouse event"); 1080 update(); 1081 } 1082 return res; 1083 } 1084 1085 1086 protected uint _keyFlags; 1087 1088 protected void updateKeyFlags(KeyAction action, KeyFlag flag, uint preserveFlag) { 1089 if (action == KeyAction.KeyDown) 1090 _keyFlags |= flag; 1091 else { 1092 if (preserveFlag && (_keyFlags & preserveFlag) == preserveFlag) { 1093 // e.g. when both lctrl and rctrl are pressed, and lctrl is up, preserve rctrl flag 1094 _keyFlags = (_keyFlags & ~flag) | preserveFlag; 1095 } else { 1096 _keyFlags &= ~flag; 1097 } 1098 } 1099 } 1100 1101 bool onKey(KeyAction action, uint keyCode, int repeatCount, dchar character = 0, bool syskey = false) { 1102 debug(KeyInput) Log.d("enter onKey action=", action, " keyCode=", keyCode, " char=", character, "(", cast(int)character, ")", " syskey=", syskey, " _keyFlags=", "%04x"d.format(_keyFlags)); 1103 KeyEvent event; 1104 if (syskey) 1105 _keyFlags |= KeyFlag.Alt; 1106 //else 1107 // _keyFlags &= ~KeyFlag.Alt; 1108 uint oldFlags = _keyFlags; 1109 if (action == KeyAction.KeyDown || action == KeyAction.KeyUp) { 1110 switch(keyCode) { 1111 case KeyCode.LSHIFT: 1112 updateKeyFlags(action, KeyFlag.LShift, KeyFlag.RShift); 1113 break; 1114 case KeyCode.RSHIFT: 1115 updateKeyFlags(action, KeyFlag.RShift, KeyFlag.LShift); 1116 break; 1117 case KeyCode.LCONTROL: 1118 updateKeyFlags(action, KeyFlag.LControl, KeyFlag.RControl); 1119 break; 1120 case KeyCode.RCONTROL: 1121 updateKeyFlags(action, KeyFlag.RControl, KeyFlag.LControl); 1122 break; 1123 case KeyCode.LALT: 1124 updateKeyFlags(action, KeyFlag.LAlt, KeyFlag.RAlt); 1125 break; 1126 case KeyCode.RALT: 1127 updateKeyFlags(action, KeyFlag.RAlt, KeyFlag.LAlt); 1128 break; 1129 case KeyCode.LWIN: 1130 updateKeyFlags(action, KeyFlag.LMenu, KeyFlag.RMenu); 1131 break; 1132 case KeyCode.RWIN: 1133 updateKeyFlags(action, KeyFlag.RMenu, KeyFlag.LMenu); 1134 break; 1135 //case KeyCode.WIN: 1136 case KeyCode.CONTROL: 1137 case KeyCode.SHIFT: 1138 case KeyCode.ALT: 1139 //case KeyCode.WIN: 1140 break; 1141 default: 1142 updateKeyFlags((GetKeyState(VK_LCONTROL) & 0x8000) != 0 ? KeyAction.KeyDown : KeyAction.KeyUp, KeyFlag.LControl, KeyFlag.RControl); 1143 updateKeyFlags((GetKeyState(VK_RCONTROL) & 0x8000) != 0 ? KeyAction.KeyDown : KeyAction.KeyUp, KeyFlag.RControl, KeyFlag.LControl); 1144 updateKeyFlags((GetKeyState(VK_LSHIFT) & 0x8000) != 0 ? KeyAction.KeyDown : KeyAction.KeyUp, KeyFlag.LShift, KeyFlag.RShift); 1145 updateKeyFlags((GetKeyState(VK_RSHIFT) & 0x8000) != 0 ? KeyAction.KeyDown : KeyAction.KeyUp, KeyFlag.RShift, KeyFlag.LShift); 1146 updateKeyFlags((GetKeyState(VK_LWIN) & 0x8000) != 0 ? KeyAction.KeyDown : KeyAction.KeyUp, KeyFlag.LMenu, KeyFlag.RMenu); 1147 updateKeyFlags((GetKeyState(VK_RWIN) & 0x8000) != 0 ? KeyAction.KeyDown : KeyAction.KeyUp, KeyFlag.RMenu, KeyFlag.LMenu); 1148 updateKeyFlags((GetKeyState(VK_LMENU) & 0x8000) != 0 ? KeyAction.KeyDown : KeyAction.KeyUp, KeyFlag.LAlt, KeyFlag.RAlt); 1149 updateKeyFlags((GetKeyState(VK_RMENU) & 0x8000) != 0 ? KeyAction.KeyDown : KeyAction.KeyUp, KeyFlag.RAlt, KeyFlag.LAlt); 1150 //updateKeyFlags((GetKeyState(VK_LALT) & 0x8000) != 0 ? KeyAction.KeyDown : KeyAction.KeyUp, KeyFlag.LAlt, KeyFlag.RAlt); 1151 //updateKeyFlags((GetKeyState(VK_RALT) & 0x8000) != 0 ? KeyAction.KeyDown : KeyAction.KeyUp, KeyFlag.RAlt, KeyFlag.LAlt); 1152 break; 1153 } 1154 //updateKeyFlags((GetKeyState(VK_CONTROL) & 0x8000) != 0 ? KeyAction.KeyDown : KeyAction.KeyUp, KeyFlag.Control); 1155 //updateKeyFlags((GetKeyState(VK_SHIFT) & 0x8000) != 0 ? KeyAction.KeyDown : KeyAction.KeyUp, KeyFlag.Shift); 1156 //updateKeyFlags((GetKeyState(VK_MENU) & 0x8000) != 0 ? KeyAction.KeyDown : KeyAction.KeyUp, KeyFlag.Alt); 1157 if (keyCode == 0xBF) 1158 keyCode = KeyCode.KEY_DIVIDE; 1159 1160 debug(KeyInput) { 1161 if (oldFlags != _keyFlags) { 1162 debug(KeyInput) Log.d(" flags updated: onKey action=", action, " keyCode=", keyCode, " char=", character, "(", cast(int)character, ")", " syskey=", syskey, " _keyFlags=", "%04x"d.format(_keyFlags)); 1163 } 1164 //if (action == KeyAction.KeyDown) 1165 // Log.d("keydown, keyFlags=", _keyFlags); 1166 } 1167 1168 event = new KeyEvent(action, keyCode, _keyFlags); 1169 } else if (action == KeyAction.Text && character != 0) { 1170 bool ctrlAZKeyCode = (character >= 1 && character <= 26); 1171 if ((_keyFlags & (KeyFlag.Control | KeyFlag.Alt)) && ctrlAZKeyCode) { 1172 event = new KeyEvent(action, KeyCode.KEY_A + character - 1, _keyFlags); 1173 } else { 1174 dchar[] text; 1175 text ~= character; 1176 uint newFlags = _keyFlags; 1177 if ((newFlags & KeyFlag.Alt) && (newFlags & KeyFlag.Control)) { 1178 newFlags &= (~(KeyFlag.LRAlt)) & (~(KeyFlag.LRControl)); 1179 debug(KeyInput) Log.d(" flags updated for text: onKey action=", action, " keyCode=", keyCode, " char=", character, "(", cast(int)character, ")", " syskey=", syskey, " _keyFlags=", "%04x"d.format(_keyFlags)); 1180 } 1181 event = new KeyEvent(action, 0, newFlags, cast(dstring)text); 1182 } 1183 } 1184 bool res = false; 1185 if (event !is null) { 1186 res = dispatchKeyEvent(event); 1187 } 1188 if (res) { 1189 debug(DebugRedraw) Log.d("Calling update() after key event"); 1190 update(); 1191 } 1192 return res; 1193 } 1194 1195 /// request window redraw 1196 override void invalidate() { 1197 InvalidateRect(_hwnd, null, FALSE); 1198 //UpdateWindow(_hwnd); 1199 } 1200 1201 /// after drawing, call to schedule redraw if animation is active 1202 override void scheduleAnimation() { 1203 invalidate(); 1204 } 1205 1206 } 1207 1208 class Win32Platform : Platform { 1209 this() { 1210 } 1211 bool registerWndClass() { 1212 //MSG msg; 1213 WNDCLASSW wndclass; 1214 1215 wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; 1216 wndclass.lpfnWndProc = cast(WNDPROC)&WndProc; 1217 wndclass.cbClsExtra = 0; 1218 wndclass.cbWndExtra = 0; 1219 wndclass.hInstance = _hInstance; 1220 wndclass.hIcon = LoadIcon(null, IDI_APPLICATION); 1221 wndclass.hCursor = LoadCursor(null, IDC_ARROW); 1222 wndclass.hbrBackground = cast(HBRUSH)GetStockObject(WHITE_BRUSH); 1223 wndclass.lpszMenuName = null; 1224 wndclass.lpszClassName = toUTF16z(WIN_CLASS_NAME); 1225 1226 if(!RegisterClassW(&wndclass)) 1227 { 1228 return false; 1229 } 1230 HDC dc = CreateCompatibleDC(NULL); 1231 SCREEN_DPI = GetDeviceCaps(dc, LOGPIXELSY); 1232 DeleteObject(dc); 1233 1234 return true; 1235 } 1236 override int enterMessageLoop() { 1237 MSG msg; 1238 while (GetMessage(&msg, null, 0, 0)) 1239 { 1240 TranslateMessage(&msg); 1241 DispatchMessage(&msg); 1242 destroyClosedWindows(); 1243 } 1244 return cast(int)msg.wParam; 1245 } 1246 1247 private Win32Window[ulong] _windowMap; 1248 private Win32Window[] _windowList; 1249 1250 /// add window to window map 1251 void onWindowCreated(HWND hwnd, Win32Window window) { 1252 Log.v("created window, adding to map"); 1253 _windowMap[cast(ulong)hwnd] = window; 1254 _windowList ~= window; 1255 } 1256 /// remove window from window map, returns true if there are some more windows left in map 1257 bool onWindowDestroyed(HWND hwnd, Win32Window window) { 1258 Log.v("destroyed window, removing from map"); 1259 Win32Window wnd = getWindow(hwnd); 1260 if (wnd) { 1261 _windowMap.remove(cast(ulong)hwnd); 1262 _windowsToDestroy ~= window; 1263 //destroy(window); 1264 } 1265 for (uint i = 0; i < _windowList.length; i++) { 1266 if (window is _windowList[i]) { 1267 for (uint j = i; j + 1 < _windowList.length; j++) 1268 _windowList[j] = _windowList[j + 1]; 1269 _windowList[$ - 1] = null; 1270 _windowList.length--; 1271 break; 1272 } 1273 } 1274 return _windowMap.length > 0; 1275 } 1276 /// returns number of currently active windows 1277 @property int windowCount() { 1278 return cast(int)_windowMap.length; 1279 } 1280 /// returns window instance by HWND 1281 Win32Window getWindow(HWND hwnd) { 1282 if ((cast(ulong)hwnd) in _windowMap) 1283 return _windowMap[cast(ulong)hwnd]; 1284 return null; 1285 } 1286 override Window createWindow(dstring windowCaption, Window parent, uint flags = WindowFlag.Resizable, uint width = 0, uint height = 0) { 1287 Log.d("Platform.createWindow is called"); 1288 width = pointsToPixels(width); 1289 height = pointsToPixels(height); 1290 Log.v("Platform.createWindow : setDefaultLanguageAndThemeIfNecessary"); 1291 setDefaultLanguageAndThemeIfNecessary(); 1292 Log.v("Platform.createWindow : new Win32Window"); 1293 return new Win32Window(this, windowCaption, parent, flags, width, height); 1294 } 1295 1296 /// calls request layout for all windows 1297 override void requestLayout() { 1298 foreach(w; _windowMap) { 1299 w.requestLayout(); 1300 w.invalidate(); 1301 } 1302 } 1303 1304 /// returns true if there is some modal window opened above this window, and this window should not process mouse/key input and should not allow closing 1305 override bool hasModalWindowsAbove(Window w) { 1306 // override in platform specific class 1307 for (uint i = 0; i + 1 < _windowList.length; i++) { 1308 if (_windowList[i] is w) { 1309 for (uint j = i + 1; j < _windowList.length; j++) { 1310 if (_windowList[j].flags & WindowFlag.Modal && _windowList[j].windowState != WindowState.hidden) 1311 return true; 1312 } 1313 return false; 1314 } 1315 } 1316 return false; 1317 } 1318 1319 /// handle theme change: e.g. reload some themed resources 1320 override void onThemeChanged() { 1321 super.onThemeChanged(); 1322 if (currentTheme) 1323 currentTheme.onThemeChanged(); 1324 foreach(w; _windowMap) 1325 w.dispatchThemeChanged(); 1326 } 1327 1328 /// list of windows for deferred destroy in message loop 1329 Win32Window[] _windowsToDestroy; 1330 1331 /// close window 1332 override void closeWindow(Window w) { 1333 Win32Window window = cast(Win32Window)w; 1334 _windowsToDestroy ~= window; 1335 SendMessage(window._hwnd, WM_CLOSE, 0, 0); 1336 //window 1337 } 1338 1339 /// destroy window objects planned for destroy 1340 void destroyClosedWindows() { 1341 foreach(Window w; _windowsToDestroy) { 1342 destroy(w); 1343 } 1344 _windowsToDestroy.length = 0; 1345 } 1346 1347 /// check has clipboard text 1348 override bool hasClipboardText(bool mouseBuffer = false) { 1349 if (mouseBuffer) 1350 return false; 1351 return (IsClipboardFormatAvailable(CF_UNICODETEXT) != 0); 1352 } 1353 1354 /// retrieves text from clipboard (when mouseBuffer == true, use mouse selection clipboard - under linux) 1355 override dstring getClipboardText(bool mouseBuffer = false) { 1356 dstring res = null; 1357 if (mouseBuffer) 1358 return res; // not supporetd under win32 1359 if (!IsClipboardFormatAvailable(CF_UNICODETEXT)) 1360 return res; 1361 if (!OpenClipboard(NULL)) 1362 return res; 1363 1364 HGLOBAL hglb = GetClipboardData(CF_UNICODETEXT); 1365 if (hglb != NULL) 1366 { 1367 LPWSTR lptstr = cast(LPWSTR)GlobalLock(hglb); 1368 if (lptstr != NULL) 1369 { 1370 wstring w = fromWStringz(lptstr); 1371 res = normalizeEndOfLineCharacters(toUTF32(w)); 1372 1373 GlobalUnlock(hglb); 1374 } 1375 } 1376 1377 CloseClipboard(); 1378 //Log.d("getClipboardText(", res, ")"); 1379 return res; 1380 } 1381 1382 /// sets text to clipboard (when mouseBuffer == true, use mouse selection clipboard - under linux) 1383 override void setClipboardText(dstring text, bool mouseBuffer = false) { 1384 //Log.d("setClipboardText(", text, ")"); 1385 if (text.length < 1 || mouseBuffer) 1386 return; 1387 if (!OpenClipboard(NULL)) 1388 return; 1389 EmptyClipboard(); 1390 wstring w = toUTF16(text); 1391 HGLOBAL hglbCopy = GlobalAlloc(GMEM_MOVEABLE, 1392 cast(uint)((w.length + 1) * TCHAR.sizeof)); 1393 if (hglbCopy == NULL) { 1394 CloseClipboard(); 1395 return; 1396 } 1397 LPWSTR lptstrCopy = cast(LPWSTR)GlobalLock(hglbCopy); 1398 for (int i = 0; i < w.length; i++) { 1399 lptstrCopy[i] = w[i]; 1400 } 1401 lptstrCopy[w.length] = 0; 1402 GlobalUnlock(hglbCopy); 1403 SetClipboardData(CF_UNICODETEXT, hglbCopy); 1404 1405 CloseClipboard(); 1406 } 1407 } 1408 1409 extern(Windows) 1410 int DLANGUIWinMain(void* hInstance, void* hPrevInstance, 1411 char* lpCmdLine, int nCmdShow) { 1412 int result; 1413 1414 try { 1415 Runtime.initialize(); 1416 1417 // call SetProcessDPIAware to support HI DPI - fix by Kapps 1418 auto ulib = LoadLibraryA("user32.dll"); 1419 alias SetProcessDPIAwareFunc = int function(); 1420 auto setDpiFunc = cast(SetProcessDPIAwareFunc)GetProcAddress(ulib, "SetProcessDPIAware"); 1421 if(setDpiFunc) // Should never fail, but just in case... 1422 setDpiFunc(); 1423 1424 result = myWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); 1425 // TODO: fix hanging on multithreading app 1426 Runtime.terminate(); 1427 } 1428 catch (Throwable e) // catch any uncaught exceptions 1429 { 1430 MessageBoxW(null, toUTF16z(e.toString()), "Error", 1431 MB_OK | MB_ICONEXCLAMATION); 1432 result = 0; // failed 1433 } 1434 1435 return result; 1436 } 1437 1438 extern(Windows) 1439 int DLANGUIWinMainProfile(string[] args) 1440 { 1441 int result; 1442 1443 try { 1444 // call SetProcessDPIAware to support HI DPI - fix by Kapps 1445 auto ulib = LoadLibraryA("user32.dll"); 1446 alias SetProcessDPIAwareFunc = int function(); 1447 auto setDpiFunc = cast(SetProcessDPIAwareFunc)GetProcAddress(ulib, "SetProcessDPIAware"); 1448 if(setDpiFunc) // Should never fail, but just in case... 1449 setDpiFunc(); 1450 1451 result = myWinMainProfile(args); 1452 } 1453 catch (Throwable e) // catch any uncaught exceptions 1454 { 1455 MessageBoxW(null, toUTF16z(e.toString()), "Error", 1456 MB_OK | MB_ICONEXCLAMATION); 1457 result = 0; // failed 1458 } 1459 1460 return result; 1461 } 1462 1463 /// split command line arg list; prepend with executable file name 1464 string[] splitCmdLine(string line) { 1465 string[] res; 1466 res ~= exeFilename(); 1467 int start = 0; 1468 bool insideQuotes = false; 1469 for (int i = 0; i <= line.length; i++) { 1470 char ch = i < line.length ? line[i] : 0; 1471 if (ch == '\"') { 1472 if (insideQuotes) { 1473 if (i > start) 1474 res ~= line[start .. i]; 1475 start = i + 1; 1476 insideQuotes = false; 1477 } else { 1478 insideQuotes = true; 1479 start = i + 1; 1480 } 1481 } else if (!insideQuotes && (ch == ' ' || ch == '\t' || ch == 0)) { 1482 if (i > start) { 1483 res ~= line[start .. i]; 1484 } 1485 start = i + 1; 1486 } 1487 } 1488 return res; 1489 } 1490 1491 private __gshared Win32Platform w32platform; 1492 1493 static if (ENABLE_OPENGL) { 1494 import bindbc.opengl; 1495 import bindbc.opengl.config : GLSupportVersion = GLSupport; 1496 1497 void initOpenGL() { 1498 try { 1499 Log.d("Loading bindbc-opengl"); 1500 auto glVer = loadOpenGL(); 1501 if(glVer < GLSupportVersion.gl32) { 1502 import std.format : format; 1503 throw new Exception(format!"OpenGL 3.2 or higher is required, got: %s"(glVer)); 1504 } 1505 Log.d("bindbc-opengl - loaded"); 1506 // 1507 //// just to check OpenGL context 1508 //Log.i("Trying to setup OpenGL context"); 1509 //Win32Window tmpWindow = new Win32Window(w32platform, ""d, null, 0); 1510 //destroy(tmpWindow); 1511 //if (openglEnabled) 1512 // Log.i("OpenGL support is enabled"); 1513 //else 1514 // Log.w("OpenGL support is disabled"); 1515 //// process messages 1516 //platform.enterMessageLoop(); 1517 } catch (Exception e) { 1518 Log.e("Exception while trying to init OpenGL", e); 1519 setOpenglEnabled(false); 1520 } 1521 } 1522 } 1523 1524 1525 int myWinMain(void* hInstance, void* hPrevInstance, char* lpCmdLine, int iCmdShow) 1526 { 1527 initLogs(); 1528 1529 Log.d("myWinMain()"); 1530 string basePath = exePath(); 1531 Log.i("Current executable: ", exePath()); 1532 string cmdline = fromStringz(lpCmdLine).dup; 1533 Log.i("Command line: ", cmdline); 1534 string[] args = splitCmdLine(cmdline); 1535 Log.i("Command line params: ", args); 1536 1537 _cmdShow = iCmdShow; 1538 _hInstance = hInstance; 1539 1540 Log.v("Creating platform"); 1541 w32platform = new Win32Platform(); 1542 Log.v("Registering window class"); 1543 if (!w32platform.registerWndClass()) { 1544 MessageBoxA(null, "This program requires Windows NT!", "DLANGUI App".toStringz, MB_ICONERROR); 1545 return 0; 1546 } 1547 Platform.setInstance(w32platform); 1548 1549 DOUBLE_CLICK_THRESHOLD_MS = GetDoubleClickTime(); 1550 1551 Log.v("Initializing font manager"); 1552 if (!initFontManager()) { 1553 Log.e("******************************************************************"); 1554 Log.e("No font files found!!!"); 1555 Log.e("Currently, only hardcoded font paths implemented."); 1556 Log.e("Probably you can modify sdlapp.d to add some fonts for your system."); 1557 Log.e("******************************************************************"); 1558 assert(false); 1559 } 1560 initResourceManagers(); 1561 1562 currentTheme = createDefaultTheme(); 1563 1564 Log.i("Entering UIAppMain: ", args); 1565 int result = -1; 1566 try { 1567 result = UIAppMain(args); 1568 Log.i("UIAppMain returned ", result); 1569 } catch (Exception e) { 1570 Log.e("Abnormal UIAppMain termination"); 1571 Log.e("UIAppMain exception: ", e); 1572 } 1573 1574 releaseResourcesOnAppExit(); 1575 1576 Log.d("Exiting main"); 1577 debug { 1578 APP_IS_SHUTTING_DOWN = true; 1579 import core.memory : GC; 1580 Log.d("Calling GC.collect"); 1581 GC.collect(); 1582 if (DrawBuf.instanceCount) 1583 Log.d("Non-zero DrawBuf instance count when exiting: ", DrawBuf.instanceCount); 1584 } 1585 1586 return result; 1587 } 1588 1589 int myWinMainProfile(string[] args) 1590 { 1591 initLogs(); 1592 1593 Log.d("myWinMain()"); 1594 string basePath = exePath(); 1595 Log.i("Current executable: ", exePath()); 1596 Log.i("Command line params: ", args); 1597 1598 _cmdShow = SW_SHOW; 1599 _hInstance = GetModuleHandle(NULL); 1600 1601 Log.v("Creating platform"); 1602 w32platform = new Win32Platform(); 1603 Log.v("Registering window class"); 1604 if (!w32platform.registerWndClass()) { 1605 MessageBoxA(null, "This program requires Windows NT!", "DLANGUI App".toStringz, MB_ICONERROR); 1606 return 0; 1607 } 1608 Platform.setInstance(w32platform); 1609 1610 DOUBLE_CLICK_THRESHOLD_MS = GetDoubleClickTime(); 1611 1612 Log.v("Initializing font manager"); 1613 if (!initFontManager()) { 1614 Log.e("******************************************************************"); 1615 Log.e("No font files found!!!"); 1616 Log.e("Currently, only hardcoded font paths implemented."); 1617 Log.e("Probably you can modify sdlapp.d to add some fonts for your system."); 1618 Log.e("******************************************************************"); 1619 assert(false); 1620 } 1621 initResourceManagers(); 1622 1623 currentTheme = createDefaultTheme(); 1624 1625 Log.i("Entering UIAppMain: ", args); 1626 int result = -1; 1627 try { 1628 result = UIAppMain(args); 1629 Log.i("UIAppMain returned ", result); 1630 } catch (Exception e) { 1631 Log.e("Abnormal UIAppMain termination"); 1632 Log.e("UIAppMain exception: ", e); 1633 } 1634 1635 releaseResourcesOnAppExit(); 1636 1637 Log.d("Exiting main"); 1638 debug { 1639 APP_IS_SHUTTING_DOWN = true; 1640 import core.memory : GC; 1641 Log.d("Calling GC.collect"); 1642 GC.collect(); 1643 if (DrawBuf.instanceCount) 1644 Log.d("Non-zero DrawBuf instance count when exiting: ", DrawBuf.instanceCount); 1645 } 1646 1647 return result; 1648 } 1649 1650 1651 extern(Windows) 1652 LRESULT WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 1653 { 1654 HDC hdc; 1655 RECT rect; 1656 1657 void * p = cast(void*)GetWindowLongPtr(hwnd, GWLP_USERDATA); 1658 Win32Window windowParam = p is null ? null : cast(Win32Window)(p); 1659 Win32Window window = w32platform.getWindow(hwnd); 1660 if (windowParam !is null && window !is null) 1661 assert(window is windowParam); 1662 if (window is null && windowParam !is null) { 1663 Log.e("Cannot find window in map by HWND"); 1664 } 1665 1666 switch (message) 1667 { 1668 case WM_CREATE: 1669 { 1670 CREATESTRUCT * pcreateStruct = cast(CREATESTRUCT*)lParam; 1671 window = cast(Win32Window)pcreateStruct.lpCreateParams; 1672 if(window !is null) 1673 { 1674 void * ptr = cast(void*) window; 1675 SetWindowLongPtr(hwnd, GWLP_USERDATA, cast(LONG_PTR)ptr); 1676 window._hwnd = hwnd; 1677 window.onCreate(); 1678 } 1679 //window.handleUnknownWindowMessage(message, wParam, lParam); 1680 } 1681 return 0; 1682 case WM_DESTROY: 1683 if (window !is null) { 1684 //window.handleUnknownWindowMessage(message, wParam, lParam); 1685 window.onDestroy(); 1686 } 1687 if (w32platform.windowCount == 0 && isInitialized) 1688 PostQuitMessage(0); 1689 return 0; 1690 case WM_WINDOWPOSCHANGED: 1691 { 1692 if (window !is null) { 1693 1694 if (IsIconic(hwnd)) { 1695 window.handleWindowStateChange(WindowState.minimized); 1696 } 1697 else { 1698 WINDOWPOS * pos = cast(WINDOWPOS*)lParam; 1699 //Log.d("WM_WINDOWPOSCHANGED: ", *pos); 1700 1701 GetClientRect(hwnd, &rect); 1702 int dx = rect.right - rect.left; 1703 int dy = rect.bottom - rect.top; 1704 WindowState state = WindowState.unspecified; 1705 if (IsZoomed(hwnd)) 1706 state = WindowState.maximized; 1707 else if (IsIconic(hwnd)) 1708 state = WindowState.minimized; 1709 else if (IsWindowVisible(hwnd)) 1710 state = WindowState.normal; 1711 else 1712 state = WindowState.hidden; 1713 window.handleWindowStateChange(state, 1714 Rect(pos.x, pos.y, dx, dy)); 1715 if (window.width != dx || window.height != dy) { 1716 window.onResize(dx, dy); 1717 InvalidateRect(hwnd, null, FALSE); 1718 } 1719 } 1720 } 1721 } 1722 return 0; 1723 case WM_ACTIVATE: 1724 { 1725 if (window) { 1726 if (wParam == WA_INACTIVE) 1727 window.handleWindowActivityChange(false); 1728 else if (wParam == WA_ACTIVE || wParam == WA_CLICKACTIVE) 1729 window.handleWindowActivityChange(true); 1730 } 1731 } 1732 return 0; 1733 case CUSTOM_MESSAGE_ID: 1734 if (window !is null) { 1735 window.handlePostedEvent(cast(uint)lParam); 1736 } 1737 return 1; 1738 case WM_ERASEBKGND: 1739 // processed 1740 return 1; 1741 case WM_PAINT: 1742 { 1743 if (window !is null) 1744 window.onPaint(); 1745 } 1746 return 0; // processed 1747 case WM_SETCURSOR: 1748 { 1749 if (window !is null) { 1750 if (LOWORD(lParam) == HTCLIENT) { 1751 window.onSetCursorType(); 1752 return 1; 1753 } 1754 } 1755 } 1756 break; 1757 case WM_MOUSELEAVE: 1758 case WM_MOUSEMOVE: 1759 case WM_LBUTTONDOWN: 1760 case WM_MBUTTONDOWN: 1761 case WM_RBUTTONDOWN: 1762 case WM_LBUTTONUP: 1763 case WM_MBUTTONUP: 1764 case WM_RBUTTONUP: 1765 case WM_MOUSEWHEEL: 1766 if (window !is null) { 1767 if (window.onMouse(message, cast(uint)wParam, cast(short)(lParam & 0xFFFF), cast(short)((lParam >> 16) & 0xFFFF))) 1768 return 0; // processed 1769 } 1770 // not processed - default handling 1771 return DefWindowProc(hwnd, message, wParam, lParam); 1772 case WM_KEYDOWN: 1773 case WM_SYSKEYDOWN: 1774 case WM_KEYUP: 1775 case WM_SYSKEYUP: 1776 if (window !is null) { 1777 int repeatCount = lParam & 0xFFFF; 1778 WPARAM vk = wParam; 1779 WPARAM new_vk = vk; 1780 UINT scancode = (lParam & 0x00ff0000) >> 16; 1781 int extended = (lParam & 0x01000000) != 0; 1782 switch (vk) { 1783 case VK_SHIFT: 1784 new_vk = MapVirtualKey(scancode, 3); //MAPVK_VSC_TO_VK_EX 1785 break; 1786 case VK_CONTROL: 1787 new_vk = extended ? VK_RCONTROL : VK_LCONTROL; 1788 break; 1789 case VK_MENU: 1790 new_vk = extended ? VK_RMENU : VK_LMENU; 1791 break; 1792 default: 1793 // not a key we map from generic to left/right specialized 1794 // just return it. 1795 new_vk = vk; 1796 break; 1797 } 1798 1799 if (window.onKey(message == WM_KEYDOWN || message == WM_SYSKEYDOWN ? KeyAction.KeyDown : KeyAction.KeyUp, cast(uint)new_vk, repeatCount, 0, message == WM_SYSKEYUP || message == WM_SYSKEYDOWN)) 1800 return 0; // processed 1801 } 1802 break; 1803 case WM_UNICHAR: 1804 if (window !is null) { 1805 int repeatCount = lParam & 0xFFFF; 1806 dchar ch = wParam == UNICODE_NOCHAR ? 0 : cast(uint)wParam; 1807 debug(KeyInput) Log.d("WM_UNICHAR ", ch, " (", cast(int)ch, ")"); 1808 if (window.onKey(KeyAction.Text, cast(uint)wParam, repeatCount, ch)) 1809 return 1; // processed 1810 return 1; 1811 } 1812 break; 1813 case WM_CHAR: 1814 if (window !is null) { 1815 int repeatCount = lParam & 0xFFFF; 1816 dchar ch = wParam == UNICODE_NOCHAR ? 0 : cast(uint)wParam; 1817 debug(KeyInput) Log.d("WM_CHAR ", ch, " (", cast(int)ch, ")"); 1818 if (window.onKey(KeyAction.Text, cast(uint)wParam, repeatCount, ch)) 1819 return 1; // processed 1820 return 1; 1821 } 1822 break; 1823 case WM_TIMER: 1824 if (window !is null) { 1825 window.handleTimer(wParam); 1826 return 0; 1827 } 1828 break; 1829 case WM_DROPFILES: 1830 if (window !is null) { 1831 HDROP hdrop = cast(HDROP)wParam; 1832 string[] files; 1833 wchar[] buf; 1834 auto count = DragQueryFileW(hdrop, 0xFFFFFFFF, cast(wchar*)NULL, 0); 1835 for (int i = 0; i < count; i++) { 1836 auto sz = DragQueryFileW(hdrop, i, cast(wchar*)NULL, 0); 1837 buf.length = sz + 2; 1838 sz = DragQueryFileW(hdrop, i, buf.ptr, sz + 1); 1839 files ~= toUTF8(buf[0..sz]); 1840 } 1841 if (files.length) 1842 window.handleDroppedFiles(files); 1843 DragFinish(hdrop); 1844 } 1845 return 0; 1846 case WM_CLOSE: 1847 if (window !is null) { 1848 bool canClose = window.handleCanClose(); 1849 if (!canClose) { 1850 Log.d("WM_CLOSE: canClose is false"); 1851 return 0; // prevent closing 1852 } 1853 Log.d("WM_CLOSE: closing window "); 1854 //destroy(window); 1855 } 1856 // default handler inside DefWindowProc will close window 1857 break; 1858 case WM_GETMINMAXINFO: 1859 case WM_NCCREATE: 1860 case WM_NCCALCSIZE: 1861 default: 1862 //Log.d("Unhandled message ", message); 1863 break; 1864 } 1865 if (window) 1866 return window.handleUnknownWindowMessage(message, wParam, lParam); 1867 return DefWindowProc(hwnd, message, wParam, lParam); 1868 } 1869 1870 //=========================================== 1871 // end of version(Windows) 1872 //=========================================== 1873