package io.gitlab.jfronny.inceptum.imgui; import imgui.ImGui; import imgui.ImGuiIO; import imgui.flag.ImGuiConfigFlags; import imgui.gl3.ImGuiImplGl3; import imgui.glfw.ImGuiImplGlfw; import io.gitlab.jfronny.commons.log.*; import io.gitlab.jfronny.inceptum.common.model.inceptum.UpdateMetadata; import io.gitlab.jfronny.inceptum.imgui.window.MainWindow; import io.gitlab.jfronny.inceptum.launcher.LauncherEnv; import io.gitlab.jfronny.inceptum.common.*; import io.gitlab.jfronny.inceptum.imgui.window.Window; import io.gitlab.jfronny.inceptum.launcher.api.account.AccountManager; import io.gitlab.jfronny.inceptum.launcher.system.instance.InstanceList; import org.jetbrains.annotations.Nullable; import org.lwjgl.PointerBuffer; import org.lwjgl.glfw.*; import org.lwjgl.opengl.GL; import org.lwjgl.opengl.GL32; import org.lwjgl.system.MemoryStack; import org.lwjgl.system.MemoryUtil; import org.lwjgl.util.tinyfd.TinyFileDialogs; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.nio.IntBuffer; import java.nio.file.*; import java.util.*; public class GuiMain { public static final MemoryLogger MEMLOG = new MemoryLogger("Inceptum"); public static final Set WINDOWS = new LinkedHashSet<>(); private static final ImGuiImplGlfw imGuiGlfw = new ImGuiImplGlfw(); private static final ImGuiImplGl3 imGuiGl3 = new ImGuiImplGl3(); /** * Pointer to the native GLFW window. */ protected static long handle; private static String glslVersion = null; public static void main(String[] args) throws IOException { LauncherEnv.initialize(new GuiEnvBackend()); Utils.LOGGER.info("Launching Inceptum v" + BuildMetadata.VERSION); Utils.LOGGER.info("Loading from " + MetaHolder.BASE_PATH); try { showGui(); } finally { LauncherEnv.terminate(); } } public static void showGui() { Logger.registerFactory(name -> new CompoundLogger(name, InceptumEnvironmentInitializer.defaultFactory(name), MEMLOG)); UpdateMetadata update = BuildMetadata.IS_PUBLIC ? Updater.getUpdate() : null; AccountManager.loadAccounts(); Utils.LOGGER.info("Initializing UI"); try { InstanceList.forEach(instance -> instance.mds().start()); } catch (IOException e) { Utils.LOGGER.error("Could not initialize MDS"); } init(); if (update == null) { WINDOWS.add(new MainWindow()); } else if (MetaHolder.isWrapper()) { LauncherEnv.showOkCancel("An update was found. Should it be installed automatically?", "Update found", () -> { try { Updater.update(update, true); exit(); } catch (IOException | URISyntaxException e) { LauncherEnv.showError("Could not download update", e); } }, () -> WINDOWS.add(new MainWindow())); } else { LauncherEnv.showOkCancel("An update was found. Automatic installs are not supported without the wrapper but you can download it nonetheless", "Update found", () -> { try { Utils.openWebBrowser(new URI(Updater.getShadowJarUrl(InceptumConfig.channel))); exit(); } catch (URISyntaxException e) { LauncherEnv.showError("Could not download update", e); } }, () -> WINDOWS.add(new MainWindow())); } run(); dispose(); } public static void open(Window window) { WINDOWS.add(window); } /** * Method to initialize application. */ public static void init() { initWindow(); initImGui(); imGuiGlfw.init(handle, true); imGuiGl3.init(glslVersion); } /** * Method to dispose all used application resources and destroy its window. */ public static void dispose() { imGuiGl3.dispose(); imGuiGlfw.dispose(); Callbacks.glfwFreeCallbacks(handle); GLFW.glfwDestroyWindow(handle); GLFW.glfwTerminate(); Objects.requireNonNull(GLFW.glfwSetErrorCallback(null)).free(); } /** * Method to create and initialize GLFW window. */ protected static void initWindow() { GLFWErrorCallback.createPrint(System.err).set(); if (!GLFW.glfwInit()) { throw new IllegalStateException("Unable to initialize GLFW"); } decideGlGlslVersions(); GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE); GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, GLFW.GLFW_FALSE); handle = GLFW.glfwCreateWindow(10, 10, "Inceptum", MemoryUtil.NULL, MemoryUtil.NULL); if (handle == MemoryUtil.NULL) { throw new RuntimeException("Failed to create the GLFW window"); } try (MemoryStack stack = MemoryStack.stackPush()) { final IntBuffer pWidth = stack.mallocInt(1); // int* final IntBuffer pHeight = stack.mallocInt(1); // int* GLFW.glfwGetWindowSize(handle, pWidth, pHeight); final GLFWVidMode vidmode = Objects.requireNonNull(GLFW.glfwGetVideoMode(GLFW.glfwGetPrimaryMonitor())); GLFW.glfwSetWindowPos(handle, (vidmode.width() - pWidth.get(0)) / 2, (vidmode.height() - pHeight.get(0)) / 2); } GLFW.glfwMakeContextCurrent(handle); GL.createCapabilities(); GLFW.glfwSwapInterval(GLFW.GLFW_TRUE); clearBuffer(); renderBuffer(); } private static void decideGlGlslVersions() { final boolean isMac = System.getProperty("os.name").toLowerCase().contains("mac"); if (isMac) { glslVersion = "#version 150"; GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 3); GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 2); GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE); // 3.2+ only GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_FORWARD_COMPAT, GLFW.GLFW_TRUE); // Required on Mac } else { glslVersion = "#version 130"; GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 3); GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 0); } } /** * Method to initialize Dear ImGui context. Could be overridden to do custom Dear ImGui setup before application start. */ protected static void initImGui() { ImGui.createContext(); ImGuiIO io = ImGui.getIO(); io.addConfigFlags(ImGuiConfigFlags.ViewportsEnable); io.setConfigViewportsNoAutoMerge(true); try (InputStream is = LauncherEnv.class.getClassLoader().getResourceAsStream("font.ttf")) { io.setFontDefault(io.getFonts().addFontFromMemoryTTF(Objects.requireNonNull(is).readAllBytes(), 16f)); } catch (IOException e) { Utils.LOGGER.error("Could not load font", e); } applyTheme(); } /** * Main application loop. */ public static void run() { while (!GLFW.glfwWindowShouldClose(handle)) { //frame clearBuffer(); imGuiGlfw.newFrame(); ImGui.newFrame(); //render if (WINDOWS.isEmpty()) exit(); else { for (Window window : WINDOWS.toArray(new Window[0])) { if (window.isNew()) window.preFirstDraw(); String title = window.getName() + "##" + System.identityHashCode(window); if (window.isCloseable()) { if (ImGui.begin(title, window.getOpenState(), window.getFlags())) { window.draw(); } } else { if (ImGui.begin(title, window.getFlags())) { window.draw(); } } ImGui.end(); if (!window.getOpenState().get() && !window.isClosed()) window.close(); } } //end frame ImGui.render(); imGuiGl3.renderDrawData(ImGui.getDrawData()); if (ImGui.getIO().hasConfigFlags(ImGuiConfigFlags.ViewportsEnable)) { final long backupWindowPtr = GLFW.glfwGetCurrentContext(); ImGui.updatePlatformWindows(); ImGui.renderPlatformWindowsDefault(); GLFW.glfwMakeContextCurrent(backupWindowPtr); } renderBuffer(); } } private static void clearBuffer() { GL32.glClear(GL32.GL_COLOR_BUFFER_BIT | GL32.GL_DEPTH_BUFFER_BIT); } private static void renderBuffer() { GLFW.glfwSwapBuffers(handle); GLFW.glfwPollEvents(); } public static void exit() { GLFW.glfwSetWindowShouldClose(handle, true); } public static void applyTheme() { if (InceptumConfig.darkTheme) ImGui.styleColorsDark(); else ImGui.styleColorsLight(); } public static @Nullable Path saveFileDialog(String title, String defaultName, String[] filters, String filterDescription) { try (MemoryStack stack = MemoryStack.stackPush()) { PointerBuffer filterPatterns = stack.mallocPointer(filters.length); for (String filter : filters) { filterPatterns.put(stack.UTF8(filter)); } filterPatterns.flip(); String file = TinyFileDialogs.tinyfd_saveFileDialog(title, defaultName, filterPatterns, filterDescription); return file == null ? null : Paths.get(file).toAbsolutePath().normalize(); } } public static List openFileDialog(String title, String defaultName, String[] filters, String filterDescription, boolean selectMultiple) { try (MemoryStack stack = MemoryStack.stackPush()) { PointerBuffer filterPatterns = stack.mallocPointer(filters.length); for (String filter : filters) { filterPatterns.put(stack.UTF8(filter)); } filterPatterns.flip(); String files = TinyFileDialogs.tinyfd_openFileDialog(title, defaultName, filterPatterns, filterDescription, selectMultiple); if (files == null) return List.of(); return Arrays.stream(files.split("\\|")) .map(Paths::get) .map(Path::toAbsolutePath) .map(Path::normalize) .toList(); } } }