diff --git a/api/debuggerapi.h b/api/debuggerapi.h index a22972ab..0ff27f12 100644 --- a/api/debuggerapi.h +++ b/api/debuggerapi.h @@ -890,6 +890,14 @@ namespace BinaryNinjaDebuggerAPI { bool IsFirstConnectToDebugServer(); bool IsFirstAttach(); + bool ShouldShowAdapterSettingsForLaunch(); + bool ShouldShowAdapterSettingsForAttach(); + bool ShouldShowAdapterSettingsForConnect(); + bool ShouldShowAdapterSettingsForConnectToDebugServer(); + + bool ShowAdapterSettingsNextTime(); + void SetShowAdapterSettingsNextTime(bool value); + bool IsTTD(); // TTD Memory Analysis Methods diff --git a/api/debuggercontroller.cpp b/api/debuggercontroller.cpp index 3206a3d7..e5201f34 100644 --- a/api/debuggercontroller.cpp +++ b/api/debuggercontroller.cpp @@ -1286,6 +1286,42 @@ bool DebuggerController::IsFirstAttach() } +bool DebuggerController::ShouldShowAdapterSettingsForLaunch() +{ + return BNDebuggerShouldShowAdapterSettingsForLaunch(m_object); +} + + +bool DebuggerController::ShouldShowAdapterSettingsForAttach() +{ + return BNDebuggerShouldShowAdapterSettingsForAttach(m_object); +} + + +bool DebuggerController::ShouldShowAdapterSettingsForConnect() +{ + return BNDebuggerShouldShowAdapterSettingsForConnect(m_object); +} + + +bool DebuggerController::ShouldShowAdapterSettingsForConnectToDebugServer() +{ + return BNDebuggerShouldShowAdapterSettingsForConnectToDebugServer(m_object); +} + + +bool DebuggerController::ShowAdapterSettingsNextTime() +{ + return BNDebuggerShowAdapterSettingsNextTime(m_object); +} + + +void DebuggerController::SetShowAdapterSettingsNextTime(bool value) +{ + BNDebuggerSetShowAdapterSettingsNextTime(m_object, value); +} + + bool DebuggerController::IsTTD() { return BNDebuggerIsTTD(m_object); diff --git a/api/ffi.h b/api/ffi.h index 6c75a5aa..61bfc333 100644 --- a/api/ffi.h +++ b/api/ffi.h @@ -754,6 +754,14 @@ extern "C" DEBUGGER_FFI_API bool BNDebuggerIsFirstConnectToDebugServer(BNDebuggerController* controller); DEBUGGER_FFI_API bool BNDebuggerIsFirstAttach(BNDebuggerController* controller); + DEBUGGER_FFI_API bool BNDebuggerShouldShowAdapterSettingsForLaunch(BNDebuggerController* controller); + DEBUGGER_FFI_API bool BNDebuggerShouldShowAdapterSettingsForAttach(BNDebuggerController* controller); + DEBUGGER_FFI_API bool BNDebuggerShouldShowAdapterSettingsForConnect(BNDebuggerController* controller); + DEBUGGER_FFI_API bool BNDebuggerShouldShowAdapterSettingsForConnectToDebugServer(BNDebuggerController* controller); + + DEBUGGER_FFI_API bool BNDebuggerShowAdapterSettingsNextTime(BNDebuggerController* controller); + DEBUGGER_FFI_API void BNDebuggerSetShowAdapterSettingsNextTime(BNDebuggerController* controller, bool value); + DEBUGGER_FFI_API bool BNDebuggerIsTTD(BNDebuggerController* controller); // TTD Memory Analysis Functions diff --git a/core/debuggercontroller.cpp b/core/debuggercontroller.cpp index e8f4952d..7d79d7bd 100644 --- a/core/debuggercontroller.cpp +++ b/core/debuggercontroller.cpp @@ -369,6 +369,7 @@ DebugStopReason DebuggerController::LaunchAndWaitInternal() } m_firstLaunch = false; + m_lastDebugStartOperation = LaunchStartOperation; DebuggerEvent event; event.type = LaunchEventType; @@ -421,6 +422,7 @@ bool DebuggerController::Attach() DebugStopReason DebuggerController::AttachAndWaitInternal() { m_firstAttach = false; + m_lastDebugStartOperation = AttachStartOperation; DebuggerEvent event; event.type = LaunchEventType; @@ -473,6 +475,7 @@ bool DebuggerController::Connect() DebugStopReason DebuggerController::ConnectAndWaitInternal() { m_firstConnect = false; + m_lastDebugStartOperation = ConnectStartOperation; DebuggerEvent event; event.type = LaunchEventType; @@ -1685,6 +1688,7 @@ DebugStopReason DebuggerController::RestartAndWait(std::chrono::milliseconds tim bool DebuggerController::ConnectToDebugServer() { m_firstConnectToDebugServer = false; + m_lastDebugStartOperation = ConnectToDebugServerStartOperation; if (m_state->IsConnectedToDebugServer()) return true; @@ -3789,6 +3793,42 @@ bool DebuggerController::IsFirstAttach() } +bool DebuggerController::ShouldShowAdapterSettingsForLaunch() +{ + return m_lastDebugStartOperation != LaunchStartOperation || m_showAdapterSettingsNextTime; +} + + +bool DebuggerController::ShouldShowAdapterSettingsForAttach() +{ + return m_lastDebugStartOperation != AttachStartOperation || m_showAdapterSettingsNextTime; +} + + +bool DebuggerController::ShouldShowAdapterSettingsForConnect() +{ + return m_lastDebugStartOperation != ConnectStartOperation || m_showAdapterSettingsNextTime; +} + + +bool DebuggerController::ShouldShowAdapterSettingsForConnectToDebugServer() +{ + return m_lastDebugStartOperation != ConnectToDebugServerStartOperation || m_showAdapterSettingsNextTime; +} + + +bool DebuggerController::ShowAdapterSettingsNextTime() +{ + return m_showAdapterSettingsNextTime; +} + + +void DebuggerController::SetShowAdapterSettingsNextTime(bool value) +{ + m_showAdapterSettingsNextTime = value; +} + + bool DebuggerController::IsTTD() { if(!m_adapter) diff --git a/core/debuggercontroller.h b/core/debuggercontroller.h index d94cfa3e..272ca8b8 100644 --- a/core/debuggercontroller.h +++ b/core/debuggercontroller.h @@ -175,6 +175,20 @@ namespace BinaryNinjaDebugger { bool m_firstConnectToDebugServer = true; bool m_firstAttach = true; + // Whether to show the adapter settings dialog before the next debug session is started. When the user starts a + // session whose kind differs from m_lastDebugStartOperation we always show the dialog regardless of this flag, + // so that switching between e.g. launch and attach never silently reuses stale settings. + enum LastDebugStartOperation + { + NoDebugStartOperation, + LaunchStartOperation, + AttachStartOperation, + ConnectStartOperation, + ConnectToDebugServerStartOperation, + }; + bool m_showAdapterSettingsNextTime = true; + LastDebugStartOperation m_lastDebugStartOperation = NoDebugStartOperation; + bool m_shouldAnnotateStackVariable = false; // Apply the controller's own state mutations for each event type. Called inline @@ -644,6 +658,15 @@ namespace BinaryNinjaDebugger { bool IsFirstConnect(); bool IsFirstConnectToDebugServer(); bool IsFirstAttach(); + + bool ShouldShowAdapterSettingsForLaunch(); + bool ShouldShowAdapterSettingsForAttach(); + bool ShouldShowAdapterSettingsForConnect(); + bool ShouldShowAdapterSettingsForConnectToDebugServer(); + + bool ShowAdapterSettingsNextTime(); + void SetShowAdapterSettingsNextTime(bool value); + bool IsTTD(); // TTD Memory Analysis Methods diff --git a/core/ffi.cpp b/core/ffi.cpp index 7ff78856..f6fd65f6 100644 --- a/core/ffi.cpp +++ b/core/ffi.cpp @@ -1410,6 +1410,42 @@ bool BNDebuggerIsFirstAttach(BNDebuggerController* controller) } +bool BNDebuggerShouldShowAdapterSettingsForLaunch(BNDebuggerController* controller) +{ + return controller->object->ShouldShowAdapterSettingsForLaunch(); +} + + +bool BNDebuggerShouldShowAdapterSettingsForAttach(BNDebuggerController* controller) +{ + return controller->object->ShouldShowAdapterSettingsForAttach(); +} + + +bool BNDebuggerShouldShowAdapterSettingsForConnect(BNDebuggerController* controller) +{ + return controller->object->ShouldShowAdapterSettingsForConnect(); +} + + +bool BNDebuggerShouldShowAdapterSettingsForConnectToDebugServer(BNDebuggerController* controller) +{ + return controller->object->ShouldShowAdapterSettingsForConnectToDebugServer(); +} + + +bool BNDebuggerShowAdapterSettingsNextTime(BNDebuggerController* controller) +{ + return controller->object->ShowAdapterSettingsNextTime(); +} + + +void BNDebuggerSetShowAdapterSettingsNextTime(BNDebuggerController* controller, bool value) +{ + controller->object->SetShowAdapterSettingsNextTime(value); +} + + bool BNDebuggerIsTTD(BNDebuggerController* controller) { return controller->object->IsTTD(); diff --git a/ui/adaptersettings.cpp b/ui/adaptersettings.cpp index 451e3827..fc79d6a6 100644 --- a/ui/adaptersettings.cpp +++ b/ui/adaptersettings.cpp @@ -67,6 +67,10 @@ AdapterSettingsDialog::AdapterSettingsDialog(QWidget* parent, DbgRefsetCurrentWidget(widget); layout->addWidget(m_stack); + // Reflect the stored preference: checked means "do not show the dialog next time" + m_useSameSettingsCheckbox = new QCheckBox("Use same settings next time"); + m_useSameSettingsCheckbox->setChecked(!m_controller->ShowAdapterSettingsNextTime()); + if (!highlightGroup.empty()) { auto adapterSettings = qobject_cast(widget); @@ -84,6 +88,7 @@ AdapterSettingsDialog::AdapterSettingsDialog(QWidget* parent, DbgRefsetDefault(true); + buttonLayout->addWidget(m_useSameSettingsCheckbox); buttonLayout->addStretch(1); buttonLayout->addWidget(cancelButton); buttonLayout->addSpacing(10); @@ -92,6 +97,22 @@ AdapterSettingsDialog::AdapterSettingsDialog(QWidget* parent, DbgRefaddSpacing(10); layout->addLayout(buttonLayout); } + else + { + // The generic settings dialog (opened from the menu) has no Accept/Cancel button and applies + // settings live, so update the preference immediately whenever the checkbox is toggled. This is + // the entry point for re-enabling the dialog after it has been suppressed for an operation. + connect(m_useSameSettingsCheckbox, &QCheckBox::toggled, this, + [this](bool checked) { m_controller->SetShowAdapterSettingsNextTime(!checked); }); + + QHBoxLayout* checkboxLayout = new QHBoxLayout; + checkboxLayout->setContentsMargins(0, 0, 0, 0); + checkboxLayout->addWidget(m_useSameSettingsCheckbox); + checkboxLayout->addStretch(1); + + layout->addSpacing(10); + layout->addLayout(checkboxLayout); + } setLayout(layout); } @@ -139,5 +160,7 @@ QWidget* AdapterSettingsDialog::getWidgetForAdapter(const QString& adapter) void AdapterSettingsDialog::apply() { + if (m_useSameSettingsCheckbox) + m_controller->SetShowAdapterSettingsNextTime(!m_useSameSettingsCheckbox->isChecked()); accept(); } diff --git a/ui/adaptersettings.h b/ui/adaptersettings.h index 8c239742..0aa74d9f 100644 --- a/ui/adaptersettings.h +++ b/ui/adaptersettings.h @@ -42,6 +42,7 @@ class AdapterSettingsDialog : public QDialog QStackedWidget* m_stack; QMap m_viewMap; QLabel* m_noSettingsLabel; + QCheckBox* m_useSameSettingsCheckbox = nullptr; QWidget* getWidgetForAdapter(const QString& adapter); diff --git a/ui/controlswidget.cpp b/ui/controlswidget.cpp index 3b119237..42e62188 100644 --- a/ui/controlswidget.cpp +++ b/ui/controlswidget.cpp @@ -267,7 +267,7 @@ void DebugControlsWidget::performLaunch() return; bool firstLaunch = m_controller->IsFirstLaunch(); - if (firstLaunch) + if (m_controller->ShouldShowAdapterSettingsForLaunch()) { auto adapterSettings = new AdapterSettingsDialog(this, m_controller, "launch"); if (adapterSettings->exec() != QDialog::Accepted) diff --git a/ui/ui.cpp b/ui/ui.cpp index ba42a950..f85cc332 100644 --- a/ui/ui.cpp +++ b/ui/ui.cpp @@ -563,7 +563,7 @@ void GlobalDebuggerUI::SetupMenu(UIContext* context) return; bool firstLaunch = controller->IsFirstLaunch(); - if (firstLaunch) + if (controller->ShouldShowAdapterSettingsForLaunch()) { auto adapterSettings = new AdapterSettingsDialog(context->mainWindow(), controller, "launch"); if (adapterSettings->exec() != QDialog::Accepted) @@ -859,7 +859,7 @@ void GlobalDebuggerUI::SetupMenu(UIContext* context) if (!controller) return; - if (controller->IsFirstAttach()) + if (controller->ShouldShowAdapterSettingsForAttach()) { auto adapterSettings = new AdapterSettingsDialog(context->mainWindow(), controller, "attach"); if (adapterSettings->exec() != QDialog::Accepted) @@ -1074,7 +1074,7 @@ void GlobalDebuggerUI::SetupMenu(UIContext* context) if (!controller) return; - if (controller->IsFirstConnectToDebugServer()) + if (controller->ShouldShowAdapterSettingsForConnectToDebugServer()) { auto adapterSettings = new AdapterSettingsDialog(context->mainWindow(), controller, "debug_server"); if (adapterSettings->exec() != QDialog::Accepted) @@ -1129,7 +1129,7 @@ void GlobalDebuggerUI::SetupMenu(UIContext* context) if (!controller) return; - if (controller->IsFirstConnect()) + if (controller->ShouldShowAdapterSettingsForConnect()) { auto adapterSettings = new AdapterSettingsDialog(context->mainWindow(), controller, "connect"); if (adapterSettings->exec() != QDialog::Accepted)