2020-11-10 15:26:38 +01:00
|
|
|
#include "window.hpp"
|
|
|
|
|
2020-12-22 18:10:01 +01:00
|
|
|
#include <hex.hpp>
|
2021-08-29 22:15:18 +02:00
|
|
|
|
2022-02-01 18:09:40 +01:00
|
|
|
#include <hex/api/plugin_manager.hpp>
|
|
|
|
#include <hex/api/content_registry.hpp>
|
|
|
|
#include <hex/api/imhex_api.hpp>
|
2023-05-11 18:44:50 +02:00
|
|
|
#include <hex/api/layout_manager.hpp>
|
2023-11-18 14:50:43 +01:00
|
|
|
#include <hex/api/shortcut_manager.hpp>
|
2023-12-11 15:54:22 +01:00
|
|
|
#include <hex/api/workspace_manager.hpp>
|
2023-12-13 11:24:25 +01:00
|
|
|
#include <hex/api/tutorial_manager.hpp>
|
2022-02-01 18:09:40 +01:00
|
|
|
|
2021-08-29 22:15:18 +02:00
|
|
|
#include <hex/helpers/utils.hpp>
|
2022-03-04 11:36:37 +01:00
|
|
|
#include <hex/helpers/fs.hpp>
|
2021-08-29 22:15:18 +02:00
|
|
|
#include <hex/helpers/logger.hpp>
|
2020-12-22 18:10:01 +01:00
|
|
|
|
2023-04-07 10:21:27 +02:00
|
|
|
#include <hex/ui/view.hpp>
|
|
|
|
#include <hex/ui/popup.hpp>
|
|
|
|
|
2021-03-06 13:09:20 +01:00
|
|
|
#include <chrono>
|
2021-08-17 13:41:44 +02:00
|
|
|
#include <csignal>
|
2020-11-10 15:26:38 +01:00
|
|
|
|
2021-12-22 15:06:16 +01:00
|
|
|
#include <romfs/romfs.hpp>
|
|
|
|
|
2021-01-13 17:28:27 +01:00
|
|
|
#include <imgui.h>
|
|
|
|
#include <imgui_internal.h>
|
|
|
|
#include <imgui_impl_glfw.h>
|
|
|
|
#include <imgui_impl_opengl3.h>
|
2021-12-31 01:10:06 +01:00
|
|
|
#include <hex/ui/imgui_imhex_extensions.h>
|
2021-03-02 13:48:23 +01:00
|
|
|
#include <implot.h>
|
|
|
|
#include <implot_internal.h>
|
2021-08-17 13:41:19 +02:00
|
|
|
#include <imnodes.h>
|
|
|
|
#include <imnodes_internal.h>
|
2020-11-10 15:26:38 +01:00
|
|
|
|
2023-07-09 12:53:31 +02:00
|
|
|
#include <wolv/utils/string.hpp>
|
|
|
|
|
2020-11-10 15:26:38 +01:00
|
|
|
#include <GLFW/glfw3.h>
|
2023-12-19 23:21:20 +01:00
|
|
|
#include <hex/ui/toast.hpp>
|
2024-01-06 17:38:55 +01:00
|
|
|
#include <wolv/utils/guards.hpp>
|
2024-02-18 02:12:57 +01:00
|
|
|
#include <fmt/printf.h>
|
2021-08-29 14:18:45 +02:00
|
|
|
|
2020-11-10 15:26:38 +01:00
|
|
|
namespace hex {
|
|
|
|
|
2021-03-06 13:09:20 +01:00
|
|
|
using namespace std::literals::chrono_literals;
|
|
|
|
|
2022-02-15 23:07:48 +01:00
|
|
|
Window::Window() {
|
2023-01-31 11:38:26 +01:00
|
|
|
constexpr static auto openEmergencyPopup = [](const std::string &title){
|
|
|
|
TaskManager::doLater([title] {
|
|
|
|
for (const auto &provider : ImHexApi::Provider::getProviders())
|
|
|
|
ImHexApi::Provider::remove(provider, false);
|
|
|
|
|
|
|
|
ImGui::OpenPopup(title.c_str());
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Handle fatal error popups for errors detected during initialization
|
2021-04-18 20:24:42 +02:00
|
|
|
{
|
2022-02-01 18:09:40 +01:00
|
|
|
for (const auto &[argument, value] : ImHexApi::System::getInitArguments()) {
|
|
|
|
if (argument == "no-plugins") {
|
2023-01-31 11:38:26 +01:00
|
|
|
openEmergencyPopup("No Plugins");
|
2024-01-12 23:03:13 +01:00
|
|
|
} else if (argument == "duplicate-plugins") {
|
|
|
|
openEmergencyPopup("Duplicate Plugins loaded");
|
2021-04-18 20:24:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-04-17 15:46:26 +02:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Initialize the window
|
2020-11-10 15:26:38 +01:00
|
|
|
this->initGLFW();
|
|
|
|
this->initImGui();
|
2021-11-28 11:57:52 +01:00
|
|
|
this->setupNativeWindow();
|
2023-02-17 12:03:53 +01:00
|
|
|
this->registerEventHandlers();
|
|
|
|
|
2023-03-21 15:33:43 +01:00
|
|
|
ContentRegistry::Settings::impl::store();
|
2023-12-08 10:29:44 +01:00
|
|
|
EventSettingsChanged::post();
|
|
|
|
EventWindowInitialized::post();
|
|
|
|
EventImHexStartupFinished::post();
|
2023-02-17 12:03:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Window::~Window() {
|
2023-12-08 10:29:44 +01:00
|
|
|
EventProviderDeleted::unsubscribe(this);
|
|
|
|
RequestCloseImHex::unsubscribe(this);
|
|
|
|
RequestUpdateWindowTitle::unsubscribe(this);
|
|
|
|
EventAbnormalTermination::unsubscribe(this);
|
|
|
|
RequestOpenPopup::unsubscribe(this);
|
2023-02-17 12:03:53 +01:00
|
|
|
|
2024-01-28 22:14:59 +01:00
|
|
|
EventWindowDeinitializing::post(m_window);
|
2023-12-11 15:54:22 +01:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
this->exitImGui();
|
|
|
|
this->exitGLFW();
|
|
|
|
}
|
2021-01-31 00:04:33 +01:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
void Window::registerEventHandlers() {
|
2022-08-09 16:29:52 +02:00
|
|
|
// Initialize default theme
|
2023-12-08 10:29:44 +01:00
|
|
|
RequestChangeTheme::post("Dark");
|
2022-08-09 16:29:52 +02:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Handle the close window request by telling GLFW to shut down
|
2023-12-08 10:29:44 +01:00
|
|
|
RequestCloseImHex::subscribe(this, [this](bool noQuestions) {
|
2023-12-19 13:10:25 +01:00
|
|
|
glfwSetWindowShouldClose(m_window, GLFW_TRUE);
|
2021-08-21 13:55:21 +02:00
|
|
|
|
|
|
|
if (!noQuestions)
|
2023-12-19 13:10:25 +01:00
|
|
|
EventWindowClosing::post(m_window);
|
2021-02-01 19:03:45 +01:00
|
|
|
});
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Handle opening popups
|
2023-12-08 10:29:44 +01:00
|
|
|
RequestOpenPopup::subscribe(this, [this](auto name) {
|
2023-12-19 13:10:25 +01:00
|
|
|
std::scoped_lock lock(m_popupMutex);
|
2022-09-19 16:09:22 +02:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
m_popupsToOpen.push_back(name);
|
2021-09-26 21:18:25 +02:00
|
|
|
});
|
2024-02-10 23:31:05 +01:00
|
|
|
|
|
|
|
LayoutManager::registerLoadCallback([this](std::string_view line) {
|
|
|
|
int width = 0, height = 0;
|
|
|
|
sscanf(line.data(), "MainWindowSize=%d,%d", &width, &height);
|
|
|
|
|
|
|
|
if (width > 0 && height > 0) {
|
|
|
|
TaskManager::doLater([width, height, this]{
|
|
|
|
glfwSetWindowSize(m_window, width, height);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
2020-11-10 15:26:38 +01:00
|
|
|
}
|
|
|
|
|
2024-02-18 02:12:57 +01:00
|
|
|
void handleException() {
|
|
|
|
try {
|
|
|
|
throw;
|
|
|
|
} catch (const std::exception &e) {
|
|
|
|
log::fatal("Unhandled exception: {}", e.what());
|
|
|
|
} catch (...) {
|
|
|
|
log::fatal("Unhandled exception: Unknown exception");
|
|
|
|
}
|
|
|
|
}
|
2023-10-04 12:00:32 +02:00
|
|
|
|
2024-02-18 02:12:57 +01:00
|
|
|
void errorRecoverLogCallback(void*, const char* fmt, ...) {
|
|
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
2023-09-07 20:33:49 +02:00
|
|
|
|
2024-02-18 02:12:57 +01:00
|
|
|
std::string message;
|
|
|
|
message.resize(std::vsnprintf(nullptr, 0, fmt, args));
|
|
|
|
std::vsnprintf(message.data(), message.size(), fmt, args);
|
|
|
|
message.resize(message.size() - 1);
|
|
|
|
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
log::error("{}", message);
|
|
|
|
}
|
2024-01-21 18:39:51 +01:00
|
|
|
|
2024-02-18 02:12:57 +01:00
|
|
|
void Window::fullFrame() {
|
|
|
|
try {
|
|
|
|
this->frameBegin();
|
|
|
|
this->frame();
|
|
|
|
this->frameEnd();
|
|
|
|
} catch (...) {
|
|
|
|
ImGui::ErrorCheckEndFrameRecover(errorRecoverLogCallback, nullptr);
|
|
|
|
handleException();
|
|
|
|
}
|
2023-09-07 20:33:49 +02:00
|
|
|
}
|
|
|
|
|
2020-11-10 15:26:38 +01:00
|
|
|
void Window::loop() {
|
2023-12-19 13:10:25 +01:00
|
|
|
while (!glfwWindowShouldClose(m_window)) {
|
|
|
|
m_lastStartFrameTime = glfwGetTime();
|
2023-05-11 23:22:06 +02:00
|
|
|
|
2024-01-02 17:11:52 +01:00
|
|
|
// Determine if the application should be in long sleep mode
|
2023-12-27 13:54:00 +01:00
|
|
|
bool shouldLongSleep = !m_unlockFrameRate;
|
|
|
|
|
2024-01-02 17:11:52 +01:00
|
|
|
// Wait 5 frames before actually enabling the long sleep mode to make animations not stutter
|
|
|
|
constexpr static auto LongSleepTimeout = 5;
|
2023-12-27 13:54:00 +01:00
|
|
|
static i32 lockTimeout = 0;
|
|
|
|
if (!shouldLongSleep) {
|
2024-01-02 17:11:52 +01:00
|
|
|
lockTimeout = LongSleepTimeout;
|
2023-12-27 13:54:00 +01:00
|
|
|
} else if (lockTimeout > 0) {
|
|
|
|
lockTimeout -= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (shouldLongSleep && lockTimeout > 0)
|
|
|
|
shouldLongSleep = false;
|
|
|
|
|
|
|
|
m_unlockFrameRate = false;
|
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
if (!glfwGetWindowAttrib(m_window, GLFW_VISIBLE) || glfwGetWindowAttrib(m_window, GLFW_ICONIFIED)) {
|
2023-02-17 12:03:53 +01:00
|
|
|
// If the application is minimized or not visible, don't render anything
|
2021-06-07 18:14:40 +02:00
|
|
|
glfwWaitEvents();
|
2021-12-16 23:48:52 +01:00
|
|
|
} else {
|
2024-01-02 17:11:52 +01:00
|
|
|
// If the application is visible, render a frame
|
|
|
|
|
|
|
|
// If the application is in long sleep mode, only render a frame every 200ms
|
|
|
|
// Long sleep mode is enabled automatically after a few frames if the window content hasn't changed
|
|
|
|
// and no events have been received
|
|
|
|
if (shouldLongSleep) {
|
2023-08-11 22:03:30 +02:00
|
|
|
// Calculate the time until the next frame
|
2024-01-02 17:11:52 +01:00
|
|
|
constexpr static auto LongSleepFPS = 5.0;
|
|
|
|
const double timeout = std::max(0.0, (1.0 / LongSleepFPS) - (glfwGetTime() - m_lastStartFrameTime));
|
2023-02-17 12:03:53 +01:00
|
|
|
|
2024-01-02 17:11:52 +01:00
|
|
|
glfwWaitEventsTimeout(timeout);
|
2024-02-18 02:12:57 +01:00
|
|
|
} else {
|
|
|
|
glfwPollEvents();
|
2022-08-03 23:32:34 +02:00
|
|
|
}
|
2021-12-16 23:48:52 +01:00
|
|
|
}
|
|
|
|
|
2024-02-18 02:12:57 +01:00
|
|
|
m_lastStartFrameTime = glfwGetTime();
|
|
|
|
|
|
|
|
static ImVec2 lastWindowSize = ImHexApi::System::getMainWindowSize();
|
|
|
|
if (ImHexApi::System::impl::isWindowResizable()) {
|
|
|
|
glfwSetWindowSizeLimits(m_window, 480_scaled, 360_scaled, GLFW_DONT_CARE, GLFW_DONT_CARE);
|
|
|
|
lastWindowSize = ImHexApi::System::getMainWindowSize();
|
|
|
|
} else {
|
|
|
|
glfwSetWindowSizeLimits(m_window, lastWindowSize.x, lastWindowSize.y, lastWindowSize.x, lastWindowSize.y);
|
|
|
|
}
|
|
|
|
|
2023-09-07 20:33:49 +02:00
|
|
|
this->fullFrame();
|
2023-08-11 22:03:30 +02:00
|
|
|
|
2023-12-20 13:42:42 +01:00
|
|
|
ImHexApi::System::impl::setLastFrameTime(glfwGetTime() - m_lastStartFrameTime);
|
|
|
|
|
2023-08-11 22:03:30 +02:00
|
|
|
// Limit frame rate
|
|
|
|
// If the target FPS are below 15, use the monitor refresh rate, if it's above 200, don't limit the frame rate
|
|
|
|
const auto targetFPS = ImHexApi::System::getTargetFPS();
|
|
|
|
if (targetFPS < 15) {
|
|
|
|
glfwSwapInterval(1);
|
|
|
|
} else if (targetFPS > 200) {
|
|
|
|
glfwSwapInterval(0);
|
|
|
|
} else {
|
2023-12-27 13:54:00 +01:00
|
|
|
if (!shouldLongSleep) {
|
2023-12-20 12:07:22 +01:00
|
|
|
glfwSwapInterval(0);
|
|
|
|
const auto frameTime = glfwGetTime() - m_lastStartFrameTime;
|
|
|
|
const auto targetFrameTime = 1.0 / targetFPS;
|
|
|
|
if (frameTime < targetFrameTime) {
|
|
|
|
glfwWaitEventsTimeout(targetFrameTime - frameTime);
|
|
|
|
}
|
2023-08-11 22:03:30 +02:00
|
|
|
}
|
|
|
|
}
|
2023-12-11 11:42:33 +01:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
m_lastFrameTime = glfwGetTime() - m_lastStartFrameTime;
|
2020-11-10 15:26:38 +01:00
|
|
|
}
|
2024-02-09 19:42:47 +01:00
|
|
|
|
|
|
|
// Hide the window as soon as the render loop exits to make the window
|
|
|
|
// disappear as soon as it's closed
|
|
|
|
glfwHideWindow(m_window);
|
2020-11-10 15:26:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Window::frameBegin() {
|
2023-02-17 12:03:53 +01:00
|
|
|
// Start new ImGui Frame
|
2020-11-10 15:26:38 +01:00
|
|
|
ImGui_ImplOpenGL3_NewFrame();
|
|
|
|
ImGui_ImplGlfw_NewFrame();
|
|
|
|
ImGui::NewFrame();
|
|
|
|
|
2024-01-29 15:44:18 +01:00
|
|
|
EventFrameBegin::post();
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Handle all undocked floating windows
|
2022-01-24 20:53:17 +01:00
|
|
|
ImGuiViewport *viewport = ImGui::GetMainViewport();
|
2022-07-07 07:16:38 +02:00
|
|
|
ImGui::SetNextWindowPos(viewport->WorkPos);
|
2022-07-06 10:21:38 +02:00
|
|
|
ImGui::SetNextWindowSize(ImHexApi::System::getMainWindowSize() - ImVec2(0, ImGui::GetTextLineHeightWithSpacing()));
|
2020-11-10 15:26:38 +01:00
|
|
|
ImGui::SetNextWindowViewport(viewport->ID);
|
2023-05-11 23:56:51 +02:00
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0F);
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0F);
|
2021-08-21 00:52:11 +02:00
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
2020-11-30 00:03:12 +01:00
|
|
|
|
2022-01-24 20:53:17 +01:00
|
|
|
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse;
|
2020-11-10 15:26:38 +01:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Render main dock space
|
2022-02-06 21:39:10 +01:00
|
|
|
if (ImGui::Begin("ImHexDockSpace", nullptr, windowFlags)) {
|
2021-08-21 00:52:11 +02:00
|
|
|
ImGui::PopStyleVar();
|
2023-11-08 11:53:26 +01:00
|
|
|
|
2021-12-10 16:09:55 +01:00
|
|
|
this->beginNativeWindowFrame();
|
2023-12-11 22:09:13 +01:00
|
|
|
} else {
|
|
|
|
ImGui::PopStyleVar();
|
2020-11-11 14:41:44 +01:00
|
|
|
}
|
2020-11-23 23:57:19 +01:00
|
|
|
ImGui::End();
|
2021-08-21 00:52:11 +02:00
|
|
|
ImGui::PopStyleVar(2);
|
2021-04-21 23:31:51 +02:00
|
|
|
|
2022-02-01 23:57:48 +01:00
|
|
|
// Plugin load error popups. These are not translated because they should always be readable, no matter if any localization could be loaded or not
|
|
|
|
{
|
2023-11-10 20:47:08 +01:00
|
|
|
auto drawPluginFolderTable = [] {
|
2023-11-16 22:24:06 +01:00
|
|
|
ImGuiExt::UnderlinedText("Plugin folders");
|
2023-07-22 22:59:05 +02:00
|
|
|
if (ImGui::BeginTable("plugins", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY | ImGuiTableFlags_SizingFixedFit, ImVec2(0, 100_scaled))) {
|
2022-02-01 23:57:48 +01:00
|
|
|
ImGui::TableSetupScrollFreeze(0, 1);
|
|
|
|
ImGui::TableSetupColumn("Path", ImGuiTableColumnFlags_WidthStretch, 0.2);
|
|
|
|
ImGui::TableSetupColumn("Exists", ImGuiTableColumnFlags_WidthFixed, ImGui::GetTextLineHeight() * 3);
|
|
|
|
|
|
|
|
ImGui::TableHeadersRow();
|
|
|
|
|
2022-03-04 11:36:37 +01:00
|
|
|
for (const auto &path : fs::getDefaultPaths(fs::ImHexPath::Plugins, true)) {
|
2022-06-09 15:58:40 +02:00
|
|
|
const auto filePath = path / "builtin.hexplug";
|
2022-02-01 23:57:48 +01:00
|
|
|
ImGui::TableNextRow();
|
|
|
|
ImGui::TableNextColumn();
|
2023-03-12 18:43:05 +01:00
|
|
|
ImGui::TextUnformatted(wolv::util::toUTF8String(filePath).c_str());
|
2022-02-01 23:57:48 +01:00
|
|
|
ImGui::TableNextColumn();
|
2023-12-23 21:09:41 +01:00
|
|
|
ImGui::TextUnformatted(wolv::io::fs::exists(filePath) ? "Yes" : "No");
|
2022-02-01 23:57:48 +01:00
|
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// No plugins error popup
|
2022-02-01 23:57:48 +01:00
|
|
|
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5F, 0.5F));
|
|
|
|
if (ImGui::BeginPopupModal("No Plugins", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove)) {
|
|
|
|
ImGui::TextUnformatted("No ImHex plugins loaded (including the built-in plugin)!");
|
|
|
|
ImGui::TextUnformatted("Make sure you installed ImHex correctly.");
|
|
|
|
ImGui::TextUnformatted("There should be at least a 'builtin.hexplug' file in your plugins folder.");
|
|
|
|
|
|
|
|
ImGui::NewLine();
|
|
|
|
|
|
|
|
drawPluginFolderTable();
|
|
|
|
|
2023-07-22 22:59:05 +02:00
|
|
|
ImGui::NewLine();
|
|
|
|
if (ImGui::Button("Close ImHex", ImVec2(ImGui::GetContentRegionAvail().x, 0)))
|
|
|
|
ImHexApi::System::closeImHex(true);
|
|
|
|
|
2022-02-01 23:57:48 +01:00
|
|
|
ImGui::EndPopup();
|
|
|
|
}
|
|
|
|
|
2024-01-12 23:03:13 +01:00
|
|
|
// Duplicate plugins error popup
|
2022-02-01 23:57:48 +01:00
|
|
|
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5F, 0.5F));
|
2024-01-12 23:03:13 +01:00
|
|
|
if (ImGui::BeginPopupModal("Duplicate Plugins loaded", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove)) {
|
|
|
|
ImGui::TextUnformatted("ImHex found and attempted to load multiple plugins with the same name!");
|
2022-02-01 23:57:48 +01:00
|
|
|
ImGui::TextUnformatted("Make sure you installed ImHex correctly and, if needed,");
|
2024-01-12 23:03:13 +01:00
|
|
|
ImGui::TextUnformatted("cleaned up older installations correctly.");
|
|
|
|
ImGui::TextUnformatted("Each plugin should only ever be loaded once.");
|
2022-02-01 23:57:48 +01:00
|
|
|
|
|
|
|
ImGui::NewLine();
|
|
|
|
|
|
|
|
drawPluginFolderTable();
|
|
|
|
|
2023-07-22 22:59:05 +02:00
|
|
|
ImGui::NewLine();
|
|
|
|
if (ImGui::Button("Close ImHex", ImVec2(ImGui::GetContentRegionAvail().x, 0)))
|
|
|
|
ImHexApi::System::closeImHex(true);
|
|
|
|
|
2022-02-01 23:57:48 +01:00
|
|
|
ImGui::EndPopup();
|
|
|
|
}
|
2021-04-21 23:31:51 +02:00
|
|
|
}
|
2023-02-17 12:03:53 +01:00
|
|
|
|
|
|
|
// Open popups when plugins requested it
|
2022-09-19 16:09:22 +02:00
|
|
|
{
|
2023-12-19 13:10:25 +01:00
|
|
|
std::scoped_lock lock(m_popupMutex);
|
|
|
|
m_popupsToOpen.remove_if([](const auto &name) {
|
2022-09-19 16:09:22 +02:00
|
|
|
if (ImGui::IsPopupOpen(name.c_str()))
|
|
|
|
return true;
|
|
|
|
else
|
|
|
|
ImGui::OpenPopup(name.c_str());
|
|
|
|
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
}
|
2022-01-18 00:10:10 +01:00
|
|
|
|
2023-04-07 10:21:27 +02:00
|
|
|
// Draw popup stack
|
|
|
|
{
|
2023-05-10 22:29:17 +02:00
|
|
|
static bool positionSet = false;
|
|
|
|
static bool sizeSet = false;
|
2023-07-23 18:22:53 +02:00
|
|
|
static double popupDelay = -2.0;
|
2024-01-06 17:38:55 +01:00
|
|
|
static u32 displayFrameCount = 0;
|
2023-05-10 22:29:17 +02:00
|
|
|
|
|
|
|
static std::unique_ptr<impl::PopupBase> currPopup;
|
2023-11-21 14:38:01 +01:00
|
|
|
static Lang name("");
|
2023-04-09 23:24:48 +02:00
|
|
|
|
2024-01-06 17:38:55 +01:00
|
|
|
AT_FIRST_TIME {
|
|
|
|
EventImHexClosing::subscribe([] {
|
|
|
|
currPopup.reset();
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2023-05-10 22:29:17 +02:00
|
|
|
if (auto &popups = impl::PopupBase::getOpenPopups(); !popups.empty()) {
|
|
|
|
if (!ImGui::IsPopupOpen(ImGuiID(0), ImGuiPopupFlags_AnyPopupId)) {
|
2023-07-23 18:22:53 +02:00
|
|
|
if (popupDelay <= -1.0) {
|
2023-12-11 11:42:33 +01:00
|
|
|
popupDelay = 0.2;
|
2023-07-23 18:22:53 +02:00
|
|
|
} else {
|
2023-12-19 13:10:25 +01:00
|
|
|
popupDelay -= m_lastFrameTime;
|
2023-12-11 11:42:33 +01:00
|
|
|
if (popupDelay < 0 || popups.size() == 1) {
|
2023-07-23 18:22:53 +02:00
|
|
|
popupDelay = -2.0;
|
|
|
|
currPopup = std::move(popups.back());
|
2023-11-21 14:38:01 +01:00
|
|
|
name = Lang(currPopup->getUnlocalizedName());
|
2024-01-06 17:38:55 +01:00
|
|
|
displayFrameCount = 0;
|
2023-07-23 18:22:53 +02:00
|
|
|
|
|
|
|
ImGui::OpenPopup(name);
|
|
|
|
popups.pop_back();
|
|
|
|
}
|
|
|
|
}
|
2023-05-10 22:29:17 +02:00
|
|
|
}
|
|
|
|
}
|
2023-04-07 10:21:27 +02:00
|
|
|
|
2023-05-10 22:29:17 +02:00
|
|
|
if (currPopup != nullptr) {
|
2023-04-07 10:21:27 +02:00
|
|
|
bool open = true;
|
|
|
|
|
2023-04-16 21:34:29 +02:00
|
|
|
const auto &minSize = currPopup->getMinSize();
|
|
|
|
const auto &maxSize = currPopup->getMaxSize();
|
|
|
|
const bool hasConstraints = minSize.x != 0 && minSize.y != 0 && maxSize.x != 0 && maxSize.y != 0;
|
2023-04-09 15:28:48 +02:00
|
|
|
|
2023-04-16 21:34:29 +02:00
|
|
|
if (hasConstraints)
|
|
|
|
ImGui::SetNextWindowSizeConstraints(minSize, maxSize);
|
|
|
|
else
|
|
|
|
ImGui::SetNextWindowSize(ImVec2(0, 0), ImGuiCond_Appearing);
|
2023-04-08 00:58:53 +02:00
|
|
|
|
2023-04-16 21:34:29 +02:00
|
|
|
auto* closeButton = currPopup->hasCloseButton() ? &open : nullptr;
|
2023-04-09 15:28:48 +02:00
|
|
|
|
2023-04-16 21:34:29 +02:00
|
|
|
const auto flags = currPopup->getFlags() | (!hasConstraints ? (ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize) : ImGuiWindowFlags_None);
|
2023-04-08 00:58:53 +02:00
|
|
|
|
2023-04-16 21:34:29 +02:00
|
|
|
if (!positionSet) {
|
2023-11-23 09:33:47 +01:00
|
|
|
ImGui::SetNextWindowPos(ImHexApi::System::getMainWindowPosition() + (ImHexApi::System::getMainWindowSize() / 2.0F), ImGuiCond_Always, ImVec2(0.5F, 0.5F));
|
2023-11-23 09:19:51 +01:00
|
|
|
|
2023-04-16 21:34:29 +02:00
|
|
|
if (sizeSet)
|
|
|
|
positionSet = true;
|
|
|
|
}
|
2023-04-09 15:28:48 +02:00
|
|
|
|
2023-04-16 21:34:29 +02:00
|
|
|
const auto createPopup = [&](bool displaying) {
|
|
|
|
if (displaying) {
|
2024-01-06 17:38:55 +01:00
|
|
|
displayFrameCount += 1;
|
2023-04-08 00:58:53 +02:00
|
|
|
currPopup->drawContent();
|
2023-04-16 21:34:29 +02:00
|
|
|
|
|
|
|
if (ImGui::GetWindowSize().x > ImGui::GetStyle().FramePadding.x * 10)
|
|
|
|
sizeSet = true;
|
2023-04-08 00:58:53 +02:00
|
|
|
|
2023-10-05 08:55:54 +02:00
|
|
|
// Reset popup position if it's outside the main window when multi-viewport is not enabled
|
|
|
|
// If not done, the popup will be stuck outside the main window and cannot be accessed anymore
|
|
|
|
if ((ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) == ImGuiConfigFlags_None) {
|
|
|
|
const auto currWindowPos = ImGui::GetWindowPos();
|
|
|
|
const auto minWindowPos = ImHexApi::System::getMainWindowPosition() - ImGui::GetWindowSize();
|
|
|
|
const auto maxWindowPos = ImHexApi::System::getMainWindowPosition() + ImHexApi::System::getMainWindowSize();
|
|
|
|
if (currWindowPos.x > maxWindowPos.x || currWindowPos.y > maxWindowPos.y || currWindowPos.x < minWindowPos.x || currWindowPos.y < minWindowPos.y) {
|
|
|
|
positionSet = false;
|
|
|
|
GImGui->MovingWindow = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-08 00:58:53 +02:00
|
|
|
ImGui::EndPopup();
|
|
|
|
}
|
2023-04-16 21:34:29 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
if (currPopup->isModal())
|
|
|
|
createPopup(ImGui::BeginPopupModal(name, closeButton, flags));
|
|
|
|
else
|
|
|
|
createPopup(ImGui::BeginPopup(name, flags));
|
|
|
|
|
2024-01-21 14:22:08 +01:00
|
|
|
if (!ImGui::IsPopupOpen(name) && displayFrameCount < 100) {
|
2024-01-06 17:38:55 +01:00
|
|
|
ImGui::OpenPopup(name);
|
|
|
|
}
|
|
|
|
|
2023-12-26 00:22:47 +01:00
|
|
|
if (currPopup->shouldClose()) {
|
2023-04-16 21:34:29 +02:00
|
|
|
log::debug("Closing popup '{}'", name);
|
|
|
|
positionSet = sizeSet = false;
|
2023-04-07 10:21:27 +02:00
|
|
|
|
2023-05-10 22:29:17 +02:00
|
|
|
currPopup = nullptr;
|
2023-04-09 23:24:48 +02:00
|
|
|
}
|
2023-04-07 10:21:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-19 23:21:20 +01:00
|
|
|
// Draw Toasts
|
|
|
|
{
|
2023-12-26 00:22:47 +01:00
|
|
|
u32 index = 0;
|
|
|
|
for (const auto &toast : impl::ToastBase::getQueuedToasts() | std::views::take(4)) {
|
|
|
|
const auto toastHeight = 60_scaled;
|
2023-12-19 23:21:20 +01:00
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5_scaled);
|
2023-12-26 00:22:47 +01:00
|
|
|
ImGui::SetNextWindowSize(ImVec2(280_scaled, toastHeight));
|
|
|
|
ImGui::SetNextWindowPos((ImHexApi::System::getMainWindowPosition() + ImHexApi::System::getMainWindowSize()) - scaled({ 10, 10 }) - scaled({ 0, (10 + toastHeight) * index }), ImGuiCond_Always, ImVec2(1, 1));
|
|
|
|
if (ImGui::Begin(hex::format("##Toast_{}", index).c_str(), nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing)) {
|
2023-12-19 23:21:20 +01:00
|
|
|
auto drawList = ImGui::GetWindowDrawList();
|
|
|
|
|
|
|
|
const auto min = ImGui::GetWindowPos();
|
|
|
|
const auto max = min + ImGui::GetWindowSize();
|
|
|
|
|
|
|
|
drawList->PushClipRect(min, min + scaled({ 5, 60 }));
|
2023-12-26 00:22:47 +01:00
|
|
|
drawList->AddRectFilled(min, max, toast->getColor(), 5_scaled);
|
2023-12-19 23:21:20 +01:00
|
|
|
drawList->PopClipRect();
|
|
|
|
|
|
|
|
ImGui::Indent();
|
2023-12-26 00:22:47 +01:00
|
|
|
toast->draw();
|
2023-12-19 23:21:20 +01:00
|
|
|
ImGui::Unindent();
|
2023-12-26 00:22:47 +01:00
|
|
|
|
|
|
|
if (ImGui::IsWindowHovered() || toast->getAppearTime() <= 0)
|
|
|
|
toast->setAppearTime(ImGui::GetTime());
|
2023-12-19 23:21:20 +01:00
|
|
|
}
|
|
|
|
ImGui::End();
|
|
|
|
ImGui::PopStyleVar();
|
|
|
|
|
2023-12-26 00:22:47 +01:00
|
|
|
index += 1;
|
2023-12-19 23:21:20 +01:00
|
|
|
}
|
|
|
|
|
2023-12-26 00:22:47 +01:00
|
|
|
std::erase_if(impl::ToastBase::getQueuedToasts(), [](const auto &toast){
|
|
|
|
return toast->getAppearTime() > 0 && (toast->getAppearTime() + impl::ToastBase::VisibilityTime) < ImGui::GetTime();
|
|
|
|
});
|
2023-12-19 23:21:20 +01:00
|
|
|
}
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Run all deferred calls
|
2022-08-17 16:15:36 +02:00
|
|
|
TaskManager::runDeferredCalls();
|
2020-11-10 15:26:38 +01:00
|
|
|
}
|
|
|
|
|
2021-06-07 18:14:40 +02:00
|
|
|
void Window::frame() {
|
2022-07-30 22:01:49 +02:00
|
|
|
auto &io = ImGui::GetIO();
|
2023-02-17 12:03:53 +01:00
|
|
|
|
|
|
|
// Loop through all views and draw them
|
2023-03-21 15:33:43 +01:00
|
|
|
for (auto &[name, view] : ContentRegistry::Views::impl::getEntries()) {
|
2021-12-12 21:46:48 +01:00
|
|
|
ImGui::GetCurrentContext()->NextWindowData.ClearFlags();
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Draw always visible views
|
2023-11-21 13:47:50 +01:00
|
|
|
view->drawAlwaysVisibleContent();
|
2021-06-07 18:14:40 +02:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Skip views that shouldn't be processed currently
|
2021-06-07 18:14:40 +02:00
|
|
|
if (!view->shouldProcess())
|
|
|
|
continue;
|
|
|
|
|
2023-12-06 13:49:58 +01:00
|
|
|
const auto openViewCount = std::ranges::count_if(ContentRegistry::Views::impl::getEntries(), [](const auto &entry) {
|
2023-12-30 23:52:25 +01:00
|
|
|
const auto &[unlocalizedName, openView] = entry;
|
2023-12-06 13:49:58 +01:00
|
|
|
|
2023-12-30 23:52:25 +01:00
|
|
|
return openView->hasViewMenuItemEntry() && openView->shouldProcess();
|
2023-12-06 13:49:58 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
ImGuiWindowClass windowClass = {};
|
|
|
|
|
|
|
|
windowClass.DockNodeFlagsOverrideSet |= ImGuiDockNodeFlags_NoCloseButton;
|
|
|
|
|
|
|
|
if (openViewCount <= 1 || LayoutManager::isLayoutLocked())
|
|
|
|
windowClass.DockNodeFlagsOverrideSet |= ImGuiDockNodeFlags_NoTabBar;
|
|
|
|
|
|
|
|
ImGui::SetNextWindowClass(&windowClass);
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Draw view
|
2023-11-21 13:47:50 +01:00
|
|
|
view->draw();
|
2023-12-13 23:03:39 +01:00
|
|
|
view->trackViewOpenState();
|
2021-12-12 00:41:44 +01:00
|
|
|
|
2023-11-17 14:46:21 +01:00
|
|
|
if (view->getWindowOpenState()) {
|
2022-02-01 22:09:44 +01:00
|
|
|
auto window = ImGui::FindWindowByName(view->getName().c_str());
|
2022-01-11 20:29:06 +01:00
|
|
|
bool hasWindow = window != nullptr;
|
2022-02-01 22:09:44 +01:00
|
|
|
bool focused = false;
|
2021-12-23 15:11:38 +01:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Get the currently focused view
|
2023-10-30 21:53:44 +01:00
|
|
|
if (hasWindow && (window->Flags & ImGuiWindowFlags_Popup) != ImGuiWindowFlags_Popup) {
|
|
|
|
auto windowName = View::toWindowName(name);
|
|
|
|
ImGui::Begin(windowName.c_str());
|
2021-12-31 11:01:22 +01:00
|
|
|
|
2023-10-30 21:53:44 +01:00
|
|
|
// Detect if the window is focused
|
2022-01-29 21:48:59 +01:00
|
|
|
focused = ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_NoPopupHierarchy);
|
2023-10-30 21:53:44 +01:00
|
|
|
|
|
|
|
// Dock the window if it's not already docked
|
|
|
|
if (view->didWindowJustOpen() && !ImGui::IsWindowDocked()) {
|
|
|
|
ImGui::DockBuilderDockWindow(windowName.c_str(), ImHexApi::System::getMainDockSpaceId());
|
2023-12-13 23:03:39 +01:00
|
|
|
EventViewOpened::post(view.get());
|
2023-10-30 21:53:44 +01:00
|
|
|
}
|
|
|
|
|
2021-12-23 15:11:38 +01:00
|
|
|
ImGui::End();
|
|
|
|
}
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Pass on currently pressed keys to the shortcut handler
|
2023-12-19 13:10:25 +01:00
|
|
|
for (const auto &key : m_pressedKeys) {
|
2021-12-23 15:11:38 +01:00
|
|
|
ShortcutManager::process(view, io.KeyCtrl, io.KeyAlt, io.KeyShift, io.KeySuper, focused, key);
|
|
|
|
}
|
|
|
|
}
|
2021-06-07 18:14:40 +02:00
|
|
|
}
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Handle global shortcuts
|
2023-12-19 13:10:25 +01:00
|
|
|
for (const auto &key : m_pressedKeys) {
|
2023-11-17 14:46:21 +01:00
|
|
|
ShortcutManager::processGlobals(io.KeyCtrl, io.KeyAlt, io.KeyShift, io.KeySuper, key);
|
2022-07-30 22:01:49 +02:00
|
|
|
}
|
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
m_pressedKeys.clear();
|
2021-06-07 18:14:40 +02:00
|
|
|
}
|
|
|
|
|
2020-11-10 15:26:38 +01:00
|
|
|
void Window::frameEnd() {
|
2023-12-08 10:29:44 +01:00
|
|
|
EventFrameEnd::post();
|
2022-01-18 00:10:10 +01:00
|
|
|
|
2023-12-13 11:24:25 +01:00
|
|
|
TutorialManager::drawTutorial();
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Clean up all tasks that are done
|
2022-08-17 16:15:36 +02:00
|
|
|
TaskManager::collectGarbage();
|
|
|
|
|
2021-12-10 16:09:55 +01:00
|
|
|
this->endNativeWindowFrame();
|
2023-02-17 12:03:53 +01:00
|
|
|
|
2024-02-18 02:12:57 +01:00
|
|
|
ImGui::ErrorCheckEndFrameRecover(errorRecoverLogCallback, nullptr);
|
|
|
|
|
2023-12-27 13:54:00 +01:00
|
|
|
// Finalize ImGui frame
|
2020-11-10 15:26:38 +01:00
|
|
|
ImGui::Render();
|
2020-11-17 13:58:50 +01:00
|
|
|
|
2023-12-27 13:54:00 +01:00
|
|
|
// Hash the draw data to determine if anything changed on the screen
|
|
|
|
// If not, there's no point in sending the draw data off to the GPU and swapping buffers
|
|
|
|
bool shouldRender = false;
|
|
|
|
{
|
|
|
|
u32 drawDataHash = 0;
|
|
|
|
static u32 previousDrawDataHash = 0;
|
|
|
|
|
|
|
|
for (const auto viewPort : ImGui::GetPlatformIO().Viewports) {
|
|
|
|
auto drawData = viewPort->DrawData;
|
|
|
|
for (int n = 0; n < drawData->CmdListsCount; n++) {
|
2023-12-27 16:33:49 +01:00
|
|
|
const ImDrawList *cmdList = drawData->CmdLists[n];
|
|
|
|
drawDataHash = ImHashData(cmdList->VtxBuffer.Data, cmdList->VtxBuffer.Size * sizeof(ImDrawVert), drawDataHash);
|
2023-12-27 13:54:00 +01:00
|
|
|
}
|
|
|
|
for (int n = 0; n < drawData->CmdListsCount; n++) {
|
2023-12-27 16:33:49 +01:00
|
|
|
const ImDrawList *cmdList = drawData->CmdLists[n];
|
|
|
|
drawDataHash = ImHashData(cmdList->IdxBuffer.Data, cmdList->IdxBuffer.Size * sizeof(ImDrawIdx), drawDataHash);
|
2023-12-27 13:54:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
shouldRender = drawDataHash != previousDrawDataHash;
|
|
|
|
previousDrawDataHash = drawDataHash;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (shouldRender) {
|
|
|
|
int displayWidth, displayHeight;
|
|
|
|
glfwGetFramebufferSize(m_window, &displayWidth, &displayHeight);
|
|
|
|
glViewport(0, 0, displayWidth, displayHeight);
|
|
|
|
glClearColor(0.00F, 0.00F, 0.00F, 0.00F);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
2020-11-10 15:26:38 +01:00
|
|
|
|
2023-12-27 13:54:00 +01:00
|
|
|
glfwSwapBuffers(m_window);
|
|
|
|
|
|
|
|
m_unlockFrameRate = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
GLFWwindow *backupContext = glfwGetCurrentContext();
|
2020-11-23 15:51:40 +01:00
|
|
|
ImGui::UpdatePlatformWindows();
|
|
|
|
ImGui::RenderPlatformWindowsDefault();
|
2023-12-27 13:54:00 +01:00
|
|
|
glfwMakeContextCurrent(backupContext);
|
2023-05-11 18:44:50 +02:00
|
|
|
|
|
|
|
// Process layout load requests
|
|
|
|
// NOTE: This needs to be done before a new frame is started, otherwise ImGui won't handle docking correctly
|
2023-12-12 00:16:21 +01:00
|
|
|
LayoutManager::process();
|
|
|
|
WorkspaceManager::process();
|
2020-11-10 15:26:38 +01:00
|
|
|
}
|
|
|
|
|
2021-03-01 08:56:49 +01:00
|
|
|
void Window::initGLFW() {
|
2024-01-28 22:14:59 +01:00
|
|
|
auto initialWindowProperties = ImHexApi::System::getInitialWindowProperties();
|
2022-01-24 20:53:17 +01:00
|
|
|
glfwSetErrorCallback([](int error, const char *desc) {
|
2023-07-12 14:33:09 +02:00
|
|
|
if (error == GLFW_PLATFORM_ERROR) {
|
|
|
|
// Ignore error spam caused by Wayland not supporting moving or resizing
|
|
|
|
// windows or querying their position and size.
|
|
|
|
if (std::string_view(desc).contains("Wayland"))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-05-14 21:39:18 +02:00
|
|
|
try {
|
2023-07-12 14:33:09 +02:00
|
|
|
log::error("GLFW Error [0x{:05X}] : {}", error, desc);
|
2023-05-14 21:39:18 +02:00
|
|
|
} catch (const std::system_error &) {
|
|
|
|
// Catch and ignore system error that might be thrown when too many errors are being logged to a file
|
|
|
|
}
|
2020-11-11 14:41:44 +01:00
|
|
|
});
|
|
|
|
|
2022-01-13 14:34:27 +01:00
|
|
|
if (!glfwInit()) {
|
|
|
|
log::fatal("Failed to initialize GLFW!");
|
|
|
|
std::abort();
|
|
|
|
}
|
2020-11-10 15:26:38 +01:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Set up used OpenGL version
|
2022-08-02 08:24:46 +02:00
|
|
|
#if defined(OS_MACOS)
|
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
|
|
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
2022-11-05 21:49:17 +01:00
|
|
|
glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE);
|
impr: Don't force using discrete graphics card on macOS (#1341)
<!--
Please provide as much information as possible about what your PR aims
to do.
PRs with no description will most likely be closed until more
information is provided.
If you're planing on changing fundamental behaviour or add big new
features, please open a GitHub Issue first before starting to work on
it.
If it's not something big and you still want to contact us about it,
feel free to do so !
-->
### Problem description
<!-- Describe the bug that you fixed/feature request that you
implemented, or link to an existing issue describing it -->
When starting ImHex on a MacBook model with both integrated and discrete
graphics, it will force the computer to use the discrete graphics card.
This causes increased power usage, meaning the fans will spin up, the
battery will drain faster, etc. This program is not very graphics
intensive, so using the discrete graphics card shouldn't be needed.
### Implementation description
<!-- Explain what you did to correct the problem -->
I changed the
[`GLFW_COCOA_GRAPHICS_SWITCHING`](https://www.glfw.org/docs/latest/window_guide.html#window_hints_osx)
setting in GLFW to not enforce using the discrete graphics.
### Screenshots
<!-- If your change is visual, take a screenshot showing it. Ideally,
make before/after sceenshots -->
### Additional things
<!-- Anything else you would like to say -->
My editor is configured to automatically remove trailing whitespace, so
I hope that those whitespace changes are ok
2023-10-05 08:39:53 +02:00
|
|
|
glfwWindowHint(GLFW_COCOA_GRAPHICS_SWITCHING, GLFW_TRUE);
|
2022-08-02 08:24:46 +02:00
|
|
|
#else
|
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
2024-02-09 22:29:35 +01:00
|
|
|
glfwWindowHint(GLFW_DECORATED, ImHexApi::System::isBorderlessWindowModeEnabled() ? GL_FALSE : GL_TRUE);
|
2022-08-02 08:24:46 +02:00
|
|
|
#endif
|
2020-11-10 15:26:38 +01:00
|
|
|
|
2021-04-21 20:06:48 +02:00
|
|
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
2023-05-22 12:00:35 +02:00
|
|
|
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE);
|
2021-11-28 11:57:52 +01:00
|
|
|
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
|
2023-10-04 12:00:32 +02:00
|
|
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
|
2020-11-30 00:03:12 +01:00
|
|
|
|
2024-01-28 22:14:59 +01:00
|
|
|
if (initialWindowProperties.has_value()) {
|
|
|
|
glfwWindowHint(GLFW_MAXIMIZED, initialWindowProperties->maximized);
|
2023-07-31 11:17:37 +02:00
|
|
|
}
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Create window
|
2023-12-19 13:10:25 +01:00
|
|
|
m_windowTitle = "ImHex";
|
|
|
|
m_window = glfwCreateWindow(1280_scaled, 720_scaled, m_windowTitle.c_str(), nullptr, nullptr);
|
2021-08-18 22:36:46 +02:00
|
|
|
|
2024-01-15 20:52:08 +01:00
|
|
|
ImHexApi::System::impl::setMainWindowHandle(m_window);
|
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
glfwSetWindowUserPointer(m_window, this);
|
2020-11-23 23:57:19 +01:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
if (m_window == nullptr) {
|
2022-01-13 14:34:27 +01:00
|
|
|
log::fatal("Failed to create window!");
|
|
|
|
std::abort();
|
|
|
|
}
|
2020-11-10 15:26:38 +01:00
|
|
|
|
2023-10-30 16:40:14 +01:00
|
|
|
// Force window to be fully opaque by default
|
2023-12-19 13:10:25 +01:00
|
|
|
glfwSetWindowOpacity(m_window, 1.0F);
|
2023-10-30 16:40:14 +01:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
glfwMakeContextCurrent(m_window);
|
2020-11-10 15:26:38 +01:00
|
|
|
glfwSwapInterval(1);
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Center window
|
2021-11-28 11:57:52 +01:00
|
|
|
GLFWmonitor *monitor = glfwGetPrimaryMonitor();
|
|
|
|
if (monitor != nullptr) {
|
|
|
|
const GLFWvidmode *mode = glfwGetVideoMode(monitor);
|
|
|
|
if (mode != nullptr) {
|
|
|
|
int monitorX, monitorY;
|
|
|
|
glfwGetMonitorPos(monitor, &monitorX, &monitorY);
|
|
|
|
|
|
|
|
int windowWidth, windowHeight;
|
2023-12-19 13:10:25 +01:00
|
|
|
glfwGetWindowSize(m_window, &windowWidth, &windowHeight);
|
2021-11-28 11:57:52 +01:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
glfwSetWindowPos(m_window, monitorX + (mode->width - windowWidth) / 2, monitorY + (mode->height - windowHeight) / 2);
|
2021-11-28 11:57:52 +01:00
|
|
|
}
|
|
|
|
}
|
2021-11-28 01:17:48 +01:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Set up initial window position
|
2021-08-18 22:36:46 +02:00
|
|
|
{
|
|
|
|
int x = 0, y = 0;
|
2023-12-19 13:10:25 +01:00
|
|
|
glfwGetWindowPos(m_window, &x, &y);
|
2022-02-01 18:09:40 +01:00
|
|
|
|
2024-01-28 22:14:59 +01:00
|
|
|
if (initialWindowProperties.has_value()) {
|
|
|
|
x = initialWindowProperties->x;
|
|
|
|
y = initialWindowProperties->y;
|
2023-07-31 11:17:37 +02:00
|
|
|
}
|
|
|
|
|
2022-02-01 18:09:40 +01:00
|
|
|
ImHexApi::System::impl::setMainWindowPosition(x, y);
|
2023-12-19 13:10:25 +01:00
|
|
|
glfwSetWindowPos(m_window, x, y);
|
2021-08-18 22:36:46 +02:00
|
|
|
}
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Set up initial window size
|
2021-08-18 22:36:46 +02:00
|
|
|
{
|
|
|
|
int width = 0, height = 0;
|
2023-12-19 13:10:25 +01:00
|
|
|
glfwGetWindowSize(m_window, &width, &height);
|
|
|
|
glfwSetWindowSize(m_window, width, height);
|
2023-07-31 11:17:37 +02:00
|
|
|
|
2024-01-28 22:14:59 +01:00
|
|
|
if (initialWindowProperties.has_value()) {
|
|
|
|
width = initialWindowProperties->width;
|
|
|
|
height = initialWindowProperties->height;
|
2023-07-31 11:17:37 +02:00
|
|
|
}
|
|
|
|
|
2022-02-01 18:09:40 +01:00
|
|
|
ImHexApi::System::impl::setMainWindowSize(width, height);
|
2023-12-19 13:10:25 +01:00
|
|
|
glfwSetWindowSize(m_window, width, height);
|
2021-08-18 22:36:46 +02:00
|
|
|
}
|
2020-12-16 22:43:07 +01:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Register window move callback
|
2023-12-19 13:10:25 +01:00
|
|
|
glfwSetWindowPosCallback(m_window, [](GLFWwindow *window, int x, int y) {
|
2022-02-01 18:09:40 +01:00
|
|
|
ImHexApi::System::impl::setMainWindowPosition(x, y);
|
2020-12-16 22:43:07 +01:00
|
|
|
|
2021-08-21 13:55:21 +02:00
|
|
|
if (auto g = ImGui::GetCurrentContext(); g == nullptr || g->WithinFrameScope) return;
|
2021-06-07 18:14:40 +02:00
|
|
|
|
2022-01-24 20:53:17 +01:00
|
|
|
auto win = static_cast<Window *>(glfwGetWindowUserPointer(window));
|
2023-12-27 21:23:54 +01:00
|
|
|
win->m_unlockFrameRate = true;
|
|
|
|
|
2024-02-18 02:12:57 +01:00
|
|
|
win->fullFrame();
|
2021-08-18 22:36:46 +02:00
|
|
|
});
|
2020-12-16 22:43:07 +01:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Register window resize callback
|
2023-12-19 13:10:25 +01:00
|
|
|
glfwSetWindowSizeCallback(m_window, [](GLFWwindow *window, int width, int height) {
|
2022-07-06 10:21:38 +02:00
|
|
|
if (!glfwGetWindowAttrib(window, GLFW_ICONIFIED))
|
|
|
|
ImHexApi::System::impl::setMainWindowSize(width, height);
|
2021-06-07 18:14:40 +02:00
|
|
|
|
2021-08-21 13:55:21 +02:00
|
|
|
if (auto g = ImGui::GetCurrentContext(); g == nullptr || g->WithinFrameScope) return;
|
2021-08-18 22:36:46 +02:00
|
|
|
|
2022-01-24 20:53:17 +01:00
|
|
|
auto win = static_cast<Window *>(glfwGetWindowUserPointer(window));
|
2023-12-27 21:23:54 +01:00
|
|
|
win->m_unlockFrameRate = true;
|
|
|
|
|
2024-02-18 02:12:57 +01:00
|
|
|
win->fullFrame();
|
2023-02-16 08:53:23 +01:00
|
|
|
});
|
|
|
|
|
2023-12-27 21:23:54 +01:00
|
|
|
glfwSetCursorPosCallback(m_window, [](GLFWwindow *window, double, double) {
|
|
|
|
if (auto g = ImGui::GetCurrentContext(); g == nullptr || g->WithinFrameScope) return;
|
|
|
|
|
|
|
|
auto win = static_cast<Window *>(glfwGetWindowUserPointer(window));
|
|
|
|
win->m_unlockFrameRate = true;
|
|
|
|
});
|
|
|
|
|
2024-02-03 23:56:08 +01:00
|
|
|
glfwSetWindowFocusCallback(m_window, [](GLFWwindow *, int focused) {
|
|
|
|
EventWindowFocused::post(focused == GLFW_TRUE);
|
|
|
|
});
|
|
|
|
|
2023-10-04 12:00:32 +02:00
|
|
|
#if !defined(OS_WEB)
|
|
|
|
// Register key press callback
|
2023-12-29 11:29:31 +01:00
|
|
|
glfwSetInputMode(m_window, GLFW_LOCK_KEY_MODS, GLFW_TRUE);
|
2023-12-19 13:10:25 +01:00
|
|
|
glfwSetKeyCallback(m_window, [](GLFWwindow *window, int key, int scanCode, int action, int mods) {
|
2023-10-04 12:00:32 +02:00
|
|
|
hex::unused(mods);
|
2022-08-03 23:32:34 +02:00
|
|
|
|
2023-06-02 14:43:45 +02:00
|
|
|
|
2023-11-18 15:18:33 +01:00
|
|
|
// Handle A-Z keys using their ASCII value instead of the keycode
|
|
|
|
if (key >= GLFW_KEY_A && key <= GLFW_KEY_Z) {
|
|
|
|
std::string_view name = glfwGetKeyName(key, scanCode);
|
|
|
|
|
|
|
|
// If the key name is only one character long, use the ASCII value instead
|
|
|
|
// Otherwise the keyboard was set to a non-English layout and the key name
|
|
|
|
// is not the same as the ASCII value
|
|
|
|
if (name.length() == 1) {
|
|
|
|
key = std::toupper(name[0]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-04 12:00:32 +02:00
|
|
|
if (key == GLFW_KEY_UNKNOWN) return;
|
2023-05-23 13:20:18 +02:00
|
|
|
|
2023-10-04 12:00:32 +02:00
|
|
|
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
|
2023-11-17 14:46:21 +01:00
|
|
|
if (key != GLFW_KEY_LEFT_CONTROL && key != GLFW_KEY_RIGHT_CONTROL &&
|
|
|
|
key != GLFW_KEY_LEFT_ALT && key != GLFW_KEY_RIGHT_ALT &&
|
|
|
|
key != GLFW_KEY_LEFT_SHIFT && key != GLFW_KEY_RIGHT_SHIFT &&
|
|
|
|
key != GLFW_KEY_LEFT_SUPER && key != GLFW_KEY_RIGHT_SUPER
|
|
|
|
) {
|
2023-12-27 13:54:00 +01:00
|
|
|
auto win = static_cast<Window *>(glfwGetWindowUserPointer(window));
|
2023-12-27 21:23:54 +01:00
|
|
|
win->m_unlockFrameRate = true;
|
2023-12-29 11:29:31 +01:00
|
|
|
|
|
|
|
if (!(mods & GLFW_MOD_NUM_LOCK)) {
|
|
|
|
if (key == GLFW_KEY_KP_0) key = GLFW_KEY_INSERT;
|
|
|
|
else if (key == GLFW_KEY_KP_1) key = GLFW_KEY_END;
|
|
|
|
else if (key == GLFW_KEY_KP_2) key = GLFW_KEY_DOWN;
|
|
|
|
else if (key == GLFW_KEY_KP_3) key = GLFW_KEY_PAGE_DOWN;
|
|
|
|
else if (key == GLFW_KEY_KP_4) key = GLFW_KEY_LEFT;
|
|
|
|
else if (key == GLFW_KEY_KP_6) key = GLFW_KEY_RIGHT;
|
|
|
|
else if (key == GLFW_KEY_KP_7) key = GLFW_KEY_HOME;
|
|
|
|
else if (key == GLFW_KEY_KP_8) key = GLFW_KEY_UP;
|
|
|
|
else if (key == GLFW_KEY_KP_9) key = GLFW_KEY_PAGE_UP;
|
|
|
|
}
|
|
|
|
|
2023-11-17 14:46:21 +01:00
|
|
|
win->m_pressedKeys.push_back(key);
|
|
|
|
}
|
2023-10-04 12:00:32 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
#endif
|
2020-11-11 14:41:44 +01:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Register window close callback
|
2023-12-19 13:10:25 +01:00
|
|
|
glfwSetWindowCloseCallback(m_window, [](GLFWwindow *window) {
|
2023-12-08 10:29:44 +01:00
|
|
|
EventWindowClosing::post(window);
|
2023-02-17 12:03:53 +01:00
|
|
|
});
|
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
glfwSetWindowSizeLimits(m_window, 480_scaled, 360_scaled, GLFW_DONT_CARE, GLFW_DONT_CARE);
|
2021-11-28 11:57:52 +01:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
glfwShowWindow(m_window);
|
2020-11-10 15:26:38 +01:00
|
|
|
}
|
|
|
|
|
2023-10-04 12:00:32 +02:00
|
|
|
void Window::resize(i32 width, i32 height) {
|
2023-12-19 13:10:25 +01:00
|
|
|
glfwSetWindowSize(m_window, width, height);
|
2023-10-04 12:00:32 +02:00
|
|
|
}
|
|
|
|
|
2020-11-10 15:26:38 +01:00
|
|
|
void Window::initImGui() {
|
|
|
|
IMGUI_CHECKVERSION();
|
2021-02-03 00:56:33 +01:00
|
|
|
|
2023-11-29 23:47:37 +01:00
|
|
|
auto fonts = ImHexApi::Fonts::getFontAtlas();
|
2022-02-01 18:09:40 +01:00
|
|
|
|
2023-12-20 16:31:31 +01:00
|
|
|
if (fonts == nullptr) {
|
|
|
|
fonts = IM_NEW(ImFontAtlas)();
|
|
|
|
|
|
|
|
fonts->AddFontDefault();
|
|
|
|
fonts->Build();
|
|
|
|
}
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Initialize ImGui and all other ImGui extensions
|
2022-02-01 22:09:44 +01:00
|
|
|
GImGui = ImGui::CreateContext(fonts);
|
|
|
|
GImPlot = ImPlot::CreateContext();
|
2021-08-17 13:39:46 +02:00
|
|
|
GImNodes = ImNodes::CreateContext();
|
2021-02-03 00:56:33 +01:00
|
|
|
|
2022-02-01 22:09:44 +01:00
|
|
|
ImGuiIO &io = ImGui::GetIO();
|
2022-01-24 20:53:17 +01:00
|
|
|
ImGuiStyle &style = ImGui::GetStyle();
|
2020-11-23 15:51:40 +01:00
|
|
|
|
2022-12-29 19:26:00 +01:00
|
|
|
ImNodes::GetStyle().Flags = ImNodesStyleFlags_NodeOutline | ImNodesStyleFlags_GridLines;
|
|
|
|
|
2021-02-18 17:10:56 +01:00
|
|
|
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable | ImGuiConfigFlags_NavEnableKeyboard;
|
2022-11-07 00:04:47 +01:00
|
|
|
io.ConfigWindowsMoveFromTitleBarOnly = true;
|
2023-02-16 16:29:41 +01:00
|
|
|
io.FontGlobalScale = 1.0F;
|
2022-03-22 08:20:14 +01:00
|
|
|
|
2022-06-03 10:35:05 +02:00
|
|
|
if (glfwGetPrimaryMonitor() != nullptr) {
|
2024-01-28 22:14:59 +01:00
|
|
|
if (ImHexApi::System::isMutliWindowModeEnabled())
|
2022-03-22 08:20:14 +01:00
|
|
|
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
|
|
|
|
}
|
2021-02-18 17:10:56 +01:00
|
|
|
|
2022-01-11 16:02:05 +01:00
|
|
|
io.ConfigViewportsNoTaskBarIcon = false;
|
2020-11-23 15:51:40 +01:00
|
|
|
|
2021-08-17 13:39:46 +02:00
|
|
|
ImNodes::PushAttributeFlag(ImNodesAttributeFlags_EnableLinkDetachWithDragClick);
|
|
|
|
ImNodes::PushAttributeFlag(ImNodesAttributeFlags_EnableLinkCreationOnSnap);
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Allow ImNodes links to always be detached without holding down any button
|
2021-08-17 13:39:46 +02:00
|
|
|
{
|
2023-02-17 12:03:53 +01:00
|
|
|
static bool always = true;
|
2021-08-17 13:39:46 +02:00
|
|
|
ImNodes::GetIO().LinkDetachWithModifierClick.Modifier = &always;
|
|
|
|
}
|
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
io.UserData = &m_imguiCustomData;
|
2021-02-25 00:17:41 +01:00
|
|
|
|
2022-07-29 17:37:30 +02:00
|
|
|
auto scale = ImHexApi::System::getGlobalScale();
|
|
|
|
style.ScaleAllSizes(scale);
|
|
|
|
io.DisplayFramebufferScale = ImVec2(scale, scale);
|
2023-02-17 12:03:00 +01:00
|
|
|
io.Fonts->SetTexID(fonts->TexID);
|
2020-12-11 14:24:42 +01:00
|
|
|
|
2020-11-23 15:51:40 +01:00
|
|
|
style.WindowMenuButtonPosition = ImGuiDir_None;
|
2022-02-01 22:09:44 +01:00
|
|
|
style.IndentSpacing = 10.0F;
|
2023-03-17 19:58:08 +01:00
|
|
|
style.DisplaySafeAreaPadding = ImVec2(0.0F, 0.0F);
|
2020-11-10 15:26:38 +01:00
|
|
|
|
2020-11-15 15:49:21 +01:00
|
|
|
// Install custom settings handler
|
2023-02-17 12:03:53 +01:00
|
|
|
{
|
|
|
|
ImGuiSettingsHandler handler;
|
|
|
|
handler.TypeName = "ImHex";
|
|
|
|
handler.TypeHash = ImHashStr("ImHex");
|
|
|
|
|
|
|
|
handler.ReadOpenFn = [](ImGuiContext *ctx, ImGuiSettingsHandler *, const char *) -> void* { return ctx; };
|
|
|
|
|
2024-02-10 23:31:05 +01:00
|
|
|
handler.ReadLineFn = [](ImGuiContext *, ImGuiSettingsHandler *, void *, const char *line) {
|
|
|
|
LayoutManager::onLoad(line);
|
2023-02-17 12:03:53 +01:00
|
|
|
};
|
|
|
|
|
2024-02-10 23:31:05 +01:00
|
|
|
handler.WriteAllFn = [](ImGuiContext *, ImGuiSettingsHandler *handler, ImGuiTextBuffer *buffer) {
|
|
|
|
buffer->appendf("[%s][General]\n", handler->TypeName);
|
|
|
|
LayoutManager::onStore(buffer);
|
|
|
|
buffer->append("\n");
|
2023-02-17 12:03:53 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
handler.UserData = this;
|
2023-12-13 11:24:25 +01:00
|
|
|
|
|
|
|
auto context = ImGui::GetCurrentContext();
|
|
|
|
context->SettingsHandlers.push_back(handler);
|
|
|
|
context->TestEngineHookItems = true;
|
2023-02-17 12:03:53 +01:00
|
|
|
|
2023-07-07 09:20:33 +02:00
|
|
|
io.IniFilename = nullptr;
|
2023-02-17 12:03:53 +01:00
|
|
|
}
|
2021-03-01 08:56:49 +01:00
|
|
|
|
2023-10-04 12:00:32 +02:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
ImGui_ImplGlfw_InitForOpenGL(m_window, true);
|
2021-04-21 20:06:48 +02:00
|
|
|
|
2022-08-02 13:12:12 +02:00
|
|
|
#if defined(OS_MACOS)
|
|
|
|
ImGui_ImplOpenGL3_Init("#version 150");
|
2023-10-04 12:00:32 +02:00
|
|
|
#elif defined(OS_WEB)
|
|
|
|
ImGui_ImplOpenGL3_Init();
|
2024-02-11 11:44:44 +01:00
|
|
|
ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback("#canvas");
|
2022-08-02 13:12:12 +02:00
|
|
|
#else
|
|
|
|
ImGui_ImplOpenGL3_Init("#version 130");
|
|
|
|
#endif
|
2021-08-21 00:52:11 +02:00
|
|
|
|
|
|
|
for (const auto &plugin : PluginManager::getPlugins())
|
|
|
|
plugin.setImGuiContext(ImGui::GetCurrentContext());
|
2023-02-16 18:06:40 +01:00
|
|
|
|
2023-12-08 10:29:44 +01:00
|
|
|
RequestInitThemeHandlers::post();
|
2020-11-10 15:26:38 +01:00
|
|
|
}
|
|
|
|
|
2022-01-18 00:10:10 +01:00
|
|
|
void Window::exitGLFW() {
|
2023-12-19 13:10:25 +01:00
|
|
|
glfwDestroyWindow(m_window);
|
2020-11-10 15:26:38 +01:00
|
|
|
glfwTerminate();
|
2023-05-15 11:30:24 +02:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
m_window = nullptr;
|
2020-11-10 15:26:38 +01:00
|
|
|
}
|
|
|
|
|
2022-01-18 00:10:10 +01:00
|
|
|
void Window::exitImGui() {
|
2020-11-10 15:26:38 +01:00
|
|
|
ImGui_ImplOpenGL3_Shutdown();
|
|
|
|
ImGui_ImplGlfw_Shutdown();
|
2021-03-02 13:48:23 +01:00
|
|
|
ImPlot::DestroyContext();
|
2020-11-10 15:26:38 +01:00
|
|
|
ImGui::DestroyContext();
|
|
|
|
}
|
|
|
|
|
2021-06-18 20:09:36 +02:00
|
|
|
}
|