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.dpi;
9 import creator.core.input;
10 import creator.core.egg;
11 import creator.panels;
12 import creator.windows;
13 import creator.utils.link;
14 import creator;
15 import creator.widgets.dialog;
16 import creator.backend.gl;
17 
18 import std.exception;
19 
20 import bindbc.sdl;
21 import bindbc.opengl;
22 import inochi2d;
23 import tinyfiledialogs;
24 import std.string;
25 import std.stdio;
26 import std.conv;
27 import std.range : repeat;
28 
29 public import bindbc.imgui;
30 public import bindbc.imgui.ogl;
31 public import creator.core.settings;
32 public import creator.core.actionstack;
33 public import creator.core.tasks;
34 public import creator.core.path;
35 public import creator.core.font;
36 public import creator.core.dpi;
37 import i18n;
38 
39 version(linux) import dportals;
40 
41 version(Windows) {
42     import core.sys.windows.windows;
43     import core.sys.windows.winuser;
44 
45     // Windows 8.1+ DPI awareness context enum
46     enum DPIAwarenessContext { 
47         DPI_AWARENESS_CONTEXT_UNAWARE = 0,
48         DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = 1,
49         DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = 2,
50         DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = 3
51     }
52 
53     // Windows 8.1+ DPI awareness enum
54     enum ProcessDPIAwareness { 
55         PROCESS_DPI_UNAWARE = 0,
56         PROCESS_SYSTEM_DPI_AWARE = 1,
57         PROCESS_PER_MONITOR_DPI_AWARE = 2
58     }
59 
60 
61     void incSetWin32DPIAwareness() {
62         void* userDLL, shcoreDLL;
63 
64         bool function() dpiAwareFunc8;
65         HRESULT function(DPIAwarenessContext) dpiAwareFuncCtx81;
66         HRESULT function(ProcessDPIAwareness) dpiAwareFunc81;
67 
68         userDLL = SDL_LoadObject("USER32.DLL");
69         if (userDLL) {
70             dpiAwareFunc8 = cast(typeof(dpiAwareFunc8)) SDL_LoadFunction(userDLL, "SetProcessDPIAware");
71             dpiAwareFuncCtx81 = cast(typeof(dpiAwareFuncCtx81)) SDL_LoadFunction(userDLL, "SetProcessDpiAwarenessContext");
72         }
73         
74         shcoreDLL = SDL_LoadObject("SHCORE.DLL");
75         if (shcoreDLL) {
76             dpiAwareFunc81 = cast(typeof(dpiAwareFunc81)) SDL_LoadFunction(shcoreDLL, "SetProcessDpiAwareness");
77         }
78         
79         if (dpiAwareFuncCtx81) {
80             dpiAwareFuncCtx81(DPIAwarenessContext.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
81             dpiAwareFuncCtx81(DPIAwarenessContext.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
82         } else if (dpiAwareFunc81) {
83             dpiAwareFunc81(ProcessDPIAwareness.PROCESS_PER_MONITOR_DPI_AWARE);
84         } else if (dpiAwareFunc8) dpiAwareFunc8();
85 
86 
87         // Unload the DLLs
88         if (userDLL) SDL_UnloadObject(userDLL);
89         if (shcoreDLL) SDL_UnloadObject(shcoreDLL);        
90     }
91 }
92 
93 private {
94     SDL_GLContext gl_context;
95     SDL_Window* window;
96     ImGuiIO* io;
97     bool done = false;
98     ImGuiID viewportDock;
99 
100     version (InBranding) {
101         Texture incLogoI2D;
102         Texture incLogo;
103         Texture incAda;
104     }
105     Texture incGrid;
106 
107     ImFont* mainFont;
108 
109     bool isDarkMode = true;
110     string[] files;
111     bool isWayland;
112     bool isTilingWM;
113 
114     
115     ImVec4[ImGuiCol.COUNT] incDarkModeColors;
116     ImVec4[ImGuiCol.COUNT] incLightModeColors;
117 
118 }
119 
120 bool incShowStatsForNerds;
121 bool incShouldPostProcess = false;
122 
123 bool incIsWayland() {
124     return isWayland;
125 }
126 
127 bool incIsTilingWM() {
128     return isTilingWM;
129 }
130 
131 /**
132     Finalizes everything by freeing imgui resources, etc.
133 */
134 void incFinalize() {
135 
136     // This is important to prevent thread leakage
137     import creator.viewport.test : incViewportTestWithdraw;
138     incViewportTestWithdraw();
139 
140     // Save settings
141     igSaveIniSettingsToDisk(igGetIO().IniFilename);
142 
143     // Cleanup
144     incGLBackendShutdown();
145     ImGui_ImplSDL2_Shutdown();
146     igDestroyContext(null);
147 
148     SDL_GL_DeleteContext(gl_context);
149     SDL_DestroyWindow(window);
150     SDL_Quit();
151 }
152 
153 /**
154     Gets dockspace of the viewport
155 */
156 ImGuiID incGetViewportDockSpace() {
157     return viewportDock;
158 }
159 
160 /**
161     Opens Window
162 */
163 void incOpenWindow() {
164     import std.process : environment;
165 
166     switch(environment.get("XDG_SESSION_DESKTOP")) {
167         case "i3":
168 
169         // Items beyond this point are just guesstimations.
170         case "awesome":
171         case "bspwm":
172         case "dwm":
173         case "echinus":
174         case "euclid-wm":
175         case "herbstluftwm":
176         case "leftwm":
177         case "notion":
178         case "qtile":
179         case "ratpoison":
180         case "snapwm":
181         case "stumpwm":
182         case "subtle":
183         case "wingo":
184         case "wmfs":
185         case "xmonad":
186         case "wayfire":
187         case "river":
188         case "labwc":
189             isTilingWM = true;
190             break;
191         
192         default:
193             isTilingWM = false;
194             break;
195     }
196 
197     auto sdlSupport = loadSDL();
198     enforce(sdlSupport != SDLSupport.noLibrary, "SDL2 library not found!");
199     enforce(sdlSupport != SDLSupport.badLibrary, "Bad SDL2 library found!");
200     
201     version(BindImGui_Dynamic) {
202         auto imSupport = loadImGui();
203         enforce(imSupport != ImGuiSupport.noLibrary, "cimgui library not found!");
204     
205         // HACK: For some reason this check fails on some macOS and Linux installations
206         version(Windows) enforce(imSupport != ImGuiSupport.badLibrary, "Bad cimgui library found!");
207     }
208 
209     SDL_Init(SDL_INIT_EVERYTHING);
210     
211     version(Windows) {
212         incSetWin32DPIAwareness();
213     }
214 
215     SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_CORE);
216     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
217     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
218 
219     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
220     SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
221     SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
222     SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
223     SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
224     SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
225     SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
226 
227     SDL_WindowFlags flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI;
228 
229     if (incSettingsGet!bool("WinMax", false)) {
230         flags |= SDL_WINDOW_MAXIMIZED;
231     }
232 
233     // Don't make KDE freak out when Inochi Creator opens
234     if (!incSettingsGet!bool("DisableCompositor")) SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
235 
236     version(InBranding) {
237         debug string WIN_TITLE = "Inochi Creator "~_("(Debug Mode)");
238         else string WIN_TITLE = "Inochi Creator "~INC_VERSION;
239     } else string WIN_TITLE = "Inochi Creator "~_("(Unsupported)");
240     
241     window = SDL_CreateWindow(
242         WIN_TITLE.toStringz, 
243         SDL_WINDOWPOS_UNDEFINED,
244         SDL_WINDOWPOS_UNDEFINED,
245         cast(uint)incSettingsGet!int("WinW", 1280), 
246         cast(uint)incSettingsGet!int("WinH", 800), 
247         flags
248     );
249     SDL_SetWindowMinimumSize(window, 960, 720);
250     
251     // On Linux we want to check whether the window was created under wayland or x11
252     version(linux) {
253         SDL_SysWMinfo info;
254         SDL_GetWindowWMInfo(window, &info);
255         isWayland = info.subsystem == SDL_SYSWM_TYPE.SDL_SYSWM_WAYLAND;
256     }
257 
258     GLSupport support;
259     gl_context = SDL_GL_CreateContext(window);
260     SDL_GL_SetSwapInterval(1);
261 
262     // Load GL 3
263     support = loadOpenGL();
264     switch(support) {
265         case GLSupport.noLibrary:
266             throw new Exception("OpenGL library could not be loaded!");
267 
268         case GLSupport.noContext:
269             throw new Exception("No valid OpenGL context was found!");
270 
271         default: break;
272     }
273 
274 
275     import std.string : fromStringz;
276     version(Windows) {
277         
278         // Windows is heck when it comes to /SUBSYSTEM:windows
279     } else {
280         debug {
281             writefln("GLInfo:\n\t%s\n\t%s\n\t%s\n\t%s\n\tgls=%s",
282                 glGetString(GL_VERSION).fromStringz,
283                 glGetString(GL_VENDOR).fromStringz,
284                 glGetString(GL_RENDERER).fromStringz,
285                 glGetString(GL_SHADING_LANGUAGE_VERSION).fromStringz,
286                 support
287             );
288         }
289     }
290 
291     // Setup Inochi2D
292     inInit(() { return igGetTime(); });
293     
294     version(InBranding) incInitAda();
295     incCreateContext();
296 
297     ShallowTexture tex;
298     version (InBranding) {
299 
300         // Load image resources
301         tex = ShallowTexture(cast(ubyte[])import("logo.png"));
302         inTexPremultiply(tex.data);
303         incLogoI2D = new Texture(tex);
304 
305         // Load image resources
306         tex = ShallowTexture(cast(ubyte[])import("icon.png"));
307         inTexPremultiply(tex.data);
308         incLogo = new Texture(tex);
309 
310         // Set X11 window icon
311         version(linux) {
312             if (!isWayland) {
313                 SDL_SetWindowIcon(window, SDL_CreateRGBSurfaceWithFormatFrom(tex.data.ptr, tex.width, tex.height, 32, 4*tex.width,  SDL_PIXELFORMAT_RGBA32));
314             }
315         }
316 
317         tex = ShallowTexture(cast(ubyte[])import("ui/ui-ada.png"));
318         inTexPremultiply(tex.data);
319         incAda = new Texture(tex);
320     }
321 
322     // Grid texture
323     tex = ShallowTexture(cast(ubyte[])import("ui/grid.png"));
324     inTexPremultiply(tex.data);
325     incGrid = new Texture(tex);
326     incGrid.setFiltering(Filtering.Point);
327     incGrid.setWrapping(Wrapping.Repeat);
328 
329     // Load Settings
330     incShowStatsForNerds = incSettingsCanGet("NerdStats") ? incSettingsGet!bool("NerdStats") : false;
331 
332     version(linux) {
333         dpInit();
334     }
335 }
336 
337 void incCreateContext() {
338 
339     // Setup IMGUI
340     auto ctx = igCreateContext(null);
341     io = igGetIO();
342     
343     // Setup font handling
344     incInitFonts();
345 
346     import std.file : exists;
347     if (!exists(incGetAppImguiConfigFile())) {
348         // TODO: Setup a base config
349     }
350 
351 
352     // Copy string out of GC memory to make sure it doesn't get yeeted before imgui exits.
353     import core.stdc.stdlib : malloc;
354     import core.stdc.string : memcpy;
355     io.IniFilename = cast(char*)malloc(incGetAppImguiConfigFile().length+1);
356     memcpy(cast(void*)io.IniFilename, toStringz(incGetAppImguiConfigFile), incGetAppImguiConfigFile().length+1);
357     igLoadIniSettingsFromDisk(io.IniFilename);
358 
359     incSetDarkMode(incSettingsGet!bool("DarkMode", true));
360 
361     io.ConfigFlags |= ImGuiConfigFlags.DockingEnable;                               // Enable Docking
362     io.ConfigWindowsResizeFromEdges = true;                                         // Enable Edge resizing
363     version (OSX) io.ConfigMacOSXBehaviors = true;                                  // macOS Behaviours on macOS
364 
365     // Force C locale due to imgui removing support for setting decimal separator.
366     import i18n.culture : i18nSetLocale;
367     i18nSetLocale("C");
368 
369     // NOTE: Viewports break DPI scaling system, as such if Viewports is enabled
370     // we will be disable DPI scaling.
371     version(NoUIScaling) {
372         if (!incIsTilingWM) io.ConfigFlags |= ImGuiConfigFlags.ViewportsEnable;         // Enable Viewports (causes freezes)
373     } else {
374         incInitDPIScaling();
375     }
376 
377     //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;                         // Enable Keyboard Navigation
378     ImGui_ImplSDL2_InitForOpenGL(window, gl_context);
379     incGLBackendInit(null);
380 
381     incInitStyling();
382     incInitDialogs();
383 }
384 
385 
386 /**
387     Initialize styling
388 */
389 void incInitStyling() {
390     //style.WindowBorderSize = 0;
391     auto style = igGetStyle();
392     style.FrameBorderSize = 1;
393     style.TabBorderSize = 1;
394     style.ChildBorderSize = 1;
395     style.PopupBorderSize = 1;
396     style.FrameBorderSize = 1;
397     style.TabBorderSize = 1;
398 
399     style.WindowRounding = 4;
400     style.ChildRounding = 0;
401     style.FrameRounding = 3;
402     style.PopupRounding = 6;
403     style.ScrollbarRounding = 18;
404     style.GrabRounding = 3;
405     style.LogSliderDeadzone = 6;
406     style.TabRounding = 6;
407 
408     style.IndentSpacing = 10;
409     style.ItemSpacing.y = 3;
410     style.FramePadding.y = 4;
411 
412     style.GrabMinSize = 13;
413     style.ScrollbarSize = 14;
414     style.ChildBorderSize = 1;
415 
416     // Don't draw the silly roll menu
417     style.WindowMenuButtonPosition = ImGuiDir.None;
418 
419     // macOS support
420     version(OSX) style.WindowTitleAlign = ImVec2(0.5, 0.5);
421     
422     
423     igStyleColorsDark(style);
424     style.Colors[ImGuiCol.Text]                   = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
425     style.Colors[ImGuiCol.TextDisabled]           = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
426     style.Colors[ImGuiCol.WindowBg]               = ImVec4(0.17f, 0.17f, 0.17f, 1.00f);
427     style.Colors[ImGuiCol.ChildBg]                = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
428     style.Colors[ImGuiCol.PopupBg]                = ImVec4(0.08f, 0.08f, 0.08f, 0.94f);
429     style.Colors[ImGuiCol.Border]                 = ImVec4(0.00f, 0.00f, 0.00f, 0.16f);
430     style.Colors[ImGuiCol.BorderShadow]           = ImVec4(0.00f, 0.00f, 0.00f, 0.16f);
431     style.Colors[ImGuiCol.FrameBg]                = ImVec4(0.12f, 0.12f, 0.12f, 1.00f);
432     style.Colors[ImGuiCol.FrameBgHovered]         = ImVec4(0.15f, 0.15f, 0.15f, 0.40f);
433     style.Colors[ImGuiCol.FrameBgActive]          = ImVec4(0.22f, 0.22f, 0.22f, 0.67f);
434     style.Colors[ImGuiCol.TitleBg]                = ImVec4(0.04f, 0.04f, 0.04f, 1.00f);
435     style.Colors[ImGuiCol.TitleBgActive]          = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);
436     style.Colors[ImGuiCol.TitleBgCollapsed]       = ImVec4(0.00f, 0.00f, 0.00f, 0.51f);
437     style.Colors[ImGuiCol.MenuBarBg]              = ImVec4(0.05f, 0.05f, 0.05f, 1.00f);
438     style.Colors[ImGuiCol.ScrollbarBg]            = ImVec4(0.02f, 0.02f, 0.02f, 0.53f);
439     style.Colors[ImGuiCol.ScrollbarGrab]          = ImVec4(0.31f, 0.31f, 0.31f, 1.00f);
440     style.Colors[ImGuiCol.ScrollbarGrabHovered]   = ImVec4(0.41f, 0.41f, 0.41f, 1.00f);
441     style.Colors[ImGuiCol.ScrollbarGrabActive]    = ImVec4(0.51f, 0.51f, 0.51f, 1.00f);
442     style.Colors[ImGuiCol.CheckMark]              = ImVec4(0.76f, 0.76f, 0.76f, 1.00f);
443     style.Colors[ImGuiCol.SliderGrab]             = ImVec4(0.25f, 0.25f, 0.25f, 1.00f);
444     style.Colors[ImGuiCol.SliderGrabActive]       = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
445     style.Colors[ImGuiCol.Button]                 = ImVec4(0.39f, 0.39f, 0.39f, 0.40f);
446     style.Colors[ImGuiCol.ButtonHovered]          = ImVec4(0.44f, 0.44f, 0.44f, 1.00f);
447     style.Colors[ImGuiCol.ButtonActive]           = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
448     style.Colors[ImGuiCol.Header]                 = ImVec4(0.25f, 0.25f, 0.25f, 1.00f);
449     style.Colors[ImGuiCol.HeaderHovered]          = ImVec4(0.28f, 0.28f, 0.28f, 0.80f);
450     style.Colors[ImGuiCol.HeaderActive]           = ImVec4(0.44f, 0.44f, 0.44f, 1.00f);
451     style.Colors[ImGuiCol.Separator]              = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);
452     style.Colors[ImGuiCol.SeparatorHovered]       = ImVec4(0.29f, 0.29f, 0.29f, 0.78f);
453     style.Colors[ImGuiCol.SeparatorActive]        = ImVec4(0.47f, 0.47f, 0.47f, 1.00f);
454     style.Colors[ImGuiCol.ResizeGrip]             = ImVec4(0.35f, 0.35f, 0.35f, 0.00f);
455     style.Colors[ImGuiCol.ResizeGripHovered]      = ImVec4(0.40f, 0.40f, 0.40f, 0.00f);
456     style.Colors[ImGuiCol.ResizeGripActive]       = ImVec4(0.55f, 0.55f, 0.56f, 0.00f);
457     style.Colors[ImGuiCol.Tab]                    = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);
458     style.Colors[ImGuiCol.TabHovered]             = ImVec4(0.34f, 0.34f, 0.34f, 0.80f);
459     style.Colors[ImGuiCol.TabActive]              = ImVec4(0.25f, 0.25f, 0.25f, 1.00f);
460     style.Colors[ImGuiCol.TabUnfocused]           = ImVec4(0.14f, 0.14f, 0.14f, 0.97f);
461     style.Colors[ImGuiCol.TabUnfocusedActive]     = ImVec4(0.17f, 0.17f, 0.17f, 1.00f);
462     style.Colors[ImGuiCol.DockingPreview]         = ImVec4(0.62f, 0.68f, 0.75f, 0.70f);
463     style.Colors[ImGuiCol.DockingEmptyBg]         = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
464     style.Colors[ImGuiCol.PlotLines]              = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
465     style.Colors[ImGuiCol.PlotLinesHovered]       = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
466     style.Colors[ImGuiCol.PlotHistogram]          = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
467     style.Colors[ImGuiCol.PlotHistogramHovered]   = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
468     style.Colors[ImGuiCol.TableHeaderBg]          = ImVec4(0.19f, 0.19f, 0.20f, 1.00f);
469     style.Colors[ImGuiCol.TableBorderStrong]      = ImVec4(0.31f, 0.31f, 0.35f, 1.00f);
470     style.Colors[ImGuiCol.TableBorderLight]       = ImVec4(0.23f, 0.23f, 0.25f, 1.00f);
471     style.Colors[ImGuiCol.TableRowBg]             = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
472     style.Colors[ImGuiCol.TableRowBgAlt]          = ImVec4(1.00f, 1.00f, 1.00f, 0.06f);
473     style.Colors[ImGuiCol.TextSelectedBg]         = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
474     style.Colors[ImGuiCol.DragDropTarget]         = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
475     style.Colors[ImGuiCol.NavHighlight]           = ImVec4(0.32f, 0.32f, 0.32f, 1.00f);
476     style.Colors[ImGuiCol.NavWindowingHighlight]  = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
477     style.Colors[ImGuiCol.NavWindowingDimBg]      = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
478     style.Colors[ImGuiCol.ModalWindowDimBg]       = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
479     incDarkModeColors = style.Colors.dup;
480     
481     igStyleColorsLight(style);
482     style.Colors[ImGuiCol.Border] = ImVec4(0.8, 0.8, 0.8, 0.5);
483     style.Colors[ImGuiCol.BorderShadow] = ImVec4(0, 0, 0, 0.05);
484     style.Colors[ImGuiCol.TitleBg] = ImVec4(0.902, 0.902, 0.902, 1);
485     style.Colors[ImGuiCol.TitleBgActive] = ImVec4(0.98, 0.98, 0.98, 1);
486     style.Colors[ImGuiCol.Separator] = ImVec4(0.86, 0.86, 0.86, 1);
487     style.Colors[ImGuiCol.ScrollbarGrab] = ImVec4(0.68, 0.68, 0.68, 1);
488     style.Colors[ImGuiCol.ScrollbarGrabActive] = ImVec4(0.68, 0.68, 0.68, 1);
489     style.Colors[ImGuiCol.ScrollbarGrabHovered] = ImVec4(0.64, 0.64, 0.64, 1);
490     style.Colors[ImGuiCol.FrameBg] = ImVec4(1, 1, 1, 1);
491     style.Colors[ImGuiCol.FrameBgHovered] = ImVec4(0.78, 0.88, 1, 1);
492     style.Colors[ImGuiCol.FrameBgActive] = ImVec4(0.76, 0.86, 1, 1);
493     style.Colors[ImGuiCol.Button] = ImVec4(0.98, 0.98, 0.98, 1);
494     style.Colors[ImGuiCol.ButtonHovered] = ImVec4(1, 1, 1, 1);
495     style.Colors[ImGuiCol.ButtonActive] = ImVec4(0.8, 0.8, 0.8, 1);
496     style.Colors[ImGuiCol.CheckMark] = ImVec4(0, 0, 0, 1);
497     style.Colors[ImGuiCol.Tab] = ImVec4(0.98, 0.98, 0.98, 1);
498     style.Colors[ImGuiCol.TabHovered] = ImVec4(1, 1, 1, 1);
499     style.Colors[ImGuiCol.TabActive] = ImVec4(0.8, 0.8, 0.8, 1);
500     style.Colors[ImGuiCol.TabUnfocused] = ImVec4(0.92, 0.92, 0.92, 1);
501     style.Colors[ImGuiCol.TabUnfocusedActive] = ImVec4(0.88, 0.88, 0.88, 1);
502     style.Colors[ImGuiCol.MenuBarBg] = ImVec4(0.863, 0.863, 0.863, 1);  
503     style.Colors[ImGuiCol.PopupBg] = ImVec4(0.941, 0.941, 0.941, 1);  
504     style.Colors[ImGuiCol.Header] = ImVec4(0.990, 0.990, 0.990, 1);  
505     style.Colors[ImGuiCol.HeaderHovered] = ImVec4(1, 1, 1, 1);
506     incLightModeColors = style.Colors.dup;
507     
508     style.Colors = isDarkMode ? incDarkModeColors : incLightModeColors;
509 }
510 
511 void incPushDarkColorScheme() {
512     auto ctx = igGetCurrentContext();
513     ctx.Style.Colors = incDarkModeColors;
514 }
515 
516 void incPushLightColorScheme() {
517     auto ctx = igGetCurrentContext();
518     ctx.Style.Colors = incLightModeColors;
519 }
520 
521 void incPopColorScheme() {
522     auto ctx = igGetCurrentContext();
523     ctx.Style.Colors = isDarkMode ? incDarkModeColors : incLightModeColors;
524 }
525 
526 void incSetDarkMode(bool darkMode) {
527     auto style = igGetStyle();
528     style.Colors = darkMode ? incDarkModeColors : incLightModeColors;
529 
530     // Set Dark mode setting
531     incSettingsSet("DarkMode", darkMode);
532     isDarkMode = darkMode;
533 }
534 
535 bool incGetDarkMode() {
536     return isDarkMode;
537 }
538 
539 /**
540     Gets whether a frame should be processed
541 */
542 bool incShouldProcess() {
543     return (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) == 0;
544 }
545 
546 /**
547     Gets SDL Window Pointer
548 */
549 SDL_Window* incGetWindowPtr() {
550     return window;
551 }
552 
553 void incFinishFileDrag() {
554     files.length = 0;
555 }
556 
557 void incBeginLoopNoEv() {
558     // Start the Dear ImGui frame
559     incGLBackendNewFrame();
560     ImGui_ImplSDL2_NewFrame();
561 
562     // Do our DPI pre-processing
563     igNewFrame();
564     incGLBackendBeginRender();
565 
566     version(linux) dpUpdate();
567 
568 
569 
570     if (files.length > 0) {
571         if (igBeginDragDropSource(ImGuiDragDropFlags.SourceExtern)) {
572             igSetDragDropPayload("__PARTS_DROP", &files, files.sizeof);
573             igBeginTooltip();
574             foreach(file; files) {
575                 import creator.widgets.label : incText;
576                 incText(file);
577             }
578             igEndTooltip();
579             igEndDragDropSource();
580         }
581     }
582 
583     // Add docking space
584     viewportDock = igDockSpaceOverViewport(null, ImGuiDockNodeFlags.NoDockingInCentralNode, null);
585     if (!incSettingsCanGet("firstrun_complete")) {
586         incSetDefaultLayout();
587         incSettingsSet("firstrun_complete", true);
588     }
589 
590     incRenderDialogs();
591     incStatusUpdate();
592 }
593 
594 void incSetDefaultLayout() {
595     import creator.panels;
596     
597     igDockBuilderRemoveNodeChildNodes(viewportDock);
598     ImGuiID 
599         dockMainID, dockIDNodes, dockIDInspector, dockIDHistory, dockIDParams,
600         dockIDToolSettings, dockIDLoggerAndTextureSlots, dockIDTimeline;
601 
602     dockMainID = viewportDock;
603     dockIDNodes = igDockBuilderSplitNode(dockMainID, ImGuiDir.Left, 0.10f, null, &dockMainID);
604     dockIDInspector = igDockBuilderSplitNode(dockIDNodes, ImGuiDir.Down, 0.60f, null, &dockIDNodes);
605     dockIDToolSettings = igDockBuilderSplitNode(dockMainID, ImGuiDir.Right, 0.10f, null, &dockMainID);
606     dockIDHistory = igDockBuilderSplitNode(dockIDToolSettings, ImGuiDir.Down, 0.50f, null, &dockIDToolSettings);
607     dockIDTimeline = igDockBuilderSplitNode(dockMainID, ImGuiDir.Down, 0.15f, null, &dockMainID);
608     dockIDParams = igDockBuilderSplitNode(dockMainID, ImGuiDir.Left, 0.15f, null, &dockMainID);
609 
610     igDockBuilderDockWindow("###Nodes", dockIDNodes);
611     igDockBuilderDockWindow("###Inspector", dockIDInspector);
612     igDockBuilderDockWindow("###Tool Settings", dockIDToolSettings);
613     igDockBuilderDockWindow("###History", dockIDHistory);
614     igDockBuilderDockWindow("###Scene", dockIDHistory);
615     debug(InExperimental) igDockBuilderDockWindow("###Tracking", dockIDHistory);
616     igDockBuilderDockWindow("###Timeline", dockIDTimeline);
617     igDockBuilderDockWindow("###Logger", dockIDTimeline);
618     igDockBuilderDockWindow("###Parameters", dockIDParams);
619     igDockBuilderDockWindow("###Texture Slots", dockIDLoggerAndTextureSlots);
620 
621     igDockBuilderFinish(viewportDock);
622 }
623 
624 /**
625     Begins the Inochi Creator rendering loop
626 */
627 void incBeginLoop() {
628     SDL_Event event;
629 
630     while(SDL_PollEvent(&event)) {
631         switch(event.type) {
632             case SDL_QUIT:
633                 incExit();
634                 break;
635 
636             case SDL_DROPFILE:
637                 files ~= cast(string)event.drop.file.fromStringz;
638                 SDL_RaiseWindow(window);
639                 break;
640             
641             default: 
642                 incGLBackendProcessEvent(&event);
643                 if (
644                     event.type == SDL_WINDOWEVENT && 
645                     event.window.event == SDL_WINDOWEVENT_CLOSE && 
646                     event.window.windowID == SDL_GetWindowID(window)
647                 ) incExit();
648                 break;
649         }
650     }
651 
652     incTaskUpdate();
653 
654     // Begin loop post-event
655     incBeginLoopNoEv();
656 }
657 
658 /**
659     Ends the Inochi Creator rendering loop
660 */
661 void incEndLoop() {
662     // incGLBackendEndRender();
663 
664     incCleanupDialogs();
665 
666     // Rendering
667     igRender();
668     glViewport(0, 0, cast(int)(io.DisplaySize.x*incGetUIScale), cast(int)(io.DisplaySize.y*incGetUIScale));
669     glClearColor(0.5, 0.5, 0.5, 1);
670     glClear(GL_COLOR_BUFFER_BIT);
671     incGLBackendRenderDrawData(igGetDrawData());
672 
673     if (io.ConfigFlags & ImGuiConfigFlags.ViewportsEnable) {
674         SDL_Window* currentWindow = SDL_GL_GetCurrentWindow();
675         SDL_GLContext currentCtx = SDL_GL_GetCurrentContext();
676         igUpdatePlatformWindows();
677         igRenderPlatformWindowsDefault();
678         SDL_GL_MakeCurrent(currentWindow, currentCtx);
679     }
680 
681     
682     version(InBranding) {
683         import creator.core.egg;
684         incAdaUpdate();
685     }
686 
687     SDL_GL_SwapWindow(window);
688 }
689 
690 /**
691     Prints ImGui debug info
692 */
693 void incDebugImGuiState(string msg, int indent = 0) {
694     debug(imgui) {
695         static int currentIndent = 0;
696 
697         string flag = "  ";
698         if (indent > 0) {
699             currentIndent += indent;
700             flag = ">>";
701         } else if (indent < 0) {
702             flag = "<<";
703         }
704 
705         //auto g = igGetCurrentContext();
706         auto win = igGetCurrentWindow();
707         writefln(
708             "%s%s%s [%s]", ' '.repeat(currentIndent * 2), flag, msg,
709             to!string(win.Name)
710         );
711 
712         if (indent < 0) {
713             currentIndent += indent;
714             if (currentIndent < 0) {
715                 debug writeln("ERROR: dedented too far!");
716                 currentIndent = 0;
717             }
718         }
719     }
720 }
721 
722 /**
723     Gets whether Inochi Creator has requested the app to close
724 */
725 bool incIsCloseRequested() {
726     return done;
727 }
728 
729 /**
730     Exit Inochi Creator
731 */
732 void incExit() {
733     done = true;
734 
735     int w, h;
736     SDL_WindowFlags flags;
737     flags = SDL_GetWindowFlags(window);
738     SDL_GetWindowSize(window, &w, &h);
739     incSettingsSet("WinW", w);
740     incSettingsSet("WinH", h);
741     incSettingsSet!bool("WinMax", (flags & SDL_WINDOW_MAXIMIZED) > 0);
742 }
743 
744 /**
745     Main font
746 */
747 ImFont* incMainFont() {
748     return mainFont;
749 }
750 
751 version (InBranding) {
752     /**
753         Gets the Inochi2D Logo
754     */
755     Texture incGetLogo() {
756         return incLogo;
757     }
758 
759     /**
760         Gets the Ada texture
761     */
762     Texture incGetAda() {
763         return incAda;
764     }
765 
766     Texture incGetLogoI2D() {
767         return incLogoI2D;
768     }
769 }
770 
771 /**
772     Gets the grid texture
773 */
774 Texture incGetGrid() {
775     return incGrid;
776 }
777 
778 void incHandleShortcuts() {
779     auto io = igGetIO();
780     
781     if (incShortcut("Ctrl+Shift+Z", true)) {
782         incActionRedo();
783     } else if (incShortcut("Ctrl+Z", true)) {
784         incActionUndo();
785     }
786 }