1 /*
2     Copyright © 2020, Inochi2D Project
3     Distributed under the 2-Clause BSD License, see LICENSE file.
4     
5     Authors: Luna Nielsen
6 */
7 module creator.core;
8 import creator.core.font;
9 import creator.panels;
10 import creator.windows;
11 import creator.utils.link;
12 import creator;
13 
14 import std.exception;
15 
16 import bindbc.sdl;
17 import bindbc.opengl;
18 import inochi2d;
19 import tinyfiledialogs;
20 import std.string;
21 import std.stdio;
22 import std.conv;
23 import std.range : repeat;
24 
25 public import bindbc.imgui;
26 public import bindbc.imgui.ogl;
27 public import creator.core.settings;
28 public import creator.core.actionstack;
29 public import creator.core.taskstack;
30 public import creator.core.path;
31 public import creator.core.font;
32 
33 private {
34     SDL_GLContext gl_context;
35     SDL_Window* window;
36     ImGuiIO* io;
37     bool done = false;
38     ImGuiID viewportDock;
39 
40     Texture inLogo;
41 
42     ImFont* mainFont;
43     ImFont* iconFont;
44     ImFont* biggerFont;
45 
46     bool isDarkMode = true;
47     string[] files;
48 }
49 
50 bool incShowStatsForNerds;
51 
52 
53 /**
54     Finalizes everything by freeing imgui resources, etc.
55 */
56 void incFinalize() {
57 
58     // This is important to prevent thread leakage
59     import creator.viewport.test : incViewportTestWithdraw;
60     incViewportTestWithdraw();
61 
62     // Save settings
63     igSaveIniSettingsToDisk(igGetIO().IniFilename);
64 
65     // Cleanup
66     ImGuiOpenGLBackend.shutdown();
67     ImGui_ImplSDL2_Shutdown();
68     igDestroyContext(null);
69 
70     SDL_GL_DeleteContext(gl_context);
71     SDL_DestroyWindow(window);
72     SDL_Quit();
73 }
74 
75 /**
76     Gets dockspace of the viewport
77 */
78 ImGuiID incGetViewportDockSpace() {
79     return viewportDock;
80 }
81 
82 /**
83     Initialize styling
84 */
85 void incInitStyling() {
86     auto style = igGetStyle();
87     //style.WindowBorderSize = 0;
88     style.ChildBorderSize = 1;
89     style.PopupBorderSize = 1;
90     style.FrameBorderSize = 1;
91     style.TabBorderSize = 1;
92 
93     style.WindowRounding = 4;
94     style.ChildRounding = 0;
95     style.FrameRounding = 3;
96     style.PopupRounding = 6;
97     style.ScrollbarRounding = 18;
98     style.GrabRounding = 3;
99     style.LogSliderDeadzone = 6;
100     style.TabRounding = 6;
101 
102     style.IndentSpacing = 10;
103     style.ItemSpacing.y = 3;
104     style.FramePadding.y = 4;
105 
106     style.GrabMinSize = 13;
107     style.ScrollbarSize = 14;
108     style.ChildBorderSize = 1;
109 }
110 
111 /**
112     Opens Window
113 */
114 void incOpenWindow() {
115     
116     import core.stdc.stdlib : exit;
117 
118     auto sdlSupport = loadSDL();
119     enforce(sdlSupport != SDLSupport.noLibrary, "SDL2 library not found!");
120     enforce(sdlSupport != SDLSupport.badLibrary, "Bad SDL2 library found!");
121     
122     auto imSupport = loadImGui();
123     enforce(imSupport != ImGuiSupport.noLibrary, "cimgui library not found!");
124     
125     // HACK: For some reason this check fails on some macOS and Linux installations
126     version(Windows) enforce(imSupport != ImGuiSupport.badLibrary, "Bad cimgui library found!");
127     SDL_Init(SDL_INIT_EVERYTHING);
128 
129     SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
130     version(OSX) {
131 		pragma(msg, "Building in macOS support mode...");
132 
133 		// macOS only supports up to GL 4.1 with some extra stuff
134         SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GLcontextFlag.SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
135         SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_CORE);
136         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
137         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
138 	} else {
139 
140         SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GLcontextFlag.SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
141         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
142         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
143     }
144     debug SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GLcontextFlag.SDL_GL_CONTEXT_DEBUG_FLAG | SDL_GLcontextFlag.SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
145     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
146     SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
147     SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
148 
149     SDL_WindowFlags flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE;
150 
151     if (incSettingsGet!bool("WinMax", false)) {
152         flags |= SDL_WINDOW_MAXIMIZED;
153     }
154 
155     // Don't make KDE freak out when Inochi Creator opens
156     if (!incSettingsGet!bool("DisableCompositor")) SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
157 
158     window = SDL_CreateWindow(
159         "Inochi Creator", 
160         SDL_WINDOWPOS_UNDEFINED,
161         SDL_WINDOWPOS_UNDEFINED,
162         cast(uint)incSettingsGet!int("WinW", 1280), 
163         cast(uint)incSettingsGet!int("WinH", 800), 
164         flags
165     );
166 
167     gl_context = SDL_GL_CreateContext(window);
168     SDL_GL_MakeCurrent(window, gl_context);
169     SDL_GL_SetSwapInterval(1);
170     
171     // Load GL 3
172     GLSupport support = loadOpenGL();
173     switch(support) {
174         case GLSupport.noLibrary:
175             throw new Exception("OpenGL library could not be loaded!");
176 
177         case GLSupport.noContext:
178             throw new Exception("No valid OpenGL 4.2 context was found!");
179 
180         default: break;
181     }
182 
183     import std.string : fromStringz;
184     debug {
185         writefln("GLInfo:\n\t%s\n\t%s\n\t%s\n\t%s\n\tgls=%s",
186             glGetString(GL_VERSION).fromStringz,
187             glGetString(GL_VENDOR).fromStringz,
188             glGetString(GL_RENDERER).fromStringz,
189             glGetString(GL_SHADING_LANGUAGE_VERSION).fromStringz,
190             support
191         );
192 
193         glEnable(GL_DEBUG_OUTPUT);
194         version(Posix) {
195             glDebugMessageCallback(&incDebugCallback, null);
196         }
197     }
198 
199     // Setup Inochi2D
200     inInit(() { return igGetTime(); });
201 
202     incCreateContext();
203 
204 
205     // Load image resources
206     inLogo = new Texture(ShallowTexture(cast(ubyte[])import("logo.png")));
207 
208     // Load Settings
209     incShowStatsForNerds = incSettingsCanGet("NerdStats") ? incSettingsGet!bool("NerdStats") : false;
210 
211     import creator.widgets.titlebar : incSetUseNativeTitlebar, incGetUseNativeTitlebar, incCanUseAppTitlebar;
212     incCanUseAppTitlebar = SDL_SetWindowHitTest(incGetWindowPtr(), null, null) != -1;
213     incSetUseNativeTitlebar(incSettingsGet("UseNativeTitleBar", false));
214     
215 }
216 
217 void incCreateContext() {
218 
219     // Setup IMGUI
220     igCreateContext(null);
221     io = igGetIO();
222     
223     // Setup font handling
224     incInitFonts();
225 
226     import std.file : exists;
227     if (!exists(incGetAppImguiConfigFile())) {
228         // TODO: Setup a base config
229     }
230 
231 
232     // Copy string out of GC memory to make sure it doesn't get yeeted before imgui exits.
233     import core.stdc.stdlib : malloc;
234     import core.stdc.string : memcpy;
235     io.IniFilename = cast(char*)malloc(incGetAppImguiConfigFile().length+1);
236     memcpy(cast(void*)io.IniFilename, toStringz(incGetAppImguiConfigFile), incGetAppImguiConfigFile().length+1);
237     igLoadIniSettingsFromDisk(io.IniFilename);
238 
239     incSetDarkMode(incSettingsGet!bool("DarkMode", true));
240 
241     io.ConfigFlags |= ImGuiConfigFlags.DockingEnable;           // Enable Docking
242     io.ConfigFlags |= ImGuiConfigFlags.ViewportsEnable;         // Enable Viewports (causes freezes)
243     //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // Enable Keyboard Navigation
244     io.ConfigWindowsResizeFromEdges = true;                     // Enable Edge resizing
245     ImGui_ImplSDL2_InitForOpenGL(window, gl_context);
246     ImGuiOpenGLBackend.init(null);
247 
248     incInitStyling();
249 }
250 
251 void incSetDarkMode(bool darkMode) {
252     auto style = igGetStyle();
253 
254     if (darkMode) {
255         style.Colors[ImGuiCol.Text]                   = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
256         style.Colors[ImGuiCol.TextDisabled]           = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
257         style.Colors[ImGuiCol.WindowBg]               = ImVec4(0.17f, 0.17f, 0.17f, 1.00f);
258         style.Colors[ImGuiCol.ChildBg]                = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
259         style.Colors[ImGuiCol.PopupBg]                = ImVec4(0.08f, 0.08f, 0.08f, 0.94f);
260         style.Colors[ImGuiCol.Border]                 = ImVec4(0.00f, 0.00f, 0.00f, 0.16f);
261         style.Colors[ImGuiCol.BorderShadow]           = ImVec4(0.00f, 0.00f, 0.00f, 0.16f);
262         style.Colors[ImGuiCol.FrameBg]                = ImVec4(0.12f, 0.12f, 0.12f, 1.00f);
263         style.Colors[ImGuiCol.FrameBgHovered]         = ImVec4(0.15f, 0.15f, 0.15f, 0.40f);
264         style.Colors[ImGuiCol.FrameBgActive]          = ImVec4(0.22f, 0.22f, 0.22f, 0.67f);
265         style.Colors[ImGuiCol.TitleBg]                = ImVec4(0.04f, 0.04f, 0.04f, 1.00f);
266         style.Colors[ImGuiCol.TitleBgActive]          = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);
267         style.Colors[ImGuiCol.TitleBgCollapsed]       = ImVec4(0.00f, 0.00f, 0.00f, 0.51f);
268         style.Colors[ImGuiCol.MenuBarBg]              = ImVec4(0.05f, 0.05f, 0.05f, 1.00f);
269         style.Colors[ImGuiCol.ScrollbarBg]            = ImVec4(0.02f, 0.02f, 0.02f, 0.53f);
270         style.Colors[ImGuiCol.ScrollbarGrab]          = ImVec4(0.31f, 0.31f, 0.31f, 1.00f);
271         style.Colors[ImGuiCol.ScrollbarGrabHovered]   = ImVec4(0.41f, 0.41f, 0.41f, 1.00f);
272         style.Colors[ImGuiCol.ScrollbarGrabActive]    = ImVec4(0.51f, 0.51f, 0.51f, 1.00f);
273         style.Colors[ImGuiCol.CheckMark]              = ImVec4(0.76f, 0.76f, 0.76f, 1.00f);
274         style.Colors[ImGuiCol.SliderGrab]             = ImVec4(0.25f, 0.25f, 0.25f, 1.00f);
275         style.Colors[ImGuiCol.SliderGrabActive]       = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
276         style.Colors[ImGuiCol.Button]                 = ImVec4(0.39f, 0.39f, 0.39f, 0.40f);
277         style.Colors[ImGuiCol.ButtonHovered]          = ImVec4(0.44f, 0.44f, 0.44f, 1.00f);
278         style.Colors[ImGuiCol.ButtonActive]           = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
279         style.Colors[ImGuiCol.Header]                 = ImVec4(0.25f, 0.25f, 0.25f, 1.00f);
280         style.Colors[ImGuiCol.HeaderHovered]          = ImVec4(0.28f, 0.28f, 0.28f, 0.80f);
281         style.Colors[ImGuiCol.HeaderActive]           = ImVec4(0.44f, 0.44f, 0.44f, 1.00f);
282         style.Colors[ImGuiCol.Separator]              = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);
283         style.Colors[ImGuiCol.SeparatorHovered]       = ImVec4(0.29f, 0.29f, 0.29f, 0.78f);
284         style.Colors[ImGuiCol.SeparatorActive]        = ImVec4(0.47f, 0.47f, 0.47f, 1.00f);
285         style.Colors[ImGuiCol.ResizeGrip]             = ImVec4(0.35f, 0.35f, 0.35f, 0.00f);
286         style.Colors[ImGuiCol.ResizeGripHovered]      = ImVec4(0.40f, 0.40f, 0.40f, 0.00f);
287         style.Colors[ImGuiCol.ResizeGripActive]       = ImVec4(0.55f, 0.55f, 0.56f, 0.00f);
288         style.Colors[ImGuiCol.Tab]                    = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);
289         style.Colors[ImGuiCol.TabHovered]             = ImVec4(0.34f, 0.34f, 0.34f, 0.80f);
290         style.Colors[ImGuiCol.TabActive]              = ImVec4(0.25f, 0.25f, 0.25f, 1.00f);
291         style.Colors[ImGuiCol.TabUnfocused]           = ImVec4(0.14f, 0.14f, 0.14f, 0.97f);
292         style.Colors[ImGuiCol.TabUnfocusedActive]     = ImVec4(0.17f, 0.17f, 0.17f, 1.00f);
293         style.Colors[ImGuiCol.DockingPreview]         = ImVec4(0.62f, 0.68f, 0.75f, 0.70f);
294         style.Colors[ImGuiCol.DockingEmptyBg]         = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
295         style.Colors[ImGuiCol.PlotLines]              = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
296         style.Colors[ImGuiCol.PlotLinesHovered]       = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
297         style.Colors[ImGuiCol.PlotHistogram]          = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
298         style.Colors[ImGuiCol.PlotHistogramHovered]   = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
299         style.Colors[ImGuiCol.TableHeaderBg]          = ImVec4(0.19f, 0.19f, 0.20f, 1.00f);
300         style.Colors[ImGuiCol.TableBorderStrong]      = ImVec4(0.31f, 0.31f, 0.35f, 1.00f);
301         style.Colors[ImGuiCol.TableBorderLight]       = ImVec4(0.23f, 0.23f, 0.25f, 1.00f);
302         style.Colors[ImGuiCol.TableRowBg]             = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
303         style.Colors[ImGuiCol.TableRowBgAlt]          = ImVec4(1.00f, 1.00f, 1.00f, 0.06f);
304         style.Colors[ImGuiCol.TextSelectedBg]         = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
305         style.Colors[ImGuiCol.DragDropTarget]         = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
306         style.Colors[ImGuiCol.NavHighlight]           = ImVec4(0.32f, 0.32f, 0.32f, 1.00f);
307         style.Colors[ImGuiCol.NavWindowingHighlight]  = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
308         style.Colors[ImGuiCol.NavWindowingDimBg]      = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
309         style.Colors[ImGuiCol.ModalWindowDimBg]       = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
310 
311         style.FrameBorderSize = 1;
312         style.TabBorderSize = 1;
313     } else {
314         igStyleColorsLight(null);
315         style.Colors[ImGuiCol.Border] = ImVec4(0.8, 0.8, 0.8, 0.5);
316         style.Colors[ImGuiCol.BorderShadow] = ImVec4(0, 0, 0, 0.05);
317 
318         style.FrameBorderSize = 1;
319     } 
320 
321     // Set Dark mode setting
322     incSettingsSet("DarkMode", darkMode);
323     isDarkMode = darkMode;
324 }
325 
326 bool incGetDarkMode() {
327     return isDarkMode;
328 }
329 
330 /**
331     Gets whether a frame should be processed
332 */
333 bool incShouldProcess() {
334     return (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) == 0;
335 }
336 
337 /**
338     Gets SDL Window Pointer
339 */
340 SDL_Window* incGetWindowPtr() {
341     return window;
342 }
343 
344 void incFinishFileDrag() {
345     files.length = 0;
346 }
347 
348 void incBeginLoopNoEv() {
349     // Start the Dear ImGui frame
350     ImGuiOpenGLBackend.new_frame();
351     ImGui_ImplSDL2_NewFrame();
352     igNewFrame();
353 
354     if (files.length > 0) {
355         if (igBeginDragDropSource(ImGuiDragDropFlags.SourceExtern)) {
356             igSetDragDropPayload("__PARTS_DROP", &files, files.sizeof);
357             igBeginTooltip();
358             foreach(file; files) {
359                 igText(file.toStringz);
360             }
361             igEndTooltip();
362             igEndDragDropSource();
363         }
364     }
365 
366     // Add docking space
367     viewportDock = igDockSpaceOverViewport(null, cast(ImGuiDockNodeFlags)0, null);
368     if (!incSettingsCanGet("firstrun_complete")) {
369         incSetDefaultLayout();
370         incSettingsSet("firstrun_complete", true);
371     }
372 }
373 
374 void incSetDefaultLayout() {
375     import creator.panels;
376     
377     igDockBuilderRemoveNodeChildNodes(viewportDock);
378     ImGuiID 
379         dockMainID, dockIDNodes, dockIDInspector, dockIDHistory, dockIDParams,
380         dockIDToolSettings, dockIDLoggerAndTextureSlots;
381 
382     dockMainID = viewportDock;
383     dockIDNodes = igDockBuilderSplitNode(dockMainID, ImGuiDir.Left, 0.10f, null, &dockMainID);
384     dockIDInspector = igDockBuilderSplitNode(dockIDNodes, ImGuiDir.Down, 0.60f, null, &dockIDNodes);
385     dockIDToolSettings = igDockBuilderSplitNode(dockMainID, ImGuiDir.Right, 0.10f, null, &dockMainID);
386     dockIDHistory = igDockBuilderSplitNode(dockIDToolSettings, ImGuiDir.Down, 0.50f, null, &dockIDToolSettings);
387     dockIDParams = igDockBuilderSplitNode(dockMainID, ImGuiDir.Left, 0.15f, null, &dockMainID);
388     dockIDLoggerAndTextureSlots = igDockBuilderSplitNode(dockMainID, ImGuiDir.Down, 0.15f, null, &dockMainID);
389 
390     igDockBuilderDockWindow("###Nodes", dockIDNodes);
391     igDockBuilderDockWindow("###Inspector", dockIDInspector);
392     igDockBuilderDockWindow("###Tool Settings", dockIDToolSettings);
393     igDockBuilderDockWindow("###History", dockIDHistory);
394     igDockBuilderDockWindow("###Tracking", dockIDHistory);
395     igDockBuilderDockWindow("###Parameters", dockIDParams);
396     igDockBuilderDockWindow("###Texture Slots", dockIDLoggerAndTextureSlots);
397     igDockBuilderDockWindow("###Logger", dockIDLoggerAndTextureSlots);
398 
399     igDockBuilderFinish(viewportDock);
400 }
401 
402 /**
403     Begins the Inochi Creator rendering loop
404 */
405 void incBeginLoop() {
406     SDL_Event event;
407 
408     while(SDL_PollEvent(&event)) {
409         switch(event.type) {
410             case SDL_QUIT:
411                 incExit();
412                 break;
413 
414             case SDL_DROPFILE:
415                 files ~= cast(string)event.drop.file.fromStringz;
416                 SDL_RaiseWindow(window);
417                 break;
418             
419             default: 
420                 ImGui_ImplSDL2_ProcessEvent(&event);
421                 if (
422                     event.type == SDL_WINDOWEVENT && 
423                     event.window.event == SDL_WINDOWEVENT_CLOSE && 
424                     event.window.windowID == SDL_GetWindowID(window)
425                 ) incExit();
426                 break;
427         }
428     }
429 
430     incTaskUpdate();
431 
432     // Begin loop post-event
433     incBeginLoopNoEv();
434 }
435 
436 /**
437     Ends the Inochi Creator rendering loop
438 */
439 void incEndLoop() {
440 
441     // Rendering
442     igRender();
443     glViewport(0, 0, cast(int)io.DisplaySize.x, cast(int)io.DisplaySize.y);
444     glClearColor(0.5, 0.5, 0.5, 1);
445     glClear(GL_COLOR_BUFFER_BIT);
446     ImGuiOpenGLBackend.render_draw_data(igGetDrawData());
447 
448     if (io.ConfigFlags & ImGuiConfigFlags.ViewportsEnable) {
449         SDL_Window* currentWindow = SDL_GL_GetCurrentWindow();
450         SDL_GLContext currentCtx = SDL_GL_GetCurrentContext();
451         igUpdatePlatformWindows();
452         igRenderPlatformWindowsDefault();
453         SDL_GL_MakeCurrent(currentWindow, currentCtx);
454     }
455 
456     SDL_GL_SwapWindow(window);
457 }
458 
459 /**
460     Prints ImGui debug info
461 */
462 void incDebugImGuiState(string msg, int indent = 0) {
463     debug(imgui) {
464         static int currentIndent = 0;
465 
466         string flag = "  ";
467         if (indent > 0) {
468             currentIndent += indent;
469             flag = ">>";
470         } else if (indent < 0) {
471             flag = "<<";
472         }
473 
474         //auto g = igGetCurrentContext();
475         auto win = igGetCurrentWindow();
476         writefln(
477             "%s%s%s [%s]", ' '.repeat(currentIndent * 2), flag, msg,
478             to!string(win.Name)
479         );
480 
481         if (indent < 0) {
482             currentIndent += indent;
483             if (currentIndent < 0) {
484                 debug writeln("ERROR: dedented too far!");
485                 currentIndent = 0;
486             }
487         }
488     }
489 }
490 
491 /**
492     Gets whether Inochi Creator has requested the app to close
493 */
494 bool incIsCloseRequested() {
495     return done;
496 }
497 
498 /**
499     Exit Inochi Creator
500 */
501 void incExit() {
502     done = true;
503 
504     int w, h;
505     SDL_WindowFlags flags;
506     flags = SDL_GetWindowFlags(window);
507     SDL_GetWindowSize(window, &w, &h);
508     incSettingsSet("WinW", w);
509     incSettingsSet("WinH", h);
510     incSettingsSet!bool("WinMax", (flags & SDL_WINDOW_MAXIMIZED) > 0);
511 }
512 
513 /**
514     Main font
515 */
516 ImFont* incMainFont() {
517     return mainFont;
518 }
519 
520 /**
521     Bigger sized font
522 */
523 ImFont* incBiggerFont() {
524     return biggerFont;
525 }
526 
527 /**
528     Bigger sized font
529 */
530 ImFont* incIconFont() {
531     return iconFont;
532 }
533 
534 /**
535     Gets the Inochi2D Logo
536 */
537 GLuint incGetLogo() {
538     return inLogo.getTextureId;
539 }
540 
541 void incHandleShortcuts() {
542     auto io = igGetIO();
543     
544     if (io.KeyCtrl && io.KeyShift && igIsKeyPressed(igGetKeyIndex(ImGuiKey.Z), true)) {
545         incActionRedo();
546     } else if (io.KeyCtrl && igIsKeyPressed(igGetKeyIndex(ImGuiKey.Z), true)) {
547         incActionUndo();
548     }
549 }
550 
551 
552 debug {
553     extern(C)
554     void incDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const(char)* message, void* userParam) nothrow {
555         import core.stdc.stdio : fprintf, stderr;
556         if (type == 0x8251) return;
557 
558         // HACK: I have no clue what causes this error
559         // but everything seems to work nontheless
560         // I'll just quietly ignore it.
561         if (type == 0x824c) return; 
562 
563         fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
564            ( type == GL_DEBUG_TYPE_ERROR ? cast(char*)"** GL ERROR **" : cast(char*)"" ),
565             type, severity, message );
566 
567     }
568 }