1 module dlangui.platforms.common.startup;
2 
3 import dlangui.core.config;
4 import dlangui.core.events;
5 import dlangui.widgets.styles;
6 import dlangui.graphics.fonts;
7 import dlangui.graphics.resources;
8 import dlangui.widgets.widget;
9 
10 static if (BACKEND_GUI) {
11 import dlangui.graphics.ftfonts;
12 
13 version (Windows) {
14     
15     /// initialize font manager - default implementation
16     /// On win32 - first it tries to init freetype, and falls back to win32 fonts.
17     /// On linux/mac - tries to init freetype with some hardcoded font paths
18     extern(C) bool initFontManager() {
19         import core.sys.windows.windows;
20         import std.utf;
21         import dlangui.platforms.windows.win32fonts;
22         try {
23             /// testing freetype font manager
24             static if (ENABLE_FREETYPE) {
25                 Log.v("Trying to init FreeType font manager");
26                 
27                 import dlangui.graphics.ftfonts;
28                 // trying to create font manager
29                 Log.v("Creating FreeTypeFontManager");
30                 FreeTypeFontManager ftfontMan = new FreeTypeFontManager();
31                 
32                 import core.sys.windows.shlobj;
33                 string fontsPath = "c:\\Windows\\Fonts\\";
34                 static if (true) { // SHGetFolderPathW not found in shell32.lib
35                     WCHAR[MAX_PATH] szPath;
36                     static if (false) {
37                         const CSIDL_FLAG_NO_ALIAS = 0x1000;
38                         const CSIDL_FLAG_DONT_UNEXPAND = 0x2000;
39                         if(SUCCEEDED(SHGetFolderPathW(NULL,
40                                     CSIDL_FONTS|CSIDL_FLAG_NO_ALIAS|CSIDL_FLAG_DONT_UNEXPAND,
41                                     NULL,
42                                     0,
43                                     szPath.ptr)))
44                         {
45                             fontsPath = toUTF8(fromWStringz(szPath));
46                         }
47                     } else {
48                         if (GetWindowsDirectory(szPath.ptr, MAX_PATH - 1)) {
49                             fontsPath = toUTF8(fromWStringz(szPath));
50                             Log.i("Windows directory: ", fontsPath);
51                             fontsPath ~= "\\Fonts\\";
52                             Log.i("Fonts directory: ", fontsPath);
53                         }
54                     }
55                 }
56                 Log.v("Registering fonts");
57                 ftfontMan.registerFont(fontsPath ~ "arial.ttf",     FontFamily.SansSerif, "Arial", false, FontWeight.Normal);
58                 ftfontMan.registerFont(fontsPath ~ "arialbd.ttf",   FontFamily.SansSerif, "Arial", false, FontWeight.Bold);
59                 ftfontMan.registerFont(fontsPath ~ "arialbi.ttf",   FontFamily.SansSerif, "Arial", true, FontWeight.Bold);
60                 ftfontMan.registerFont(fontsPath ~ "ariali.ttf",    FontFamily.SansSerif, "Arial", true, FontWeight.Normal);
61                 ftfontMan.registerFont(fontsPath ~ "cour.ttf",      FontFamily.MonoSpace, "Courier New", false, FontWeight.Normal);
62                 ftfontMan.registerFont(fontsPath ~ "courbd.ttf",    FontFamily.MonoSpace, "Courier New", false, FontWeight.Bold);
63                 ftfontMan.registerFont(fontsPath ~ "courbi.ttf",    FontFamily.MonoSpace, "Courier New", true, FontWeight.Bold);
64                 ftfontMan.registerFont(fontsPath ~ "couri.ttf",     FontFamily.MonoSpace, "Courier New", true, FontWeight.Normal);
65                 ftfontMan.registerFont(fontsPath ~ "times.ttf",     FontFamily.Serif, "Times New Roman", false, FontWeight.Normal);
66                 ftfontMan.registerFont(fontsPath ~ "timesbd.ttf",   FontFamily.Serif, "Times New Roman", false, FontWeight.Bold);
67                 ftfontMan.registerFont(fontsPath ~ "timesbi.ttf",   FontFamily.Serif, "Times New Roman", true, FontWeight.Bold);
68                 ftfontMan.registerFont(fontsPath ~ "timesi.ttf",    FontFamily.Serif, "Times New Roman", true, FontWeight.Normal);
69                 ftfontMan.registerFont(fontsPath ~ "consola.ttf",   FontFamily.MonoSpace, "Consolas", false, FontWeight.Normal);
70                 ftfontMan.registerFont(fontsPath ~ "consolab.ttf",  FontFamily.MonoSpace, "Consolas", false, FontWeight.Bold);
71                 ftfontMan.registerFont(fontsPath ~ "consolai.ttf",  FontFamily.MonoSpace, "Consolas", true, FontWeight.Normal);
72                 ftfontMan.registerFont(fontsPath ~ "consolaz.ttf",  FontFamily.MonoSpace, "Consolas", true, FontWeight.Bold);
73                 ftfontMan.registerFont(fontsPath ~ "verdana.ttf",   FontFamily.SansSerif, "Verdana", false, FontWeight.Normal);
74                 ftfontMan.registerFont(fontsPath ~ "verdanab.ttf",  FontFamily.SansSerif, "Verdana", false, FontWeight.Bold);
75                 ftfontMan.registerFont(fontsPath ~ "verdanai.ttf",  FontFamily.SansSerif, "Verdana", true, FontWeight.Normal);
76                 ftfontMan.registerFont(fontsPath ~ "verdanaz.ttf",  FontFamily.SansSerif, "Verdana", true, FontWeight.Bold);
77                 if (ftfontMan.registeredFontCount()) {
78                     FontManager.instance = ftfontMan;
79                 } else {
80                     Log.w("No fonts registered in FreeType font manager. Disabling FreeType.");
81                     destroy(ftfontMan);
82                 }
83             }
84         } catch (Exception e) {
85             Log.e("Cannot create FreeTypeFontManager - falling back to win32");
86         }
87         
88         // use Win32 font manager
89         if (FontManager.instance is null) {
90             FontManager.instance = new Win32FontManager();
91         }
92         return true;
93     }
94     
95 } else {
96     import dlangui.graphics.ftfonts;
97     bool registerFonts(FreeTypeFontManager ft, string path) {
98         import std.file;
99         if (!exists(path) || !isDir(path))
100             return false;
101         ft.registerFont(path ~ "DejaVuSans.ttf", FontFamily.SansSerif, "DejaVuSans", false, FontWeight.Normal);
102         ft.registerFont(path ~ "DejaVuSans-Bold.ttf", FontFamily.SansSerif, "DejaVuSans", false, FontWeight.Bold);
103         ft.registerFont(path ~ "DejaVuSans-Oblique.ttf", FontFamily.SansSerif, "DejaVuSans", true, FontWeight.Normal);
104         ft.registerFont(path ~ "DejaVuSans-BoldOblique.ttf", FontFamily.SansSerif, "DejaVuSans", true, FontWeight.Bold);
105         ft.registerFont(path ~ "DejaVuSansMono.ttf", FontFamily.MonoSpace, "DejaVuSansMono", false, FontWeight.Normal);
106         ft.registerFont(path ~ "DejaVuSansMono-Bold.ttf", FontFamily.MonoSpace, "DejaVuSansMono", false, FontWeight.Bold);
107         ft.registerFont(path ~ "DejaVuSansMono-Oblique.ttf", FontFamily.MonoSpace, "DejaVuSansMono", true, FontWeight.Normal);
108         ft.registerFont(path ~ "DejaVuSansMono-BoldOblique.ttf", FontFamily.MonoSpace, "DejaVuSansMono", true, FontWeight.Bold);
109         return true;
110     }
111 
112 	string[] findFontsInDirectory(string dir) {
113 		import dlangui.core.files;
114 		import std.file : DirEntry;
115 		DirEntry[] entries;
116         try {
117             entries = listDirectory(dir, AttrFilter.files, ["*.ttf"]);
118         } catch(Exception e) {
119             return null;
120         }
121 
122 		string[] res;
123 		foreach(entry; entries) {
124 			res ~= entry.name;
125 		}
126 		return res;
127 	}
128 
129 	void registerFontsFromDirectory(FreeTypeFontManager ft, string dir) {
130 		string[] fontFiles = findFontsInDirectory(dir);
131 		Log.d("Fonts in ", dir, " : ", fontFiles);
132 		foreach(file; fontFiles)
133 			ft.registerFont(file);
134 	}
135     
136     /// initialize font manager - default implementation
137     /// On win32 - first it tries to init freetype, and falls back to win32 fonts.
138     /// On linux/mac - tries to init freetype with some hardcoded font paths
139     extern(C) bool initFontManager() {
140         FreeTypeFontManager ft = new FreeTypeFontManager();
141         
142         if (!registerFontConfigFonts(ft)) {
143             // TODO: use FontConfig
144             Log.w("No fonts found using FontConfig. Trying hardcoded paths.");
145 			version (Android) {
146 				ft.registerFontsFromDirectory("/system/fonts");
147 			} else {
148 	            ft.registerFonts("/usr/share/fonts/truetype/dejavu/");
149 	            ft.registerFonts("/usr/share/fonts/TTF/");
150 	            ft.registerFonts("/usr/share/fonts/dejavu/");
151 	            ft.registerFonts("/usr/share/fonts/truetype/ttf-dejavu/"); // let it compile on Debian Wheezy
152 			}
153             version(OSX) {
154                 ft.registerFont("/Library/Fonts/Arial.ttf", FontFamily.SansSerif, "Arial", false, FontWeight.Normal, true);
155                 ft.registerFont("/Library/Fonts/Arial Bold.ttf", FontFamily.SansSerif, "Arial", false, FontWeight.Bold, true);
156                 ft.registerFont("/Library/Fonts/Arial Italic.ttf", FontFamily.SansSerif, "Arial", true, FontWeight.Normal, true);
157                 ft.registerFont("/Library/Fonts/Arial Bold Italic.ttf", FontFamily.SansSerif, "Arial", true, FontWeight.Bold, true);
158                 ft.registerFont("/Library/Fonts/Arial.ttf", FontFamily.SansSerif, "Arial", false, FontWeight.Normal, true);
159                 ft.registerFont("/Library/Fonts/Arial Bold.ttf", FontFamily.SansSerif, "Arial", false, FontWeight.Bold, true);
160                 ft.registerFont("/Library/Fonts/Arial Italic.ttf", FontFamily.SansSerif, "Arial", true, FontWeight.Normal, true);
161                 ft.registerFont("/Library/Fonts/Arial Bold Italic.ttf", FontFamily.SansSerif, "Arial", true, FontWeight.Bold, true);
162                 ft.registerFont("/Library/Fonts/Arial Narrow.ttf", FontFamily.SansSerif, "Arial Narrow", false, FontWeight.Normal, true);
163                 ft.registerFont("/Library/Fonts/Arial Narrow Bold.ttf", FontFamily.SansSerif, "Arial Narrow", false, FontWeight.Bold, true);
164                 ft.registerFont("/Library/Fonts/Arial Narrow Italic.ttf", FontFamily.SansSerif, "Arial Narrow", true, FontWeight.Normal, true);
165                 ft.registerFont("/Library/Fonts/Arial Narrow Bold Italic.ttf", FontFamily.SansSerif, "Arial Narrow", true, FontWeight.Bold, true);
166                 ft.registerFont("/Library/Fonts/Courier New.ttf", FontFamily.MonoSpace, "Courier New", false, FontWeight.Normal, true);
167                 ft.registerFont("/Library/Fonts/Courier New Bold.ttf", FontFamily.MonoSpace, "Courier New", false, FontWeight.Bold, true);
168                 ft.registerFont("/Library/Fonts/Courier New Italic.ttf", FontFamily.MonoSpace, "Courier New", true, FontWeight.Normal, true);
169                 ft.registerFont("/Library/Fonts/Courier New Bold Italic.ttf", FontFamily.MonoSpace, "Courier New", true, FontWeight.Bold, true);
170                 ft.registerFont("/Library/Fonts/Georgia.ttf", FontFamily.Serif, "Georgia", false, FontWeight.Normal, true);
171                 ft.registerFont("/Library/Fonts/Georgia Bold.ttf", FontFamily.Serif, "Georgia", false, FontWeight.Bold, true);
172                 ft.registerFont("/Library/Fonts/Georgia Italic.ttf", FontFamily.Serif, "Georgia", true, FontWeight.Normal, true);
173                 ft.registerFont("/Library/Fonts/Georgia Bold Italic.ttf", FontFamily.Serif, "Georgia", true, FontWeight.Bold, true);
174                 ft.registerFont("/Library/Fonts/Comic Sans MS.ttf", FontFamily.SansSerif, "Comic Sans", false, FontWeight.Normal, true);
175                 ft.registerFont("/Library/Fonts/Comic Sans MS Bold.ttf", FontFamily.SansSerif, "Comic Sans", false, FontWeight.Bold, true);
176                 ft.registerFont("/Library/Fonts/Tahoma.ttf", FontFamily.SansSerif, "Tahoma", false, FontWeight.Normal, true);
177                 ft.registerFont("/Library/Fonts/Tahoma Bold.ttf", FontFamily.SansSerif, "Tahoma", false, FontWeight.Bold, true);
178 
179                 ft.registerFont("/Library/Fonts/Microsoft/Arial.ttf", FontFamily.SansSerif, "Arial", false, FontWeight.Normal, true);
180                 ft.registerFont("/Library/Fonts/Microsoft/Arial Bold.ttf", FontFamily.SansSerif, "Arial", false, FontWeight.Bold, true);
181                 ft.registerFont("/Library/Fonts/Microsoft/Arial Italic.ttf", FontFamily.SansSerif, "Arial", true, FontWeight.Normal, true);
182                 ft.registerFont("/Library/Fonts/Microsoft/Arial Bold Italic.ttf", FontFamily.SansSerif, "Arial", true, FontWeight.Bold, true);
183                 ft.registerFont("/Library/Fonts/Microsoft/Calibri.ttf", FontFamily.SansSerif, "Calibri", false, FontWeight.Normal, true);
184                 ft.registerFont("/Library/Fonts/Microsoft/Calibri Bold.ttf", FontFamily.SansSerif, "Calibri", false, FontWeight.Bold, true);
185                 ft.registerFont("/Library/Fonts/Microsoft/Calibri Italic.ttf", FontFamily.SansSerif, "Calibri", true, FontWeight.Normal, true);
186                 ft.registerFont("/Library/Fonts/Microsoft/Calibri Bold Italic.ttf", FontFamily.SansSerif, "Calibri", true, FontWeight.Bold, true);
187                 ft.registerFont("/Library/Fonts/Microsoft/Times New Roman.ttf", FontFamily.Serif, "Times New Roman", false, FontWeight.Normal, true);
188                 ft.registerFont("/Library/Fonts/Microsoft/Times New Roman Bold.ttf", FontFamily.Serif, "Times New Roman", false, FontWeight.Bold, true);
189                 ft.registerFont("/Library/Fonts/Microsoft/Times New Roman Italic.ttf", FontFamily.Serif, "Times New Roman", true, FontWeight.Normal, true);
190                 ft.registerFont("/Library/Fonts/Microsoft/Times New Roman Bold Italic.ttf", FontFamily.Serif, "Times New Roman", true, FontWeight.Bold, true);
191                 ft.registerFont("/Library/Fonts/Microsoft/Verdana.ttf", FontFamily.SansSerif, "Verdana", false, FontWeight.Normal, true);
192                 ft.registerFont("/Library/Fonts/Microsoft/Verdana Bold.ttf", FontFamily.SansSerif, "Verdana", false, FontWeight.Bold, true);
193                 ft.registerFont("/Library/Fonts/Microsoft/Verdana Italic.ttf", FontFamily.SansSerif, "Verdana", true, FontWeight.Normal, true);
194                 ft.registerFont("/Library/Fonts/Microsoft/Verdana Bold Italic.ttf", FontFamily.SansSerif, "Verdana", true, FontWeight.Bold, true);
195 
196                 ft.registerFont("/Library/Fonts/Microsoft/Consolas.ttf", FontFamily.MonoSpace, "Consolas", false, FontWeight.Normal, true);
197                 ft.registerFont("/Library/Fonts/Microsoft/Consolas Bold.ttf", FontFamily.MonoSpace, "Consolas", false, FontWeight.Bold, true);
198                 ft.registerFont("/Library/Fonts/Microsoft/Consolas Italic.ttf", FontFamily.MonoSpace, "Consolas", true, FontWeight.Normal, true);
199                 ft.registerFont("/Library/Fonts/Microsoft/Consolas Bold Italic.ttf", FontFamily.MonoSpace, "Consolas", true, FontWeight.Bold, true);
200 
201                 ft.registerFont("/System/Library/Fonts/Menlo.ttc", FontFamily.MonoSpace, "Menlo", false, FontWeight.Normal, true);
202             }
203         }
204         
205         if (!ft.registeredFontCount)
206             return false;
207         
208         FontManager.instance = ft;
209         return true;
210     }
211 }
212 }
213 
214 /// initialize logging (for win32 - to file ui.log, for other platforms - stderr; log level is TRACE for debug builds, and WARN for release builds)
215 extern (C) void initLogs() {
216     static if (BACKEND_CONSOLE) {
217         static import std.stdio;
218         debug {
219             Log.setFileLogger(new std.stdio.File("ui.log", "w"));
220             Log.i("Debug build. Logging to file ui.log");
221             Log.setLogLevel(LogLevel.Trace);
222         } else {
223             // no logging unless version ForceLogs is set
224             version(ForceLogs) {
225                 Log.setFileLogger(new std.stdio.File("ui.log", "w"));
226                 Log.i("Logging to file ui.log");
227                 //Log.setLogLevel(LogLevel.Trace);
228             }
229         }
230     } else {
231         static import std.stdio;
232         version (Windows) {
233             debug {
234                 Log.setFileLogger(new std.stdio.File("ui.log", "w"));
235             } else {
236                 // no logging unless version ForceLogs is set
237                 version(ForceLogs) {
238                     Log.setFileLogger(new std.stdio.File("ui.log", "w"));
239                     Log.i("Logging to file ui.log");
240                 }
241             }
242         } else version(Android) {
243             Log.setLogTag("dlangui");
244             Log.setLogLevel(LogLevel.Trace);
245         } else {
246             Log.setStderrLogger();
247         }
248         debug {
249             Log.setLogLevel(LogLevel.Trace);
250         } else {
251             version(ForceLogs) {
252                 Log.setLogLevel(LogLevel.Trace);
253                 Log.i("Log level: trace");
254             } else {
255                 Log.setLogLevel(LogLevel.Warn);
256                 Log.i("Log level: warn");
257             }
258         }
259     }
260     Log.i("Logger is initialized");
261 }
262 
263 /// call this on application initialization
264 extern (C) void initResourceManagers() {
265     Log.d("initResourceManagers()");
266     import dlangui.graphics.fonts;
267     _gamma65 = new glyph_gamma_table!65(1.0);
268     _gamma256 = new glyph_gamma_table!256(1.0);
269     static if (ENABLE_FREETYPE) {
270         import dlangui.graphics.ftfonts;
271         STD_FONT_FACES = [
272             "Arial": 12,
273             "Times New Roman": 12,
274             "Courier New": 10,
275             "DejaVu Serif": 10,
276             "DejaVu Sans": 10,
277             "DejaVu Sans Mono": 10,
278             "Liberation Serif": 11,
279             "Liberation Sans": 11,
280             "Liberation Mono": 11,
281             "Verdana": 10,
282             "Menlo": 13,
283             "Consolas": 12,
284             "DejaVuSansMono": 10,
285             "Lucida Sans Typewriter": 10,
286             "Lucida Console": 12,
287             "FreeMono": 8,
288             "FreeSans": 8,
289             "FreeSerif": 8,
290         ];
291     }
292     static if (ENABLE_OPENGL) {
293         import dlangui.graphics.gldrawbuf;
294         initGLCaches();
295     }
296     import dlangui.graphics.resources;
297     embedStandardDlangUIResources();
298     static if (BACKEND_GUI) {
299         _imageCache = new ImageCache();
300     }
301     _drawableCache = new DrawableCache();
302     static if (BACKEND_GUI) {
303         version (Windows) {
304             import dlangui.platforms.windows.win32fonts;
305             initWin32FontsTables();
306         }
307     }
308 
309     Log.d("Calling initSharedResourceManagers()");
310     initSharedResourceManagers();
311 
312     Log.d("Calling initStandardEditorActions()");
313     import dlangui.widgets.editors;
314     initStandardEditorActions();
315 
316     Log.d("Calling registerStandardWidgets()");
317     registerStandardWidgets();
318 
319 
320     Log.d("initResourceManagers() -- finished");
321 }
322 
323 /// register standard widgets to use in DML
324 void registerStandardWidgets() {
325     Log.d("Registering standard widgets for DML");
326     import dlangui.widgets.metadata;
327     import dlangui.widgets.widget;
328     import dlangui.widgets.layouts;
329     import dlangui.widgets.controls;
330     import dlangui.widgets.scrollbar;
331     import dlangui.widgets.lists;
332     import dlangui.widgets.combobox;
333     import dlangui.widgets.editors;
334     import dlangui.widgets.grid;
335     import dlangui.widgets.groupbox;
336     import dlangui.widgets.progressbar;
337     import dlangui.dialogs.filedlg;
338     import dlangui.widgets.menu;
339     import dlangui.widgets.tree;
340     mixin(registerWidgets!(FileNameEditLine, DirEditLine, //dlangui.dialogs.filedlg
341                            ComboBox, ComboEdit, //dlangui.widgets.combobox
342                            Widget, TextWidget, MultilineTextWidget, Button, ImageWidget, ImageButton, ImageCheckButton, ImageTextButton, 
343                            SwitchButton, RadioButton, CheckBox, HSpacer, VSpacer, CanvasWidget, // dlangui.widgets.controls
344                            ScrollBar, SliderWidget, // dlangui.widgets.scrollbar
345                            EditLine, EditBox, LogWidget,//dlangui.widgets.editors
346                            GroupBox, // dlangui.widgets.groupbox
347                            ProgressBarWidget, // dlangui.widgets.progressbar
348                            StringGridWidget, //dlangui.widgets.grid
349                            VerticalLayout, HorizontalLayout, TableLayout, FrameLayout, // dlangui.widgets.layouts
350                            ListWidget, StringListWidget,//dlangui.widgets.lists
351                            MainMenu, //dlangui.widgets.menu
352                            TreeWidget, // dlangui.widgets.tree
353                            )("void registerWidgets"));
354     registerWidgets();
355 }
356 
357 /// call this from shared static this()
358 extern (C) void initSharedResourceManagers() {
359     //Log.d("initSharedResourceManagers()");
360     //import dlangui.core.i18n;
361     //if (!i18n) {
362     //    Log.d("Creating i18n object");
363     //    i18n = new shared UIStringTranslator();
364     //}
365 }
366 
367 shared static this() {
368     //initSharedResourceManagers();
369 }
370 
371 /// call this when all resources are supposed to be freed to report counts of non-freed resources by type
372 extern (C) void releaseResourcesOnAppExit() {
373     
374     //
375     debug setAppShuttingDownFlag();
376     
377     debug {
378         if (Widget.instanceCount() > 0) {
379             Log.e("Non-zero Widget instance count when exiting: ", Widget.instanceCount);
380         }
381     }
382     
383     currentTheme = null;
384     drawableCache = null;
385     static if (BACKEND_GUI) {
386         imageCache = null;
387     }
388     FontManager.instance = null;
389     static if (ENABLE_OPENGL) {
390         import dlangui.graphics.gldrawbuf;
391         destroyGLCaches();
392     }
393     
394     debug {
395         if (DrawBuf.instanceCount > 0) {
396             Log.e("Non-zero DrawBuf instance count when exiting: ", DrawBuf.instanceCount);
397         }
398         if (Style.instanceCount > 0) {
399             Log.e("Non-zero Style instance count when exiting: ", Style.instanceCount);
400         }
401         if (ImageDrawable.instanceCount > 0) {
402             Log.e("Non-zero ImageDrawable instance count when exiting: ", ImageDrawable.instanceCount);
403         }
404         if (Drawable.instanceCount > 0) {
405             Log.e("Non-zero Drawable instance count when exiting: ", Drawable.instanceCount);
406         }
407         static if (ENABLE_FREETYPE) {
408             import dlangui.graphics.ftfonts;
409             if (FreeTypeFontFile.instanceCount > 0) {
410                 Log.e("Non-zero FreeTypeFontFile instance count when exiting: ", FreeTypeFontFile.instanceCount);
411             }
412             if (FreeTypeFont.instanceCount > 0) {
413                 Log.e("Non-zero FreeTypeFont instance count when exiting: ", FreeTypeFont.instanceCount);
414             }
415         }
416     }
417 }
418 
419 version(unittest) {
420     version (Windows) {
421         mixin APP_ENTRY_POINT;
422 
423         /// entry point for dlangui based application
424         extern (C) int UIAppMain(string[] args) {
425             // just to enable running unit tests
426             import core.runtime;
427             import std.stdio;
428             if (!runModuleUnitTests()) {
429                 writeln("Error occured in unit tests. Press enter.");
430                 readln();
431                 return 1;
432             }
433             return 0;
434         }
435     }
436 }
437 
438