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