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