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