diff --git a/lldb/source/Core/IOHandlerCursesGUI.cpp b/lldb/source/Core/IOHandlerCursesGUI.cpp --- a/lldb/source/Core/IOHandlerCursesGUI.cpp +++ b/lldb/source/Core/IOHandlerCursesGUI.cpp @@ -608,9 +608,19 @@ m_delete = del; } } - // + // Get the rectangle in our parent window Rect GetBounds() const { return Rect(GetParentOrigin(), GetSize()); } + + Rect GetCenteredRect(int width, int height) { + Size size = GetSize(); + width = std::min(size.width, width); + height = std::min(size.height, height); + int x = (size.width - width) / 2; + int y = (size.height - height) / 2; + return Rect(Point(x, y), Size(width, height)); + } + int GetChar() { return ::wgetch(m_window); } Point GetParentOrigin() const { return Point(GetParentX(), GetParentY()); } int GetParentX() const { return getparx(m_window); } @@ -1050,6 +1060,15 @@ // Select the last element in the field if multiple elements exists. virtual void FieldDelegateSelectLastElement() { return; } + + bool FieldDelegateIsVisible() { return m_is_visible; } + + void FieldDelegateHide() { m_is_visible = false; } + + void FieldDelegateShow() { m_is_visible = true; } + +protected: + bool m_is_visible = true; }; typedef std::shared_ptr FieldDelegateSP; @@ -1812,6 +1831,10 @@ virtual ~FormDelegate() = default; + virtual std::string GetName() = 0; + + virtual void UpdateFieldsVisibility() { return; } + FieldDelegateSP &GetField(int field_index) { return m_fields[field_index]; } FormAction &GetAction(int action_index) { return m_actions[action_index]; } @@ -1948,6 +1971,8 @@ int height = 0; height += GetErrorHeight(); for (int i = 0; i < m_delegate_sp->GetNumberOfFields(); i++) { + if (!m_delegate_sp->GetField(i)->FieldDelegateIsVisible()) + continue; height += m_delegate_sp->GetField(i)->FieldDelegateGetHeight(); } height += GetActionsHeight(); @@ -1963,6 +1988,8 @@ int offset = GetErrorHeight(); for (int i = 0; i < m_selection_index; i++) { + if (!m_delegate_sp->GetField(i)->FieldDelegateIsVisible()) + continue; offset += m_delegate_sp->GetField(i)->FieldDelegateGetHeight(); } context.Offset(offset); @@ -2018,6 +2045,8 @@ int width = surface.GetWidth(); bool a_field_is_selected = m_selection_type == SelectionType::Field; for (int i = 0; i < m_delegate_sp->GetNumberOfFields(); i++) { + if (!m_delegate_sp->GetField(i)->FieldDelegateIsVisible()) + continue; bool is_field_selected = a_field_is_selected && m_selection_index == i; FieldDelegateSP &field = m_delegate_sp->GetField(i); int height = field->FieldDelegateGetHeight(); @@ -2080,10 +2109,12 @@ } bool WindowDelegateDraw(Window &window, bool force) override { + m_delegate_sp->UpdateFieldsVisibility(); window.Erase(); - window.DrawTitleBox(window.GetName(), "Press Esc to cancel"); + window.DrawTitleBox(m_delegate_sp->GetName().c_str(), + "Press Esc to cancel"); Rect content_bounds = window.GetFrame(); content_bounds.Inset(2, 2); @@ -2093,6 +2124,21 @@ return true; } + void SkipNextHiddenFields() { + while (true) { + if (m_delegate_sp->GetField(m_selection_index)->FieldDelegateIsVisible()) + return; + + if (m_selection_index == m_delegate_sp->GetNumberOfFields() - 1) { + m_selection_type = SelectionType::Action; + m_selection_index = 0; + return; + } + + m_selection_index++; + } + } + HandleCharResult SelecteNext(int key) { if (m_selection_type == SelectionType::Action) { if (m_selection_index < m_delegate_sp->GetNumberOfActions() - 1) { @@ -2102,8 +2148,12 @@ m_selection_index = 0; m_selection_type = SelectionType::Field; - FieldDelegateSP &next_field = m_delegate_sp->GetField(m_selection_index); - next_field->FieldDelegateSelectFirstElement(); + SkipNextHiddenFields(); + if (m_selection_type == SelectionType::Field) { + FieldDelegateSP &next_field = + m_delegate_sp->GetField(m_selection_index); + next_field->FieldDelegateSelectFirstElement(); + } return eKeyHandled; } @@ -2121,13 +2171,31 @@ } m_selection_index++; + SkipNextHiddenFields(); - FieldDelegateSP &next_field = m_delegate_sp->GetField(m_selection_index); - next_field->FieldDelegateSelectFirstElement(); + if (m_selection_type == SelectionType::Field) { + FieldDelegateSP &next_field = m_delegate_sp->GetField(m_selection_index); + next_field->FieldDelegateSelectFirstElement(); + } return eKeyHandled; } + void SkipPreviousHiddenFields() { + while (true) { + if (m_delegate_sp->GetField(m_selection_index)->FieldDelegateIsVisible()) + return; + + if (m_selection_index == 0) { + m_selection_type = SelectionType::Action; + m_selection_index = 0; + return; + } + + m_selection_index--; + } + } + HandleCharResult SelectePrevious(int key) { if (m_selection_type == SelectionType::Action) { if (m_selection_index > 0) { @@ -2136,9 +2204,12 @@ } m_selection_index = m_delegate_sp->GetNumberOfFields() - 1; m_selection_type = SelectionType::Field; - FieldDelegateSP &previous_field = - m_delegate_sp->GetField(m_selection_index); - previous_field->FieldDelegateSelectLastElement(); + SkipPreviousHiddenFields(); + if (m_selection_type == SelectionType::Field) { + FieldDelegateSP &previous_field = + m_delegate_sp->GetField(m_selection_index); + previous_field->FieldDelegateSelectLastElement(); + } return eKeyHandled; } @@ -2156,10 +2227,13 @@ } m_selection_index--; + SkipPreviousHiddenFields(); - FieldDelegateSP &previous_field = - m_delegate_sp->GetField(m_selection_index); - previous_field->FieldDelegateSelectLastElement(); + if (m_selection_type == SelectionType::Field) { + FieldDelegateSP &previous_field = + m_delegate_sp->GetField(m_selection_index); + previous_field->FieldDelegateSelectLastElement(); + } return eKeyHandled; } @@ -2213,6 +2287,154 @@ int m_first_visible_line; }; +/////////////////////////// +// Form Delegate Instances +/////////////////////////// + +class ProcessAttachFormDelegate : public FormDelegate { +public: + ProcessAttachFormDelegate(Debugger &debugger) : m_debugger(debugger) { + std::vector types; + types.push_back(std::string("Name")); + types.push_back(std::string("PID")); + m_type_field = AddChoicesField("Attach By", 2, types); + + m_pid_field = AddIntegerField("PID", 0); + + m_name_field = AddTextField("Process Name", ""); + + m_plugin_field = AddTextField("Plugin Name", ""); + + m_continue_field = AddBooleanField("Continue once attached.", false); + + m_wait_for_field = AddBooleanField("Wait for process to launch.", false); + m_include_existing_field = + AddBooleanField("Include existing processes.", false); + + AddAction("Attach", [this](Window &window) { Attach(window); }); + } + + std::string GetName() override { return "Attach Process"; } + + void UpdateFieldsVisibility() override { + if (m_type_field->GetChoiceContent() == "Name") { + m_pid_field->FieldDelegateHide(); + m_name_field->FieldDelegateShow(); + m_wait_for_field->FieldDelegateShow(); + if (m_wait_for_field->GetBoolean()) + m_include_existing_field->FieldDelegateShow(); + else + m_include_existing_field->FieldDelegateHide(); + } else { + m_pid_field->FieldDelegateShow(); + m_name_field->FieldDelegateHide(); + m_wait_for_field->FieldDelegateHide(); + m_include_existing_field->FieldDelegateHide(); + } + } + + void StopProcessIfNecessary() { + ExecutionContext exe_ctx = + m_debugger.GetCommandInterpreter().GetExecutionContext(); + + if (!exe_ctx.HasProcessScope()) + return; + + Process *process = exe_ctx.GetProcessPtr(); + if (!(process && process->IsAlive())) + return; + + if (process->GetShouldDetach()) { + Status detach_status(process->Detach(false)); + if (detach_status.Fail()) + SetError("Failed to detach from existing process."); + } else { + Status destroy_status(process->Destroy(false)); + if (destroy_status.Fail()) + SetError("Failed to kill exising process."); + } + } + + Target *GetTarget() { + Target *target = m_debugger.GetSelectedTarget().get(); + + if (target != nullptr) + return target; + + TargetSP new_target_sp; + m_debugger.GetTargetList().CreateTarget( + m_debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp); + + target = new_target_sp.get(); + + if (target == nullptr) + SetError("Failed to create target."); + + return target; + } + + ProcessAttachInfo GetAttachInfo() { + ProcessAttachInfo attach_info; + attach_info.SetContinueOnceAttached(m_continue_field->GetBoolean()); + if (m_type_field->GetChoiceContent() == "Name") { + attach_info.GetExecutableFile().SetFile(m_name_field->GetText(), + FileSpec::Style::native); + attach_info.SetWaitForLaunch(m_wait_for_field->GetBoolean()); + if (m_wait_for_field->GetBoolean()) + attach_info.SetIgnoreExisting(!m_include_existing_field->GetBoolean()); + } else { + attach_info.SetProcessID(m_pid_field->GetInteger()); + } + if (!m_plugin_field->GetText().empty()) + attach_info.SetProcessPluginName(m_plugin_field->GetText()); + + return attach_info; + } + + void Attach(Window &window) { + ClearError(); + + StopProcessIfNecessary(); + if (HasError()) + return; + + Target *target = GetTarget(); + if (HasError()) + return; + + StreamString stream; + ProcessAttachInfo attach_info = GetAttachInfo(); + Status status = target->Attach(attach_info, &stream); + + if (status.Fail()) { + SetError(status.AsCString()); + return; + } + + ProcessSP process_sp(target->GetProcessSP()); + if (!process_sp) { + SetError("Attached sucessfully but target has no process."); + return; + } + + if (attach_info.GetContinueOnceAttached()) + process_sp->Resume(); + + window.GetParent()->RemoveSubWindow(&window); + } + +protected: + Debugger &m_debugger; + + ChoicesFieldDelegate *m_type_field; + IntegerFieldDelegate *m_pid_field; + TextFieldDelegate *m_name_field; + TextFieldDelegate *m_plugin_field; + BooleanFieldDelegate *m_continue_field; + BooleanFieldDelegate *m_wait_for_field; + BooleanFieldDelegate *m_include_existing_field; +}; + class MenuDelegate { public: virtual ~MenuDelegate() = default; @@ -4462,6 +4684,19 @@ } return MenuActionResult::Handled; + case eMenuID_ProcessAttach: { + WindowSP main_window_sp = m_app.GetMainWindow(); + FormDelegateSP form_delegate_sp = + FormDelegateSP(new ProcessAttachFormDelegate(m_debugger)); + Rect bounds = main_window_sp->GetCenteredRect(80, 19); + WindowSP form_window_sp = main_window_sp->CreateSubWindow( + form_delegate_sp->GetName().c_str(), bounds, true); + WindowDelegateSP window_delegate_sp = + WindowDelegateSP(new FormWindowDelegate(form_delegate_sp)); + form_window_sp->SetDelegate(window_delegate_sp); + return MenuActionResult::Handled; + } + case eMenuID_ProcessContinue: { ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();