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