From 891cc42f08256ed28d5b45c01b178131d2e33a31 Mon Sep 17 00:00:00 2001 From: WerWolv Date: Thu, 16 Dec 2021 23:48:52 +0100 Subject: [PATCH] ui: Added global running tasks progress bar --- .../ImGui/include/imgui_imhex_extensions.h | 2 ++ .../ImGui/source/imgui_imhex_extensions.cpp | 23 +++++++++++++ .../content/views/view_information.hpp | 1 - .../builtin/source/content/tools_entries.cpp | 15 +++++++-- plugins/builtin/source/content/ui_items.cpp | 33 +++++++++++++++++++ .../content/views/view_disassembler.cpp | 7 +++- .../source/content/views/view_information.cpp | 24 +++++++------- .../content/views/view_pattern_editor.cpp | 3 ++ .../source/content/views/view_strings.cpp | 9 +++-- .../source/content/views/view_yara.cpp | 7 ++++ plugins/libimhex/CMakeLists.txt | 1 + .../libimhex/include/hex/api/imhex_api.hpp | 8 ++++- plugins/libimhex/include/hex/api/task.hpp | 28 ++++++++++++++++ .../include/hex/helpers/shared_data.hpp | 5 +++ plugins/libimhex/source/api/imhex_api.cpp | 9 ++--- plugins/libimhex/source/api/task.cpp | 32 ++++++++++++++++++ .../libimhex/source/helpers/shared_data.cpp | 3 ++ source/window/window.cpp | 12 ++++--- 18 files changed, 193 insertions(+), 29 deletions(-) create mode 100644 plugins/libimhex/include/hex/api/task.hpp create mode 100644 plugins/libimhex/source/api/task.cpp diff --git a/external/ImGui/include/imgui_imhex_extensions.h b/external/ImGui/include/imgui_imhex_extensions.h index eab0997c7..e8a9ffd18 100644 --- a/external/ImGui/include/imgui_imhex_extensions.h +++ b/external/ImGui/include/imgui_imhex_extensions.h @@ -86,4 +86,6 @@ namespace ImGui { void StyleCustomColorsDark(); void StyleCustomColorsLight(); void StyleCustomColorsClassic(); + + void SmallProgressBar(float fraction, float yOffset = 0.0F); } \ No newline at end of file diff --git a/external/ImGui/source/imgui_imhex_extensions.cpp b/external/ImGui/source/imgui_imhex_extensions.cpp index f66354796..cbb5fe5e4 100644 --- a/external/ImGui/source/imgui_imhex_extensions.cpp +++ b/external/ImGui/source/imgui_imhex_extensions.cpp @@ -492,4 +492,27 @@ namespace ImGui { return pressed; } + void SmallProgressBar(float fraction, float yOffset) { + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + ImVec2 pos = window->DC.CursorPos + ImVec2(0, yOffset); + ImVec2 size = CalcItemSize(ImVec2(100, 5), 100, g.FontSize + style.FramePadding.y * 2.0f); + ImRect bb(pos, pos + size); + ItemSize(size, 0); + if (!ItemAdd(bb, 0)) + return; + + // Render + fraction = ImSaturate(fraction); + RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); + bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize)); + const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y); + RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), 0.0f, fraction, style.FrameRounding); + } + } \ No newline at end of file diff --git a/plugins/builtin/include/content/views/view_information.hpp b/plugins/builtin/include/content/views/view_information.hpp index 0122295a8..364a04c7e 100644 --- a/plugins/builtin/include/content/views/view_information.hpp +++ b/plugins/builtin/include/content/views/view_information.hpp @@ -31,7 +31,6 @@ namespace hex::plugin::builtin { std::array m_valueCounts = { 0 }; bool m_analyzing = false; - std::atomic m_bytesAnalyzed; std::pair m_analyzedRegion = { 0, 0 }; diff --git a/plugins/builtin/source/content/tools_entries.cpp b/plugins/builtin/source/content/tools_entries.cpp index 697d25fd3..3090c3792 100644 --- a/plugins/builtin/source/content/tools_entries.cpp +++ b/plugins/builtin/source/content/tools_entries.cpp @@ -741,9 +741,12 @@ namespace hex::plugin::builtin { } size_t fileSize = file.getSize(); + + auto task = ImHexApi::Tasks::createTask("hex.builtin.tools.file_tools.shredder.shredding", fileSize); for (const auto &pattern : overwritePattern) { for (u64 offset = 0; offset < fileSize; offset += 3) { file.write(pattern.data(), std::min(pattern.size(), fileSize - offset)); + task.update(offset); } file.flush(); @@ -851,8 +854,11 @@ namespace hex::plugin::builtin { return; } + auto task = ImHexApi::Tasks::createTask("hex.builtin.tools.file_tools.splitter.splitting", file.getSize()); u32 index = 1; for (u64 offset = 0; offset < file.getSize(); offset += splitSize) { + task.update(offset); + File partFile(baseOutputPath + hex::format(".{:05}", index), File::Mode::Create); if (!partFile.isValid()) { @@ -978,9 +984,14 @@ namespace hex::plugin::builtin { return; } - for (const auto &file : files) { - File input(file, File::Mode::Read); + auto task = ImHexApi::Tasks::createTask("hex.builtin.tools.file_tools.combiner.combining", files.size()); + u64 fileIndex = 0; + for (const auto &file : files) { + task.update(fileIndex); + fileIndex++; + + File input(file, File::Mode::Read); if (!input.isValid()) { View::showErrorPopup(hex::format("hex.builtin.tools.file_tools.combiner.open_input"_lang, std::filesystem::path(file).filename().string())); return; diff --git a/plugins/builtin/source/content/ui_items.cpp b/plugins/builtin/source/content/ui_items.cpp index 252913673..b06def0e3 100644 --- a/plugins/builtin/source/content/ui_items.cpp +++ b/plugins/builtin/source/content/ui_items.cpp @@ -9,6 +9,8 @@ #include #include +#include + namespace hex::plugin::builtin { void addFooterItems() { @@ -20,12 +22,43 @@ namespace hex::plugin::builtin { } ContentRegistry::Interface::addFooterItem([] { + static float framerate = 0; if (ImGui::HasSecondPassed()) { framerate = 1.0F / ImGui::GetIO().DeltaTime; } ImGui::TextUnformatted(hex::format("FPS {0:2}.{1:02}", u32(framerate), u32(framerate * 100) % 100).c_str()); + + }); + + ContentRegistry::Interface::addFooterItem([] { + size_t taskCount = 0; + double taskProgress = 0.0; + std::string taskName; + + { + std::scoped_lock lock(SharedData::tasksMutex); + + taskCount = SharedData::runningTasks.size(); + if (taskCount > 0) { + taskProgress = SharedData::runningTasks.front()->getProgress(); + taskName = SharedData::runningTasks.front()->getName(); + } + } + + if (taskCount > 0) { + if (taskCount > 0) + ImGui::TextSpinner(hex::format("({})", taskCount).c_str()); + else + ImGui::TextSpinner(""); + + ImGui::SameLine(); + + ImGui::SmallProgressBar(taskProgress, (ImGui::GetCurrentWindow()->MenuBarHeight() - 10 * SharedData::globalScale) / 2.0); + ImGui::InfoTooltip(taskName.c_str()); + } + }); } diff --git a/plugins/builtin/source/content/views/view_disassembler.cpp b/plugins/builtin/source/content/views/view_disassembler.cpp index 8c10f53ab..4b466d4a6 100644 --- a/plugins/builtin/source/content/views/view_disassembler.cpp +++ b/plugins/builtin/source/content/views/view_disassembler.cpp @@ -66,7 +66,12 @@ namespace hex::plugin::builtin { auto provider = ImHexApi::Provider::get(); std::vector buffer(2048, 0x00); - for (u64 address = 0; address < (this->m_codeRegion[1] - this->m_codeRegion[0] + 1); address += 2048) { + size_t size = (this->m_codeRegion[1] - this->m_codeRegion[0] + 1); + + auto task = ImHexApi::Tasks::createTask("hex.builtin.view.disassembler.disassembling", size); + for (u64 address = 0; address < size; address += 2048) { + task.update(address); + size_t bufferSize = std::min(u64(2048), (this->m_codeRegion[1] - this->m_codeRegion[0] + 1) - address); provider->read(this->m_codeRegion[0] + address, buffer.data(), bufferSize); diff --git a/plugins/builtin/source/content/views/view_information.cpp b/plugins/builtin/source/content/views/view_information.cpp index 0a1d90852..4cb26b0e1 100644 --- a/plugins/builtin/source/content/views/view_information.cpp +++ b/plugins/builtin/source/content/views/view_information.cpp @@ -71,8 +71,19 @@ namespace hex::plugin::builtin { std::thread([this]{ auto provider = ImHexApi::Provider::get(); + auto task = ImHexApi::Tasks::createTask("hex.builtin.view.information.analyzing", provider->getSize()); + this->m_analyzedRegion = { provider->getBaseAddress(), provider->getBaseAddress() + provider->getSize() }; + { + magic::compile(); + + this->m_fileDescription = magic::getDescription(provider); + this->m_mimeType = magic::getMIMEType(provider); + } + + this->m_dataValid = true; + { this->m_blockSize = std::max(std::ceil(provider->getSize() / 2048.0F), 256); std::vector buffer(this->m_blockSize, 0x00); @@ -90,22 +101,13 @@ namespace hex::plugin::builtin { } this->m_blockEntropy.push_back(calculateEntropy(blockValueCounts, this->m_blockSize)); - - this->m_bytesAnalyzed = i; + task.update(i); } this->m_averageEntropy = calculateEntropy(this->m_valueCounts, provider->getSize()); this->m_highestBlockEntropy = *std::max_element(this->m_blockEntropy.begin(), this->m_blockEntropy.end()); } - { - magic::compile(); - - this->m_fileDescription = magic::getDescription(provider); - this->m_mimeType = magic::getMIMEType(provider); - this->m_dataValid = true; - } - this->m_analyzing = false; }).detach(); } @@ -127,10 +129,8 @@ namespace hex::plugin::builtin { if (this->m_analyzing) { ImGui::TextSpinner("hex.builtin.view.information.analyzing"_lang); - ImGui::ProgressBar(this->m_bytesAnalyzed / static_cast(provider->getSize())); } else { ImGui::NewLine(); - ImGui::NewLine(); } ImGui::NewLine(); diff --git a/plugins/builtin/source/content/views/view_pattern_editor.cpp b/plugins/builtin/source/content/views/view_pattern_editor.cpp index 1b3b2c762..098db04eb 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -384,6 +384,8 @@ namespace hex::plugin::builtin { } void ViewPatternEditor::drawAlwaysVisible() { + + ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5F, 0.5F)); if (ImGui::BeginPopupModal("hex.builtin.view.pattern_editor.accept_pattern"_lang, &this->m_acceptPatternWindowOpen, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::TextWrapped("%s", static_cast("hex.builtin.view.pattern_editor.accept_pattern.desc"_lang)); @@ -423,6 +425,7 @@ namespace hex::plugin::builtin { } bool opened = true; + ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5F, 0.5F)); if (ImGui::BeginPopupModal("hex.builtin.view.pattern_editor.menu.file.load_pattern"_lang, &opened, ImGuiWindowFlags_AlwaysAutoResize)) { if (ImGui::BeginListBox("##patterns", ImVec2(-FLT_MIN, 0))) { diff --git a/plugins/builtin/source/content/views/view_strings.cpp b/plugins/builtin/source/content/views/view_strings.cpp index e2d51b795..6e8ec748f 100644 --- a/plugins/builtin/source/content/views/view_strings.cpp +++ b/plugins/builtin/source/content/views/view_strings.cpp @@ -65,16 +65,19 @@ namespace hex::plugin::builtin { std::thread([this] { auto provider = ImHexApi::Provider::get(); + auto task = ImHexApi::Tasks::createTask("hex.builtin.view.strings.searching", provider->getActualSize()); std::vector buffer(1024, 0x00); u32 foundCharacters = 0; - for (u64 offset = 0; offset < provider->getSize(); offset += buffer.size()) { - size_t readSize = std::min(u64(buffer.size()), provider->getSize() - offset); + for (u64 offset = 0; offset < provider->getActualSize(); offset += buffer.size()) { + task.update(offset); + + size_t readSize = std::min(u64(buffer.size()), provider->getActualSize() - offset); provider->read(offset + provider->getBaseAddress(), buffer.data(), readSize); for (u32 i = 0; i < readSize; i++) { - if (buffer[i] >= ' ' && buffer[i] <= '~' && offset < provider->getSize() - 1) + if (buffer[i] >= ' ' && buffer[i] <= '~' && offset < provider->getActualSize() - 1) foundCharacters++; else { if (foundCharacters >= this->m_minimumLength) { diff --git a/plugins/builtin/source/content/views/view_yara.cpp b/plugins/builtin/source/content/views/view_yara.cpp index 9d71ef63d..ac5d36d03 100644 --- a/plugins/builtin/source/content/views/view_yara.cpp +++ b/plugins/builtin/source/content/views/view_yara.cpp @@ -144,6 +144,10 @@ namespace hex::plugin::builtin { this->m_matching = true; std::thread([this] { + if (!ImHexApi::Provider::isValid()) return; + + auto provider = ImHexApi::Provider::get(); + auto task = ImHexApi::Tasks::createTask("hex.builtin.view.yara.matching", provider->getActualSize()); YR_COMPILER *compiler = nullptr; yr_compiler_create(&compiler); @@ -192,11 +196,13 @@ namespace hex::plugin::builtin { YR_MEMORY_BLOCK_ITERATOR iterator; struct ScanContext { + Task *task; std::vector buffer; YR_MEMORY_BLOCK currBlock; }; ScanContext context; + context.task = &task; context.currBlock.base = 0; context.currBlock.fetch_data = [](auto *block) -> const u8* { auto &context = *static_cast(block->context); @@ -237,6 +243,7 @@ namespace hex::plugin::builtin { context.currBlock.base = address; context.currBlock.size = ImHexApi::Provider::get()->getActualSize() - address; context.currBlock.context = &context; + context.task->update(address); if (context.currBlock.size == 0) return nullptr; diff --git a/plugins/libimhex/CMakeLists.txt b/plugins/libimhex/CMakeLists.txt index 075b5c293..816b71d90 100644 --- a/plugins/libimhex/CMakeLists.txt +++ b/plugins/libimhex/CMakeLists.txt @@ -92,6 +92,7 @@ set(LIBIMHEX_SOURCES source/api/event.cpp source/api/imhex_api.cpp source/api/content_registry.cpp + source/api/task.cpp source/data_processor/attribute.cpp source/data_processor/link.cpp diff --git a/plugins/libimhex/include/hex/api/imhex_api.hpp b/plugins/libimhex/include/hex/api/imhex_api.hpp index 347bf9c19..d763be2fb 100644 --- a/plugins/libimhex/include/hex/api/imhex_api.hpp +++ b/plugins/libimhex/include/hex/api/imhex_api.hpp @@ -7,6 +7,7 @@ #include #include +#include namespace hex { @@ -15,7 +16,6 @@ namespace hex { namespace ImHexApi { namespace Common { - void sayHello(); void closeImHex(bool noQuestions = false); void restartImHex(); @@ -55,6 +55,12 @@ namespace hex { }; + namespace Tasks { + + Task createTask(const std::string &unlocalizedName, u64 maxValue); + + } + }; } diff --git a/plugins/libimhex/include/hex/api/task.hpp b/plugins/libimhex/include/hex/api/task.hpp new file mode 100644 index 000000000..89d54121f --- /dev/null +++ b/plugins/libimhex/include/hex/api/task.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include + +namespace hex { + + class Task { + public: + Task(const std::string& unlocalizedName, u64 maxValue); + ~Task(); + + void update(u64 currValue); + void finish(); + + [[nodiscard]] + double getProgress() const; + + [[nodiscard]] + const std::string& getName() const; + + private: + std::string m_name; + u64 m_maxValue, m_currValue; + }; + +} \ No newline at end of file diff --git a/plugins/libimhex/include/hex/helpers/shared_data.hpp b/plugins/libimhex/include/hex/helpers/shared_data.hpp index a81a27586..501dea486 100644 --- a/plugins/libimhex/include/hex/helpers/shared_data.hpp +++ b/plugins/libimhex/include/hex/helpers/shared_data.hpp @@ -5,10 +5,12 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -77,6 +79,9 @@ namespace hex { static std::vector footerItems; static std::vector toolbarItems; + static std::mutex tasksMutex; + static std::list runningTasks; + static std::vector providerNames; static std::vector dataProcessorNodes; diff --git a/plugins/libimhex/source/api/imhex_api.cpp b/plugins/libimhex/source/api/imhex_api.cpp index 798c16433..1735fff5c 100644 --- a/plugins/libimhex/source/api/imhex_api.cpp +++ b/plugins/libimhex/source/api/imhex_api.cpp @@ -9,10 +9,6 @@ namespace hex { - void ImHexApi::Common::sayHello() { - log::warn("Hello!"); - } - void ImHexApi::Common::closeImHex(bool noQuestions) { EventManager::post(noQuestions); } @@ -85,4 +81,9 @@ namespace hex { delete provider; } + + Task ImHexApi::Tasks::createTask(const std::string &unlocalizedName, u64 maxValue) { + return Task(unlocalizedName, maxValue); + } + } \ No newline at end of file diff --git a/plugins/libimhex/source/api/task.cpp b/plugins/libimhex/source/api/task.cpp new file mode 100644 index 000000000..5f9e0c7c3 --- /dev/null +++ b/plugins/libimhex/source/api/task.cpp @@ -0,0 +1,32 @@ +#include + +#include + +namespace hex { + + Task::Task(const std::string& unlocalizedName, u64 maxValue) : m_name(LangEntry(unlocalizedName)), m_maxValue(maxValue), m_currValue(0) { + SharedData::runningTasks.push_back(this); + } + + Task::~Task() { + this->finish(); + } + + void Task::finish() { + SharedData::runningTasks.remove(this); + } + + void Task::update(u64 currValue) { + if (this->m_currValue < this->m_maxValue) + this->m_currValue = currValue; + } + + double Task::getProgress() const { + return static_cast(this->m_currValue) / static_cast(this->m_maxValue); + } + + const std::string& Task::getName() const { + return this->m_name; + } + +} \ No newline at end of file diff --git a/plugins/libimhex/source/helpers/shared_data.cpp b/plugins/libimhex/source/helpers/shared_data.cpp index 7ba384235..4472dccb3 100644 --- a/plugins/libimhex/source/helpers/shared_data.cpp +++ b/plugins/libimhex/source/helpers/shared_data.cpp @@ -29,6 +29,9 @@ namespace hex { std::vector SharedData::footerItems; std::vector SharedData::toolbarItems; + std::mutex SharedData::tasksMutex; + std::list SharedData::runningTasks; + std::vector SharedData::providerNames; std::vector SharedData::dataProcessorNodes; diff --git a/source/window/window.cpp b/source/window/window.cpp index 654358327..5d36b04cc 100644 --- a/source/window/window.cpp +++ b/source/window/window.cpp @@ -279,15 +279,17 @@ namespace hex { } void Window::loop() { - double timeout; this->m_lastFrameTime = glfwGetTime(); while (!glfwWindowShouldClose(this->m_window)) { - if (!glfwGetWindowAttrib(this->m_window, GLFW_VISIBLE) || glfwGetWindowAttrib(this->m_window, GLFW_ICONIFIED)) + if (!glfwGetWindowAttrib(this->m_window, GLFW_VISIBLE) || glfwGetWindowAttrib(this->m_window, GLFW_ICONIFIED)) { glfwWaitEvents(); - else - timeout = (1.0 / 5.0) - (glfwGetTime() - this->m_lastFrameTime); + + } else { + double timeout = (1.0 / 5.0) - (glfwGetTime() - this->m_lastFrameTime); timeout = timeout > 0 ? timeout : 0; - glfwWaitEventsTimeout(ImGui::IsPopupOpen(ImGuiID(0), ImGuiPopupFlags_AnyPopupId) ? 0 : timeout); + glfwWaitEventsTimeout(ImGui::IsPopupOpen(ImGuiID(0), ImGuiPopupFlags_AnyPopupId) || !SharedData::runningTasks.empty() ? 0 : timeout); + } + this->frameBegin(); this->frame();