Files
IQ.Pilot/iqpilot/tools/cabana/ui/app.cc
2026-09-03 18:17:35 -05:00

211 lines
5.8 KiB
C++

#include "tools/cabana/ui/app.h"
#include <atomic>
#include <cstdio>
#include <stdexcept>
#include <utility>
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
#include "imgui_impl_opengl3_loader.h"
#include "implot.h"
#include <GLFW/glfw3.h>
#include "tools/cabana/settings.h"
#include "tools/cabana/ui/inistate.h"
#include "tools/cabana/ui/util.h"
#include "tools/cabana/ui/mainwin.h"
#include "tools/cabana/utils/util.h"
namespace {
std::atomic<bool> g_signal_exit{false};
std::vector<KeyEvent> g_key_events;
void keyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) {
ImGui_ImplGlfw_KeyCallback(window, key, scancode, action, mods);
if (action == GLFW_PRESS) g_key_events.push_back({key, mods});
}
GLFWwindow *g_focus_lost_window = nullptr;
void windowFocusCallback(GLFWwindow *w, int f) {
#ifdef __APPLE__
ImGui_ImplGlfw_WindowFocusCallback(w, f);
#else
if (f) {
g_focus_lost_window = nullptr;
ImGui_ImplGlfw_WindowFocusCallback(w, f);
} else {
g_focus_lost_window = w;
}
#endif
}
bool anyMouseButtonDown(GLFWwindow *w) {
for (int b = GLFW_MOUSE_BUTTON_1; b <= GLFW_MOUSE_BUTTON_LAST; ++b) {
if (glfwGetMouseButton(w, b) == GLFW_PRESS) return true;
}
return false;
}
void deliverPendingFocusLoss() {
if (g_focus_lost_window == nullptr || anyMouseButtonDown(g_focus_lost_window)) return;
ImGui_ImplGlfw_WindowFocusCallback(g_focus_lost_window, GLFW_FALSE);
g_focus_lost_window = nullptr;
}
void hookViewportCallbacks() {
for (ImGuiViewport *viewport : ImGui::GetPlatformIO().Viewports) {
if (viewport->PlatformHandle == nullptr || viewport == ImGui::GetMainViewport()) continue;
glfwSetKeyCallback((GLFWwindow *)viewport->PlatformHandle, keyCallback);
}
}
void glfwErrorCallback(int error, const char *description) {
fprintf(stderr, "GLFW error %d: %s\n", error, description);
}
void renderFrame(GLFWwindow *window, MainWindow *win) {
glfwPollEvents();
deliverPendingFocusLoss();
utils::drainMainThreadQueue();
int fb_w = 0, fb_h = 0;
glfwGetFramebufferSize(window, &fb_w, &fb_h);
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
win->draw();
ImGui::Render();
const ImVec4 &bg = ImGui::GetStyle().Colors[ImGuiCol_WindowBg];
glViewport(0, 0, fb_w, fb_h);
glClearColor(bg.x, bg.y, bg.z, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
GLFWwindow *backup_context = glfwGetCurrentContext();
ImGui::UpdatePlatformWindows();
hookViewportCallbacks();
ImGui::RenderPlatformWindowsDefault();
glfwMakeContextCurrent(backup_context);
}
glfwSwapBuffers(window);
}
class GlfwRuntime {
public:
GlfwRuntime() {
glfwSetErrorCallback(glfwErrorCallback);
#ifdef __APPLE__
setMacAppName("Cabana");
#endif
if (!glfwInit()) throw std::runtime_error("glfwInit failed");
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
#endif
window_ = glfwCreateWindow(1600, 900, "Cabana", nullptr, nullptr);
if (window_ == nullptr) {
glfwTerminate();
throw std::runtime_error("glfwCreateWindow failed");
}
glfwMakeContextCurrent(window_);
glfwSwapInterval(1);
}
~GlfwRuntime() {
if (window_ != nullptr) glfwDestroyWindow(window_);
glfwTerminate();
}
GlfwRuntime(const GlfwRuntime &) = delete;
GlfwRuntime &operator=(const GlfwRuntime &) = delete;
GLFWwindow *window() const { return window_; }
private:
GLFWwindow *window_ = nullptr;
};
class ImGuiRuntime {
public:
explicit ImGuiRuntime(GLFWwindow *window) {
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImPlot::CreateContext();
ImGuiIO &io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
io.IniFilename = nullptr;
io.LogFilename = nullptr;
if (!ImGui_ImplGlfw_InitForOpenGL(window, true)) {
ImPlot::DestroyContext();
ImGui::DestroyContext();
throw std::runtime_error("ImGui_ImplGlfw_InitForOpenGL failed");
}
glfwSetKeyCallback(window, keyCallback);
glfwSetWindowFocusCallback(window, windowFocusCallback);
if (!ImGui_ImplOpenGL3_Init("#version 330")) {
ImGui_ImplGlfw_Shutdown();
ImPlot::DestroyContext();
ImGui::DestroyContext();
throw std::runtime_error("ImGui_ImplOpenGL3_Init failed");
}
}
~ImGuiRuntime() {
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImPlot::DestroyContext();
ImGui::DestroyContext();
}
ImGuiRuntime(const ImGuiRuntime &) = delete;
ImGuiRuntime &operator=(const ImGuiRuntime &) = delete;
};
}
std::vector<KeyEvent> takeKeyEvents() {
return std::exchange(g_key_events, {});
}
int run(std::unique_ptr<AbstractStream> stream, StreamLoader stream_loader, const std::string &dbc_file) {
try {
UnixSignalHandler signal_handler([]() { g_signal_exit = true; });
GlfwRuntime glfw;
ImGuiRuntime imgui(glfw.window());
loadFonts();
applyTheme(settings.theme);
inistate::addSettingsHandler();
inistate::load();
inistate::applyWindowGeometry(glfw.window());
MainWindow win(glfw.window(), std::move(stream), std::move(stream_loader), dbc_file);
while (!win.exited()) {
if (g_signal_exit.exchange(false)) {
printf("\nexiting...\n");
win.close();
} else if (glfwWindowShouldClose(glfw.window())) {
glfwSetWindowShouldClose(glfw.window(), GLFW_FALSE);
win.close();
}
renderFrame(glfw.window(), &win);
}
return 0;
} catch (const std::exception &e) {
fprintf(stderr, "%s\n", e.what());
return 1;
}
}