diff --git a/src/core/common/version.hpp b/src/core/common/version.hpp index ce904aa..1eee952 100644 --- a/src/core/common/version.hpp +++ b/src/core/common/version.hpp @@ -6,7 +6,7 @@ namespace clrsync::core { -const std::string GIT_SEMVER = "0.1.6+git.g7641846"; +const std::string GIT_SEMVER = "0.1.6+git.g292a748"; const std::string version_string(); } // namespace clrsync::core diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index a3d79dc..c18c41d 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -21,6 +21,8 @@ set(GUI_SOURCES platform/linux/font_loader_linux.cpp platform/macos/font_loader_macos.cpp platform/windows/font_loader_windows.cpp + backend/glfw_opengl.cpp + ui_manager.cpp ) if(WIN32) diff --git a/src/gui/backend/backend.hpp b/src/gui/backend/backend.hpp new file mode 100644 index 0000000..a02a531 --- /dev/null +++ b/src/gui/backend/backend.hpp @@ -0,0 +1,38 @@ +#ifndef CLRSYNC_BACKEND_HPP +#define CLRSYNC_BACKEND_HPP + +#include + +namespace clrsync::gui::backend{ + + struct window_config{ + std::string title = "clrsync"; + int width = 1280; + int height = 720; + bool resizable = true; + bool decorated = true; + bool transparent_framebuffer = true; + float clear_color[4] = {0.1f, 0.1f, 0.1f, 0.0f}; + }; + class backend_interface{ + public: + virtual ~backend_interface() = default; + + virtual bool initialize(const window_config& config) = 0; + virtual void shutdown() = 0; + virtual bool should_close() const = 0; + virtual void begin_frame() = 0; + virtual void end_frame() = 0; + + virtual void* get_native_window() const = 0; + virtual void* get_graphics_context() const = 0; + + virtual bool init_imgui_backend() = 0; + virtual void shutdown_imgui_backend() = 0; + virtual void imgui_new_frame() = 0; + virtual void imgui_render_draw_data(void* draw_data) = 0; + }; + +} + +#endif // CLRSYNC_BACKEND_HPP diff --git a/src/gui/backend/glfw_opengl.cpp b/src/gui/backend/glfw_opengl.cpp new file mode 100644 index 0000000..a547b9b --- /dev/null +++ b/src/gui/backend/glfw_opengl.cpp @@ -0,0 +1,141 @@ +#include "gui/backend/glfw_opengl.hpp" +#include +#include +#include +#include +#include + +namespace clrsync::gui::backend +{ + +glfw_opengl_backend::glfw_opengl_backend() = default; + +glfw_opengl_backend::~glfw_opengl_backend() +{ + glfw_opengl_backend::shutdown(); +} + +bool glfw_opengl_backend::initialize(const window_config &config) +{ + glfwSetErrorCallback([](int error, const char* description) { + std::cerr << "GLFW Error " << error << ": " << description << std::endl; + }); + + if (!glfwInit()) + { + std::cerr << "Failed to initialize GLFW" << std::endl; + return false; + } + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); +#ifdef __APPLE__ + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); +#else + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); +#endif + glfwWindowHint(GLFW_RESIZABLE, config.resizable ? GLFW_TRUE : GLFW_FALSE); + glfwWindowHint(GLFW_DECORATED, config.decorated ? GLFW_TRUE : GLFW_FALSE); + glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, config.transparent_framebuffer ? GLFW_TRUE : GLFW_FALSE); + + m_window = glfwCreateWindow(config.width, config.height, config.title.c_str(), nullptr, nullptr); + if (!m_window) + { + std::cerr << "Failed to create GLFW window" << std::endl; + glfwTerminate(); + return false; + } + + glfwMakeContextCurrent(m_window); + glfwSwapInterval(1); + + return true; +} + +void glfw_opengl_backend::shutdown() +{ + if (m_window) + { + glfwDestroyWindow(m_window); + m_window = nullptr; + } + glfwTerminate(); +} + +bool glfw_opengl_backend::should_close() const +{ + return m_window && glfwWindowShouldClose(m_window); +} + +void glfw_opengl_backend::begin_frame() +{ + glfwPollEvents(); + + if (m_window) + { + int display_w, display_h; + glfwGetFramebufferSize(m_window, &display_w, &display_h); + glViewport(0, 0, display_w, display_h); + glClearColor(0.1f, 0.1f, 0.1f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + } +} + +void glfw_opengl_backend::end_frame() +{ + if (m_window) + { + glfwSwapBuffers(m_window); + } +} + +void *glfw_opengl_backend::get_native_window() const +{ + return static_cast(m_window); +} + +void *glfw_opengl_backend::get_graphics_context() const +{ + return static_cast(m_window); +} + +bool glfw_opengl_backend::init_imgui_backend() +{ + if (!m_window) + return false; + + if (!ImGui_ImplGlfw_InitForOpenGL(m_window, true)) + return false; + +#ifdef __APPLE__ + const char* glsl_version = "#version 150"; +#else + const char* glsl_version = "#version 120"; +#endif + + if (!ImGui_ImplOpenGL3_Init(glsl_version)) + { + ImGui_ImplGlfw_Shutdown(); + return false; + } + + return true; +} + +void glfw_opengl_backend::shutdown_imgui_backend() +{ + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplGlfw_Shutdown(); +} + +void glfw_opengl_backend::imgui_new_frame() +{ + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplGlfw_NewFrame(); +} + +void glfw_opengl_backend::imgui_render_draw_data(void* draw_data) +{ + ImGui_ImplOpenGL3_RenderDrawData(static_cast(draw_data)); +} + +} // namespace clrsync::gui::backend \ No newline at end of file diff --git a/src/gui/backend/glfw_opengl.hpp b/src/gui/backend/glfw_opengl.hpp new file mode 100644 index 0000000..db93f35 --- /dev/null +++ b/src/gui/backend/glfw_opengl.hpp @@ -0,0 +1,36 @@ +#ifndef CLRSYNC_GLFW_OPENGL_HPP +#define CLRSYNC_GLFW_OPENGL_HPP + +#define GL_SILENCE_DEPRECATION + +#include + +#include "gui/backend/backend.hpp" + +namespace clrsync::gui::backend{ + + class glfw_opengl_backend : public backend_interface{ + public: + glfw_opengl_backend(); + ~glfw_opengl_backend(); + + bool initialize(const window_config& config) override; + void shutdown() override; + bool should_close() const override; + void begin_frame() override; + void end_frame() override; + + void* get_native_window() const override; + void* get_graphics_context() const override; + + bool init_imgui_backend() override; + void shutdown_imgui_backend() override; + void imgui_new_frame() override; + void imgui_render_draw_data(void* draw_data) override; + + private: + GLFWwindow* m_window = nullptr; + }; +} + +#endif // CLRSYNC_GLFW_OPENGL_HPP diff --git a/src/gui/main.cpp b/src/gui/main.cpp index 4209cda..1c83d85 100644 --- a/src/gui/main.cpp +++ b/src/gui/main.cpp @@ -9,8 +9,10 @@ #include "core/config/config.hpp" #include "core/io/toml_file.hpp" +#include "gui/backend/glfw_opengl.hpp" #include "gui/helpers/imgui_helpers.hpp" #include "gui/platform/font_loader.hpp" +#include "gui/ui_manager.hpp" #include "gui/views/about_window.hpp" #include "gui/views/color_scheme_editor.hpp" #include "gui/views/settings_window.hpp" @@ -35,32 +37,42 @@ int main(int, char **) static std::string ini_path = (base.parent_path() / "layout.ini").string(); bool first_time = !std::filesystem::exists(ini_path); - GLFWwindow *window = init_glfw(); - if (!window) - return 1; + printf("GLFW Version: %s\n", glfwGetVersionString()); - printf("GLFV Version: %s\n", glfwGetVersionString()); + auto backend = clrsync::gui::backend::glfw_opengl_backend(); + auto window_config = clrsync::gui::backend::window_config(); + window_config.title = "clrsync"; + window_config.width = 1280; + window_config.height = 720; + window_config.transparent_framebuffer = true; - std::cout << "GLFW runtime platform: "; - switch (glfwGetPlatform()) + if (!backend.initialize(window_config)) { - case GLFW_PLATFORM_WAYLAND: - std::cout << "Wayland\n"; - break; - case GLFW_PLATFORM_X11: - std::cout << "X11\n"; - break; - case GLFW_PLATFORM_COCOA: - std::cout << "Cocoa\n"; - break; - case GLFW_PLATFORM_WIN32: - std::cout << "Win32\n"; - break; - default: - std::cout << "Unknown\n"; + std::cerr << "Failed to initialize backend." << std::endl; + return 1; } - init_imgui(window, ini_path); + std::cout << "GLFW runtime platform: "; + switch (glfwGetPlatform()) { + case GLFW_PLATFORM_WAYLAND: std::cout << "Wayland\n"; break; + case GLFW_PLATFORM_X11: std::cout << "X11\n"; break; + case GLFW_PLATFORM_COCOA: std::cout << "Cocoa\n"; break; + case GLFW_PLATFORM_WIN32: std::cout << "Win32\n"; break; + default: std::cout << "Unknown\n"; + } + + clrsync::gui::ui_manager ui_manager(&backend); + + clrsync::gui::ui_config ui_cfg; + ui_cfg.ini_path = ini_path; + ui_cfg.enable_docking = true; + ui_cfg.enable_keyboard_nav = true; + + if (!ui_manager.initialize(ui_cfg)) + { + std::cerr << "Failed to initialize UI manager." << std::endl; + return 1; + } font_loader loader; @@ -80,11 +92,12 @@ int main(int, char **) templateEditor.apply_current_palette(colorEditor.controller().current_palette()); settingsWindow.set_palette(colorEditor.controller().current_palette()); - while (!glfwWindowShouldClose(window)) + while (!backend.should_close()) { - glfwPollEvents(); + backend.begin_frame(); + loader.push_default_font(); - begin_frame(); + ui_manager.begin_frame(); render_menu_bar(&aboutWindow, &settingsWindow); setup_main_dockspace(first_time); @@ -95,8 +108,12 @@ int main(int, char **) settingsWindow.render(); loader.pop_font(); - end_frame(window); + + ui_manager.end_frame(); + backend.end_frame(); } - shutdown(window); + + ui_manager.shutdown(); + backend.shutdown(); return 0; } \ No newline at end of file diff --git a/src/gui/ui_manager.cpp b/src/gui/ui_manager.cpp new file mode 100644 index 0000000..d377b63 --- /dev/null +++ b/src/gui/ui_manager.cpp @@ -0,0 +1,71 @@ +#include "gui/ui_manager.hpp" +#include "gui/backend/backend.hpp" + +#include + +namespace clrsync::gui +{ +ui_manager::ui_manager(backend::backend_interface* backend) + : m_backend(backend) +{ +} + +ui_manager::~ui_manager() +{ + shutdown(); +} + +bool ui_manager::initialize(const ui_config& config) +{ + IMGUI_CHECKVERSION(); + m_imgui_context = ImGui::CreateContext(); + if (!m_imgui_context) + { + return false; + } + + ImGuiIO& io = ImGui::GetIO(); + + if (config.enable_docking) + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; + + if (config.enable_keyboard_nav) + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; + + if (!config.ini_path.empty()) + io.IniFilename = config.ini_path.c_str(); + + ImGui::StyleColorsDark(); + + if (!m_backend->init_imgui_backend()) + { + ImGui::DestroyContext(static_cast(m_imgui_context)); + m_imgui_context = nullptr; + return false; + } + + return true; +} + +void ui_manager::shutdown() +{ + if (m_imgui_context) + { + m_backend->shutdown_imgui_backend(); + ImGui::DestroyContext(static_cast(m_imgui_context)); + m_imgui_context = nullptr; + } +} + +void ui_manager::begin_frame() +{ + m_backend->imgui_new_frame(); + ImGui::NewFrame(); +} + +void ui_manager::end_frame() +{ + ImGui::Render(); + m_backend->imgui_render_draw_data(ImGui::GetDrawData()); +} +} \ No newline at end of file diff --git a/src/gui/ui_manager.hpp b/src/gui/ui_manager.hpp new file mode 100644 index 0000000..77738d1 --- /dev/null +++ b/src/gui/ui_manager.hpp @@ -0,0 +1,38 @@ +#ifndef CLRSYNC_UI_MANAGER_HPP +#define CLRSYNC_UI_MANAGER_HPP +#include + +namespace clrsync::gui::backend +{ +class backend_interface; +} + +namespace clrsync::gui +{ + +struct ui_config +{ + std::string ini_path = ""; + bool enable_docking = true; + bool enable_keyboard_nav = true; +}; + +class ui_manager +{ +public: + explicit ui_manager(backend::backend_interface *backend); + ~ui_manager(); + + bool initialize(const ui_config& config = ui_config()); + void shutdown(); + + void begin_frame(); + void end_frame(); + +private: + backend::backend_interface *m_backend; + void *m_imgui_context = nullptr; +}; +} + +#endif