LCOV - code coverage report
Current view: top level - src/gui - gui_manager.cpp (source / functions) Coverage Total Hit
Test: merged.info Lines: 11.6 % 146 17
Test Date: 2026-06-01 11:15:25 Functions: 50.0 % 6 3

            Line data    Source code
       1              : #include "gui/gui_manager.h"
       2              : #include "gui/pedalboard/pedal_board.h"
       3              : #include "gui/theme/theme.h"
       4              : #include "gui/dialogs/file_dialog.h"
       5              : #include "gui/commands/command.h"
       6              : #include "gui/state/gui_graph_state.h"
       7              : #include "preset_manager.h"
       8              : 
       9              : #include "gui/gl_setup.h"
      10              : #include <imgui.h>
      11              : #include <imgui_impl_sdl2.h>
      12              : #include <imgui_impl_opengl3.h>
      13              : #include <iostream>
      14              : #include <cstring>
      15              : #include <cmath>
      16              : #include <algorithm>
      17              : #include <cstdio>
      18              : #include <SDL2/SDL.h>
      19              : #if defined(__APPLE__)
      20              : #  include <TargetConditionals.h>
      21              : #endif
      22              : #if defined(EMSCRIPTEN) || (defined(__APPLE__) && TARGET_OS_IOS)
      23              : #  define AMPLITRON_NO_DESKTOP_SHELL 1
      24              : #endif
      25              : #ifdef __EMSCRIPTEN__
      26              : #  include <emscripten.h>
      27              : #endif
      28              : 
      29              : #pragma GCC diagnostic push
      30              : #if defined(__GNUC__) && !defined(__clang__)
      31              : #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
      32              : #endif
      33              : #define NANOSVG_IMPLEMENTATION
      34              : #include "nanosvg.h"
      35              : #define NANOSVGRAST_IMPLEMENTATION
      36              : #include "nanosvgrast.h"
      37              : #pragma GCC diagnostic pop
      38              : 
      39              : namespace Amplitron {
      40              : 
      41           20 : GuiManager::GuiManager(AudioEngine& engine)
      42           15 :     : engine_(engine),
      43           15 :       command_history_(),
      44            5 :       tuner_pedal_(std::make_shared<TunerPedal>()),
      45           10 :       gui_presets_(engine, command_history_),
      46           20 :       gui_midi_(midi_manager_)
      47            5 : {
      48           15 :     pedal_board_ = std::make_unique<PedalBoard>(engine_, command_history_, &gui_midi_);
      49           15 :     gui_presets_.set_pedal_board(pedal_board_.get());
      50           15 :     gui_presets_.set_midi_manager(&midi_manager_);
      51           15 : }
      52              : 
      53           20 : GuiManager::~GuiManager() {
      54           15 :     shutdown();
      55           25 : }
      56              : 
      57            0 : bool GuiManager::initialize(int width, int height) {
      58            0 :     window_width_ = width;
      59            0 :     window_height_ = height;
      60              : 
      61            0 :     if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) {
      62            0 :         std::cerr << "SDL_Init failed: " << SDL_GetError() << std::endl;
      63            0 :         return false;
      64              :     }
      65              : 
      66            0 :     SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
      67            0 :     SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, GLSetup::GL_CONTEXT_PROFILE);
      68            0 :     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, GLSetup::GL_MAJOR);
      69            0 :     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, GLSetup::GL_MINOR);
      70            0 :     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
      71            0 :     SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
      72            0 :     SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
      73              : 
      74            0 :     window_ = SDL_CreateWindow(
      75              :         Theme::WINDOW_TITLE,
      76              :         SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
      77            0 :         window_width_, window_height_,
      78              :         SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI
      79              :     );
      80              : 
      81            0 :     if (!window_) {
      82            0 :         std::cerr << "SDL_CreateWindow failed: " << SDL_GetError() << std::endl;
      83            0 :         return false;
      84              :     }
      85              : 
      86            0 :     gl_context_ = SDL_GL_CreateContext(window_);
      87            0 :     if (!gl_context_) {
      88            0 :         std::cerr << "SDL_GL_CreateContext failed: " << SDL_GetError() << std::endl;
      89            0 :         return false;
      90              :     }
      91            0 :     SDL_GL_MakeCurrent(window_, gl_context_);
      92            0 :     SDL_GL_SetSwapInterval(1); // vsync
      93              : 
      94              :     // Initialize ImGui
      95            0 :     IMGUI_CHECKVERSION();
      96            0 :     ImGui::CreateContext();
      97            0 :     ImGuiIO& io = ImGui::GetIO();
      98            0 :     io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
      99              : 
     100              :     // Amplitron design system
     101            0 :     ImGui::StyleColorsDark();
     102            0 :     Theme::ApplyStyle();
     103              : 
     104              :     // --- DPI scaling and font loading ---
     105            0 :     float dpi_scale = 1.0f;
     106            0 :     {
     107            0 :         int draw_w = window_width_, draw_h = window_height_;
     108            0 :         SDL_GL_GetDrawableSize(window_, &draw_w, &draw_h);
     109            0 :         if (window_width_ > 0)
     110            0 :             dpi_scale = static_cast<float>(draw_w) / static_cast<float>(window_width_);
     111              :     }
     112              : 
     113              : #ifdef __EMSCRIPTEN__
     114              :     // If SDL didn't pick up a high DPI scaling factor inside the browser, fallback safely
     115              :     if (dpi_scale <= 1.0f) {
     116              :         dpi_scale = emscripten_get_device_pixel_ratio();
     117              :         if (dpi_scale <= 0.0f) dpi_scale = 1.0f;
     118              :     }
     119              : #endif
     120              : 
     121            0 :     GuiGraphState::get_instance().dpi_scale = dpi_scale;
     122              : 
     123            0 :     {
     124            0 :         const float base_font_size = 14.0f;
     125            0 :         const float scaled_size    = base_font_size * dpi_scale;
     126              : 
     127            0 :         ImFont* loaded_font = nullptr;
     128            0 :         auto try_font = [&](const std::string& path) {
     129            0 :             if (!loaded_font)
     130            0 :                 loaded_font = io.Fonts->AddFontFromFileTTF(path.c_str(), scaled_size);
     131            0 :         };
     132              : 
     133            0 :         char* base_path = SDL_GetBasePath();
     134            0 :         if (base_path) {
     135            0 :             try_font(std::string(base_path) + "assets/fonts/Roboto-Medium.ttf");
     136            0 :             SDL_free(base_path);
     137            0 :         }
     138            0 :         try_font("assets/fonts/Roboto-Medium.ttf");
     139            0 :         try_font("../assets/fonts/Roboto-Medium.ttf");
     140            0 :         try_font("external/imgui/misc/fonts/Roboto-Medium.ttf");
     141            0 :         try_font("../external/imgui/misc/fonts/Roboto-Medium.ttf");
     142              : 
     143            0 :         if (!loaded_font) {
     144            0 :             io.Fonts->AddFontDefault();
     145            0 :             io.FontGlobalScale = 1.0f;
     146            0 :         } else {
     147              :             // On all platforms (Desktop & Web viewports), ImGui operates in logical coordinates.
     148              :             // We load fonts at high physical resolution (scaled_size) to keep text sharp,
     149              :             // and set FontGlobalScale to 1.0f / dpi_scale to draw them at their intended logical size.
     150            0 :             io.FontGlobalScale = 1.0f / dpi_scale;
     151              :         }
     152              :     }
     153              : 
     154              :     // Load window icon from assets/icon.svg
     155            0 :     {
     156            0 :         std::string icon_path;
     157            0 :         char* base = SDL_GetBasePath();
     158            0 :         if (base) {
     159            0 :             icon_path = std::string(base) + "assets/icon.svg";
     160            0 :             SDL_free(base);
     161            0 :         }
     162            0 :         NSVGimage* svg = nullptr;
     163            0 :         if (!icon_path.empty())
     164            0 :             svg = nsvgParseFromFile(icon_path.c_str(), "px", 96.0f);
     165            0 :         if (!svg)
     166            0 :             svg = nsvgParseFromFile("../assets/icon.svg", "px", 96.0f);
     167            0 :         if (!svg)
     168            0 :             svg = nsvgParseFromFile("assets/icon.svg", "px", 96.0f);
     169            0 :         if (svg) {
     170            0 :             const int icon_size = 64;
     171            0 :             NSVGrasterizer* rast = nsvgCreateRasterizer();
     172            0 :             if (rast) {
     173            0 :                 unsigned char* img = new unsigned char[icon_size * icon_size * 4];
     174            0 :                 nsvgRasterize(rast, svg, 0, 0,
     175            0 :                              icon_size / svg->width,
     176            0 :                              img, icon_size, icon_size,
     177              :                              icon_size * 4);
     178              : 
     179            0 :                 SDL_Surface* icon = SDL_CreateRGBSurfaceFrom(
     180            0 :                     img, icon_size, icon_size, 32, icon_size * 4,
     181              :                     0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
     182            0 :                 if (icon) {
     183            0 :                     SDL_SetWindowIcon(window_, icon);
     184            0 :                     SDL_FreeSurface(icon);
     185            0 :                 }
     186            0 :                 delete[] img;
     187            0 :                 nsvgDeleteRasterizer(rast);
     188            0 :             }
     189            0 :             nsvgDelete(svg);
     190            0 :         } else {
     191            0 :             std::cerr << "Warning: Could not load assets/icon.svg" << std::endl;
     192              :         }
     193            0 :     }
     194              : 
     195            0 :     ImGui_ImplSDL2_InitForOpenGL(window_, gl_context_);
     196            0 :     ImGui_ImplOpenGL3_Init(GLSetup::GLSL_VERSION);
     197              : 
     198            0 :     pedal_board_ = std::make_unique<PedalBoard>(engine_, command_history_, &gui_midi_);
     199            0 :     gui_presets_.set_pedal_board(pedal_board_.get());
     200            0 :     gui_presets_.set_midi_manager(&midi_manager_);
     201              : 
     202            0 :     PresetManager::load_config();
     203              : 
     204              :     // MIDI: load config first; if no saved mappings, install defaults
     205            0 :     midi_manager_.load_config();
     206            0 :     if (midi_manager_.mappings().empty()) {
     207            0 :         midi_manager_.install_default_mappings();
     208            0 :     }
     209            0 :     midi_manager_.initialize();
     210              : 
     211              : #ifndef AMPLITRON_NO_DESKTOP_SHELL
     212            0 :     update_check_thread_ = std::thread([this]() { this->check_for_updates(); });
     213              : #endif
     214              : 
     215            0 :     initialized_ = true;
     216            0 :     return true;
     217            0 : }
     218              : 
     219              : 
     220           33 : void GuiManager::shutdown() {
     221           33 :     if (!initialized_) return;
     222            0 :     initialized_ = false;
     223              : 
     224            0 :     if (update_check_thread_.joinable()) {
     225            0 :         update_check_thread_.join();
     226            0 :     }
     227              : 
     228            0 :     midi_manager_.save_config();
     229            0 :     midi_manager_.shutdown();
     230              : 
     231            0 :     engine_.clear_tuner_tap();
     232            0 :     pedal_board_.reset();
     233              : 
     234            0 :     ImGui_ImplOpenGL3_Shutdown();
     235            0 :     ImGui_ImplSDL2_Shutdown();
     236            0 :     ImGui::DestroyContext();
     237              : 
     238            0 :     if (gl_context_) {
     239            0 :         SDL_GL_DeleteContext(gl_context_);
     240            0 :         gl_context_ = nullptr;
     241            0 :     }
     242            0 :     if (window_) {
     243            0 :         SDL_DestroyWindow(window_);
     244            0 :         window_ = nullptr;
     245            0 :     }
     246            0 :     SDL_Quit();
     247           11 : }
     248              : 
     249              : } // namespace Amplitron
        

Generated by: LCOV version 2.0-1