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