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