Line data Source code
1 : #include "gui/window_context.h"
2 :
3 : #include <SDL2/SDL.h>
4 : #include <imgui.h>
5 : #include <imgui_impl_opengl3.h>
6 : #include <imgui_impl_sdl2.h>
7 :
8 : #include <iostream>
9 :
10 : #include "gui/gl_setup.h"
11 : #include "gui/state/gui_graph_state.h"
12 : #include "gui/theme/theme.h"
13 :
14 : #ifdef __EMSCRIPTEN__
15 : #include <emscripten.h>
16 : #endif
17 :
18 : #pragma GCC diagnostic push
19 : #if defined(__GNUC__) && !defined(__clang__)
20 : #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
21 : #endif
22 : #define NANOSVG_IMPLEMENTATION
23 : #include "nanosvg.h"
24 : #define NANOSVGRAST_IMPLEMENTATION
25 : #include "nanosvgrast.h"
26 : #pragma GCC diagnostic pop
27 :
28 : namespace Amplitron {
29 :
30 20 : WindowContext::WindowContext() = default;
31 :
32 20 : WindowContext::~WindowContext() { shutdown(); }
33 :
34 0 : bool WindowContext::initialize(int width, int height, const std::string& title) {
35 0 : width_ = width;
36 0 : height_ = height;
37 :
38 0 : if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) {
39 0 : std::cerr << "SDL_Init failed: " << SDL_GetError() << std::endl;
40 0 : return false;
41 : }
42 :
43 0 : SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
44 0 : SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, GLSetup::GL_CONTEXT_PROFILE);
45 0 : SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, GLSetup::GL_MAJOR);
46 0 : SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, GLSetup::GL_MINOR);
47 0 : SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
48 0 : SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
49 0 : SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
50 :
51 0 : window_ = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
52 0 : width_, height_,
53 : SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
54 :
55 0 : if (!window_) {
56 0 : std::cerr << "SDL_CreateWindow failed: " << SDL_GetError() << std::endl;
57 0 : SDL_Quit();
58 0 : return false;
59 : }
60 :
61 0 : gl_context_ = SDL_GL_CreateContext(window_);
62 0 : if (!gl_context_) {
63 0 : std::cerr << "SDL_GL_CreateContext failed: " << SDL_GetError() << std::endl;
64 0 : SDL_DestroyWindow(window_);
65 0 : window_ = nullptr;
66 0 : SDL_Quit();
67 0 : return false;
68 : }
69 0 : SDL_GL_MakeCurrent(window_, gl_context_);
70 0 : SDL_GL_SetSwapInterval(1); // vsync
71 :
72 : // Initialize ImGui
73 0 : IMGUI_CHECKVERSION();
74 0 : ImGui::CreateContext();
75 0 : ImGuiIO& io = ImGui::GetIO();
76 0 : io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
77 :
78 0 : ImGui::StyleColorsDark();
79 0 : Theme::ApplyStyle();
80 :
81 0 : if (window_) {
82 0 : SDL_GetWindowSize(window_, &width_, &height_);
83 0 : }
84 :
85 0 : load_fonts();
86 0 : load_icon();
87 :
88 0 : ImGui_ImplSDL2_InitForOpenGL(window_, gl_context_);
89 0 : ImGui_ImplOpenGL3_Init(GLSetup::GLSL_VERSION);
90 :
91 0 : initialized_ = true;
92 0 : return true;
93 0 : }
94 :
95 0 : void WindowContext::load_fonts() {
96 0 : dpi_scale_ = 1.0f;
97 0 : int draw_w = width_, draw_h = height_;
98 0 : SDL_GL_GetDrawableSize(window_, &draw_w, &draw_h);
99 0 : if (width_ > 0) {
100 0 : dpi_scale_ = static_cast<float>(draw_w) / static_cast<float>(width_);
101 0 : }
102 :
103 : #ifdef __EMSCRIPTEN__
104 : if (dpi_scale_ <= 1.0f) {
105 : dpi_scale_ = emscripten_get_device_pixel_ratio();
106 : if (dpi_scale_ <= 0.0f) {
107 : dpi_scale_ = 1.0f;
108 : }
109 : }
110 : #endif
111 :
112 0 : GuiGraphState::get_instance().dpi_scale = dpi_scale_;
113 0 : ImGuiIO& io = ImGui::GetIO();
114 :
115 0 : const float base_font_size = 14.0f;
116 0 : const float scaled_size = base_font_size;
117 :
118 0 : ImFont* loaded_font = nullptr;
119 0 : auto try_font = [&](const std::string& path) {
120 0 : if (!loaded_font) {
121 0 : loaded_font = io.Fonts->AddFontFromFileTTF(path.c_str(), scaled_size);
122 0 : }
123 0 : };
124 :
125 0 : char* base_path = SDL_GetBasePath();
126 0 : if (base_path) {
127 0 : try_font(std::string(base_path) + "assets/fonts/Roboto-Medium.ttf");
128 0 : SDL_free(base_path);
129 0 : }
130 0 : try_font("assets/fonts/Roboto-Medium.ttf");
131 : #ifdef __EMSCRIPTEN__
132 : try_font("/assets/fonts/Roboto-Medium.ttf");
133 : #endif
134 0 : try_font("../assets/fonts/Roboto-Medium.ttf");
135 0 : try_font("external/imgui/misc/fonts/Roboto-Medium.ttf");
136 0 : try_font("../external/imgui/misc/fonts/Roboto-Medium.ttf");
137 :
138 0 : if (!loaded_font) {
139 0 : io.Fonts->AddFontDefault();
140 0 : } else {
141 0 : io.FontGlobalScale = 1.0f;
142 : }
143 0 : }
144 :
145 0 : void WindowContext::load_icon() {
146 0 : std::string icon_path;
147 0 : char* base = SDL_GetBasePath();
148 0 : if (base) {
149 0 : icon_path = std::string(base) + "assets/icon.svg";
150 0 : SDL_free(base);
151 0 : }
152 0 : NSVGimage* svg = nullptr;
153 0 : if (!icon_path.empty()) {
154 0 : svg = nsvgParseFromFile(icon_path.c_str(), "px", 96.0f);
155 0 : }
156 0 : if (!svg) {
157 0 : svg = nsvgParseFromFile("../assets/icon.svg", "px", 96.0f);
158 0 : }
159 0 : if (!svg) {
160 0 : svg = nsvgParseFromFile("assets/icon.svg", "px", 96.0f);
161 0 : }
162 :
163 0 : if (svg) {
164 0 : const int icon_size = 64;
165 0 : NSVGrasterizer* rast = nsvgCreateRasterizer();
166 0 : if (rast) {
167 0 : unsigned char* img = new unsigned char[icon_size * icon_size * 4];
168 0 : nsvgRasterize(rast, svg, 0, 0, icon_size / svg->width, img, icon_size, icon_size,
169 : icon_size * 4);
170 :
171 0 : SDL_Surface* icon =
172 0 : SDL_CreateRGBSurfaceFrom(img, icon_size, icon_size, 32, icon_size * 4, 0x000000FF,
173 : 0x0000FF00, 0x00FF0000, 0xFF000000);
174 0 : if (icon) {
175 0 : SDL_SetWindowIcon(window_, icon);
176 0 : SDL_FreeSurface(icon);
177 0 : }
178 0 : delete[] img;
179 0 : nsvgDeleteRasterizer(rast);
180 0 : }
181 0 : nsvgDelete(svg);
182 0 : } else {
183 0 : std::cerr << "Warning: Could not load assets/icon.svg" << std::endl;
184 : }
185 0 : }
186 :
187 15 : void WindowContext::shutdown() {
188 15 : if (!initialized_) return;
189 0 : initialized_ = false;
190 :
191 0 : ImGui_ImplOpenGL3_Shutdown();
192 0 : ImGui_ImplSDL2_Shutdown();
193 0 : ImGui::DestroyContext();
194 :
195 0 : if (gl_context_) {
196 0 : SDL_GL_DeleteContext(gl_context_);
197 0 : gl_context_ = nullptr;
198 0 : }
199 0 : if (window_) {
200 0 : SDL_DestroyWindow(window_);
201 0 : window_ = nullptr;
202 0 : }
203 0 : SDL_Quit();
204 5 : }
205 :
206 0 : bool WindowContext::poll_events() {
207 0 : SDL_Event event;
208 0 : while (SDL_PollEvent(&event)) {
209 0 : ImGui_ImplSDL2_ProcessEvent(&event);
210 0 : if (event.type == SDL_QUIT) return false;
211 0 : if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE &&
212 0 : event.window.windowID == SDL_GetWindowID(window_))
213 0 : return false;
214 : }
215 0 : if (window_) {
216 0 : SDL_GetWindowSize(window_, &width_, &height_);
217 0 : }
218 0 : return true;
219 0 : }
220 :
221 0 : void WindowContext::begin_frame() {
222 0 : ImGui_ImplOpenGL3_NewFrame();
223 0 : ImGui_ImplSDL2_NewFrame();
224 0 : ImGui::NewFrame();
225 0 : }
226 :
227 0 : void WindowContext::end_frame() {
228 0 : ImGui::Render();
229 0 : int display_w, display_h;
230 0 : SDL_GL_GetDrawableSize(window_, &display_w, &display_h);
231 0 : glViewport(0, 0, display_w, display_h);
232 0 : glClearColor(0.078f, 0.071f, 0.063f, 1.0f);
233 0 : glClear(GL_COLOR_BUFFER_BIT);
234 0 : ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
235 0 : SDL_GL_SwapWindow(window_);
236 0 : }
237 :
238 : } // namespace Amplitron
|