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