Branch data Line data Source code
1 : : #include "GuiLayer.h"
2 : : #include "GuiPlatform.h"
3 : : #include "Version.h"
4 : : #include "Config.h"
5 : : #include <iostream>
6 : : #include <vector>
7 : : #include <algorithm>
8 : : #include <mutex>
9 : : #include <chrono>
10 : :
11 : : #if defined(ENABLE_IMGUI) && !defined(HEADLESS_GUI)
12 : : #include "imgui.h"
13 : : #include "imgui_impl_glfw.h"
14 : : #include "imgui_impl_opengl3.h"
15 : : #include <GLFW/glfw3.h>
16 : : #if defined(__APPLE__)
17 : : #include <OpenGL/gl.h>
18 : : #else
19 : : #include <GL/gl.h>
20 : : #endif
21 : :
22 : : static GLFWwindow* g_window = nullptr;
23 : : #endif
24 : :
25 : : extern std::atomic<bool> g_running;
26 : :
27 : : class LinuxGuiPlatform : public IGuiPlatform {
28 : : public:
29 : 5 : void SetAlwaysOnTop(bool enabled) override {
30 : : #if defined(ENABLE_IMGUI) && !defined(HEADLESS_GUI)
31 : : if (g_window) {
32 : : glfwSetWindowAttrib(g_window, GLFW_FLOATING, enabled ? GLFW_TRUE : GLFW_FALSE);
33 : : }
34 : : #else
35 : 5 : m_always_on_top_mock = enabled;
36 : : #endif
37 : 5 : }
38 : :
39 : 2 : void ResizeWindow(int x, int y, int w, int h) override {
40 : : #if defined(ENABLE_IMGUI) && !defined(HEADLESS_GUI)
41 : : if (g_window) {
42 : : glfwSetWindowSize(g_window, w, h);
43 : : }
44 : : #endif
45 : 2 : }
46 : :
47 : 3 : void SaveWindowGeometry(bool is_graph_mode) override {
48 : : #if defined(ENABLE_IMGUI) && !defined(HEADLESS_GUI)
49 : : if (g_window) {
50 : : int x, y, w, h;
51 : : glfwGetWindowPos(g_window, &x, &y);
52 : : glfwGetWindowSize(g_window, &w, &h);
53 : : Config::win_pos_x = x;
54 : : Config::win_pos_y = y;
55 : : if (is_graph_mode) {
56 : : Config::win_w_large = w;
57 : : Config::win_h_large = h;
58 : : } else {
59 : : Config::win_w_small = w;
60 : : Config::win_h_small = h;
61 : : }
62 : : }
63 : : #endif
64 : 3 : }
65 : :
66 : 2 : bool OpenPresetFileDialog(std::string& outPath) override {
67 : 2 : std::cout << "[GUI] File Dialog not implemented on Linux yet." << std::endl;
68 : 2 : return false;
69 : : }
70 : :
71 : 2 : bool SavePresetFileDialog(std::string& outPath, const std::string& defaultName) override {
72 : 2 : std::cout << "[GUI] File Dialog not implemented on Linux yet." << std::endl;
73 : 2 : return false;
74 : : }
75 : :
76 : 2 : void* GetWindowHandle() override {
77 : : #if defined(ENABLE_IMGUI) && !defined(HEADLESS_GUI)
78 : : return (void*)g_window;
79 : : #else
80 : 2 : return nullptr;
81 : : #endif
82 : : }
83 : :
84 : 5 : bool GetAlwaysOnTopMock() override { return m_always_on_top_mock; }
85 : :
86 : : // Mock access for tests
87 : : bool m_always_on_top_mock = false;
88 : : };
89 : :
90 : : static LinuxGuiPlatform g_platform;
91 : 15 : IGuiPlatform& GetGuiPlatform() { return g_platform; }
92 : :
93 : : // Compatibility Helpers
94 : 1 : void ResizeWindowPlatform(int x, int y, int w, int h) { GetGuiPlatform().ResizeWindow(x, y, w, h); }
95 : 2 : void SaveCurrentWindowGeometryPlatform(bool is_graph_mode) { GetGuiPlatform().SaveWindowGeometry(is_graph_mode); }
96 : 4 : void SetWindowAlwaysOnTopPlatform(bool enabled) { GetGuiPlatform().SetAlwaysOnTop(enabled); }
97 : 1 : bool OpenPresetFileDialogPlatform(std::string& outPath) { return GetGuiPlatform().OpenPresetFileDialog(outPath); }
98 : 1 : bool SavePresetFileDialogPlatform(std::string& outPath, const std::string& defaultName) { return GetGuiPlatform().SavePresetFileDialog(outPath, defaultName); }
99 : :
100 : : #if defined(ENABLE_IMGUI) && !defined(HEADLESS_GUI)
101 : :
102 : : static void glfw_error_callback(int error, const char* description) {
103 : : fprintf(stderr, "Glfw Error %d: %s\n", error, description);
104 : : }
105 : :
106 : : bool GuiLayer::Init() {
107 : : glfwSetErrorCallback(glfw_error_callback);
108 : : if (!glfwInit()) return false;
109 : :
110 : : // GL 3.0 + GLSL 130
111 : : const char* glsl_version = "#version 130";
112 : : glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
113 : : glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
114 : :
115 : : int start_w = Config::show_graphs ? Config::win_w_large : Config::win_w_small;
116 : : int start_h = Config::show_graphs ? Config::win_h_large : Config::win_h_small;
117 : :
118 : : std::string title = "lmuFFB v" + std::string(LMUFFB_VERSION);
119 : : g_window = glfwCreateWindow(start_w, start_h, title.c_str(), NULL, NULL);
120 : : if (g_window == NULL) return false;
121 : :
122 : : glfwMakeContextCurrent(g_window);
123 : : glfwSwapInterval(1); // Enable vsync
124 : :
125 : : if (Config::m_always_on_top) SetWindowAlwaysOnTopPlatform(true);
126 : :
127 : : IMGUI_CHECKVERSION();
128 : : ImGui::CreateContext();
129 : : ImGuiIO& io = ImGui::GetIO(); (void)io;
130 : : io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
131 : :
132 : : SetupGUIStyle();
133 : :
134 : : ImGui_ImplGlfw_InitForOpenGL(g_window, true);
135 : : ImGui_ImplOpenGL3_Init(glsl_version);
136 : :
137 : : return true;
138 : : }
139 : :
140 : : void GuiLayer::Shutdown(FFBEngine& engine) {
141 : : SaveCurrentWindowGeometryPlatform(Config::show_graphs);
142 : : Config::Save(engine);
143 : :
144 : : ImGui_ImplOpenGL3_Shutdown();
145 : : ImGui_ImplGlfw_Shutdown();
146 : : ImGui::DestroyContext();
147 : :
148 : : glfwDestroyWindow(g_window);
149 : : glfwTerminate();
150 : : }
151 : :
152 : : void* GuiLayer::GetWindowHandle() {
153 : : return (void*)g_window;
154 : : }
155 : :
156 : : bool GuiLayer::Render(FFBEngine& engine) {
157 : : if (glfwWindowShouldClose(g_window)) {
158 : : g_running = false;
159 : : return false;
160 : : }
161 : :
162 : : glfwPollEvents();
163 : :
164 : : ImGui_ImplOpenGL3_NewFrame();
165 : : ImGui_ImplGlfw_NewFrame();
166 : : ImGui::NewFrame();
167 : :
168 : : DrawTuningWindow(engine);
169 : : if (Config::show_graphs) DrawDebugWindow(engine);
170 : :
171 : : ImGui::Render();
172 : : int display_w, display_h;
173 : : glfwGetFramebufferSize(g_window, &display_w, &display_h);
174 : : glViewport(0, 0, display_w, display_h);
175 : : glClearColor(0.45f, 0.55f, 0.60f, 1.00f);
176 : : glClear(GL_COLOR_BUFFER_BIT);
177 : : ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
178 : :
179 : : glfwSwapBuffers(g_window);
180 : :
181 : : return true; // Always return true to keep the main loop running at full speed
182 : : }
183 : :
184 : : #else
185 : : // Stub Implementation for Headless Builds (or if IMGUI disabled)
186 : 1 : bool GuiLayer::Init() {
187 : 1 : std::cout << "[GUI] Disabled (Headless Mode)" << std::endl;
188 : 1 : return true;
189 : : }
190 : 1 : void GuiLayer::Shutdown(FFBEngine& engine) {
191 [ + - + - ]: 1 : Config::Save(engine);
192 : 1 : }
193 : 27 : bool GuiLayer::Render(FFBEngine& engine) { return true; }
194 : 1 : void* GuiLayer::GetWindowHandle() { return nullptr; }
195 : :
196 : : #endif
|