1 module dlangui.platforms.windows.winapp;
2 
3 version (Windows) {
4 
5 import core.runtime;
6 import win32.windows;
7 import std.string;
8 import std.utf;
9 import std.stdio;
10 import std.algorithm;
11 import std.file;
12 import dlangui.platforms.common.platform;
13 import dlangui.platforms.windows.win32fonts;
14 import dlangui.platforms.windows.win32drawbuf;
15 import dlangui.widgets.styles;
16 import dlangui.widgets.widget;
17 import dlangui.graphics.drawbuf;
18 import dlangui.graphics.images;
19 import dlangui.graphics.fonts;
20 import dlangui.core.logger;
21 
22 version (USE_OPENGL) {
23     import dlangui.graphics.glsupport;
24 }
25 
26 pragma(lib, "gdi32.lib");
27 pragma(lib, "user32.lib");
28 pragma(lib, "libpng15.lib");
29 
30 /// this function should be defined in user application!
31 extern (C) int UIAppMain(string[] args);
32 
33 immutable WIN_CLASS_NAME = "DLANGUI_APP";
34 
35 __gshared HINSTANCE _hInstance;
36 __gshared int _cmdShow;
37 
38 version (USE_OPENGL) {
39     bool setupPixelFormat(HDC hDC)
40     {
41         PIXELFORMATDESCRIPTOR pfd = {
42             PIXELFORMATDESCRIPTOR.sizeof,  /* size */
43             1,                              /* version */
44             PFD_SUPPORT_OPENGL |
45                 PFD_DRAW_TO_WINDOW |
46                 PFD_DOUBLEBUFFER,               /* support double-buffering */
47             PFD_TYPE_RGBA,                  /* color type */
48             16,                             /* prefered color depth */
49             0, 0, 0, 0, 0, 0,               /* color bits (ignored) */
50             0,                              /* no alpha buffer */
51             0,                              /* alpha bits (ignored) */
52             0,                              /* no accumulation buffer */
53             0, 0, 0, 0,                     /* accum bits (ignored) */
54             16,                             /* depth buffer */
55             0,                              /* no stencil buffer */
56             0,                              /* no auxiliary buffers */
57             0,                              /* main layer PFD_MAIN_PLANE */
58             0,                              /* reserved */
59             0, 0, 0,                        /* no layer, visible, damage masks */
60         };
61         int pixelFormat;
62 
63         pixelFormat = ChoosePixelFormat(hDC, &pfd);
64         if (pixelFormat == 0) {
65             Log.e("ChoosePixelFormat failed.");
66             return false;
67         }
68 
69         if (SetPixelFormat(hDC, pixelFormat, &pfd) != TRUE) {
70             Log.e("SetPixelFormat failed.");
71             return false;
72         }
73         return true;
74     }
75 
76     HPALETTE setupPalette(HDC hDC)
77     {
78         import core.stdc.stdlib;
79         HPALETTE hPalette = NULL;
80         int pixelFormat = GetPixelFormat(hDC);
81         PIXELFORMATDESCRIPTOR pfd;
82         LOGPALETTE* pPal;
83         int paletteSize;
84 
85         DescribePixelFormat(hDC, pixelFormat, PIXELFORMATDESCRIPTOR.sizeof, &pfd);
86 
87         if (pfd.dwFlags & PFD_NEED_PALETTE) {
88             paletteSize = 1 << pfd.cColorBits;
89         } else {
90             return null;
91         }
92 
93         pPal = cast(LOGPALETTE*)
94             malloc(LOGPALETTE.sizeof + paletteSize * PALETTEENTRY.sizeof);
95         pPal.palVersion = 0x300;
96         pPal.palNumEntries = cast(ushort)paletteSize;
97 
98         /* build a simple RGB color palette */
99         {
100             int redMask = (1 << pfd.cRedBits) - 1;
101             int greenMask = (1 << pfd.cGreenBits) - 1;
102             int blueMask = (1 << pfd.cBlueBits) - 1;
103             int i;
104 
105             for (i=0; i<paletteSize; ++i) {
106                 pPal.palPalEntry[i].peRed = cast(ubyte)(
107                     (((i >> pfd.cRedShift) & redMask) * 255) / redMask);
108                 pPal.palPalEntry[i].peGreen = cast(ubyte)(
109                     (((i >> pfd.cGreenShift) & greenMask) * 255) / greenMask);
110                 pPal.palPalEntry[i].peBlue = cast(ubyte)(
111                     (((i >> pfd.cBlueShift) & blueMask) * 255) / blueMask);
112                 pPal.palPalEntry[i].peFlags = 0;
113             }
114         }
115 
116         hPalette = CreatePalette(pPal);
117         free(pPal);
118 
119         if (hPalette) {
120             SelectPalette(hDC, hPalette, FALSE);
121             RealizePalette(hDC);
122         }
123 
124         return hPalette;
125     }
126 
127     private __gshared bool DERELICT_GL3_RELOADED = false;
128 }
129 
130 class Win32Window : Window {
131     Win32Platform _platform;
132     HWND _hwnd;
133     version (USE_OPENGL) {
134         HGLRC _hGLRC; // opengl context
135         HPALETTE _hPalette;
136     }
137     string _caption;
138     Win32ColorDrawBuf _drawbuf;
139     bool useOpengl;
140     this(Win32Platform platform, string windowCaption, Window parent) {
141         _platform = platform;
142         _caption = windowCaption;
143         _hwnd = CreateWindow(toUTF16z(WIN_CLASS_NAME),      // window class name
144                             toUTF16z(windowCaption),  // window caption
145                             WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,  // window style
146                             CW_USEDEFAULT,        // initial x position
147                             CW_USEDEFAULT,        // initial y position
148                             CW_USEDEFAULT,        // initial x size
149                             CW_USEDEFAULT,        // initial y size
150                             null,                 // parent window handle
151                             null,                 // window menu handle
152                             _hInstance,           // program instance handle
153                             cast(void*)this);                // creation parameters
154 
155         version (USE_OPENGL) {
156             import derelict.opengl3.wgl;
157 
158             /* initialize OpenGL rendering */
159             HDC hDC = GetDC(_hwnd);
160 
161             if (!DERELICT_GL3_RELOADED || openglEnabled) {
162                 if (setupPixelFormat(hDC)) {
163                     _hPalette = setupPalette(hDC);
164                     _hGLRC = wglCreateContext(hDC);
165                     if (_hGLRC) {
166                         wglMakeCurrent(hDC, _hGLRC);
167 
168                         if (!DERELICT_GL3_RELOADED) {
169                             // run this code only once
170                             DERELICT_GL3_RELOADED = true;
171                             try {
172                                 import derelict.opengl3.gl3;
173                                 DerelictGL3.reload();
174                                 // successful
175                                 if (initShaders()) {
176                                     setOpenglEnabled();
177                                     useOpengl = true;
178                                 } else {
179                                     Log.e("Failed to compile shaders");
180                                 }
181                             } catch (Exception e) {
182                                 Log.e("Derelict exception", e);
183                             }
184                         } else {
185 						    if (initShaders()) {
186 							    setOpenglEnabled();
187 							    useOpengl = true;
188 						    } else {
189 							    Log.e("Failed to compile shaders");
190 						    }
191                         }
192                     }
193                 } else {
194                     Log.e("Pixelformat failed");
195                     // disable GL
196                     DERELICT_GL3_RELOADED = true;
197                 }
198             }
199         }
200     }
201 
202     version (USE_OPENGL) {
203         private void paintUsingOpenGL() {
204             // hack to stop infinite WM_PAINT loop
205             PAINTSTRUCT ps;
206             HDC hdc2 = BeginPaint(_hwnd, &ps);
207             EndPaint(_hwnd, &ps);
208 
209 
210             import derelict.opengl3.gl3;
211             import derelict.opengl3.wgl;
212             import dlangui.graphics.gldrawbuf;
213             //Log.d("onPaint() start drawing opengl viewport: ", _dx, "x", _dy);
214             //PAINTSTRUCT ps;
215             //HDC hdc = BeginPaint(_hwnd, &ps);
216             //scope(exit) EndPaint(_hwnd, &ps);
217             HDC hdc = GetDC(_hwnd);
218             wglMakeCurrent(hdc, _hGLRC);
219             glDisable(GL_DEPTH_TEST);
220             glViewport(0, 0, _dx, _dy);
221 			float a = 1.0f;
222 			float r = ((_backgroundColor >> 16) & 255) / 255.0f;
223 			float g = ((_backgroundColor >> 8) & 255) / 255.0f;
224 			float b = ((_backgroundColor >> 0) & 255) / 255.0f;
225             glClearColor(r, g, b, a);
226             glClear(GL_COLOR_BUFFER_BIT);
227 
228             GLDrawBuf buf = new GLDrawBuf(_dx, _dy, false);
229             buf.beforeDrawing();
230             static if (false) {
231                 // for testing for render
232                 buf.fillRect(Rect(100, 100, 200, 200), 0x704020);
233                 buf.fillRect(Rect(40, 70, 100, 120), 0x000000);
234                 buf.fillRect(Rect(80, 80, 150, 150), 0x80008000); // green
235                 drawableCache.get("exit").drawTo(buf, Rect(300, 100, 364, 164));
236                 drawableCache.get("btn_default_pressed").drawTo(buf, Rect(300, 200, 564, 264));
237                 drawableCache.get("btn_default_normal").drawTo(buf, Rect(300, 0, 400, 50));
238                 drawableCache.get("btn_default_selected").drawTo(buf, Rect(0, 0, 100, 50));
239                 FontRef fnt = currentTheme.font;
240                 fnt.drawText(buf, 40, 40, "Some Text 1234567890 !@#$^*", 0x80FF0000);
241             } else {
242                 onDraw(buf);
243             }
244             buf.afterDrawing();
245             SwapBuffers(hdc);
246             wglMakeCurrent(hdc, null);
247         }
248     }
249 
250     ~this() {
251         Log.d("Window destructor");
252         version (USE_OPENGL) {
253             import derelict.opengl3.wgl;
254             if (_hGLRC) {
255 			    uninitShaders();
256                 wglMakeCurrent (null, null) ;
257                 wglDeleteContext(_hGLRC);
258                 _hGLRC = null;
259             }
260         }
261         if (_hwnd)
262             DestroyWindow(_hwnd);
263         _hwnd = null;
264     }
265     Win32ColorDrawBuf getDrawBuf() {
266         //RECT rect;
267         //GetClientRect(_hwnd, &rect);
268         //int dx = rect.right - rect.left;
269         //int dy = rect.bottom - rect.top;
270         if (_drawbuf is null)
271             _drawbuf = new Win32ColorDrawBuf(_dx, _dy);
272         else
273             _drawbuf.resize(_dx, _dy);
274         return _drawbuf;
275     }
276     override void show() {
277         ShowWindow(_hwnd, _cmdShow);
278         UpdateWindow(_hwnd);
279     }
280     override @property string windowCaption() {
281         return _caption;
282     }
283     override @property void windowCaption(string caption) {
284         _caption = caption;
285         SetWindowTextW(_hwnd, toUTF16z(_caption));
286     }
287     void onCreate() {
288         Log.d("Window onCreate");
289         _platform.onWindowCreated(_hwnd, this);
290     }
291     void onDestroy() {
292         Log.d("Window onDestroy");
293         _platform.onWindowDestroyed(_hwnd, this);
294     }
295 
296     private void paintUsingGDI() {
297         PAINTSTRUCT ps;
298         HDC hdc = BeginPaint(_hwnd, &ps);
299         scope(exit) EndPaint(_hwnd, &ps);
300 
301         Win32ColorDrawBuf buf = getDrawBuf();
302         buf.fill(_backgroundColor);
303         onDraw(buf);
304         buf.drawTo(hdc, 0, 0);
305     }
306 
307     void onPaint() {
308         Log.d("onPaint()");
309         long paintStart = currentTimeMillis;
310         version (USE_OPENGL) {
311             if (useOpengl && _hGLRC) {
312                 paintUsingOpenGL();
313             } else {
314                 paintUsingGDI();
315             }
316         } else {
317             paintUsingGDI();
318         }
319         long paintEnd = currentTimeMillis;
320         Log.d("WM_PAINT handling took ", paintEnd - paintStart, " ms");
321     }
322 
323 	protected ButtonDetails _lbutton;
324 	protected ButtonDetails _mbutton;
325 	protected ButtonDetails _rbutton;
326 
327     private bool _mouseTracking;
328 	private bool onMouse(uint message, uint flags, short x, short y) {
329 		//Log.d("Win32 Mouse Message ", message, " flags=", flags, " x=", x, " y=", y);
330         MouseButton button = MouseButton.None;
331         MouseAction action = MouseAction.ButtonDown;
332         ButtonDetails * pbuttonDetails = null;
333         short wheelDelta = 0;
334         switch (message) {
335             case WM_MOUSEMOVE:
336                 action = MouseAction.Move;
337                 break;
338             case WM_LBUTTONDOWN:
339                 action = MouseAction.ButtonDown;
340                 button = MouseButton.Left;
341                 pbuttonDetails = &_lbutton;
342                 break;
343             case WM_RBUTTONDOWN:
344                 action = MouseAction.ButtonDown;
345                 button = MouseButton.Right;
346                 pbuttonDetails = &_rbutton;
347                 break;
348             case WM_MBUTTONDOWN:
349                 action = MouseAction.ButtonDown;
350                 button = MouseButton.Middle;
351                 pbuttonDetails = &_mbutton;
352                 break;
353             case WM_LBUTTONUP:
354                 action = MouseAction.ButtonUp;
355                 button = MouseButton.Left;
356                 pbuttonDetails = &_lbutton;
357                 break;
358             case WM_RBUTTONUP:
359                 action = MouseAction.ButtonUp;
360                 button = MouseButton.Right;
361                 pbuttonDetails = &_rbutton;
362                 break;
363             case WM_MBUTTONUP:
364                 action = MouseAction.ButtonUp;
365                 button = MouseButton.Middle;
366                 pbuttonDetails = &_mbutton;
367                 break;
368             case WM_MOUSELEAVE:
369                 Log.d("WM_MOUSELEAVE");
370                 action = MouseAction.Leave;
371                 break;
372             case WM_MOUSEWHEEL:
373                 {
374                     action = MouseAction.Wheel;
375                     wheelDelta = (cast(short)(flags >> 16)) / 120;
376                     POINT pt;
377                     pt.x = x;
378                     pt.y = y;
379                     ScreenToClient(_hwnd, &pt);
380                     x = cast(short)pt.x;
381                     y = cast(short)pt.y;
382                 }
383                 break;
384             default:
385                 // unsupported event
386                 return false;
387         }
388         if (action == MouseAction.ButtonDown) {
389             pbuttonDetails.down(x, y, cast(ushort)flags);
390         } else if (action == MouseAction.ButtonUp) {
391             pbuttonDetails.up(x, y, cast(ushort)flags);
392         }
393         if (((message == WM_MOUSELEAVE) || (x < 0 || y < 0 || x > _dx || y > _dy)) && _mouseTracking) {
394             action = MouseAction.Leave;
395             Log.d("WM_MOUSELEAVE - releasing capture");
396             _mouseTracking = false;
397             ReleaseCapture();
398         }
399         if (message != WM_MOUSELEAVE && !_mouseTracking) {
400             if (x >=0 && y >= 0 && x < _dx && y < _dy) {
401                 Log.d("Setting capture");
402                 _mouseTracking = true;
403                 SetCapture(_hwnd);
404             }
405         }
406         MouseEvent event = new MouseEvent(action, button, cast(ushort)flags, x, y, wheelDelta);
407         event.lbutton = _lbutton;
408         event.rbutton = _rbutton;
409         event.mbutton = _mbutton;
410 		bool res = dispatchMouseEvent(event);
411         if (res) {
412             Log.d("Calling update() after mouse event");
413             update();
414         }
415         return res;
416 	}
417 
418 
419     protected uint _keyFlags;
420 
421     protected void updateKeyFlags(KeyAction action, KeyFlag flag) {
422         if (action == KeyAction.KeyDown)
423             _keyFlags |= flag;
424         else
425             _keyFlags &= ~flag;
426     }
427 
428     bool onKey(KeyAction action, uint keyCode, int repeatCount, dchar character = 0) {
429         KeyEvent event;
430         if (action == KeyAction.KeyDown || action == KeyAction.KeyUp) {
431             switch(keyCode) {
432                 case KeyCode.SHIFT:
433                     updateKeyFlags(action, KeyFlag.Shift);
434                     break;
435                 case KeyCode.CONTROL:
436                     updateKeyFlags(action, KeyFlag.Control);
437                     break;
438                 case KeyCode.ALT:
439                     updateKeyFlags(action, KeyFlag.Alt);
440                     break;
441                 default:
442                     break;
443             }
444             event = new KeyEvent(action, keyCode, _keyFlags);
445         } else if (action == KeyAction.Text && character != 0) {
446             dchar[] text;
447             text ~= character;
448             event = new KeyEvent(action, 0, _keyFlags, cast(dstring)text);
449         }
450         bool res = false;
451         if (event !is null) {
452             res = dispatchKeyEvent(event);
453         }
454         if (res) {
455             Log.d("Calling update() after key event");
456             update();
457         }
458         return res;
459     }
460 
461     /// request window redraw
462     override void invalidate() {
463         InvalidateRect(_hwnd, null, FALSE);
464 		UpdateWindow(_hwnd);
465     }
466 
467     /// after drawing, call to schedule redraw if animation is active
468     override void scheduleAnimation() {
469         invalidate();
470     }
471 
472 }
473 
474 class Win32Platform : Platform {
475     this() {
476     }
477     bool registerWndClass() {
478         //MSG  msg;
479         WNDCLASS wndclass;
480 
481         wndclass.style         = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
482         wndclass.lpfnWndProc   = &WndProc;
483         wndclass.cbClsExtra    = 0;
484         wndclass.cbWndExtra    = 0;
485         wndclass.hInstance     = _hInstance;
486         wndclass.hIcon         = LoadIcon(null, IDI_APPLICATION);
487         wndclass.hCursor       = LoadCursor(null, IDC_ARROW);
488         wndclass.hbrBackground = cast(HBRUSH)GetStockObject(WHITE_BRUSH);
489         wndclass.lpszMenuName  = null;
490         wndclass.lpszClassName = toUTF16z(WIN_CLASS_NAME);
491 
492         if(!RegisterClass(&wndclass))
493         {
494             return false;
495         }
496         return true;
497     }
498     override int enterMessageLoop() {
499         MSG  msg;
500         while (GetMessage(&msg, null, 0, 0))
501         {
502             TranslateMessage(&msg);
503             DispatchMessage(&msg);
504         }
505         return msg.wParam;
506     }
507     private Win32Window[ulong] _windowMap;
508     /// add window to window map
509     void onWindowCreated(HWND hwnd, Win32Window window) {
510         _windowMap[cast(ulong)hwnd] = window;
511     }
512     /// remove window from window map, returns true if there are some more windows left in map
513     bool onWindowDestroyed(HWND hwnd, Win32Window window) {
514         Win32Window wnd = getWindow(hwnd);
515         if (wnd) {
516             _windowMap.remove(cast(ulong)hwnd);
517             destroy(window);
518         }
519         return _windowMap.length > 0;
520     }
521     /// returns number of currently active windows
522     @property int windowCount() {
523         return cast(int)_windowMap.length;
524     }
525     /// returns window instance by HWND
526     Win32Window getWindow(HWND hwnd) {
527         if ((cast(ulong)hwnd) in _windowMap)
528             return _windowMap[cast(ulong)hwnd];
529         return null;
530     }
531     override Window createWindow(string windowCaption, Window parent) {
532         return new Win32Window(this, windowCaption, parent);
533     }
534 }
535 
536 extern(Windows)
537 int DLANGUIWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
538             LPSTR lpCmdLine, int nCmdShow) {
539     int result;
540 
541     try
542     {
543         Runtime.initialize();
544         result = myWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
545         //Runtime.terminate();
546     }
547     catch (Throwable e) // catch any uncaught exceptions
548     {
549         MessageBox(null, toUTF16z(e.toString()), "Error",
550                     MB_OK | MB_ICONEXCLAMATION);
551         result = 0;     // failed
552     }
553 
554     return result;
555 }
556 
557 string[] splitCmdLine(string line) {
558     string[] res;
559     int start = 0;
560     bool insideQuotes = false;
561     for (int i = 0; i <= line.length; i++) {
562         char ch = i < line.length ? line[i] : 0;
563         if (ch == '\"') {
564             if (insideQuotes) {
565                 if (i > start)
566                     res ~= line[start .. i];
567                 start = i + 1;
568                 insideQuotes = false;
569             } else {
570                 insideQuotes = true;
571                 start = i + 1;
572             }
573         } else if (!insideQuotes && (ch == ' ' || ch == '\t' || ch == 0)) {
574             if (i > start) {
575                 res ~= line[start .. i];
576             }
577             start = i + 1;
578         }
579     }
580     return res;
581 }
582 
583 private __gshared Win32Platform platform;
584 
585 int myWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow)
586 {
587 	setFileLogger(std.stdio.File("ui.log", "w"));
588 	setLogLevel(LogLevel.Trace);
589     Log.d("myWinMain()");
590     string basePath = exePath();
591     Log.i("Current executable: ", exePath());
592     string cmdline = fromStringz(lpCmdLine);
593     Log.i("Command line: ", cmdline);
594     string[] args = splitCmdLine(cmdline);
595     Log.i("Command line params: ", args);
596 
597     _cmdShow = iCmdShow;
598     _hInstance = hInstance;
599 
600     platform = new Win32Platform();
601     if (!platform.registerWndClass()) {
602         MessageBoxA(null, "This program requires Windows NT!", "DLANGUI App".toStringz, MB_ICONERROR);
603         return 0;
604     }
605     Platform.setInstance(platform);
606 
607 
608     if (true) {
609         /// testing freetype font manager
610         import dlangui.graphics.ftfonts;
611         import win32.shlobj;
612         FreeTypeFontManager ftfontMan = new FreeTypeFontManager();
613         string fontsPath = "c:\\Windows\\Fonts\\";
614         static if (false) { // SHGetFolderPathW not found in shell32.lib
615             WCHAR szPath[MAX_PATH];
616             const CSIDL_FLAG_NO_ALIAS = 0x1000;
617             const CSIDL_FLAG_DONT_UNEXPAND = 0x2000;
618             if(SUCCEEDED(SHGetFolderPathW(NULL,
619                                           CSIDL_FONTS|CSIDL_FLAG_NO_ALIAS|CSIDL_FLAG_DONT_UNEXPAND,
620                                           NULL,
621                                           0,
622                                           szPath.ptr)))
623             {
624                 fontsPath = toUTF8(fromWStringz(szPath));
625             }
626         }
627         ftfontMan.registerFont(fontsPath ~ "arial.ttf", FontFamily.SansSerif, "Arial", false, FontWeight.Normal);
628         ftfontMan.registerFont(fontsPath ~ "arialbd.ttf", FontFamily.SansSerif, "Arial", false, FontWeight.Bold);
629         ftfontMan.registerFont(fontsPath ~ "arialbi.ttf", FontFamily.SansSerif, "Arial", true, FontWeight.Bold);
630         ftfontMan.registerFont(fontsPath ~ "ariali.ttf", FontFamily.SansSerif, "Arial", true, FontWeight.Normal);
631         ftfontMan.registerFont(fontsPath ~ "cour.ttf", FontFamily.MonoSpace, "Courier New", false, FontWeight.Normal);
632         ftfontMan.registerFont(fontsPath ~ "courbd.ttf", FontFamily.MonoSpace, "Courier New", false, FontWeight.Bold);
633         ftfontMan.registerFont(fontsPath ~ "courbi.ttf", FontFamily.MonoSpace, "Courier New", true, FontWeight.Bold);
634         ftfontMan.registerFont(fontsPath ~ "couri.ttf", FontFamily.MonoSpace, "Courier New", true, FontWeight.Normal);
635         ftfontMan.registerFont(fontsPath ~ "times.ttf", FontFamily.Serif, "Times New Roman", false, FontWeight.Normal);
636         ftfontMan.registerFont(fontsPath ~ "timesbd.ttf", FontFamily.Serif, "Times New Roman", false, FontWeight.Bold);
637         ftfontMan.registerFont(fontsPath ~ "timesbi.ttf", FontFamily.Serif, "Times New Roman", true, FontWeight.Bold);
638         ftfontMan.registerFont(fontsPath ~ "timesi.ttf", FontFamily.Serif, "Times New Roman", true, FontWeight.Normal);
639         FontManager.instance = ftfontMan;
640     }
641 
642     // use Win32 font manager
643     if (FontManager.instance is null) {
644 	    //Win32FontManager fontMan = new Win32FontManager();
645 	    FontManager.instance = new Win32FontManager();
646     }
647 
648 	currentTheme = createDefaultTheme();
649 
650     version (USE_OPENGL) {
651         import derelict.opengl3.gl3;
652         DerelictGL3.load();
653 
654         // just to check OpenGL context
655         Log.i("Trying to setup OpenGL context");
656         Win32Window tmpWindow = new Win32Window(platform, "", null);
657         destroy(tmpWindow);
658         if (openglEnabled)
659             Log.i("OpenGL support is enabled");
660         else
661             Log.w("OpenGL support is disabled");
662         // process messages
663         platform.enterMessageLoop();
664     }
665 
666     // Load versions 1.2+ and all supported ARB and EXT extensions.
667 
668     Log.i("Entering UIAppMain: ", args);
669     int result = UIAppMain(args);
670     Log.i("UIAppMain returned ", result);
671     return result;
672 }
673 
674 
675 extern(Windows)
676 LRESULT WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
677 {
678     HDC hdc;
679     RECT rect;
680 
681     void * p = cast(void*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
682     Win32Window windowParam = p is null ? null : cast(Win32Window)(p);
683     Win32Window window = platform.getWindow(hwnd);
684     if (windowParam !is null && window !is null)
685         assert(window is windowParam);
686     if (window is null && windowParam !is null) {
687         Log.e("Cannot find window in map by HWND");
688     }
689 
690     switch (message)
691     {
692         case WM_CREATE:
693             {
694                 CREATESTRUCT * pcreateStruct = cast(CREATESTRUCT*)lParam;
695                 window = cast(Win32Window)pcreateStruct.lpCreateParams;
696                 void * ptr = cast(void*) window;
697                 SetWindowLongPtr(hwnd, GWLP_USERDATA, cast(LONG_PTR)ptr);
698                 window._hwnd = hwnd;
699                 window.onCreate();
700             }
701             return 0;
702         case WM_DESTROY:
703             if (window !is null)
704                 window.onDestroy();
705             if (platform.windowCount == 0)
706                 PostQuitMessage(0);
707             return 0;
708         case WM_WINDOWPOSCHANGED:
709             {
710                 if (window !is null) {
711                     WINDOWPOS * pos = cast(WINDOWPOS*)lParam;
712                     GetClientRect(hwnd, &rect);
713                     int dx = rect.right - rect.left;
714                     int dy = rect.bottom - rect.top;
715                     //window.onResize(pos.cx, pos.cy);
716                     window.onResize(dx, dy);
717                     InvalidateRect(hwnd, null, FALSE);
718                 }
719             }
720             return 0;
721         case WM_ERASEBKGND:
722             // processed
723             return 1;
724         case WM_PAINT:
725             {
726                 if (window !is null)
727                     window.onPaint();
728             }
729             return 0; // processed
730         case WM_MOUSELEAVE:
731 		case WM_MOUSEMOVE:
732 		case WM_LBUTTONDOWN:
733 		case WM_MBUTTONDOWN:
734 		case WM_RBUTTONDOWN:
735 		case WM_LBUTTONUP:
736 		case WM_MBUTTONUP:
737 		case WM_RBUTTONUP:
738         case WM_MOUSEWHEEL:
739 			if (window !is null) {
740 				if (window.onMouse(message, cast(uint)wParam, cast(short)(lParam & 0xFFFF), cast(short)((lParam >> 16) & 0xFFFF)))
741                     return 0; // processed
742             }
743             // not processed - default handling
744             return DefWindowProc(hwnd, message, wParam, lParam);
745         case WM_KEYDOWN:
746         case WM_KEYUP:
747 			if (window !is null) {
748                 int repeatCount = lParam & 0xFFFF;
749 				if (window.onKey(message == WM_KEYDOWN ? KeyAction.KeyDown : KeyAction.KeyUp, wParam, repeatCount))
750                     return 0; // processed
751                 return 0;
752             }
753             break;
754         case WM_UNICHAR:
755 			if (window !is null) {
756                 int repeatCount = lParam & 0xFFFF;
757 				if (window.onKey(KeyAction.Text, wParam, repeatCount, wParam == UNICODE_NOCHAR ? 0 : wParam))
758                     return 1; // processed
759                 return 1;
760             }
761             break;
762         case WM_GETMINMAXINFO:
763         case WM_NCCREATE:
764         case WM_NCCALCSIZE:
765         default:
766             //Log.d("Unhandled message ", message);
767             break;
768     }
769 
770     return DefWindowProc(hwnd, message, wParam, lParam);
771 }
772 
773 //===========================================
774 // end of version(Windows)
775 //===========================================
776 } 
777 
778