diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index df84563530..ee8062e102 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -13,12 +13,18 @@ add_executable(launcher src/logger/crash-report.cpp src/options/parser.cpp src/main.cpp + src/ui/frame.cpp src/client/configuration.cpp src/client/cache.cpp src/client/available-feature-flags # Show In IDE ) embed_resource(launcher src/client/available-feature-flags) -target_link_libraries(launcher reborn-util LIB_LIEF trampoline-headers) +target_link_libraries(launcher + reborn-util + LIB_LIEF + imgui + trampoline-headers +) # RPath set_target_properties(launcher PROPERTIES INSTALL_RPATH "$ORIGIN/lib/native") target_link_options(launcher PRIVATE "LINKER:--disable-new-dtags") diff --git a/launcher/src/client/available-feature-flags b/launcher/src/client/available-feature-flags index 75212c0ee8..c3b76ffed9 100644 --- a/launcher/src/client/available-feature-flags +++ b/launcher/src/client/available-feature-flags @@ -45,7 +45,6 @@ FALSE Remove Forced GUI Lag (Can Break Joining Servers) TRUE Add Buckets TRUE Classic HUD TRUE Translucent Toolbar -FALSE Force EGL TRUE Improved Classic Title Screen FALSE Disable Speed Bridging FALSE Disable Creative Mode Mining Delay diff --git a/launcher/src/client/configuration.cpp b/launcher/src/client/configuration.cpp index b3dab56aaa..9d049fe210 100644 --- a/launcher/src/client/configuration.cpp +++ b/launcher/src/client/configuration.cpp @@ -12,6 +12,7 @@ #include "../util/util.h" #include "configuration.h" #include "cache.h" +#include "../ui/frame.h" // Strip Feature Flag Default std::string strip_feature_flag_default(const std::string &flag, bool *default_ret) { @@ -38,8 +39,7 @@ std::string strip_feature_flag_default(const std::string &flag, bool *default_re } // Load Available Feature Flags -extern unsigned char available_feature_flags[]; -extern size_t available_feature_flags_len; +EMBEDDED_RESOURCE(available_feature_flags); void load_available_feature_flags(const std::function &callback) { // Load Data const std::string data(available_feature_flags, available_feature_flags + available_feature_flags_len); @@ -149,12 +149,27 @@ void handle_non_launch_client_only_commands(const options_t &options) { } } +struct Test final : Frame { + Test(): Frame(DIALOG_TITLE, 640, 480) {} + int render() override { + ImGui::Button("Hello World!"); + ImGui::PushFont(monospace); + ImGui::Button("Custom Font"); + ImGui::PopFont(); + return 0; + } +}; + // Configure Client Options #define LIST_DIALOG_SIZE "400" void configure_client(const options_t &options) { // Load Cache launcher_cache cache = options.no_cache ? empty_cache : load_cache(); + Test *test = new Test; + test->run(); + delete test; + // --default if (options.use_default) { // Use Default Feature Flags diff --git a/launcher/src/ui/frame.cpp b/launcher/src/ui/frame.cpp new file mode 100644 index 0000000000..a1661d9474 --- /dev/null +++ b/launcher/src/ui/frame.cpp @@ -0,0 +1,87 @@ +#include "frame.h" + +#include +#include + +#include + +// Init/Cleanup +Frame::Frame(const char *title, const int width, const int height) { + // Create Window + init_glfw(); + window = create_glfw_window(title, width, height); + // V-Sync + glfwSwapInterval(1); + // Setup ImGui Context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO &io = ImGui::GetIO(); + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; + io.IniFilename = nullptr; + io.LogFilename = nullptr; + // Setup Platform/Renderer Backends + ImGui_ImplGlfw_InitForOpenGL(window, true); + ImGui_ImplOpenGL2_Init(); +} +Frame::~Frame() { + ImGui_ImplOpenGL2_Shutdown(); + ImGui_ImplGlfw_Shutdown(); + ImGui::DestroyContext(); + cleanup_glfw(window); +} + +// Run Loop +int Frame::run() { + int ret = 0; + constexpr ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + while (!glfwWindowShouldClose(window) || ret != 0) { + glfwPollEvents(); + // Update Style + static float last_scale = -1.0f; + float scale; + get_glfw_scale(window, &scale, nullptr); + if (scale != last_scale) { + last_scale = scale; + setup_style(scale); + } + // Start Frame + ImGui_ImplOpenGL2_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); + // Main Window + ImGui::SetNextWindowPos({0, 0}); + int width, height; + glfwGetFramebufferSize(window, &width, &height); + ImGui::SetNextWindowSize({float(width), float(height)}); + if (ImGui::Begin("###Main", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse)) { + ret = render(); + } + ImGui::End(); + // Render To OpenGL + ImGui::Render(); + glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w); + glClear(GL_COLOR_BUFFER_BIT); + ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData()); + glfwSwapBuffers(window); + } + return ret; +} + +// Style +EMBEDDED_RESOURCE(Roboto_Medium_ttf); +EMBEDDED_RESOURCE(Cousine_Regular_ttf); +void Frame::setup_style(const float scale) { + // Fonts + const ImGuiIO &io = ImGui::GetIO(); + io.Fonts->Clear(); + ImFontConfig font_cfg; + font_cfg.FontDataOwnedByAtlas = false; + io.Fonts->AddFontFromMemoryTTF(Roboto_Medium_ttf, int(Roboto_Medium_ttf_len), 24.0f * scale, &font_cfg); + monospace = io.Fonts->AddFontFromMemoryTTF(Cousine_Regular_ttf, int(Cousine_Regular_ttf_len), 18.0f * scale, &font_cfg); + // Style + ImGuiStyle &style = ImGui::GetStyle(); + style = ImGuiStyle(); + style.WindowBorderSize = 0; + ImGui::StyleColorsDark(&style); + style.ScaleAllSizes(scale); +} diff --git a/launcher/src/ui/frame.h b/launcher/src/ui/frame.h new file mode 100644 index 0000000000..66c7eb5fc8 --- /dev/null +++ b/launcher/src/ui/frame.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +// UI Frame +struct Frame { + Frame(const char *title, int width, int height); + virtual ~Frame(); + // Run + int run(); + virtual int render() = 0; + // Properties +protected: + ImFont *monospace = nullptr; +private: + GLFWwindow *window = nullptr; + // Internal + float get_scale(); + void setup_style(float scale); +}; \ No newline at end of file diff --git a/libreborn/CMakeLists.txt b/libreborn/CMakeLists.txt index c079a6c164..43b93e0d95 100644 --- a/libreborn/CMakeLists.txt +++ b/libreborn/CMakeLists.txt @@ -14,6 +14,10 @@ add_library(reborn-util SHARED src/util/env.cpp ) target_link_libraries(reborn-util PRIVATE utf8cpp) +if(TARGET glfw) + target_sources(reborn-util PRIVATE src/util/glfw.cpp) + target_link_libraries(reborn-util PRIVATE glfw) +endif() setup_header_dirs(reborn-util "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}/include" diff --git a/libreborn/include/libreborn/glfw.h b/libreborn/include/libreborn/glfw.h new file mode 100644 index 0000000000..ff8e50a24b --- /dev/null +++ b/libreborn/include/libreborn/glfw.h @@ -0,0 +1,11 @@ +#pragma once + +// GLFW Helpers +#ifdef GLFW_VERSION_MAJOR + +void init_glfw(); +GLFWwindow *create_glfw_window(const char *title, int width, int height); +void cleanup_glfw(GLFWwindow *window); +void get_glfw_scale(GLFWwindow *window, float *x_scale, float *y_scale); + +#endif \ No newline at end of file diff --git a/libreborn/include/libreborn/libreborn.h b/libreborn/include/libreborn/libreborn.h index 54bf1b7deb..68d8df5580 100644 --- a/libreborn/include/libreborn/libreborn.h +++ b/libreborn/include/libreborn/libreborn.h @@ -7,3 +7,4 @@ #include "string.h" #include "exec.h" #include "patch.h" +#include "glfw.h" \ No newline at end of file diff --git a/libreborn/include/libreborn/patch.h b/libreborn/include/libreborn/patch.h index eff308e6e9..95606a3734 100644 --- a/libreborn/include/libreborn/patch.h +++ b/libreborn/include/libreborn/patch.h @@ -3,7 +3,7 @@ #include // Patching Functions -#if defined(REBORN_HAS_PATCH_CODE) +#ifdef REBORN_HAS_PATCH_CODE // Init void reborn_init_patch(); diff --git a/libreborn/include/libreborn/util.h b/libreborn/include/libreborn/util.h index 0abeb0c02c..838c0657e6 100644 --- a/libreborn/include/libreborn/util.h +++ b/libreborn/include/libreborn/util.h @@ -74,4 +74,9 @@ const char *get_home_subdirectory_for_game_data(); void ensure_directory(const char *path); // Safe write() -void safe_write(int fd, const void *buf, size_t size); \ No newline at end of file +void safe_write(int fd, const void *buf, size_t size); + +// embed_resource() +#define EMBEDDED_RESOURCE(name) \ + extern unsigned char name[]; \ + extern size_t name##_len diff --git a/libreborn/src/util/glfw.cpp b/libreborn/src/util/glfw.cpp new file mode 100644 index 0000000000..babd1a47d8 --- /dev/null +++ b/libreborn/src/util/glfw.cpp @@ -0,0 +1,68 @@ +#define GLFW_INCLUDE_NONE +#include + +#include +#include +#include +#include + +// Handle GLFW Error +static void glfw_error(__attribute__((unused)) int error, const char *description) { + WARN("GLFW Error: %s", description); +} + +// Init +void init_glfw() { + reborn_check_display(); + glfwSetErrorCallback(glfw_error); + if (!glfwInit()) { + ERR("Unable To Initialize GLFW"); + } +} + +// Create Window +GLFWwindow *create_glfw_window(const char *title, const int width, const int height) { + // App ID + glfwWindowHintString(GLFW_X11_CLASS_NAME, MCPI_APP_ID); + glfwWindowHintString(GLFW_WAYLAND_APP_ID, MCPI_APP_ID); + // Create Window + GLFWwindow *window = glfwCreateWindow(width, height, title, nullptr, nullptr); + if (!window) { + ERR("Unable To Create GLFW Window"); + } + // Make Window Context Current + glfwMakeContextCurrent(window); + // Return + return window; +} + +// Cleanup +void cleanup_glfw(GLFWwindow *window) { + // Ignore GLFW Errors During Termination + glfwSetErrorCallback(nullptr); + // Terminate GLFW + glfwDestroyWindow(window); + glfwTerminate(); +} + +// Framebuffer Scaling +void get_glfw_scale(GLFWwindow *window, float *x_scale, float *y_scale) { + // Get Window Size + int window_width; + int window_height; + glfwGetWindowSize(window, &window_width, &window_height); + if (window_width <= 0 || window_height <= 0) { + return; + } + // Get Framebuffer Size + int framebuffer_width; + int framebuffer_height; + glfwGetFramebufferSize(window, &framebuffer_width, &framebuffer_height); + // Calculate Scale + if (x_scale) { + *x_scale = float(framebuffer_width) / float(window_width); + } + if (y_scale) { + *y_scale = float(framebuffer_height) / float(window_height); + } +} \ No newline at end of file diff --git a/media-layer/core/src/window/events.cpp b/media-layer/core/src/window/events.cpp index f53dff2a48..c20952855e 100644 --- a/media-layer/core/src/window/events.cpp +++ b/media-layer/core/src/window/events.cpp @@ -179,23 +179,13 @@ static void convert_to_pixels(GLFWwindow *window, double *xpos, double *ypos) { if (media_SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON && raw_mouse_motion_enabled) { return; } - // Get Window Size - int window_width; - int window_height; - glfwGetWindowSize(window, &window_width, &window_height); - if (window_width <= 0 || window_height <= 0) { - return; - } - // Get Framebuffer Size - int framebuffer_width; - int framebuffer_height; - glfwGetFramebufferSize(window, &framebuffer_width, &framebuffer_height); - // Calculate Ratios - const double width_ratio = double(framebuffer_width) / double(window_width); - const double height_ratio = double(framebuffer_height) / double(window_height); + // Get Scale + float x_scale = 1; + float y_scale = 1; + get_glfw_scale(window, &x_scale, &y_scale); // Multiply - *xpos *= width_ratio; - *ypos *= height_ratio; + *xpos *= x_scale; + *ypos *= y_scale; } // Last Mouse Location diff --git a/media-layer/core/src/window/media.cpp b/media-layer/core/src/window/media.cpp index 477d5cf9eb..ea799b59f9 100644 --- a/media-layer/core/src/window/media.cpp +++ b/media-layer/core/src/window/media.cpp @@ -7,11 +7,6 @@ // Window GLFWwindow *glfw_window = nullptr; -// Handle GLFW Error -static void glfw_error(__attribute__((unused)) int error, const char *description) { - WARN("GLFW Error: %s", description); -} - // Disable V-Sync static bool disable_vsync = false; void media_disable_vsync() { @@ -21,15 +16,6 @@ void media_disable_vsync() { } } -// Force EGL -static int force_egl = 0; -void media_force_egl() { - if (force_egl == -1) { - IMPOSSIBLE(); - } - force_egl = 1; -} - // Init Media Layer #define GL_VERSION 0x1f02 typedef const char *(*glGetString_t)(unsigned int name); @@ -40,39 +26,20 @@ void media_SDL_WM_SetCaption(const char *title, __attribute__((unused)) const ch } // Init GLFW - reborn_check_display(); - glfwSetErrorCallback(glfw_error); - if (!glfwInit()) { - ERR("Unable To Initialize GLFW"); - } + init_glfw(); // Create OpenGL Context glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 1); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5); - // Use EGL - if (force_egl) { - glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); - } - force_egl = -1; // Extra Settings glfwWindowHint(GLFW_AUTO_ICONIFY, GLFW_FALSE); - glfwWindowHint(GLFW_ALPHA_BITS, 0); // Fix Transparent Window On Wayland - // App ID - glfwWindowHintString(GLFW_X11_CLASS_NAME, MCPI_APP_ID); - glfwWindowHintString(GLFW_WAYLAND_APP_ID, MCPI_APP_ID); // Create Window - glfw_window = glfwCreateWindow(DEFAULT_WIDTH, DEFAULT_HEIGHT, title, nullptr, nullptr); - if (!glfw_window) { - ERR("Unable To Create GLFW Window"); - } + glfw_window = create_glfw_window(title, DEFAULT_WIDTH, DEFAULT_HEIGHT); // Event Handlers _media_register_event_listeners(); - // Make Window Context Current - glfwMakeContextCurrent(glfw_window); - // Debug const glGetString_t glGetString = (glGetString_t) glfwGetProcAddress("glGetString"); DEBUG("Using OpenGL %s", (*glGetString)(GL_VERSION)); @@ -93,12 +60,8 @@ void media_SDL_WM_SetCaption(const char *title, __attribute__((unused)) const ch // Cleanup Media Layer void media_cleanup() { if (glfw_window) { - // Ignore GLFW Errors During Termination - glfwSetErrorCallback(nullptr); - // Terminate GLFW - glfwDestroyWindow(glfw_window); - glfwTerminate(); + cleanup_glfw(glfw_window); // Cleanup OpenAL _media_audio_cleanup(); diff --git a/media-layer/core/src/window/media.h b/media-layer/core/src/window/media.h index 34d09b207d..4ea346ef18 100644 --- a/media-layer/core/src/window/media.h +++ b/media-layer/core/src/window/media.h @@ -1,11 +1,11 @@ #pragma once -#include -#include - #define GLFW_INCLUDE_NONE #include +#include +#include + #include // Interactivity diff --git a/media-layer/include/media-layer/core.h b/media-layer/include/media-layer/core.h index 27b23cd57e..d82a3c9a85 100644 --- a/media-layer/include/media-layer/core.h +++ b/media-layer/include/media-layer/core.h @@ -18,7 +18,6 @@ void media_cleanup(); void media_get_framebuffer_size(int *width, int *height); void media_set_interactable(int is_interactable); void media_disable_vsync(); -void media_force_egl(); void media_set_raw_mouse_motion_enabled(int enabled); int media_has_extension(const char *name); void media_begin_offscreen_render(int width, int height); diff --git a/media-layer/trampoline/src/media-layer-core.cpp b/media-layer/trampoline/src/media-layer-core.cpp index ba53abc2d8..48ebc0197d 100644 --- a/media-layer/trampoline/src/media-layer-core.cpp +++ b/media-layer/trampoline/src/media-layer-core.cpp @@ -165,15 +165,6 @@ CALL(64, media_set_raw_mouse_motion_enabled, void, (int enabled)) #endif } -CALL(66, media_force_egl, void, ()) -#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST - trampoline(true); -#else - func(); - return 0; -#endif -} - CALL(71, media_has_extension, int, (const char *name)) #ifdef MEDIA_LAYER_TRAMPOLINE_GUEST return trampoline(false, copy_array(name)); diff --git a/mods/src/misc/graphics.cpp b/mods/src/misc/graphics.cpp index 240cd73434..4ae00d652e 100644 --- a/mods/src/misc/graphics.cpp +++ b/mods/src/misc/graphics.cpp @@ -508,11 +508,6 @@ void _init_misc_graphics() { media_disable_vsync(); } - // Force EGL - if (feature_has("Force EGL", server_disabled)) { - media_force_egl(); - } - // Properly Generate Buffers if (feature_has("Proper OpenGL Buffer Generation", server_enabled)) { overwrite_calls(Common_anGenBuffers, anGenBuffers_injection);