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