1
0
mirror of synced 2024-12-01 02:37:18 +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
#include <wolv/io/fs.hpp>
#include <hex/helpers/auto_reset.hpp>
#include <map>
#include <string>
@ -20,7 +21,7 @@ namespace hex {
static void importFromFile(const std::fs::path &path);
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 void reset();
@ -30,8 +31,8 @@ namespace hex {
private:
WorkspaceManager() = default;
static std::map<std::string, Workspace> s_workspaces;
static decltype(s_workspaces)::iterator s_currentWorkspace, s_previousWorkspace;
static AutoReset<std::map<std::string, Workspace>> s_workspaces;
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/event_manager.hpp>
#include <hex/helpers/auto_reset.hpp>
#include <nlohmann/json.hpp>
namespace hex {
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;
}
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;
nodeCategoryStorage.clear();
nodeCategoryStorage->clear();
// Add all achievements to the node storage
for (auto &[categoryName, achievements] : getAchievements()) {
auto &nodes = nodeCategoryStorage[categoryName];
auto &nodes = (*nodeCategoryStorage)[categoryName];
for (auto &[achievementName, achievement] : achievements) {
nodes.emplace_back(achievement.get());
@ -32,21 +34,21 @@ namespace hex {
}
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;
auto &nodeCategoryStorage = getAchievementNodes();
startNodes.clear();
startNodes->clear();
// Add all parents and children to the nodes
for (auto &[categoryName, achievements] : nodeCategoryStorage) {
for (auto &achievementNode : achievements) {
for (auto &requirement : achievementNode.achievement->getRequirements()) {
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;
});
@ -59,7 +61,7 @@ namespace hex {
for (auto &requirement : achievementNode.achievement->getVisibilityRequirements()) {
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;
});
@ -74,12 +76,12 @@ namespace hex {
for (auto &[categoryName, achievements] : nodeCategoryStorage) {
for (auto &achievementNode : achievements) {
if (!achievementNode.hasParents()) {
startNodes[categoryName].emplace_back(&achievementNode);
(*startNodes)[categoryName].emplace_back(&achievementNode);
}
for (const auto &parent : achievementNode.parents) {
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 achievementIter = achievements.find(unlocalizedName);
const auto achievementIter = achievements.find(unlocalizedName);
if (achievementIter == achievements.end()) {
return;
}
auto &nodes = getAchievementNodes()[categoryName];
const auto &nodes = getAchievementNodes()[categoryName];
for (const auto &node : nodes) {
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.writeString(result);
break;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,7 +8,7 @@ namespace hex {
namespace {
std::map<Shortcut, ShortcutManager::ShortcutEntry> s_globalShortcuts;
AutoReset<std::map<Shortcut, ShortcutManager::ShortcutEntry>> s_globalShortcuts;
std::atomic<bool> s_paused;
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) {
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) {
@ -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) {
Shortcut pressedShortcut = getShortcut(ctrl, alt, shift, super, focused, keyCode);
const Shortcut pressedShortcut = getShortcut(ctrl, alt, shift, super, focused, keyCode);
if (keyCode != 0)
s_prevShortcut = Shortcut(pressedShortcut.getKeys());
@ -65,7 +65,7 @@ namespace hex {
}
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)
s_prevShortcut = Shortcut(pressedShortcut.getKeys());
@ -73,7 +73,7 @@ namespace hex {
}
void ShortcutManager::clearShortcuts() {
s_globalShortcuts.clear();
s_globalShortcuts->clear();
}
void ShortcutManager::resumeShortcuts() {
@ -90,17 +90,18 @@ namespace hex {
}
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);
return result;
}
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)
result.push_back(entry);

View File

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

View File

@ -10,21 +10,21 @@ namespace hex {
namespace {
std::map<std::string, nlohmann::json> s_themes;
std::map<std::string, ThemeManager::ThemeHandler> s_themeHandlers;
std::map<std::string, ThemeManager::StyleHandler> s_styleHandlers;
std::string s_imageTheme;
std::string s_currTheme;
AutoReset<std::map<std::string, nlohmann::json>> s_themes;
AutoReset<std::map<std::string, ThemeManager::ThemeHandler>> s_themeHandlers;
AutoReset<std::map<std::string, ThemeManager::StyleHandler>> s_styleHandlers;
AutoReset<std::string> s_imageTheme;
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) {
s_themeHandlers[name] = { colorMap, getFunction, setFunction };
(*s_themeHandlers)[name] = { colorMap, getFunction, setFunction };
}
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) {
@ -32,7 +32,7 @@ namespace hex {
auto theme = nlohmann::json::parse(content);
if (theme.contains("name") && theme.contains("colors")) {
s_themes[theme["name"].get<std::string>()] = theme;
(*s_themes)[theme["name"].get<std::string>()] = theme;
} else {
hex::log::error("Invalid theme file");
}
@ -77,7 +77,7 @@ namespace hex {
{ "base", s_currTheme }
};
for (const auto &[type, handler] : s_themeHandlers) {
for (const auto &[type, handler] : *s_themeHandlers) {
theme["colors"][type] = {};
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] = {};
for (const auto &[key, style] : handler.styleMap) {
@ -105,17 +105,17 @@ namespace hex {
}
void ThemeManager::changeTheme(std::string name) {
if (!s_themes.contains(name)) {
if (s_themes.empty()) {
if (!s_themes->contains(name)) {
if (s_themes->empty()) {
return;
} 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);
name = defaultTheme;
}
}
const auto &theme = s_themes[name];
const auto &theme = (*s_themes)[name];
if (theme.contains("base")) {
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()) {
if (!s_themeHandlers.contains(type)) {
if (!s_themeHandlers->contains(type)) {
log::warn("No theme handler found for '{}'", type);
continue;
}
const auto &handler = s_themeHandlers[type];
const auto &handler = (*s_themeHandlers)[type];
for (const auto &[key, value] : content.items()) {
if (!handler.colorMap.contains(key)) {
log::warn("No color found for '{}.{}'", type, key);
@ -146,19 +146,19 @@ namespace hex {
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()) {
if (!s_styleHandlers.contains(type)) {
if (!s_styleHandlers->contains(type)) {
log::warn("No style handler found for '{}'", type);
continue;
}
auto &handler = s_styleHandlers[type];
auto &handler = (*s_styleHandlers)[type];
for (const auto &[key, value] : content.items()) {
if (!handler.styleMap.contains(key))
continue;
@ -167,12 +167,12 @@ namespace hex {
const float scale = style.needsScaling ? 1_scaled : 1.0F;
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;
else
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()) {
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);
else
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> themeNames;
for (const auto &[name, theme] : s_themes)
for (const auto &[name, theme] : *s_themes)
themeNames.push_back(name);
return themeNames;
}
void ThemeManager::reset() {
s_themes.clear();
s_styleHandlers.clear();
s_themeHandlers.clear();
s_imageTheme.clear();
s_currTheme.clear();
s_themes->clear();
s_styleHandlers->clear();
s_themeHandlers->clear();
s_imageTheme->clear();
s_currTheme->clear();
}

View File

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

View File

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

View File

@ -5,8 +5,7 @@
#include <hex/helpers/logger.hpp>
#include <hex/helpers/fmt.hpp>
#include <hex/helpers/utils_linux.hpp>
#include <xdg.hpp>
#include <hex/helpers/auto_reset.hpp>
#if defined(OS_WINDOWS)
#include <windows.h>
@ -23,7 +22,6 @@
#include <nfd.hpp>
#endif
#include <algorithm>
#include <filesystem>
#include <wolv/io/file.hpp>
@ -32,7 +30,7 @@
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) {
s_fileBrowserErrorCallback = callback;
}
@ -40,8 +38,9 @@ namespace hex::fs {
// With help from https://github.com/owncloud/client/blob/cba22aa34b3677406e0499aadd126ce1d94637a2/src/gui/openfilemanager.cpp
void openFileExternal(const std::fs::path &filePath) {
// Make sure the file exists before trying to open it
if (!wolv::io::fs::exists(filePath))
if (!wolv::io::fs::exists(filePath)) {
return;
}
#if defined(OS_WINDOWS)
hex::unused(
@ -58,8 +57,9 @@ namespace hex::fs {
void openFolderExternal(const std::fs::path &dirPath) {
// Make sure the folder exists before trying to open it
if (!wolv::io::fs::exists(dirPath))
if (!wolv::io::fs::exists(dirPath)) {
return;
}
#if defined(OS_WINDOWS)
hex::unused(system(
@ -76,8 +76,9 @@ namespace hex::fs {
void openFolderWithSelectionExternal(const std::fs::path &selectedFilePath) {
// Make sure the file exists before trying to open it
if (!wolv::io::fs::exists(selectedFilePath))
if (!wolv::io::fs::exists(selectedFilePath)) {
return;
}
#if defined(OS_WINDOWS)
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) {
// 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) {
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) {
// Handle errors if initialization failed
log::error("NFD init returned an error: {}", NFD::GetError());
if (s_fileBrowserErrorCallback != nullptr) {
auto error = NFD::GetError();
s_fileBrowserErrorCallback(error != nullptr ? error : "No details");
if (*s_fileBrowserErrorCallback != nullptr) {
const auto error = NFD::GetError();
(*s_fileBrowserErrorCallback)(error != nullptr ? error : "No details");
}
return false;
@ -267,9 +268,9 @@ namespace hex::fs {
log::error("Requested file dialog returned an error: {}", NFD::GetError());
if (s_fileBrowserErrorCallback != nullptr) {
auto error = NFD::GetError();
s_fileBrowserErrorCallback(error != nullptr ? error : "No details");
if (*s_fileBrowserErrorCallback != nullptr) {
const auto error = NFD::GetError();
(*s_fileBrowserErrorCallback)(error != nullptr ? error : "No details");
}
}
@ -326,7 +327,7 @@ namespace hex::fs {
// Add additional data directories to the path
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
if (ProjectFile::hasPath()) {
@ -457,7 +458,7 @@ namespace hex::fs {
// 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
wolv::io::File file(path / TestFileName, wolv::io::File::Mode::Create);
bool result = file.isValid();
const bool result = file.isValid();
if (!file.remove())
return false;

View File

@ -47,6 +47,12 @@
void setupMacosWindowStyle(GLFWwindow *window) {
NSWindow* cocoaWindow = glfwGetCocoaWindow(window);
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

View File

@ -1,10 +1,11 @@
#include <hex/ui/popup.hpp>
#include <hex/helpers/auto_reset.hpp>
namespace hex::impl {
[[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;
}

View File

@ -1,9 +1,10 @@
#include <hex/ui/toast.hpp>
#include <hex/helpers/auto_reset.hpp>
namespace hex::impl {
[[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;
}

View File

@ -9,17 +9,11 @@
#include <hex/helpers/logger.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/layout_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/popup.hpp>
#include <hex/ui/toast.hpp>
#include <nlohmann/json.hpp>
@ -59,108 +53,41 @@ namespace hex::init {
return result;
}
bool deleteSharedData() {
// 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());
bool prepareExit() {
// Terminate all asynchronous tasks
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
if (ImGui::GetCurrentContext() != nullptr)
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;
}
bool loadPlugins() {
// Load all plugins
bool hasExtraPluginFolders = !PluginManager::getPluginLoadPaths().empty();
#if !defined(IMHEX_STATIC_LINK_PLUGINS)
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Plugins)) {
PluginManager::addLoadPath(dir);
@ -170,7 +97,7 @@ namespace hex::init {
#endif
// 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 (plugins.empty()) {
@ -180,7 +107,7 @@ namespace hex::init {
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
#if !defined(DEBUG)
return true;
@ -189,7 +116,7 @@ namespace hex::init {
if (!executablePath.has_value())
return true;
if (hasExtraPluginFolders)
if (!PluginManager::getPluginLoadPaths().empty())
return true;
// Check if the plugin is somewhere in the same directory tree as the executable
@ -283,7 +210,6 @@ namespace hex::init {
bool unloadPlugins() {
PluginManager::unload();
PluginManager::getPluginLoadPaths().clear();
return true;
}
@ -320,7 +246,7 @@ namespace hex::init {
// Run all exit tasks, and print to console
void runExitTasks() {
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");
}
}
@ -337,7 +263,7 @@ namespace hex::init {
std::vector<Task> getExitTasks() {
return {
{ "Saving settings", storeSettings, false },
{ "Cleaning up shared data", deleteSharedData, false },
{ "Prepare exit", prepareExit, false },
{ "Unloading plugins", unloadPlugins, false },
{ "Deleting old files", deleteOldFiles, false },
};