1
0
mirror of synced 2025-03-01 23:50:28 +01:00

impr: Improve situation where ImHex crashes on exit when resources aren't cleared properly

This commit is contained in:
WerWolv 2024-01-30 11:21:34 +01:00
parent 0cba735eb3
commit d584edf546
19 changed files with 376 additions and 363 deletions

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <wolv/io/fs.hpp> #include <wolv/io/fs.hpp>
#include <hex/helpers/auto_reset.hpp>
#include <map> #include <map>
#include <string> #include <string>
@ -20,7 +21,7 @@ namespace hex {
static void importFromFile(const std::fs::path &path); static void importFromFile(const std::fs::path &path);
static bool exportToFile(std::fs::path path = {}, std::string workspaceName = {}); static bool exportToFile(std::fs::path path = {}, std::string workspaceName = {});
static const auto& getWorkspaces() { return s_workspaces; } static const auto& getWorkspaces() { return *s_workspaces; }
static const auto& getCurrentWorkspace() { return s_currentWorkspace; } static const auto& getCurrentWorkspace() { return s_currentWorkspace; }
static void reset(); static void reset();
@ -30,8 +31,8 @@ namespace hex {
private: private:
WorkspaceManager() = default; WorkspaceManager() = default;
static std::map<std::string, Workspace> s_workspaces; static AutoReset<std::map<std::string, Workspace>> s_workspaces;
static decltype(s_workspaces)::iterator s_currentWorkspace, s_previousWorkspace; static decltype(s_workspaces)::Type::iterator s_currentWorkspace, s_previousWorkspace;
}; };
} }

View File

@ -0,0 +1,64 @@
#pragma once
#include <hex/api/event_manager.hpp>
namespace hex {
template<typename T>
class AutoReset {
public:
using Type = T;
AutoReset() {
EventImHexClosing::subscribe(this, [this] {
this->reset();
});
}
~AutoReset() {
EventImHexClosing::unsubscribe(this);
}
T* operator->() {
return &m_value;
}
const T* operator->() const {
return &m_value;
}
T& operator*() {
return m_value;
}
const T& operator*() const {
return m_value;
}
operator T&() {
return m_value;
}
operator const T&() const {
return m_value;
}
T& operator=(const T &value) {
m_value = value;
return m_value;
}
T& operator=(T &&value) noexcept {
m_value = std::move(value);
return m_value;
}
void reset() {
m_value = T();
}
private:
T m_value;
};
}

View File

@ -1,27 +1,29 @@
#include <hex/api/achievement_manager.hpp> #include <hex/api/achievement_manager.hpp>
#include <hex/api/event_manager.hpp> #include <hex/api/event_manager.hpp>
#include <hex/helpers/auto_reset.hpp>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
namespace hex { namespace hex {
std::unordered_map<std::string, std::unordered_map<std::string, std::unique_ptr<Achievement>>> &AchievementManager::getAchievements() { std::unordered_map<std::string, std::unordered_map<std::string, std::unique_ptr<Achievement>>> &AchievementManager::getAchievements() {
static std::unordered_map<std::string, std::unordered_map<std::string, std::unique_ptr<Achievement>>> achievements; static AutoReset<std::unordered_map<std::string, std::unordered_map<std::string, std::unique_ptr<Achievement>>>> achievements;
return achievements; return achievements;
} }
std::unordered_map<std::string, std::list<AchievementManager::AchievementNode>>& AchievementManager::getAchievementNodes(bool rebuild) { std::unordered_map<std::string, std::list<AchievementManager::AchievementNode>>& AchievementManager::getAchievementNodes(bool rebuild) {
static std::unordered_map<std::string, std::list<AchievementNode>> nodeCategoryStorage; static AutoReset<std::unordered_map<std::string, std::list<AchievementNode>>> nodeCategoryStorage;
if (!nodeCategoryStorage.empty() || !rebuild) if (!nodeCategoryStorage->empty() || !rebuild)
return nodeCategoryStorage; return nodeCategoryStorage;
nodeCategoryStorage.clear(); nodeCategoryStorage->clear();
// Add all achievements to the node storage // Add all achievements to the node storage
for (auto &[categoryName, achievements] : getAchievements()) { for (auto &[categoryName, achievements] : getAchievements()) {
auto &nodes = nodeCategoryStorage[categoryName]; auto &nodes = (*nodeCategoryStorage)[categoryName];
for (auto &[achievementName, achievement] : achievements) { for (auto &[achievementName, achievement] : achievements) {
nodes.emplace_back(achievement.get()); nodes.emplace_back(achievement.get());
@ -32,21 +34,21 @@ namespace hex {
} }
std::unordered_map<std::string, std::vector<AchievementManager::AchievementNode*>>& AchievementManager::getAchievementStartNodes(bool rebuild) { std::unordered_map<std::string, std::vector<AchievementManager::AchievementNode*>>& AchievementManager::getAchievementStartNodes(bool rebuild) {
static std::unordered_map<std::string, std::vector<AchievementNode*>> startNodes; static AutoReset<std::unordered_map<std::string, std::vector<AchievementNode*>>> startNodes;
if (!startNodes.empty() || !rebuild) if (!startNodes->empty() || !rebuild)
return startNodes; return startNodes;
auto &nodeCategoryStorage = getAchievementNodes(); auto &nodeCategoryStorage = getAchievementNodes();
startNodes.clear(); startNodes->clear();
// Add all parents and children to the nodes // Add all parents and children to the nodes
for (auto &[categoryName, achievements] : nodeCategoryStorage) { for (auto &[categoryName, achievements] : nodeCategoryStorage) {
for (auto &achievementNode : achievements) { for (auto &achievementNode : achievements) {
for (auto &requirement : achievementNode.achievement->getRequirements()) { for (auto &requirement : achievementNode.achievement->getRequirements()) {
for (auto &[requirementCategoryName, requirementAchievements] : nodeCategoryStorage) { for (auto &[requirementCategoryName, requirementAchievements] : nodeCategoryStorage) {
auto iter = std::find_if(requirementAchievements.begin(), requirementAchievements.end(), [&requirement](auto &node) { auto iter = std::ranges::find_if(requirementAchievements, [&requirement](auto &node) {
return node.achievement->getUnlocalizedName() == requirement; return node.achievement->getUnlocalizedName() == requirement;
}); });
@ -59,7 +61,7 @@ namespace hex {
for (auto &requirement : achievementNode.achievement->getVisibilityRequirements()) { for (auto &requirement : achievementNode.achievement->getVisibilityRequirements()) {
for (auto &[requirementCategoryName, requirementAchievements] : nodeCategoryStorage) { for (auto &[requirementCategoryName, requirementAchievements] : nodeCategoryStorage) {
auto iter = std::find_if(requirementAchievements.begin(), requirementAchievements.end(), [&requirement](auto &node) { auto iter = std::ranges::find_if(requirementAchievements, [&requirement](auto &node) {
return node.achievement->getUnlocalizedName() == requirement; return node.achievement->getUnlocalizedName() == requirement;
}); });
@ -74,12 +76,12 @@ namespace hex {
for (auto &[categoryName, achievements] : nodeCategoryStorage) { for (auto &[categoryName, achievements] : nodeCategoryStorage) {
for (auto &achievementNode : achievements) { for (auto &achievementNode : achievements) {
if (!achievementNode.hasParents()) { if (!achievementNode.hasParents()) {
startNodes[categoryName].emplace_back(&achievementNode); (*startNodes)[categoryName].emplace_back(&achievementNode);
} }
for (const auto &parent : achievementNode.parents) { for (const auto &parent : achievementNode.parents) {
if (parent->achievement->getUnlocalizedCategory() != achievementNode.achievement->getUnlocalizedCategory()) if (parent->achievement->getUnlocalizedCategory() != achievementNode.achievement->getUnlocalizedCategory())
startNodes[categoryName].emplace_back(&achievementNode); (*startNodes)[categoryName].emplace_back(&achievementNode);
} }
} }
} }
@ -97,14 +99,12 @@ namespace hex {
auto &[categoryName, achievements] = *categoryIter; auto &[categoryName, achievements] = *categoryIter;
auto achievementIter = achievements.find(unlocalizedName); const auto achievementIter = achievements.find(unlocalizedName);
if (achievementIter == achievements.end()) { if (achievementIter == achievements.end()) {
return; return;
} }
auto &nodes = getAchievementNodes()[categoryName]; const auto &nodes = getAchievementNodes()[categoryName];
for (const auto &node : nodes) { for (const auto &node : nodes) {
auto &achievement = node.achievement; auto &achievement = node.achievement;
@ -238,7 +238,7 @@ namespace hex {
} }
} }
auto result = json.dump(4); const auto result = json.dump(4);
file.setSize(0); file.setSize(0);
file.writeString(result); file.writeString(result);
break; break;

View File

@ -3,6 +3,7 @@
#include <hex/helpers/fs.hpp> #include <hex/helpers/fs.hpp>
#include <hex/helpers/logger.hpp> #include <hex/helpers/logger.hpp>
#include <hex/helpers/auto_reset.hpp>
#include <hex/ui/view.hpp> #include <hex/ui/view.hpp>
#include <hex/data_processor/node.hpp> #include <hex/data_processor/node.hpp>
@ -40,7 +41,7 @@ namespace hex {
} }
nlohmann::json &getSettingsData() { nlohmann::json &getSettingsData() {
static nlohmann::json settings; static AutoReset<nlohmann::json> settings;
return settings; return settings;
} }
@ -89,19 +90,21 @@ namespace hex {
} }
void store() { void store() {
auto settingsData = getSettingsData(); const auto &settingsData = getSettingsData();
// During a crash settings can be empty, causing them to be overwritten. // During a crash settings can be empty, causing them to be overwritten.
if (settingsData.empty()) { if (settingsData.empty()) {
return; return;
} }
const auto result = settingsData.dump(4);
if (result.empty()) {
return;
}
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Config)) { for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Config)) {
wolv::io::File file(dir / SettingsFile, wolv::io::File::Mode::Write); wolv::io::File file(dir / SettingsFile, wolv::io::File::Mode::Write);
if (file.isValid()) { if (file.isValid()) {
auto result = settingsData.dump(4);
file.setSize(0); file.setSize(0);
file.writeString(result); file.writeString(result);
break; break;
@ -137,7 +140,7 @@ namespace hex {
} }
std::vector<Category> &getSettings() { std::vector<Category> &getSettings() {
static std::vector<Category> categories; static AutoReset<std::vector<Category>> categories;
return categories; return categories;
} }
@ -245,7 +248,7 @@ namespace hex {
void ColorPicker::load(const nlohmann::json &data) { void ColorPicker::load(const nlohmann::json &data) {
if (data.is_number()) { if (data.is_number()) {
ImColor color(data.get<u32>()); const ImColor color(data.get<u32>());
m_value = { color.Value.x, color.Value.y, color.Value.z, color.Value.w }; m_value = { color.Value.x, color.Value.y, color.Value.z, color.Value.w };
} else { } else {
log::warn("Invalid data type loaded from settings for color picker!"); log::warn("Invalid data type loaded from settings for color picker!");
@ -264,7 +267,7 @@ namespace hex {
bool DropDown::draw(const std::string &name) { bool DropDown::draw(const std::string &name) {
const char *preview = ""; auto preview = "";
if (static_cast<size_t>(m_value) < m_items.size()) if (static_cast<size_t>(m_value) < m_items.size())
preview = m_items[m_value].c_str(); preview = m_items[m_value].c_str();
@ -408,13 +411,13 @@ namespace hex {
namespace impl { namespace impl {
std::vector<Entry> &getEntries() { std::vector<Entry> &getEntries() {
static std::vector<Entry> commands; static AutoReset<std::vector<Entry>> commands;
return commands; return commands;
} }
std::vector<Handler> &getHandlers() { std::vector<Handler> &getHandlers() {
static std::vector<Handler> commands; static AutoReset<std::vector<Handler>> commands;
return commands; return commands;
} }
@ -521,25 +524,25 @@ namespace hex {
namespace impl { namespace impl {
std::map<std::string, Visualizer> &getVisualizers() { std::map<std::string, Visualizer> &getVisualizers() {
static std::map<std::string, Visualizer> visualizers; static AutoReset<std::map<std::string, Visualizer>> visualizers;
return visualizers; return visualizers;
} }
std::map<std::string, Visualizer> &getInlineVisualizers() { std::map<std::string, Visualizer> &getInlineVisualizers() {
static std::map<std::string, Visualizer> visualizers; static AutoReset<std::map<std::string, Visualizer>> visualizers;
return visualizers; return visualizers;
} }
std::map<std::string, pl::api::PragmaHandler> &getPragmas() { std::map<std::string, pl::api::PragmaHandler> &getPragmas() {
static std::map<std::string, pl::api::PragmaHandler> pragmas; static AutoReset<std::map<std::string, pl::api::PragmaHandler>> pragmas;
return pragmas; return pragmas;
} }
std::vector<FunctionDefinition> &getFunctions() { std::vector<FunctionDefinition> &getFunctions() {
static std::vector<FunctionDefinition> functions; static AutoReset<std::vector<FunctionDefinition>> functions;
return functions; return functions;
} }
@ -555,7 +558,7 @@ namespace hex {
namespace impl { namespace impl {
std::map<std::string, std::unique_ptr<View>> &getEntries() { std::map<std::string, std::unique_ptr<View>> &getEntries() {
static std::map<std::string, std::unique_ptr<View>> views; static AutoReset<std::map<std::string, std::unique_ptr<View>>> views;
return views; return views;
} }
@ -590,7 +593,7 @@ namespace hex {
namespace impl { namespace impl {
std::vector<Entry> &getEntries() { std::vector<Entry> &getEntries() {
static std::vector<Entry> tools; static AutoReset<std::vector<Entry>> tools;
return tools; return tools;
} }
@ -616,7 +619,7 @@ namespace hex {
namespace impl { namespace impl {
std::vector<Entry> &getEntries() { std::vector<Entry> &getEntries() {
static std::vector<Entry> entries; static AutoReset<std::vector<Entry>> entries;
return entries; return entries;
} }
@ -641,7 +644,7 @@ namespace hex {
namespace impl { namespace impl {
std::vector<Entry> &getEntries() { std::vector<Entry> &getEntries() {
static std::vector<Entry> nodes; static AutoReset<std::vector<Entry>> nodes;
return nodes; return nodes;
} }
@ -696,13 +699,13 @@ namespace hex {
namespace impl { namespace impl {
std::map<std::string, std::string> &getLanguages() { std::map<std::string, std::string> &getLanguages() {
static std::map<std::string, std::string> languages; static AutoReset<std::map<std::string, std::string>> languages;
return languages; return languages;
} }
std::map<std::string, std::vector<LocalizationManager::LanguageDefinition>> &getLanguageDefinitions() { std::map<std::string, std::vector<LocalizationManager::LanguageDefinition>> &getLanguageDefinitions() {
static std::map<std::string, std::vector<LocalizationManager::LanguageDefinition>> definitions; static AutoReset<std::map<std::string, std::vector<LocalizationManager::LanguageDefinition>>> definitions;
return definitions; return definitions;
} }
@ -784,7 +787,7 @@ namespace hex {
} }
void addMenuItemToToolbar(const UnlocalizedString& unlocalizedName, ImGuiCustomCol color) { void addMenuItemToToolbar(const UnlocalizedString& unlocalizedName, ImGuiCustomCol color) {
auto maxIndex = std::ranges::max_element(impl::getMenuItems(), [](const auto &a, const auto &b) { const auto maxIndex = std::ranges::max_element(impl::getMenuItems(), [](const auto &a, const auto &b) {
return a.second.toolbarIndex < b.second.toolbarIndex; return a.second.toolbarIndex < b.second.toolbarIndex;
})->second.toolbarIndex; })->second.toolbarIndex;
@ -809,38 +812,38 @@ namespace hex {
namespace impl { namespace impl {
std::multimap<u32, MainMenuItem> &getMainMenuItems() { std::multimap<u32, MainMenuItem> &getMainMenuItems() {
static std::multimap<u32, MainMenuItem> items; static AutoReset<std::multimap<u32, MainMenuItem>> items;
return items; return items;
} }
std::multimap<u32, MenuItem> &getMenuItems() { std::multimap<u32, MenuItem> &getMenuItems() {
static std::multimap<u32, MenuItem> items; static AutoReset<std::multimap<u32, MenuItem>> items;
return items; return items;
} }
std::vector<DrawCallback> &getWelcomeScreenEntries() { std::vector<DrawCallback> &getWelcomeScreenEntries() {
static std::vector<DrawCallback> entries; static AutoReset<std::vector<DrawCallback>> entries;
return entries; return entries;
} }
std::vector<DrawCallback> &getFooterItems() { std::vector<DrawCallback> &getFooterItems() {
static std::vector<DrawCallback> items; static AutoReset<std::vector<DrawCallback>> items;
return items; return items;
} }
std::vector<DrawCallback> &getToolbarItems() { std::vector<DrawCallback> &getToolbarItems() {
static std::vector<DrawCallback> items; static AutoReset<std::vector<DrawCallback>> items;
return items; return items;
} }
std::vector<SidebarItem> &getSidebarItems() { std::vector<SidebarItem> &getSidebarItems() {
static std::vector<SidebarItem> items; static AutoReset<std::vector<SidebarItem>> items;
return items; return items;
} }
std::vector<TitleBarButton> &getTitleBarButtons() { std::vector<TitleBarButton> &getTitleBarButtons() {
static std::vector<TitleBarButton> buttons; static AutoReset<std::vector<TitleBarButton>> buttons;
return buttons; return buttons;
} }
@ -867,7 +870,7 @@ namespace hex {
} }
std::vector<std::string> &getEntries() { std::vector<std::string> &getEntries() {
static std::vector<std::string> providerNames; static AutoReset<std::vector<std::string>> providerNames;
return providerNames; return providerNames;
} }
@ -894,7 +897,7 @@ namespace hex {
namespace impl { namespace impl {
std::vector<Entry> &getEntries() { std::vector<Entry> &getEntries() {
static std::vector<Entry> entries; static AutoReset<std::vector<Entry>> entries;
return entries; return entries;
} }
@ -915,7 +918,7 @@ namespace hex {
namespace impl { namespace impl {
std::vector<Entry> &getEntries() { std::vector<Entry> &getEntries() {
static std::vector<Entry> entries; static AutoReset<std::vector<Entry>> entries;
return entries; return entries;
} }
@ -996,13 +999,13 @@ namespace hex {
} }
std::vector<std::shared_ptr<DataVisualizer>> &getVisualizers() { std::vector<std::shared_ptr<DataVisualizer>> &getVisualizers() {
static std::vector<std::shared_ptr<DataVisualizer>> visualizers; static AutoReset<std::vector<std::shared_ptr<DataVisualizer>>> visualizers;
return visualizers; return visualizers;
} }
std::vector<std::shared_ptr<MiniMapVisualizer>> &getMiniMapVisualizers() { std::vector<std::shared_ptr<MiniMapVisualizer>> &getMiniMapVisualizers() {
static std::vector<std::shared_ptr<MiniMapVisualizer>> visualizers; static AutoReset<std::vector<std::shared_ptr<MiniMapVisualizer>>> visualizers;
return visualizers; return visualizers;
} }
@ -1029,7 +1032,7 @@ namespace hex {
namespace impl { namespace impl {
std::vector<std::unique_ptr<Algorithm>>& getAlgorithms() { std::vector<std::unique_ptr<Algorithm>>& getAlgorithms() {
static std::vector<std::unique_ptr<Algorithm>> algorithms; static AutoReset<std::vector<std::unique_ptr<Algorithm>>> algorithms;
return algorithms; return algorithms;
} }
@ -1047,7 +1050,7 @@ namespace hex {
namespace impl { namespace impl {
std::vector<std::unique_ptr<Hash>> &getHashes() { std::vector<std::unique_ptr<Hash>> &getHashes() {
static std::vector<std::unique_ptr<Hash>> hashes; static AutoReset<std::vector<std::unique_ptr<Hash>>> hashes;
return hashes; return hashes;
} }
@ -1064,29 +1067,41 @@ namespace hex {
namespace impl { namespace impl {
struct Service { class Service {
std::string name; public:
std::jthread thread; Service(std::string name, std::jthread thread) : m_name(std::move(name)), m_thread(std::move(thread)) { }
Service(const Service&) = delete;
Service(Service &&) = default;
~Service() {
m_thread.request_stop();
if (m_thread.joinable())
m_thread.join();
}
Service& operator=(const Service&) = delete;
Service& operator=(Service &&) = default;
[[nodiscard]] const std::string &getName() const {
return m_name;
}
[[nodiscard]] const std::jthread &getThread() const {
return m_thread;
}
private:
std::string m_name;
std::jthread m_thread;
}; };
std::vector<Service> &getServices() { std::vector<Service> &getServices() {
static std::vector<Service> services; static AutoReset<std::vector<Service>> services;
return services; return services;
} }
void stopServices() { void stopServices() {
auto &services = getServices(); auto &services = getServices();
for (auto &service : services) {
service.thread.request_stop();
}
for (auto &service : services) {
if (service.thread.joinable())
service.thread.join();
}
services.clear(); services.clear();
} }
@ -1095,7 +1110,7 @@ namespace hex {
void registerService(const UnlocalizedString &unlocalizedName, const impl::Callback &callback) { void registerService(const UnlocalizedString &unlocalizedName, const impl::Callback &callback) {
log::debug("Registered new background service: {}", unlocalizedName.get()); log::debug("Registered new background service: {}", unlocalizedName.get());
impl::getServices().push_back(impl::Service { impl::getServices().emplace_back(
unlocalizedName, unlocalizedName,
std::jthread([callback = auto(callback)](const std::stop_token &stopToken){ std::jthread([callback = auto(callback)](const std::stop_token &stopToken){
while (!stopToken.stop_requested()) { while (!stopToken.stop_requested()) {
@ -1103,7 +1118,7 @@ namespace hex {
std::this_thread::sleep_for(std::chrono::milliseconds(50)); std::this_thread::sleep_for(std::chrono::milliseconds(50));
} }
}) })
}); );
} }
} }
@ -1113,7 +1128,7 @@ namespace hex {
namespace impl { namespace impl {
std::map<std::string, NetworkCallback> &getNetworkEndpoints() { std::map<std::string, NetworkCallback> &getNetworkEndpoints() {
static std::map<std::string, NetworkCallback> endpoints; static AutoReset<std::map<std::string, NetworkCallback>> endpoints;
return endpoints; return endpoints;
} }
@ -1133,7 +1148,7 @@ namespace hex {
namespace impl { namespace impl {
std::map<std::string, Experiment> &getExperiments() { std::map<std::string, Experiment> &getExperiments() {
static std::map<std::string, Experiment> experiments; static AutoReset<std::map<std::string, Experiment>> experiments;
return experiments; return experiments;
} }
@ -1184,7 +1199,7 @@ namespace hex {
namespace impl { namespace impl {
std::vector<ReportGenerator> &getGenerators() { std::vector<ReportGenerator> &getGenerators() {
static std::vector<ReportGenerator> generators; static AutoReset<std::vector<ReportGenerator>> generators;
return generators; return generators;
} }

View File

@ -5,11 +5,11 @@
#include <hex/providers/provider.hpp> #include <hex/providers/provider.hpp>
#include <hex/helpers/fmt.hpp> #include <hex/helpers/fmt.hpp>
#include <hex/helpers/utils.hpp> #include <hex/helpers/utils.hpp>
#include <hex/helpers/auto_reset.hpp>
#include <wolv/utils/string.hpp> #include <wolv/utils/string.hpp>
#include <utility> #include <utility>
#include <unistd.h>
#include <imgui.h> #include <imgui.h>
#include <imgui_internal.h> #include <imgui_internal.h>
@ -19,6 +19,7 @@
#include <windows.h> #include <windows.h>
#else #else
#include <sys/utsname.h> #include <sys/utsname.h>
#include <unistd.h>
#endif #endif
namespace hex { namespace hex {
@ -36,39 +37,39 @@ namespace hex {
namespace impl { namespace impl {
static std::map<u32, Highlighting> s_backgroundHighlights;
std::map<u32, Highlighting> &getBackgroundHighlights() { std::map<u32, Highlighting> &getBackgroundHighlights() {
return s_backgroundHighlights; static AutoReset<std::map<u32, Highlighting>> backgroundHighlights;
return backgroundHighlights;
} }
static std::map<u32, HighlightingFunction> s_backgroundHighlightingFunctions;
std::map<u32, HighlightingFunction> &getBackgroundHighlightingFunctions() { std::map<u32, HighlightingFunction> &getBackgroundHighlightingFunctions() {
return s_backgroundHighlightingFunctions; static AutoReset<std::map<u32, HighlightingFunction>> backgroundHighlightingFunctions;
return backgroundHighlightingFunctions;
} }
static std::map<u32, Highlighting> s_foregroundHighlights;
std::map<u32, Highlighting> &getForegroundHighlights() { std::map<u32, Highlighting> &getForegroundHighlights() {
return s_foregroundHighlights; static AutoReset<std::map<u32, Highlighting>> foregroundHighlights;
return foregroundHighlights;
} }
static std::map<u32, HighlightingFunction> s_foregroundHighlightingFunctions;
std::map<u32, HighlightingFunction> &getForegroundHighlightingFunctions() { std::map<u32, HighlightingFunction> &getForegroundHighlightingFunctions() {
return s_foregroundHighlightingFunctions; static AutoReset<std::map<u32, HighlightingFunction>> foregroundHighlightingFunctions;
return foregroundHighlightingFunctions;
} }
static std::map<u32, Tooltip> s_tooltips;
std::map<u32, Tooltip> &getTooltips() { std::map<u32, Tooltip> &getTooltips() {
return s_tooltips; static AutoReset<std::map<u32, Tooltip>> tooltips;
return tooltips;
} }
static std::map<u32, TooltipFunction> s_tooltipFunctions;
std::map<u32, TooltipFunction> &getTooltipFunctions() { std::map<u32, TooltipFunction> &getTooltipFunctions() {
return s_tooltipFunctions; static AutoReset<std::map<u32, TooltipFunction>> tooltipFunctions;
return tooltipFunctions;
} }
static std::optional<ProviderRegion> s_currentSelection; static AutoReset<std::optional<ProviderRegion>> s_currentSelection;
void setCurrentSelection(const std::optional<ProviderRegion> &region) { void setCurrentSelection(const std::optional<ProviderRegion> &region) {
s_currentSelection = region; *s_currentSelection = region;
} }
} }
@ -226,7 +227,7 @@ namespace hex {
namespace ImHexApi::Provider { namespace ImHexApi::Provider {
static i64 s_currentProvider = -1; static i64 s_currentProvider = -1;
static std::vector<prv::Provider *> s_providers; static AutoReset<std::vector<prv::Provider *>> s_providers;
namespace impl { namespace impl {
@ -245,7 +246,7 @@ namespace hex {
if (!ImHexApi::Provider::isValid()) if (!ImHexApi::Provider::isValid())
return nullptr; return nullptr;
return s_providers[s_currentProvider]; return (*s_providers)[s_currentProvider];
} }
const std::vector<prv::Provider *> &getProviders() { const std::vector<prv::Provider *> &getProviders() {
@ -256,7 +257,7 @@ namespace hex {
if (TaskManager::getRunningTaskCount() > 0) if (TaskManager::getRunningTaskCount() > 0)
return; return;
if (index < s_providers.size() && s_currentProvider != index) { if (index < s_providers->size() && s_currentProvider != index) {
auto oldProvider = get(); auto oldProvider = get();
s_currentProvider = index; s_currentProvider = index;
EventProviderChanged::post(oldProvider, get()); EventProviderChanged::post(oldProvider, get());
@ -268,7 +269,7 @@ namespace hex {
} }
bool isValid() { bool isValid() {
return !s_providers.empty() && s_currentProvider >= 0 && s_currentProvider < i64(s_providers.size()); return !s_providers->empty() && s_currentProvider >= 0 && s_currentProvider < i64(s_providers->size());
} }
void markDirty() { void markDirty() {
@ -276,12 +277,12 @@ namespace hex {
} }
void resetDirty() { void resetDirty() {
for (auto &provider : s_providers) for (const auto &provider : *s_providers)
provider->markDirty(false); provider->markDirty(false);
} }
bool isDirty() { bool isDirty() {
return std::ranges::any_of(s_providers, [](const auto &provider) { return std::ranges::any_of(*s_providers, [](const auto &provider) {
return provider->isDirty(); return provider->isDirty();
}); });
} }
@ -293,11 +294,11 @@ namespace hex {
if (skipLoadInterface) if (skipLoadInterface)
provider->skipLoadInterface(); provider->skipLoadInterface();
s_providers.push_back(provider); s_providers->push_back(provider);
EventProviderCreated::post(provider); EventProviderCreated::post(provider);
if (select || s_providers.size() == 1) if (select || s_providers->size() == 1)
setCurrentProvider(s_providers.size() - 1); setCurrentProvider(s_providers->size() - 1);
} }
void remove(prv::Provider *provider, bool noQuestions) { void remove(prv::Provider *provider, bool noQuestions) {
@ -316,29 +317,29 @@ namespace hex {
return; return;
} }
auto it = std::find(s_providers.begin(), s_providers.end(), provider); const auto it = std::ranges::find(*s_providers, provider);
if (it == s_providers.end()) if (it == s_providers->end())
return; return;
if (!s_providers.empty()) { if (!s_providers->empty()) {
if (it == s_providers.begin()) { if (it == s_providers->begin()) {
// If the first provider is being closed, select the one that's the first one now // If the first provider is being closed, select the one that's the first one now
setCurrentProvider(0); setCurrentProvider(0);
if (s_providers.size() > 1) if (s_providers->size() > 1)
EventProviderChanged::post(s_providers[0], s_providers[1]); EventProviderChanged::post(s_providers->at(0), s_providers->at(1));
} }
else if (std::distance(s_providers.begin(), it) == s_currentProvider) { else if (std::distance(s_providers->begin(), it) == s_currentProvider) {
// If the current provider is being closed, select the one that's before it // If the current provider is being closed, select the one that's before it
setCurrentProvider(s_currentProvider - 1); setCurrentProvider(s_currentProvider - 1);
} }
else { else {
// If any other provider is being closed, find the current provider in the list again and select it again // If any other provider is being closed, find the current provider in the list again and select it again
auto currentProvider = get(); const auto currentProvider = get();
auto currentIt = std::find(s_providers.begin(), s_providers.end(), currentProvider); const auto currentIt = std::ranges::find(*s_providers, currentProvider);
if (currentIt != s_providers.end()) { if (currentIt != s_providers->end()) {
auto newIndex = std::distance(s_providers.begin(), currentIt); auto newIndex = std::distance(s_providers->begin(), currentIt);
if (s_currentProvider == newIndex) if (s_currentProvider == newIndex)
newIndex -= 1; newIndex -= 1;
@ -351,11 +352,11 @@ namespace hex {
} }
} }
s_providers.erase(it); s_providers->erase(it);
if (s_currentProvider >= i64(s_providers.size())) if (s_currentProvider >= i64(s_providers->size()))
setCurrentProvider(0); setCurrentProvider(0);
if (s_providers.empty()) if (s_providers->empty())
EventProviderChanged::post(provider, nullptr); EventProviderChanged::post(provider, nullptr);
provider->close(); provider->close();
@ -391,11 +392,11 @@ namespace hex {
static ImVec2 s_mainWindowPos; static ImVec2 s_mainWindowPos;
static ImVec2 s_mainWindowSize; static ImVec2 s_mainWindowSize;
void setMainWindowPosition(i32 x, i32 y) { void setMainWindowPosition(i32 x, i32 y) {
s_mainWindowPos = ImVec2(x, y); s_mainWindowPos = ImVec2(float(x), float(y));
} }
void setMainWindowSize(u32 width, u32 height) { void setMainWindowSize(u32 width, u32 height) {
s_mainWindowSize = ImVec2(width, height); s_mainWindowSize = ImVec2(float(width), float(height));
} }
static ImGuiID s_mainDockSpaceId; static ImGuiID s_mainDockSpaceId;
@ -436,7 +437,7 @@ namespace hex {
} }
static std::string s_gpuVendor; static AutoReset<std::string> s_gpuVendor;
void setGPUVendor(const std::string &vendor) { void setGPUVendor(const std::string &vendor) {
s_gpuVendor = vendor; s_gpuVendor = vendor;
} }
@ -536,7 +537,7 @@ namespace hex {
} }
std::map<std::string, std::string> &getInitArguments() { std::map<std::string, std::string> &getInitArguments() {
static std::map<std::string, std::string> initArgs; static AutoReset<std::map<std::string, std::string>> initArgs;
return initArgs; return initArgs;
} }
@ -556,7 +557,7 @@ namespace hex {
std::vector<std::fs::path> &getAdditionalFolderPaths() { std::vector<std::fs::path> &getAdditionalFolderPaths() {
static std::vector<std::fs::path> additionalFolderPaths; static AutoReset<std::vector<std::fs::path>> additionalFolderPaths;
return additionalFolderPaths; return additionalFolderPaths;
} }
@ -595,7 +596,7 @@ namespace hex {
return hex::format("{}.{}.{}", info.dwMajorVersion, info.dwMinorVersion, info.dwBuildNumber); return hex::format("{}.{}.{}", info.dwMajorVersion, info.dwMinorVersion, info.dwBuildNumber);
#elif defined(OS_LINUX) || defined(OS_MACOS) || defined(OS_WEB) #elif defined(OS_LINUX) || defined(OS_MACOS) || defined(OS_WEB)
struct utsname details; struct utsname details = { };
if (uname(&details) != 0) { if (uname(&details) != 0) {
return "Unknown"; return "Unknown";
@ -627,13 +628,13 @@ namespace hex {
return "Unknown"; return "Unknown";
} }
#elif defined(OS_LINUX) || defined(OS_MACOS) || defined(OS_WEB) #elif defined(OS_LINUX) || defined(OS_MACOS) || defined(OS_WEB)
struct utsname details; struct utsname details = { };
if (uname(&details) != 0) { if (uname(&details) != 0) {
return "Unknown"; return "Unknown";
} }
return std::string(details.machine); return { details.machine };
#else #else
return "Unknown"; return "Unknown";
#endif #endif
@ -732,7 +733,7 @@ namespace hex {
} }
void setWindowResizable(bool resizable) { void setWindowResizable(bool resizable) {
glfwSetWindowAttrib(impl::s_mainWindowHandle, GLFW_RESIZABLE, resizable); glfwSetWindowAttrib(impl::s_mainWindowHandle, GLFW_RESIZABLE, int(resizable));
impl::s_windowResizable = resizable; impl::s_windowResizable = resizable;
} }
@ -745,14 +746,14 @@ namespace hex {
namespace impl { namespace impl {
std::map<std::string, MessagingHandler> &getHandlers() { std::map<std::string, MessagingHandler> &getHandlers() {
static std::map<std::string, MessagingHandler> handlers; static AutoReset<std::map<std::string, MessagingHandler>> handlers;
return handlers; return handlers;
} }
void runHandler(const std::string &eventName, const std::vector<u8> &args) { void runHandler(const std::string &eventName, const std::vector<u8> &args) {
const auto& handlers = impl::getHandlers(); const auto& handlers = impl::getHandlers();
auto matchHandler = handlers.find(eventName); const auto matchHandler = handlers.find(eventName);
if (matchHandler == handlers.end()) { if (matchHandler == handlers.end()) {
log::error("Forward event handler {} not found", eventName); log::error("Forward event handler {} not found", eventName);
@ -777,12 +778,12 @@ namespace hex {
namespace impl { namespace impl {
std::vector<Font>& getFonts() { std::vector<Font>& getFonts() {
static std::vector<Font> fonts; static AutoReset<std::vector<Font>> fonts;
return fonts; return fonts;
} }
static std::fs::path s_customFontPath; static AutoReset<std::fs::path> s_customFontPath;
void setCustomFontPath(const std::fs::path &path) { void setCustomFontPath(const std::fs::path &path) {
s_customFontPath = path; s_customFontPath = path;
} }
@ -792,7 +793,7 @@ namespace hex {
s_fontSize = size; s_fontSize = size;
} }
static std::unique_ptr<ImFontAtlas> s_fontAtlas; static AutoReset<std::unique_ptr<ImFontAtlas>> s_fontAtlas;
void setFontAtlas(ImFontAtlas* fontAtlas) { void setFontAtlas(ImFontAtlas* fontAtlas) {
s_fontAtlas = std::unique_ptr<ImFontAtlas>(fontAtlas); s_fontAtlas = std::unique_ptr<ImFontAtlas>(fontAtlas);
} }
@ -875,7 +876,7 @@ namespace hex {
} }
ImFontAtlas* getFontAtlas() { ImFontAtlas* getFontAtlas() {
return impl::s_fontAtlas.get(); return impl::s_fontAtlas->get();
} }
ImFont* Bold() { ImFont* Bold() {

View File

@ -6,16 +6,16 @@
#include <imgui.h> #include <imgui.h>
#include <hex/api/content_registry.hpp> #include <hex/api/content_registry.hpp>
#include <hex/api/imhex_api.hpp>
#include <hex/ui/view.hpp> #include <hex/ui/view.hpp>
#include <hex/helpers/auto_reset.hpp>
namespace hex { namespace hex {
namespace { namespace {
std::optional<std::fs::path> s_layoutPathToLoad; AutoReset<std::optional<std::fs::path>> s_layoutPathToLoad;
std::optional<std::string> s_layoutStringToLoad; AutoReset<std::optional<std::string>> s_layoutStringToLoad;
std::vector<LayoutManager::Layout> s_layouts; AutoReset<std::vector<LayoutManager::Layout>> s_layouts;
bool s_layoutLocked = false; bool s_layoutLocked = false;
@ -33,7 +33,7 @@ namespace hex {
void LayoutManager::save(const std::string &name) { void LayoutManager::save(const std::string &name) {
auto fileName = name; auto fileName = name;
fileName = wolv::util::replaceStrings(fileName, " ", "_"); fileName = wolv::util::replaceStrings(fileName, " ", "_");
std::transform(fileName.begin(), fileName.end(), fileName.begin(), tolower); std::ranges::transform(fileName, fileName.begin(), tolower);
fileName += ".hexlyt"; fileName += ".hexlyt";
std::fs::path layoutPath; std::fs::path layoutPath;
@ -71,8 +71,8 @@ namespace hex {
} }
void LayoutManager::process() { void LayoutManager::process() {
if (s_layoutPathToLoad.has_value()) { if (s_layoutPathToLoad->has_value()) {
const auto pathString = wolv::util::toUTF8String(*s_layoutPathToLoad); const auto pathString = wolv::util::toUTF8String(**s_layoutPathToLoad);
LayoutManager::closeAllViews(); LayoutManager::closeAllViews();
ImGui::LoadIniSettingsFromDisk(pathString.c_str()); ImGui::LoadIniSettingsFromDisk(pathString.c_str());
@ -81,9 +81,9 @@ namespace hex {
log::info("Loaded layout from {}", pathString); log::info("Loaded layout from {}", pathString);
} }
if (s_layoutStringToLoad.has_value()) { if (s_layoutStringToLoad->has_value()) {
LayoutManager::closeAllViews(); LayoutManager::closeAllViews();
ImGui::LoadIniSettingsFromMemory(s_layoutStringToLoad->c_str()); ImGui::LoadIniSettingsFromMemory((*s_layoutStringToLoad)->c_str());
s_layoutStringToLoad = std::nullopt; s_layoutStringToLoad = std::nullopt;
log::info("Loaded layout from string"); log::info("Loaded layout from string");
@ -91,7 +91,7 @@ namespace hex {
} }
void LayoutManager::reload() { void LayoutManager::reload() {
s_layouts.clear(); s_layouts->clear();
for (const auto &directory : hex::fs::getDefaultPaths(fs::ImHexPath::Layouts)) { for (const auto &directory : hex::fs::getDefaultPaths(fs::ImHexPath::Layouts)) {
for (const auto &entry : std::fs::directory_iterator(directory)) { for (const auto &entry : std::fs::directory_iterator(directory)) {
@ -107,7 +107,7 @@ namespace hex {
name[i] = char(std::toupper(name[i])); name[i] = char(std::toupper(name[i]));
} }
s_layouts.push_back({ s_layouts->push_back({
name, name,
path path
}); });
@ -118,7 +118,7 @@ namespace hex {
void LayoutManager::reset() { void LayoutManager::reset() {
s_layoutPathToLoad.reset(); s_layoutPathToLoad.reset();
s_layoutStringToLoad.reset(); s_layoutStringToLoad.reset();
s_layouts.clear(); s_layouts->clear();
} }
bool LayoutManager::isLayoutLocked() { bool LayoutManager::isLayoutLocked() {

View File

@ -7,17 +7,17 @@ namespace hex {
namespace { namespace {
std::string s_fallbackLanguage; AutoReset<std::string> s_fallbackLanguage;
std::string s_selectedLanguage; AutoReset<std::string> s_selectedLanguage;
std::map<std::string, std::string> s_currStrings; AutoReset<std::map<std::string, std::string>> s_currStrings;
} }
namespace impl { namespace impl {
void resetLanguageStrings() { void resetLanguageStrings() {
s_currStrings.clear(); s_currStrings->clear();
s_selectedLanguage.clear(); s_selectedLanguage->clear();
} }
void setFallbackLanguage(const std::string &language) { void setFallbackLanguage(const std::string &language) {
@ -41,7 +41,7 @@ namespace hex {
} }
void loadLanguage(const std::string &language) { void loadLanguage(const std::string &language) {
s_currStrings.clear(); s_currStrings->clear();
auto &definitions = ContentRegistry::Language::impl::getLanguageDefinitions(); auto &definitions = ContentRegistry::Language::impl::getLanguageDefinitions();
@ -49,12 +49,12 @@ namespace hex {
return; return;
for (auto &definition : definitions[language]) for (auto &definition : definitions[language])
s_currStrings.insert(definition.getEntries().begin(), definition.getEntries().end()); s_currStrings->insert(definition.getEntries().begin(), definition.getEntries().end());
const auto& fallbackLanguage = getFallbackLanguage(); const auto& fallbackLanguage = getFallbackLanguage();
if (language != fallbackLanguage) { if (language != fallbackLanguage) {
for (auto &definition : definitions[fallbackLanguage]) for (auto &definition : definitions[fallbackLanguage])
s_currStrings.insert(definition.getEntries().begin(), definition.getEntries().end()); s_currStrings->insert(definition.getEntries().begin(), definition.getEntries().end());
} }
s_selectedLanguage = language; s_selectedLanguage = language;
@ -122,8 +122,8 @@ namespace hex {
const std::string &Lang::get() const { const std::string &Lang::get() const {
auto &lang = LocalizationManager::s_currStrings; auto &lang = LocalizationManager::s_currStrings;
if (lang.contains(m_unlocalizedString)) if (lang->contains(m_unlocalizedString))
return lang[m_unlocalizedString]; return lang->at(m_unlocalizedString);
else else
return m_unlocalizedString; return m_unlocalizedString;
} }

View File

@ -3,7 +3,6 @@
#include <hex/helpers/logger.hpp> #include <hex/helpers/logger.hpp>
#include <hex/helpers/fmt.hpp> #include <hex/helpers/fmt.hpp>
#include <hex/helpers/utils.hpp>
#include <wolv/utils/string.hpp> #include <wolv/utils/string.hpp>
@ -303,13 +302,13 @@ namespace hex {
} }
std::vector<std::fs::path> &PluginManager::getPluginPaths() { std::vector<std::fs::path> &PluginManager::getPluginPaths() {
static std::vector<std::fs::path> pluginPaths; static AutoReset<std::vector<std::fs::path>> pluginPaths;
return pluginPaths; return pluginPaths;
} }
std::vector<std::fs::path> &PluginManager::getPluginLoadPaths() { std::vector<std::fs::path> &PluginManager::getPluginLoadPaths() {
static std::vector<std::fs::path> pluginPaths; static AutoReset<std::vector<std::fs::path>> pluginPaths;
return pluginPaths; return pluginPaths;
} }

View File

@ -1,20 +1,18 @@
#include <hex/api/project_file_manager.hpp> #include <hex/api/project_file_manager.hpp>
#include <hex/providers/provider.hpp>
#include <wolv/io/fs.hpp> #include <wolv/io/fs.hpp>
namespace hex { namespace hex {
namespace { namespace {
std::vector<ProjectFile::Handler> s_handlers; AutoReset<std::vector<ProjectFile::Handler>> s_handlers;
std::vector<ProjectFile::ProviderHandler> s_providerHandlers; AutoReset<std::vector<ProjectFile::ProviderHandler>> s_providerHandlers;
std::fs::path s_currProjectPath; AutoReset<std::fs::path> s_currProjectPath;
std::function<bool(const std::fs::path&)> s_loadProjectFunction; AutoReset<std::function<bool(const std::fs::path&)>> s_loadProjectFunction;
std::function<bool(std::optional<std::fs::path>, bool)> s_storeProjectFunction; AutoReset<std::function<bool(std::optional<std::fs::path>, bool)>> s_storeProjectFunction;
} }
@ -28,19 +26,19 @@ namespace hex {
} }
bool ProjectFile::load(const std::fs::path &filePath) { bool ProjectFile::load(const std::fs::path &filePath) {
return s_loadProjectFunction(filePath); return (*s_loadProjectFunction)(filePath);
} }
bool ProjectFile::store(std::optional<std::fs::path> filePath, bool updateLocation) { bool ProjectFile::store(std::optional<std::fs::path> filePath, bool updateLocation) {
return s_storeProjectFunction(std::move(filePath), updateLocation); return (*s_storeProjectFunction)(std::move(filePath), updateLocation);
} }
bool ProjectFile::hasPath() { bool ProjectFile::hasPath() {
return !s_currProjectPath.empty(); return !s_currProjectPath->empty();
} }
void ProjectFile::clearPath() { void ProjectFile::clearPath() {
s_currProjectPath.clear(); s_currProjectPath->clear();
} }
std::fs::path ProjectFile::getPath() { std::fs::path ProjectFile::getPath() {

View File

@ -8,7 +8,7 @@ namespace hex {
namespace { namespace {
std::map<Shortcut, ShortcutManager::ShortcutEntry> s_globalShortcuts; AutoReset<std::map<Shortcut, ShortcutManager::ShortcutEntry>> s_globalShortcuts;
std::atomic<bool> s_paused; std::atomic<bool> s_paused;
std::optional<Shortcut> s_prevShortcut; std::optional<Shortcut> s_prevShortcut;
@ -16,7 +16,7 @@ namespace hex {
void ShortcutManager::addGlobalShortcut(const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const std::function<void()> &callback) { void ShortcutManager::addGlobalShortcut(const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const std::function<void()> &callback) {
s_globalShortcuts.insert({ shortcut, { shortcut, unlocalizedName, callback } }); s_globalShortcuts->insert({ shortcut, { shortcut, unlocalizedName, callback } });
} }
void ShortcutManager::addShortcut(View *view, const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const std::function<void()> &callback) { void ShortcutManager::addShortcut(View *view, const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const std::function<void()> &callback) {
@ -57,7 +57,7 @@ namespace hex {
} }
void ShortcutManager::process(const std::unique_ptr<View> &currentView, bool ctrl, bool alt, bool shift, bool super, bool focused, u32 keyCode) { void ShortcutManager::process(const std::unique_ptr<View> &currentView, bool ctrl, bool alt, bool shift, bool super, bool focused, u32 keyCode) {
Shortcut pressedShortcut = getShortcut(ctrl, alt, shift, super, focused, keyCode); const Shortcut pressedShortcut = getShortcut(ctrl, alt, shift, super, focused, keyCode);
if (keyCode != 0) if (keyCode != 0)
s_prevShortcut = Shortcut(pressedShortcut.getKeys()); s_prevShortcut = Shortcut(pressedShortcut.getKeys());
@ -65,7 +65,7 @@ namespace hex {
} }
void ShortcutManager::processGlobals(bool ctrl, bool alt, bool shift, bool super, u32 keyCode) { void ShortcutManager::processGlobals(bool ctrl, bool alt, bool shift, bool super, u32 keyCode) {
Shortcut pressedShortcut = getShortcut(ctrl, alt, shift, super, false, keyCode); const Shortcut pressedShortcut = getShortcut(ctrl, alt, shift, super, false, keyCode);
if (keyCode != 0) if (keyCode != 0)
s_prevShortcut = Shortcut(pressedShortcut.getKeys()); s_prevShortcut = Shortcut(pressedShortcut.getKeys());
@ -73,7 +73,7 @@ namespace hex {
} }
void ShortcutManager::clearShortcuts() { void ShortcutManager::clearShortcuts() {
s_globalShortcuts.clear(); s_globalShortcuts->clear();
} }
void ShortcutManager::resumeShortcuts() { void ShortcutManager::resumeShortcuts() {
@ -90,17 +90,18 @@ namespace hex {
} }
std::vector<ShortcutManager::ShortcutEntry> ShortcutManager::getGlobalShortcuts() { std::vector<ShortcutManager::ShortcutEntry> ShortcutManager::getGlobalShortcuts() {
std::vector<ShortcutManager::ShortcutEntry> result; std::vector<ShortcutEntry> result;
for (auto &[shortcut, entry] : s_globalShortcuts) for (auto &[shortcut, entry] : *s_globalShortcuts)
result.push_back(entry); result.push_back(entry);
return result; return result;
} }
std::vector<ShortcutManager::ShortcutEntry> ShortcutManager::getViewShortcuts(const View *view) { std::vector<ShortcutManager::ShortcutEntry> ShortcutManager::getViewShortcuts(const View *view) {
std::vector<ShortcutManager::ShortcutEntry> result; std::vector<ShortcutEntry> result;
result.reserve(view->m_shortcuts.size());
for (auto &[shortcut, entry] : view->m_shortcuts) for (auto &[shortcut, entry] : view->m_shortcuts)
result.push_back(entry); result.push_back(entry);

View File

@ -7,7 +7,6 @@
#include <ranges> #include <ranges>
#include <jthread.hpp> #include <jthread.hpp>
#include <hex/helpers/utils.hpp>
#if defined(OS_WINDOWS) #if defined(OS_WINDOWS)
#include <windows.h> #include <windows.h>
@ -149,7 +148,7 @@ namespace hex {
bool TaskHolder::isRunning() const { bool TaskHolder::isRunning() const {
auto task = m_task.lock(); const auto &task = m_task.lock();
if (!task) if (!task)
return false; return false;
@ -157,7 +156,7 @@ namespace hex {
} }
bool TaskHolder::hadException() const { bool TaskHolder::hadException() const {
auto task = m_task.lock(); const auto &task = m_task.lock();
if (!task) if (!task)
return false; return false;
@ -165,7 +164,7 @@ namespace hex {
} }
bool TaskHolder::shouldInterrupt() const { bool TaskHolder::shouldInterrupt() const {
auto task = m_task.lock(); const auto &task = m_task.lock();
if (!task) if (!task)
return false; return false;
@ -173,7 +172,7 @@ namespace hex {
} }
bool TaskHolder::wasInterrupted() const { bool TaskHolder::wasInterrupted() const {
auto task = m_task.lock(); const auto &task = m_task.lock();
if (!task) if (!task)
return false; return false;
@ -181,7 +180,7 @@ namespace hex {
} }
void TaskHolder::interrupt() const { void TaskHolder::interrupt() const {
auto task = m_task.lock(); const auto &task = m_task.lock();
if (!task) if (!task)
return; return;
@ -189,9 +188,9 @@ namespace hex {
} }
u32 TaskHolder::getProgress() const { u32 TaskHolder::getProgress() const {
auto task = m_task.lock(); const auto &task = m_task.lock();
if (!task) if (!task)
return false; return 0;
// If the max value is 0, the task has no progress // If the max value is 0, the task has no progress
if (task->getMaxValue() == 0) if (task->getMaxValue() == 0)
@ -265,7 +264,7 @@ namespace hex {
void TaskManager::exit() { void TaskManager::exit() {
// Interrupt all tasks // Interrupt all tasks
for (auto &task : s_tasks) { for (const auto &task : s_tasks) {
task->interrupt(); task->interrupt();
} }
@ -342,7 +341,7 @@ namespace hex {
size_t TaskManager::getRunningTaskCount() { size_t TaskManager::getRunningTaskCount() {
std::scoped_lock lock(s_queueMutex); std::scoped_lock lock(s_queueMutex);
return std::count_if(s_tasks.begin(), s_tasks.end(), [](const auto &task){ return std::ranges::count_if(s_tasks, [](const auto &task){
return !task->isBackgroundTask(); return !task->isBackgroundTask();
}); });
} }
@ -350,7 +349,7 @@ namespace hex {
size_t TaskManager::getRunningBackgroundTaskCount() { size_t TaskManager::getRunningBackgroundTaskCount() {
std::scoped_lock lock(s_queueMutex); std::scoped_lock lock(s_queueMutex);
return std::count_if(s_tasks.begin(), s_tasks.end(), [](const auto &task){ return std::ranges::count_if(s_tasks, [](const auto &task){
return task->isBackgroundTask(); return task->isBackgroundTask();
}); });
} }

View File

@ -10,21 +10,21 @@ namespace hex {
namespace { namespace {
std::map<std::string, nlohmann::json> s_themes; AutoReset<std::map<std::string, nlohmann::json>> s_themes;
std::map<std::string, ThemeManager::ThemeHandler> s_themeHandlers; AutoReset<std::map<std::string, ThemeManager::ThemeHandler>> s_themeHandlers;
std::map<std::string, ThemeManager::StyleHandler> s_styleHandlers; AutoReset<std::map<std::string, ThemeManager::StyleHandler>> s_styleHandlers;
std::string s_imageTheme; AutoReset<std::string> s_imageTheme;
std::string s_currTheme; AutoReset<std::string> s_currTheme;
} }
void ThemeManager::addThemeHandler(const std::string &name, const ColorMap &colorMap, const std::function<ImColor(u32)> &getFunction, const std::function<void(u32, ImColor)> &setFunction) { void ThemeManager::addThemeHandler(const std::string &name, const ColorMap &colorMap, const std::function<ImColor(u32)> &getFunction, const std::function<void(u32, ImColor)> &setFunction) {
s_themeHandlers[name] = { colorMap, getFunction, setFunction }; (*s_themeHandlers)[name] = { colorMap, getFunction, setFunction };
} }
void ThemeManager::addStyleHandler(const std::string &name, const StyleMap &styleMap) { void ThemeManager::addStyleHandler(const std::string &name, const StyleMap &styleMap) {
s_styleHandlers[name] = { styleMap }; (*s_styleHandlers)[name] = { styleMap };
} }
void ThemeManager::addTheme(const std::string &content) { void ThemeManager::addTheme(const std::string &content) {
@ -32,7 +32,7 @@ namespace hex {
auto theme = nlohmann::json::parse(content); auto theme = nlohmann::json::parse(content);
if (theme.contains("name") && theme.contains("colors")) { if (theme.contains("name") && theme.contains("colors")) {
s_themes[theme["name"].get<std::string>()] = theme; (*s_themes)[theme["name"].get<std::string>()] = theme;
} else { } else {
hex::log::error("Invalid theme file"); hex::log::error("Invalid theme file");
} }
@ -77,7 +77,7 @@ namespace hex {
{ "base", s_currTheme } { "base", s_currTheme }
}; };
for (const auto &[type, handler] : s_themeHandlers) { for (const auto &[type, handler] : *s_themeHandlers) {
theme["colors"][type] = {}; theme["colors"][type] = {};
for (const auto &[key, value] : handler.colorMap) { for (const auto &[key, value] : handler.colorMap) {
@ -86,7 +86,7 @@ namespace hex {
} }
} }
for (const auto &[type, handler] : s_styleHandlers) { for (const auto &[type, handler] : *s_styleHandlers) {
theme["styles"][type] = {}; theme["styles"][type] = {};
for (const auto &[key, style] : handler.styleMap) { for (const auto &[key, style] : handler.styleMap) {
@ -105,17 +105,17 @@ namespace hex {
} }
void ThemeManager::changeTheme(std::string name) { void ThemeManager::changeTheme(std::string name) {
if (!s_themes.contains(name)) { if (!s_themes->contains(name)) {
if (s_themes.empty()) { if (s_themes->empty()) {
return; return;
} else { } else {
const std::string &defaultTheme = s_themes.begin()->first; const std::string &defaultTheme = s_themes->begin()->first;
hex::log::error("Theme '{}' does not exist, using default theme '{}' instead!", name, defaultTheme); hex::log::error("Theme '{}' does not exist, using default theme '{}' instead!", name, defaultTheme);
name = defaultTheme; name = defaultTheme;
} }
} }
const auto &theme = s_themes[name]; const auto &theme = (*s_themes)[name];
if (theme.contains("base")) { if (theme.contains("base")) {
if (theme["base"].is_string()) { if (theme["base"].is_string()) {
@ -126,14 +126,14 @@ namespace hex {
} }
} }
if (theme.contains("colors") && !s_themeHandlers.empty()) { if (theme.contains("colors") && !s_themeHandlers->empty()) {
for (const auto&[type, content] : theme["colors"].items()) { for (const auto&[type, content] : theme["colors"].items()) {
if (!s_themeHandlers.contains(type)) { if (!s_themeHandlers->contains(type)) {
log::warn("No theme handler found for '{}'", type); log::warn("No theme handler found for '{}'", type);
continue; continue;
} }
const auto &handler = s_themeHandlers[type]; const auto &handler = (*s_themeHandlers)[type];
for (const auto &[key, value] : content.items()) { for (const auto &[key, value] : content.items()) {
if (!handler.colorMap.contains(key)) { if (!handler.colorMap.contains(key)) {
log::warn("No color found for '{}.{}'", type, key); log::warn("No color found for '{}.{}'", type, key);
@ -146,19 +146,19 @@ namespace hex {
continue; continue;
} }
s_themeHandlers[type].setFunction(s_themeHandlers[type].colorMap.at(key), color.value()); (*s_themeHandlers)[type].setFunction((*s_themeHandlers)[type].colorMap.at(key), color.value());
} }
} }
} }
if (theme.contains("styles") && !s_styleHandlers.empty()) { if (theme.contains("styles") && !s_styleHandlers->empty()) {
for (const auto&[type, content] : theme["styles"].items()) { for (const auto&[type, content] : theme["styles"].items()) {
if (!s_styleHandlers.contains(type)) { if (!s_styleHandlers->contains(type)) {
log::warn("No style handler found for '{}'", type); log::warn("No style handler found for '{}'", type);
continue; continue;
} }
auto &handler = s_styleHandlers[type]; auto &handler = (*s_styleHandlers)[type];
for (const auto &[key, value] : content.items()) { for (const auto &[key, value] : content.items()) {
if (!handler.styleMap.contains(key)) if (!handler.styleMap.contains(key))
continue; continue;
@ -167,12 +167,12 @@ namespace hex {
const float scale = style.needsScaling ? 1_scaled : 1.0F; const float scale = style.needsScaling ? 1_scaled : 1.0F;
if (value.is_number_float()) { if (value.is_number_float()) {
if (auto newValue = std::get_if<float*>(&style.value); newValue != nullptr) if (const auto newValue = std::get_if<float*>(&style.value); newValue != nullptr)
**newValue = value.get<float>() * scale; **newValue = value.get<float>() * scale;
else else
log::warn("Style variable '{}' was of type ImVec2 but a float was expected.", name); log::warn("Style variable '{}' was of type ImVec2 but a float was expected.", name);
} else if (value.is_array() && value.size() == 2 && value[0].is_number_float() && value[1].is_number_float()) { } else if (value.is_array() && value.size() == 2 && value[0].is_number_float() && value[1].is_number_float()) {
if (auto newValue = std::get_if<ImVec2*>(&style.value); newValue != nullptr) if (const auto newValue = std::get_if<ImVec2*>(&style.value); newValue != nullptr)
**newValue = ImVec2(value[0].get<float>() * scale, value[1].get<float>() * scale); **newValue = ImVec2(value[0].get<float>() * scale, value[1].get<float>() * scale);
else else
log::warn("Style variable '{}' was of type float but a ImVec2 was expected.", name); log::warn("Style variable '{}' was of type float but a ImVec2 was expected.", name);
@ -203,18 +203,18 @@ namespace hex {
std::vector<std::string> ThemeManager::getThemeNames() { std::vector<std::string> ThemeManager::getThemeNames() {
std::vector<std::string> themeNames; std::vector<std::string> themeNames;
for (const auto &[name, theme] : s_themes) for (const auto &[name, theme] : *s_themes)
themeNames.push_back(name); themeNames.push_back(name);
return themeNames; return themeNames;
} }
void ThemeManager::reset() { void ThemeManager::reset() {
s_themes.clear(); s_themes->clear();
s_styleHandlers.clear(); s_styleHandlers->clear();
s_themeHandlers.clear(); s_themeHandlers->clear();
s_imageTheme.clear(); s_imageTheme->clear();
s_currTheme.clear(); s_currTheme->clear();
} }

View File

@ -13,11 +13,11 @@ namespace hex {
namespace { namespace {
std::map<std::string, TutorialManager::Tutorial> s_tutorials; AutoReset<std::map<std::string, TutorialManager::Tutorial>> s_tutorials;
decltype(s_tutorials)::iterator s_currentTutorial = s_tutorials.end(); auto s_currentTutorial = s_tutorials->end();
std::map<ImGuiID, std::string> s_highlights; AutoReset<std::map<ImGuiID, std::string>> s_highlights;
std::vector<std::pair<ImRect, std::string>> s_highlightDisplays; AutoReset<std::vector<std::pair<ImRect, std::string>>> s_highlightDisplays;
class IDStack { class IDStack {
@ -67,19 +67,19 @@ namespace hex {
TutorialManager::Tutorial& TutorialManager::createTutorial(const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription) { TutorialManager::Tutorial& TutorialManager::createTutorial(const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription) {
return s_tutorials.try_emplace(unlocalizedName, Tutorial(unlocalizedName, unlocalizedDescription)).first->second; return s_tutorials->try_emplace(unlocalizedName, Tutorial(unlocalizedName, unlocalizedDescription)).first->second;
} }
void TutorialManager::startTutorial(const UnlocalizedString &unlocalizedName) { void TutorialManager::startTutorial(const UnlocalizedString &unlocalizedName) {
s_currentTutorial = s_tutorials.find(unlocalizedName); s_currentTutorial = s_tutorials->find(unlocalizedName);
if (s_currentTutorial == s_tutorials.end()) if (s_currentTutorial == s_tutorials->end())
return; return;
s_currentTutorial->second.start(); s_currentTutorial->second.start();
} }
void TutorialManager::drawHighlights() { void TutorialManager::drawHighlights() {
for (const auto &[rect, unlocalizedText] : s_highlightDisplays) { for (const auto &[rect, unlocalizedText] : *s_highlightDisplays) {
const auto drawList = ImGui::GetForegroundDrawList(); const auto drawList = ImGui::GetForegroundDrawList();
drawList->PushClipRectFullScreen(); drawList->PushClipRectFullScreen();
@ -122,7 +122,7 @@ namespace hex {
drawList->PopClipRect(); drawList->PopClipRect();
} }
s_highlightDisplays.clear(); s_highlightDisplays->clear();
} }
void TutorialManager::drawMessageBox(std::optional<Tutorial::Step::Message> message) { void TutorialManager::drawMessageBox(std::optional<Tutorial::Step::Message> message) {
@ -196,7 +196,7 @@ namespace hex {
void TutorialManager::drawTutorial() { void TutorialManager::drawTutorial() {
drawHighlights(); drawHighlights();
if (s_currentTutorial == s_tutorials.end()) if (s_currentTutorial == s_tutorials->end())
return; return;
const auto &currentStep = s_currentTutorial->second.m_currentStep; const auto &currentStep = s_currentTutorial->second.m_currentStep;
@ -210,11 +210,11 @@ namespace hex {
void TutorialManager::reset() { void TutorialManager::reset() {
s_tutorials.clear(); s_tutorials->clear();
s_currentTutorial = s_tutorials.end(); s_currentTutorial = s_tutorials->end();
s_highlights.clear(); s_highlights->clear();
s_highlightDisplays.clear(); s_highlightDisplays->clear();
} }
TutorialManager::Tutorial::Step& TutorialManager::Tutorial::addStep() { TutorialManager::Tutorial::Step& TutorialManager::Tutorial::addStep() {
@ -252,7 +252,7 @@ namespace hex {
}, id); }, id);
} }
s_highlights.emplace(idStack.get(), text); s_highlights->emplace(idStack.get(), text);
} }
} }
@ -271,7 +271,7 @@ namespace hex {
}, id); }, id);
} }
s_highlights.erase(idStack.get()); s_highlights->erase(idStack.get());
} }
} }
@ -285,7 +285,7 @@ namespace hex {
if (m_parent->m_currentStep != m_parent->m_steps.end()) if (m_parent->m_currentStep != m_parent->m_steps.end())
m_parent->m_currentStep->addHighlights(); m_parent->m_currentStep->addHighlights();
else else
s_currentTutorial = s_tutorials.end(); s_currentTutorial = s_tutorials->end();
} }
@ -369,9 +369,9 @@ namespace hex {
} }
void ImGuiTestEngineHook_ItemAdd(ImGuiContext*, ImGuiID id, const ImRect& bb, const ImGuiLastItemData*) { void ImGuiTestEngineHook_ItemAdd(ImGuiContext*, ImGuiID id, const ImRect& bb, const ImGuiLastItemData*) {
const auto element = hex::s_highlights.find(id); const auto element = hex::s_highlights->find(id);
if (element != hex::s_highlights.end()) { if (element != hex::s_highlights->end()) {
hex::s_highlightDisplays.emplace_back(bb, element->second); hex::s_highlightDisplays->emplace_back(bb, element->second);
} }
} }

View File

@ -11,12 +11,12 @@
namespace hex { namespace hex {
std::map<std::string, WorkspaceManager::Workspace> WorkspaceManager::s_workspaces; AutoReset<std::map<std::string, WorkspaceManager::Workspace>> WorkspaceManager::s_workspaces;
decltype(WorkspaceManager::s_workspaces)::iterator WorkspaceManager::s_currentWorkspace = s_workspaces.end(); decltype(WorkspaceManager::s_workspaces)::Type::iterator WorkspaceManager::s_currentWorkspace = s_workspaces->end();
decltype(WorkspaceManager::s_workspaces)::iterator WorkspaceManager::s_previousWorkspace = s_workspaces.end(); decltype(WorkspaceManager::s_workspaces)::Type::iterator WorkspaceManager::s_previousWorkspace = s_workspaces->end();
void WorkspaceManager::createWorkspace(const std::string& name, const std::string &layout) { void WorkspaceManager::createWorkspace(const std::string& name, const std::string &layout) {
s_currentWorkspace = s_workspaces.insert_or_assign(name, Workspace { s_currentWorkspace = s_workspaces->insert_or_assign(name, Workspace {
.layout = layout.empty() ? LayoutManager::saveToString() : layout, .layout = layout.empty() ? LayoutManager::saveToString() : layout,
.path = {} .path = {}
}).first; }).first;
@ -28,8 +28,8 @@ namespace hex {
} }
void WorkspaceManager::switchWorkspace(const std::string& name) { void WorkspaceManager::switchWorkspace(const std::string& name) {
const auto newWorkspace = s_workspaces.find(name); const auto newWorkspace = s_workspaces->find(name);
if (newWorkspace != s_workspaces.end()) { if (newWorkspace != s_workspaces->end()) {
s_currentWorkspace = newWorkspace; s_currentWorkspace = newWorkspace;
log::info("Switching to workspace '{}'", name); log::info("Switching to workspace '{}'", name);
} }
@ -46,10 +46,10 @@ namespace hex {
try { try {
auto json = nlohmann::json::parse(content.begin(), content.end()); auto json = nlohmann::json::parse(content.begin(), content.end());
std::string name = json["name"]; const std::string name = json["name"];
std::string layout = json["layout"]; std::string layout = json["layout"];
s_workspaces[name] = Workspace { (*s_workspaces)[name] = Workspace {
.layout = std::move(layout), .layout = std::move(layout),
.path = path .path = path
}; };
@ -60,7 +60,7 @@ namespace hex {
bool WorkspaceManager::exportToFile(std::fs::path path, std::string workspaceName) { bool WorkspaceManager::exportToFile(std::fs::path path, std::string workspaceName) {
if (path.empty()) { if (path.empty()) {
if (s_currentWorkspace == s_workspaces.end()) if (s_currentWorkspace == s_workspaces->end())
return false; return false;
path = s_currentWorkspace->second.path; path = s_currentWorkspace->second.path;
@ -86,7 +86,7 @@ namespace hex {
void WorkspaceManager::process() { void WorkspaceManager::process() {
if (s_previousWorkspace != s_currentWorkspace) { if (s_previousWorkspace != s_currentWorkspace) {
if (s_previousWorkspace != s_workspaces.end()) if (s_previousWorkspace != s_workspaces->end())
exportToFile(s_previousWorkspace->second.path, s_previousWorkspace->first); exportToFile(s_previousWorkspace->second.path, s_previousWorkspace->first);
LayoutManager::closeAllViews(); LayoutManager::closeAllViews();
@ -98,9 +98,9 @@ namespace hex {
void WorkspaceManager::reset() { void WorkspaceManager::reset() {
s_workspaces.clear(); s_workspaces->clear();
s_currentWorkspace = s_workspaces.end(); s_currentWorkspace = s_workspaces->end();
s_previousWorkspace = s_workspaces.end(); s_previousWorkspace = s_workspaces->end();
} }

View File

@ -5,8 +5,7 @@
#include <hex/helpers/logger.hpp> #include <hex/helpers/logger.hpp>
#include <hex/helpers/fmt.hpp> #include <hex/helpers/fmt.hpp>
#include <hex/helpers/utils_linux.hpp> #include <hex/helpers/utils_linux.hpp>
#include <hex/helpers/auto_reset.hpp>
#include <xdg.hpp>
#if defined(OS_WINDOWS) #if defined(OS_WINDOWS)
#include <windows.h> #include <windows.h>
@ -23,7 +22,6 @@
#include <nfd.hpp> #include <nfd.hpp>
#endif #endif
#include <algorithm>
#include <filesystem> #include <filesystem>
#include <wolv/io/file.hpp> #include <wolv/io/file.hpp>
@ -32,7 +30,7 @@
namespace hex::fs { namespace hex::fs {
static std::function<void(const std::string&)> s_fileBrowserErrorCallback; static AutoReset<std::function<void(const std::string&)>> s_fileBrowserErrorCallback;
void setFileBrowserErrorCallback(const std::function<void(const std::string&)> &callback) { void setFileBrowserErrorCallback(const std::function<void(const std::string&)> &callback) {
s_fileBrowserErrorCallback = callback; s_fileBrowserErrorCallback = callback;
} }
@ -40,8 +38,9 @@ namespace hex::fs {
// With help from https://github.com/owncloud/client/blob/cba22aa34b3677406e0499aadd126ce1d94637a2/src/gui/openfilemanager.cpp // With help from https://github.com/owncloud/client/blob/cba22aa34b3677406e0499aadd126ce1d94637a2/src/gui/openfilemanager.cpp
void openFileExternal(const std::fs::path &filePath) { void openFileExternal(const std::fs::path &filePath) {
// Make sure the file exists before trying to open it // Make sure the file exists before trying to open it
if (!wolv::io::fs::exists(filePath)) if (!wolv::io::fs::exists(filePath)) {
return; return;
}
#if defined(OS_WINDOWS) #if defined(OS_WINDOWS)
hex::unused( hex::unused(
@ -58,8 +57,9 @@ namespace hex::fs {
void openFolderExternal(const std::fs::path &dirPath) { void openFolderExternal(const std::fs::path &dirPath) {
// Make sure the folder exists before trying to open it // Make sure the folder exists before trying to open it
if (!wolv::io::fs::exists(dirPath)) if (!wolv::io::fs::exists(dirPath)) {
return; return;
}
#if defined(OS_WINDOWS) #if defined(OS_WINDOWS)
hex::unused(system( hex::unused(system(
@ -76,8 +76,9 @@ namespace hex::fs {
void openFolderWithSelectionExternal(const std::fs::path &selectedFilePath) { void openFolderWithSelectionExternal(const std::fs::path &selectedFilePath) {
// Make sure the file exists before trying to open it // Make sure the file exists before trying to open it
if (!wolv::io::fs::exists(selectedFilePath)) if (!wolv::io::fs::exists(selectedFilePath)) {
return; return;
}
#if defined(OS_WINDOWS) #if defined(OS_WINDOWS)
hex::unused(system( hex::unused(system(
@ -203,7 +204,7 @@ namespace hex::fs {
bool openFileBrowser(DialogMode mode, const std::vector<ItemFilter> &validExtensions, const std::function<void(std::fs::path)> &callback, const std::string &defaultPath, bool multiple) { bool openFileBrowser(DialogMode mode, const std::vector<ItemFilter> &validExtensions, const std::function<void(std::fs::path)> &callback, const std::string &defaultPath, bool multiple) {
// Turn the content of the ItemFilter objects into something NFD understands // Turn the content of the ItemFilter objects into something NFD understands
std::vector<nfdfilteritem_t> validExtensionsNfd; std::vector<nfdfilteritem_t> validExtensionsNfd(validExtensions.size());
for (const auto &extension : validExtensions) { for (const auto &extension : validExtensions) {
validExtensionsNfd.emplace_back(nfdfilteritem_t{ extension.name.c_str(), extension.spec.c_str() }); validExtensionsNfd.emplace_back(nfdfilteritem_t{ extension.name.c_str(), extension.spec.c_str() });
} }
@ -215,9 +216,9 @@ namespace hex::fs {
if (NFD::Init() != NFD_OKAY) { if (NFD::Init() != NFD_OKAY) {
// Handle errors if initialization failed // Handle errors if initialization failed
log::error("NFD init returned an error: {}", NFD::GetError()); log::error("NFD init returned an error: {}", NFD::GetError());
if (s_fileBrowserErrorCallback != nullptr) { if (*s_fileBrowserErrorCallback != nullptr) {
auto error = NFD::GetError(); const auto error = NFD::GetError();
s_fileBrowserErrorCallback(error != nullptr ? error : "No details"); (*s_fileBrowserErrorCallback)(error != nullptr ? error : "No details");
} }
return false; return false;
@ -267,9 +268,9 @@ namespace hex::fs {
log::error("Requested file dialog returned an error: {}", NFD::GetError()); log::error("Requested file dialog returned an error: {}", NFD::GetError());
if (s_fileBrowserErrorCallback != nullptr) { if (*s_fileBrowserErrorCallback != nullptr) {
auto error = NFD::GetError(); const auto error = NFD::GetError();
s_fileBrowserErrorCallback(error != nullptr ? error : "No details"); (*s_fileBrowserErrorCallback)(error != nullptr ? error : "No details");
} }
} }
@ -326,7 +327,7 @@ namespace hex::fs {
// Add additional data directories to the path // Add additional data directories to the path
auto additionalDirs = ImHexApi::System::getAdditionalFolderPaths(); auto additionalDirs = ImHexApi::System::getAdditionalFolderPaths();
std::copy(additionalDirs.begin(), additionalDirs.end(), std::back_inserter(paths)); std::ranges::copy(additionalDirs, std::back_inserter(paths));
// Add the project file directory to the path, if one is loaded // Add the project file directory to the path, if one is loaded
if (ProjectFile::hasPath()) { if (ProjectFile::hasPath()) {
@ -457,7 +458,7 @@ namespace hex::fs {
// Try to create a new file in the given path // Try to create a new file in the given path
// If that fails, or the file cannot be deleted anymore afterward; the path is not writable // If that fails, or the file cannot be deleted anymore afterward; the path is not writable
wolv::io::File file(path / TestFileName, wolv::io::File::Mode::Create); wolv::io::File file(path / TestFileName, wolv::io::File::Mode::Create);
bool result = file.isValid(); const bool result = file.isValid();
if (!file.remove()) if (!file.remove())
return false; return false;

View File

@ -47,6 +47,12 @@
void setupMacosWindowStyle(GLFWwindow *window) { void setupMacosWindowStyle(GLFWwindow *window) {
NSWindow* cocoaWindow = glfwGetCocoaWindow(window); NSWindow* cocoaWindow = glfwGetCocoaWindow(window);
cocoaWindow.titleVisibility = NSWindowTitleHidden; cocoaWindow.titleVisibility = NSWindowTitleHidden;
NSVisualEffectView *visualEffectView = [[NSVisualEffectView alloc] init];
[visualEffectView setMaterial:NSVisualEffectMaterialAppearanceBased];
[visualEffectView setBlendingMode:NSVisualEffectBlendingModeBehindWindow];
[cocoaWindow.contentView addSubview:visualEffectView positioned:NSWindowBelow relativeTo:nil];
} }
@interface HexDocument : NSDocument @interface HexDocument : NSDocument

View File

@ -1,10 +1,11 @@
#include <hex/ui/popup.hpp> #include <hex/ui/popup.hpp>
#include <hex/helpers/auto_reset.hpp>
namespace hex::impl { namespace hex::impl {
[[nodiscard]] std::vector<std::unique_ptr<PopupBase>> &PopupBase::getOpenPopups() { [[nodiscard]] std::vector<std::unique_ptr<PopupBase>> &PopupBase::getOpenPopups() {
static std::vector<std::unique_ptr<PopupBase>> openPopups; static AutoReset<std::vector<std::unique_ptr<PopupBase>>> openPopups;
return openPopups; return openPopups;
} }

View File

@ -1,9 +1,10 @@
#include <hex/ui/toast.hpp> #include <hex/ui/toast.hpp>
#include <hex/helpers/auto_reset.hpp>
namespace hex::impl { namespace hex::impl {
[[nodiscard]] std::list<std::unique_ptr<ToastBase>> &ToastBase::getQueuedToasts() { [[nodiscard]] std::list<std::unique_ptr<ToastBase>> &ToastBase::getQueuedToasts() {
static std::list<std::unique_ptr<ToastBase>> queuedToasts; static AutoReset<std::list<std::unique_ptr<ToastBase>>> queuedToasts;
return queuedToasts; return queuedToasts;
} }

View File

@ -9,17 +9,11 @@
#include <hex/helpers/logger.hpp> #include <hex/helpers/logger.hpp>
#include <hex/api/content_registry.hpp> #include <hex/api/content_registry.hpp>
#include <hex/api/project_file_manager.hpp>
#include <hex/api/theme_manager.hpp>
#include <hex/api/plugin_manager.hpp> #include <hex/api/plugin_manager.hpp>
#include <hex/api/layout_manager.hpp>
#include <hex/api/achievement_manager.hpp> #include <hex/api/achievement_manager.hpp>
#include <hex/api/tutorial_manager.hpp>
#include <hex/api/workspace_manager.hpp>
#include <hex/ui/view.hpp> #include <hex/ui/view.hpp>
#include <hex/ui/popup.hpp> #include <hex/ui/popup.hpp>
#include <hex/ui/toast.hpp>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
@ -59,108 +53,41 @@ namespace hex::init {
return result; return result;
} }
bool deleteSharedData() { bool prepareExit() {
// This function is called when ImHex is closed. It deletes all shared data that was created by plugins
// This is a bit of a hack but necessary because when ImHex gets closed, all plugins are unloaded in order for
// destructors to be called correctly. To prevent crashes when ImHex exits, we need to delete all shared data
EventImHexClosing::post();
EventManager::clear();
while (ImHexApi::Provider::isValid())
ImHexApi::Provider::remove(ImHexApi::Provider::get());
// Terminate all asynchronous tasks // Terminate all asynchronous tasks
TaskManager::exit(); TaskManager::exit();
ContentRegistry::Provider::impl::getEntries().clear();
ImHexApi::System::getInitArguments().clear();
ImHexApi::HexEditor::impl::getBackgroundHighlights().clear();
ImHexApi::HexEditor::impl::getForegroundHighlights().clear();
ImHexApi::HexEditor::impl::getBackgroundHighlightingFunctions().clear();
ImHexApi::HexEditor::impl::getForegroundHighlightingFunctions().clear();
ImHexApi::HexEditor::impl::getTooltips().clear();
ImHexApi::HexEditor::impl::getTooltipFunctions().clear();
ImHexApi::System::getAdditionalFolderPaths().clear();
ImHexApi::Messaging::impl::getHandlers().clear();
ImHexApi::Fonts::getCustomFontPath().clear();
ImHexApi::Fonts::impl::getFonts().clear();
ContentRegistry::Settings::impl::getSettings().clear();
ContentRegistry::Settings::impl::getSettingsData().clear();
ContentRegistry::CommandPaletteCommands::impl::getEntries().clear();
ContentRegistry::CommandPaletteCommands::impl::getHandlers().clear();
ContentRegistry::PatternLanguage::impl::getFunctions().clear();
ContentRegistry::PatternLanguage::impl::getPragmas().clear();
ContentRegistry::PatternLanguage::impl::getVisualizers().clear();
ContentRegistry::PatternLanguage::impl::getInlineVisualizers().clear();
ContentRegistry::Views::impl::getEntries().clear();
impl::PopupBase::getOpenPopups().clear();
impl::ToastBase::getQueuedToasts().clear();
ContentRegistry::Tools::impl::getEntries().clear();
ContentRegistry::DataInspector::impl::getEntries().clear();
ContentRegistry::Language::impl::getLanguages().clear();
ContentRegistry::Language::impl::getLanguageDefinitions().clear();
LocalizationManager::impl::resetLanguageStrings();
ContentRegistry::Interface::impl::getWelcomeScreenEntries().clear();
ContentRegistry::Interface::impl::getFooterItems().clear();
ContentRegistry::Interface::impl::getToolbarItems().clear();
ContentRegistry::Interface::impl::getMainMenuItems().clear();
ContentRegistry::Interface::impl::getMenuItems().clear();
ContentRegistry::Interface::impl::getSidebarItems().clear();
ContentRegistry::Interface::impl::getTitleBarButtons().clear();
ShortcutManager::clearShortcuts();
ContentRegistry::DataProcessorNode::impl::getEntries().clear();
ContentRegistry::DataFormatter::impl::getEntries().clear();
ContentRegistry::FileHandler::impl::getEntries().clear();
ContentRegistry::Hashes::impl::getHashes().clear();
ContentRegistry::HexEditor::impl::getVisualizers().clear();
ContentRegistry::HexEditor::impl::getMiniMapVisualizers().clear();
ContentRegistry::BackgroundServices::impl::stopServices();
ContentRegistry::CommunicationInterface::impl::getNetworkEndpoints().clear();
ContentRegistry::Experiments::impl::getExperiments().clear();
ContentRegistry::Reports::impl::getGenerators().clear();
ContentRegistry::Diffing::impl::getAlgorithms().clear();
WorkspaceManager::reset();
LayoutManager::reset();
ThemeManager::reset();
AchievementManager::getAchievements().clear();
TutorialManager::reset();
ProjectFile::getHandlers().clear();
ProjectFile::getProviderHandlers().clear();
ProjectFile::setProjectFunctions(nullptr, nullptr);
fs::setFileBrowserErrorCallback(nullptr);
// Unlock font atlas so it can be deleted in case of a crash // Unlock font atlas so it can be deleted in case of a crash
if (ImGui::GetCurrentContext() != nullptr) if (ImGui::GetCurrentContext() != nullptr)
ImGui::GetIO().Fonts->Locked = false; ImGui::GetIO().Fonts->Locked = false;
// Print a nice message if a crash happened while cleaning up resources
// To the person fixing this:
// ALWAYS wrap static heap allocated objects inside libimhex such as std::vector, std::string, std::function, etc. in a AutoReset<T>
// e.g `AutoReset<std::vector<MyStruct>> m_structs;`
//
// The reason this is necessary is because each plugin / dynamic library gets its own instance of `std::allocator`
// which will try to free the allocated memory when the object is destroyed. However since the storage is static, this
// will happen only when libimhex is unloaded after main() returns. At this point all plugins have been unloaded already so
// the std::allocator will try to free memory in a heap that does not exist anymore which will cause a crash.
// By wrapping the object in a AutoReset<T>, the `EventImHexClosing` event will automatically handle clearing the object
// while the heap is still valid.
// The heap stays valid right up to the point where `PluginManager::unload()` is called.
EventAbnormalTermination::post([] {
log::fatal("A crash happened while cleaning up resources during exit!");
log::fatal("This is most certainly because WerWolv again forgot to mark a heap allocated object as 'AutoReset'.");
log::fatal("Please report this issue on the ImHex GitHub page!");
log::fatal("To the person fixing this, read the comment above this message for more information.");
});
EventImHexClosing::post();
EventManager::clear();
return true; return true;
} }
bool loadPlugins() { bool loadPlugins() {
// Load all plugins // Load all plugins
bool hasExtraPluginFolders = !PluginManager::getPluginLoadPaths().empty();
#if !defined(IMHEX_STATIC_LINK_PLUGINS) #if !defined(IMHEX_STATIC_LINK_PLUGINS)
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Plugins)) { for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Plugins)) {
PluginManager::addLoadPath(dir); PluginManager::addLoadPath(dir);
@ -170,7 +97,7 @@ namespace hex::init {
#endif #endif
// Get loaded plugins // Get loaded plugins
auto &plugins = PluginManager::getPlugins(); const auto &plugins = PluginManager::getPlugins();
// If no plugins were loaded, ImHex wasn't installed properly. This will trigger an error popup later on // If no plugins were loaded, ImHex wasn't installed properly. This will trigger an error popup later on
if (plugins.empty()) { if (plugins.empty()) {
@ -180,7 +107,7 @@ namespace hex::init {
return false; return false;
} }
const auto shouldLoadPlugin = [hasExtraPluginFolders, executablePath = wolv::io::fs::getExecutablePath()](const Plugin &plugin) { const auto shouldLoadPlugin = [executablePath = wolv::io::fs::getExecutablePath()](const Plugin &plugin) {
// In debug builds, ignore all plugins that are not part of the executable directory // In debug builds, ignore all plugins that are not part of the executable directory
#if !defined(DEBUG) #if !defined(DEBUG)
return true; return true;
@ -189,7 +116,7 @@ namespace hex::init {
if (!executablePath.has_value()) if (!executablePath.has_value())
return true; return true;
if (hasExtraPluginFolders) if (!PluginManager::getPluginLoadPaths().empty())
return true; return true;
// Check if the plugin is somewhere in the same directory tree as the executable // Check if the plugin is somewhere in the same directory tree as the executable
@ -283,7 +210,6 @@ namespace hex::init {
bool unloadPlugins() { bool unloadPlugins() {
PluginManager::unload(); PluginManager::unload();
PluginManager::getPluginLoadPaths().clear();
return true; return true;
} }
@ -320,7 +246,7 @@ namespace hex::init {
// Run all exit tasks, and print to console // Run all exit tasks, and print to console
void runExitTasks() { void runExitTasks() {
for (const auto &[name, task, async] : init::getExitTasks()) { for (const auto &[name, task, async] : init::getExitTasks()) {
bool result = task(); const bool result = task();
log::info("Exit task '{0}' finished {1}", name, result ? "successfully" : "unsuccessfully"); log::info("Exit task '{0}' finished {1}", name, result ? "successfully" : "unsuccessfully");
} }
} }
@ -337,7 +263,7 @@ namespace hex::init {
std::vector<Task> getExitTasks() { std::vector<Task> getExitTasks() {
return { return {
{ "Saving settings", storeSettings, false }, { "Saving settings", storeSettings, false },
{ "Cleaning up shared data", deleteSharedData, false }, { "Prepare exit", prepareExit, false },
{ "Unloading plugins", unloadPlugins, false }, { "Unloading plugins", unloadPlugins, false },
{ "Deleting old files", deleteOldFiles, false }, { "Deleting old files", deleteOldFiles, false },
}; };