1 #include "window.hpp"
2 
3 #include <hex.hpp>
4 #include <hex/api/content_registry.hpp>
5 
6 #include <iostream>
7 #include <numeric>
8 #include <typeinfo>
9 
10 #include <imgui.h>
11 #include <imgui_internal.h>
12 #include <imgui_impl_glfw.h>
13 #include <imgui_impl_opengl3.h>
14 #include <imgui_freetype.h>
15 #include <imgui_imhex_extensions.h>
16 
17 #include "helpers/plugin_handler.hpp"
18 
19 #include <glad/glad.h>
20 #include <GLFW/glfw3.h>
21 
22 namespace hex {
23 
ImHexSettingsHandler_ReadOpenFn(ImGuiContext * ctx,ImGuiSettingsHandler *,const char *)24     void *ImHexSettingsHandler_ReadOpenFn(ImGuiContext *ctx, ImGuiSettingsHandler *, const char *) {
25         return ctx; // Unused, but the return value has to be non-null
26     }
27 
ImHexSettingsHandler_ReadLine(ImGuiContext *,ImGuiSettingsHandler * handler,void *,const char * line)28     void ImHexSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler *handler, void *, const char* line) {
29         for (auto &view : ContentRegistry::Views::getEntries()) {
30             std::string format = std::string(view->getName()) + "=%d";
31             sscanf(line, format.c_str(), &view->getWindowOpenState());
32         }
33     }
34 
ImHexSettingsHandler_WriteAll(ImGuiContext * ctx,ImGuiSettingsHandler * handler,ImGuiTextBuffer * buf)35     void ImHexSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler *handler, ImGuiTextBuffer *buf) {
36         buf->reserve(buf->size() + 0x20); // Ballpark reserve
37 
38         buf->appendf("[%s][General]\n", handler->TypeName);
39 
40         for (auto &view : ContentRegistry::Views::getEntries()) {
41             buf->appendf("%s=%d\n", typeid(*view).name(), view->getWindowOpenState());
42         }
43 
44         buf->append("\n");
45     }
46 
Window(int & argc,char ** & argv)47     Window::Window(int &argc, char **&argv) {
48         hex::SharedData::mainArgc = argc;
49         hex::SharedData::mainArgv = argv;
50 
51         this->initGLFW();
52         this->initImGui();
53 
54         EventManager::subscribe(Events::SettingsChanged, this, [](auto) -> std::any {
55             {
56                 auto theme = ContentRegistry::Settings::getSetting("hex.builtin.setting.interface", "hex.builtin.setting.interface.color");
57 
58                 if (theme.has_value()) {
59                     switch (static_cast<int>(theme.value())) {
60                         default:
61                         case 0: /* Dark theme */
62                             ImGui::StyleColorsDark();
63                             break;
64                         case 1: /* Light theme */
65                             ImGui::StyleColorsLight();
66                             break;
67                         case 2: /* Classic theme */
68                             ImGui::StyleColorsClassic();
69                             break;
70                     }
71                     ImGui::GetStyle().Colors[ImGuiCol_DockingEmptyBg] = ImGui::GetStyle().Colors[ImGuiCol_WindowBg];
72                 }
73             }
74 
75             {
76                 auto language = ContentRegistry::Settings::getSetting("hex.builtin.setting.interface", "hex.builtin.setting.interface.language");
77 
78                 if (language.has_value())
79                     LangEntry::loadLanguage(static_cast<std::string>(language.value()));
80             }
81 
82             return { };
83         });
84 
85         EventManager::subscribe(Events::FileLoaded, this, [this](auto userData) -> std::any {
86             auto path = std::any_cast<std::string>(userData);
87 
88             this->m_recentFiles.push_front(path);
89 
90             {
91                 std::list<std::string> uniques;
92                 for (auto &file : this->m_recentFiles) {
93 
94                     bool exists = false;
95                     for (auto &unique : uniques) {
96                         if (file == unique)
97                             exists = true;
98                     }
99 
100                     if (!exists)
101                         uniques.push_back(file);
102 
103                     if (uniques.size() > 5)
104                         break;
105                 }
106                 this->m_recentFiles = uniques;
107             }
108 
109             {
110                 std::vector<std::string> recentFilesVector;
111                 std::copy(this->m_recentFiles.begin(), this->m_recentFiles.end(), std::back_inserter(recentFilesVector));
112 
113                 ContentRegistry::Settings::write("hex.builtin.setting.imhex", "hex.builtin.setting.imhex.recent_files", recentFilesVector);
114             }
115 
116             return { };
117         });
118 
119         EventManager::subscribe(Events::CloseImHex, this, [this](auto) -> std::any {
120             glfwSetWindowShouldClose(this->m_window, true);
121 
122             return { };
123         });
124 
125         this->initPlugins();
126 
127         ContentRegistry::Settings::load();
128         View::postEvent(Events::SettingsChanged);
129 
130         for (const auto &path : ContentRegistry::Settings::read("hex.builtin.setting.imhex", "hex.builtin.setting.imhex.recent_files"))
131             this->m_recentFiles.push_back(path);
132     }
133 
~Window()134     Window::~Window() {
135         this->deinitImGui();
136         this->deinitGLFW();
137         ContentRegistry::Settings::store();
138 
139         this->deinitPlugins();
140 
141         EventManager::unsubscribe(Events::SettingsChanged, this);
142         EventManager::unsubscribe(Events::FileLoaded, this);
143         EventManager::unsubscribe(Events::CloseImHex, this);
144     }
145 
loop()146     void Window::loop() {
147         while (!glfwWindowShouldClose(this->m_window)) {
148             this->frameBegin();
149 
150             for (const auto &call : View::getDeferedCalls())
151                 call();
152             View::getDeferedCalls().clear();
153 
154             for (auto &view : ContentRegistry::Views::getEntries()) {
155                 view->drawAlwaysVisible();
156 
157                 if (!view->shouldProcess())
158                     continue;
159 
160                 auto minSize = view->getMinSize();
161                 minSize.x *= this->m_globalScale;
162                 minSize.y *= this->m_globalScale;
163 
164                 ImGui::SetNextWindowSizeConstraints(minSize, view->getMaxSize());
165                 view->drawContent();
166             }
167 
168             View::drawCommonInterfaces();
169 
170             #ifdef DEBUG
171                 if (this->m_demoWindowOpen)
172                     ImGui::ShowDemoWindow(&this->m_demoWindowOpen);
173             #endif
174 
175             this->frameEnd();
176         }
177     }
178 
setFont(const std::filesystem::path & path)179     bool Window::setFont(const std::filesystem::path &path) {
180         if (!std::filesystem::exists(path))
181             return false;
182 
183         auto &io = ImGui::GetIO();
184 
185         // If we have a custom font, then rescaling is unnecessary and will make it blurry
186         io.FontGlobalScale = 1.0f;
187 
188         // Load font data & build atlas
189         std::uint8_t *px;
190         int w, h;
191 
192         ImVector<ImWchar> ranges;
193         ImFontGlyphRangesBuilder glyphRangesBuilder;
194         glyphRangesBuilder.AddRanges(io.Fonts->GetGlyphRangesDefault());
195         glyphRangesBuilder.AddRanges(io.Fonts->GetGlyphRangesJapanese());
196         glyphRangesBuilder.AddRanges(io.Fonts->GetGlyphRangesChineseFull());
197         glyphRangesBuilder.AddRanges(io.Fonts->GetGlyphRangesCyrillic());
198         glyphRangesBuilder.AddRanges(io.Fonts->GetGlyphRangesKorean());
199         glyphRangesBuilder.AddRanges(io.Fonts->GetGlyphRangesThai());
200         glyphRangesBuilder.AddRanges(io.Fonts->GetGlyphRangesVietnamese());
201         glyphRangesBuilder.BuildRanges(&ranges);
202 
203         io.Fonts->AddFontFromFileTTF(path.string().c_str(), std::floor(14.0f * this->m_fontScale), nullptr, ranges.Data); // Needs conversion to char for Windows
204         ImGuiFreeType::BuildFontAtlas(io.Fonts, ImGuiFreeType::Monochrome);
205         io.Fonts->GetTexDataAsRGBA32(&px, &w, &h);
206 
207         // Create new font atlas
208         GLuint tex;
209         glGenTextures(1, &tex);
210         glBindTexture(GL_TEXTURE_2D, tex);
211         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
212         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
213         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA8, GL_UNSIGNED_INT, px);
214         io.Fonts->SetTexID(reinterpret_cast<ImTextureID>(tex));
215 
216         return true;
217     }
218 
frameBegin()219     void Window::frameBegin() {
220         glfwPollEvents();
221 
222         ImGui_ImplOpenGL3_NewFrame();
223         ImGui_ImplGlfw_NewFrame();
224         ImGui::NewFrame();
225 
226         ImGuiViewport* viewport = ImGui::GetMainViewport();
227         ImGui::SetNextWindowPos(viewport->GetWorkPos());
228         ImGui::SetNextWindowSize(viewport->GetWorkSize());
229         ImGui::SetNextWindowViewport(viewport->ID);
230         ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
231         ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
232 
233         ImGuiWindowFlags windowFlags = ImGuiWindowFlags_MenuBar     | ImGuiWindowFlags_NoDocking
234                                      | ImGuiWindowFlags_NoTitleBar  | ImGuiWindowFlags_NoCollapse
235                                      | ImGuiWindowFlags_NoMove      | ImGuiWindowFlags_NoResize
236                                      | ImGuiWindowFlags_NoNavFocus  | ImGuiWindowFlags_NoBringToFrontOnFocus
237                                      | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse;
238 
239         ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
240 
241         if (ImGui::Begin("DockSpace", nullptr, windowFlags)) {
242             ImGui::PopStyleVar(2);
243 
244             ImGui::DockSpace(ImGui::GetID("MainDock"), ImVec2(0.0f, ImGui::GetContentRegionAvail().y - ImGui::GetTextLineHeightWithSpacing() - 1));
245 
246             ImGui::Separator();
247             for (const auto &callback : ContentRegistry::Interface::getFooterItems()) {
248                 auto prevIdx = ImGui::GetWindowDrawList()->_VtxCurrentIdx;
249                 callback();
250                 auto currIdx = ImGui::GetWindowDrawList()->_VtxCurrentIdx;
251 
252                 // Only draw separator if something was actually drawn
253                 if (prevIdx != currIdx) {
254                     ImGui::SameLine();
255                     ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
256                     ImGui::SameLine();
257                 }
258             }
259 
260             if (ImGui::BeginMenuBar()) {
261 
262                 for (const auto& menu : { "hex.menu.file"_lang, "hex.menu.edit"_lang, "hex.menu.view"_lang, "hex.menu.help"_lang })
263                     if (ImGui::BeginMenu(menu)) ImGui::EndMenu();
264 
265                 if (ImGui::BeginMenu("hex.menu.view"_lang)) {
266                     for (auto &view : ContentRegistry::Views::getEntries()) {
267                         if (view->hasViewMenuItemEntry())
268                             ImGui::MenuItem((std::string(view->getName()) + " " + "hex.menu.view"_lang).c_str(), "", &view->getWindowOpenState());
269                     }
270                     ImGui::EndMenu();
271                 }
272 
273                 for (auto &view : ContentRegistry::Views::getEntries()) {
274                     view->drawMenu();
275                 }
276 
277                 if (ImGui::BeginMenu("hex.menu.view"_lang)) {
278                     ImGui::Separator();
279                     ImGui::MenuItem("hex.menu.view.fps"_lang, "", &this->m_fpsVisible);
280                     #ifdef DEBUG
281                         ImGui::MenuItem("hex.menu.view.demo"_lang, "", &this->m_demoWindowOpen);
282                     #endif
283                     ImGui::EndMenu();
284                 }
285 
286                 if (this->m_fpsVisible) {
287                     char buffer[0x20];
288                     snprintf(buffer, 0x20, "%.1f FPS", ImGui::GetIO().Framerate);
289 
290                     ImGui::SameLine(ImGui::GetWindowWidth() - ImGui::GetFontSize() * strlen(buffer) + 20);
291                     ImGui::TextUnformatted(buffer);
292                 }
293 
294                 ImGui::EndMenuBar();
295             }
296 
297             if (auto &[key, mods] = Window::s_currShortcut; key != -1) {
298                 for (auto &view : ContentRegistry::Views::getEntries()) {
299                     if (view->shouldProcess()) {
300                         if (view->handleShortcut(key, mods))
301                             break;
302                     }
303                 }
304 
305                 Window::s_currShortcut = { -1, -1 };
306             }
307 
308             bool anyViewOpen = false;
309             for (auto &view : ContentRegistry::Views::getEntries())
310                 anyViewOpen = anyViewOpen || (view->getWindowOpenState() && view->isAvailable());
311 
312             if (!anyViewOpen && SharedData::currentProvider == nullptr) {
313                 char title[256];
314                 ImFormatString(title, IM_ARRAYSIZE(title), "%s/DockSpace_%08X", ImGui::GetCurrentWindow()->Name, ImGui::GetID("MainDock"));
315                 if (ImGui::Begin(title)) {
316                     ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10 * this->m_globalScale, 10 * this->m_globalScale));
317                     if (ImGui::BeginChild("Welcome Screen", ImVec2(0, 0), false, ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_NoDecoration)) {
318                         this->drawWelcomeScreen();
319                     }
320                     ImGui::EndChild();
321                     ImGui::PopStyleVar();
322                 }
323                 ImGui::End();
324             }
325 
326         }
327         ImGui::End();
328     }
329 
frameEnd()330     void Window::frameEnd() {
331         ImGui::Render();
332 
333         int displayWidth, displayHeight;
334         glfwGetFramebufferSize(this->m_window, &displayWidth, &displayHeight);
335         glViewport(0, 0, displayWidth, displayHeight);
336         glClearColor(0.45f, 0.55f, 0.60f, 1.00f);
337         glClear(GL_COLOR_BUFFER_BIT);
338         ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
339 
340         GLFWwindow* backup_current_context = glfwGetCurrentContext();
341         ImGui::UpdatePlatformWindows();
342         ImGui::RenderPlatformWindowsDefault();
343         glfwMakeContextCurrent(backup_current_context);
344 
345         glfwSwapBuffers(this->m_window);
346     }
347 
drawWelcomeScreen()348     void Window::drawWelcomeScreen() {
349         ImGui::UnderlinedText("hex.welcome.header.main"_lang, ImGui::GetStyleColorVec4(ImGuiCol_Text));
350 
351         ImGui::NewLine();
352 
353         const auto availableSpace = ImGui::GetContentRegionAvail();
354         const auto rowHeight = ImGui::GetTextLineHeightWithSpacing() * 6;
355 
356         ImGui::Indent();
357         if (ImGui::BeginTable("Welcome Left", 1, ImGuiTableFlags_NoBordersInBody, ImVec2(availableSpace.x / 2, availableSpace.y))) {
358             ImGui::TableNextRow(ImGuiTableRowFlags_None, rowHeight);
359             ImGui::TableNextColumn();
360             ImGui::TextUnformatted("hex.welcome.header.start"_lang);
361             {
362                 if (ImGui::BulletHyperlink("hex.welcome.start.open_file"_lang))
363                     EventManager::post(Events::OpenWindow, "Open File");
364                 if (ImGui::BulletHyperlink("hex.welcome.start.open_project"_lang))
365                     EventManager::post(Events::OpenWindow, "Open Project");
366             }
367             ImGui::TableNextRow(ImGuiTableRowFlags_None, rowHeight);
368             ImGui::TableNextColumn();
369             ImGui::TextUnformatted("hex.welcome.start.recent"_lang);
370             {
371                 if (!this->m_recentFiles.empty()) {
372                     for (auto &path : this->m_recentFiles) {
373                         if (ImGui::BulletHyperlink(std::filesystem::path(path).filename().string().c_str())) {
374                             EventManager::post(Events::FileDropped, path.c_str());
375                             break;
376                         }
377                     }
378                 }
379             }
380             ImGui::TableNextRow(ImGuiTableRowFlags_None, rowHeight);
381             ImGui::TableNextColumn();
382             ImGui::TextUnformatted("hex.welcome.header.help"_lang);
383             {
384                 if (ImGui::BulletHyperlink("hex.welcome.help.repo"_lang)) hex::openWebpage("hex.welcome.help.repo.link"_lang);
385                 if (ImGui::BulletHyperlink("hex.welcome.help.gethelp"_lang)) hex::openWebpage("hex.welcome.help.gethelp.link"_lang);
386             }
387 
388             ImGui::EndTable();
389         }
390         ImGui::SameLine();
391         if (ImGui::BeginTable("Welcome Right", 1, ImGuiTableFlags_NoBordersInBody, ImVec2(availableSpace.x / 2, availableSpace.y))) {
392             ImGui::TableNextRow(ImGuiTableRowFlags_None, rowHeight);
393             ImGui::TableNextColumn();
394             ImGui::TextUnformatted("hex.welcome.header.customize"_lang);
395             {
396                 if (ImGui::DescriptionButton("hex.welcome.customize.settings.title"_lang, "hex.welcome.customize.settings.desc"_lang, ImVec2(ImGui::GetContentRegionAvail().x * 0.8F, 0)))
397                     EventManager::post(Events::OpenWindow, "hex.view.settings.title");
398             }
399             ImGui::TableNextRow(ImGuiTableRowFlags_None, rowHeight);
400             ImGui::TableNextColumn();
401             ImGui::TextUnformatted("hex.welcome.header.learn"_lang);
402             {
403                 if (ImGui::DescriptionButton("hex.welcome.learn.latest.title"_lang, "hex.welcome.learn.latest.desc"_lang, ImVec2(ImGui::GetContentRegionAvail().x * 0.8F, 0)))
404                     hex::openWebpage("hex.welcome.learn.latest.link"_lang);
405                 if (ImGui::DescriptionButton("hex.welcome.learn.pattern.title"_lang, "hex.welcome.learn.pattern.desc"_lang, ImVec2(ImGui::GetContentRegionAvail().x * 0.8F, 0)))
406                     hex::openWebpage("hex.welcome.learn.pattern.link"_lang);
407                 if (ImGui::DescriptionButton("hex.welcome.learn.plugins.title"_lang, "hex.welcome.learn.plugins.desc"_lang, ImVec2(ImGui::GetContentRegionAvail().x * 0.8F, 0)))
408                     hex::openWebpage("hex.welcome.learn.plugins.link"_lang);
409             }
410 
411             auto extraWelcomeScreenEntries = ContentRegistry::Interface::getWelcomeScreenEntries();
412             if (!extraWelcomeScreenEntries.empty()) {
413                 ImGui::TableNextRow(ImGuiTableRowFlags_None, rowHeight);
414                 ImGui::TableNextColumn();
415                 ImGui::TextUnformatted("hex.welcome.header.various"_lang);
416                 {
417                     for (const auto &callback : extraWelcomeScreenEntries)
418                         callback();
419                 }
420             }
421 
422 
423             ImGui::EndTable();
424         }
425     }
426 
initGLFW()427      void Window::initGLFW() {
428         glfwSetErrorCallback([](int error, const char* desc) {
429             fprintf(stderr, "Glfw Error %d: %s\n", error, desc);
430         });
431 
432         if (!glfwInit())
433             throw std::runtime_error("Failed to initialize GLFW!");
434 
435         #ifdef __APPLE__
436             glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
437         #endif
438 
439         if (auto *monitor = glfwGetPrimaryMonitor(); monitor) {
440             float xscale, yscale;
441             glfwGetMonitorContentScale(monitor, &xscale, &yscale);
442 
443             // In case the horizontal and vertical scale are different, fall back on the average
444             this->m_globalScale = this->m_fontScale = std::midpoint(xscale, yscale);
445         }
446 
447         glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
448         glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
449         glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
450 
451 
452         this->m_window = glfwCreateWindow(1280 * this->m_globalScale, 720 * this->m_globalScale, "ImHex", nullptr, nullptr);
453 
454 
455         if (this->m_window == nullptr)
456             throw std::runtime_error("Failed to create window!");
457 
458         glfwMakeContextCurrent(this->m_window);
459         glfwSwapInterval(1);
460 
461          {
462              int x = 0, y = 0;
463              glfwGetWindowPos(this->m_window, &x, &y);
464              SharedData::windowPos = ImVec2(x, y);
465          }
466 
467          {
468              int width = 0, height = 0;
469              glfwGetWindowSize(this->m_window, &width, &height);
470              SharedData::windowSize = ImVec2(width, height);
471          }
472 
473          glfwSetWindowPosCallback(this->m_window, [](GLFWwindow *window, int x, int y) {
474              SharedData::windowPos = ImVec2(x, y);
475          });
476 
477         glfwSetWindowSizeCallback(this->m_window, [](GLFWwindow *window, int width, int height) {
478             SharedData::windowSize = ImVec2(width, height);
479         });
480 
481         glfwSetKeyCallback(this->m_window, [](GLFWwindow *window, int key, int scancode, int action, int mods) {
482             if (action == GLFW_PRESS) {
483                 Window::s_currShortcut = { key, mods };
484                 auto &io = ImGui::GetIO();
485                 io.KeysDown[key] = true;
486                 io.KeyCtrl  = (mods & GLFW_MOD_CONTROL) != 0;
487                 io.KeyShift = (mods & GLFW_MOD_SHIFT) != 0;
488                 io.KeyAlt   = (mods & GLFW_MOD_ALT) != 0;
489             }
490             else if (action == GLFW_RELEASE) {
491                 auto &io = ImGui::GetIO();
492                 io.KeysDown[key] = false;
493                 io.KeyCtrl  = (mods & GLFW_MOD_CONTROL) != 0;
494                 io.KeyShift = (mods & GLFW_MOD_SHIFT) != 0;
495                 io.KeyAlt   = (mods & GLFW_MOD_ALT) != 0;
496             }
497         });
498 
499         glfwSetDropCallback(this->m_window, [](GLFWwindow *window, int count, const char **paths) {
500             if (count != 1)
501                 return;
502 
503             View::postEvent(Events::FileDropped, paths[0]);
504         });
505 
506         glfwSetWindowCloseCallback(this->m_window, [](GLFWwindow *window) {
507             View::postEvent(Events::WindowClosing, window);
508         });
509 
510 
511         glfwSetWindowSizeLimits(this->m_window, 720, 480, GLFW_DONT_CARE, GLFW_DONT_CARE);
512 
513         if (gladLoadGL() == 0)
514             throw std::runtime_error("Failed to initialize OpenGL loader!");
515     }
516 
initImGui()517     void Window::initImGui() {
518         IMGUI_CHECKVERSION();
519 
520         GImGui = ImGui::CreateContext();
521 
522         ImGuiIO& io = ImGui::GetIO();
523         ImGuiStyle& style = ImGui::GetStyle();
524 
525         io.ConfigFlags |= ImGuiConfigFlags_DockingEnable | ImGuiConfigFlags_NavEnableKeyboard;
526         #if !defined(OS_LINUX)
527             io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
528         #endif
529 
530 
531         io.ConfigViewportsNoTaskBarIcon = false;
532         io.KeyMap[ImGuiKey_Tab]         = GLFW_KEY_TAB;
533         io.KeyMap[ImGuiKey_LeftArrow]   = GLFW_KEY_LEFT;
534         io.KeyMap[ImGuiKey_RightArrow]  = GLFW_KEY_RIGHT;
535         io.KeyMap[ImGuiKey_UpArrow]     = GLFW_KEY_UP;
536         io.KeyMap[ImGuiKey_DownArrow]   = GLFW_KEY_DOWN;
537         io.KeyMap[ImGuiKey_PageUp]      = GLFW_KEY_PAGE_UP;
538         io.KeyMap[ImGuiKey_PageDown]    = GLFW_KEY_PAGE_DOWN;
539         io.KeyMap[ImGuiKey_Home]        = GLFW_KEY_HOME;
540         io.KeyMap[ImGuiKey_End]         = GLFW_KEY_END;
541         io.KeyMap[ImGuiKey_Insert]      = GLFW_KEY_INSERT;
542         io.KeyMap[ImGuiKey_Delete]      = GLFW_KEY_DELETE;
543         io.KeyMap[ImGuiKey_Backspace]   = GLFW_KEY_BACKSPACE;
544         io.KeyMap[ImGuiKey_Space]       = GLFW_KEY_SPACE;
545         io.KeyMap[ImGuiKey_Enter]       = GLFW_KEY_ENTER;
546         io.KeyMap[ImGuiKey_Escape]      = GLFW_KEY_ESCAPE;
547         io.KeyMap[ImGuiKey_KeyPadEnter] = GLFW_KEY_KP_ENTER;
548         io.KeyMap[ImGuiKey_A]           = GLFW_KEY_A;
549         io.KeyMap[ImGuiKey_C]           = GLFW_KEY_C;
550         io.KeyMap[ImGuiKey_V]           = GLFW_KEY_V;
551         io.KeyMap[ImGuiKey_X]           = GLFW_KEY_X;
552         io.KeyMap[ImGuiKey_Y]           = GLFW_KEY_Y;
553         io.KeyMap[ImGuiKey_Z]           = GLFW_KEY_Z;
554 
555         if (this->m_globalScale != 0.0f)
556             style.ScaleAllSizes(this->m_globalScale);
557 
558         #if defined(OS_WINDOWS)
559             std::filesystem::path resourcePath = std::filesystem::path((SharedData::mainArgv)[0]).parent_path();
560         #elif defined(OS_LINUX) || defined(OS_MACOS)
561             std::filesystem::path resourcePath = "/usr/share/ImHex";
562         #else
563             std::filesystem::path resourcePath = "";
564             #warning "Unsupported OS for custom font support"
565         #endif
566 
567         if (!resourcePath.empty() && this->setFont(resourcePath / "font.ttf")) {
568 
569         }
570         else if ((this->m_fontScale != 0.0f) && (this->m_fontScale != 1.0f)) {
571             io.Fonts->Clear();
572 
573             ImFontConfig cfg;
574             cfg.OversampleH = cfg.OversampleV = 1, cfg.PixelSnapH = true;
575             cfg.SizePixels = 13.0f * this->m_fontScale;
576             io.Fonts->AddFontDefault(&cfg);
577         }
578 
579         style.WindowMenuButtonPosition = ImGuiDir_None;
580         style.IndentSpacing = 10.0F;
581 
582         // Install custom settings handler
583         ImGuiSettingsHandler handler;
584         handler.TypeName = "ImHex";
585         handler.TypeHash = ImHashStr("ImHex");
586         handler.ReadOpenFn = ImHexSettingsHandler_ReadOpenFn;
587         handler.ReadLineFn = ImHexSettingsHandler_ReadLine;
588         handler.WriteAllFn = ImHexSettingsHandler_WriteAll;
589         handler.UserData   = this;
590         ImGui::GetCurrentContext()->SettingsHandlers.push_back(handler);
591 
592         ImGui_ImplGlfw_InitForOpenGL(this->m_window, true);
593         ImGui_ImplOpenGL3_Init("#version 150");
594     }
595 
initPlugins()596     void Window::initPlugins() {
597         try {
598             auto pluginFolderPath = std::filesystem::path("/usr/local/share/imhex/plugins");
599             PluginHandler::load(pluginFolderPath.string());
600         } catch (std::runtime_error &e) { return; }
601 
602         for (const auto &plugin : PluginHandler::getPlugins()) {
603             plugin.initializePlugin();
604         }
605     }
606 
deinitGLFW()607     void Window::deinitGLFW() {
608         glfwDestroyWindow(this->m_window);
609         glfwTerminate();
610     }
611 
deinitImGui()612     void Window::deinitImGui() {
613         ImGui_ImplOpenGL3_Shutdown();
614         ImGui_ImplGlfw_Shutdown();
615         ImGui::DestroyContext();
616     }
617 
deinitPlugins()618     void Window::deinitPlugins() {
619         PluginHandler::unload();
620     }
621 
622 }