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