Skip to content

Commit 253fd4a

Browse files
committed
[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
1 parent 40f7d40 commit 253fd4a

File tree

5 files changed

+41
-24
lines changed

5 files changed

+41
-24
lines changed

plugins/warp/ui/matched.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ WarpMatchedWidget::WarpMatchedWidget(BinaryViewRef current)
4747

4848
Update();
4949

50-
connect(m_tableWidget->GetTableView(), &QTableView::clicked, this, [this](const QModelIndex& index) {
50+
connect(m_tableWidget->GetTableView(), &QTableView::doubleClicked, this, [this](const QModelIndex& index) {
5151
if (m_current == nullptr)
5252
return;
5353
if (!index.isValid())
@@ -60,6 +60,7 @@ WarpMatchedWidget::WarpMatchedWidget(BinaryViewRef current)
6060
return;
6161
// Navigate to the address in the view, so the user feels like they are doing something.
6262
auto currentView = m_current->GetCurrentView();
63+
// TODO: Navigate unconditionally steals focus, need to figure out how to prevent the loss of focus.
6364
m_current->Navigate(currentView, selectedItem.value());
6465
});
6566
}

plugins/warp/ui/plugin.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -174,23 +174,22 @@ WarpSidebarWidget::WarpSidebarWidget(BinaryViewRef data) : SidebarWidget("WARP")
174174
// Do a full update if analysis has been done, otherwise we may persist old data and not have new data.
175175
m_analysisEvent = new AnalysisCompletionEvent(m_data, [this]() { ExecuteOnMainThread([this]() { Update(); }); });
176176

177-
const std::shared_ptr<WarpFetcher> fetcher = WarpFetcher::Global();
178-
fetcher->AddCompletionCallback([this]() {
179-
Update();
177+
m_fetcher = WarpFetcher::Global();
178+
m_callbackId = m_fetcher->AddCompletionCallback([this]() {
179+
ExecuteOnMainThread([this]() { Update(); });
180180
return KeepCallback;
181181
});
182182

183183
// NOTE: This fetcher is shared with the fetch dialog that is constructed on initialization of this plugin.
184-
m_currentFunctionWidget->SetFetcher(fetcher);
184+
m_currentFunctionWidget->SetFetcher(m_fetcher);
185185
}
186186

187187
WarpSidebarWidget::~WarpSidebarWidget()
188188
{
189189
m_analysisEvent->Cancel();
190+
m_fetcher->RemoveCompletionCallback(m_callbackId);
190191
}
191192

192-
void WarpSidebarWidget::focus() {}
193-
194193
void WarpSidebarWidget::Update()
195194
{
196195
m_currentFunctionWidget->UpdateMatches();

plugins/warp/ui/plugin.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ class WarpSidebarWidget : public SidebarWidget
1818
QAction* m_matcherAction;
1919
bool isMatcherRunning = false;
2020

21+
std::shared_ptr<WarpFetcher> m_fetcher;
22+
WarpFetcher::CallbackId m_callbackId;
23+
2124
WarpCurrentFunctionWidget* m_currentFunctionWidget;
2225
WarpMatchedWidget* m_matchedWidget;
2326
WarpContainersPane* m_containerWidget;
@@ -29,8 +32,6 @@ class WarpSidebarWidget : public SidebarWidget
2932

3033
QWidget* headerWidget() override { return m_headerWidget; }
3134

32-
void focus() override;
33-
3435
void Update();
3536

3637
void setMatcherActionIcon(bool running);

plugins/warp/ui/shared/fetcher.cpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,20 @@ std::vector<FunctionRef> WarpFetcher::FlushPendingFunctions()
2424

2525
void WarpFetcher::ExecuteCompletionCallback()
2626
{
27-
BinaryNinja::ExecuteOnMainThread([this]() {
28-
// TODO: Holding the mutex here is dangerous!
27+
std::vector<std::pair<CallbackId, CompletionCallback>> callbacks;
28+
{
2929
std::lock_guard<std::mutex> lock(m_requestMutex);
30-
m_completionCallbacks.erase(
31-
std::ranges::remove_if(m_completionCallbacks, [](const auto& cb) { return cb() == RemoveCallback; })
32-
.begin(),
33-
m_completionCallbacks.end());
34-
});
30+
callbacks.insert(callbacks.end(), m_completionCallbacks.begin(), m_completionCallbacks.end());
31+
}
32+
33+
std::vector<CallbackId> toRemove = {};
34+
for (auto& [id, cb] : callbacks)
35+
if (cb() == RemoveCallback)
36+
toRemove.push_back(id);
37+
38+
std::lock_guard<std::mutex> lock(m_requestMutex);
39+
for (auto id : toRemove)
40+
m_completionCallbacks.erase(id);
3541
}
3642

3743
std::shared_ptr<WarpFetcher> WarpFetcher::Global()

plugins/warp/ui/shared/fetcher.h

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,29 @@ class WarpFetcher
2424
std::mutex m_requestMutex;
2525
std::vector<FunctionRef> m_pendingRequests;
2626
std::unordered_set<Warp::FunctionGUID> m_processedGuids;
27-
28-
// List of callbacks to call when done fetching data, assume that others are using this as well.
29-
std::vector<std::function<WarpFetchCompletionStatus()>> m_completionCallbacks;
30-
3127
public:
28+
using CallbackId = uint64_t;
29+
using CompletionCallback = std::function<WarpFetchCompletionStatus()>;
30+
3231
explicit WarpFetcher();
3332

3433
// The global fetcher instance, this is used for the fetch dialog and the sidebar.
3534
static std::shared_ptr<WarpFetcher> Global();
3635

3736
std::atomic<bool> m_requestInProgress = false;
3837

39-
void AddCompletionCallback(std::function<WarpFetchCompletionStatus()> cb)
38+
[[nodiscard]] CallbackId AddCompletionCallback(CompletionCallback cb)
4039
{
4140
std::lock_guard<std::mutex> lock(m_requestMutex);
42-
m_completionCallbacks.push_back(std::move(cb));
41+
const CallbackId id = m_nextCallbackId++;
42+
m_completionCallbacks.emplace(id, std::move(cb));
43+
return id;
44+
}
45+
46+
void RemoveCompletionCallback(CallbackId id)
47+
{
48+
std::lock_guard<std::mutex> lock(m_requestMutex);
49+
m_completionCallbacks.erase(id);
4350
}
4451

4552
void AddPendingFunction(const FunctionRef& func);
@@ -49,7 +56,10 @@ class WarpFetcher
4956
void ClearProcessed();
5057

5158
private:
52-
std::vector<FunctionRef> FlushPendingFunctions();
53-
59+
// List of callbacks to call when done fetching data, assume that others are using this as well.
60+
std::atomic<CallbackId> m_nextCallbackId = 1;
61+
std::unordered_map<CallbackId, CompletionCallback> m_completionCallbacks;
5462
void ExecuteCompletionCallback();
63+
64+
std::vector<FunctionRef> FlushPendingFunctions();
5565
};

0 commit comments

Comments
 (0)