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