277 lines
10 KiB
Java
277 lines
10 KiB
Java
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<Window> 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<Path> 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();
|
|
}
|
|
}
|
|
}
|