summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
authorMason Reed <mason@vector35.com>2025-10-12 22:48:41 -0400
committerMason Reed <mason@vector35.com>2025-10-12 22:50:20 -0400
commit253fd4ad2d8095cc3c640c99d75ab3fd1cb7282e (patch)
tree2bb2549d4aa29971143ec013df9cc6971abc6be2 /plugins
parent40f7d40e394657575001a8146d8233f1fc4356fd (diff)
[WARP] Fix misc sidebar navigation / focus event bugs
- Fixed crash related to pending fetch callback for deleted view frame - Fixed focus not being kept for tables in the sidebar, as well as focus in general - Made matched function list navigation require a double click, to fix accidental navigation to other functions
Diffstat (limited to 'plugins')
-rw-r--r--plugins/warp/ui/matched.cpp3
-rw-r--r--plugins/warp/ui/plugin.cpp11
-rw-r--r--plugins/warp/ui/plugin.h5
-rw-r--r--plugins/warp/ui/shared/fetcher.cpp20
-rw-r--r--plugins/warp/ui/shared/fetcher.h26
5 files changed, 41 insertions, 24 deletions
diff --git a/plugins/warp/ui/matched.cpp b/plugins/warp/ui/matched.cpp
index 7112e74e..786b9d26 100644
--- a/plugins/warp/ui/matched.cpp
+++ b/plugins/warp/ui/matched.cpp
@@ -47,7 +47,7 @@ WarpMatchedWidget::WarpMatchedWidget(BinaryViewRef current)
Update();
- connect(m_tableWidget->GetTableView(), &QTableView::clicked, this, [this](const QModelIndex& index) {
+ connect(m_tableWidget->GetTableView(), &QTableView::doubleClicked, this, [this](const QModelIndex& index) {
if (m_current == nullptr)
return;
if (!index.isValid())
@@ -60,6 +60,7 @@ WarpMatchedWidget::WarpMatchedWidget(BinaryViewRef current)
return;
// Navigate to the address in the view, so the user feels like they are doing something.
auto currentView = m_current->GetCurrentView();
+ // TODO: Navigate unconditionally steals focus, need to figure out how to prevent the loss of focus.
m_current->Navigate(currentView, selectedItem.value());
});
}
diff --git a/plugins/warp/ui/plugin.cpp b/plugins/warp/ui/plugin.cpp
index e8088d7c..f3c1639b 100644
--- a/plugins/warp/ui/plugin.cpp
+++ b/plugins/warp/ui/plugin.cpp
@@ -174,23 +174,22 @@ WarpSidebarWidget::WarpSidebarWidget(BinaryViewRef data) : SidebarWidget("WARP")
// Do a full update if analysis has been done, otherwise we may persist old data and not have new data.
m_analysisEvent = new AnalysisCompletionEvent(m_data, [this]() { ExecuteOnMainThread([this]() { Update(); }); });
- const std::shared_ptr<WarpFetcher> fetcher = WarpFetcher::Global();
- fetcher->AddCompletionCallback([this]() {
- Update();
+ m_fetcher = WarpFetcher::Global();
+ m_callbackId = m_fetcher->AddCompletionCallback([this]() {
+ ExecuteOnMainThread([this]() { Update(); });
return KeepCallback;
});
// NOTE: This fetcher is shared with the fetch dialog that is constructed on initialization of this plugin.
- m_currentFunctionWidget->SetFetcher(fetcher);
+ m_currentFunctionWidget->SetFetcher(m_fetcher);
}
WarpSidebarWidget::~WarpSidebarWidget()
{
m_analysisEvent->Cancel();
+ m_fetcher->RemoveCompletionCallback(m_callbackId);
}
-void WarpSidebarWidget::focus() {}
-
void WarpSidebarWidget::Update()
{
m_currentFunctionWidget->UpdateMatches();
diff --git a/plugins/warp/ui/plugin.h b/plugins/warp/ui/plugin.h
index 7f406e19..e70b7afa 100644
--- a/plugins/warp/ui/plugin.h
+++ b/plugins/warp/ui/plugin.h
@@ -18,6 +18,9 @@ class WarpSidebarWidget : public SidebarWidget
QAction* m_matcherAction;
bool isMatcherRunning = false;
+ std::shared_ptr<WarpFetcher> m_fetcher;
+ WarpFetcher::CallbackId m_callbackId;
+
WarpCurrentFunctionWidget* m_currentFunctionWidget;
WarpMatchedWidget* m_matchedWidget;
WarpContainersPane* m_containerWidget;
@@ -29,8 +32,6 @@ public:
QWidget* headerWidget() override { return m_headerWidget; }
- void focus() override;
-
void Update();
void setMatcherActionIcon(bool running);
diff --git a/plugins/warp/ui/shared/fetcher.cpp b/plugins/warp/ui/shared/fetcher.cpp
index 1a262878..403d7cdb 100644
--- a/plugins/warp/ui/shared/fetcher.cpp
+++ b/plugins/warp/ui/shared/fetcher.cpp
@@ -24,14 +24,20 @@ std::vector<FunctionRef> WarpFetcher::FlushPendingFunctions()
void WarpFetcher::ExecuteCompletionCallback()
{
- BinaryNinja::ExecuteOnMainThread([this]() {
- // TODO: Holding the mutex here is dangerous!
+ std::vector<std::pair<CallbackId, CompletionCallback>> callbacks;
+ {
std::lock_guard<std::mutex> lock(m_requestMutex);
- m_completionCallbacks.erase(
- std::ranges::remove_if(m_completionCallbacks, [](const auto& cb) { return cb() == RemoveCallback; })
- .begin(),
- m_completionCallbacks.end());
- });
+ callbacks.insert(callbacks.end(), m_completionCallbacks.begin(), m_completionCallbacks.end());
+ }
+
+ std::vector<CallbackId> toRemove = {};
+ for (auto& [id, cb] : callbacks)
+ if (cb() == RemoveCallback)
+ toRemove.push_back(id);
+
+ std::lock_guard<std::mutex> lock(m_requestMutex);
+ for (auto id : toRemove)
+ m_completionCallbacks.erase(id);
}
std::shared_ptr<WarpFetcher> WarpFetcher::Global()
diff --git a/plugins/warp/ui/shared/fetcher.h b/plugins/warp/ui/shared/fetcher.h
index 52d0a0e2..3464ecd4 100644
--- a/plugins/warp/ui/shared/fetcher.h
+++ b/plugins/warp/ui/shared/fetcher.h
@@ -24,11 +24,10 @@ class WarpFetcher
std::mutex m_requestMutex;
std::vector<FunctionRef> m_pendingRequests;
std::unordered_set<Warp::FunctionGUID> m_processedGuids;
-
- // List of callbacks to call when done fetching data, assume that others are using this as well.
- std::vector<std::function<WarpFetchCompletionStatus()>> m_completionCallbacks;
-
public:
+ using CallbackId = uint64_t;
+ using CompletionCallback = std::function<WarpFetchCompletionStatus()>;
+
explicit WarpFetcher();
// The global fetcher instance, this is used for the fetch dialog and the sidebar.
@@ -36,10 +35,18 @@ public:
std::atomic<bool> m_requestInProgress = false;
- void AddCompletionCallback(std::function<WarpFetchCompletionStatus()> cb)
+ [[nodiscard]] CallbackId AddCompletionCallback(CompletionCallback cb)
{
std::lock_guard<std::mutex> lock(m_requestMutex);
- m_completionCallbacks.push_back(std::move(cb));
+ const CallbackId id = m_nextCallbackId++;
+ m_completionCallbacks.emplace(id, std::move(cb));
+ return id;
+ }
+
+ void RemoveCompletionCallback(CallbackId id)
+ {
+ std::lock_guard<std::mutex> lock(m_requestMutex);
+ m_completionCallbacks.erase(id);
}
void AddPendingFunction(const FunctionRef& func);
@@ -49,7 +56,10 @@ public:
void ClearProcessed();
private:
- std::vector<FunctionRef> FlushPendingFunctions();
-
+ // List of callbacks to call when done fetching data, assume that others are using this as well.
+ std::atomic<CallbackId> m_nextCallbackId = 1;
+ std::unordered_map<CallbackId, CompletionCallback> m_completionCallbacks;
void ExecuteCompletionCallback();
+
+ std::vector<FunctionRef> FlushPendingFunctions();
};