#include #include #include #include #include #include #include #include #include #include #include namespace hex::plugin::builtin { static std::string s_popupMessage; static std::function s_yesCallback, s_noCallback; static std::set s_selectableFileIndices; static std::vector s_selectableFiles; static std::function s_selectableFileOpenCallback; static std::vector s_selectableFilesValidExtensions; static bool s_selectableFileMultiple; static void drawGlobalPopups() { // "Are you sure you want to exit?" Popup if (ImGui::BeginPopupModal("hex.builtin.popup.exit_application.title"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::NewLine(); ImGui::TextUnformatted("hex.builtin.popup.exit_application.desc"_lang); ImGui::NewLine(); View::confirmButtons("hex.builtin.common.yes"_lang, "hex.builtin.common.no"_lang, [] { ImHexApi::Provider::resetDirty(); ImHexApi::Common::closeImHex(); }, [] { ImGui::CloseCurrentPopup(); } ); if (ImGui::IsKeyDown(ImGui::GetKeyIndex(ImGuiKey_Escape))) ImGui::CloseCurrentPopup(); ImGui::EndPopup(); } // "Are you sure you want to close provider?" Popup if (ImGui::BeginPopupModal("hex.builtin.popup.close_provider.title"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::NewLine(); ImGui::TextUnformatted("hex.builtin.popup.close_provider.desc"_lang); ImGui::NewLine(); View::confirmButtons("hex.builtin.common.yes"_lang, "hex.builtin.common.no"_lang, [] { ImHexApi::Provider::remove(ImHexApi::Provider::impl::getClosingProvider(), true); ImGui::CloseCurrentPopup(); }, [] { ImGui::CloseCurrentPopup(); } ); if (ImGui::IsKeyDown(ImGui::GetKeyIndex(ImGuiKey_Escape))) ImGui::CloseCurrentPopup(); ImGui::EndPopup(); } if (ImGui::BeginPopupModal("hex.builtin.popup.waiting_for_tasks.title"_lang, nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove)) { ImGui::TextUnformatted("hex.builtin.popup.waiting_for_tasks.desc"_lang); ImGui::Separator(); ImGui::SetCursorPosX((ImGui::GetWindowWidth() - ImGui::CalcTextSize("[-]").x) / 2); ImGui::TextSpinner(""); ImGui::NewLine(); ImGui::SetCursorPosX((ImGui::GetWindowWidth() - 150_scaled) / 2); if (ImGui::ButtonEx("hex.builtin.common.cancel"_lang, ImVec2(150, 0)) || ImGui::IsKeyDown(ImGuiKey_Escape)) ImGui::CloseCurrentPopup(); if (TaskManager::getRunningTaskCount() == 0 && TaskManager::getRunningBackgroundTaskCount() == 0) { ImGui::CloseCurrentPopup(); ImHexApi::Common::closeImHex(); } ImGui::EndPopup(); } auto windowSize = ImHexApi::System::getMainWindowSize(); // Info popup ImGui::SetNextWindowSizeConstraints(scaled(ImVec2(400, 100)), scaled(ImVec2(600, 300))); if (ImGui::BeginPopupModal("hex.builtin.common.info"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::TextFormattedWrapped("{}", s_popupMessage.c_str()); ImGui::NewLine(); ImGui::Separator(); if (ImGui::Button("hex.builtin.common.okay"_lang) || ImGui::IsKeyDown(ImGuiKey_Escape)) ImGui::CloseCurrentPopup(); ImGui::SetWindowPos((windowSize - ImGui::GetWindowSize()) / 2, ImGuiCond_Appearing); ImGui::EndPopup(); } // Error popup ImGui::SetNextWindowSizeConstraints(scaled(ImVec2(400, 100)), scaled(ImVec2(600, 300))); if (ImGui::BeginPopupModal("hex.builtin.common.error"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::TextFormattedWrapped("{}", s_popupMessage.c_str()); ImGui::NewLine(); ImGui::Separator(); if (ImGui::Button("hex.builtin.common.okay"_lang) || ImGui::IsKeyDown(ImGuiKey_Escape)) ImGui::CloseCurrentPopup(); ImGui::SetWindowPos((windowSize - ImGui::GetWindowSize()) / 2, ImGuiCond_Appearing); ImGui::EndPopup(); } // Fatal error popup ImGui::SetNextWindowSizeConstraints(scaled(ImVec2(400, 100)), scaled(ImVec2(600, 300))); if (ImGui::BeginPopupModal("hex.builtin.common.fatal"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::TextFormattedWrapped("{}", s_popupMessage.c_str()); ImGui::NewLine(); ImGui::Separator(); if (ImGui::Button("hex.builtin.common.okay"_lang) || ImGui::IsKeyDown(ImGuiKey_Escape)) { ImHexApi::Common::closeImHex(); ImGui::CloseCurrentPopup(); } ImGui::SetWindowPos((windowSize - ImGui::GetWindowSize()) / 2, ImGuiCond_Appearing); ImGui::EndPopup(); } // Yes/No question popup ImGui::SetNextWindowSizeConstraints(scaled(ImVec2(400, 100)), scaled(ImVec2(600, 300))); if (ImGui::BeginPopupModal("hex.builtin.common.question"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::TextFormattedWrapped("{}", s_popupMessage.c_str()); ImGui::NewLine(); ImGui::Separator(); View::confirmButtons( "hex.builtin.common.yes"_lang, "hex.builtin.common.no"_lang, [] { s_yesCallback(); ImGui::CloseCurrentPopup(); }, [] { s_noCallback(); ImGui::CloseCurrentPopup(); }); ImGui::SetWindowPos((windowSize - ImGui::GetWindowSize()) / 2, ImGuiCond_Appearing); ImGui::EndPopup(); } // File chooser popup bool opened = true; ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5F, 0.5F)); if (ImGui::BeginPopupModal("hex.builtin.common.choose_file"_lang, &opened, ImGuiWindowFlags_AlwaysAutoResize)) { if (ImGui::BeginListBox("##files", scaled(ImVec2(500, 400)))) { u32 index = 0; for (auto &path : s_selectableFiles) { ImGui::PushID(index); bool selected = s_selectableFileIndices.contains(index); if (ImGui::Selectable(wolv::util::toUTF8String(path.filename()).c_str(), selected)) { if (!s_selectableFileMultiple) { s_selectableFileIndices.clear(); s_selectableFileIndices.insert(index); } else { if (selected) { s_selectableFileIndices.erase(index); } else { s_selectableFileIndices.insert(index); } } } ImGui::PopID(); index++; } ImGui::EndListBox(); } if (ImGui::Button("hex.builtin.common.open"_lang)) { for (auto &index : s_selectableFileIndices) s_selectableFileOpenCallback(s_selectableFiles[index]); ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("hex.builtin.common.browse"_lang)) { fs::openFileBrowser(fs::DialogMode::Open, s_selectableFilesValidExtensions, [](const auto &path) { s_selectableFileOpenCallback(path); ImGui::CloseCurrentPopup(); }, {}, s_selectableFileMultiple); } ImGui::EndPopup(); } // Task exception popup for (const auto &task : TaskManager::getRunningTasks()) { if (task->hadException()) { EventManager::post(hex::format("hex.builtin.popup.error.task_exception"_lang, LangEntry(task->getUnlocalizedName()), task->getExceptionMessage())); task->clearException(); break; } } } void addGlobalUIItems() { EventManager::subscribe(drawGlobalPopups); EventManager::subscribe([](const std::string &message) { s_popupMessage = message; TaskManager::doLater([] { ImGui::OpenPopup("hex.builtin.common.info"_lang); }); }); EventManager::subscribe([](const std::string &message) { s_popupMessage = message; TaskManager::doLater([] { ImGui::OpenPopup("hex.builtin.common.error"_lang); }); }); EventManager::subscribe([](const std::string &message) { s_popupMessage = message; TaskManager::doLater([] { ImGui::OpenPopup("hex.builtin.common.fatal"_lang); }); }); EventManager::subscribe([](const std::string &message, const std::function &yesCallback, const std::function &noCallback) { s_popupMessage = message; s_yesCallback = yesCallback; s_noCallback = noCallback; TaskManager::doLater([] { ImGui::OpenPopup("hex.builtin.common.question"_lang); }); }); EventManager::subscribe([](const std::vector &paths, const std::vector &validExtensions, const std::function &callback, bool multiple) { s_selectableFileIndices = { }; s_selectableFiles = paths; std::sort(s_selectableFiles.begin(), s_selectableFiles.end(), [](const auto &a, const auto &b) { return a.filename() < b.filename(); } ); s_selectableFilesValidExtensions = validExtensions; s_selectableFileOpenCallback = callback; s_selectableFileMultiple = multiple; TaskManager::doLater([] { ImGui::OpenPopup("hex.builtin.common.choose_file"_lang); }); }); } void addFooterItems() { if (hex::isProcessElevated()) { ContentRegistry::Interface::addFooterItem([] { ImGui::TextUnformatted(ICON_VS_SHIELD); }); } #if defined(DEBUG) ContentRegistry::Interface::addFooterItem([] { static float framerate = 0; if (ImGui::HasSecondPassed()) { framerate = 1.0F / ImGui::GetIO().DeltaTime; } ImGui::TextFormatted("FPS {0:2}.{1:02}", u32(framerate), u32(framerate * 100) % 100); }); #endif ContentRegistry::Interface::addFooterItem([] { auto taskCount = TaskManager::getRunningTaskCount(); if (taskCount > 0) { const auto &tasks = TaskManager::getRunningTasks(); auto frontTask = tasks.front(); auto progress = frontTask->getMaxValue() == 0 ? 1 : float(frontTask->getValue()) / frontTask->getMaxValue(); ImHexApi::System::setTaskBarProgress(ImHexApi::System::TaskProgressState::Progress, ImHexApi::System::TaskProgressType::Normal, progress * 100); auto widgetStart = ImGui::GetCursorPos(); ImGui::TextSpinner(hex::format("({})", taskCount).c_str()); ImGui::SameLine(); ImGui::SmallProgressBar(progress, (ImGui::GetCurrentWindow()->MenuBarHeight() - 10_scaled) / 2.0); ImGui::SameLine(); auto widgetEnd = ImGui::GetCursorPos(); ImGui::SetCursorPos(widgetStart); ImGui::InvisibleButton("FrontTask", ImVec2(widgetEnd.x - widgetStart.x, ImGui::GetCurrentWindow()->MenuBarHeight())); ImGui::SetCursorPos(widgetEnd); ImGui::InfoTooltip(LangEntry(frontTask->getUnlocalizedName()).get().c_str()); if (ImGui::BeginPopupContextItem("FrontTask", ImGuiPopupFlags_MouseButtonLeft)) { for (const auto &task : tasks) { if (task->isBackgroundTask()) continue; ImGui::PushID(&task); ImGui::TextFormatted("{}", LangEntry(task->getUnlocalizedName())); ImGui::SameLine(); ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); ImGui::SameLine(); ImGui::SmallProgressBar(frontTask->getMaxValue() == 0 ? 1 : (float(frontTask->getValue()) / frontTask->getMaxValue()), (ImGui::GetTextLineHeightWithSpacing() - 5_scaled) / 2); ImGui::SameLine(); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); if (ImGui::ToolBarButton(ICON_VS_DEBUG_STOP, ImGui::GetStyleColorVec4(ImGuiCol_Text))) task->interrupt(); ImGui::PopStyleVar(); ImGui::PopID(); } ImGui::EndPopup(); } ImGui::SameLine(); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, scaled(ImVec2(1, 2))); if (ImGui::ToolBarButton(ICON_VS_DEBUG_STOP, ImGui::GetStyleColorVec4(ImGuiCol_Text))) frontTask->interrupt(); ImGui::PopStyleVar(); } else { ImHexApi::System::setTaskBarProgress(ImHexApi::System::TaskProgressState::Reset, ImHexApi::System::TaskProgressType::Normal, 0); } }); } void addToolbarItems() { ContentRegistry::Interface::addToolbarItem([] { auto provider = ImHexApi::Provider::get(); bool providerValid = provider != nullptr; bool tasksRunning = TaskManager::getRunningTaskCount() > 0; // Undo ImGui::BeginDisabled(!providerValid || !provider->canUndo()); { if (ImGui::ToolBarButton(ICON_VS_DISCARD, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarBlue))) provider->undo(); } ImGui::EndDisabled(); // Redo ImGui::BeginDisabled(!providerValid || !provider->canRedo()); { if (ImGui::ToolBarButton(ICON_VS_REDO, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarBlue))) provider->redo(); } ImGui::EndDisabled(); ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); ImGui::BeginDisabled(tasksRunning); { // Create new file if (ImGui::ToolBarButton(ICON_VS_FILE, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarGray))) { auto newProvider = hex::ImHexApi::Provider::createProvider("hex.builtin.provider.mem_file", true); if (newProvider != nullptr && !newProvider->open()) hex::ImHexApi::Provider::remove(newProvider); else EventManager::post(newProvider); } // Open file if (ImGui::ToolBarButton(ICON_VS_FOLDER_OPENED, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarBrown))) EventManager::post("Open File"); } ImGui::EndDisabled(); ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); // Save file ImGui::BeginDisabled(!providerValid || !provider->isWritable() || !provider->isSavable()); { if (ImGui::ToolBarButton(ICON_VS_SAVE, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarBlue))) provider->save(); } ImGui::EndDisabled(); // Save file as ImGui::BeginDisabled(!providerValid || !provider->isSavable()); { if (ImGui::ToolBarButton(ICON_VS_SAVE_AS, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarBlue))) fs::openFileBrowser(fs::DialogMode::Save, {}, [&provider](auto path) { provider->saveAs(path); }); } ImGui::EndDisabled(); ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); // Create bookmark ImGui::BeginDisabled(!providerValid || !provider->isReadable() || !ImHexApi::HexEditor::isSelectionValid()); { if (ImGui::ToolBarButton(ICON_VS_BOOKMARK, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen))) { auto region = ImHexApi::HexEditor::getSelection(); if (region.has_value()) ImHexApi::Bookmarks::add(region->getStartAddress(), region->getSize(), {}, {}); } } ImGui::EndDisabled(); ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); ImGui::Spacing(); ImGui::Spacing(); ImGui::Spacing(); // Provider switcher ImGui::BeginDisabled(!providerValid || tasksRunning); { auto &providers = ImHexApi::Provider::getProviders(); ImGui::PushStyleColor(ImGuiCol_TabActive, ImGui::GetColorU32(ImGuiCol_MenuBarBg)); ImGui::PushStyleColor(ImGuiCol_TabUnfocusedActive, ImGui::GetColorU32(ImGuiCol_MenuBarBg)); auto providerSelectorVisible = ImGui::BeginTabBar("provider_switcher", ImGuiTabBarFlags_FittingPolicyScroll | ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_AutoSelectNewTabs); ImGui::PopStyleColor(2); if (providerSelectorVisible) { for (size_t i = 0; i < providers.size(); i++) { auto &tabProvider = providers[i]; bool open = true; ImGui::PushID(tabProvider); if (ImGui::BeginTabItem(tabProvider->getName().c_str(), &open, tabProvider->isDirty() ? ImGuiTabItemFlags_UnsavedDocument : ImGuiTabItemFlags_None)) { ImHexApi::Provider::setCurrentProvider(i); ImGui::EndTabItem(); } ImGui::PopID(); if (!open) { ImHexApi::Provider::remove(providers[i]); break; } } ImGui::EndTabBar(); } } ImGui::EndDisabled(); }); } void handleBorderlessWindowMode() { // Intel's OpenGL driver has weird bugs that cause the drawn window to be offset to the bottom right. // This can be fixed by either using Mesa3D's OpenGL Software renderer or by simply disabling it. // If you want to try if it works anyways on your GPU, set the hex.builtin.setting.interface.force_borderless_window_mode setting to 1 if (ImHexApi::System::isBorderlessWindowModeEnabled()) { bool isIntelGPU = hex::containsIgnoreCase(ImHexApi::System::getGPUVendor(), "Intel"); ImHexApi::System::impl::setBorderlessWindowMode(!isIntelGPU); if (isIntelGPU) log::warn("Intel GPU detected! Intel's OpenGL driver has bugs that can cause issues when using ImHex. If you experience any rendering bugs, please try the Mesa3D Software Renderer"); } } }