144d93782SGreg Clayton //===-- IOHandler.cpp -------------------------------------------*- C++ -*-===// 244d93782SGreg Clayton // 344d93782SGreg Clayton // The LLVM Compiler Infrastructure 444d93782SGreg Clayton // 544d93782SGreg Clayton // This file is distributed under the University of Illinois Open Source 644d93782SGreg Clayton // License. See LICENSE.TXT for details. 744d93782SGreg Clayton // 844d93782SGreg Clayton //===----------------------------------------------------------------------===// 944d93782SGreg Clayton 102f3df613SZachary Turner #include "lldb/Core/IOHandler.h" 112f3df613SZachary Turner 12315b6884SEugene Zelenko #ifndef LLDB_DISABLE_CURSES 1327801f4fSBruce Mitchener #include <curses.h> 14315b6884SEugene Zelenko #include <panel.h> 15315b6884SEugene Zelenko #endif 1644d93782SGreg Clayton 177c9aa073STodd Fiala #if defined(__APPLE__) 187c9aa073STodd Fiala #include <deque> 197c9aa073STodd Fiala #endif 2044d93782SGreg Clayton #include <string> 2144d93782SGreg Clayton 2244d93782SGreg Clayton #include "lldb/Core/Debugger.h" 2344d93782SGreg Clayton #include "lldb/Core/StreamFile.h" 24672d2c12SJonas Devlieghere #include "lldb/Host/File.h" 25672d2c12SJonas Devlieghere #include "lldb/Utility/Predicate.h" 26672d2c12SJonas Devlieghere #include "lldb/Utility/Status.h" 27672d2c12SJonas Devlieghere #include "lldb/Utility/StreamString.h" 28672d2c12SJonas Devlieghere #include "lldb/Utility/StringList.h" 29672d2c12SJonas Devlieghere #include "lldb/lldb-forward.h" 302f3df613SZachary Turner 31cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 3244d93782SGreg Clayton #include "lldb/Host/Editline.h" 33cacde7dfSTodd Fiala #endif 3444d93782SGreg Clayton #include "lldb/Interpreter/CommandCompletions.h" 3544d93782SGreg Clayton #include "lldb/Interpreter/CommandInterpreter.h" 362f3df613SZachary Turner #ifndef LLDB_DISABLE_CURSES 372f3df613SZachary Turner #include "lldb/Breakpoint/BreakpointLocation.h" 382f3df613SZachary Turner #include "lldb/Core/Module.h" 392f3df613SZachary Turner #include "lldb/Core/ValueObject.h" 402f3df613SZachary Turner #include "lldb/Core/ValueObjectRegister.h" 4144d93782SGreg Clayton #include "lldb/Symbol/Block.h" 4244d93782SGreg Clayton #include "lldb/Symbol/Function.h" 4344d93782SGreg Clayton #include "lldb/Symbol/Symbol.h" 44c5dac77aSEugene Zelenko #include "lldb/Symbol/VariableList.h" 45c5dac77aSEugene Zelenko #include "lldb/Target/Process.h" 462f3df613SZachary Turner #include "lldb/Target/RegisterContext.h" 47c5dac77aSEugene Zelenko #include "lldb/Target/StackFrame.h" 482f3df613SZachary Turner #include "lldb/Target/StopInfo.h" 49b9c1b51eSKate Stone #include "lldb/Target/Target.h" 50b9c1b51eSKate Stone #include "lldb/Target/Thread.h" 51d821c997SPavel Labath #include "lldb/Utility/State.h" 52c5dac77aSEugene Zelenko #endif 537c9aa073STodd Fiala 54672d2c12SJonas Devlieghere #include "llvm/ADT/StringRef.h" 552f3df613SZachary Turner 56fab31220STed Woodward #ifdef _MSC_VER 57aaea8ee6SZachary Turner #include "lldb/Host/windows/windows.h" 58fab31220STed Woodward #endif 59fab31220STed Woodward 60672d2c12SJonas Devlieghere #include <memory> 61672d2c12SJonas Devlieghere #include <mutex> 622f3df613SZachary Turner 63672d2c12SJonas Devlieghere #include <assert.h> 64672d2c12SJonas Devlieghere #include <ctype.h> 65672d2c12SJonas Devlieghere #include <errno.h> 66672d2c12SJonas Devlieghere #include <locale.h> 67672d2c12SJonas Devlieghere #include <stdint.h> 68672d2c12SJonas Devlieghere #include <stdio.h> 69672d2c12SJonas Devlieghere #include <string.h> 70672d2c12SJonas Devlieghere #include <type_traits> 712f3df613SZachary Turner 7244d93782SGreg Clayton using namespace lldb; 7344d93782SGreg Clayton using namespace lldb_private; 7444d93782SGreg Clayton 75b9c1b51eSKate Stone IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type) 76b9c1b51eSKate Stone : IOHandler(debugger, type, 7744d93782SGreg Clayton StreamFileSP(), // Adopt STDIN from top input reader 7844d93782SGreg Clayton StreamFileSP(), // Adopt STDOUT from top input reader 79340b0309SGreg Clayton StreamFileSP(), // Adopt STDERR from top input reader 80340b0309SGreg Clayton 0) // Flags 81b9c1b51eSKate Stone {} 8244d93782SGreg Clayton 83b9c1b51eSKate Stone IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type, 8444d93782SGreg Clayton const lldb::StreamFileSP &input_sp, 8544d93782SGreg Clayton const lldb::StreamFileSP &output_sp, 86b9c1b51eSKate Stone const lldb::StreamFileSP &error_sp, uint32_t flags) 87b9c1b51eSKate Stone : m_debugger(debugger), m_input_sp(input_sp), m_output_sp(output_sp), 88b9c1b51eSKate Stone m_error_sp(error_sp), m_popped(false), m_flags(flags), m_type(type), 89b9c1b51eSKate Stone m_user_data(nullptr), m_done(false), m_active(false) { 9044d93782SGreg Clayton // If any files are not specified, then adopt them from the top input reader. 9144d93782SGreg Clayton if (!m_input_sp || !m_output_sp || !m_error_sp) 92b9c1b51eSKate Stone debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_sp, m_output_sp, 9344d93782SGreg Clayton m_error_sp); 9444d93782SGreg Clayton } 9544d93782SGreg Clayton 96315b6884SEugene Zelenko IOHandler::~IOHandler() = default; 9744d93782SGreg Clayton 98b9c1b51eSKate Stone int IOHandler::GetInputFD() { 99c5dac77aSEugene Zelenko return (m_input_sp ? m_input_sp->GetFile().GetDescriptor() : -1); 10044d93782SGreg Clayton } 10144d93782SGreg Clayton 102b9c1b51eSKate Stone int IOHandler::GetOutputFD() { 103c5dac77aSEugene Zelenko return (m_output_sp ? m_output_sp->GetFile().GetDescriptor() : -1); 10444d93782SGreg Clayton } 10544d93782SGreg Clayton 106b9c1b51eSKate Stone int IOHandler::GetErrorFD() { 107c5dac77aSEugene Zelenko return (m_error_sp ? m_error_sp->GetFile().GetDescriptor() : -1); 10844d93782SGreg Clayton } 10944d93782SGreg Clayton 110b9c1b51eSKate Stone FILE *IOHandler::GetInputFILE() { 111c5dac77aSEugene Zelenko return (m_input_sp ? m_input_sp->GetFile().GetStream() : nullptr); 11244d93782SGreg Clayton } 11344d93782SGreg Clayton 114b9c1b51eSKate Stone FILE *IOHandler::GetOutputFILE() { 115c5dac77aSEugene Zelenko return (m_output_sp ? m_output_sp->GetFile().GetStream() : nullptr); 11644d93782SGreg Clayton } 11744d93782SGreg Clayton 118b9c1b51eSKate Stone FILE *IOHandler::GetErrorFILE() { 119c5dac77aSEugene Zelenko return (m_error_sp ? m_error_sp->GetFile().GetStream() : nullptr); 12044d93782SGreg Clayton } 12144d93782SGreg Clayton 122b9c1b51eSKate Stone StreamFileSP &IOHandler::GetInputStreamFile() { return m_input_sp; } 12344d93782SGreg Clayton 124b9c1b51eSKate Stone StreamFileSP &IOHandler::GetOutputStreamFile() { return m_output_sp; } 12544d93782SGreg Clayton 126b9c1b51eSKate Stone StreamFileSP &IOHandler::GetErrorStreamFile() { return m_error_sp; } 12744d93782SGreg Clayton 128b9c1b51eSKate Stone bool IOHandler::GetIsInteractive() { 129340b0309SGreg Clayton return GetInputStreamFile()->GetFile().GetIsInteractive(); 130340b0309SGreg Clayton } 131340b0309SGreg Clayton 132b9c1b51eSKate Stone bool IOHandler::GetIsRealTerminal() { 133340b0309SGreg Clayton return GetInputStreamFile()->GetFile().GetIsRealTerminal(); 134340b0309SGreg Clayton } 13544d93782SGreg Clayton 136b9c1b51eSKate Stone void IOHandler::SetPopped(bool b) { m_popped.SetValue(b, eBroadcastOnChange); } 137e30f11d9SKate Stone 138b9c1b51eSKate Stone void IOHandler::WaitForPop() { m_popped.WaitForValueEqualTo(true); } 139e30f11d9SKate Stone 140b9c1b51eSKate Stone void IOHandlerStack::PrintAsync(Stream *stream, const char *s, size_t len) { 141b9c1b51eSKate Stone if (stream) { 14216ff8604SSaleem Abdulrasool std::lock_guard<std::recursive_mutex> guard(m_mutex); 1434446487dSPavel Labath if (m_top) 1444446487dSPavel Labath m_top->PrintAsync(stream, s, len); 1454446487dSPavel Labath } 1464446487dSPavel Labath } 1474446487dSPavel Labath 1487a120c8bSZachary Turner IOHandlerConfirm::IOHandlerConfirm(Debugger &debugger, llvm::StringRef prompt, 149b9c1b51eSKate Stone bool default_response) 150b9c1b51eSKate Stone : IOHandlerEditline( 151b9c1b51eSKate Stone debugger, IOHandler::Type::Confirm, 152c5dac77aSEugene Zelenko nullptr, // nullptr editline_name means no history loaded/saved 153514d8cd8SZachary Turner llvm::StringRef(), // No prompt 154514d8cd8SZachary Turner llvm::StringRef(), // No continuation prompt 15544d93782SGreg Clayton false, // Multi-line 156e30f11d9SKate Stone false, // Don't colorize the prompt (i.e. the confirm message.) 157b9c1b51eSKate Stone 0, *this), 158b9c1b51eSKate Stone m_default_response(default_response), m_user_response(default_response) { 15944d93782SGreg Clayton StreamString prompt_stream; 16044d93782SGreg Clayton prompt_stream.PutCString(prompt); 16144d93782SGreg Clayton if (m_default_response) 16244d93782SGreg Clayton prompt_stream.Printf(": [Y/n] "); 16344d93782SGreg Clayton else 16444d93782SGreg Clayton prompt_stream.Printf(": [y/N] "); 16544d93782SGreg Clayton 166514d8cd8SZachary Turner SetPrompt(prompt_stream.GetString()); 16744d93782SGreg Clayton } 16844d93782SGreg Clayton 169315b6884SEugene Zelenko IOHandlerConfirm::~IOHandlerConfirm() = default; 17044d93782SGreg Clayton 1717f88829cSRaphael Isemann int IOHandlerConfirm::IOHandlerComplete( 1727f88829cSRaphael Isemann IOHandler &io_handler, const char *current_line, const char *cursor, 1737f88829cSRaphael Isemann const char *last_char, int skip_first_n_matches, int max_matches, 1747f88829cSRaphael Isemann StringList &matches, StringList &descriptions) { 175b9c1b51eSKate Stone if (current_line == cursor) { 176b9c1b51eSKate Stone if (m_default_response) { 17744d93782SGreg Clayton matches.AppendString("y"); 178b9c1b51eSKate Stone } else { 17944d93782SGreg Clayton matches.AppendString("n"); 18044d93782SGreg Clayton } 18144d93782SGreg Clayton } 18244d93782SGreg Clayton return matches.GetSize(); 18344d93782SGreg Clayton } 18444d93782SGreg Clayton 185b9c1b51eSKate Stone void IOHandlerConfirm::IOHandlerInputComplete(IOHandler &io_handler, 186b9c1b51eSKate Stone std::string &line) { 187b9c1b51eSKate Stone if (line.empty()) { 18844d93782SGreg Clayton // User just hit enter, set the response to the default 18944d93782SGreg Clayton m_user_response = m_default_response; 19044d93782SGreg Clayton io_handler.SetIsDone(true); 19144d93782SGreg Clayton return; 19244d93782SGreg Clayton } 19344d93782SGreg Clayton 194b9c1b51eSKate Stone if (line.size() == 1) { 195b9c1b51eSKate Stone switch (line[0]) { 19644d93782SGreg Clayton case 'y': 19744d93782SGreg Clayton case 'Y': 19844d93782SGreg Clayton m_user_response = true; 19944d93782SGreg Clayton io_handler.SetIsDone(true); 20044d93782SGreg Clayton return; 20144d93782SGreg Clayton case 'n': 20244d93782SGreg Clayton case 'N': 20344d93782SGreg Clayton m_user_response = false; 20444d93782SGreg Clayton io_handler.SetIsDone(true); 20544d93782SGreg Clayton return; 20644d93782SGreg Clayton default: 20744d93782SGreg Clayton break; 20844d93782SGreg Clayton } 20944d93782SGreg Clayton } 21044d93782SGreg Clayton 211b9c1b51eSKate Stone if (line == "yes" || line == "YES" || line == "Yes") { 21244d93782SGreg Clayton m_user_response = true; 21344d93782SGreg Clayton io_handler.SetIsDone(true); 214b9c1b51eSKate Stone } else if (line == "no" || line == "NO" || line == "No") { 21544d93782SGreg Clayton m_user_response = false; 21644d93782SGreg Clayton io_handler.SetIsDone(true); 21744d93782SGreg Clayton } 21844d93782SGreg Clayton } 21944d93782SGreg Clayton 2207f88829cSRaphael Isemann int IOHandlerDelegate::IOHandlerComplete( 2217f88829cSRaphael Isemann IOHandler &io_handler, const char *current_line, const char *cursor, 2227f88829cSRaphael Isemann const char *last_char, int skip_first_n_matches, int max_matches, 2237f88829cSRaphael Isemann StringList &matches, StringList &descriptions) { 224b9c1b51eSKate Stone switch (m_completion) { 22544d93782SGreg Clayton case Completion::None: 22644d93782SGreg Clayton break; 22744d93782SGreg Clayton 22844d93782SGreg Clayton case Completion::LLDBCommand: 229b9c1b51eSKate Stone return io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion( 230b9c1b51eSKate Stone current_line, cursor, last_char, skip_first_n_matches, max_matches, 2317f88829cSRaphael Isemann matches, descriptions); 232b9c1b51eSKate Stone case Completion::Expression: { 2337f88829cSRaphael Isemann CompletionResult result; 234a2e76c0bSRaphael Isemann CompletionRequest request(current_line, current_line - cursor, 2357f88829cSRaphael Isemann skip_first_n_matches, max_matches, result); 236b9c1b51eSKate Stone CommandCompletions::InvokeCommonCompletionCallbacks( 237b9c1b51eSKate Stone io_handler.GetDebugger().GetCommandInterpreter(), 238a2e76c0bSRaphael Isemann CommandCompletions::eVariablePathCompletion, request, nullptr); 2397f88829cSRaphael Isemann result.GetMatches(matches); 2407f88829cSRaphael Isemann result.GetDescriptions(descriptions); 24144d93782SGreg Clayton 2421a6d7ab5SRaphael Isemann size_t num_matches = request.GetNumberOfMatches(); 243b9c1b51eSKate Stone if (num_matches > 0) { 24444d93782SGreg Clayton std::string common_prefix; 2451a6d7ab5SRaphael Isemann matches.LongestCommonPrefix(common_prefix); 246a2e76c0bSRaphael Isemann const size_t partial_name_len = request.GetCursorArgumentPrefix().size(); 24744d93782SGreg Clayton 24805097246SAdrian Prantl // If we matched a unique single command, add a space... Only do this if 24905097246SAdrian Prantl // the completer told us this was a complete word, however... 250a2e76c0bSRaphael Isemann if (num_matches == 1 && request.GetWordComplete()) { 25144d93782SGreg Clayton common_prefix.push_back(' '); 25244d93782SGreg Clayton } 25344d93782SGreg Clayton common_prefix.erase(0, partial_name_len); 25444d93782SGreg Clayton matches.InsertStringAtIndex(0, std::move(common_prefix)); 25544d93782SGreg Clayton } 25644d93782SGreg Clayton return num_matches; 257b9c1b51eSKate Stone } break; 25844d93782SGreg Clayton } 25944d93782SGreg Clayton 26044d93782SGreg Clayton return 0; 26144d93782SGreg Clayton } 26244d93782SGreg Clayton 263b9c1b51eSKate Stone IOHandlerEditline::IOHandlerEditline( 264b9c1b51eSKate Stone Debugger &debugger, IOHandler::Type type, 26544d93782SGreg Clayton const char *editline_name, // Used for saving history files 266514d8cd8SZachary Turner llvm::StringRef prompt, llvm::StringRef continuation_prompt, 267514d8cd8SZachary Turner bool multi_line, bool color_prompts, uint32_t line_number_start, 268514d8cd8SZachary Turner IOHandlerDelegate &delegate) 269b9c1b51eSKate Stone : IOHandlerEditline(debugger, type, 27044d93782SGreg Clayton StreamFileSP(), // Inherit input from top input reader 27144d93782SGreg Clayton StreamFileSP(), // Inherit output from top input reader 27244d93782SGreg Clayton StreamFileSP(), // Inherit error from top input reader 273340b0309SGreg Clayton 0, // Flags 27444d93782SGreg Clayton editline_name, // Used for saving history files 275b9c1b51eSKate Stone prompt, continuation_prompt, multi_line, color_prompts, 276b9c1b51eSKate Stone line_number_start, delegate) {} 27744d93782SGreg Clayton 278b9c1b51eSKate Stone IOHandlerEditline::IOHandlerEditline( 279b9c1b51eSKate Stone Debugger &debugger, IOHandler::Type type, 280b9c1b51eSKate Stone const lldb::StreamFileSP &input_sp, const lldb::StreamFileSP &output_sp, 281b9c1b51eSKate Stone const lldb::StreamFileSP &error_sp, uint32_t flags, 28244d93782SGreg Clayton const char *editline_name, // Used for saving history files 283514d8cd8SZachary Turner llvm::StringRef prompt, llvm::StringRef continuation_prompt, 284514d8cd8SZachary Turner bool multi_line, bool color_prompts, uint32_t line_number_start, 285514d8cd8SZachary Turner IOHandlerDelegate &delegate) 286b9c1b51eSKate Stone : IOHandler(debugger, type, input_sp, output_sp, error_sp, flags), 287cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 28844d93782SGreg Clayton m_editline_ap(), 289cacde7dfSTodd Fiala #endif 290b9c1b51eSKate Stone m_delegate(delegate), m_prompt(), m_continuation_prompt(), 291b9c1b51eSKate Stone m_current_lines_ptr(nullptr), m_base_line_number(line_number_start), 292b9c1b51eSKate Stone m_curr_line_idx(UINT32_MAX), m_multi_line(multi_line), 293b9c1b51eSKate Stone m_color_prompts(color_prompts), m_interrupt_exits(true), 294b9c1b51eSKate Stone m_editing(false) { 29544d93782SGreg Clayton SetPrompt(prompt); 29644d93782SGreg Clayton 297cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 298914b8d98SDeepak Panickal bool use_editline = false; 299340b0309SGreg Clayton 300340b0309SGreg Clayton use_editline = m_input_sp->GetFile().GetIsRealTerminal(); 30144d93782SGreg Clayton 302b9c1b51eSKate Stone if (use_editline) { 303b9c1b51eSKate Stone m_editline_ap.reset(new Editline(editline_name, GetInputFILE(), 304b9c1b51eSKate Stone GetOutputFILE(), GetErrorFILE(), 305e30f11d9SKate Stone m_color_prompts)); 306e30f11d9SKate Stone m_editline_ap->SetIsInputCompleteCallback(IsInputCompleteCallback, this); 30744d93782SGreg Clayton m_editline_ap->SetAutoCompleteCallback(AutoCompleteCallback, this); 308e30f11d9SKate Stone // See if the delegate supports fixing indentation 309e30f11d9SKate Stone const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters(); 310b9c1b51eSKate Stone if (indent_chars) { 311b9c1b51eSKate Stone // The delegate does support indentation, hook it up so when any 31205097246SAdrian Prantl // indentation character is typed, the delegate gets a chance to fix it 313b9c1b51eSKate Stone m_editline_ap->SetFixIndentationCallback(FixIndentationCallback, this, 314b9c1b51eSKate Stone indent_chars); 315e30f11d9SKate Stone } 31644d93782SGreg Clayton } 317cacde7dfSTodd Fiala #endif 318e30f11d9SKate Stone SetBaseLineNumber(m_base_line_number); 319514d8cd8SZachary Turner SetPrompt(prompt); 320e30f11d9SKate Stone SetContinuationPrompt(continuation_prompt); 32144d93782SGreg Clayton } 32244d93782SGreg Clayton 323b9c1b51eSKate Stone IOHandlerEditline::~IOHandlerEditline() { 324cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 32544d93782SGreg Clayton m_editline_ap.reset(); 326cacde7dfSTodd Fiala #endif 32744d93782SGreg Clayton } 32844d93782SGreg Clayton 329b9c1b51eSKate Stone void IOHandlerEditline::Activate() { 330e30f11d9SKate Stone IOHandler::Activate(); 331e30f11d9SKate Stone m_delegate.IOHandlerActivated(*this); 332e30f11d9SKate Stone } 333e30f11d9SKate Stone 334b9c1b51eSKate Stone void IOHandlerEditline::Deactivate() { 335e30f11d9SKate Stone IOHandler::Deactivate(); 336e30f11d9SKate Stone m_delegate.IOHandlerDeactivated(*this); 337e30f11d9SKate Stone } 338e30f11d9SKate Stone 339b9c1b51eSKate Stone bool IOHandlerEditline::GetLine(std::string &line, bool &interrupted) { 340cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 341b9c1b51eSKate Stone if (m_editline_ap) { 342e30f11d9SKate Stone return m_editline_ap->GetLine(line, interrupted); 343b9c1b51eSKate Stone } else { 344cacde7dfSTodd Fiala #endif 34544d93782SGreg Clayton line.clear(); 34644d93782SGreg Clayton 34744d93782SGreg Clayton FILE *in = GetInputFILE(); 348b9c1b51eSKate Stone if (in) { 349b9c1b51eSKate Stone if (GetIsInteractive()) { 350c5dac77aSEugene Zelenko const char *prompt = nullptr; 351e30f11d9SKate Stone 352e30f11d9SKate Stone if (m_multi_line && m_curr_line_idx > 0) 353e30f11d9SKate Stone prompt = GetContinuationPrompt(); 354e30f11d9SKate Stone 355c5dac77aSEugene Zelenko if (prompt == nullptr) 356e30f11d9SKate Stone prompt = GetPrompt(); 357e30f11d9SKate Stone 358b9c1b51eSKate Stone if (prompt && prompt[0]) { 35944d93782SGreg Clayton FILE *out = GetOutputFILE(); 360b9c1b51eSKate Stone if (out) { 36144d93782SGreg Clayton ::fprintf(out, "%s", prompt); 36244d93782SGreg Clayton ::fflush(out); 36344d93782SGreg Clayton } 36444d93782SGreg Clayton } 36544d93782SGreg Clayton } 36644d93782SGreg Clayton char buffer[256]; 36744d93782SGreg Clayton bool done = false; 3680f86e6e7SGreg Clayton bool got_line = false; 369e034a04eSGreg Clayton m_editing = true; 370b9c1b51eSKate Stone while (!done) { 371b9c1b51eSKate Stone if (fgets(buffer, sizeof(buffer), in) == nullptr) { 372c7797accSGreg Clayton const int saved_errno = errno; 373c9cf5798SGreg Clayton if (feof(in)) 37444d93782SGreg Clayton done = true; 375b9c1b51eSKate Stone else if (ferror(in)) { 376c7797accSGreg Clayton if (saved_errno != EINTR) 377c7797accSGreg Clayton done = true; 378c7797accSGreg Clayton } 379b9c1b51eSKate Stone } else { 3800f86e6e7SGreg Clayton got_line = true; 38144d93782SGreg Clayton size_t buffer_len = strlen(buffer); 38244d93782SGreg Clayton assert(buffer[buffer_len] == '\0'); 38344d93782SGreg Clayton char last_char = buffer[buffer_len - 1]; 384b9c1b51eSKate Stone if (last_char == '\r' || last_char == '\n') { 38544d93782SGreg Clayton done = true; 38644d93782SGreg Clayton // Strip trailing newlines 387b9c1b51eSKate Stone while (last_char == '\r' || last_char == '\n') { 38844d93782SGreg Clayton --buffer_len; 38944d93782SGreg Clayton if (buffer_len == 0) 39044d93782SGreg Clayton break; 39144d93782SGreg Clayton last_char = buffer[buffer_len - 1]; 39244d93782SGreg Clayton } 39344d93782SGreg Clayton } 39444d93782SGreg Clayton line.append(buffer, buffer_len); 39544d93782SGreg Clayton } 39644d93782SGreg Clayton } 397e034a04eSGreg Clayton m_editing = false; 39805097246SAdrian Prantl // We might have gotten a newline on a line by itself make sure to return 39905097246SAdrian Prantl // true in this case. 4000f86e6e7SGreg Clayton return got_line; 401b9c1b51eSKate Stone } else { 40244d93782SGreg Clayton // No more input file, we are done... 40344d93782SGreg Clayton SetIsDone(true); 40444d93782SGreg Clayton } 405340b0309SGreg Clayton return false; 406cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 40744d93782SGreg Clayton } 408cacde7dfSTodd Fiala #endif 40944d93782SGreg Clayton } 41044d93782SGreg Clayton 411cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 412b9c1b51eSKate Stone bool IOHandlerEditline::IsInputCompleteCallback(Editline *editline, 41344d93782SGreg Clayton StringList &lines, 414b9c1b51eSKate Stone void *baton) { 41544d93782SGreg Clayton IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton; 416b9c1b51eSKate Stone return editline_reader->m_delegate.IOHandlerIsInputComplete(*editline_reader, 417b9c1b51eSKate Stone lines); 418e30f11d9SKate Stone } 419e30f11d9SKate Stone 420b9c1b51eSKate Stone int IOHandlerEditline::FixIndentationCallback(Editline *editline, 421e30f11d9SKate Stone const StringList &lines, 422e30f11d9SKate Stone int cursor_position, 423b9c1b51eSKate Stone void *baton) { 424e30f11d9SKate Stone IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton; 425b9c1b51eSKate Stone return editline_reader->m_delegate.IOHandlerFixIndentation( 426b9c1b51eSKate Stone *editline_reader, lines, cursor_position); 42744d93782SGreg Clayton } 42844d93782SGreg Clayton 4297f88829cSRaphael Isemann int IOHandlerEditline::AutoCompleteCallback( 4307f88829cSRaphael Isemann const char *current_line, const char *cursor, const char *last_char, 4317f88829cSRaphael Isemann int skip_first_n_matches, int max_matches, StringList &matches, 4327f88829cSRaphael Isemann StringList &descriptions, void *baton) { 43344d93782SGreg Clayton IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton; 43444d93782SGreg Clayton if (editline_reader) 435b9c1b51eSKate Stone return editline_reader->m_delegate.IOHandlerComplete( 436b9c1b51eSKate Stone *editline_reader, current_line, cursor, last_char, skip_first_n_matches, 4377f88829cSRaphael Isemann max_matches, matches, descriptions); 43844d93782SGreg Clayton return 0; 43944d93782SGreg Clayton } 440cacde7dfSTodd Fiala #endif 44144d93782SGreg Clayton 442b9c1b51eSKate Stone const char *IOHandlerEditline::GetPrompt() { 443cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 444b9c1b51eSKate Stone if (m_editline_ap) { 44544d93782SGreg Clayton return m_editline_ap->GetPrompt(); 446b9c1b51eSKate Stone } else { 447cacde7dfSTodd Fiala #endif 448cacde7dfSTodd Fiala if (m_prompt.empty()) 449c5dac77aSEugene Zelenko return nullptr; 450cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 451cacde7dfSTodd Fiala } 452cacde7dfSTodd Fiala #endif 45344d93782SGreg Clayton return m_prompt.c_str(); 45444d93782SGreg Clayton } 45544d93782SGreg Clayton 456514d8cd8SZachary Turner bool IOHandlerEditline::SetPrompt(llvm::StringRef prompt) { 457514d8cd8SZachary Turner m_prompt = prompt; 458514d8cd8SZachary Turner 459cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 46044d93782SGreg Clayton if (m_editline_ap) 461c5dac77aSEugene Zelenko m_editline_ap->SetPrompt(m_prompt.empty() ? nullptr : m_prompt.c_str()); 462cacde7dfSTodd Fiala #endif 46344d93782SGreg Clayton return true; 46444d93782SGreg Clayton } 46544d93782SGreg Clayton 466b9c1b51eSKate Stone const char *IOHandlerEditline::GetContinuationPrompt() { 467b9c1b51eSKate Stone return (m_continuation_prompt.empty() ? nullptr 468b9c1b51eSKate Stone : m_continuation_prompt.c_str()); 469e30f11d9SKate Stone } 470e30f11d9SKate Stone 471514d8cd8SZachary Turner void IOHandlerEditline::SetContinuationPrompt(llvm::StringRef prompt) { 472514d8cd8SZachary Turner m_continuation_prompt = prompt; 473e30f11d9SKate Stone 474d553d00cSZachary Turner #ifndef LLDB_DISABLE_LIBEDIT 475e30f11d9SKate Stone if (m_editline_ap) 476b9c1b51eSKate Stone m_editline_ap->SetContinuationPrompt(m_continuation_prompt.empty() 477b9c1b51eSKate Stone ? nullptr 478b9c1b51eSKate Stone : m_continuation_prompt.c_str()); 479d553d00cSZachary Turner #endif 480e30f11d9SKate Stone } 481e30f11d9SKate Stone 482b9c1b51eSKate Stone void IOHandlerEditline::SetBaseLineNumber(uint32_t line) { 483f6913cd7SGreg Clayton m_base_line_number = line; 484f6913cd7SGreg Clayton } 485e30f11d9SKate Stone 486b9c1b51eSKate Stone uint32_t IOHandlerEditline::GetCurrentLineIndex() const { 487d553d00cSZachary Turner #ifndef LLDB_DISABLE_LIBEDIT 488e30f11d9SKate Stone if (m_editline_ap) 489e30f11d9SKate Stone return m_editline_ap->GetCurrentLine(); 490e30f11d9SKate Stone #endif 491e30f11d9SKate Stone return m_curr_line_idx; 492e30f11d9SKate Stone } 493e30f11d9SKate Stone 494b9c1b51eSKate Stone bool IOHandlerEditline::GetLines(StringList &lines, bool &interrupted) { 495e30f11d9SKate Stone m_current_lines_ptr = &lines; 496e30f11d9SKate Stone 49744d93782SGreg Clayton bool success = false; 498cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 499b9c1b51eSKate Stone if (m_editline_ap) { 500e30f11d9SKate Stone return m_editline_ap->GetLines(m_base_line_number, lines, interrupted); 501b9c1b51eSKate Stone } else { 502cacde7dfSTodd Fiala #endif 503e30f11d9SKate Stone bool done = false; 50497206d57SZachary Turner Status error; 50544d93782SGreg Clayton 506b9c1b51eSKate Stone while (!done) { 507f6913cd7SGreg Clayton // Show line numbers if we are asked to 50844d93782SGreg Clayton std::string line; 509b9c1b51eSKate Stone if (m_base_line_number > 0 && GetIsInteractive()) { 510f6913cd7SGreg Clayton FILE *out = GetOutputFILE(); 511f6913cd7SGreg Clayton if (out) 512b9c1b51eSKate Stone ::fprintf(out, "%u%s", m_base_line_number + (uint32_t)lines.GetSize(), 513b9c1b51eSKate Stone GetPrompt() == nullptr ? " " : ""); 514f6913cd7SGreg Clayton } 515f6913cd7SGreg Clayton 516e30f11d9SKate Stone m_curr_line_idx = lines.GetSize(); 517e30f11d9SKate Stone 518f0066ad0SGreg Clayton bool interrupted = false; 519b9c1b51eSKate Stone if (GetLine(line, interrupted) && !interrupted) { 52044d93782SGreg Clayton lines.AppendString(line); 521e30f11d9SKate Stone done = m_delegate.IOHandlerIsInputComplete(*this, lines); 522b9c1b51eSKate Stone } else { 523e30f11d9SKate Stone done = true; 52444d93782SGreg Clayton } 52544d93782SGreg Clayton } 52644d93782SGreg Clayton success = lines.GetSize() > 0; 527cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 52844d93782SGreg Clayton } 529cacde7dfSTodd Fiala #endif 53044d93782SGreg Clayton return success; 53144d93782SGreg Clayton } 53244d93782SGreg Clayton 53305097246SAdrian Prantl // Each IOHandler gets to run until it is done. It should read data from the 53405097246SAdrian Prantl // "in" and place output into "out" and "err and return when done. 535b9c1b51eSKate Stone void IOHandlerEditline::Run() { 53644d93782SGreg Clayton std::string line; 537b9c1b51eSKate Stone while (IsActive()) { 538f0066ad0SGreg Clayton bool interrupted = false; 539b9c1b51eSKate Stone if (m_multi_line) { 54044d93782SGreg Clayton StringList lines; 541b9c1b51eSKate Stone if (GetLines(lines, interrupted)) { 542b9c1b51eSKate Stone if (interrupted) { 543e30f11d9SKate Stone m_done = m_interrupt_exits; 544e30f11d9SKate Stone m_delegate.IOHandlerInputInterrupted(*this, line); 545e30f11d9SKate Stone 546b9c1b51eSKate Stone } else { 54744d93782SGreg Clayton line = lines.CopyList(); 54844d93782SGreg Clayton m_delegate.IOHandlerInputComplete(*this, line); 54944d93782SGreg Clayton } 550b9c1b51eSKate Stone } else { 55144d93782SGreg Clayton m_done = true; 55244d93782SGreg Clayton } 553b9c1b51eSKate Stone } else { 554b9c1b51eSKate Stone if (GetLine(line, interrupted)) { 555e30f11d9SKate Stone if (interrupted) 556e30f11d9SKate Stone m_delegate.IOHandlerInputInterrupted(*this, line); 557e30f11d9SKate Stone else 55844d93782SGreg Clayton m_delegate.IOHandlerInputComplete(*this, line); 559b9c1b51eSKate Stone } else { 56044d93782SGreg Clayton m_done = true; 56144d93782SGreg Clayton } 56244d93782SGreg Clayton } 56344d93782SGreg Clayton } 56444d93782SGreg Clayton } 56544d93782SGreg Clayton 566b9c1b51eSKate Stone void IOHandlerEditline::Cancel() { 567cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 568e68f5d6bSGreg Clayton if (m_editline_ap) 5694446487dSPavel Labath m_editline_ap->Cancel(); 570cacde7dfSTodd Fiala #endif 571e68f5d6bSGreg Clayton } 572e68f5d6bSGreg Clayton 573b9c1b51eSKate Stone bool IOHandlerEditline::Interrupt() { 574f0066ad0SGreg Clayton // Let the delgate handle it first 575f0066ad0SGreg Clayton if (m_delegate.IOHandlerInterrupt(*this)) 576f0066ad0SGreg Clayton return true; 577f0066ad0SGreg Clayton 578cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 57944d93782SGreg Clayton if (m_editline_ap) 580f0066ad0SGreg Clayton return m_editline_ap->Interrupt(); 581cacde7dfSTodd Fiala #endif 582f0066ad0SGreg Clayton return false; 58344d93782SGreg Clayton } 58444d93782SGreg Clayton 585b9c1b51eSKate Stone void IOHandlerEditline::GotEOF() { 586cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 58744d93782SGreg Clayton if (m_editline_ap) 58844d93782SGreg Clayton m_editline_ap->Interrupt(); 589cacde7dfSTodd Fiala #endif 59044d93782SGreg Clayton } 59144d93782SGreg Clayton 592b9c1b51eSKate Stone void IOHandlerEditline::PrintAsync(Stream *stream, const char *s, size_t len) { 5934446487dSPavel Labath #ifndef LLDB_DISABLE_LIBEDIT 5944446487dSPavel Labath if (m_editline_ap) 5954446487dSPavel Labath m_editline_ap->PrintAsync(stream, s, len); 5964446487dSPavel Labath else 5974446487dSPavel Labath #endif 598fab31220STed Woodward { 599fab31220STed Woodward #ifdef _MSC_VER 600341e4789SDawn Perchik const char *prompt = GetPrompt(); 601b9c1b51eSKate Stone if (prompt) { 602fab31220STed Woodward // Back up over previous prompt using Windows API 603fab31220STed Woodward CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info; 604fab31220STed Woodward HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE); 605fab31220STed Woodward GetConsoleScreenBufferInfo(console_handle, &screen_buffer_info); 606fab31220STed Woodward COORD coord = screen_buffer_info.dwCursorPosition; 607fab31220STed Woodward coord.X -= strlen(prompt); 608fab31220STed Woodward if (coord.X < 0) 609fab31220STed Woodward coord.X = 0; 610fab31220STed Woodward SetConsoleCursorPosition(console_handle, coord); 611fab31220STed Woodward } 612fab31220STed Woodward #endif 6134446487dSPavel Labath IOHandler::PrintAsync(stream, s, len); 614341e4789SDawn Perchik #ifdef _MSC_VER 615fab31220STed Woodward if (prompt) 616b9c1b51eSKate Stone IOHandler::PrintAsync(GetOutputStreamFile().get(), prompt, 617b9c1b51eSKate Stone strlen(prompt)); 618341e4789SDawn Perchik #endif 619fab31220STed Woodward } 6204446487dSPavel Labath } 6214446487dSPavel Labath 62205097246SAdrian Prantl // we may want curses to be disabled for some builds for instance, windows 623914b8d98SDeepak Panickal #ifndef LLDB_DISABLE_CURSES 624914b8d98SDeepak Panickal 62544d93782SGreg Clayton #define KEY_RETURN 10 62644d93782SGreg Clayton #define KEY_ESCAPE 27 62744d93782SGreg Clayton 628b9c1b51eSKate Stone namespace curses { 62944d93782SGreg Clayton class Menu; 63044d93782SGreg Clayton class MenuDelegate; 63144d93782SGreg Clayton class Window; 63244d93782SGreg Clayton class WindowDelegate; 63344d93782SGreg Clayton typedef std::shared_ptr<Menu> MenuSP; 63444d93782SGreg Clayton typedef std::shared_ptr<MenuDelegate> MenuDelegateSP; 63544d93782SGreg Clayton typedef std::shared_ptr<Window> WindowSP; 63644d93782SGreg Clayton typedef std::shared_ptr<WindowDelegate> WindowDelegateSP; 63744d93782SGreg Clayton typedef std::vector<MenuSP> Menus; 63844d93782SGreg Clayton typedef std::vector<WindowSP> Windows; 63944d93782SGreg Clayton typedef std::vector<WindowDelegateSP> WindowDelegates; 64044d93782SGreg Clayton 64144d93782SGreg Clayton #if 0 64244d93782SGreg Clayton type summary add -s "x=${var.x}, y=${var.y}" curses::Point 64344d93782SGreg Clayton type summary add -s "w=${var.width}, h=${var.height}" curses::Size 64444d93782SGreg Clayton type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect 64544d93782SGreg Clayton #endif 646315b6884SEugene Zelenko 647b9c1b51eSKate Stone struct Point { 64844d93782SGreg Clayton int x; 64944d93782SGreg Clayton int y; 65044d93782SGreg Clayton 651b9c1b51eSKate Stone Point(int _x = 0, int _y = 0) : x(_x), y(_y) {} 65244d93782SGreg Clayton 653b9c1b51eSKate Stone void Clear() { 65444d93782SGreg Clayton x = 0; 65544d93782SGreg Clayton y = 0; 65644d93782SGreg Clayton } 65744d93782SGreg Clayton 658b9c1b51eSKate Stone Point &operator+=(const Point &rhs) { 65944d93782SGreg Clayton x += rhs.x; 66044d93782SGreg Clayton y += rhs.y; 66144d93782SGreg Clayton return *this; 66244d93782SGreg Clayton } 66344d93782SGreg Clayton 664b9c1b51eSKate Stone void Dump() { printf("(x=%i, y=%i)\n", x, y); } 66544d93782SGreg Clayton }; 66644d93782SGreg Clayton 667b9c1b51eSKate Stone bool operator==(const Point &lhs, const Point &rhs) { 66844d93782SGreg Clayton return lhs.x == rhs.x && lhs.y == rhs.y; 66944d93782SGreg Clayton } 670315b6884SEugene Zelenko 671b9c1b51eSKate Stone bool operator!=(const Point &lhs, const Point &rhs) { 67244d93782SGreg Clayton return lhs.x != rhs.x || lhs.y != rhs.y; 67344d93782SGreg Clayton } 67444d93782SGreg Clayton 675b9c1b51eSKate Stone struct Size { 67644d93782SGreg Clayton int width; 67744d93782SGreg Clayton int height; 678b9c1b51eSKate Stone Size(int w = 0, int h = 0) : width(w), height(h) {} 67944d93782SGreg Clayton 680b9c1b51eSKate Stone void Clear() { 68144d93782SGreg Clayton width = 0; 68244d93782SGreg Clayton height = 0; 68344d93782SGreg Clayton } 68444d93782SGreg Clayton 685b9c1b51eSKate Stone void Dump() { printf("(w=%i, h=%i)\n", width, height); } 68644d93782SGreg Clayton }; 68744d93782SGreg Clayton 688b9c1b51eSKate Stone bool operator==(const Size &lhs, const Size &rhs) { 68944d93782SGreg Clayton return lhs.width == rhs.width && lhs.height == rhs.height; 69044d93782SGreg Clayton } 691315b6884SEugene Zelenko 692b9c1b51eSKate Stone bool operator!=(const Size &lhs, const Size &rhs) { 69344d93782SGreg Clayton return lhs.width != rhs.width || lhs.height != rhs.height; 69444d93782SGreg Clayton } 69544d93782SGreg Clayton 696b9c1b51eSKate Stone struct Rect { 69744d93782SGreg Clayton Point origin; 69844d93782SGreg Clayton Size size; 69944d93782SGreg Clayton 700b9c1b51eSKate Stone Rect() : origin(), size() {} 70144d93782SGreg Clayton 702b9c1b51eSKate Stone Rect(const Point &p, const Size &s) : origin(p), size(s) {} 70344d93782SGreg Clayton 704b9c1b51eSKate Stone void Clear() { 70544d93782SGreg Clayton origin.Clear(); 70644d93782SGreg Clayton size.Clear(); 70744d93782SGreg Clayton } 70844d93782SGreg Clayton 709b9c1b51eSKate Stone void Dump() { 710b9c1b51eSKate Stone printf("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width, 711b9c1b51eSKate Stone size.height); 71244d93782SGreg Clayton } 71344d93782SGreg Clayton 714b9c1b51eSKate Stone void Inset(int w, int h) { 71544d93782SGreg Clayton if (size.width > w * 2) 71644d93782SGreg Clayton size.width -= w * 2; 71744d93782SGreg Clayton origin.x += w; 71844d93782SGreg Clayton 71944d93782SGreg Clayton if (size.height > h * 2) 72044d93782SGreg Clayton size.height -= h * 2; 72144d93782SGreg Clayton origin.y += h; 72244d93782SGreg Clayton } 723315b6884SEugene Zelenko 72405097246SAdrian Prantl // Return a status bar rectangle which is the last line of this rectangle. 72505097246SAdrian Prantl // This rectangle will be modified to not include the status bar area. 726b9c1b51eSKate Stone Rect MakeStatusBar() { 72744d93782SGreg Clayton Rect status_bar; 728b9c1b51eSKate Stone if (size.height > 1) { 72944d93782SGreg Clayton status_bar.origin.x = origin.x; 73044d93782SGreg Clayton status_bar.origin.y = size.height; 73144d93782SGreg Clayton status_bar.size.width = size.width; 73244d93782SGreg Clayton status_bar.size.height = 1; 73344d93782SGreg Clayton --size.height; 73444d93782SGreg Clayton } 73544d93782SGreg Clayton return status_bar; 73644d93782SGreg Clayton } 73744d93782SGreg Clayton 73805097246SAdrian Prantl // Return a menubar rectangle which is the first line of this rectangle. This 73905097246SAdrian Prantl // rectangle will be modified to not include the menubar area. 740b9c1b51eSKate Stone Rect MakeMenuBar() { 74144d93782SGreg Clayton Rect menubar; 742b9c1b51eSKate Stone if (size.height > 1) { 74344d93782SGreg Clayton menubar.origin.x = origin.x; 74444d93782SGreg Clayton menubar.origin.y = origin.y; 74544d93782SGreg Clayton menubar.size.width = size.width; 74644d93782SGreg Clayton menubar.size.height = 1; 74744d93782SGreg Clayton ++origin.y; 74844d93782SGreg Clayton --size.height; 74944d93782SGreg Clayton } 75044d93782SGreg Clayton return menubar; 75144d93782SGreg Clayton } 75244d93782SGreg Clayton 753b9c1b51eSKate Stone void HorizontalSplitPercentage(float top_percentage, Rect &top, 754b9c1b51eSKate Stone Rect &bottom) const { 75544d93782SGreg Clayton float top_height = top_percentage * size.height; 75644d93782SGreg Clayton HorizontalSplit(top_height, top, bottom); 75744d93782SGreg Clayton } 75844d93782SGreg Clayton 759b9c1b51eSKate Stone void HorizontalSplit(int top_height, Rect &top, Rect &bottom) const { 76044d93782SGreg Clayton top = *this; 761b9c1b51eSKate Stone if (top_height < size.height) { 76244d93782SGreg Clayton top.size.height = top_height; 76344d93782SGreg Clayton bottom.origin.x = origin.x; 76444d93782SGreg Clayton bottom.origin.y = origin.y + top.size.height; 76544d93782SGreg Clayton bottom.size.width = size.width; 76644d93782SGreg Clayton bottom.size.height = size.height - top.size.height; 767b9c1b51eSKate Stone } else { 76844d93782SGreg Clayton bottom.Clear(); 76944d93782SGreg Clayton } 77044d93782SGreg Clayton } 77144d93782SGreg Clayton 772b9c1b51eSKate Stone void VerticalSplitPercentage(float left_percentage, Rect &left, 773b9c1b51eSKate Stone Rect &right) const { 77444d93782SGreg Clayton float left_width = left_percentage * size.width; 77544d93782SGreg Clayton VerticalSplit(left_width, left, right); 77644d93782SGreg Clayton } 77744d93782SGreg Clayton 778b9c1b51eSKate Stone void VerticalSplit(int left_width, Rect &left, Rect &right) const { 77944d93782SGreg Clayton left = *this; 780b9c1b51eSKate Stone if (left_width < size.width) { 78144d93782SGreg Clayton left.size.width = left_width; 78244d93782SGreg Clayton right.origin.x = origin.x + left.size.width; 78344d93782SGreg Clayton right.origin.y = origin.y; 78444d93782SGreg Clayton right.size.width = size.width - left.size.width; 78544d93782SGreg Clayton right.size.height = size.height; 786b9c1b51eSKate Stone } else { 78744d93782SGreg Clayton right.Clear(); 78844d93782SGreg Clayton } 78944d93782SGreg Clayton } 79044d93782SGreg Clayton }; 79144d93782SGreg Clayton 792b9c1b51eSKate Stone bool operator==(const Rect &lhs, const Rect &rhs) { 79344d93782SGreg Clayton return lhs.origin == rhs.origin && lhs.size == rhs.size; 79444d93782SGreg Clayton } 795315b6884SEugene Zelenko 796b9c1b51eSKate Stone bool operator!=(const Rect &lhs, const Rect &rhs) { 79744d93782SGreg Clayton return lhs.origin != rhs.origin || lhs.size != rhs.size; 79844d93782SGreg Clayton } 79944d93782SGreg Clayton 800b9c1b51eSKate Stone enum HandleCharResult { 80144d93782SGreg Clayton eKeyNotHandled = 0, 80244d93782SGreg Clayton eKeyHandled = 1, 80344d93782SGreg Clayton eQuitApplication = 2 80444d93782SGreg Clayton }; 80544d93782SGreg Clayton 806b9c1b51eSKate Stone enum class MenuActionResult { 80744d93782SGreg Clayton Handled, 80844d93782SGreg Clayton NotHandled, 80944d93782SGreg Clayton Quit // Exit all menus and quit 81044d93782SGreg Clayton }; 81144d93782SGreg Clayton 812b9c1b51eSKate Stone struct KeyHelp { 81344d93782SGreg Clayton int ch; 81444d93782SGreg Clayton const char *description; 81544d93782SGreg Clayton }; 81644d93782SGreg Clayton 817b9c1b51eSKate Stone class WindowDelegate { 81844d93782SGreg Clayton public: 819b9c1b51eSKate Stone virtual ~WindowDelegate() = default; 82044d93782SGreg Clayton 821b9c1b51eSKate Stone virtual bool WindowDelegateDraw(Window &window, bool force) { 82244d93782SGreg Clayton return false; // Drawing not handled 82344d93782SGreg Clayton } 82444d93782SGreg Clayton 825b9c1b51eSKate Stone virtual HandleCharResult WindowDelegateHandleChar(Window &window, int key) { 82644d93782SGreg Clayton return eKeyNotHandled; 82744d93782SGreg Clayton } 82844d93782SGreg Clayton 829b9c1b51eSKate Stone virtual const char *WindowDelegateGetHelpText() { return nullptr; } 83044d93782SGreg Clayton 831b9c1b51eSKate Stone virtual KeyHelp *WindowDelegateGetKeyHelp() { return nullptr; } 83244d93782SGreg Clayton }; 83344d93782SGreg Clayton 834b9c1b51eSKate Stone class HelpDialogDelegate : public WindowDelegate { 83544d93782SGreg Clayton public: 83644d93782SGreg Clayton HelpDialogDelegate(const char *text, KeyHelp *key_help_array); 83744d93782SGreg Clayton 838bd5ae6b4SGreg Clayton ~HelpDialogDelegate() override; 83944d93782SGreg Clayton 840b9c1b51eSKate Stone bool WindowDelegateDraw(Window &window, bool force) override; 84144d93782SGreg Clayton 842b9c1b51eSKate Stone HandleCharResult WindowDelegateHandleChar(Window &window, int key) override; 84344d93782SGreg Clayton 844b9c1b51eSKate Stone size_t GetNumLines() const { return m_text.GetSize(); } 84544d93782SGreg Clayton 846b9c1b51eSKate Stone size_t GetMaxLineLength() const { return m_text.GetMaxStringLength(); } 84744d93782SGreg Clayton 84844d93782SGreg Clayton protected: 84944d93782SGreg Clayton StringList m_text; 85044d93782SGreg Clayton int m_first_visible_line; 85144d93782SGreg Clayton }; 85244d93782SGreg Clayton 853b9c1b51eSKate Stone class Window { 85444d93782SGreg Clayton public: 855b9c1b51eSKate Stone Window(const char *name) 856b9c1b51eSKate Stone : m_name(name), m_window(nullptr), m_panel(nullptr), m_parent(nullptr), 857b9c1b51eSKate Stone m_subwindows(), m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX), 858b9c1b51eSKate Stone m_prev_active_window_idx(UINT32_MAX), m_delete(false), 859b9c1b51eSKate Stone m_needs_update(true), m_can_activate(true), m_is_subwin(false) {} 86044d93782SGreg Clayton 861b9c1b51eSKate Stone Window(const char *name, WINDOW *w, bool del = true) 862b9c1b51eSKate Stone : m_name(name), m_window(nullptr), m_panel(nullptr), m_parent(nullptr), 863b9c1b51eSKate Stone m_subwindows(), m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX), 864b9c1b51eSKate Stone m_prev_active_window_idx(UINT32_MAX), m_delete(del), 865b9c1b51eSKate Stone m_needs_update(true), m_can_activate(true), m_is_subwin(false) { 86644d93782SGreg Clayton if (w) 86744d93782SGreg Clayton Reset(w); 86844d93782SGreg Clayton } 86944d93782SGreg Clayton 870b9c1b51eSKate Stone Window(const char *name, const Rect &bounds) 871b9c1b51eSKate Stone : m_name(name), m_window(nullptr), m_parent(nullptr), m_subwindows(), 872b9c1b51eSKate Stone m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX), 873b9c1b51eSKate Stone m_prev_active_window_idx(UINT32_MAX), m_delete(true), 874b9c1b51eSKate Stone m_needs_update(true), m_can_activate(true), m_is_subwin(false) { 875b9c1b51eSKate Stone Reset(::newwin(bounds.size.height, bounds.size.width, bounds.origin.y, 876b9c1b51eSKate Stone bounds.origin.y)); 87744d93782SGreg Clayton } 87844d93782SGreg Clayton 879b9c1b51eSKate Stone virtual ~Window() { 88044d93782SGreg Clayton RemoveSubWindows(); 88144d93782SGreg Clayton Reset(); 88244d93782SGreg Clayton } 88344d93782SGreg Clayton 884b9c1b51eSKate Stone void Reset(WINDOW *w = nullptr, bool del = true) { 88544d93782SGreg Clayton if (m_window == w) 88644d93782SGreg Clayton return; 88744d93782SGreg Clayton 888b9c1b51eSKate Stone if (m_panel) { 88944d93782SGreg Clayton ::del_panel(m_panel); 890c5dac77aSEugene Zelenko m_panel = nullptr; 89144d93782SGreg Clayton } 892b9c1b51eSKate Stone if (m_window && m_delete) { 89344d93782SGreg Clayton ::delwin(m_window); 894c5dac77aSEugene Zelenko m_window = nullptr; 89544d93782SGreg Clayton m_delete = false; 89644d93782SGreg Clayton } 897b9c1b51eSKate Stone if (w) { 89844d93782SGreg Clayton m_window = w; 89944d93782SGreg Clayton m_panel = ::new_panel(m_window); 90044d93782SGreg Clayton m_delete = del; 90144d93782SGreg Clayton } 90244d93782SGreg Clayton } 90344d93782SGreg Clayton 90444d93782SGreg Clayton void AttributeOn(attr_t attr) { ::wattron(m_window, attr); } 90544d93782SGreg Clayton void AttributeOff(attr_t attr) { ::wattroff(m_window, attr); } 906b9c1b51eSKate Stone void Box(chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) { 907b9c1b51eSKate Stone ::box(m_window, v_char, h_char); 908b9c1b51eSKate Stone } 90944d93782SGreg Clayton void Clear() { ::wclear(m_window); } 91044d93782SGreg Clayton void Erase() { ::werase(m_window); } 911b9c1b51eSKate Stone Rect GetBounds() { 912b9c1b51eSKate Stone return Rect(GetParentOrigin(), GetSize()); 913b9c1b51eSKate Stone } // Get the rectangle in our parent window 91444d93782SGreg Clayton int GetChar() { return ::wgetch(m_window); } 91544d93782SGreg Clayton int GetCursorX() { return getcurx(m_window); } 91644d93782SGreg Clayton int GetCursorY() { return getcury(m_window); } 917b9c1b51eSKate Stone Rect GetFrame() { 918b9c1b51eSKate Stone return Rect(Point(), GetSize()); 919b9c1b51eSKate Stone } // Get our rectangle in our own coordinate system 92044d93782SGreg Clayton Point GetParentOrigin() { return Point(GetParentX(), GetParentY()); } 92144d93782SGreg Clayton Size GetSize() { return Size(GetWidth(), GetHeight()); } 92244d93782SGreg Clayton int GetParentX() { return getparx(m_window); } 92344d93782SGreg Clayton int GetParentY() { return getpary(m_window); } 92444d93782SGreg Clayton int GetMaxX() { return getmaxx(m_window); } 92544d93782SGreg Clayton int GetMaxY() { return getmaxy(m_window); } 92644d93782SGreg Clayton int GetWidth() { return GetMaxX(); } 92744d93782SGreg Clayton int GetHeight() { return GetMaxY(); } 92844d93782SGreg Clayton void MoveCursor(int x, int y) { ::wmove(m_window, y, x); } 92944d93782SGreg Clayton void MoveWindow(int x, int y) { MoveWindow(Point(x, y)); } 93044d93782SGreg Clayton void Resize(int w, int h) { ::wresize(m_window, h, w); } 931b9c1b51eSKate Stone void Resize(const Size &size) { 932b9c1b51eSKate Stone ::wresize(m_window, size.height, size.width); 933b9c1b51eSKate Stone } 93444d93782SGreg Clayton void PutChar(int ch) { ::waddch(m_window, ch); } 93544d93782SGreg Clayton void PutCString(const char *s, int len = -1) { ::waddnstr(m_window, s, len); } 93644d93782SGreg Clayton void Refresh() { ::wrefresh(m_window); } 937b9c1b51eSKate Stone void DeferredRefresh() { 93844d93782SGreg Clayton // We are using panels, so we don't need to call this... 93944d93782SGreg Clayton //::wnoutrefresh(m_window); 94044d93782SGreg Clayton } 941b9c1b51eSKate Stone void SetBackground(int color_pair_idx) { 942b9c1b51eSKate Stone ::wbkgd(m_window, COLOR_PAIR(color_pair_idx)); 943b9c1b51eSKate Stone } 94444d93782SGreg Clayton void UnderlineOn() { AttributeOn(A_UNDERLINE); } 94544d93782SGreg Clayton void UnderlineOff() { AttributeOff(A_UNDERLINE); } 94644d93782SGreg Clayton 947b9c1b51eSKate Stone void PutCStringTruncated(const char *s, int right_pad) { 94844d93782SGreg Clayton int bytes_left = GetWidth() - GetCursorX(); 949b9c1b51eSKate Stone if (bytes_left > right_pad) { 95044d93782SGreg Clayton bytes_left -= right_pad; 95144d93782SGreg Clayton ::waddnstr(m_window, s, bytes_left); 95244d93782SGreg Clayton } 95344d93782SGreg Clayton } 95444d93782SGreg Clayton 955b9c1b51eSKate Stone void MoveWindow(const Point &origin) { 95644d93782SGreg Clayton const bool moving_window = origin != GetParentOrigin(); 957b9c1b51eSKate Stone if (m_is_subwin && moving_window) { 95844d93782SGreg Clayton // Can't move subwindows, must delete and re-create 95944d93782SGreg Clayton Size size = GetSize(); 960b9c1b51eSKate Stone Reset(::subwin(m_parent->m_window, size.height, size.width, origin.y, 961b9c1b51eSKate Stone origin.x), 962b9c1b51eSKate Stone true); 963b9c1b51eSKate Stone } else { 96444d93782SGreg Clayton ::mvwin(m_window, origin.y, origin.x); 96544d93782SGreg Clayton } 96644d93782SGreg Clayton } 96744d93782SGreg Clayton 968b9c1b51eSKate Stone void SetBounds(const Rect &bounds) { 96944d93782SGreg Clayton const bool moving_window = bounds.origin != GetParentOrigin(); 970b9c1b51eSKate Stone if (m_is_subwin && moving_window) { 97144d93782SGreg Clayton // Can't move subwindows, must delete and re-create 972b9c1b51eSKate Stone Reset(::subwin(m_parent->m_window, bounds.size.height, bounds.size.width, 973b9c1b51eSKate Stone bounds.origin.y, bounds.origin.x), 974b9c1b51eSKate Stone true); 975b9c1b51eSKate Stone } else { 97644d93782SGreg Clayton if (moving_window) 97744d93782SGreg Clayton MoveWindow(bounds.origin); 97844d93782SGreg Clayton Resize(bounds.size); 97944d93782SGreg Clayton } 98044d93782SGreg Clayton } 98144d93782SGreg Clayton 982b9c1b51eSKate Stone void Printf(const char *format, ...) __attribute__((format(printf, 2, 3))) { 98344d93782SGreg Clayton va_list args; 98444d93782SGreg Clayton va_start(args, format); 98544d93782SGreg Clayton vwprintw(m_window, format, args); 98644d93782SGreg Clayton va_end(args); 98744d93782SGreg Clayton } 98844d93782SGreg Clayton 989b9c1b51eSKate Stone void Touch() { 99044d93782SGreg Clayton ::touchwin(m_window); 99144d93782SGreg Clayton if (m_parent) 99244d93782SGreg Clayton m_parent->Touch(); 99344d93782SGreg Clayton } 99444d93782SGreg Clayton 995b9c1b51eSKate Stone WindowSP CreateSubWindow(const char *name, const Rect &bounds, 996b9c1b51eSKate Stone bool make_active) { 99744d93782SGreg Clayton WindowSP subwindow_sp; 998b9c1b51eSKate Stone if (m_window) { 999b9c1b51eSKate Stone subwindow_sp.reset(new Window( 1000b9c1b51eSKate Stone name, ::subwin(m_window, bounds.size.height, bounds.size.width, 1001b9c1b51eSKate Stone bounds.origin.y, bounds.origin.x), 1002b9c1b51eSKate Stone true)); 100344d93782SGreg Clayton subwindow_sp->m_is_subwin = true; 1004b9c1b51eSKate Stone } else { 1005b9c1b51eSKate Stone subwindow_sp.reset( 1006b9c1b51eSKate Stone new Window(name, ::newwin(bounds.size.height, bounds.size.width, 1007b9c1b51eSKate Stone bounds.origin.y, bounds.origin.x), 1008b9c1b51eSKate Stone true)); 100944d93782SGreg Clayton subwindow_sp->m_is_subwin = false; 101044d93782SGreg Clayton } 101144d93782SGreg Clayton subwindow_sp->m_parent = this; 1012b9c1b51eSKate Stone if (make_active) { 101344d93782SGreg Clayton m_prev_active_window_idx = m_curr_active_window_idx; 101444d93782SGreg Clayton m_curr_active_window_idx = m_subwindows.size(); 101544d93782SGreg Clayton } 101644d93782SGreg Clayton m_subwindows.push_back(subwindow_sp); 101744d93782SGreg Clayton ::top_panel(subwindow_sp->m_panel); 101844d93782SGreg Clayton m_needs_update = true; 101944d93782SGreg Clayton return subwindow_sp; 102044d93782SGreg Clayton } 102144d93782SGreg Clayton 1022b9c1b51eSKate Stone bool RemoveSubWindow(Window *window) { 102344d93782SGreg Clayton Windows::iterator pos, end = m_subwindows.end(); 102444d93782SGreg Clayton size_t i = 0; 1025b9c1b51eSKate Stone for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) { 1026b9c1b51eSKate Stone if ((*pos).get() == window) { 102744d93782SGreg Clayton if (m_prev_active_window_idx == i) 102844d93782SGreg Clayton m_prev_active_window_idx = UINT32_MAX; 1029b9c1b51eSKate Stone else if (m_prev_active_window_idx != UINT32_MAX && 1030b9c1b51eSKate Stone m_prev_active_window_idx > i) 103144d93782SGreg Clayton --m_prev_active_window_idx; 103244d93782SGreg Clayton 103344d93782SGreg Clayton if (m_curr_active_window_idx == i) 103444d93782SGreg Clayton m_curr_active_window_idx = UINT32_MAX; 1035b9c1b51eSKate Stone else if (m_curr_active_window_idx != UINT32_MAX && 1036b9c1b51eSKate Stone m_curr_active_window_idx > i) 103744d93782SGreg Clayton --m_curr_active_window_idx; 103844d93782SGreg Clayton window->Erase(); 103944d93782SGreg Clayton m_subwindows.erase(pos); 104044d93782SGreg Clayton m_needs_update = true; 104144d93782SGreg Clayton if (m_parent) 104244d93782SGreg Clayton m_parent->Touch(); 104344d93782SGreg Clayton else 104444d93782SGreg Clayton ::touchwin(stdscr); 104544d93782SGreg Clayton return true; 104644d93782SGreg Clayton } 104744d93782SGreg Clayton } 104844d93782SGreg Clayton return false; 104944d93782SGreg Clayton } 105044d93782SGreg Clayton 1051b9c1b51eSKate Stone WindowSP FindSubWindow(const char *name) { 105244d93782SGreg Clayton Windows::iterator pos, end = m_subwindows.end(); 105344d93782SGreg Clayton size_t i = 0; 1054b9c1b51eSKate Stone for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) { 1055*8d20cfdfSJonas Devlieghere if ((*pos)->m_name == name) 105644d93782SGreg Clayton return *pos; 105744d93782SGreg Clayton } 105844d93782SGreg Clayton return WindowSP(); 105944d93782SGreg Clayton } 106044d93782SGreg Clayton 1061b9c1b51eSKate Stone void RemoveSubWindows() { 106244d93782SGreg Clayton m_curr_active_window_idx = UINT32_MAX; 106344d93782SGreg Clayton m_prev_active_window_idx = UINT32_MAX; 106444d93782SGreg Clayton for (Windows::iterator pos = m_subwindows.begin(); 1065b9c1b51eSKate Stone pos != m_subwindows.end(); pos = m_subwindows.erase(pos)) { 106644d93782SGreg Clayton (*pos)->Erase(); 106744d93782SGreg Clayton } 106844d93782SGreg Clayton if (m_parent) 106944d93782SGreg Clayton m_parent->Touch(); 107044d93782SGreg Clayton else 107144d93782SGreg Clayton ::touchwin(stdscr); 107244d93782SGreg Clayton } 107344d93782SGreg Clayton 1074b9c1b51eSKate Stone WINDOW *get() { return m_window; } 107544d93782SGreg Clayton 1076b9c1b51eSKate Stone operator WINDOW *() { return m_window; } 107744d93782SGreg Clayton 107844d93782SGreg Clayton //---------------------------------------------------------------------- 107944d93782SGreg Clayton // Window drawing utilities 108044d93782SGreg Clayton //---------------------------------------------------------------------- 1081b9c1b51eSKate Stone void DrawTitleBox(const char *title, const char *bottom_message = nullptr) { 108244d93782SGreg Clayton attr_t attr = 0; 108344d93782SGreg Clayton if (IsActive()) 108444d93782SGreg Clayton attr = A_BOLD | COLOR_PAIR(2); 108544d93782SGreg Clayton else 108644d93782SGreg Clayton attr = 0; 108744d93782SGreg Clayton if (attr) 108844d93782SGreg Clayton AttributeOn(attr); 108944d93782SGreg Clayton 109044d93782SGreg Clayton Box(); 109144d93782SGreg Clayton MoveCursor(3, 0); 109244d93782SGreg Clayton 1093b9c1b51eSKate Stone if (title && title[0]) { 109444d93782SGreg Clayton PutChar('<'); 109544d93782SGreg Clayton PutCString(title); 109644d93782SGreg Clayton PutChar('>'); 109744d93782SGreg Clayton } 109844d93782SGreg Clayton 1099b9c1b51eSKate Stone if (bottom_message && bottom_message[0]) { 110044d93782SGreg Clayton int bottom_message_length = strlen(bottom_message); 110144d93782SGreg Clayton int x = GetWidth() - 3 - (bottom_message_length + 2); 110244d93782SGreg Clayton 1103b9c1b51eSKate Stone if (x > 0) { 110444d93782SGreg Clayton MoveCursor(x, GetHeight() - 1); 110544d93782SGreg Clayton PutChar('['); 110644d93782SGreg Clayton PutCString(bottom_message); 110744d93782SGreg Clayton PutChar(']'); 1108b9c1b51eSKate Stone } else { 110944d93782SGreg Clayton MoveCursor(1, GetHeight() - 1); 111044d93782SGreg Clayton PutChar('['); 111144d93782SGreg Clayton PutCStringTruncated(bottom_message, 1); 111244d93782SGreg Clayton } 111344d93782SGreg Clayton } 111444d93782SGreg Clayton if (attr) 111544d93782SGreg Clayton AttributeOff(attr); 111644d93782SGreg Clayton } 111744d93782SGreg Clayton 1118b9c1b51eSKate Stone virtual void Draw(bool force) { 111944d93782SGreg Clayton if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw(*this, force)) 112044d93782SGreg Clayton return; 112144d93782SGreg Clayton 112244d93782SGreg Clayton for (auto &subwindow_sp : m_subwindows) 112344d93782SGreg Clayton subwindow_sp->Draw(force); 112444d93782SGreg Clayton } 112544d93782SGreg Clayton 1126b9c1b51eSKate Stone bool CreateHelpSubwindow() { 1127b9c1b51eSKate Stone if (m_delegate_sp) { 112844d93782SGreg Clayton const char *text = m_delegate_sp->WindowDelegateGetHelpText(); 112944d93782SGreg Clayton KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp(); 1130b9c1b51eSKate Stone if ((text && text[0]) || key_help) { 1131e1a6074bSBenjamin Kramer std::unique_ptr<HelpDialogDelegate> help_delegate_ap( 1132b9c1b51eSKate Stone new HelpDialogDelegate(text, key_help)); 113344d93782SGreg Clayton const size_t num_lines = help_delegate_ap->GetNumLines(); 113444d93782SGreg Clayton const size_t max_length = help_delegate_ap->GetMaxLineLength(); 113544d93782SGreg Clayton Rect bounds = GetBounds(); 113644d93782SGreg Clayton bounds.Inset(1, 1); 1137b9c1b51eSKate Stone if (max_length + 4 < static_cast<size_t>(bounds.size.width)) { 113844d93782SGreg Clayton bounds.origin.x += (bounds.size.width - max_length + 4) / 2; 113944d93782SGreg Clayton bounds.size.width = max_length + 4; 1140b9c1b51eSKate Stone } else { 1141b9c1b51eSKate Stone if (bounds.size.width > 100) { 114244d93782SGreg Clayton const int inset_w = bounds.size.width / 4; 114344d93782SGreg Clayton bounds.origin.x += inset_w; 114444d93782SGreg Clayton bounds.size.width -= 2 * inset_w; 114544d93782SGreg Clayton } 114644d93782SGreg Clayton } 114744d93782SGreg Clayton 1148b9c1b51eSKate Stone if (num_lines + 2 < static_cast<size_t>(bounds.size.height)) { 114944d93782SGreg Clayton bounds.origin.y += (bounds.size.height - num_lines + 2) / 2; 115044d93782SGreg Clayton bounds.size.height = num_lines + 2; 1151b9c1b51eSKate Stone } else { 1152b9c1b51eSKate Stone if (bounds.size.height > 100) { 115344d93782SGreg Clayton const int inset_h = bounds.size.height / 4; 115444d93782SGreg Clayton bounds.origin.y += inset_h; 115544d93782SGreg Clayton bounds.size.height -= 2 * inset_h; 115644d93782SGreg Clayton } 115744d93782SGreg Clayton } 11585fdb09bbSGreg Clayton WindowSP help_window_sp; 11595fdb09bbSGreg Clayton Window *parent_window = GetParent(); 11605fdb09bbSGreg Clayton if (parent_window) 11615fdb09bbSGreg Clayton help_window_sp = parent_window->CreateSubWindow("Help", bounds, true); 11625fdb09bbSGreg Clayton else 11635fdb09bbSGreg Clayton help_window_sp = CreateSubWindow("Help", bounds, true); 1164b9c1b51eSKate Stone help_window_sp->SetDelegate( 1165b9c1b51eSKate Stone WindowDelegateSP(help_delegate_ap.release())); 116644d93782SGreg Clayton return true; 116744d93782SGreg Clayton } 116844d93782SGreg Clayton } 116944d93782SGreg Clayton return false; 117044d93782SGreg Clayton } 117144d93782SGreg Clayton 1172b9c1b51eSKate Stone virtual HandleCharResult HandleChar(int key) { 117344d93782SGreg Clayton // Always check the active window first 117444d93782SGreg Clayton HandleCharResult result = eKeyNotHandled; 117544d93782SGreg Clayton WindowSP active_window_sp = GetActiveWindow(); 1176b9c1b51eSKate Stone if (active_window_sp) { 117744d93782SGreg Clayton result = active_window_sp->HandleChar(key); 117844d93782SGreg Clayton if (result != eKeyNotHandled) 117944d93782SGreg Clayton return result; 118044d93782SGreg Clayton } 118144d93782SGreg Clayton 1182b9c1b51eSKate Stone if (m_delegate_sp) { 118344d93782SGreg Clayton result = m_delegate_sp->WindowDelegateHandleChar(*this, key); 118444d93782SGreg Clayton if (result != eKeyNotHandled) 118544d93782SGreg Clayton return result; 118644d93782SGreg Clayton } 118744d93782SGreg Clayton 118805097246SAdrian Prantl // Then check for any windows that want any keys that weren't handled. This 118905097246SAdrian Prantl // is typically only for a menubar. Make a copy of the subwindows in case 119005097246SAdrian Prantl // any HandleChar() functions muck with the subwindows. If we don't do 119105097246SAdrian Prantl // this, we can crash when iterating over the subwindows. 119244d93782SGreg Clayton Windows subwindows(m_subwindows); 1193b9c1b51eSKate Stone for (auto subwindow_sp : subwindows) { 1194b9c1b51eSKate Stone if (!subwindow_sp->m_can_activate) { 119544d93782SGreg Clayton HandleCharResult result = subwindow_sp->HandleChar(key); 119644d93782SGreg Clayton if (result != eKeyNotHandled) 119744d93782SGreg Clayton return result; 119844d93782SGreg Clayton } 119944d93782SGreg Clayton } 120044d93782SGreg Clayton 120144d93782SGreg Clayton return eKeyNotHandled; 120244d93782SGreg Clayton } 120344d93782SGreg Clayton 1204b9c1b51eSKate Stone bool SetActiveWindow(Window *window) { 120544d93782SGreg Clayton const size_t num_subwindows = m_subwindows.size(); 1206b9c1b51eSKate Stone for (size_t i = 0; i < num_subwindows; ++i) { 1207b9c1b51eSKate Stone if (m_subwindows[i].get() == window) { 120844d93782SGreg Clayton m_prev_active_window_idx = m_curr_active_window_idx; 120944d93782SGreg Clayton ::top_panel(window->m_panel); 121044d93782SGreg Clayton m_curr_active_window_idx = i; 121144d93782SGreg Clayton return true; 121244d93782SGreg Clayton } 121344d93782SGreg Clayton } 121444d93782SGreg Clayton return false; 121544d93782SGreg Clayton } 121644d93782SGreg Clayton 1217b9c1b51eSKate Stone WindowSP GetActiveWindow() { 1218b9c1b51eSKate Stone if (!m_subwindows.empty()) { 1219b9c1b51eSKate Stone if (m_curr_active_window_idx >= m_subwindows.size()) { 1220b9c1b51eSKate Stone if (m_prev_active_window_idx < m_subwindows.size()) { 122144d93782SGreg Clayton m_curr_active_window_idx = m_prev_active_window_idx; 122244d93782SGreg Clayton m_prev_active_window_idx = UINT32_MAX; 1223b9c1b51eSKate Stone } else if (IsActive()) { 122444d93782SGreg Clayton m_prev_active_window_idx = UINT32_MAX; 122544d93782SGreg Clayton m_curr_active_window_idx = UINT32_MAX; 122644d93782SGreg Clayton 122744d93782SGreg Clayton // Find first window that wants to be active if this window is active 122844d93782SGreg Clayton const size_t num_subwindows = m_subwindows.size(); 1229b9c1b51eSKate Stone for (size_t i = 0; i < num_subwindows; ++i) { 1230b9c1b51eSKate Stone if (m_subwindows[i]->GetCanBeActive()) { 123144d93782SGreg Clayton m_curr_active_window_idx = i; 123244d93782SGreg Clayton break; 123344d93782SGreg Clayton } 123444d93782SGreg Clayton } 123544d93782SGreg Clayton } 123644d93782SGreg Clayton } 123744d93782SGreg Clayton 123844d93782SGreg Clayton if (m_curr_active_window_idx < m_subwindows.size()) 123944d93782SGreg Clayton return m_subwindows[m_curr_active_window_idx]; 124044d93782SGreg Clayton } 124144d93782SGreg Clayton return WindowSP(); 124244d93782SGreg Clayton } 124344d93782SGreg Clayton 1244b9c1b51eSKate Stone bool GetCanBeActive() const { return m_can_activate; } 124544d93782SGreg Clayton 1246b9c1b51eSKate Stone void SetCanBeActive(bool b) { m_can_activate = b; } 124744d93782SGreg Clayton 1248b9c1b51eSKate Stone const WindowDelegateSP &GetDelegate() const { return m_delegate_sp; } 124944d93782SGreg Clayton 1250b9c1b51eSKate Stone void SetDelegate(const WindowDelegateSP &delegate_sp) { 125144d93782SGreg Clayton m_delegate_sp = delegate_sp; 125244d93782SGreg Clayton } 125344d93782SGreg Clayton 1254b9c1b51eSKate Stone Window *GetParent() const { return m_parent; } 125544d93782SGreg Clayton 1256b9c1b51eSKate Stone bool IsActive() const { 125744d93782SGreg Clayton if (m_parent) 125844d93782SGreg Clayton return m_parent->GetActiveWindow().get() == this; 125944d93782SGreg Clayton else 126044d93782SGreg Clayton return true; // Top level window is always active 126144d93782SGreg Clayton } 126244d93782SGreg Clayton 1263b9c1b51eSKate Stone void SelectNextWindowAsActive() { 126444d93782SGreg Clayton // Move active focus to next window 126544d93782SGreg Clayton const size_t num_subwindows = m_subwindows.size(); 1266b9c1b51eSKate Stone if (m_curr_active_window_idx == UINT32_MAX) { 126744d93782SGreg Clayton uint32_t idx = 0; 1268b9c1b51eSKate Stone for (auto subwindow_sp : m_subwindows) { 1269b9c1b51eSKate Stone if (subwindow_sp->GetCanBeActive()) { 127044d93782SGreg Clayton m_curr_active_window_idx = idx; 127144d93782SGreg Clayton break; 127244d93782SGreg Clayton } 127344d93782SGreg Clayton ++idx; 127444d93782SGreg Clayton } 1275b9c1b51eSKate Stone } else if (m_curr_active_window_idx + 1 < num_subwindows) { 127644d93782SGreg Clayton bool handled = false; 127744d93782SGreg Clayton m_prev_active_window_idx = m_curr_active_window_idx; 1278b9c1b51eSKate Stone for (size_t idx = m_curr_active_window_idx + 1; idx < num_subwindows; 1279b9c1b51eSKate Stone ++idx) { 1280b9c1b51eSKate Stone if (m_subwindows[idx]->GetCanBeActive()) { 128144d93782SGreg Clayton m_curr_active_window_idx = idx; 128244d93782SGreg Clayton handled = true; 128344d93782SGreg Clayton break; 128444d93782SGreg Clayton } 128544d93782SGreg Clayton } 1286b9c1b51eSKate Stone if (!handled) { 1287b9c1b51eSKate Stone for (size_t idx = 0; idx <= m_prev_active_window_idx; ++idx) { 1288b9c1b51eSKate Stone if (m_subwindows[idx]->GetCanBeActive()) { 128944d93782SGreg Clayton m_curr_active_window_idx = idx; 129044d93782SGreg Clayton break; 129144d93782SGreg Clayton } 129244d93782SGreg Clayton } 129344d93782SGreg Clayton } 1294b9c1b51eSKate Stone } else { 129544d93782SGreg Clayton m_prev_active_window_idx = m_curr_active_window_idx; 1296b9c1b51eSKate Stone for (size_t idx = 0; idx < num_subwindows; ++idx) { 1297b9c1b51eSKate Stone if (m_subwindows[idx]->GetCanBeActive()) { 129844d93782SGreg Clayton m_curr_active_window_idx = idx; 129944d93782SGreg Clayton break; 130044d93782SGreg Clayton } 130144d93782SGreg Clayton } 130244d93782SGreg Clayton } 130344d93782SGreg Clayton } 130444d93782SGreg Clayton 1305b9c1b51eSKate Stone const char *GetName() const { return m_name.c_str(); } 1306315b6884SEugene Zelenko 130744d93782SGreg Clayton protected: 130844d93782SGreg Clayton std::string m_name; 130944d93782SGreg Clayton WINDOW *m_window; 131044d93782SGreg Clayton PANEL *m_panel; 131144d93782SGreg Clayton Window *m_parent; 131244d93782SGreg Clayton Windows m_subwindows; 131344d93782SGreg Clayton WindowDelegateSP m_delegate_sp; 131444d93782SGreg Clayton uint32_t m_curr_active_window_idx; 131544d93782SGreg Clayton uint32_t m_prev_active_window_idx; 131644d93782SGreg Clayton bool m_delete; 131744d93782SGreg Clayton bool m_needs_update; 131844d93782SGreg Clayton bool m_can_activate; 131944d93782SGreg Clayton bool m_is_subwin; 132044d93782SGreg Clayton 132144d93782SGreg Clayton private: 132244d93782SGreg Clayton DISALLOW_COPY_AND_ASSIGN(Window); 132344d93782SGreg Clayton }; 132444d93782SGreg Clayton 1325b9c1b51eSKate Stone class MenuDelegate { 132644d93782SGreg Clayton public: 1327315b6884SEugene Zelenko virtual ~MenuDelegate() = default; 132844d93782SGreg Clayton 1329b9c1b51eSKate Stone virtual MenuActionResult MenuDelegateAction(Menu &menu) = 0; 133044d93782SGreg Clayton }; 133144d93782SGreg Clayton 1332b9c1b51eSKate Stone class Menu : public WindowDelegate { 133344d93782SGreg Clayton public: 1334b9c1b51eSKate Stone enum class Type { Invalid, Bar, Item, Separator }; 133544d93782SGreg Clayton 133644d93782SGreg Clayton // Menubar or separator constructor 133744d93782SGreg Clayton Menu(Type type); 133844d93782SGreg Clayton 133944d93782SGreg Clayton // Menuitem constructor 1340b9c1b51eSKate Stone Menu(const char *name, const char *key_name, int key_value, 134144d93782SGreg Clayton uint64_t identifier); 134244d93782SGreg Clayton 1343315b6884SEugene Zelenko ~Menu() override = default; 134444d93782SGreg Clayton 1345b9c1b51eSKate Stone const MenuDelegateSP &GetDelegate() const { return m_delegate_sp; } 134644d93782SGreg Clayton 1347b9c1b51eSKate Stone void SetDelegate(const MenuDelegateSP &delegate_sp) { 134844d93782SGreg Clayton m_delegate_sp = delegate_sp; 134944d93782SGreg Clayton } 135044d93782SGreg Clayton 1351b9c1b51eSKate Stone void RecalculateNameLengths(); 135244d93782SGreg Clayton 1353b9c1b51eSKate Stone void AddSubmenu(const MenuSP &menu_sp); 135444d93782SGreg Clayton 1355b9c1b51eSKate Stone int DrawAndRunMenu(Window &window); 135644d93782SGreg Clayton 1357b9c1b51eSKate Stone void DrawMenuTitle(Window &window, bool highlight); 135844d93782SGreg Clayton 1359b9c1b51eSKate Stone bool WindowDelegateDraw(Window &window, bool force) override; 136044d93782SGreg Clayton 1361b9c1b51eSKate Stone HandleCharResult WindowDelegateHandleChar(Window &window, int key) override; 136244d93782SGreg Clayton 1363b9c1b51eSKate Stone MenuActionResult ActionPrivate(Menu &menu) { 136444d93782SGreg Clayton MenuActionResult result = MenuActionResult::NotHandled; 1365b9c1b51eSKate Stone if (m_delegate_sp) { 136644d93782SGreg Clayton result = m_delegate_sp->MenuDelegateAction(menu); 136744d93782SGreg Clayton if (result != MenuActionResult::NotHandled) 136844d93782SGreg Clayton return result; 1369b9c1b51eSKate Stone } else if (m_parent) { 137044d93782SGreg Clayton result = m_parent->ActionPrivate(menu); 137144d93782SGreg Clayton if (result != MenuActionResult::NotHandled) 137244d93782SGreg Clayton return result; 137344d93782SGreg Clayton } 137444d93782SGreg Clayton return m_canned_result; 137544d93782SGreg Clayton } 137644d93782SGreg Clayton 1377b9c1b51eSKate Stone MenuActionResult Action() { 137805097246SAdrian Prantl // Call the recursive action so it can try to handle it with the menu 137905097246SAdrian Prantl // delegate, and if not, try our parent menu 138044d93782SGreg Clayton return ActionPrivate(*this); 138144d93782SGreg Clayton } 138244d93782SGreg Clayton 1383b9c1b51eSKate Stone void SetCannedResult(MenuActionResult result) { m_canned_result = result; } 138444d93782SGreg Clayton 1385b9c1b51eSKate Stone Menus &GetSubmenus() { return m_submenus; } 138644d93782SGreg Clayton 1387b9c1b51eSKate Stone const Menus &GetSubmenus() const { return m_submenus; } 138844d93782SGreg Clayton 1389b9c1b51eSKate Stone int GetSelectedSubmenuIndex() const { return m_selected; } 139044d93782SGreg Clayton 1391b9c1b51eSKate Stone void SetSelectedSubmenuIndex(int idx) { m_selected = idx; } 139244d93782SGreg Clayton 1393b9c1b51eSKate Stone Type GetType() const { return m_type; } 139444d93782SGreg Clayton 1395b9c1b51eSKate Stone int GetStartingColumn() const { return m_start_col; } 139644d93782SGreg Clayton 1397b9c1b51eSKate Stone void SetStartingColumn(int col) { m_start_col = col; } 139844d93782SGreg Clayton 1399b9c1b51eSKate Stone int GetKeyValue() const { return m_key_value; } 140044d93782SGreg Clayton 1401b9c1b51eSKate Stone void SetKeyValue(int key_value) { m_key_value = key_value; } 140244d93782SGreg Clayton 1403b9c1b51eSKate Stone std::string &GetName() { return m_name; } 140444d93782SGreg Clayton 1405b9c1b51eSKate Stone std::string &GetKeyName() { return m_key_name; } 140644d93782SGreg Clayton 1407b9c1b51eSKate Stone int GetDrawWidth() const { 140844d93782SGreg Clayton return m_max_submenu_name_length + m_max_submenu_key_name_length + 8; 140944d93782SGreg Clayton } 141044d93782SGreg Clayton 1411b9c1b51eSKate Stone uint64_t GetIdentifier() const { return m_identifier; } 141244d93782SGreg Clayton 1413b9c1b51eSKate Stone void SetIdentifier(uint64_t identifier) { m_identifier = identifier; } 141444d93782SGreg Clayton 141544d93782SGreg Clayton protected: 141644d93782SGreg Clayton std::string m_name; 141744d93782SGreg Clayton std::string m_key_name; 141844d93782SGreg Clayton uint64_t m_identifier; 141944d93782SGreg Clayton Type m_type; 142044d93782SGreg Clayton int m_key_value; 142144d93782SGreg Clayton int m_start_col; 142244d93782SGreg Clayton int m_max_submenu_name_length; 142344d93782SGreg Clayton int m_max_submenu_key_name_length; 142444d93782SGreg Clayton int m_selected; 142544d93782SGreg Clayton Menu *m_parent; 142644d93782SGreg Clayton Menus m_submenus; 142744d93782SGreg Clayton WindowSP m_menu_window_sp; 142844d93782SGreg Clayton MenuActionResult m_canned_result; 142944d93782SGreg Clayton MenuDelegateSP m_delegate_sp; 143044d93782SGreg Clayton }; 143144d93782SGreg Clayton 143244d93782SGreg Clayton // Menubar or separator constructor 1433b9c1b51eSKate Stone Menu::Menu(Type type) 1434b9c1b51eSKate Stone : m_name(), m_key_name(), m_identifier(0), m_type(type), m_key_value(0), 1435b9c1b51eSKate Stone m_start_col(0), m_max_submenu_name_length(0), 1436b9c1b51eSKate Stone m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr), 1437b9c1b51eSKate Stone m_submenus(), m_canned_result(MenuActionResult::NotHandled), 1438b9c1b51eSKate Stone m_delegate_sp() {} 143944d93782SGreg Clayton 144044d93782SGreg Clayton // Menuitem constructor 1441b9c1b51eSKate Stone Menu::Menu(const char *name, const char *key_name, int key_value, 1442b9c1b51eSKate Stone uint64_t identifier) 1443b9c1b51eSKate Stone : m_name(), m_key_name(), m_identifier(identifier), m_type(Type::Invalid), 1444b9c1b51eSKate Stone m_key_value(key_value), m_start_col(0), m_max_submenu_name_length(0), 1445b9c1b51eSKate Stone m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr), 1446b9c1b51eSKate Stone m_submenus(), m_canned_result(MenuActionResult::NotHandled), 1447b9c1b51eSKate Stone m_delegate_sp() { 1448b9c1b51eSKate Stone if (name && name[0]) { 144944d93782SGreg Clayton m_name = name; 145044d93782SGreg Clayton m_type = Type::Item; 145144d93782SGreg Clayton if (key_name && key_name[0]) 145244d93782SGreg Clayton m_key_name = key_name; 1453b9c1b51eSKate Stone } else { 145444d93782SGreg Clayton m_type = Type::Separator; 145544d93782SGreg Clayton } 145644d93782SGreg Clayton } 145744d93782SGreg Clayton 1458b9c1b51eSKate Stone void Menu::RecalculateNameLengths() { 145944d93782SGreg Clayton m_max_submenu_name_length = 0; 146044d93782SGreg Clayton m_max_submenu_key_name_length = 0; 146144d93782SGreg Clayton Menus &submenus = GetSubmenus(); 146244d93782SGreg Clayton const size_t num_submenus = submenus.size(); 1463b9c1b51eSKate Stone for (size_t i = 0; i < num_submenus; ++i) { 146444d93782SGreg Clayton Menu *submenu = submenus[i].get(); 14653985c8c6SSaleem Abdulrasool if (static_cast<size_t>(m_max_submenu_name_length) < submenu->m_name.size()) 146644d93782SGreg Clayton m_max_submenu_name_length = submenu->m_name.size(); 1467b9c1b51eSKate Stone if (static_cast<size_t>(m_max_submenu_key_name_length) < 1468b9c1b51eSKate Stone submenu->m_key_name.size()) 146944d93782SGreg Clayton m_max_submenu_key_name_length = submenu->m_key_name.size(); 147044d93782SGreg Clayton } 147144d93782SGreg Clayton } 147244d93782SGreg Clayton 1473b9c1b51eSKate Stone void Menu::AddSubmenu(const MenuSP &menu_sp) { 147444d93782SGreg Clayton menu_sp->m_parent = this; 14753985c8c6SSaleem Abdulrasool if (static_cast<size_t>(m_max_submenu_name_length) < menu_sp->m_name.size()) 147644d93782SGreg Clayton m_max_submenu_name_length = menu_sp->m_name.size(); 1477b9c1b51eSKate Stone if (static_cast<size_t>(m_max_submenu_key_name_length) < 1478b9c1b51eSKate Stone menu_sp->m_key_name.size()) 147944d93782SGreg Clayton m_max_submenu_key_name_length = menu_sp->m_key_name.size(); 148044d93782SGreg Clayton m_submenus.push_back(menu_sp); 148144d93782SGreg Clayton } 148244d93782SGreg Clayton 1483b9c1b51eSKate Stone void Menu::DrawMenuTitle(Window &window, bool highlight) { 1484b9c1b51eSKate Stone if (m_type == Type::Separator) { 148544d93782SGreg Clayton window.MoveCursor(0, window.GetCursorY()); 148644d93782SGreg Clayton window.PutChar(ACS_LTEE); 148744d93782SGreg Clayton int width = window.GetWidth(); 1488b9c1b51eSKate Stone if (width > 2) { 148944d93782SGreg Clayton width -= 2; 14903985c8c6SSaleem Abdulrasool for (int i = 0; i < width; ++i) 149144d93782SGreg Clayton window.PutChar(ACS_HLINE); 149244d93782SGreg Clayton } 149344d93782SGreg Clayton window.PutChar(ACS_RTEE); 1494b9c1b51eSKate Stone } else { 149544d93782SGreg Clayton const int shortcut_key = m_key_value; 149644d93782SGreg Clayton bool underlined_shortcut = false; 149744d93782SGreg Clayton const attr_t hilgight_attr = A_REVERSE; 149844d93782SGreg Clayton if (highlight) 149944d93782SGreg Clayton window.AttributeOn(hilgight_attr); 1500b9c1b51eSKate Stone if (isprint(shortcut_key)) { 150144d93782SGreg Clayton size_t lower_pos = m_name.find(tolower(shortcut_key)); 150244d93782SGreg Clayton size_t upper_pos = m_name.find(toupper(shortcut_key)); 150344d93782SGreg Clayton const char *name = m_name.c_str(); 150444d93782SGreg Clayton size_t pos = std::min<size_t>(lower_pos, upper_pos); 1505b9c1b51eSKate Stone if (pos != std::string::npos) { 150644d93782SGreg Clayton underlined_shortcut = true; 1507b9c1b51eSKate Stone if (pos > 0) { 150844d93782SGreg Clayton window.PutCString(name, pos); 150944d93782SGreg Clayton name += pos; 151044d93782SGreg Clayton } 151144d93782SGreg Clayton const attr_t shortcut_attr = A_UNDERLINE | A_BOLD; 151244d93782SGreg Clayton window.AttributeOn(shortcut_attr); 151344d93782SGreg Clayton window.PutChar(name[0]); 151444d93782SGreg Clayton window.AttributeOff(shortcut_attr); 151544d93782SGreg Clayton name++; 151644d93782SGreg Clayton if (name[0]) 151744d93782SGreg Clayton window.PutCString(name); 151844d93782SGreg Clayton } 151944d93782SGreg Clayton } 152044d93782SGreg Clayton 1521b9c1b51eSKate Stone if (!underlined_shortcut) { 152244d93782SGreg Clayton window.PutCString(m_name.c_str()); 152344d93782SGreg Clayton } 152444d93782SGreg Clayton 152544d93782SGreg Clayton if (highlight) 152644d93782SGreg Clayton window.AttributeOff(hilgight_attr); 152744d93782SGreg Clayton 1528b9c1b51eSKate Stone if (m_key_name.empty()) { 1529b9c1b51eSKate Stone if (!underlined_shortcut && isprint(m_key_value)) { 153044d93782SGreg Clayton window.AttributeOn(COLOR_PAIR(3)); 153144d93782SGreg Clayton window.Printf(" (%c)", m_key_value); 153244d93782SGreg Clayton window.AttributeOff(COLOR_PAIR(3)); 153344d93782SGreg Clayton } 1534b9c1b51eSKate Stone } else { 153544d93782SGreg Clayton window.AttributeOn(COLOR_PAIR(3)); 153644d93782SGreg Clayton window.Printf(" (%s)", m_key_name.c_str()); 153744d93782SGreg Clayton window.AttributeOff(COLOR_PAIR(3)); 153844d93782SGreg Clayton } 153944d93782SGreg Clayton } 154044d93782SGreg Clayton } 154144d93782SGreg Clayton 1542b9c1b51eSKate Stone bool Menu::WindowDelegateDraw(Window &window, bool force) { 154344d93782SGreg Clayton Menus &submenus = GetSubmenus(); 154444d93782SGreg Clayton const size_t num_submenus = submenus.size(); 154544d93782SGreg Clayton const int selected_idx = GetSelectedSubmenuIndex(); 154644d93782SGreg Clayton Menu::Type menu_type = GetType(); 1547b9c1b51eSKate Stone switch (menu_type) { 1548b9c1b51eSKate Stone case Menu::Type::Bar: { 154944d93782SGreg Clayton window.SetBackground(2); 155044d93782SGreg Clayton window.MoveCursor(0, 0); 1551b9c1b51eSKate Stone for (size_t i = 0; i < num_submenus; ++i) { 155244d93782SGreg Clayton Menu *menu = submenus[i].get(); 155344d93782SGreg Clayton if (i > 0) 155444d93782SGreg Clayton window.PutChar(' '); 155544d93782SGreg Clayton menu->SetStartingColumn(window.GetCursorX()); 155644d93782SGreg Clayton window.PutCString("| "); 155744d93782SGreg Clayton menu->DrawMenuTitle(window, false); 155844d93782SGreg Clayton } 155944d93782SGreg Clayton window.PutCString(" |"); 156044d93782SGreg Clayton window.DeferredRefresh(); 1561b9c1b51eSKate Stone } break; 156244d93782SGreg Clayton 1563b9c1b51eSKate Stone case Menu::Type::Item: { 156444d93782SGreg Clayton int y = 1; 156544d93782SGreg Clayton int x = 3; 156644d93782SGreg Clayton // Draw the menu 156744d93782SGreg Clayton int cursor_x = 0; 156844d93782SGreg Clayton int cursor_y = 0; 156944d93782SGreg Clayton window.Erase(); 157044d93782SGreg Clayton window.SetBackground(2); 157144d93782SGreg Clayton window.Box(); 1572b9c1b51eSKate Stone for (size_t i = 0; i < num_submenus; ++i) { 1573b9c1b51eSKate Stone const bool is_selected = (i == static_cast<size_t>(selected_idx)); 157444d93782SGreg Clayton window.MoveCursor(x, y + i); 1575b9c1b51eSKate Stone if (is_selected) { 157644d93782SGreg Clayton // Remember where we want the cursor to be 157744d93782SGreg Clayton cursor_x = x - 1; 157844d93782SGreg Clayton cursor_y = y + i; 157944d93782SGreg Clayton } 158044d93782SGreg Clayton submenus[i]->DrawMenuTitle(window, is_selected); 158144d93782SGreg Clayton } 158244d93782SGreg Clayton window.MoveCursor(cursor_x, cursor_y); 158344d93782SGreg Clayton window.DeferredRefresh(); 1584b9c1b51eSKate Stone } break; 158544d93782SGreg Clayton 158644d93782SGreg Clayton default: 158744d93782SGreg Clayton case Menu::Type::Separator: 158844d93782SGreg Clayton break; 158944d93782SGreg Clayton } 159044d93782SGreg Clayton return true; // Drawing handled... 159144d93782SGreg Clayton } 159244d93782SGreg Clayton 1593b9c1b51eSKate Stone HandleCharResult Menu::WindowDelegateHandleChar(Window &window, int key) { 159444d93782SGreg Clayton HandleCharResult result = eKeyNotHandled; 159544d93782SGreg Clayton 159644d93782SGreg Clayton Menus &submenus = GetSubmenus(); 159744d93782SGreg Clayton const size_t num_submenus = submenus.size(); 159844d93782SGreg Clayton const int selected_idx = GetSelectedSubmenuIndex(); 159944d93782SGreg Clayton Menu::Type menu_type = GetType(); 1600b9c1b51eSKate Stone if (menu_type == Menu::Type::Bar) { 160144d93782SGreg Clayton MenuSP run_menu_sp; 1602b9c1b51eSKate Stone switch (key) { 160344d93782SGreg Clayton case KEY_DOWN: 160444d93782SGreg Clayton case KEY_UP: 160544d93782SGreg Clayton // Show last menu or first menu 16063985c8c6SSaleem Abdulrasool if (selected_idx < static_cast<int>(num_submenus)) 160744d93782SGreg Clayton run_menu_sp = submenus[selected_idx]; 160844d93782SGreg Clayton else if (!submenus.empty()) 160944d93782SGreg Clayton run_menu_sp = submenus.front(); 161044d93782SGreg Clayton result = eKeyHandled; 161144d93782SGreg Clayton break; 161244d93782SGreg Clayton 161344d93782SGreg Clayton case KEY_RIGHT: 161444d93782SGreg Clayton ++m_selected; 16153985c8c6SSaleem Abdulrasool if (m_selected >= static_cast<int>(num_submenus)) 161644d93782SGreg Clayton m_selected = 0; 16173985c8c6SSaleem Abdulrasool if (m_selected < static_cast<int>(num_submenus)) 161844d93782SGreg Clayton run_menu_sp = submenus[m_selected]; 161944d93782SGreg Clayton else if (!submenus.empty()) 162044d93782SGreg Clayton run_menu_sp = submenus.front(); 162144d93782SGreg Clayton result = eKeyHandled; 162244d93782SGreg Clayton break; 162344d93782SGreg Clayton 162444d93782SGreg Clayton case KEY_LEFT: 162544d93782SGreg Clayton --m_selected; 162644d93782SGreg Clayton if (m_selected < 0) 162744d93782SGreg Clayton m_selected = num_submenus - 1; 16283985c8c6SSaleem Abdulrasool if (m_selected < static_cast<int>(num_submenus)) 162944d93782SGreg Clayton run_menu_sp = submenus[m_selected]; 163044d93782SGreg Clayton else if (!submenus.empty()) 163144d93782SGreg Clayton run_menu_sp = submenus.front(); 163244d93782SGreg Clayton result = eKeyHandled; 163344d93782SGreg Clayton break; 163444d93782SGreg Clayton 163544d93782SGreg Clayton default: 1636b9c1b51eSKate Stone for (size_t i = 0; i < num_submenus; ++i) { 1637b9c1b51eSKate Stone if (submenus[i]->GetKeyValue() == key) { 163844d93782SGreg Clayton SetSelectedSubmenuIndex(i); 163944d93782SGreg Clayton run_menu_sp = submenus[i]; 164044d93782SGreg Clayton result = eKeyHandled; 164144d93782SGreg Clayton break; 164244d93782SGreg Clayton } 164344d93782SGreg Clayton } 164444d93782SGreg Clayton break; 164544d93782SGreg Clayton } 164644d93782SGreg Clayton 1647b9c1b51eSKate Stone if (run_menu_sp) { 164805097246SAdrian Prantl // Run the action on this menu in case we need to populate the menu with 164905097246SAdrian Prantl // dynamic content and also in case check marks, and any other menu 165005097246SAdrian Prantl // decorations need to be calculated 165144d93782SGreg Clayton if (run_menu_sp->Action() == MenuActionResult::Quit) 165244d93782SGreg Clayton return eQuitApplication; 165344d93782SGreg Clayton 165444d93782SGreg Clayton Rect menu_bounds; 165544d93782SGreg Clayton menu_bounds.origin.x = run_menu_sp->GetStartingColumn(); 165644d93782SGreg Clayton menu_bounds.origin.y = 1; 165744d93782SGreg Clayton menu_bounds.size.width = run_menu_sp->GetDrawWidth(); 165844d93782SGreg Clayton menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2; 165944d93782SGreg Clayton if (m_menu_window_sp) 166044d93782SGreg Clayton window.GetParent()->RemoveSubWindow(m_menu_window_sp.get()); 166144d93782SGreg Clayton 1662b9c1b51eSKate Stone m_menu_window_sp = window.GetParent()->CreateSubWindow( 1663b9c1b51eSKate Stone run_menu_sp->GetName().c_str(), menu_bounds, true); 166444d93782SGreg Clayton m_menu_window_sp->SetDelegate(run_menu_sp); 166544d93782SGreg Clayton } 1666b9c1b51eSKate Stone } else if (menu_type == Menu::Type::Item) { 1667b9c1b51eSKate Stone switch (key) { 166844d93782SGreg Clayton case KEY_DOWN: 1669b9c1b51eSKate Stone if (m_submenus.size() > 1) { 167044d93782SGreg Clayton const int start_select = m_selected; 1671b9c1b51eSKate Stone while (++m_selected != start_select) { 16723985c8c6SSaleem Abdulrasool if (static_cast<size_t>(m_selected) >= num_submenus) 167344d93782SGreg Clayton m_selected = 0; 167444d93782SGreg Clayton if (m_submenus[m_selected]->GetType() == Type::Separator) 167544d93782SGreg Clayton continue; 167644d93782SGreg Clayton else 167744d93782SGreg Clayton break; 167844d93782SGreg Clayton } 167944d93782SGreg Clayton return eKeyHandled; 168044d93782SGreg Clayton } 168144d93782SGreg Clayton break; 168244d93782SGreg Clayton 168344d93782SGreg Clayton case KEY_UP: 1684b9c1b51eSKate Stone if (m_submenus.size() > 1) { 168544d93782SGreg Clayton const int start_select = m_selected; 1686b9c1b51eSKate Stone while (--m_selected != start_select) { 16873985c8c6SSaleem Abdulrasool if (m_selected < static_cast<int>(0)) 168844d93782SGreg Clayton m_selected = num_submenus - 1; 168944d93782SGreg Clayton if (m_submenus[m_selected]->GetType() == Type::Separator) 169044d93782SGreg Clayton continue; 169144d93782SGreg Clayton else 169244d93782SGreg Clayton break; 169344d93782SGreg Clayton } 169444d93782SGreg Clayton return eKeyHandled; 169544d93782SGreg Clayton } 169644d93782SGreg Clayton break; 169744d93782SGreg Clayton 169844d93782SGreg Clayton case KEY_RETURN: 1699b9c1b51eSKate Stone if (static_cast<size_t>(selected_idx) < num_submenus) { 170044d93782SGreg Clayton if (submenus[selected_idx]->Action() == MenuActionResult::Quit) 170144d93782SGreg Clayton return eQuitApplication; 170244d93782SGreg Clayton window.GetParent()->RemoveSubWindow(&window); 170344d93782SGreg Clayton return eKeyHandled; 170444d93782SGreg Clayton } 170544d93782SGreg Clayton break; 170644d93782SGreg Clayton 1707b9c1b51eSKate Stone case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in 1708b9c1b51eSKate Stone // case other chars are entered for escaped sequences 170944d93782SGreg Clayton window.GetParent()->RemoveSubWindow(&window); 171044d93782SGreg Clayton return eKeyHandled; 171144d93782SGreg Clayton 171244d93782SGreg Clayton default: 1713b9c1b51eSKate Stone for (size_t i = 0; i < num_submenus; ++i) { 171444d93782SGreg Clayton Menu *menu = submenus[i].get(); 1715b9c1b51eSKate Stone if (menu->GetKeyValue() == key) { 171644d93782SGreg Clayton SetSelectedSubmenuIndex(i); 171744d93782SGreg Clayton window.GetParent()->RemoveSubWindow(&window); 171844d93782SGreg Clayton if (menu->Action() == MenuActionResult::Quit) 171944d93782SGreg Clayton return eQuitApplication; 172044d93782SGreg Clayton return eKeyHandled; 172144d93782SGreg Clayton } 172244d93782SGreg Clayton } 172344d93782SGreg Clayton break; 172444d93782SGreg Clayton } 1725b9c1b51eSKate Stone } else if (menu_type == Menu::Type::Separator) { 172644d93782SGreg Clayton } 172744d93782SGreg Clayton return result; 172844d93782SGreg Clayton } 172944d93782SGreg Clayton 1730b9c1b51eSKate Stone class Application { 173144d93782SGreg Clayton public: 1732b9c1b51eSKate Stone Application(FILE *in, FILE *out) 1733b9c1b51eSKate Stone : m_window_sp(), m_screen(nullptr), m_in(in), m_out(out) {} 173444d93782SGreg Clayton 1735b9c1b51eSKate Stone ~Application() { 173644d93782SGreg Clayton m_window_delegates.clear(); 173744d93782SGreg Clayton m_window_sp.reset(); 1738b9c1b51eSKate Stone if (m_screen) { 173944d93782SGreg Clayton ::delscreen(m_screen); 1740c5dac77aSEugene Zelenko m_screen = nullptr; 174144d93782SGreg Clayton } 174244d93782SGreg Clayton } 174344d93782SGreg Clayton 1744b9c1b51eSKate Stone void Initialize() { 174544d93782SGreg Clayton ::setlocale(LC_ALL, ""); 174644d93782SGreg Clayton ::setlocale(LC_CTYPE, ""); 1747c5dac77aSEugene Zelenko m_screen = ::newterm(nullptr, m_out, m_in); 174844d93782SGreg Clayton ::start_color(); 174944d93782SGreg Clayton ::curs_set(0); 175044d93782SGreg Clayton ::noecho(); 175144d93782SGreg Clayton ::keypad(stdscr, TRUE); 175244d93782SGreg Clayton } 175344d93782SGreg Clayton 1754b9c1b51eSKate Stone void Terminate() { ::endwin(); } 175544d93782SGreg Clayton 1756b9c1b51eSKate Stone void Run(Debugger &debugger) { 175744d93782SGreg Clayton bool done = false; 175844d93782SGreg Clayton int delay_in_tenths_of_a_second = 1; 175944d93782SGreg Clayton 176005097246SAdrian Prantl // Alas the threading model in curses is a bit lame so we need to resort to 176105097246SAdrian Prantl // polling every 0.5 seconds. We could poll for stdin ourselves and then 176205097246SAdrian Prantl // pass the keys down but then we need to translate all of the escape 176305097246SAdrian Prantl // sequences ourselves. So we resort to polling for input because we need 176405097246SAdrian Prantl // to receive async process events while in this loop. 176544d93782SGreg Clayton 1766b9c1b51eSKate Stone halfdelay(delay_in_tenths_of_a_second); // Poll using some number of tenths 1767b9c1b51eSKate Stone // of seconds seconds when calling 1768b9c1b51eSKate Stone // Window::GetChar() 176944d93782SGreg Clayton 1770b9c1b51eSKate Stone ListenerSP listener_sp( 1771b9c1b51eSKate Stone Listener::MakeListener("lldb.IOHandler.curses.Application")); 177244d93782SGreg Clayton ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass()); 177344d93782SGreg Clayton ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass()); 177444d93782SGreg Clayton ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass()); 177544d93782SGreg Clayton debugger.EnableForwardEvents(listener_sp); 177644d93782SGreg Clayton 177744d93782SGreg Clayton bool update = true; 177844d93782SGreg Clayton #if defined(__APPLE__) 177944d93782SGreg Clayton std::deque<int> escape_chars; 178044d93782SGreg Clayton #endif 178144d93782SGreg Clayton 1782b9c1b51eSKate Stone while (!done) { 1783b9c1b51eSKate Stone if (update) { 178444d93782SGreg Clayton m_window_sp->Draw(false); 178505097246SAdrian Prantl // All windows should be calling Window::DeferredRefresh() instead of 178605097246SAdrian Prantl // Window::Refresh() so we can do a single update and avoid any screen 178705097246SAdrian Prantl // blinking 178844d93782SGreg Clayton update_panels(); 178944d93782SGreg Clayton 1790b9c1b51eSKate Stone // Cursor hiding isn't working on MacOSX, so hide it in the top left 1791b9c1b51eSKate Stone // corner 179244d93782SGreg Clayton m_window_sp->MoveCursor(0, 0); 179344d93782SGreg Clayton 179444d93782SGreg Clayton doupdate(); 179544d93782SGreg Clayton update = false; 179644d93782SGreg Clayton } 179744d93782SGreg Clayton 179844d93782SGreg Clayton #if defined(__APPLE__) 179905097246SAdrian Prantl // Terminal.app doesn't map its function keys correctly, F1-F4 default 180005097246SAdrian Prantl // to: \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if 1801b9c1b51eSKate Stone // possible 180244d93782SGreg Clayton int ch; 180344d93782SGreg Clayton if (escape_chars.empty()) 180444d93782SGreg Clayton ch = m_window_sp->GetChar(); 1805b9c1b51eSKate Stone else { 180644d93782SGreg Clayton ch = escape_chars.front(); 180744d93782SGreg Clayton escape_chars.pop_front(); 180844d93782SGreg Clayton } 1809b9c1b51eSKate Stone if (ch == KEY_ESCAPE) { 181044d93782SGreg Clayton int ch2 = m_window_sp->GetChar(); 1811b9c1b51eSKate Stone if (ch2 == 'O') { 181244d93782SGreg Clayton int ch3 = m_window_sp->GetChar(); 1813b9c1b51eSKate Stone switch (ch3) { 1814b9c1b51eSKate Stone case 'P': 1815b9c1b51eSKate Stone ch = KEY_F(1); 1816b9c1b51eSKate Stone break; 1817b9c1b51eSKate Stone case 'Q': 1818b9c1b51eSKate Stone ch = KEY_F(2); 1819b9c1b51eSKate Stone break; 1820b9c1b51eSKate Stone case 'R': 1821b9c1b51eSKate Stone ch = KEY_F(3); 1822b9c1b51eSKate Stone break; 1823b9c1b51eSKate Stone case 'S': 1824b9c1b51eSKate Stone ch = KEY_F(4); 1825b9c1b51eSKate Stone break; 182644d93782SGreg Clayton default: 182744d93782SGreg Clayton escape_chars.push_back(ch2); 182844d93782SGreg Clayton if (ch3 != -1) 182944d93782SGreg Clayton escape_chars.push_back(ch3); 183044d93782SGreg Clayton break; 183144d93782SGreg Clayton } 1832b9c1b51eSKate Stone } else if (ch2 != -1) 183344d93782SGreg Clayton escape_chars.push_back(ch2); 183444d93782SGreg Clayton } 183544d93782SGreg Clayton #else 183644d93782SGreg Clayton int ch = m_window_sp->GetChar(); 183744d93782SGreg Clayton 183844d93782SGreg Clayton #endif 1839b9c1b51eSKate Stone if (ch == -1) { 1840b9c1b51eSKate Stone if (feof(m_in) || ferror(m_in)) { 184144d93782SGreg Clayton done = true; 1842b9c1b51eSKate Stone } else { 184344d93782SGreg Clayton // Just a timeout from using halfdelay(), check for events 184444d93782SGreg Clayton EventSP event_sp; 1845b9c1b51eSKate Stone while (listener_sp->PeekAtNextEvent()) { 1846d35031e1SPavel Labath listener_sp->GetEvent(event_sp, std::chrono::seconds(0)); 184744d93782SGreg Clayton 1848b9c1b51eSKate Stone if (event_sp) { 184944d93782SGreg Clayton Broadcaster *broadcaster = event_sp->GetBroadcaster(); 1850b9c1b51eSKate Stone if (broadcaster) { 185144d93782SGreg Clayton // uint32_t event_type = event_sp->GetType(); 1852b9c1b51eSKate Stone ConstString broadcaster_class( 1853b9c1b51eSKate Stone broadcaster->GetBroadcasterClass()); 1854b9c1b51eSKate Stone if (broadcaster_class == broadcaster_class_process) { 1855b9c1b51eSKate Stone debugger.GetCommandInterpreter().UpdateExecutionContext( 1856b9c1b51eSKate Stone nullptr); 185744d93782SGreg Clayton update = true; 185844d93782SGreg Clayton continue; // Don't get any key, just update our view 185944d93782SGreg Clayton } 186044d93782SGreg Clayton } 186144d93782SGreg Clayton } 186244d93782SGreg Clayton } 186344d93782SGreg Clayton } 1864b9c1b51eSKate Stone } else { 186544d93782SGreg Clayton HandleCharResult key_result = m_window_sp->HandleChar(ch); 1866b9c1b51eSKate Stone switch (key_result) { 186744d93782SGreg Clayton case eKeyHandled: 1868c5dac77aSEugene Zelenko debugger.GetCommandInterpreter().UpdateExecutionContext(nullptr); 186944d93782SGreg Clayton update = true; 187044d93782SGreg Clayton break; 187144d93782SGreg Clayton case eKeyNotHandled: 187244d93782SGreg Clayton break; 187344d93782SGreg Clayton case eQuitApplication: 187444d93782SGreg Clayton done = true; 187544d93782SGreg Clayton break; 187644d93782SGreg Clayton } 187744d93782SGreg Clayton } 187844d93782SGreg Clayton } 187944d93782SGreg Clayton 188044d93782SGreg Clayton debugger.CancelForwardEvents(listener_sp); 188144d93782SGreg Clayton } 188244d93782SGreg Clayton 1883b9c1b51eSKate Stone WindowSP &GetMainWindow() { 188444d93782SGreg Clayton if (!m_window_sp) 188544d93782SGreg Clayton m_window_sp.reset(new Window("main", stdscr, false)); 188644d93782SGreg Clayton return m_window_sp; 188744d93782SGreg Clayton } 188844d93782SGreg Clayton 1889b9c1b51eSKate Stone WindowDelegates &GetWindowDelegates() { return m_window_delegates; } 189044d93782SGreg Clayton 189144d93782SGreg Clayton protected: 189244d93782SGreg Clayton WindowSP m_window_sp; 189344d93782SGreg Clayton WindowDelegates m_window_delegates; 189444d93782SGreg Clayton SCREEN *m_screen; 189544d93782SGreg Clayton FILE *m_in; 189644d93782SGreg Clayton FILE *m_out; 189744d93782SGreg Clayton }; 189844d93782SGreg Clayton 189944d93782SGreg Clayton } // namespace curses 190044d93782SGreg Clayton 190144d93782SGreg Clayton using namespace curses; 190244d93782SGreg Clayton 1903b9c1b51eSKate Stone struct Row { 19048369b28dSGreg Clayton ValueObjectManager value; 190544d93782SGreg Clayton Row *parent; 19068369b28dSGreg Clayton // The process stop ID when the children were calculated. 19078369b28dSGreg Clayton uint32_t children_stop_id; 190844d93782SGreg Clayton int row_idx; 190944d93782SGreg Clayton int x; 191044d93782SGreg Clayton int y; 191144d93782SGreg Clayton bool might_have_children; 191244d93782SGreg Clayton bool expanded; 191344d93782SGreg Clayton bool calculated_children; 191444d93782SGreg Clayton std::vector<Row> children; 191544d93782SGreg Clayton 1916b9c1b51eSKate Stone Row(const ValueObjectSP &v, Row *p) 19178369b28dSGreg Clayton : value(v, lldb::eDynamicDontRunTarget, true), parent(p), row_idx(0), 19188369b28dSGreg Clayton x(1), y(1), might_have_children(v ? v->MightHaveChildren() : false), 1919b9c1b51eSKate Stone expanded(false), calculated_children(false), children() {} 192044d93782SGreg Clayton 1921b9c1b51eSKate Stone size_t GetDepth() const { 192244d93782SGreg Clayton if (parent) 192344d93782SGreg Clayton return 1 + parent->GetDepth(); 192444d93782SGreg Clayton return 0; 192544d93782SGreg Clayton } 192644d93782SGreg Clayton 1927b9c1b51eSKate Stone void Expand() { 192844d93782SGreg Clayton expanded = true; 19298369b28dSGreg Clayton } 19308369b28dSGreg Clayton 19318369b28dSGreg Clayton std::vector<Row> &GetChildren() { 19328369b28dSGreg Clayton ProcessSP process_sp = value.GetProcessSP(); 19338369b28dSGreg Clayton auto stop_id = process_sp->GetStopID(); 19348369b28dSGreg Clayton if (process_sp && stop_id != children_stop_id) { 19358369b28dSGreg Clayton children_stop_id = stop_id; 19368369b28dSGreg Clayton calculated_children = false; 19378369b28dSGreg Clayton } 1938b9c1b51eSKate Stone if (!calculated_children) { 19398369b28dSGreg Clayton children.clear(); 194044d93782SGreg Clayton calculated_children = true; 19418369b28dSGreg Clayton ValueObjectSP valobj = value.GetSP(); 1942b9c1b51eSKate Stone if (valobj) { 194344d93782SGreg Clayton const size_t num_children = valobj->GetNumChildren(); 1944b9c1b51eSKate Stone for (size_t i = 0; i < num_children; ++i) { 194544d93782SGreg Clayton children.push_back(Row(valobj->GetChildAtIndex(i, true), this)); 194644d93782SGreg Clayton } 194744d93782SGreg Clayton } 194844d93782SGreg Clayton } 19498369b28dSGreg Clayton return children; 195044d93782SGreg Clayton } 195144d93782SGreg Clayton 19528369b28dSGreg Clayton void Unexpand() { 19538369b28dSGreg Clayton expanded = false; 19548369b28dSGreg Clayton calculated_children = false; 19558369b28dSGreg Clayton children.clear(); 19568369b28dSGreg Clayton } 195744d93782SGreg Clayton 1958b9c1b51eSKate Stone void DrawTree(Window &window) { 195944d93782SGreg Clayton if (parent) 196044d93782SGreg Clayton parent->DrawTreeForChild(window, this, 0); 196144d93782SGreg Clayton 1962b9c1b51eSKate Stone if (might_have_children) { 196305097246SAdrian Prantl // It we can get UTF8 characters to work we should try to use the 196405097246SAdrian Prantl // "symbol" UTF8 string below 196544d93782SGreg Clayton // const char *symbol = ""; 196644d93782SGreg Clayton // if (row.expanded) 196744d93782SGreg Clayton // symbol = "\xe2\x96\xbd "; 196844d93782SGreg Clayton // else 196944d93782SGreg Clayton // symbol = "\xe2\x96\xb7 "; 197044d93782SGreg Clayton // window.PutCString (symbol); 197144d93782SGreg Clayton 197205097246SAdrian Prantl // The ACS_DARROW and ACS_RARROW don't look very nice they are just a 'v' 197305097246SAdrian Prantl // or '>' character... 197444d93782SGreg Clayton // if (expanded) 197544d93782SGreg Clayton // window.PutChar (ACS_DARROW); 197644d93782SGreg Clayton // else 197744d93782SGreg Clayton // window.PutChar (ACS_RARROW); 197805097246SAdrian Prantl // Since we can't find any good looking right arrow/down arrow symbols, 197905097246SAdrian Prantl // just use a diamond... 198044d93782SGreg Clayton window.PutChar(ACS_DIAMOND); 198144d93782SGreg Clayton window.PutChar(ACS_HLINE); 198244d93782SGreg Clayton } 198344d93782SGreg Clayton } 198444d93782SGreg Clayton 1985b9c1b51eSKate Stone void DrawTreeForChild(Window &window, Row *child, uint32_t reverse_depth) { 198644d93782SGreg Clayton if (parent) 198744d93782SGreg Clayton parent->DrawTreeForChild(window, this, reverse_depth + 1); 198844d93782SGreg Clayton 19898369b28dSGreg Clayton if (&GetChildren().back() == child) { 199044d93782SGreg Clayton // Last child 1991b9c1b51eSKate Stone if (reverse_depth == 0) { 199244d93782SGreg Clayton window.PutChar(ACS_LLCORNER); 199344d93782SGreg Clayton window.PutChar(ACS_HLINE); 1994b9c1b51eSKate Stone } else { 199544d93782SGreg Clayton window.PutChar(' '); 199644d93782SGreg Clayton window.PutChar(' '); 199744d93782SGreg Clayton } 1998b9c1b51eSKate Stone } else { 1999b9c1b51eSKate Stone if (reverse_depth == 0) { 200044d93782SGreg Clayton window.PutChar(ACS_LTEE); 200144d93782SGreg Clayton window.PutChar(ACS_HLINE); 2002b9c1b51eSKate Stone } else { 200344d93782SGreg Clayton window.PutChar(ACS_VLINE); 200444d93782SGreg Clayton window.PutChar(' '); 200544d93782SGreg Clayton } 200644d93782SGreg Clayton } 200744d93782SGreg Clayton } 200844d93782SGreg Clayton }; 200944d93782SGreg Clayton 2010b9c1b51eSKate Stone struct DisplayOptions { 201144d93782SGreg Clayton bool show_types; 201244d93782SGreg Clayton }; 201344d93782SGreg Clayton 201444d93782SGreg Clayton class TreeItem; 201544d93782SGreg Clayton 2016b9c1b51eSKate Stone class TreeDelegate { 201744d93782SGreg Clayton public: 2018c5dac77aSEugene Zelenko TreeDelegate() = default; 2019315b6884SEugene Zelenko virtual ~TreeDelegate() = default; 2020315b6884SEugene Zelenko 202144d93782SGreg Clayton virtual void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) = 0; 202244d93782SGreg Clayton virtual void TreeDelegateGenerateChildren(TreeItem &item) = 0; 2023b9c1b51eSKate Stone virtual bool TreeDelegateItemSelected( 2024b9c1b51eSKate Stone TreeItem &item) = 0; // Return true if we need to update views 202544d93782SGreg Clayton }; 2026315b6884SEugene Zelenko 202744d93782SGreg Clayton typedef std::shared_ptr<TreeDelegate> TreeDelegateSP; 202844d93782SGreg Clayton 2029b9c1b51eSKate Stone class TreeItem { 203044d93782SGreg Clayton public: 2031b9c1b51eSKate Stone TreeItem(TreeItem *parent, TreeDelegate &delegate, bool might_have_children) 2032b9c1b51eSKate Stone : m_parent(parent), m_delegate(delegate), m_user_data(nullptr), 2033b9c1b51eSKate Stone m_identifier(0), m_row_idx(-1), m_children(), 2034b9c1b51eSKate Stone m_might_have_children(might_have_children), m_is_expanded(false) {} 203544d93782SGreg Clayton 2036b9c1b51eSKate Stone TreeItem &operator=(const TreeItem &rhs) { 2037b9c1b51eSKate Stone if (this != &rhs) { 203844d93782SGreg Clayton m_parent = rhs.m_parent; 203944d93782SGreg Clayton m_delegate = rhs.m_delegate; 2040ec990867SGreg Clayton m_user_data = rhs.m_user_data; 204144d93782SGreg Clayton m_identifier = rhs.m_identifier; 204244d93782SGreg Clayton m_row_idx = rhs.m_row_idx; 204344d93782SGreg Clayton m_children = rhs.m_children; 204444d93782SGreg Clayton m_might_have_children = rhs.m_might_have_children; 204544d93782SGreg Clayton m_is_expanded = rhs.m_is_expanded; 204644d93782SGreg Clayton } 204744d93782SGreg Clayton return *this; 204844d93782SGreg Clayton } 204944d93782SGreg Clayton 2050b9c1b51eSKate Stone size_t GetDepth() const { 205144d93782SGreg Clayton if (m_parent) 205244d93782SGreg Clayton return 1 + m_parent->GetDepth(); 205344d93782SGreg Clayton return 0; 205444d93782SGreg Clayton } 205544d93782SGreg Clayton 2056b9c1b51eSKate Stone int GetRowIndex() const { return m_row_idx; } 205744d93782SGreg Clayton 2058b9c1b51eSKate Stone void ClearChildren() { m_children.clear(); } 205944d93782SGreg Clayton 2060b9c1b51eSKate Stone void Resize(size_t n, const TreeItem &t) { m_children.resize(n, t); } 206144d93782SGreg Clayton 2062b9c1b51eSKate Stone TreeItem &operator[](size_t i) { return m_children[i]; } 206344d93782SGreg Clayton 2064b9c1b51eSKate Stone void SetRowIndex(int row_idx) { m_row_idx = row_idx; } 206544d93782SGreg Clayton 2066b9c1b51eSKate Stone size_t GetNumChildren() { 206744d93782SGreg Clayton m_delegate.TreeDelegateGenerateChildren(*this); 206844d93782SGreg Clayton return m_children.size(); 206944d93782SGreg Clayton } 207044d93782SGreg Clayton 2071b9c1b51eSKate Stone void ItemWasSelected() { m_delegate.TreeDelegateItemSelected(*this); } 2072315b6884SEugene Zelenko 2073b9c1b51eSKate Stone void CalculateRowIndexes(int &row_idx) { 207444d93782SGreg Clayton SetRowIndex(row_idx); 207544d93782SGreg Clayton ++row_idx; 207644d93782SGreg Clayton 2077ec990867SGreg Clayton const bool expanded = IsExpanded(); 2078ec990867SGreg Clayton 207905097246SAdrian Prantl // The root item must calculate its children, or we must calculate the 208005097246SAdrian Prantl // number of children if the item is expanded 2081c5dac77aSEugene Zelenko if (m_parent == nullptr || expanded) 208244d93782SGreg Clayton GetNumChildren(); 208344d93782SGreg Clayton 2084b9c1b51eSKate Stone for (auto &item : m_children) { 208544d93782SGreg Clayton if (expanded) 208644d93782SGreg Clayton item.CalculateRowIndexes(row_idx); 208744d93782SGreg Clayton else 208844d93782SGreg Clayton item.SetRowIndex(-1); 208944d93782SGreg Clayton } 209044d93782SGreg Clayton } 209144d93782SGreg Clayton 2092b9c1b51eSKate Stone TreeItem *GetParent() { return m_parent; } 209344d93782SGreg Clayton 2094b9c1b51eSKate Stone bool IsExpanded() const { return m_is_expanded; } 209544d93782SGreg Clayton 2096b9c1b51eSKate Stone void Expand() { m_is_expanded = true; } 209744d93782SGreg Clayton 2098b9c1b51eSKate Stone void Unexpand() { m_is_expanded = false; } 209944d93782SGreg Clayton 2100b9c1b51eSKate Stone bool Draw(Window &window, const int first_visible_row, 2101b9c1b51eSKate Stone const uint32_t selected_row_idx, int &row_idx, int &num_rows_left) { 210244d93782SGreg Clayton if (num_rows_left <= 0) 210344d93782SGreg Clayton return false; 210444d93782SGreg Clayton 2105b9c1b51eSKate Stone if (m_row_idx >= first_visible_row) { 210644d93782SGreg Clayton window.MoveCursor(2, row_idx + 1); 210744d93782SGreg Clayton 210844d93782SGreg Clayton if (m_parent) 210944d93782SGreg Clayton m_parent->DrawTreeForChild(window, this, 0); 211044d93782SGreg Clayton 2111b9c1b51eSKate Stone if (m_might_have_children) { 2112b9c1b51eSKate Stone // It we can get UTF8 characters to work we should try to use the 211305097246SAdrian Prantl // "symbol" UTF8 string below 211444d93782SGreg Clayton // const char *symbol = ""; 211544d93782SGreg Clayton // if (row.expanded) 211644d93782SGreg Clayton // symbol = "\xe2\x96\xbd "; 211744d93782SGreg Clayton // else 211844d93782SGreg Clayton // symbol = "\xe2\x96\xb7 "; 211944d93782SGreg Clayton // window.PutCString (symbol); 212044d93782SGreg Clayton 212144d93782SGreg Clayton // The ACS_DARROW and ACS_RARROW don't look very nice they are just a 212244d93782SGreg Clayton // 'v' or '>' character... 212344d93782SGreg Clayton // if (expanded) 212444d93782SGreg Clayton // window.PutChar (ACS_DARROW); 212544d93782SGreg Clayton // else 212644d93782SGreg Clayton // window.PutChar (ACS_RARROW); 212705097246SAdrian Prantl // Since we can't find any good looking right arrow/down arrow symbols, 212805097246SAdrian Prantl // just use a diamond... 212944d93782SGreg Clayton window.PutChar(ACS_DIAMOND); 213044d93782SGreg Clayton window.PutChar(ACS_HLINE); 213144d93782SGreg Clayton } 2132b9c1b51eSKate Stone bool highlight = (selected_row_idx == static_cast<size_t>(m_row_idx)) && 2133b9c1b51eSKate Stone window.IsActive(); 213444d93782SGreg Clayton 213544d93782SGreg Clayton if (highlight) 213644d93782SGreg Clayton window.AttributeOn(A_REVERSE); 213744d93782SGreg Clayton 213844d93782SGreg Clayton m_delegate.TreeDelegateDrawTreeItem(*this, window); 213944d93782SGreg Clayton 214044d93782SGreg Clayton if (highlight) 214144d93782SGreg Clayton window.AttributeOff(A_REVERSE); 214244d93782SGreg Clayton ++row_idx; 214344d93782SGreg Clayton --num_rows_left; 214444d93782SGreg Clayton } 214544d93782SGreg Clayton 214644d93782SGreg Clayton if (num_rows_left <= 0) 214744d93782SGreg Clayton return false; // We are done drawing... 214844d93782SGreg Clayton 2149b9c1b51eSKate Stone if (IsExpanded()) { 2150b9c1b51eSKate Stone for (auto &item : m_children) { 215105097246SAdrian Prantl // If we displayed all the rows and item.Draw() returns false we are 215205097246SAdrian Prantl // done drawing and can exit this for loop 2153b9c1b51eSKate Stone if (!item.Draw(window, first_visible_row, selected_row_idx, row_idx, 2154b9c1b51eSKate Stone num_rows_left)) 215544d93782SGreg Clayton break; 215644d93782SGreg Clayton } 215744d93782SGreg Clayton } 215844d93782SGreg Clayton return num_rows_left >= 0; // Return true if not done drawing yet 215944d93782SGreg Clayton } 216044d93782SGreg Clayton 2161b9c1b51eSKate Stone void DrawTreeForChild(Window &window, TreeItem *child, 2162b9c1b51eSKate Stone uint32_t reverse_depth) { 216344d93782SGreg Clayton if (m_parent) 216444d93782SGreg Clayton m_parent->DrawTreeForChild(window, this, reverse_depth + 1); 216544d93782SGreg Clayton 2166b9c1b51eSKate Stone if (&m_children.back() == child) { 216744d93782SGreg Clayton // Last child 2168b9c1b51eSKate Stone if (reverse_depth == 0) { 216944d93782SGreg Clayton window.PutChar(ACS_LLCORNER); 217044d93782SGreg Clayton window.PutChar(ACS_HLINE); 2171b9c1b51eSKate Stone } else { 217244d93782SGreg Clayton window.PutChar(' '); 217344d93782SGreg Clayton window.PutChar(' '); 217444d93782SGreg Clayton } 2175b9c1b51eSKate Stone } else { 2176b9c1b51eSKate Stone if (reverse_depth == 0) { 217744d93782SGreg Clayton window.PutChar(ACS_LTEE); 217844d93782SGreg Clayton window.PutChar(ACS_HLINE); 2179b9c1b51eSKate Stone } else { 218044d93782SGreg Clayton window.PutChar(ACS_VLINE); 218144d93782SGreg Clayton window.PutChar(' '); 218244d93782SGreg Clayton } 218344d93782SGreg Clayton } 218444d93782SGreg Clayton } 218544d93782SGreg Clayton 2186b9c1b51eSKate Stone TreeItem *GetItemForRowIndex(uint32_t row_idx) { 21873985c8c6SSaleem Abdulrasool if (static_cast<uint32_t>(m_row_idx) == row_idx) 218844d93782SGreg Clayton return this; 218944d93782SGreg Clayton if (m_children.empty()) 2190c5dac77aSEugene Zelenko return nullptr; 2191b9c1b51eSKate Stone if (IsExpanded()) { 2192b9c1b51eSKate Stone for (auto &item : m_children) { 219344d93782SGreg Clayton TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx); 219444d93782SGreg Clayton if (selected_item_ptr) 219544d93782SGreg Clayton return selected_item_ptr; 219644d93782SGreg Clayton } 219744d93782SGreg Clayton } 2198c5dac77aSEugene Zelenko return nullptr; 219944d93782SGreg Clayton } 220044d93782SGreg Clayton 2201b9c1b51eSKate Stone void *GetUserData() const { return m_user_data; } 2202ec990867SGreg Clayton 2203b9c1b51eSKate Stone void SetUserData(void *user_data) { m_user_data = user_data; } 2204ec990867SGreg Clayton 2205b9c1b51eSKate Stone uint64_t GetIdentifier() const { return m_identifier; } 220644d93782SGreg Clayton 2207b9c1b51eSKate Stone void SetIdentifier(uint64_t identifier) { m_identifier = identifier; } 220844d93782SGreg Clayton 2209b9c1b51eSKate Stone void SetMightHaveChildren(bool b) { m_might_have_children = b; } 2210ec990867SGreg Clayton 221144d93782SGreg Clayton protected: 221244d93782SGreg Clayton TreeItem *m_parent; 221344d93782SGreg Clayton TreeDelegate &m_delegate; 2214ec990867SGreg Clayton void *m_user_data; 221544d93782SGreg Clayton uint64_t m_identifier; 2216b9c1b51eSKate Stone int m_row_idx; // Zero based visible row index, -1 if not visible or for the 2217b9c1b51eSKate Stone // root item 221844d93782SGreg Clayton std::vector<TreeItem> m_children; 221944d93782SGreg Clayton bool m_might_have_children; 222044d93782SGreg Clayton bool m_is_expanded; 222144d93782SGreg Clayton }; 222244d93782SGreg Clayton 2223b9c1b51eSKate Stone class TreeWindowDelegate : public WindowDelegate { 222444d93782SGreg Clayton public: 2225b9c1b51eSKate Stone TreeWindowDelegate(Debugger &debugger, const TreeDelegateSP &delegate_sp) 2226b9c1b51eSKate Stone : m_debugger(debugger), m_delegate_sp(delegate_sp), 2227b9c1b51eSKate Stone m_root(nullptr, *delegate_sp, true), m_selected_item(nullptr), 2228b9c1b51eSKate Stone m_num_rows(0), m_selected_row_idx(0), m_first_visible_row(0), 2229b9c1b51eSKate Stone m_min_x(0), m_min_y(0), m_max_x(0), m_max_y(0) {} 223044d93782SGreg Clayton 2231b9c1b51eSKate Stone int NumVisibleRows() const { return m_max_y - m_min_y; } 223244d93782SGreg Clayton 2233b9c1b51eSKate Stone bool WindowDelegateDraw(Window &window, bool force) override { 2234b9c1b51eSKate Stone ExecutionContext exe_ctx( 2235b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext()); 223644d93782SGreg Clayton Process *process = exe_ctx.GetProcessPtr(); 223744d93782SGreg Clayton 223844d93782SGreg Clayton bool display_content = false; 2239b9c1b51eSKate Stone if (process) { 224044d93782SGreg Clayton StateType state = process->GetState(); 2241b9c1b51eSKate Stone if (StateIsStoppedState(state, true)) { 224244d93782SGreg Clayton // We are stopped, so it is ok to 224344d93782SGreg Clayton display_content = true; 2244b9c1b51eSKate Stone } else if (StateIsRunningState(state)) { 224544d93782SGreg Clayton return true; // Don't do any updating when we are running 224644d93782SGreg Clayton } 224744d93782SGreg Clayton } 224844d93782SGreg Clayton 224944d93782SGreg Clayton m_min_x = 2; 225044d93782SGreg Clayton m_min_y = 1; 225144d93782SGreg Clayton m_max_x = window.GetWidth() - 1; 225244d93782SGreg Clayton m_max_y = window.GetHeight() - 1; 225344d93782SGreg Clayton 225444d93782SGreg Clayton window.Erase(); 225544d93782SGreg Clayton window.DrawTitleBox(window.GetName()); 225644d93782SGreg Clayton 2257b9c1b51eSKate Stone if (display_content) { 225844d93782SGreg Clayton const int num_visible_rows = NumVisibleRows(); 225944d93782SGreg Clayton m_num_rows = 0; 226044d93782SGreg Clayton m_root.CalculateRowIndexes(m_num_rows); 226144d93782SGreg Clayton 226205097246SAdrian Prantl // If we unexpanded while having something selected our total number of 226305097246SAdrian Prantl // rows is less than the num visible rows, then make sure we show all the 226405097246SAdrian Prantl // rows by setting the first visible row accordingly. 226544d93782SGreg Clayton if (m_first_visible_row > 0 && m_num_rows < num_visible_rows) 226644d93782SGreg Clayton m_first_visible_row = 0; 226744d93782SGreg Clayton 226844d93782SGreg Clayton // Make sure the selected row is always visible 226944d93782SGreg Clayton if (m_selected_row_idx < m_first_visible_row) 227044d93782SGreg Clayton m_first_visible_row = m_selected_row_idx; 227144d93782SGreg Clayton else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx) 227244d93782SGreg Clayton m_first_visible_row = m_selected_row_idx - num_visible_rows + 1; 227344d93782SGreg Clayton 227444d93782SGreg Clayton int row_idx = 0; 227544d93782SGreg Clayton int num_rows_left = num_visible_rows; 2276b9c1b51eSKate Stone m_root.Draw(window, m_first_visible_row, m_selected_row_idx, row_idx, 2277b9c1b51eSKate Stone num_rows_left); 227844d93782SGreg Clayton // Get the selected row 227944d93782SGreg Clayton m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); 2280b9c1b51eSKate Stone } else { 2281c5dac77aSEugene Zelenko m_selected_item = nullptr; 228244d93782SGreg Clayton } 228344d93782SGreg Clayton 228444d93782SGreg Clayton window.DeferredRefresh(); 228544d93782SGreg Clayton 228644d93782SGreg Clayton return true; // Drawing handled 228744d93782SGreg Clayton } 228844d93782SGreg Clayton 2289b9c1b51eSKate Stone const char *WindowDelegateGetHelpText() override { 229044d93782SGreg Clayton return "Thread window keyboard shortcuts:"; 229144d93782SGreg Clayton } 229244d93782SGreg Clayton 2293b9c1b51eSKate Stone KeyHelp *WindowDelegateGetKeyHelp() override { 229444d93782SGreg Clayton static curses::KeyHelp g_source_view_key_help[] = { 229544d93782SGreg Clayton {KEY_UP, "Select previous item"}, 229644d93782SGreg Clayton {KEY_DOWN, "Select next item"}, 229744d93782SGreg Clayton {KEY_RIGHT, "Expand the selected item"}, 2298b9c1b51eSKate Stone {KEY_LEFT, 2299b9c1b51eSKate Stone "Unexpand the selected item or select parent if not expanded"}, 230044d93782SGreg Clayton {KEY_PPAGE, "Page up"}, 230144d93782SGreg Clayton {KEY_NPAGE, "Page down"}, 230244d93782SGreg Clayton {'h', "Show help dialog"}, 230344d93782SGreg Clayton {' ', "Toggle item expansion"}, 230444d93782SGreg Clayton {',', "Page up"}, 230544d93782SGreg Clayton {'.', "Page down"}, 2306b9c1b51eSKate Stone {'\0', nullptr}}; 230744d93782SGreg Clayton return g_source_view_key_help; 230844d93782SGreg Clayton } 230944d93782SGreg Clayton 2310b9c1b51eSKate Stone HandleCharResult WindowDelegateHandleChar(Window &window, int c) override { 2311b9c1b51eSKate Stone switch (c) { 231244d93782SGreg Clayton case ',': 231344d93782SGreg Clayton case KEY_PPAGE: 231444d93782SGreg Clayton // Page up key 2315b9c1b51eSKate Stone if (m_first_visible_row > 0) { 231644d93782SGreg Clayton if (m_first_visible_row > m_max_y) 231744d93782SGreg Clayton m_first_visible_row -= m_max_y; 231844d93782SGreg Clayton else 231944d93782SGreg Clayton m_first_visible_row = 0; 232044d93782SGreg Clayton m_selected_row_idx = m_first_visible_row; 232144d93782SGreg Clayton m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); 232244d93782SGreg Clayton if (m_selected_item) 232344d93782SGreg Clayton m_selected_item->ItemWasSelected(); 232444d93782SGreg Clayton } 232544d93782SGreg Clayton return eKeyHandled; 232644d93782SGreg Clayton 232744d93782SGreg Clayton case '.': 232844d93782SGreg Clayton case KEY_NPAGE: 232944d93782SGreg Clayton // Page down key 2330b9c1b51eSKate Stone if (m_num_rows > m_max_y) { 2331b9c1b51eSKate Stone if (m_first_visible_row + m_max_y < m_num_rows) { 233244d93782SGreg Clayton m_first_visible_row += m_max_y; 233344d93782SGreg Clayton m_selected_row_idx = m_first_visible_row; 233444d93782SGreg Clayton m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); 233544d93782SGreg Clayton if (m_selected_item) 233644d93782SGreg Clayton m_selected_item->ItemWasSelected(); 233744d93782SGreg Clayton } 233844d93782SGreg Clayton } 233944d93782SGreg Clayton return eKeyHandled; 234044d93782SGreg Clayton 234144d93782SGreg Clayton case KEY_UP: 2342b9c1b51eSKate Stone if (m_selected_row_idx > 0) { 234344d93782SGreg Clayton --m_selected_row_idx; 234444d93782SGreg Clayton m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); 234544d93782SGreg Clayton if (m_selected_item) 234644d93782SGreg Clayton m_selected_item->ItemWasSelected(); 234744d93782SGreg Clayton } 234844d93782SGreg Clayton return eKeyHandled; 2349315b6884SEugene Zelenko 235044d93782SGreg Clayton case KEY_DOWN: 2351b9c1b51eSKate Stone if (m_selected_row_idx + 1 < m_num_rows) { 235244d93782SGreg Clayton ++m_selected_row_idx; 235344d93782SGreg Clayton m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); 235444d93782SGreg Clayton if (m_selected_item) 235544d93782SGreg Clayton m_selected_item->ItemWasSelected(); 235644d93782SGreg Clayton } 235744d93782SGreg Clayton return eKeyHandled; 235844d93782SGreg Clayton 235944d93782SGreg Clayton case KEY_RIGHT: 2360b9c1b51eSKate Stone if (m_selected_item) { 236144d93782SGreg Clayton if (!m_selected_item->IsExpanded()) 236244d93782SGreg Clayton m_selected_item->Expand(); 236344d93782SGreg Clayton } 236444d93782SGreg Clayton return eKeyHandled; 236544d93782SGreg Clayton 236644d93782SGreg Clayton case KEY_LEFT: 2367b9c1b51eSKate Stone if (m_selected_item) { 236844d93782SGreg Clayton if (m_selected_item->IsExpanded()) 236944d93782SGreg Clayton m_selected_item->Unexpand(); 2370b9c1b51eSKate Stone else if (m_selected_item->GetParent()) { 237144d93782SGreg Clayton m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex(); 237244d93782SGreg Clayton m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); 237344d93782SGreg Clayton if (m_selected_item) 237444d93782SGreg Clayton m_selected_item->ItemWasSelected(); 237544d93782SGreg Clayton } 237644d93782SGreg Clayton } 237744d93782SGreg Clayton return eKeyHandled; 237844d93782SGreg Clayton 237944d93782SGreg Clayton case ' ': 238044d93782SGreg Clayton // Toggle expansion state when SPACE is pressed 2381b9c1b51eSKate Stone if (m_selected_item) { 238244d93782SGreg Clayton if (m_selected_item->IsExpanded()) 238344d93782SGreg Clayton m_selected_item->Unexpand(); 238444d93782SGreg Clayton else 238544d93782SGreg Clayton m_selected_item->Expand(); 238644d93782SGreg Clayton } 238744d93782SGreg Clayton return eKeyHandled; 238844d93782SGreg Clayton 238944d93782SGreg Clayton case 'h': 239044d93782SGreg Clayton window.CreateHelpSubwindow(); 239144d93782SGreg Clayton return eKeyHandled; 239244d93782SGreg Clayton 239344d93782SGreg Clayton default: 239444d93782SGreg Clayton break; 239544d93782SGreg Clayton } 239644d93782SGreg Clayton return eKeyNotHandled; 239744d93782SGreg Clayton } 239844d93782SGreg Clayton 239944d93782SGreg Clayton protected: 240044d93782SGreg Clayton Debugger &m_debugger; 240144d93782SGreg Clayton TreeDelegateSP m_delegate_sp; 240244d93782SGreg Clayton TreeItem m_root; 240344d93782SGreg Clayton TreeItem *m_selected_item; 240444d93782SGreg Clayton int m_num_rows; 240544d93782SGreg Clayton int m_selected_row_idx; 240644d93782SGreg Clayton int m_first_visible_row; 240744d93782SGreg Clayton int m_min_x; 240844d93782SGreg Clayton int m_min_y; 240944d93782SGreg Clayton int m_max_x; 241044d93782SGreg Clayton int m_max_y; 241144d93782SGreg Clayton }; 241244d93782SGreg Clayton 2413b9c1b51eSKate Stone class FrameTreeDelegate : public TreeDelegate { 241444d93782SGreg Clayton public: 2415b9c1b51eSKate Stone FrameTreeDelegate() : TreeDelegate() { 2416b9c1b51eSKate Stone FormatEntity::Parse( 2417b9c1b51eSKate Stone "frame #${frame.index}: {${function.name}${function.pc-offset}}}", 2418554f68d3SGreg Clayton m_format); 241944d93782SGreg Clayton } 242044d93782SGreg Clayton 2421315b6884SEugene Zelenko ~FrameTreeDelegate() override = default; 242244d93782SGreg Clayton 2423b9c1b51eSKate Stone void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override { 2424ec990867SGreg Clayton Thread *thread = (Thread *)item.GetUserData(); 2425b9c1b51eSKate Stone if (thread) { 242644d93782SGreg Clayton const uint64_t frame_idx = item.GetIdentifier(); 2427ec990867SGreg Clayton StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_idx); 2428b9c1b51eSKate Stone if (frame_sp) { 242944d93782SGreg Clayton StreamString strm; 2430b9c1b51eSKate Stone const SymbolContext &sc = 2431b9c1b51eSKate Stone frame_sp->GetSymbolContext(eSymbolContextEverything); 243244d93782SGreg Clayton ExecutionContext exe_ctx(frame_sp); 2433b9c1b51eSKate Stone if (FormatEntity::Format(m_format, strm, &sc, &exe_ctx, nullptr, 2434b9c1b51eSKate Stone nullptr, false, false)) { 243544d93782SGreg Clayton int right_pad = 1; 2436c156427dSZachary Turner window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad); 243744d93782SGreg Clayton } 243844d93782SGreg Clayton } 243944d93782SGreg Clayton } 244044d93782SGreg Clayton } 2441315b6884SEugene Zelenko 2442b9c1b51eSKate Stone void TreeDelegateGenerateChildren(TreeItem &item) override { 244344d93782SGreg Clayton // No children for frames yet... 244444d93782SGreg Clayton } 244544d93782SGreg Clayton 2446b9c1b51eSKate Stone bool TreeDelegateItemSelected(TreeItem &item) override { 2447ec990867SGreg Clayton Thread *thread = (Thread *)item.GetUserData(); 2448b9c1b51eSKate Stone if (thread) { 2449b9c1b51eSKate Stone thread->GetProcess()->GetThreadList().SetSelectedThreadByID( 2450b9c1b51eSKate Stone thread->GetID()); 245144d93782SGreg Clayton const uint64_t frame_idx = item.GetIdentifier(); 2452ec990867SGreg Clayton thread->SetSelectedFrameByIndex(frame_idx); 245344d93782SGreg Clayton return true; 245444d93782SGreg Clayton } 245544d93782SGreg Clayton return false; 245644d93782SGreg Clayton } 2457315b6884SEugene Zelenko 2458554f68d3SGreg Clayton protected: 2459554f68d3SGreg Clayton FormatEntity::Entry m_format; 246044d93782SGreg Clayton }; 246144d93782SGreg Clayton 2462b9c1b51eSKate Stone class ThreadTreeDelegate : public TreeDelegate { 246344d93782SGreg Clayton public: 2464b9c1b51eSKate Stone ThreadTreeDelegate(Debugger &debugger) 2465b9c1b51eSKate Stone : TreeDelegate(), m_debugger(debugger), m_tid(LLDB_INVALID_THREAD_ID), 2466b9c1b51eSKate Stone m_stop_id(UINT32_MAX) { 2467b9c1b51eSKate Stone FormatEntity::Parse("thread #${thread.index}: tid = ${thread.id}{, stop " 2468b9c1b51eSKate Stone "reason = ${thread.stop-reason}}", 2469554f68d3SGreg Clayton m_format); 247044d93782SGreg Clayton } 247144d93782SGreg Clayton 2472315b6884SEugene Zelenko ~ThreadTreeDelegate() override = default; 247344d93782SGreg Clayton 2474b9c1b51eSKate Stone ProcessSP GetProcess() { 2475b9c1b51eSKate Stone return m_debugger.GetCommandInterpreter() 2476b9c1b51eSKate Stone .GetExecutionContext() 2477b9c1b51eSKate Stone .GetProcessSP(); 2478ec990867SGreg Clayton } 2479ec990867SGreg Clayton 2480b9c1b51eSKate Stone ThreadSP GetThread(const TreeItem &item) { 2481ec990867SGreg Clayton ProcessSP process_sp = GetProcess(); 2482ec990867SGreg Clayton if (process_sp) 2483ec990867SGreg Clayton return process_sp->GetThreadList().FindThreadByID(item.GetIdentifier()); 2484ec990867SGreg Clayton return ThreadSP(); 2485ec990867SGreg Clayton } 2486ec990867SGreg Clayton 2487b9c1b51eSKate Stone void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override { 2488ec990867SGreg Clayton ThreadSP thread_sp = GetThread(item); 2489b9c1b51eSKate Stone if (thread_sp) { 249044d93782SGreg Clayton StreamString strm; 249144d93782SGreg Clayton ExecutionContext exe_ctx(thread_sp); 2492b9c1b51eSKate Stone if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr, 2493b9c1b51eSKate Stone nullptr, false, false)) { 249444d93782SGreg Clayton int right_pad = 1; 2495c156427dSZachary Turner window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad); 249644d93782SGreg Clayton } 249744d93782SGreg Clayton } 249844d93782SGreg Clayton } 2499315b6884SEugene Zelenko 2500b9c1b51eSKate Stone void TreeDelegateGenerateChildren(TreeItem &item) override { 2501ec990867SGreg Clayton ProcessSP process_sp = GetProcess(); 2502b9c1b51eSKate Stone if (process_sp && process_sp->IsAlive()) { 250344d93782SGreg Clayton StateType state = process_sp->GetState(); 2504b9c1b51eSKate Stone if (StateIsStoppedState(state, true)) { 2505ec990867SGreg Clayton ThreadSP thread_sp = GetThread(item); 2506b9c1b51eSKate Stone if (thread_sp) { 2507b9c1b51eSKate Stone if (m_stop_id == process_sp->GetStopID() && 2508b9c1b51eSKate Stone thread_sp->GetID() == m_tid) 250944d93782SGreg Clayton return; // Children are already up to date 2510b9c1b51eSKate Stone if (!m_frame_delegate_sp) { 251144d93782SGreg Clayton // Always expand the thread item the first time we show it 2512ec990867SGreg Clayton m_frame_delegate_sp.reset(new FrameTreeDelegate()); 251344d93782SGreg Clayton } 251444d93782SGreg Clayton 251544d93782SGreg Clayton m_stop_id = process_sp->GetStopID(); 251644d93782SGreg Clayton m_tid = thread_sp->GetID(); 251744d93782SGreg Clayton 251844d93782SGreg Clayton TreeItem t(&item, *m_frame_delegate_sp, false); 251944d93782SGreg Clayton size_t num_frames = thread_sp->GetStackFrameCount(); 252044d93782SGreg Clayton item.Resize(num_frames, t); 2521b9c1b51eSKate Stone for (size_t i = 0; i < num_frames; ++i) { 2522ec990867SGreg Clayton item[i].SetUserData(thread_sp.get()); 252344d93782SGreg Clayton item[i].SetIdentifier(i); 252444d93782SGreg Clayton } 252544d93782SGreg Clayton } 252644d93782SGreg Clayton return; 252744d93782SGreg Clayton } 252844d93782SGreg Clayton } 252944d93782SGreg Clayton item.ClearChildren(); 253044d93782SGreg Clayton } 253144d93782SGreg Clayton 2532b9c1b51eSKate Stone bool TreeDelegateItemSelected(TreeItem &item) override { 2533ec990867SGreg Clayton ProcessSP process_sp = GetProcess(); 2534b9c1b51eSKate Stone if (process_sp && process_sp->IsAlive()) { 2535ec990867SGreg Clayton StateType state = process_sp->GetState(); 2536b9c1b51eSKate Stone if (StateIsStoppedState(state, true)) { 2537ec990867SGreg Clayton ThreadSP thread_sp = GetThread(item); 2538b9c1b51eSKate Stone if (thread_sp) { 253944d93782SGreg Clayton ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList(); 2540bb19a13cSSaleem Abdulrasool std::lock_guard<std::recursive_mutex> guard(thread_list.GetMutex()); 254144d93782SGreg Clayton ThreadSP selected_thread_sp = thread_list.GetSelectedThread(); 2542b9c1b51eSKate Stone if (selected_thread_sp->GetID() != thread_sp->GetID()) { 254344d93782SGreg Clayton thread_list.SetSelectedThreadByID(thread_sp->GetID()); 254444d93782SGreg Clayton return true; 254544d93782SGreg Clayton } 254644d93782SGreg Clayton } 2547ec990867SGreg Clayton } 2548ec990867SGreg Clayton } 254944d93782SGreg Clayton return false; 255044d93782SGreg Clayton } 255144d93782SGreg Clayton 255244d93782SGreg Clayton protected: 255344d93782SGreg Clayton Debugger &m_debugger; 255444d93782SGreg Clayton std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp; 255544d93782SGreg Clayton lldb::user_id_t m_tid; 255644d93782SGreg Clayton uint32_t m_stop_id; 2557554f68d3SGreg Clayton FormatEntity::Entry m_format; 255844d93782SGreg Clayton }; 255944d93782SGreg Clayton 2560b9c1b51eSKate Stone class ThreadsTreeDelegate : public TreeDelegate { 2561ec990867SGreg Clayton public: 2562b9c1b51eSKate Stone ThreadsTreeDelegate(Debugger &debugger) 2563b9c1b51eSKate Stone : TreeDelegate(), m_thread_delegate_sp(), m_debugger(debugger), 2564b9c1b51eSKate Stone m_stop_id(UINT32_MAX) { 2565554f68d3SGreg Clayton FormatEntity::Parse("process ${process.id}{, name = ${process.name}}", 2566554f68d3SGreg Clayton m_format); 2567ec990867SGreg Clayton } 2568ec990867SGreg Clayton 2569315b6884SEugene Zelenko ~ThreadsTreeDelegate() override = default; 2570ec990867SGreg Clayton 2571b9c1b51eSKate Stone ProcessSP GetProcess() { 2572b9c1b51eSKate Stone return m_debugger.GetCommandInterpreter() 2573b9c1b51eSKate Stone .GetExecutionContext() 2574b9c1b51eSKate Stone .GetProcessSP(); 2575ec990867SGreg Clayton } 2576ec990867SGreg Clayton 2577b9c1b51eSKate Stone void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override { 2578ec990867SGreg Clayton ProcessSP process_sp = GetProcess(); 2579b9c1b51eSKate Stone if (process_sp && process_sp->IsAlive()) { 2580ec990867SGreg Clayton StreamString strm; 2581ec990867SGreg Clayton ExecutionContext exe_ctx(process_sp); 2582b9c1b51eSKate Stone if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr, 2583b9c1b51eSKate Stone nullptr, false, false)) { 2584ec990867SGreg Clayton int right_pad = 1; 2585c156427dSZachary Turner window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad); 2586ec990867SGreg Clayton } 2587ec990867SGreg Clayton } 2588ec990867SGreg Clayton } 2589ec990867SGreg Clayton 2590b9c1b51eSKate Stone void TreeDelegateGenerateChildren(TreeItem &item) override { 2591ec990867SGreg Clayton ProcessSP process_sp = GetProcess(); 2592b9c1b51eSKate Stone if (process_sp && process_sp->IsAlive()) { 2593ec990867SGreg Clayton StateType state = process_sp->GetState(); 2594b9c1b51eSKate Stone if (StateIsStoppedState(state, true)) { 2595ec990867SGreg Clayton const uint32_t stop_id = process_sp->GetStopID(); 2596ec990867SGreg Clayton if (m_stop_id == stop_id) 2597ec990867SGreg Clayton return; // Children are already up to date 2598ec990867SGreg Clayton 2599ec990867SGreg Clayton m_stop_id = stop_id; 2600ec990867SGreg Clayton 2601b9c1b51eSKate Stone if (!m_thread_delegate_sp) { 2602ec990867SGreg Clayton // Always expand the thread item the first time we show it 2603ec990867SGreg Clayton // item.Expand(); 2604ec990867SGreg Clayton m_thread_delegate_sp.reset(new ThreadTreeDelegate(m_debugger)); 2605ec990867SGreg Clayton } 2606ec990867SGreg Clayton 2607ec990867SGreg Clayton TreeItem t(&item, *m_thread_delegate_sp, false); 2608ec990867SGreg Clayton ThreadList &threads = process_sp->GetThreadList(); 2609bb19a13cSSaleem Abdulrasool std::lock_guard<std::recursive_mutex> guard(threads.GetMutex()); 2610ec990867SGreg Clayton size_t num_threads = threads.GetSize(); 2611ec990867SGreg Clayton item.Resize(num_threads, t); 2612b9c1b51eSKate Stone for (size_t i = 0; i < num_threads; ++i) { 2613ec990867SGreg Clayton item[i].SetIdentifier(threads.GetThreadAtIndex(i)->GetID()); 2614ec990867SGreg Clayton item[i].SetMightHaveChildren(true); 2615ec990867SGreg Clayton } 2616ec990867SGreg Clayton return; 2617ec990867SGreg Clayton } 2618ec990867SGreg Clayton } 2619ec990867SGreg Clayton item.ClearChildren(); 2620ec990867SGreg Clayton } 2621ec990867SGreg Clayton 2622b9c1b51eSKate Stone bool TreeDelegateItemSelected(TreeItem &item) override { return false; } 2623ec990867SGreg Clayton 2624ec990867SGreg Clayton protected: 2625ec990867SGreg Clayton std::shared_ptr<ThreadTreeDelegate> m_thread_delegate_sp; 2626ec990867SGreg Clayton Debugger &m_debugger; 2627ec990867SGreg Clayton uint32_t m_stop_id; 2628554f68d3SGreg Clayton FormatEntity::Entry m_format; 2629ec990867SGreg Clayton }; 2630ec990867SGreg Clayton 2631b9c1b51eSKate Stone class ValueObjectListDelegate : public WindowDelegate { 263244d93782SGreg Clayton public: 2633b9c1b51eSKate Stone ValueObjectListDelegate() 26348369b28dSGreg Clayton : m_rows(), m_selected_row(nullptr), 2635b9c1b51eSKate Stone m_selected_row_idx(0), m_first_visible_row(0), m_num_rows(0), 2636b9c1b51eSKate Stone m_max_x(0), m_max_y(0) {} 263744d93782SGreg Clayton 2638b9c1b51eSKate Stone ValueObjectListDelegate(ValueObjectList &valobj_list) 26398369b28dSGreg Clayton : m_rows(), m_selected_row(nullptr), 2640b9c1b51eSKate Stone m_selected_row_idx(0), m_first_visible_row(0), m_num_rows(0), 2641b9c1b51eSKate Stone m_max_x(0), m_max_y(0) { 264244d93782SGreg Clayton SetValues(valobj_list); 264344d93782SGreg Clayton } 264444d93782SGreg Clayton 2645315b6884SEugene Zelenko ~ValueObjectListDelegate() override = default; 264644d93782SGreg Clayton 2647b9c1b51eSKate Stone void SetValues(ValueObjectList &valobj_list) { 2648c5dac77aSEugene Zelenko m_selected_row = nullptr; 264944d93782SGreg Clayton m_selected_row_idx = 0; 265044d93782SGreg Clayton m_first_visible_row = 0; 265144d93782SGreg Clayton m_num_rows = 0; 265244d93782SGreg Clayton m_rows.clear(); 26538369b28dSGreg Clayton for (auto &valobj_sp : valobj_list.GetObjects()) 26548369b28dSGreg Clayton m_rows.push_back(Row(valobj_sp, nullptr)); 265544d93782SGreg Clayton } 265644d93782SGreg Clayton 2657b9c1b51eSKate Stone bool WindowDelegateDraw(Window &window, bool force) override { 265844d93782SGreg Clayton m_num_rows = 0; 265944d93782SGreg Clayton m_min_x = 2; 266044d93782SGreg Clayton m_min_y = 1; 266144d93782SGreg Clayton m_max_x = window.GetWidth() - 1; 266244d93782SGreg Clayton m_max_y = window.GetHeight() - 1; 266344d93782SGreg Clayton 266444d93782SGreg Clayton window.Erase(); 266544d93782SGreg Clayton window.DrawTitleBox(window.GetName()); 266644d93782SGreg Clayton 266744d93782SGreg Clayton const int num_visible_rows = NumVisibleRows(); 266844d93782SGreg Clayton const int num_rows = CalculateTotalNumberRows(m_rows); 266944d93782SGreg Clayton 267005097246SAdrian Prantl // If we unexpanded while having something selected our total number of 267105097246SAdrian Prantl // rows is less than the num visible rows, then make sure we show all the 267205097246SAdrian Prantl // rows by setting the first visible row accordingly. 267344d93782SGreg Clayton if (m_first_visible_row > 0 && num_rows < num_visible_rows) 267444d93782SGreg Clayton m_first_visible_row = 0; 267544d93782SGreg Clayton 267644d93782SGreg Clayton // Make sure the selected row is always visible 267744d93782SGreg Clayton if (m_selected_row_idx < m_first_visible_row) 267844d93782SGreg Clayton m_first_visible_row = m_selected_row_idx; 267944d93782SGreg Clayton else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx) 268044d93782SGreg Clayton m_first_visible_row = m_selected_row_idx - num_visible_rows + 1; 268144d93782SGreg Clayton 268244d93782SGreg Clayton DisplayRows(window, m_rows, g_options); 268344d93782SGreg Clayton 268444d93782SGreg Clayton window.DeferredRefresh(); 268544d93782SGreg Clayton 268644d93782SGreg Clayton // Get the selected row 268744d93782SGreg Clayton m_selected_row = GetRowForRowIndex(m_selected_row_idx); 268805097246SAdrian Prantl // Keep the cursor on the selected row so the highlight and the cursor are 268905097246SAdrian Prantl // always on the same line 269044d93782SGreg Clayton if (m_selected_row) 2691b9c1b51eSKate Stone window.MoveCursor(m_selected_row->x, m_selected_row->y); 269244d93782SGreg Clayton 269344d93782SGreg Clayton return true; // Drawing handled 269444d93782SGreg Clayton } 269544d93782SGreg Clayton 2696b9c1b51eSKate Stone KeyHelp *WindowDelegateGetKeyHelp() override { 269744d93782SGreg Clayton static curses::KeyHelp g_source_view_key_help[] = { 269844d93782SGreg Clayton {KEY_UP, "Select previous item"}, 269944d93782SGreg Clayton {KEY_DOWN, "Select next item"}, 270044d93782SGreg Clayton {KEY_RIGHT, "Expand selected item"}, 270144d93782SGreg Clayton {KEY_LEFT, "Unexpand selected item or select parent if not expanded"}, 270244d93782SGreg Clayton {KEY_PPAGE, "Page up"}, 270344d93782SGreg Clayton {KEY_NPAGE, "Page down"}, 270444d93782SGreg Clayton {'A', "Format as annotated address"}, 270544d93782SGreg Clayton {'b', "Format as binary"}, 270644d93782SGreg Clayton {'B', "Format as hex bytes with ASCII"}, 270744d93782SGreg Clayton {'c', "Format as character"}, 270844d93782SGreg Clayton {'d', "Format as a signed integer"}, 270944d93782SGreg Clayton {'D', "Format selected value using the default format for the type"}, 271044d93782SGreg Clayton {'f', "Format as float"}, 271144d93782SGreg Clayton {'h', "Show help dialog"}, 271244d93782SGreg Clayton {'i', "Format as instructions"}, 271344d93782SGreg Clayton {'o', "Format as octal"}, 271444d93782SGreg Clayton {'p', "Format as pointer"}, 271544d93782SGreg Clayton {'s', "Format as C string"}, 271644d93782SGreg Clayton {'t', "Toggle showing/hiding type names"}, 271744d93782SGreg Clayton {'u', "Format as an unsigned integer"}, 271844d93782SGreg Clayton {'x', "Format as hex"}, 271944d93782SGreg Clayton {'X', "Format as uppercase hex"}, 272044d93782SGreg Clayton {' ', "Toggle item expansion"}, 272144d93782SGreg Clayton {',', "Page up"}, 272244d93782SGreg Clayton {'.', "Page down"}, 2723b9c1b51eSKate Stone {'\0', nullptr}}; 272444d93782SGreg Clayton return g_source_view_key_help; 272544d93782SGreg Clayton } 272644d93782SGreg Clayton 2727b9c1b51eSKate Stone HandleCharResult WindowDelegateHandleChar(Window &window, int c) override { 2728b9c1b51eSKate Stone switch (c) { 272944d93782SGreg Clayton case 'x': 273044d93782SGreg Clayton case 'X': 273144d93782SGreg Clayton case 'o': 273244d93782SGreg Clayton case 's': 273344d93782SGreg Clayton case 'u': 273444d93782SGreg Clayton case 'd': 273544d93782SGreg Clayton case 'D': 273644d93782SGreg Clayton case 'i': 273744d93782SGreg Clayton case 'A': 273844d93782SGreg Clayton case 'p': 273944d93782SGreg Clayton case 'c': 274044d93782SGreg Clayton case 'b': 274144d93782SGreg Clayton case 'B': 274244d93782SGreg Clayton case 'f': 274344d93782SGreg Clayton // Change the format for the currently selected item 27448369b28dSGreg Clayton if (m_selected_row) { 27458369b28dSGreg Clayton auto valobj_sp = m_selected_row->value.GetSP(); 27468369b28dSGreg Clayton if (valobj_sp) 27478369b28dSGreg Clayton valobj_sp->SetFormat(FormatForChar(c)); 27488369b28dSGreg Clayton } 274944d93782SGreg Clayton return eKeyHandled; 275044d93782SGreg Clayton 275144d93782SGreg Clayton case 't': 275244d93782SGreg Clayton // Toggle showing type names 275344d93782SGreg Clayton g_options.show_types = !g_options.show_types; 275444d93782SGreg Clayton return eKeyHandled; 275544d93782SGreg Clayton 275644d93782SGreg Clayton case ',': 275744d93782SGreg Clayton case KEY_PPAGE: 275844d93782SGreg Clayton // Page up key 2759b9c1b51eSKate Stone if (m_first_visible_row > 0) { 27603985c8c6SSaleem Abdulrasool if (static_cast<int>(m_first_visible_row) > m_max_y) 276144d93782SGreg Clayton m_first_visible_row -= m_max_y; 276244d93782SGreg Clayton else 276344d93782SGreg Clayton m_first_visible_row = 0; 276444d93782SGreg Clayton m_selected_row_idx = m_first_visible_row; 276544d93782SGreg Clayton } 276644d93782SGreg Clayton return eKeyHandled; 276744d93782SGreg Clayton 276844d93782SGreg Clayton case '.': 276944d93782SGreg Clayton case KEY_NPAGE: 277044d93782SGreg Clayton // Page down key 2771b9c1b51eSKate Stone if (m_num_rows > static_cast<size_t>(m_max_y)) { 2772b9c1b51eSKate Stone if (m_first_visible_row + m_max_y < m_num_rows) { 277344d93782SGreg Clayton m_first_visible_row += m_max_y; 277444d93782SGreg Clayton m_selected_row_idx = m_first_visible_row; 277544d93782SGreg Clayton } 277644d93782SGreg Clayton } 277744d93782SGreg Clayton return eKeyHandled; 277844d93782SGreg Clayton 277944d93782SGreg Clayton case KEY_UP: 278044d93782SGreg Clayton if (m_selected_row_idx > 0) 278144d93782SGreg Clayton --m_selected_row_idx; 278244d93782SGreg Clayton return eKeyHandled; 2783315b6884SEugene Zelenko 278444d93782SGreg Clayton case KEY_DOWN: 278544d93782SGreg Clayton if (m_selected_row_idx + 1 < m_num_rows) 278644d93782SGreg Clayton ++m_selected_row_idx; 278744d93782SGreg Clayton return eKeyHandled; 278844d93782SGreg Clayton 278944d93782SGreg Clayton case KEY_RIGHT: 2790b9c1b51eSKate Stone if (m_selected_row) { 279144d93782SGreg Clayton if (!m_selected_row->expanded) 279244d93782SGreg Clayton m_selected_row->Expand(); 279344d93782SGreg Clayton } 279444d93782SGreg Clayton return eKeyHandled; 279544d93782SGreg Clayton 279644d93782SGreg Clayton case KEY_LEFT: 2797b9c1b51eSKate Stone if (m_selected_row) { 279844d93782SGreg Clayton if (m_selected_row->expanded) 279944d93782SGreg Clayton m_selected_row->Unexpand(); 280044d93782SGreg Clayton else if (m_selected_row->parent) 280144d93782SGreg Clayton m_selected_row_idx = m_selected_row->parent->row_idx; 280244d93782SGreg Clayton } 280344d93782SGreg Clayton return eKeyHandled; 280444d93782SGreg Clayton 280544d93782SGreg Clayton case ' ': 280644d93782SGreg Clayton // Toggle expansion state when SPACE is pressed 2807b9c1b51eSKate Stone if (m_selected_row) { 280844d93782SGreg Clayton if (m_selected_row->expanded) 280944d93782SGreg Clayton m_selected_row->Unexpand(); 281044d93782SGreg Clayton else 281144d93782SGreg Clayton m_selected_row->Expand(); 281244d93782SGreg Clayton } 281344d93782SGreg Clayton return eKeyHandled; 281444d93782SGreg Clayton 281544d93782SGreg Clayton case 'h': 281644d93782SGreg Clayton window.CreateHelpSubwindow(); 281744d93782SGreg Clayton return eKeyHandled; 281844d93782SGreg Clayton 281944d93782SGreg Clayton default: 282044d93782SGreg Clayton break; 282144d93782SGreg Clayton } 282244d93782SGreg Clayton return eKeyNotHandled; 282344d93782SGreg Clayton } 282444d93782SGreg Clayton 282544d93782SGreg Clayton protected: 282644d93782SGreg Clayton std::vector<Row> m_rows; 282744d93782SGreg Clayton Row *m_selected_row; 282844d93782SGreg Clayton uint32_t m_selected_row_idx; 282944d93782SGreg Clayton uint32_t m_first_visible_row; 283044d93782SGreg Clayton uint32_t m_num_rows; 283144d93782SGreg Clayton int m_min_x; 283244d93782SGreg Clayton int m_min_y; 283344d93782SGreg Clayton int m_max_x; 283444d93782SGreg Clayton int m_max_y; 283544d93782SGreg Clayton 2836b9c1b51eSKate Stone static Format FormatForChar(int c) { 2837b9c1b51eSKate Stone switch (c) { 2838b9c1b51eSKate Stone case 'x': 2839b9c1b51eSKate Stone return eFormatHex; 2840b9c1b51eSKate Stone case 'X': 2841b9c1b51eSKate Stone return eFormatHexUppercase; 2842b9c1b51eSKate Stone case 'o': 2843b9c1b51eSKate Stone return eFormatOctal; 2844b9c1b51eSKate Stone case 's': 2845b9c1b51eSKate Stone return eFormatCString; 2846b9c1b51eSKate Stone case 'u': 2847b9c1b51eSKate Stone return eFormatUnsigned; 2848b9c1b51eSKate Stone case 'd': 2849b9c1b51eSKate Stone return eFormatDecimal; 2850b9c1b51eSKate Stone case 'D': 2851b9c1b51eSKate Stone return eFormatDefault; 2852b9c1b51eSKate Stone case 'i': 2853b9c1b51eSKate Stone return eFormatInstruction; 2854b9c1b51eSKate Stone case 'A': 2855b9c1b51eSKate Stone return eFormatAddressInfo; 2856b9c1b51eSKate Stone case 'p': 2857b9c1b51eSKate Stone return eFormatPointer; 2858b9c1b51eSKate Stone case 'c': 2859b9c1b51eSKate Stone return eFormatChar; 2860b9c1b51eSKate Stone case 'b': 2861b9c1b51eSKate Stone return eFormatBinary; 2862b9c1b51eSKate Stone case 'B': 2863b9c1b51eSKate Stone return eFormatBytesWithASCII; 2864b9c1b51eSKate Stone case 'f': 2865b9c1b51eSKate Stone return eFormatFloat; 286644d93782SGreg Clayton } 286744d93782SGreg Clayton return eFormatDefault; 286844d93782SGreg Clayton } 286944d93782SGreg Clayton 2870b9c1b51eSKate Stone bool DisplayRowObject(Window &window, Row &row, DisplayOptions &options, 2871b9c1b51eSKate Stone bool highlight, bool last_child) { 28728369b28dSGreg Clayton ValueObject *valobj = row.value.GetSP().get(); 287344d93782SGreg Clayton 2874c5dac77aSEugene Zelenko if (valobj == nullptr) 287544d93782SGreg Clayton return false; 287644d93782SGreg Clayton 2877b9c1b51eSKate Stone const char *type_name = 2878b9c1b51eSKate Stone options.show_types ? valobj->GetTypeName().GetCString() : nullptr; 287944d93782SGreg Clayton const char *name = valobj->GetName().GetCString(); 288044d93782SGreg Clayton const char *value = valobj->GetValueAsCString(); 288144d93782SGreg Clayton const char *summary = valobj->GetSummaryAsCString(); 288244d93782SGreg Clayton 288344d93782SGreg Clayton window.MoveCursor(row.x, row.y); 288444d93782SGreg Clayton 288544d93782SGreg Clayton row.DrawTree(window); 288644d93782SGreg Clayton 288744d93782SGreg Clayton if (highlight) 288844d93782SGreg Clayton window.AttributeOn(A_REVERSE); 288944d93782SGreg Clayton 289044d93782SGreg Clayton if (type_name && type_name[0]) 289144d93782SGreg Clayton window.Printf("(%s) ", type_name); 289244d93782SGreg Clayton 289344d93782SGreg Clayton if (name && name[0]) 289444d93782SGreg Clayton window.PutCString(name); 289544d93782SGreg Clayton 289644d93782SGreg Clayton attr_t changd_attr = 0; 289744d93782SGreg Clayton if (valobj->GetValueDidChange()) 289844d93782SGreg Clayton changd_attr = COLOR_PAIR(5) | A_BOLD; 289944d93782SGreg Clayton 2900b9c1b51eSKate Stone if (value && value[0]) { 290144d93782SGreg Clayton window.PutCString(" = "); 290244d93782SGreg Clayton if (changd_attr) 290344d93782SGreg Clayton window.AttributeOn(changd_attr); 290444d93782SGreg Clayton window.PutCString(value); 290544d93782SGreg Clayton if (changd_attr) 290644d93782SGreg Clayton window.AttributeOff(changd_attr); 290744d93782SGreg Clayton } 290844d93782SGreg Clayton 2909b9c1b51eSKate Stone if (summary && summary[0]) { 291044d93782SGreg Clayton window.PutChar(' '); 291144d93782SGreg Clayton if (changd_attr) 291244d93782SGreg Clayton window.AttributeOn(changd_attr); 291344d93782SGreg Clayton window.PutCString(summary); 291444d93782SGreg Clayton if (changd_attr) 291544d93782SGreg Clayton window.AttributeOff(changd_attr); 291644d93782SGreg Clayton } 291744d93782SGreg Clayton 291844d93782SGreg Clayton if (highlight) 291944d93782SGreg Clayton window.AttributeOff(A_REVERSE); 292044d93782SGreg Clayton 292144d93782SGreg Clayton return true; 292244d93782SGreg Clayton } 2923315b6884SEugene Zelenko 2924b9c1b51eSKate Stone void DisplayRows(Window &window, std::vector<Row> &rows, 2925b9c1b51eSKate Stone DisplayOptions &options) { 292644d93782SGreg Clayton // > 0x25B7 292744d93782SGreg Clayton // \/ 0x25BD 292844d93782SGreg Clayton 292944d93782SGreg Clayton bool window_is_active = window.IsActive(); 2930b9c1b51eSKate Stone for (auto &row : rows) { 293144d93782SGreg Clayton const bool last_child = row.parent && &rows[rows.size() - 1] == &row; 293244d93782SGreg Clayton // Save the row index in each Row structure 293344d93782SGreg Clayton row.row_idx = m_num_rows; 293444d93782SGreg Clayton if ((m_num_rows >= m_first_visible_row) && 2935b9c1b51eSKate Stone ((m_num_rows - m_first_visible_row) < 2936b9c1b51eSKate Stone static_cast<size_t>(NumVisibleRows()))) { 293744d93782SGreg Clayton row.x = m_min_x; 293844d93782SGreg Clayton row.y = m_num_rows - m_first_visible_row + 1; 2939b9c1b51eSKate Stone if (DisplayRowObject(window, row, options, 2940b9c1b51eSKate Stone window_is_active && 2941b9c1b51eSKate Stone m_num_rows == m_selected_row_idx, 2942b9c1b51eSKate Stone last_child)) { 294344d93782SGreg Clayton ++m_num_rows; 2944b9c1b51eSKate Stone } else { 294544d93782SGreg Clayton row.x = 0; 294644d93782SGreg Clayton row.y = 0; 294744d93782SGreg Clayton } 2948b9c1b51eSKate Stone } else { 294944d93782SGreg Clayton row.x = 0; 295044d93782SGreg Clayton row.y = 0; 295144d93782SGreg Clayton ++m_num_rows; 295244d93782SGreg Clayton } 295344d93782SGreg Clayton 29548369b28dSGreg Clayton auto &children = row.GetChildren(); 29558369b28dSGreg Clayton if (row.expanded && !children.empty()) { 29568369b28dSGreg Clayton DisplayRows(window, children, options); 295744d93782SGreg Clayton } 295844d93782SGreg Clayton } 295944d93782SGreg Clayton } 296044d93782SGreg Clayton 29618369b28dSGreg Clayton int CalculateTotalNumberRows(std::vector<Row> &rows) { 296244d93782SGreg Clayton int row_count = 0; 29638369b28dSGreg Clayton for (auto &row : rows) { 296444d93782SGreg Clayton ++row_count; 296544d93782SGreg Clayton if (row.expanded) 29668369b28dSGreg Clayton row_count += CalculateTotalNumberRows(row.GetChildren()); 296744d93782SGreg Clayton } 296844d93782SGreg Clayton return row_count; 296944d93782SGreg Clayton } 2970315b6884SEugene Zelenko 2971b9c1b51eSKate Stone static Row *GetRowForRowIndexImpl(std::vector<Row> &rows, size_t &row_index) { 2972b9c1b51eSKate Stone for (auto &row : rows) { 297344d93782SGreg Clayton if (row_index == 0) 297444d93782SGreg Clayton return &row; 2975b9c1b51eSKate Stone else { 297644d93782SGreg Clayton --row_index; 29778369b28dSGreg Clayton auto &children = row.GetChildren(); 29788369b28dSGreg Clayton if (row.expanded && !children.empty()) { 29798369b28dSGreg Clayton Row *result = GetRowForRowIndexImpl(children, row_index); 298044d93782SGreg Clayton if (result) 298144d93782SGreg Clayton return result; 298244d93782SGreg Clayton } 298344d93782SGreg Clayton } 298444d93782SGreg Clayton } 2985c5dac77aSEugene Zelenko return nullptr; 298644d93782SGreg Clayton } 298744d93782SGreg Clayton 2988b9c1b51eSKate Stone Row *GetRowForRowIndex(size_t row_index) { 298944d93782SGreg Clayton return GetRowForRowIndexImpl(m_rows, row_index); 299044d93782SGreg Clayton } 299144d93782SGreg Clayton 2992b9c1b51eSKate Stone int NumVisibleRows() const { return m_max_y - m_min_y; } 299344d93782SGreg Clayton 299444d93782SGreg Clayton static DisplayOptions g_options; 299544d93782SGreg Clayton }; 299644d93782SGreg Clayton 2997b9c1b51eSKate Stone class FrameVariablesWindowDelegate : public ValueObjectListDelegate { 299844d93782SGreg Clayton public: 2999b9c1b51eSKate Stone FrameVariablesWindowDelegate(Debugger &debugger) 3000b9c1b51eSKate Stone : ValueObjectListDelegate(), m_debugger(debugger), 3001b9c1b51eSKate Stone m_frame_block(nullptr) {} 300244d93782SGreg Clayton 3003315b6884SEugene Zelenko ~FrameVariablesWindowDelegate() override = default; 300444d93782SGreg Clayton 3005b9c1b51eSKate Stone const char *WindowDelegateGetHelpText() override { 300644d93782SGreg Clayton return "Frame variable window keyboard shortcuts:"; 300744d93782SGreg Clayton } 300844d93782SGreg Clayton 3009b9c1b51eSKate Stone bool WindowDelegateDraw(Window &window, bool force) override { 3010b9c1b51eSKate Stone ExecutionContext exe_ctx( 3011b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext()); 301244d93782SGreg Clayton Process *process = exe_ctx.GetProcessPtr(); 3013c5dac77aSEugene Zelenko Block *frame_block = nullptr; 3014c5dac77aSEugene Zelenko StackFrame *frame = nullptr; 301544d93782SGreg Clayton 3016b9c1b51eSKate Stone if (process) { 301744d93782SGreg Clayton StateType state = process->GetState(); 3018b9c1b51eSKate Stone if (StateIsStoppedState(state, true)) { 301944d93782SGreg Clayton frame = exe_ctx.GetFramePtr(); 302044d93782SGreg Clayton if (frame) 302144d93782SGreg Clayton frame_block = frame->GetFrameBlock(); 3022b9c1b51eSKate Stone } else if (StateIsRunningState(state)) { 302344d93782SGreg Clayton return true; // Don't do any updating when we are running 302444d93782SGreg Clayton } 302544d93782SGreg Clayton } 302644d93782SGreg Clayton 302744d93782SGreg Clayton ValueObjectList local_values; 3028b9c1b51eSKate Stone if (frame_block) { 302944d93782SGreg Clayton // Only update the variables if they have changed 3030b9c1b51eSKate Stone if (m_frame_block != frame_block) { 303144d93782SGreg Clayton m_frame_block = frame_block; 303244d93782SGreg Clayton 303344d93782SGreg Clayton VariableList *locals = frame->GetVariableList(true); 3034b9c1b51eSKate Stone if (locals) { 303544d93782SGreg Clayton const DynamicValueType use_dynamic = eDynamicDontRunTarget; 303644d93782SGreg Clayton const size_t num_locals = locals->GetSize(); 3037b9c1b51eSKate Stone for (size_t i = 0; i < num_locals; ++i) { 3038b9c1b51eSKate Stone ValueObjectSP value_sp = frame->GetValueObjectForFrameVariable( 3039b9c1b51eSKate Stone locals->GetVariableAtIndex(i), use_dynamic); 3040b9c1b51eSKate Stone if (value_sp) { 3041eb72dc7dSGreg Clayton ValueObjectSP synthetic_value_sp = value_sp->GetSyntheticValue(); 3042eb72dc7dSGreg Clayton if (synthetic_value_sp) 3043eb72dc7dSGreg Clayton local_values.Append(synthetic_value_sp); 3044eb72dc7dSGreg Clayton else 3045eb72dc7dSGreg Clayton local_values.Append(value_sp); 3046eb72dc7dSGreg Clayton } 3047eb72dc7dSGreg Clayton } 304844d93782SGreg Clayton // Update the values 304944d93782SGreg Clayton SetValues(local_values); 305044d93782SGreg Clayton } 305144d93782SGreg Clayton } 3052b9c1b51eSKate Stone } else { 3053c5dac77aSEugene Zelenko m_frame_block = nullptr; 305444d93782SGreg Clayton // Update the values with an empty list if there is no frame 305544d93782SGreg Clayton SetValues(local_values); 305644d93782SGreg Clayton } 305744d93782SGreg Clayton 305844d93782SGreg Clayton return ValueObjectListDelegate::WindowDelegateDraw(window, force); 305944d93782SGreg Clayton } 306044d93782SGreg Clayton 306144d93782SGreg Clayton protected: 306244d93782SGreg Clayton Debugger &m_debugger; 306344d93782SGreg Clayton Block *m_frame_block; 306444d93782SGreg Clayton }; 306544d93782SGreg Clayton 3066b9c1b51eSKate Stone class RegistersWindowDelegate : public ValueObjectListDelegate { 306744d93782SGreg Clayton public: 3068b9c1b51eSKate Stone RegistersWindowDelegate(Debugger &debugger) 3069b9c1b51eSKate Stone : ValueObjectListDelegate(), m_debugger(debugger) {} 307044d93782SGreg Clayton 3071315b6884SEugene Zelenko ~RegistersWindowDelegate() override = default; 307244d93782SGreg Clayton 3073b9c1b51eSKate Stone const char *WindowDelegateGetHelpText() override { 307444d93782SGreg Clayton return "Register window keyboard shortcuts:"; 307544d93782SGreg Clayton } 307644d93782SGreg Clayton 3077b9c1b51eSKate Stone bool WindowDelegateDraw(Window &window, bool force) override { 3078b9c1b51eSKate Stone ExecutionContext exe_ctx( 3079b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext()); 308044d93782SGreg Clayton StackFrame *frame = exe_ctx.GetFramePtr(); 308144d93782SGreg Clayton 308244d93782SGreg Clayton ValueObjectList value_list; 3083b9c1b51eSKate Stone if (frame) { 3084b9c1b51eSKate Stone if (frame->GetStackID() != m_stack_id) { 308544d93782SGreg Clayton m_stack_id = frame->GetStackID(); 308644d93782SGreg Clayton RegisterContextSP reg_ctx(frame->GetRegisterContext()); 3087b9c1b51eSKate Stone if (reg_ctx) { 308844d93782SGreg Clayton const uint32_t num_sets = reg_ctx->GetRegisterSetCount(); 3089b9c1b51eSKate Stone for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) { 3090b9c1b51eSKate Stone value_list.Append( 3091b9c1b51eSKate Stone ValueObjectRegisterSet::Create(frame, reg_ctx, set_idx)); 309244d93782SGreg Clayton } 309344d93782SGreg Clayton } 309444d93782SGreg Clayton SetValues(value_list); 309544d93782SGreg Clayton } 3096b9c1b51eSKate Stone } else { 309744d93782SGreg Clayton Process *process = exe_ctx.GetProcessPtr(); 309844d93782SGreg Clayton if (process && process->IsAlive()) 309944d93782SGreg Clayton return true; // Don't do any updating if we are running 3100b9c1b51eSKate Stone else { 310105097246SAdrian Prantl // Update the values with an empty list if there is no process or the 310205097246SAdrian Prantl // process isn't alive anymore 310344d93782SGreg Clayton SetValues(value_list); 310444d93782SGreg Clayton } 310544d93782SGreg Clayton } 310644d93782SGreg Clayton return ValueObjectListDelegate::WindowDelegateDraw(window, force); 310744d93782SGreg Clayton } 310844d93782SGreg Clayton 310944d93782SGreg Clayton protected: 311044d93782SGreg Clayton Debugger &m_debugger; 311144d93782SGreg Clayton StackID m_stack_id; 311244d93782SGreg Clayton }; 311344d93782SGreg Clayton 3114b9c1b51eSKate Stone static const char *CursesKeyToCString(int ch) { 311544d93782SGreg Clayton static char g_desc[32]; 3116b9c1b51eSKate Stone if (ch >= KEY_F0 && ch < KEY_F0 + 64) { 311744d93782SGreg Clayton snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0); 311844d93782SGreg Clayton return g_desc; 311944d93782SGreg Clayton } 3120b9c1b51eSKate Stone switch (ch) { 3121b9c1b51eSKate Stone case KEY_DOWN: 3122b9c1b51eSKate Stone return "down"; 3123b9c1b51eSKate Stone case KEY_UP: 3124b9c1b51eSKate Stone return "up"; 3125b9c1b51eSKate Stone case KEY_LEFT: 3126b9c1b51eSKate Stone return "left"; 3127b9c1b51eSKate Stone case KEY_RIGHT: 3128b9c1b51eSKate Stone return "right"; 3129b9c1b51eSKate Stone case KEY_HOME: 3130b9c1b51eSKate Stone return "home"; 3131b9c1b51eSKate Stone case KEY_BACKSPACE: 3132b9c1b51eSKate Stone return "backspace"; 3133b9c1b51eSKate Stone case KEY_DL: 3134b9c1b51eSKate Stone return "delete-line"; 3135b9c1b51eSKate Stone case KEY_IL: 3136b9c1b51eSKate Stone return "insert-line"; 3137b9c1b51eSKate Stone case KEY_DC: 3138b9c1b51eSKate Stone return "delete-char"; 3139b9c1b51eSKate Stone case KEY_IC: 3140b9c1b51eSKate Stone return "insert-char"; 3141b9c1b51eSKate Stone case KEY_CLEAR: 3142b9c1b51eSKate Stone return "clear"; 3143b9c1b51eSKate Stone case KEY_EOS: 3144b9c1b51eSKate Stone return "clear-to-eos"; 3145b9c1b51eSKate Stone case KEY_EOL: 3146b9c1b51eSKate Stone return "clear-to-eol"; 3147b9c1b51eSKate Stone case KEY_SF: 3148b9c1b51eSKate Stone return "scroll-forward"; 3149b9c1b51eSKate Stone case KEY_SR: 3150b9c1b51eSKate Stone return "scroll-backward"; 3151b9c1b51eSKate Stone case KEY_NPAGE: 3152b9c1b51eSKate Stone return "page-down"; 3153b9c1b51eSKate Stone case KEY_PPAGE: 3154b9c1b51eSKate Stone return "page-up"; 3155b9c1b51eSKate Stone case KEY_STAB: 3156b9c1b51eSKate Stone return "set-tab"; 3157b9c1b51eSKate Stone case KEY_CTAB: 3158b9c1b51eSKate Stone return "clear-tab"; 3159b9c1b51eSKate Stone case KEY_CATAB: 3160b9c1b51eSKate Stone return "clear-all-tabs"; 3161b9c1b51eSKate Stone case KEY_ENTER: 3162b9c1b51eSKate Stone return "enter"; 3163b9c1b51eSKate Stone case KEY_PRINT: 3164b9c1b51eSKate Stone return "print"; 3165b9c1b51eSKate Stone case KEY_LL: 3166b9c1b51eSKate Stone return "lower-left key"; 3167b9c1b51eSKate Stone case KEY_A1: 3168b9c1b51eSKate Stone return "upper left of keypad"; 3169b9c1b51eSKate Stone case KEY_A3: 3170b9c1b51eSKate Stone return "upper right of keypad"; 3171b9c1b51eSKate Stone case KEY_B2: 3172b9c1b51eSKate Stone return "center of keypad"; 3173b9c1b51eSKate Stone case KEY_C1: 3174b9c1b51eSKate Stone return "lower left of keypad"; 3175b9c1b51eSKate Stone case KEY_C3: 3176b9c1b51eSKate Stone return "lower right of keypad"; 3177b9c1b51eSKate Stone case KEY_BTAB: 3178b9c1b51eSKate Stone return "back-tab key"; 3179b9c1b51eSKate Stone case KEY_BEG: 3180b9c1b51eSKate Stone return "begin key"; 3181b9c1b51eSKate Stone case KEY_CANCEL: 3182b9c1b51eSKate Stone return "cancel key"; 3183b9c1b51eSKate Stone case KEY_CLOSE: 3184b9c1b51eSKate Stone return "close key"; 3185b9c1b51eSKate Stone case KEY_COMMAND: 3186b9c1b51eSKate Stone return "command key"; 3187b9c1b51eSKate Stone case KEY_COPY: 3188b9c1b51eSKate Stone return "copy key"; 3189b9c1b51eSKate Stone case KEY_CREATE: 3190b9c1b51eSKate Stone return "create key"; 3191b9c1b51eSKate Stone case KEY_END: 3192b9c1b51eSKate Stone return "end key"; 3193b9c1b51eSKate Stone case KEY_EXIT: 3194b9c1b51eSKate Stone return "exit key"; 3195b9c1b51eSKate Stone case KEY_FIND: 3196b9c1b51eSKate Stone return "find key"; 3197b9c1b51eSKate Stone case KEY_HELP: 3198b9c1b51eSKate Stone return "help key"; 3199b9c1b51eSKate Stone case KEY_MARK: 3200b9c1b51eSKate Stone return "mark key"; 3201b9c1b51eSKate Stone case KEY_MESSAGE: 3202b9c1b51eSKate Stone return "message key"; 3203b9c1b51eSKate Stone case KEY_MOVE: 3204b9c1b51eSKate Stone return "move key"; 3205b9c1b51eSKate Stone case KEY_NEXT: 3206b9c1b51eSKate Stone return "next key"; 3207b9c1b51eSKate Stone case KEY_OPEN: 3208b9c1b51eSKate Stone return "open key"; 3209b9c1b51eSKate Stone case KEY_OPTIONS: 3210b9c1b51eSKate Stone return "options key"; 3211b9c1b51eSKate Stone case KEY_PREVIOUS: 3212b9c1b51eSKate Stone return "previous key"; 3213b9c1b51eSKate Stone case KEY_REDO: 3214b9c1b51eSKate Stone return "redo key"; 3215b9c1b51eSKate Stone case KEY_REFERENCE: 3216b9c1b51eSKate Stone return "reference key"; 3217b9c1b51eSKate Stone case KEY_REFRESH: 3218b9c1b51eSKate Stone return "refresh key"; 3219b9c1b51eSKate Stone case KEY_REPLACE: 3220b9c1b51eSKate Stone return "replace key"; 3221b9c1b51eSKate Stone case KEY_RESTART: 3222b9c1b51eSKate Stone return "restart key"; 3223b9c1b51eSKate Stone case KEY_RESUME: 3224b9c1b51eSKate Stone return "resume key"; 3225b9c1b51eSKate Stone case KEY_SAVE: 3226b9c1b51eSKate Stone return "save key"; 3227b9c1b51eSKate Stone case KEY_SBEG: 3228b9c1b51eSKate Stone return "shifted begin key"; 3229b9c1b51eSKate Stone case KEY_SCANCEL: 3230b9c1b51eSKate Stone return "shifted cancel key"; 3231b9c1b51eSKate Stone case KEY_SCOMMAND: 3232b9c1b51eSKate Stone return "shifted command key"; 3233b9c1b51eSKate Stone case KEY_SCOPY: 3234b9c1b51eSKate Stone return "shifted copy key"; 3235b9c1b51eSKate Stone case KEY_SCREATE: 3236b9c1b51eSKate Stone return "shifted create key"; 3237b9c1b51eSKate Stone case KEY_SDC: 3238b9c1b51eSKate Stone return "shifted delete-character key"; 3239b9c1b51eSKate Stone case KEY_SDL: 3240b9c1b51eSKate Stone return "shifted delete-line key"; 3241b9c1b51eSKate Stone case KEY_SELECT: 3242b9c1b51eSKate Stone return "select key"; 3243b9c1b51eSKate Stone case KEY_SEND: 3244b9c1b51eSKate Stone return "shifted end key"; 3245b9c1b51eSKate Stone case KEY_SEOL: 3246b9c1b51eSKate Stone return "shifted clear-to-end-of-line key"; 3247b9c1b51eSKate Stone case KEY_SEXIT: 3248b9c1b51eSKate Stone return "shifted exit key"; 3249b9c1b51eSKate Stone case KEY_SFIND: 3250b9c1b51eSKate Stone return "shifted find key"; 3251b9c1b51eSKate Stone case KEY_SHELP: 3252b9c1b51eSKate Stone return "shifted help key"; 3253b9c1b51eSKate Stone case KEY_SHOME: 3254b9c1b51eSKate Stone return "shifted home key"; 3255b9c1b51eSKate Stone case KEY_SIC: 3256b9c1b51eSKate Stone return "shifted insert-character key"; 3257b9c1b51eSKate Stone case KEY_SLEFT: 3258b9c1b51eSKate Stone return "shifted left-arrow key"; 3259b9c1b51eSKate Stone case KEY_SMESSAGE: 3260b9c1b51eSKate Stone return "shifted message key"; 3261b9c1b51eSKate Stone case KEY_SMOVE: 3262b9c1b51eSKate Stone return "shifted move key"; 3263b9c1b51eSKate Stone case KEY_SNEXT: 3264b9c1b51eSKate Stone return "shifted next key"; 3265b9c1b51eSKate Stone case KEY_SOPTIONS: 3266b9c1b51eSKate Stone return "shifted options key"; 3267b9c1b51eSKate Stone case KEY_SPREVIOUS: 3268b9c1b51eSKate Stone return "shifted previous key"; 3269b9c1b51eSKate Stone case KEY_SPRINT: 3270b9c1b51eSKate Stone return "shifted print key"; 3271b9c1b51eSKate Stone case KEY_SREDO: 3272b9c1b51eSKate Stone return "shifted redo key"; 3273b9c1b51eSKate Stone case KEY_SREPLACE: 3274b9c1b51eSKate Stone return "shifted replace key"; 3275b9c1b51eSKate Stone case KEY_SRIGHT: 3276b9c1b51eSKate Stone return "shifted right-arrow key"; 3277b9c1b51eSKate Stone case KEY_SRSUME: 3278b9c1b51eSKate Stone return "shifted resume key"; 3279b9c1b51eSKate Stone case KEY_SSAVE: 3280b9c1b51eSKate Stone return "shifted save key"; 3281b9c1b51eSKate Stone case KEY_SSUSPEND: 3282b9c1b51eSKate Stone return "shifted suspend key"; 3283b9c1b51eSKate Stone case KEY_SUNDO: 3284b9c1b51eSKate Stone return "shifted undo key"; 3285b9c1b51eSKate Stone case KEY_SUSPEND: 3286b9c1b51eSKate Stone return "suspend key"; 3287b9c1b51eSKate Stone case KEY_UNDO: 3288b9c1b51eSKate Stone return "undo key"; 3289b9c1b51eSKate Stone case KEY_MOUSE: 3290b9c1b51eSKate Stone return "Mouse event has occurred"; 3291b9c1b51eSKate Stone case KEY_RESIZE: 3292b9c1b51eSKate Stone return "Terminal resize event"; 329327801f4fSBruce Mitchener #ifdef KEY_EVENT 3294b9c1b51eSKate Stone case KEY_EVENT: 3295b9c1b51eSKate Stone return "We were interrupted by an event"; 329627801f4fSBruce Mitchener #endif 3297b9c1b51eSKate Stone case KEY_RETURN: 3298b9c1b51eSKate Stone return "return"; 3299b9c1b51eSKate Stone case ' ': 3300b9c1b51eSKate Stone return "space"; 3301b9c1b51eSKate Stone case '\t': 3302b9c1b51eSKate Stone return "tab"; 3303b9c1b51eSKate Stone case KEY_ESCAPE: 3304b9c1b51eSKate Stone return "escape"; 330544d93782SGreg Clayton default: 330644d93782SGreg Clayton if (isprint(ch)) 330744d93782SGreg Clayton snprintf(g_desc, sizeof(g_desc), "%c", ch); 330844d93782SGreg Clayton else 330944d93782SGreg Clayton snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch); 331044d93782SGreg Clayton return g_desc; 331144d93782SGreg Clayton } 3312c5dac77aSEugene Zelenko return nullptr; 331344d93782SGreg Clayton } 331444d93782SGreg Clayton 3315b9c1b51eSKate Stone HelpDialogDelegate::HelpDialogDelegate(const char *text, 3316b9c1b51eSKate Stone KeyHelp *key_help_array) 3317b9c1b51eSKate Stone : m_text(), m_first_visible_line(0) { 3318b9c1b51eSKate Stone if (text && text[0]) { 331944d93782SGreg Clayton m_text.SplitIntoLines(text); 332044d93782SGreg Clayton m_text.AppendString(""); 332144d93782SGreg Clayton } 3322b9c1b51eSKate Stone if (key_help_array) { 3323b9c1b51eSKate Stone for (KeyHelp *key = key_help_array; key->ch; ++key) { 332444d93782SGreg Clayton StreamString key_description; 3325b9c1b51eSKate Stone key_description.Printf("%10s - %s", CursesKeyToCString(key->ch), 3326b9c1b51eSKate Stone key->description); 3327c156427dSZachary Turner m_text.AppendString(key_description.GetString()); 332844d93782SGreg Clayton } 332944d93782SGreg Clayton } 333044d93782SGreg Clayton } 333144d93782SGreg Clayton 3332315b6884SEugene Zelenko HelpDialogDelegate::~HelpDialogDelegate() = default; 333344d93782SGreg Clayton 3334b9c1b51eSKate Stone bool HelpDialogDelegate::WindowDelegateDraw(Window &window, bool force) { 333544d93782SGreg Clayton window.Erase(); 333644d93782SGreg Clayton const int window_height = window.GetHeight(); 333744d93782SGreg Clayton int x = 2; 333844d93782SGreg Clayton int y = 1; 333944d93782SGreg Clayton const int min_y = y; 334044d93782SGreg Clayton const int max_y = window_height - 1 - y; 33413985c8c6SSaleem Abdulrasool const size_t num_visible_lines = max_y - min_y + 1; 334244d93782SGreg Clayton const size_t num_lines = m_text.GetSize(); 334344d93782SGreg Clayton const char *bottom_message; 334444d93782SGreg Clayton if (num_lines <= num_visible_lines) 334544d93782SGreg Clayton bottom_message = "Press any key to exit"; 334644d93782SGreg Clayton else 334744d93782SGreg Clayton bottom_message = "Use arrows to scroll, any other key to exit"; 334844d93782SGreg Clayton window.DrawTitleBox(window.GetName(), bottom_message); 3349b9c1b51eSKate Stone while (y <= max_y) { 335044d93782SGreg Clayton window.MoveCursor(x, y); 3351b9c1b51eSKate Stone window.PutCStringTruncated( 3352b9c1b51eSKate Stone m_text.GetStringAtIndex(m_first_visible_line + y - min_y), 1); 335344d93782SGreg Clayton ++y; 335444d93782SGreg Clayton } 335544d93782SGreg Clayton return true; 335644d93782SGreg Clayton } 335744d93782SGreg Clayton 3358b9c1b51eSKate Stone HandleCharResult HelpDialogDelegate::WindowDelegateHandleChar(Window &window, 3359b9c1b51eSKate Stone int key) { 336044d93782SGreg Clayton bool done = false; 336144d93782SGreg Clayton const size_t num_lines = m_text.GetSize(); 336244d93782SGreg Clayton const size_t num_visible_lines = window.GetHeight() - 2; 336344d93782SGreg Clayton 3364b9c1b51eSKate Stone if (num_lines <= num_visible_lines) { 336544d93782SGreg Clayton done = true; 336605097246SAdrian Prantl // If we have all lines visible and don't need scrolling, then any key 336705097246SAdrian Prantl // press will cause us to exit 3368b9c1b51eSKate Stone } else { 3369b9c1b51eSKate Stone switch (key) { 337044d93782SGreg Clayton case KEY_UP: 337144d93782SGreg Clayton if (m_first_visible_line > 0) 337244d93782SGreg Clayton --m_first_visible_line; 337344d93782SGreg Clayton break; 337444d93782SGreg Clayton 337544d93782SGreg Clayton case KEY_DOWN: 337644d93782SGreg Clayton if (m_first_visible_line + num_visible_lines < num_lines) 337744d93782SGreg Clayton ++m_first_visible_line; 337844d93782SGreg Clayton break; 337944d93782SGreg Clayton 338044d93782SGreg Clayton case KEY_PPAGE: 338144d93782SGreg Clayton case ',': 3382b9c1b51eSKate Stone if (m_first_visible_line > 0) { 33833985c8c6SSaleem Abdulrasool if (static_cast<size_t>(m_first_visible_line) >= num_visible_lines) 338444d93782SGreg Clayton m_first_visible_line -= num_visible_lines; 338544d93782SGreg Clayton else 338644d93782SGreg Clayton m_first_visible_line = 0; 338744d93782SGreg Clayton } 338844d93782SGreg Clayton break; 3389315b6884SEugene Zelenko 339044d93782SGreg Clayton case KEY_NPAGE: 339144d93782SGreg Clayton case '.': 3392b9c1b51eSKate Stone if (m_first_visible_line + num_visible_lines < num_lines) { 339344d93782SGreg Clayton m_first_visible_line += num_visible_lines; 33943985c8c6SSaleem Abdulrasool if (static_cast<size_t>(m_first_visible_line) > num_lines) 339544d93782SGreg Clayton m_first_visible_line = num_lines - num_visible_lines; 339644d93782SGreg Clayton } 339744d93782SGreg Clayton break; 3398315b6884SEugene Zelenko 339944d93782SGreg Clayton default: 340044d93782SGreg Clayton done = true; 340144d93782SGreg Clayton break; 340244d93782SGreg Clayton } 340344d93782SGreg Clayton } 340444d93782SGreg Clayton if (done) 340544d93782SGreg Clayton window.GetParent()->RemoveSubWindow(&window); 340644d93782SGreg Clayton return eKeyHandled; 340744d93782SGreg Clayton } 340844d93782SGreg Clayton 3409b9c1b51eSKate Stone class ApplicationDelegate : public WindowDelegate, public MenuDelegate { 341044d93782SGreg Clayton public: 341144d93782SGreg Clayton enum { 341244d93782SGreg Clayton eMenuID_LLDB = 1, 341344d93782SGreg Clayton eMenuID_LLDBAbout, 341444d93782SGreg Clayton eMenuID_LLDBExit, 341544d93782SGreg Clayton 341644d93782SGreg Clayton eMenuID_Target, 341744d93782SGreg Clayton eMenuID_TargetCreate, 341844d93782SGreg Clayton eMenuID_TargetDelete, 341944d93782SGreg Clayton 342044d93782SGreg Clayton eMenuID_Process, 342144d93782SGreg Clayton eMenuID_ProcessAttach, 342244d93782SGreg Clayton eMenuID_ProcessDetach, 342344d93782SGreg Clayton eMenuID_ProcessLaunch, 342444d93782SGreg Clayton eMenuID_ProcessContinue, 342544d93782SGreg Clayton eMenuID_ProcessHalt, 342644d93782SGreg Clayton eMenuID_ProcessKill, 342744d93782SGreg Clayton 342844d93782SGreg Clayton eMenuID_Thread, 342944d93782SGreg Clayton eMenuID_ThreadStepIn, 343044d93782SGreg Clayton eMenuID_ThreadStepOver, 343144d93782SGreg Clayton eMenuID_ThreadStepOut, 343244d93782SGreg Clayton 343344d93782SGreg Clayton eMenuID_View, 343444d93782SGreg Clayton eMenuID_ViewBacktrace, 343544d93782SGreg Clayton eMenuID_ViewRegisters, 343644d93782SGreg Clayton eMenuID_ViewSource, 343744d93782SGreg Clayton eMenuID_ViewVariables, 343844d93782SGreg Clayton 343944d93782SGreg Clayton eMenuID_Help, 344044d93782SGreg Clayton eMenuID_HelpGUIHelp 344144d93782SGreg Clayton }; 344244d93782SGreg Clayton 3443b9c1b51eSKate Stone ApplicationDelegate(Application &app, Debugger &debugger) 3444b9c1b51eSKate Stone : WindowDelegate(), MenuDelegate(), m_app(app), m_debugger(debugger) {} 344544d93782SGreg Clayton 3446315b6884SEugene Zelenko ~ApplicationDelegate() override = default; 3447bd5ae6b4SGreg Clayton 3448b9c1b51eSKate Stone bool WindowDelegateDraw(Window &window, bool force) override { 344944d93782SGreg Clayton return false; // Drawing not handled, let standard window drawing happen 345044d93782SGreg Clayton } 345144d93782SGreg Clayton 3452b9c1b51eSKate Stone HandleCharResult WindowDelegateHandleChar(Window &window, int key) override { 3453b9c1b51eSKate Stone switch (key) { 34545fdb09bbSGreg Clayton case '\t': 345544d93782SGreg Clayton window.SelectNextWindowAsActive(); 345644d93782SGreg Clayton return eKeyHandled; 34575fdb09bbSGreg Clayton 34585fdb09bbSGreg Clayton case 'h': 34595fdb09bbSGreg Clayton window.CreateHelpSubwindow(); 34605fdb09bbSGreg Clayton return eKeyHandled; 34615fdb09bbSGreg Clayton 34625fdb09bbSGreg Clayton case KEY_ESCAPE: 34635fdb09bbSGreg Clayton return eQuitApplication; 34645fdb09bbSGreg Clayton 34655fdb09bbSGreg Clayton default: 34665fdb09bbSGreg Clayton break; 346744d93782SGreg Clayton } 346844d93782SGreg Clayton return eKeyNotHandled; 346944d93782SGreg Clayton } 347044d93782SGreg Clayton 3471b9c1b51eSKate Stone const char *WindowDelegateGetHelpText() override { 34725fdb09bbSGreg Clayton return "Welcome to the LLDB curses GUI.\n\n" 34735fdb09bbSGreg Clayton "Press the TAB key to change the selected view.\n" 3474b9c1b51eSKate Stone "Each view has its own keyboard shortcuts, press 'h' to open a " 3475b9c1b51eSKate Stone "dialog to display them.\n\n" 34765fdb09bbSGreg Clayton "Common key bindings for all views:"; 34775fdb09bbSGreg Clayton } 34785fdb09bbSGreg Clayton 3479b9c1b51eSKate Stone KeyHelp *WindowDelegateGetKeyHelp() override { 34805fdb09bbSGreg Clayton static curses::KeyHelp g_source_view_key_help[] = { 34815fdb09bbSGreg Clayton {'\t', "Select next view"}, 34825fdb09bbSGreg Clayton {'h', "Show help dialog with view specific key bindings"}, 34835fdb09bbSGreg Clayton {',', "Page up"}, 34845fdb09bbSGreg Clayton {'.', "Page down"}, 34855fdb09bbSGreg Clayton {KEY_UP, "Select previous"}, 34865fdb09bbSGreg Clayton {KEY_DOWN, "Select next"}, 34875fdb09bbSGreg Clayton {KEY_LEFT, "Unexpand or select parent"}, 34885fdb09bbSGreg Clayton {KEY_RIGHT, "Expand"}, 34895fdb09bbSGreg Clayton {KEY_PPAGE, "Page up"}, 34905fdb09bbSGreg Clayton {KEY_NPAGE, "Page down"}, 3491b9c1b51eSKate Stone {'\0', nullptr}}; 34925fdb09bbSGreg Clayton return g_source_view_key_help; 34935fdb09bbSGreg Clayton } 34945fdb09bbSGreg Clayton 3495b9c1b51eSKate Stone MenuActionResult MenuDelegateAction(Menu &menu) override { 3496b9c1b51eSKate Stone switch (menu.GetIdentifier()) { 3497b9c1b51eSKate Stone case eMenuID_ThreadStepIn: { 3498b9c1b51eSKate Stone ExecutionContext exe_ctx = 3499b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 3500b9c1b51eSKate Stone if (exe_ctx.HasThreadScope()) { 350144d93782SGreg Clayton Process *process = exe_ctx.GetProcessPtr(); 3502b9c1b51eSKate Stone if (process && process->IsAlive() && 3503b9c1b51eSKate Stone StateIsStoppedState(process->GetState(), true)) 35044b4b2478SJim Ingham exe_ctx.GetThreadRef().StepIn(true); 350544d93782SGreg Clayton } 350644d93782SGreg Clayton } 350744d93782SGreg Clayton return MenuActionResult::Handled; 350844d93782SGreg Clayton 3509b9c1b51eSKate Stone case eMenuID_ThreadStepOut: { 3510b9c1b51eSKate Stone ExecutionContext exe_ctx = 3511b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 3512b9c1b51eSKate Stone if (exe_ctx.HasThreadScope()) { 351344d93782SGreg Clayton Process *process = exe_ctx.GetProcessPtr(); 3514b9c1b51eSKate Stone if (process && process->IsAlive() && 3515b9c1b51eSKate Stone StateIsStoppedState(process->GetState(), true)) 351644d93782SGreg Clayton exe_ctx.GetThreadRef().StepOut(); 351744d93782SGreg Clayton } 351844d93782SGreg Clayton } 351944d93782SGreg Clayton return MenuActionResult::Handled; 352044d93782SGreg Clayton 3521b9c1b51eSKate Stone case eMenuID_ThreadStepOver: { 3522b9c1b51eSKate Stone ExecutionContext exe_ctx = 3523b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 3524b9c1b51eSKate Stone if (exe_ctx.HasThreadScope()) { 352544d93782SGreg Clayton Process *process = exe_ctx.GetProcessPtr(); 3526b9c1b51eSKate Stone if (process && process->IsAlive() && 3527b9c1b51eSKate Stone StateIsStoppedState(process->GetState(), true)) 352844d93782SGreg Clayton exe_ctx.GetThreadRef().StepOver(true); 352944d93782SGreg Clayton } 353044d93782SGreg Clayton } 353144d93782SGreg Clayton return MenuActionResult::Handled; 353244d93782SGreg Clayton 3533b9c1b51eSKate Stone case eMenuID_ProcessContinue: { 3534b9c1b51eSKate Stone ExecutionContext exe_ctx = 3535b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 3536b9c1b51eSKate Stone if (exe_ctx.HasProcessScope()) { 353744d93782SGreg Clayton Process *process = exe_ctx.GetProcessPtr(); 3538b9c1b51eSKate Stone if (process && process->IsAlive() && 3539b9c1b51eSKate Stone StateIsStoppedState(process->GetState(), true)) 354044d93782SGreg Clayton process->Resume(); 354144d93782SGreg Clayton } 354244d93782SGreg Clayton } 354344d93782SGreg Clayton return MenuActionResult::Handled; 354444d93782SGreg Clayton 3545b9c1b51eSKate Stone case eMenuID_ProcessKill: { 3546b9c1b51eSKate Stone ExecutionContext exe_ctx = 3547b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 3548b9c1b51eSKate Stone if (exe_ctx.HasProcessScope()) { 354944d93782SGreg Clayton Process *process = exe_ctx.GetProcessPtr(); 355044d93782SGreg Clayton if (process && process->IsAlive()) 3551ede3193bSJason Molenda process->Destroy(false); 355244d93782SGreg Clayton } 355344d93782SGreg Clayton } 355444d93782SGreg Clayton return MenuActionResult::Handled; 355544d93782SGreg Clayton 3556b9c1b51eSKate Stone case eMenuID_ProcessHalt: { 3557b9c1b51eSKate Stone ExecutionContext exe_ctx = 3558b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 3559b9c1b51eSKate Stone if (exe_ctx.HasProcessScope()) { 356044d93782SGreg Clayton Process *process = exe_ctx.GetProcessPtr(); 356144d93782SGreg Clayton if (process && process->IsAlive()) 356244d93782SGreg Clayton process->Halt(); 356344d93782SGreg Clayton } 356444d93782SGreg Clayton } 356544d93782SGreg Clayton return MenuActionResult::Handled; 356644d93782SGreg Clayton 3567b9c1b51eSKate Stone case eMenuID_ProcessDetach: { 3568b9c1b51eSKate Stone ExecutionContext exe_ctx = 3569b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 3570b9c1b51eSKate Stone if (exe_ctx.HasProcessScope()) { 357144d93782SGreg Clayton Process *process = exe_ctx.GetProcessPtr(); 357244d93782SGreg Clayton if (process && process->IsAlive()) 357344d93782SGreg Clayton process->Detach(false); 357444d93782SGreg Clayton } 357544d93782SGreg Clayton } 357644d93782SGreg Clayton return MenuActionResult::Handled; 357744d93782SGreg Clayton 3578b9c1b51eSKate Stone case eMenuID_Process: { 3579b9c1b51eSKate Stone // Populate the menu with all of the threads if the process is stopped 358005097246SAdrian Prantl // when the Process menu gets selected and is about to display its 358105097246SAdrian Prantl // submenu. 358244d93782SGreg Clayton Menus &submenus = menu.GetSubmenus(); 3583b9c1b51eSKate Stone ExecutionContext exe_ctx = 3584b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 358544d93782SGreg Clayton Process *process = exe_ctx.GetProcessPtr(); 3586b9c1b51eSKate Stone if (process && process->IsAlive() && 3587b9c1b51eSKate Stone StateIsStoppedState(process->GetState(), true)) { 358844d93782SGreg Clayton if (submenus.size() == 7) 358944d93782SGreg Clayton menu.AddSubmenu(MenuSP(new Menu(Menu::Type::Separator))); 359044d93782SGreg Clayton else if (submenus.size() > 8) 359144d93782SGreg Clayton submenus.erase(submenus.begin() + 8, submenus.end()); 359244d93782SGreg Clayton 359344d93782SGreg Clayton ThreadList &threads = process->GetThreadList(); 3594bb19a13cSSaleem Abdulrasool std::lock_guard<std::recursive_mutex> guard(threads.GetMutex()); 359544d93782SGreg Clayton size_t num_threads = threads.GetSize(); 3596b9c1b51eSKate Stone for (size_t i = 0; i < num_threads; ++i) { 359744d93782SGreg Clayton ThreadSP thread_sp = threads.GetThreadAtIndex(i); 359844d93782SGreg Clayton char menu_char = '\0'; 359944d93782SGreg Clayton if (i < 9) 360044d93782SGreg Clayton menu_char = '1' + i; 360144d93782SGreg Clayton StreamString thread_menu_title; 360244d93782SGreg Clayton thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID()); 360344d93782SGreg Clayton const char *thread_name = thread_sp->GetName(); 360444d93782SGreg Clayton if (thread_name && thread_name[0]) 360544d93782SGreg Clayton thread_menu_title.Printf(" %s", thread_name); 3606b9c1b51eSKate Stone else { 360744d93782SGreg Clayton const char *queue_name = thread_sp->GetQueueName(); 360844d93782SGreg Clayton if (queue_name && queue_name[0]) 360944d93782SGreg Clayton thread_menu_title.Printf(" %s", queue_name); 361044d93782SGreg Clayton } 3611b9c1b51eSKate Stone menu.AddSubmenu( 3612c156427dSZachary Turner MenuSP(new Menu(thread_menu_title.GetString().str().c_str(), 3613c156427dSZachary Turner nullptr, menu_char, thread_sp->GetID()))); 361444d93782SGreg Clayton } 3615b9c1b51eSKate Stone } else if (submenus.size() > 7) { 361605097246SAdrian Prantl // Remove the separator and any other thread submenu items that were 361705097246SAdrian Prantl // previously added 361844d93782SGreg Clayton submenus.erase(submenus.begin() + 7, submenus.end()); 361944d93782SGreg Clayton } 3620b9c1b51eSKate Stone // Since we are adding and removing items we need to recalculate the name 3621b9c1b51eSKate Stone // lengths 362244d93782SGreg Clayton menu.RecalculateNameLengths(); 362344d93782SGreg Clayton } 362444d93782SGreg Clayton return MenuActionResult::Handled; 362544d93782SGreg Clayton 3626b9c1b51eSKate Stone case eMenuID_ViewVariables: { 362744d93782SGreg Clayton WindowSP main_window_sp = m_app.GetMainWindow(); 362844d93782SGreg Clayton WindowSP source_window_sp = main_window_sp->FindSubWindow("Source"); 362944d93782SGreg Clayton WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables"); 363044d93782SGreg Clayton WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers"); 363144d93782SGreg Clayton const Rect source_bounds = source_window_sp->GetBounds(); 363244d93782SGreg Clayton 3633b9c1b51eSKate Stone if (variables_window_sp) { 363444d93782SGreg Clayton const Rect variables_bounds = variables_window_sp->GetBounds(); 363544d93782SGreg Clayton 363644d93782SGreg Clayton main_window_sp->RemoveSubWindow(variables_window_sp.get()); 363744d93782SGreg Clayton 3638b9c1b51eSKate Stone if (registers_window_sp) { 3639b9c1b51eSKate Stone // We have a registers window, so give all the area back to the 3640b9c1b51eSKate Stone // registers window 364144d93782SGreg Clayton Rect registers_bounds = variables_bounds; 364244d93782SGreg Clayton registers_bounds.size.width = source_bounds.size.width; 364344d93782SGreg Clayton registers_window_sp->SetBounds(registers_bounds); 3644b9c1b51eSKate Stone } else { 364505097246SAdrian Prantl // We have no registers window showing so give the bottom area back 364605097246SAdrian Prantl // to the source view 364744d93782SGreg Clayton source_window_sp->Resize(source_bounds.size.width, 3648b9c1b51eSKate Stone source_bounds.size.height + 3649b9c1b51eSKate Stone variables_bounds.size.height); 365044d93782SGreg Clayton } 3651b9c1b51eSKate Stone } else { 365244d93782SGreg Clayton Rect new_variables_rect; 3653b9c1b51eSKate Stone if (registers_window_sp) { 365444d93782SGreg Clayton // We have a registers window so split the area of the registers 365544d93782SGreg Clayton // window into two columns where the left hand side will be the 365644d93782SGreg Clayton // variables and the right hand side will be the registers 365744d93782SGreg Clayton const Rect variables_bounds = registers_window_sp->GetBounds(); 365844d93782SGreg Clayton Rect new_registers_rect; 3659b9c1b51eSKate Stone variables_bounds.VerticalSplitPercentage(0.50, new_variables_rect, 3660b9c1b51eSKate Stone new_registers_rect); 366144d93782SGreg Clayton registers_window_sp->SetBounds(new_registers_rect); 3662b9c1b51eSKate Stone } else { 366344d93782SGreg Clayton // No variables window, grab the bottom part of the source window 366444d93782SGreg Clayton Rect new_source_rect; 3665b9c1b51eSKate Stone source_bounds.HorizontalSplitPercentage(0.70, new_source_rect, 3666b9c1b51eSKate Stone new_variables_rect); 366744d93782SGreg Clayton source_window_sp->SetBounds(new_source_rect); 366844d93782SGreg Clayton } 3669b9c1b51eSKate Stone WindowSP new_window_sp = main_window_sp->CreateSubWindow( 3670b9c1b51eSKate Stone "Variables", new_variables_rect, false); 3671b9c1b51eSKate Stone new_window_sp->SetDelegate( 3672b9c1b51eSKate Stone WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger))); 367344d93782SGreg Clayton } 367444d93782SGreg Clayton touchwin(stdscr); 367544d93782SGreg Clayton } 367644d93782SGreg Clayton return MenuActionResult::Handled; 367744d93782SGreg Clayton 3678b9c1b51eSKate Stone case eMenuID_ViewRegisters: { 367944d93782SGreg Clayton WindowSP main_window_sp = m_app.GetMainWindow(); 368044d93782SGreg Clayton WindowSP source_window_sp = main_window_sp->FindSubWindow("Source"); 368144d93782SGreg Clayton WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables"); 368244d93782SGreg Clayton WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers"); 368344d93782SGreg Clayton const Rect source_bounds = source_window_sp->GetBounds(); 368444d93782SGreg Clayton 3685b9c1b51eSKate Stone if (registers_window_sp) { 3686b9c1b51eSKate Stone if (variables_window_sp) { 368744d93782SGreg Clayton const Rect variables_bounds = variables_window_sp->GetBounds(); 368844d93782SGreg Clayton 3689b9c1b51eSKate Stone // We have a variables window, so give all the area back to the 3690b9c1b51eSKate Stone // variables window 3691b9c1b51eSKate Stone variables_window_sp->Resize(variables_bounds.size.width + 3692b9c1b51eSKate Stone registers_window_sp->GetWidth(), 369344d93782SGreg Clayton variables_bounds.size.height); 3694b9c1b51eSKate Stone } else { 369505097246SAdrian Prantl // We have no variables window showing so give the bottom area back 369605097246SAdrian Prantl // to the source view 369744d93782SGreg Clayton source_window_sp->Resize(source_bounds.size.width, 3698b9c1b51eSKate Stone source_bounds.size.height + 3699b9c1b51eSKate Stone registers_window_sp->GetHeight()); 370044d93782SGreg Clayton } 370144d93782SGreg Clayton main_window_sp->RemoveSubWindow(registers_window_sp.get()); 3702b9c1b51eSKate Stone } else { 370344d93782SGreg Clayton Rect new_regs_rect; 3704b9c1b51eSKate Stone if (variables_window_sp) { 370505097246SAdrian Prantl // We have a variables window, split it into two columns where the 370605097246SAdrian Prantl // left hand side will be the variables and the right hand side will 370705097246SAdrian Prantl // be the registers 370844d93782SGreg Clayton const Rect variables_bounds = variables_window_sp->GetBounds(); 370944d93782SGreg Clayton Rect new_vars_rect; 3710b9c1b51eSKate Stone variables_bounds.VerticalSplitPercentage(0.50, new_vars_rect, 3711b9c1b51eSKate Stone new_regs_rect); 371244d93782SGreg Clayton variables_window_sp->SetBounds(new_vars_rect); 3713b9c1b51eSKate Stone } else { 371444d93782SGreg Clayton // No registers window, grab the bottom part of the source window 371544d93782SGreg Clayton Rect new_source_rect; 3716b9c1b51eSKate Stone source_bounds.HorizontalSplitPercentage(0.70, new_source_rect, 3717b9c1b51eSKate Stone new_regs_rect); 371844d93782SGreg Clayton source_window_sp->SetBounds(new_source_rect); 371944d93782SGreg Clayton } 3720b9c1b51eSKate Stone WindowSP new_window_sp = 3721b9c1b51eSKate Stone main_window_sp->CreateSubWindow("Registers", new_regs_rect, false); 3722b9c1b51eSKate Stone new_window_sp->SetDelegate( 3723b9c1b51eSKate Stone WindowDelegateSP(new RegistersWindowDelegate(m_debugger))); 372444d93782SGreg Clayton } 372544d93782SGreg Clayton touchwin(stdscr); 372644d93782SGreg Clayton } 372744d93782SGreg Clayton return MenuActionResult::Handled; 372844d93782SGreg Clayton 372944d93782SGreg Clayton case eMenuID_HelpGUIHelp: 37305fdb09bbSGreg Clayton m_app.GetMainWindow()->CreateHelpSubwindow(); 373144d93782SGreg Clayton return MenuActionResult::Handled; 373244d93782SGreg Clayton 373344d93782SGreg Clayton default: 373444d93782SGreg Clayton break; 373544d93782SGreg Clayton } 373644d93782SGreg Clayton 373744d93782SGreg Clayton return MenuActionResult::NotHandled; 373844d93782SGreg Clayton } 3739b9c1b51eSKate Stone 374044d93782SGreg Clayton protected: 374144d93782SGreg Clayton Application &m_app; 374244d93782SGreg Clayton Debugger &m_debugger; 374344d93782SGreg Clayton }; 374444d93782SGreg Clayton 3745b9c1b51eSKate Stone class StatusBarWindowDelegate : public WindowDelegate { 374644d93782SGreg Clayton public: 3747b9c1b51eSKate Stone StatusBarWindowDelegate(Debugger &debugger) : m_debugger(debugger) { 3748b9c1b51eSKate Stone FormatEntity::Parse("Thread: ${thread.id%tid}", m_format); 374944d93782SGreg Clayton } 375044d93782SGreg Clayton 3751315b6884SEugene Zelenko ~StatusBarWindowDelegate() override = default; 3752bd5ae6b4SGreg Clayton 3753b9c1b51eSKate Stone bool WindowDelegateDraw(Window &window, bool force) override { 3754b9c1b51eSKate Stone ExecutionContext exe_ctx = 3755b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 375644d93782SGreg Clayton Process *process = exe_ctx.GetProcessPtr(); 375744d93782SGreg Clayton Thread *thread = exe_ctx.GetThreadPtr(); 375844d93782SGreg Clayton StackFrame *frame = exe_ctx.GetFramePtr(); 375944d93782SGreg Clayton window.Erase(); 376044d93782SGreg Clayton window.SetBackground(2); 376144d93782SGreg Clayton window.MoveCursor(0, 0); 3762b9c1b51eSKate Stone if (process) { 376344d93782SGreg Clayton const StateType state = process->GetState(); 3764b9c1b51eSKate Stone window.Printf("Process: %5" PRIu64 " %10s", process->GetID(), 3765b9c1b51eSKate Stone StateAsCString(state)); 376644d93782SGreg Clayton 3767b9c1b51eSKate Stone if (StateIsStoppedState(state, true)) { 37685b031ebcSEd Maste StreamString strm; 3769b9c1b51eSKate Stone if (thread && FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, 3770b9c1b51eSKate Stone nullptr, nullptr, false, false)) { 377144d93782SGreg Clayton window.MoveCursor(40, 0); 3772c156427dSZachary Turner window.PutCStringTruncated(strm.GetString().str().c_str(), 1); 37735b031ebcSEd Maste } 377444d93782SGreg Clayton 377544d93782SGreg Clayton window.MoveCursor(60, 0); 377644d93782SGreg Clayton if (frame) 3777b9c1b51eSKate Stone window.Printf("Frame: %3u PC = 0x%16.16" PRIx64, 3778b9c1b51eSKate Stone frame->GetFrameIndex(), 3779b9c1b51eSKate Stone frame->GetFrameCodeAddress().GetOpcodeLoadAddress( 3780b9c1b51eSKate Stone exe_ctx.GetTargetPtr())); 3781b9c1b51eSKate Stone } else if (state == eStateExited) { 378244d93782SGreg Clayton const char *exit_desc = process->GetExitDescription(); 378344d93782SGreg Clayton const int exit_status = process->GetExitStatus(); 378444d93782SGreg Clayton if (exit_desc && exit_desc[0]) 378544d93782SGreg Clayton window.Printf(" with status = %i (%s)", exit_status, exit_desc); 378644d93782SGreg Clayton else 378744d93782SGreg Clayton window.Printf(" with status = %i", exit_status); 378844d93782SGreg Clayton } 378944d93782SGreg Clayton } 379044d93782SGreg Clayton window.DeferredRefresh(); 379144d93782SGreg Clayton return true; 379244d93782SGreg Clayton } 379344d93782SGreg Clayton 379444d93782SGreg Clayton protected: 379544d93782SGreg Clayton Debugger &m_debugger; 3796554f68d3SGreg Clayton FormatEntity::Entry m_format; 379744d93782SGreg Clayton }; 379844d93782SGreg Clayton 3799b9c1b51eSKate Stone class SourceFileWindowDelegate : public WindowDelegate { 380044d93782SGreg Clayton public: 3801b9c1b51eSKate Stone SourceFileWindowDelegate(Debugger &debugger) 3802b9c1b51eSKate Stone : WindowDelegate(), m_debugger(debugger), m_sc(), m_file_sp(), 3803b9c1b51eSKate Stone m_disassembly_scope(nullptr), m_disassembly_sp(), m_disassembly_range(), 3804b9c1b51eSKate Stone m_title(), m_line_width(4), m_selected_line(0), m_pc_line(0), 3805b9c1b51eSKate Stone m_stop_id(0), m_frame_idx(UINT32_MAX), m_first_visible_line(0), 3806b9c1b51eSKate Stone m_min_x(0), m_min_y(0), m_max_x(0), m_max_y(0) {} 380744d93782SGreg Clayton 3808315b6884SEugene Zelenko ~SourceFileWindowDelegate() override = default; 380944d93782SGreg Clayton 3810b9c1b51eSKate Stone void Update(const SymbolContext &sc) { m_sc = sc; } 381144d93782SGreg Clayton 3812b9c1b51eSKate Stone uint32_t NumVisibleLines() const { return m_max_y - m_min_y; } 381344d93782SGreg Clayton 3814b9c1b51eSKate Stone const char *WindowDelegateGetHelpText() override { 381544d93782SGreg Clayton return "Source/Disassembly window keyboard shortcuts:"; 381644d93782SGreg Clayton } 381744d93782SGreg Clayton 3818b9c1b51eSKate Stone KeyHelp *WindowDelegateGetKeyHelp() override { 381944d93782SGreg Clayton static curses::KeyHelp g_source_view_key_help[] = { 382044d93782SGreg Clayton {KEY_RETURN, "Run to selected line with one shot breakpoint"}, 382144d93782SGreg Clayton {KEY_UP, "Select previous source line"}, 382244d93782SGreg Clayton {KEY_DOWN, "Select next source line"}, 382344d93782SGreg Clayton {KEY_PPAGE, "Page up"}, 382444d93782SGreg Clayton {KEY_NPAGE, "Page down"}, 382544d93782SGreg Clayton {'b', "Set breakpoint on selected source/disassembly line"}, 382644d93782SGreg Clayton {'c', "Continue process"}, 382744d93782SGreg Clayton {'d', "Detach and resume process"}, 382844d93782SGreg Clayton {'D', "Detach with process suspended"}, 382944d93782SGreg Clayton {'h', "Show help dialog"}, 383044d93782SGreg Clayton {'k', "Kill process"}, 383144d93782SGreg Clayton {'n', "Step over (source line)"}, 383244d93782SGreg Clayton {'N', "Step over (single instruction)"}, 383344d93782SGreg Clayton {'o', "Step out"}, 383444d93782SGreg Clayton {'s', "Step in (source line)"}, 383544d93782SGreg Clayton {'S', "Step in (single instruction)"}, 383644d93782SGreg Clayton {',', "Page up"}, 383744d93782SGreg Clayton {'.', "Page down"}, 3838b9c1b51eSKate Stone {'\0', nullptr}}; 383944d93782SGreg Clayton return g_source_view_key_help; 384044d93782SGreg Clayton } 384144d93782SGreg Clayton 3842b9c1b51eSKate Stone bool WindowDelegateDraw(Window &window, bool force) override { 3843b9c1b51eSKate Stone ExecutionContext exe_ctx = 3844b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 384544d93782SGreg Clayton Process *process = exe_ctx.GetProcessPtr(); 3846c5dac77aSEugene Zelenko Thread *thread = nullptr; 384744d93782SGreg Clayton 384844d93782SGreg Clayton bool update_location = false; 3849b9c1b51eSKate Stone if (process) { 385044d93782SGreg Clayton StateType state = process->GetState(); 3851b9c1b51eSKate Stone if (StateIsStoppedState(state, true)) { 385244d93782SGreg Clayton // We are stopped, so it is ok to 385344d93782SGreg Clayton update_location = true; 385444d93782SGreg Clayton } 385544d93782SGreg Clayton } 385644d93782SGreg Clayton 385744d93782SGreg Clayton m_min_x = 1; 3858ec990867SGreg Clayton m_min_y = 2; 385944d93782SGreg Clayton m_max_x = window.GetMaxX() - 1; 386044d93782SGreg Clayton m_max_y = window.GetMaxY() - 1; 386144d93782SGreg Clayton 386244d93782SGreg Clayton const uint32_t num_visible_lines = NumVisibleLines(); 386344d93782SGreg Clayton StackFrameSP frame_sp; 386444d93782SGreg Clayton bool set_selected_line_to_pc = false; 386544d93782SGreg Clayton 3866b9c1b51eSKate Stone if (update_location) { 386744d93782SGreg Clayton const bool process_alive = process ? process->IsAlive() : false; 386844d93782SGreg Clayton bool thread_changed = false; 3869b9c1b51eSKate Stone if (process_alive) { 387044d93782SGreg Clayton thread = exe_ctx.GetThreadPtr(); 3871b9c1b51eSKate Stone if (thread) { 387244d93782SGreg Clayton frame_sp = thread->GetSelectedFrame(); 387344d93782SGreg Clayton auto tid = thread->GetID(); 387444d93782SGreg Clayton thread_changed = tid != m_tid; 387544d93782SGreg Clayton m_tid = tid; 3876b9c1b51eSKate Stone } else { 3877b9c1b51eSKate Stone if (m_tid != LLDB_INVALID_THREAD_ID) { 387844d93782SGreg Clayton thread_changed = true; 387944d93782SGreg Clayton m_tid = LLDB_INVALID_THREAD_ID; 388044d93782SGreg Clayton } 388144d93782SGreg Clayton } 388244d93782SGreg Clayton } 388344d93782SGreg Clayton const uint32_t stop_id = process ? process->GetStopID() : 0; 388444d93782SGreg Clayton const bool stop_id_changed = stop_id != m_stop_id; 388544d93782SGreg Clayton bool frame_changed = false; 388644d93782SGreg Clayton m_stop_id = stop_id; 3887ec990867SGreg Clayton m_title.Clear(); 3888b9c1b51eSKate Stone if (frame_sp) { 388944d93782SGreg Clayton m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything); 3890b9c1b51eSKate Stone if (m_sc.module_sp) { 3891b9c1b51eSKate Stone m_title.Printf( 3892b9c1b51eSKate Stone "%s", m_sc.module_sp->GetFileSpec().GetFilename().GetCString()); 3893ec990867SGreg Clayton ConstString func_name = m_sc.GetFunctionName(); 3894ec990867SGreg Clayton if (func_name) 3895ec990867SGreg Clayton m_title.Printf("`%s", func_name.GetCString()); 3896ec990867SGreg Clayton } 389744d93782SGreg Clayton const uint32_t frame_idx = frame_sp->GetFrameIndex(); 389844d93782SGreg Clayton frame_changed = frame_idx != m_frame_idx; 389944d93782SGreg Clayton m_frame_idx = frame_idx; 3900b9c1b51eSKate Stone } else { 390144d93782SGreg Clayton m_sc.Clear(true); 390244d93782SGreg Clayton frame_changed = m_frame_idx != UINT32_MAX; 390344d93782SGreg Clayton m_frame_idx = UINT32_MAX; 390444d93782SGreg Clayton } 390544d93782SGreg Clayton 3906b9c1b51eSKate Stone const bool context_changed = 3907b9c1b51eSKate Stone thread_changed || frame_changed || stop_id_changed; 390844d93782SGreg Clayton 3909b9c1b51eSKate Stone if (process_alive) { 3910b9c1b51eSKate Stone if (m_sc.line_entry.IsValid()) { 391144d93782SGreg Clayton m_pc_line = m_sc.line_entry.line; 391244d93782SGreg Clayton if (m_pc_line != UINT32_MAX) 391344d93782SGreg Clayton --m_pc_line; // Convert to zero based line number... 391444d93782SGreg Clayton // Update the selected line if the stop ID changed... 391544d93782SGreg Clayton if (context_changed) 391644d93782SGreg Clayton m_selected_line = m_pc_line; 391744d93782SGreg Clayton 3918b9c1b51eSKate Stone if (m_file_sp && m_file_sp->FileSpecMatches(m_sc.line_entry.file)) { 391905097246SAdrian Prantl // Same file, nothing to do, we should either have the lines or not 392005097246SAdrian Prantl // (source file missing) 3921b9c1b51eSKate Stone if (m_selected_line >= static_cast<size_t>(m_first_visible_line)) { 392244d93782SGreg Clayton if (m_selected_line >= m_first_visible_line + num_visible_lines) 392344d93782SGreg Clayton m_first_visible_line = m_selected_line - 10; 3924b9c1b51eSKate Stone } else { 392544d93782SGreg Clayton if (m_selected_line > 10) 392644d93782SGreg Clayton m_first_visible_line = m_selected_line - 10; 392744d93782SGreg Clayton else 392844d93782SGreg Clayton m_first_visible_line = 0; 392944d93782SGreg Clayton } 3930b9c1b51eSKate Stone } else { 393144d93782SGreg Clayton // File changed, set selected line to the line with the PC 393244d93782SGreg Clayton m_selected_line = m_pc_line; 3933b9c1b51eSKate Stone m_file_sp = 3934b9c1b51eSKate Stone m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file); 3935b9c1b51eSKate Stone if (m_file_sp) { 393644d93782SGreg Clayton const size_t num_lines = m_file_sp->GetNumLines(); 393744d93782SGreg Clayton int m_line_width = 1; 393844d93782SGreg Clayton for (size_t n = num_lines; n >= 10; n = n / 10) 393944d93782SGreg Clayton ++m_line_width; 394044d93782SGreg Clayton 3941b9c1b51eSKate Stone snprintf(m_line_format, sizeof(m_line_format), " %%%iu ", 3942b9c1b51eSKate Stone m_line_width); 3943b9c1b51eSKate Stone if (num_lines < num_visible_lines || 3944b9c1b51eSKate Stone m_selected_line < num_visible_lines) 394544d93782SGreg Clayton m_first_visible_line = 0; 394644d93782SGreg Clayton else 394744d93782SGreg Clayton m_first_visible_line = m_selected_line - 10; 394844d93782SGreg Clayton } 394944d93782SGreg Clayton } 3950b9c1b51eSKate Stone } else { 395144d93782SGreg Clayton m_file_sp.reset(); 395244d93782SGreg Clayton } 395344d93782SGreg Clayton 3954b9c1b51eSKate Stone if (!m_file_sp || m_file_sp->GetNumLines() == 0) { 395544d93782SGreg Clayton // Show disassembly 395644d93782SGreg Clayton bool prefer_file_cache = false; 3957b9c1b51eSKate Stone if (m_sc.function) { 3958b9c1b51eSKate Stone if (m_disassembly_scope != m_sc.function) { 395944d93782SGreg Clayton m_disassembly_scope = m_sc.function; 3960b9c1b51eSKate Stone m_disassembly_sp = m_sc.function->GetInstructions( 3961b9c1b51eSKate Stone exe_ctx, nullptr, prefer_file_cache); 3962b9c1b51eSKate Stone if (m_disassembly_sp) { 396344d93782SGreg Clayton set_selected_line_to_pc = true; 396444d93782SGreg Clayton m_disassembly_range = m_sc.function->GetAddressRange(); 3965b9c1b51eSKate Stone } else { 396644d93782SGreg Clayton m_disassembly_range.Clear(); 396744d93782SGreg Clayton } 3968b9c1b51eSKate Stone } else { 396944d93782SGreg Clayton set_selected_line_to_pc = context_changed; 397044d93782SGreg Clayton } 3971b9c1b51eSKate Stone } else if (m_sc.symbol) { 3972b9c1b51eSKate Stone if (m_disassembly_scope != m_sc.symbol) { 397344d93782SGreg Clayton m_disassembly_scope = m_sc.symbol; 3974b9c1b51eSKate Stone m_disassembly_sp = m_sc.symbol->GetInstructions( 3975b9c1b51eSKate Stone exe_ctx, nullptr, prefer_file_cache); 3976b9c1b51eSKate Stone if (m_disassembly_sp) { 397744d93782SGreg Clayton set_selected_line_to_pc = true; 3978b9c1b51eSKate Stone m_disassembly_range.GetBaseAddress() = 3979b9c1b51eSKate Stone m_sc.symbol->GetAddress(); 398044d93782SGreg Clayton m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize()); 3981b9c1b51eSKate Stone } else { 398244d93782SGreg Clayton m_disassembly_range.Clear(); 398344d93782SGreg Clayton } 3984b9c1b51eSKate Stone } else { 398544d93782SGreg Clayton set_selected_line_to_pc = context_changed; 398644d93782SGreg Clayton } 398744d93782SGreg Clayton } 398844d93782SGreg Clayton } 3989b9c1b51eSKate Stone } else { 399044d93782SGreg Clayton m_pc_line = UINT32_MAX; 399144d93782SGreg Clayton } 399244d93782SGreg Clayton } 399344d93782SGreg Clayton 3994ec990867SGreg Clayton const int window_width = window.GetWidth(); 399544d93782SGreg Clayton window.Erase(); 399644d93782SGreg Clayton window.DrawTitleBox("Sources"); 3997b9c1b51eSKate Stone if (!m_title.GetString().empty()) { 3998ec990867SGreg Clayton window.AttributeOn(A_REVERSE); 3999ec990867SGreg Clayton window.MoveCursor(1, 1); 4000ec990867SGreg Clayton window.PutChar(' '); 4001c156427dSZachary Turner window.PutCStringTruncated(m_title.GetString().str().c_str(), 1); 4002ec990867SGreg Clayton int x = window.GetCursorX(); 4003b9c1b51eSKate Stone if (x < window_width - 1) { 4004ec990867SGreg Clayton window.Printf("%*s", window_width - x - 1, ""); 4005ec990867SGreg Clayton } 4006ec990867SGreg Clayton window.AttributeOff(A_REVERSE); 4007ec990867SGreg Clayton } 400844d93782SGreg Clayton 400944d93782SGreg Clayton Target *target = exe_ctx.GetTargetPtr(); 401044d93782SGreg Clayton const size_t num_source_lines = GetNumSourceLines(); 4011b9c1b51eSKate Stone if (num_source_lines > 0) { 401244d93782SGreg Clayton // Display source 401344d93782SGreg Clayton BreakpointLines bp_lines; 4014b9c1b51eSKate Stone if (target) { 401544d93782SGreg Clayton BreakpointList &bp_list = target->GetBreakpointList(); 401644d93782SGreg Clayton const size_t num_bps = bp_list.GetSize(); 4017b9c1b51eSKate Stone for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) { 401844d93782SGreg Clayton BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx); 401944d93782SGreg Clayton const size_t num_bps_locs = bp_sp->GetNumLocations(); 4020b9c1b51eSKate Stone for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) { 4021b9c1b51eSKate Stone BreakpointLocationSP bp_loc_sp = 4022b9c1b51eSKate Stone bp_sp->GetLocationAtIndex(bp_loc_idx); 402344d93782SGreg Clayton LineEntry bp_loc_line_entry; 4024b9c1b51eSKate Stone if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry( 4025b9c1b51eSKate Stone bp_loc_line_entry)) { 4026b9c1b51eSKate Stone if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file) { 402744d93782SGreg Clayton bp_lines.insert(bp_loc_line_entry.line); 402844d93782SGreg Clayton } 402944d93782SGreg Clayton } 403044d93782SGreg Clayton } 403144d93782SGreg Clayton } 403244d93782SGreg Clayton } 403344d93782SGreg Clayton 403444d93782SGreg Clayton const attr_t selected_highlight_attr = A_REVERSE; 403544d93782SGreg Clayton const attr_t pc_highlight_attr = COLOR_PAIR(1); 403644d93782SGreg Clayton 4037b9c1b51eSKate Stone for (size_t i = 0; i < num_visible_lines; ++i) { 403844d93782SGreg Clayton const uint32_t curr_line = m_first_visible_line + i; 4039b9c1b51eSKate Stone if (curr_line < num_source_lines) { 4040ec990867SGreg Clayton const int line_y = m_min_y + i; 404144d93782SGreg Clayton window.MoveCursor(1, line_y); 404244d93782SGreg Clayton const bool is_pc_line = curr_line == m_pc_line; 404344d93782SGreg Clayton const bool line_is_selected = m_selected_line == curr_line; 404444d93782SGreg Clayton // Highlight the line as the PC line first, then if the selected line 404544d93782SGreg Clayton // isn't the same as the PC line, highlight it differently 404644d93782SGreg Clayton attr_t highlight_attr = 0; 404744d93782SGreg Clayton attr_t bp_attr = 0; 404844d93782SGreg Clayton if (is_pc_line) 404944d93782SGreg Clayton highlight_attr = pc_highlight_attr; 405044d93782SGreg Clayton else if (line_is_selected) 405144d93782SGreg Clayton highlight_attr = selected_highlight_attr; 405244d93782SGreg Clayton 405344d93782SGreg Clayton if (bp_lines.find(curr_line + 1) != bp_lines.end()) 405444d93782SGreg Clayton bp_attr = COLOR_PAIR(2); 405544d93782SGreg Clayton 405644d93782SGreg Clayton if (bp_attr) 405744d93782SGreg Clayton window.AttributeOn(bp_attr); 405844d93782SGreg Clayton 405944d93782SGreg Clayton window.Printf(m_line_format, curr_line + 1); 406044d93782SGreg Clayton 406144d93782SGreg Clayton if (bp_attr) 406244d93782SGreg Clayton window.AttributeOff(bp_attr); 406344d93782SGreg Clayton 406444d93782SGreg Clayton window.PutChar(ACS_VLINE); 406544d93782SGreg Clayton // Mark the line with the PC with a diamond 406644d93782SGreg Clayton if (is_pc_line) 406744d93782SGreg Clayton window.PutChar(ACS_DIAMOND); 406844d93782SGreg Clayton else 406944d93782SGreg Clayton window.PutChar(' '); 407044d93782SGreg Clayton 407144d93782SGreg Clayton if (highlight_attr) 407244d93782SGreg Clayton window.AttributeOn(highlight_attr); 4073b9c1b51eSKate Stone const uint32_t line_len = 4074b9c1b51eSKate Stone m_file_sp->GetLineLength(curr_line + 1, false); 407544d93782SGreg Clayton if (line_len > 0) 407644d93782SGreg Clayton window.PutCString(m_file_sp->PeekLineData(curr_line + 1), line_len); 407744d93782SGreg Clayton 4078b9c1b51eSKate Stone if (is_pc_line && frame_sp && 4079b9c1b51eSKate Stone frame_sp->GetConcreteFrameIndex() == 0) { 408044d93782SGreg Clayton StopInfoSP stop_info_sp; 408144d93782SGreg Clayton if (thread) 408244d93782SGreg Clayton stop_info_sp = thread->GetStopInfo(); 4083b9c1b51eSKate Stone if (stop_info_sp) { 408444d93782SGreg Clayton const char *stop_description = stop_info_sp->GetDescription(); 4085b9c1b51eSKate Stone if (stop_description && stop_description[0]) { 408644d93782SGreg Clayton size_t stop_description_len = strlen(stop_description); 4087ec990867SGreg Clayton int desc_x = window_width - stop_description_len - 16; 408844d93782SGreg Clayton window.Printf("%*s", desc_x - window.GetCursorX(), ""); 4089b9c1b51eSKate Stone // window.MoveCursor(window_width - stop_description_len - 15, 4090b9c1b51eSKate Stone // line_y); 4091b9c1b51eSKate Stone window.Printf("<<< Thread %u: %s ", thread->GetIndexID(), 4092b9c1b51eSKate Stone stop_description); 409344d93782SGreg Clayton } 4094b9c1b51eSKate Stone } else { 4095ec990867SGreg Clayton window.Printf("%*s", window_width - window.GetCursorX() - 1, ""); 409644d93782SGreg Clayton } 409744d93782SGreg Clayton } 409844d93782SGreg Clayton if (highlight_attr) 409944d93782SGreg Clayton window.AttributeOff(highlight_attr); 4100b9c1b51eSKate Stone } else { 410144d93782SGreg Clayton break; 410244d93782SGreg Clayton } 410344d93782SGreg Clayton } 4104b9c1b51eSKate Stone } else { 410544d93782SGreg Clayton size_t num_disassembly_lines = GetNumDisassemblyLines(); 4106b9c1b51eSKate Stone if (num_disassembly_lines > 0) { 410744d93782SGreg Clayton // Display disassembly 410844d93782SGreg Clayton BreakpointAddrs bp_file_addrs; 410944d93782SGreg Clayton Target *target = exe_ctx.GetTargetPtr(); 4110b9c1b51eSKate Stone if (target) { 411144d93782SGreg Clayton BreakpointList &bp_list = target->GetBreakpointList(); 411244d93782SGreg Clayton const size_t num_bps = bp_list.GetSize(); 4113b9c1b51eSKate Stone for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) { 411444d93782SGreg Clayton BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx); 411544d93782SGreg Clayton const size_t num_bps_locs = bp_sp->GetNumLocations(); 4116b9c1b51eSKate Stone for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; 4117b9c1b51eSKate Stone ++bp_loc_idx) { 4118b9c1b51eSKate Stone BreakpointLocationSP bp_loc_sp = 4119b9c1b51eSKate Stone bp_sp->GetLocationAtIndex(bp_loc_idx); 412044d93782SGreg Clayton LineEntry bp_loc_line_entry; 4121b9c1b51eSKate Stone const lldb::addr_t file_addr = 4122b9c1b51eSKate Stone bp_loc_sp->GetAddress().GetFileAddress(); 4123b9c1b51eSKate Stone if (file_addr != LLDB_INVALID_ADDRESS) { 412444d93782SGreg Clayton if (m_disassembly_range.ContainsFileAddress(file_addr)) 412544d93782SGreg Clayton bp_file_addrs.insert(file_addr); 412644d93782SGreg Clayton } 412744d93782SGreg Clayton } 412844d93782SGreg Clayton } 412944d93782SGreg Clayton } 413044d93782SGreg Clayton 413144d93782SGreg Clayton const attr_t selected_highlight_attr = A_REVERSE; 413244d93782SGreg Clayton const attr_t pc_highlight_attr = COLOR_PAIR(1); 413344d93782SGreg Clayton 413444d93782SGreg Clayton StreamString strm; 413544d93782SGreg Clayton 413644d93782SGreg Clayton InstructionList &insts = m_disassembly_sp->GetInstructionList(); 413744d93782SGreg Clayton Address pc_address; 413844d93782SGreg Clayton 413944d93782SGreg Clayton if (frame_sp) 414044d93782SGreg Clayton pc_address = frame_sp->GetFrameCodeAddress(); 4141b9c1b51eSKate Stone const uint32_t pc_idx = 4142b9c1b51eSKate Stone pc_address.IsValid() 4143b9c1b51eSKate Stone ? insts.GetIndexOfInstructionAtAddress(pc_address) 4144b9c1b51eSKate Stone : UINT32_MAX; 4145b9c1b51eSKate Stone if (set_selected_line_to_pc) { 414644d93782SGreg Clayton m_selected_line = pc_idx; 414744d93782SGreg Clayton } 414844d93782SGreg Clayton 414944d93782SGreg Clayton const uint32_t non_visible_pc_offset = (num_visible_lines / 5); 41503985c8c6SSaleem Abdulrasool if (static_cast<size_t>(m_first_visible_line) >= num_disassembly_lines) 415144d93782SGreg Clayton m_first_visible_line = 0; 415244d93782SGreg Clayton 4153b9c1b51eSKate Stone if (pc_idx < num_disassembly_lines) { 41543985c8c6SSaleem Abdulrasool if (pc_idx < static_cast<uint32_t>(m_first_visible_line) || 415544d93782SGreg Clayton pc_idx >= m_first_visible_line + num_visible_lines) 415644d93782SGreg Clayton m_first_visible_line = pc_idx - non_visible_pc_offset; 415744d93782SGreg Clayton } 415844d93782SGreg Clayton 4159b9c1b51eSKate Stone for (size_t i = 0; i < num_visible_lines; ++i) { 416044d93782SGreg Clayton const uint32_t inst_idx = m_first_visible_line + i; 416144d93782SGreg Clayton Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get(); 416244d93782SGreg Clayton if (!inst) 416344d93782SGreg Clayton break; 416444d93782SGreg Clayton 4165ec990867SGreg Clayton const int line_y = m_min_y + i; 4166ec990867SGreg Clayton window.MoveCursor(1, line_y); 416744d93782SGreg Clayton const bool is_pc_line = frame_sp && inst_idx == pc_idx; 416844d93782SGreg Clayton const bool line_is_selected = m_selected_line == inst_idx; 416944d93782SGreg Clayton // Highlight the line as the PC line first, then if the selected line 417044d93782SGreg Clayton // isn't the same as the PC line, highlight it differently 417144d93782SGreg Clayton attr_t highlight_attr = 0; 417244d93782SGreg Clayton attr_t bp_attr = 0; 417344d93782SGreg Clayton if (is_pc_line) 417444d93782SGreg Clayton highlight_attr = pc_highlight_attr; 417544d93782SGreg Clayton else if (line_is_selected) 417644d93782SGreg Clayton highlight_attr = selected_highlight_attr; 417744d93782SGreg Clayton 4178b9c1b51eSKate Stone if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) != 4179b9c1b51eSKate Stone bp_file_addrs.end()) 418044d93782SGreg Clayton bp_attr = COLOR_PAIR(2); 418144d93782SGreg Clayton 418244d93782SGreg Clayton if (bp_attr) 418344d93782SGreg Clayton window.AttributeOn(bp_attr); 418444d93782SGreg Clayton 4185324a1036SSaleem Abdulrasool window.Printf(" 0x%16.16llx ", 4186b9c1b51eSKate Stone static_cast<unsigned long long>( 4187b9c1b51eSKate Stone inst->GetAddress().GetLoadAddress(target))); 418844d93782SGreg Clayton 418944d93782SGreg Clayton if (bp_attr) 419044d93782SGreg Clayton window.AttributeOff(bp_attr); 419144d93782SGreg Clayton 419244d93782SGreg Clayton window.PutChar(ACS_VLINE); 419344d93782SGreg Clayton // Mark the line with the PC with a diamond 419444d93782SGreg Clayton if (is_pc_line) 419544d93782SGreg Clayton window.PutChar(ACS_DIAMOND); 419644d93782SGreg Clayton else 419744d93782SGreg Clayton window.PutChar(' '); 419844d93782SGreg Clayton 419944d93782SGreg Clayton if (highlight_attr) 420044d93782SGreg Clayton window.AttributeOn(highlight_attr); 420144d93782SGreg Clayton 420244d93782SGreg Clayton const char *mnemonic = inst->GetMnemonic(&exe_ctx); 420344d93782SGreg Clayton const char *operands = inst->GetOperands(&exe_ctx); 420444d93782SGreg Clayton const char *comment = inst->GetComment(&exe_ctx); 420544d93782SGreg Clayton 4206c5dac77aSEugene Zelenko if (mnemonic != nullptr && mnemonic[0] == '\0') 4207c5dac77aSEugene Zelenko mnemonic = nullptr; 4208c5dac77aSEugene Zelenko if (operands != nullptr && operands[0] == '\0') 4209c5dac77aSEugene Zelenko operands = nullptr; 4210c5dac77aSEugene Zelenko if (comment != nullptr && comment[0] == '\0') 4211c5dac77aSEugene Zelenko comment = nullptr; 421244d93782SGreg Clayton 421344d93782SGreg Clayton strm.Clear(); 421444d93782SGreg Clayton 4215c5dac77aSEugene Zelenko if (mnemonic != nullptr && operands != nullptr && comment != nullptr) 421644d93782SGreg Clayton strm.Printf("%-8s %-25s ; %s", mnemonic, operands, comment); 4217c5dac77aSEugene Zelenko else if (mnemonic != nullptr && operands != nullptr) 421844d93782SGreg Clayton strm.Printf("%-8s %s", mnemonic, operands); 4219c5dac77aSEugene Zelenko else if (mnemonic != nullptr) 422044d93782SGreg Clayton strm.Printf("%s", mnemonic); 422144d93782SGreg Clayton 422244d93782SGreg Clayton int right_pad = 1; 4223c156427dSZachary Turner window.PutCStringTruncated(strm.GetData(), right_pad); 422444d93782SGreg Clayton 4225b9c1b51eSKate Stone if (is_pc_line && frame_sp && 4226b9c1b51eSKate Stone frame_sp->GetConcreteFrameIndex() == 0) { 422744d93782SGreg Clayton StopInfoSP stop_info_sp; 422844d93782SGreg Clayton if (thread) 422944d93782SGreg Clayton stop_info_sp = thread->GetStopInfo(); 4230b9c1b51eSKate Stone if (stop_info_sp) { 423144d93782SGreg Clayton const char *stop_description = stop_info_sp->GetDescription(); 4232b9c1b51eSKate Stone if (stop_description && stop_description[0]) { 423344d93782SGreg Clayton size_t stop_description_len = strlen(stop_description); 4234ec990867SGreg Clayton int desc_x = window_width - stop_description_len - 16; 423544d93782SGreg Clayton window.Printf("%*s", desc_x - window.GetCursorX(), ""); 4236b9c1b51eSKate Stone // window.MoveCursor(window_width - stop_description_len - 15, 4237b9c1b51eSKate Stone // line_y); 4238b9c1b51eSKate Stone window.Printf("<<< Thread %u: %s ", thread->GetIndexID(), 4239b9c1b51eSKate Stone stop_description); 424044d93782SGreg Clayton } 4241b9c1b51eSKate Stone } else { 4242ec990867SGreg Clayton window.Printf("%*s", window_width - window.GetCursorX() - 1, ""); 424344d93782SGreg Clayton } 424444d93782SGreg Clayton } 424544d93782SGreg Clayton if (highlight_attr) 424644d93782SGreg Clayton window.AttributeOff(highlight_attr); 424744d93782SGreg Clayton } 424844d93782SGreg Clayton } 424944d93782SGreg Clayton } 425044d93782SGreg Clayton window.DeferredRefresh(); 425144d93782SGreg Clayton return true; // Drawing handled 425244d93782SGreg Clayton } 425344d93782SGreg Clayton 4254b9c1b51eSKate Stone size_t GetNumLines() { 425544d93782SGreg Clayton size_t num_lines = GetNumSourceLines(); 425644d93782SGreg Clayton if (num_lines == 0) 425744d93782SGreg Clayton num_lines = GetNumDisassemblyLines(); 425844d93782SGreg Clayton return num_lines; 425944d93782SGreg Clayton } 426044d93782SGreg Clayton 4261b9c1b51eSKate Stone size_t GetNumSourceLines() const { 426244d93782SGreg Clayton if (m_file_sp) 426344d93782SGreg Clayton return m_file_sp->GetNumLines(); 426444d93782SGreg Clayton return 0; 426544d93782SGreg Clayton } 4266315b6884SEugene Zelenko 4267b9c1b51eSKate Stone size_t GetNumDisassemblyLines() const { 426844d93782SGreg Clayton if (m_disassembly_sp) 426944d93782SGreg Clayton return m_disassembly_sp->GetInstructionList().GetSize(); 427044d93782SGreg Clayton return 0; 427144d93782SGreg Clayton } 427244d93782SGreg Clayton 4273b9c1b51eSKate Stone HandleCharResult WindowDelegateHandleChar(Window &window, int c) override { 427444d93782SGreg Clayton const uint32_t num_visible_lines = NumVisibleLines(); 427544d93782SGreg Clayton const size_t num_lines = GetNumLines(); 427644d93782SGreg Clayton 4277b9c1b51eSKate Stone switch (c) { 427844d93782SGreg Clayton case ',': 427944d93782SGreg Clayton case KEY_PPAGE: 428044d93782SGreg Clayton // Page up key 42813985c8c6SSaleem Abdulrasool if (static_cast<uint32_t>(m_first_visible_line) > num_visible_lines) 428244d93782SGreg Clayton m_first_visible_line -= num_visible_lines; 428344d93782SGreg Clayton else 428444d93782SGreg Clayton m_first_visible_line = 0; 428544d93782SGreg Clayton m_selected_line = m_first_visible_line; 428644d93782SGreg Clayton return eKeyHandled; 428744d93782SGreg Clayton 428844d93782SGreg Clayton case '.': 428944d93782SGreg Clayton case KEY_NPAGE: 429044d93782SGreg Clayton // Page down key 429144d93782SGreg Clayton { 429244d93782SGreg Clayton if (m_first_visible_line + num_visible_lines < num_lines) 429344d93782SGreg Clayton m_first_visible_line += num_visible_lines; 429444d93782SGreg Clayton else if (num_lines < num_visible_lines) 429544d93782SGreg Clayton m_first_visible_line = 0; 429644d93782SGreg Clayton else 429744d93782SGreg Clayton m_first_visible_line = num_lines - num_visible_lines; 429844d93782SGreg Clayton m_selected_line = m_first_visible_line; 429944d93782SGreg Clayton } 430044d93782SGreg Clayton return eKeyHandled; 430144d93782SGreg Clayton 430244d93782SGreg Clayton case KEY_UP: 4303b9c1b51eSKate Stone if (m_selected_line > 0) { 430444d93782SGreg Clayton m_selected_line--; 43053985c8c6SSaleem Abdulrasool if (static_cast<size_t>(m_first_visible_line) > m_selected_line) 430644d93782SGreg Clayton m_first_visible_line = m_selected_line; 430744d93782SGreg Clayton } 430844d93782SGreg Clayton return eKeyHandled; 430944d93782SGreg Clayton 431044d93782SGreg Clayton case KEY_DOWN: 4311b9c1b51eSKate Stone if (m_selected_line + 1 < num_lines) { 431244d93782SGreg Clayton m_selected_line++; 431344d93782SGreg Clayton if (m_first_visible_line + num_visible_lines < m_selected_line) 431444d93782SGreg Clayton m_first_visible_line++; 431544d93782SGreg Clayton } 431644d93782SGreg Clayton return eKeyHandled; 431744d93782SGreg Clayton 431844d93782SGreg Clayton case '\r': 431944d93782SGreg Clayton case '\n': 432044d93782SGreg Clayton case KEY_ENTER: 432144d93782SGreg Clayton // Set a breakpoint and run to the line using a one shot breakpoint 4322b9c1b51eSKate Stone if (GetNumSourceLines() > 0) { 4323b9c1b51eSKate Stone ExecutionContext exe_ctx = 4324b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 4325b9c1b51eSKate Stone if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive()) { 4326b9c1b51eSKate Stone BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint( 4327b9c1b51eSKate Stone nullptr, // Don't limit the breakpoint to certain modules 432844d93782SGreg Clayton m_file_sp->GetFileSpec(), // Source file 4329b9c1b51eSKate Stone m_selected_line + 4330b9c1b51eSKate Stone 1, // Source line number (m_selected_line is zero based) 4331431b1584SAdrian Prantl 0, // Unspecified column. 43322411167fSJim Ingham 0, // No offset 433344d93782SGreg Clayton eLazyBoolCalculate, // Check inlines using global setting 433444d93782SGreg Clayton eLazyBoolCalculate, // Skip prologue using global setting, 433544d93782SGreg Clayton false, // internal 4336055ad9beSIlia K false, // request_hardware 4337055ad9beSIlia K eLazyBoolCalculate); // move_to_nearest_code 433844d93782SGreg Clayton // Make breakpoint one shot 433944d93782SGreg Clayton bp_sp->GetOptions()->SetOneShot(true); 434044d93782SGreg Clayton exe_ctx.GetProcessRef().Resume(); 434144d93782SGreg Clayton } 4342b9c1b51eSKate Stone } else if (m_selected_line < GetNumDisassemblyLines()) { 4343b9c1b51eSKate Stone const Instruction *inst = m_disassembly_sp->GetInstructionList() 4344b9c1b51eSKate Stone .GetInstructionAtIndex(m_selected_line) 4345b9c1b51eSKate Stone .get(); 4346b9c1b51eSKate Stone ExecutionContext exe_ctx = 4347b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 4348b9c1b51eSKate Stone if (exe_ctx.HasTargetScope()) { 434944d93782SGreg Clayton Address addr = inst->GetAddress(); 4350b9c1b51eSKate Stone BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint( 4351b9c1b51eSKate Stone addr, // lldb_private::Address 435244d93782SGreg Clayton false, // internal 435344d93782SGreg Clayton false); // request_hardware 435444d93782SGreg Clayton // Make breakpoint one shot 435544d93782SGreg Clayton bp_sp->GetOptions()->SetOneShot(true); 435644d93782SGreg Clayton exe_ctx.GetProcessRef().Resume(); 435744d93782SGreg Clayton } 435844d93782SGreg Clayton } 435944d93782SGreg Clayton return eKeyHandled; 436044d93782SGreg Clayton 436144d93782SGreg Clayton case 'b': // 'b' == toggle breakpoint on currently selected line 4362b9c1b51eSKate Stone if (m_selected_line < GetNumSourceLines()) { 4363b9c1b51eSKate Stone ExecutionContext exe_ctx = 4364b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 4365b9c1b51eSKate Stone if (exe_ctx.HasTargetScope()) { 4366b9c1b51eSKate Stone BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint( 4367b9c1b51eSKate Stone nullptr, // Don't limit the breakpoint to certain modules 436844d93782SGreg Clayton m_file_sp->GetFileSpec(), // Source file 4369b9c1b51eSKate Stone m_selected_line + 4370b9c1b51eSKate Stone 1, // Source line number (m_selected_line is zero based) 4371431b1584SAdrian Prantl 0, // No column specified. 43722411167fSJim Ingham 0, // No offset 437344d93782SGreg Clayton eLazyBoolCalculate, // Check inlines using global setting 437444d93782SGreg Clayton eLazyBoolCalculate, // Skip prologue using global setting, 437544d93782SGreg Clayton false, // internal 4376055ad9beSIlia K false, // request_hardware 4377055ad9beSIlia K eLazyBoolCalculate); // move_to_nearest_code 437844d93782SGreg Clayton } 4379b9c1b51eSKate Stone } else if (m_selected_line < GetNumDisassemblyLines()) { 4380b9c1b51eSKate Stone const Instruction *inst = m_disassembly_sp->GetInstructionList() 4381b9c1b51eSKate Stone .GetInstructionAtIndex(m_selected_line) 4382b9c1b51eSKate Stone .get(); 4383b9c1b51eSKate Stone ExecutionContext exe_ctx = 4384b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 4385b9c1b51eSKate Stone if (exe_ctx.HasTargetScope()) { 438644d93782SGreg Clayton Address addr = inst->GetAddress(); 4387b9c1b51eSKate Stone BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint( 4388b9c1b51eSKate Stone addr, // lldb_private::Address 438944d93782SGreg Clayton false, // internal 439044d93782SGreg Clayton false); // request_hardware 439144d93782SGreg Clayton } 439244d93782SGreg Clayton } 439344d93782SGreg Clayton return eKeyHandled; 439444d93782SGreg Clayton 439544d93782SGreg Clayton case 'd': // 'd' == detach and let run 439644d93782SGreg Clayton case 'D': // 'D' == detach and keep stopped 439744d93782SGreg Clayton { 4398b9c1b51eSKate Stone ExecutionContext exe_ctx = 4399b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 440044d93782SGreg Clayton if (exe_ctx.HasProcessScope()) 440144d93782SGreg Clayton exe_ctx.GetProcessRef().Detach(c == 'D'); 440244d93782SGreg Clayton } 440344d93782SGreg Clayton return eKeyHandled; 440444d93782SGreg Clayton 440544d93782SGreg Clayton case 'k': 440644d93782SGreg Clayton // 'k' == kill 440744d93782SGreg Clayton { 4408b9c1b51eSKate Stone ExecutionContext exe_ctx = 4409b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 441044d93782SGreg Clayton if (exe_ctx.HasProcessScope()) 4411ede3193bSJason Molenda exe_ctx.GetProcessRef().Destroy(false); 441244d93782SGreg Clayton } 441344d93782SGreg Clayton return eKeyHandled; 441444d93782SGreg Clayton 441544d93782SGreg Clayton case 'c': 441644d93782SGreg Clayton // 'c' == continue 441744d93782SGreg Clayton { 4418b9c1b51eSKate Stone ExecutionContext exe_ctx = 4419b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 442044d93782SGreg Clayton if (exe_ctx.HasProcessScope()) 442144d93782SGreg Clayton exe_ctx.GetProcessRef().Resume(); 442244d93782SGreg Clayton } 442344d93782SGreg Clayton return eKeyHandled; 442444d93782SGreg Clayton 442544d93782SGreg Clayton case 'o': 442644d93782SGreg Clayton // 'o' == step out 442744d93782SGreg Clayton { 4428b9c1b51eSKate Stone ExecutionContext exe_ctx = 4429b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 4430b9c1b51eSKate Stone if (exe_ctx.HasThreadScope() && 4431b9c1b51eSKate Stone StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) { 443244d93782SGreg Clayton exe_ctx.GetThreadRef().StepOut(); 443344d93782SGreg Clayton } 443444d93782SGreg Clayton } 443544d93782SGreg Clayton return eKeyHandled; 4436315b6884SEugene Zelenko 443744d93782SGreg Clayton case 'n': // 'n' == step over 443844d93782SGreg Clayton case 'N': // 'N' == step over instruction 443944d93782SGreg Clayton { 4440b9c1b51eSKate Stone ExecutionContext exe_ctx = 4441b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 4442b9c1b51eSKate Stone if (exe_ctx.HasThreadScope() && 4443b9c1b51eSKate Stone StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) { 444444d93782SGreg Clayton bool source_step = (c == 'n'); 444544d93782SGreg Clayton exe_ctx.GetThreadRef().StepOver(source_step); 444644d93782SGreg Clayton } 444744d93782SGreg Clayton } 444844d93782SGreg Clayton return eKeyHandled; 4449315b6884SEugene Zelenko 445044d93782SGreg Clayton case 's': // 's' == step into 445144d93782SGreg Clayton case 'S': // 'S' == step into instruction 445244d93782SGreg Clayton { 4453b9c1b51eSKate Stone ExecutionContext exe_ctx = 4454b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 4455b9c1b51eSKate Stone if (exe_ctx.HasThreadScope() && 4456b9c1b51eSKate Stone StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) { 445744d93782SGreg Clayton bool source_step = (c == 's'); 44584b4b2478SJim Ingham exe_ctx.GetThreadRef().StepIn(source_step); 445944d93782SGreg Clayton } 446044d93782SGreg Clayton } 446144d93782SGreg Clayton return eKeyHandled; 446244d93782SGreg Clayton 446344d93782SGreg Clayton case 'h': 446444d93782SGreg Clayton window.CreateHelpSubwindow(); 446544d93782SGreg Clayton return eKeyHandled; 446644d93782SGreg Clayton 446744d93782SGreg Clayton default: 446844d93782SGreg Clayton break; 446944d93782SGreg Clayton } 447044d93782SGreg Clayton return eKeyNotHandled; 447144d93782SGreg Clayton } 447244d93782SGreg Clayton 447344d93782SGreg Clayton protected: 447444d93782SGreg Clayton typedef std::set<uint32_t> BreakpointLines; 447544d93782SGreg Clayton typedef std::set<lldb::addr_t> BreakpointAddrs; 447644d93782SGreg Clayton 447744d93782SGreg Clayton Debugger &m_debugger; 447844d93782SGreg Clayton SymbolContext m_sc; 447944d93782SGreg Clayton SourceManager::FileSP m_file_sp; 448044d93782SGreg Clayton SymbolContextScope *m_disassembly_scope; 448144d93782SGreg Clayton lldb::DisassemblerSP m_disassembly_sp; 448244d93782SGreg Clayton AddressRange m_disassembly_range; 4483ec990867SGreg Clayton StreamString m_title; 448444d93782SGreg Clayton lldb::user_id_t m_tid; 448544d93782SGreg Clayton char m_line_format[8]; 448644d93782SGreg Clayton int m_line_width; 448744d93782SGreg Clayton uint32_t m_selected_line; // The selected line 448844d93782SGreg Clayton uint32_t m_pc_line; // The line with the PC 448944d93782SGreg Clayton uint32_t m_stop_id; 449044d93782SGreg Clayton uint32_t m_frame_idx; 449144d93782SGreg Clayton int m_first_visible_line; 449244d93782SGreg Clayton int m_min_x; 449344d93782SGreg Clayton int m_min_y; 449444d93782SGreg Clayton int m_max_x; 449544d93782SGreg Clayton int m_max_y; 449644d93782SGreg Clayton }; 449744d93782SGreg Clayton 449844d93782SGreg Clayton DisplayOptions ValueObjectListDelegate::g_options = {true}; 449944d93782SGreg Clayton 4500b9c1b51eSKate Stone IOHandlerCursesGUI::IOHandlerCursesGUI(Debugger &debugger) 4501b9c1b51eSKate Stone : IOHandler(debugger, IOHandler::Type::Curses) {} 450244d93782SGreg Clayton 4503b9c1b51eSKate Stone void IOHandlerCursesGUI::Activate() { 450444d93782SGreg Clayton IOHandler::Activate(); 4505b9c1b51eSKate Stone if (!m_app_ap) { 450644d93782SGreg Clayton m_app_ap.reset(new Application(GetInputFILE(), GetOutputFILE())); 450744d93782SGreg Clayton 450844d93782SGreg Clayton // This is both a window and a menu delegate 4509b9c1b51eSKate Stone std::shared_ptr<ApplicationDelegate> app_delegate_sp( 4510b9c1b51eSKate Stone new ApplicationDelegate(*m_app_ap, m_debugger)); 451144d93782SGreg Clayton 4512b9c1b51eSKate Stone MenuDelegateSP app_menu_delegate_sp = 4513b9c1b51eSKate Stone std::static_pointer_cast<MenuDelegate>(app_delegate_sp); 4514b9c1b51eSKate Stone MenuSP lldb_menu_sp( 4515b9c1b51eSKate Stone new Menu("LLDB", "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB)); 4516b9c1b51eSKate Stone MenuSP exit_menuitem_sp( 4517b9c1b51eSKate Stone new Menu("Exit", nullptr, 'x', ApplicationDelegate::eMenuID_LLDBExit)); 451844d93782SGreg Clayton exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit); 4519b9c1b51eSKate Stone lldb_menu_sp->AddSubmenu(MenuSP(new Menu( 4520b9c1b51eSKate Stone "About LLDB", nullptr, 'a', ApplicationDelegate::eMenuID_LLDBAbout))); 452144d93782SGreg Clayton lldb_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator))); 452244d93782SGreg Clayton lldb_menu_sp->AddSubmenu(exit_menuitem_sp); 452344d93782SGreg Clayton 4524b9c1b51eSKate Stone MenuSP target_menu_sp(new Menu("Target", "F2", KEY_F(2), 4525b9c1b51eSKate Stone ApplicationDelegate::eMenuID_Target)); 4526b9c1b51eSKate Stone target_menu_sp->AddSubmenu(MenuSP(new Menu( 4527b9c1b51eSKate Stone "Create", nullptr, 'c', ApplicationDelegate::eMenuID_TargetCreate))); 4528b9c1b51eSKate Stone target_menu_sp->AddSubmenu(MenuSP(new Menu( 4529b9c1b51eSKate Stone "Delete", nullptr, 'd', ApplicationDelegate::eMenuID_TargetDelete))); 453044d93782SGreg Clayton 4531b9c1b51eSKate Stone MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3), 4532b9c1b51eSKate Stone ApplicationDelegate::eMenuID_Process)); 4533b9c1b51eSKate Stone process_menu_sp->AddSubmenu(MenuSP(new Menu( 4534b9c1b51eSKate Stone "Attach", nullptr, 'a', ApplicationDelegate::eMenuID_ProcessAttach))); 4535b9c1b51eSKate Stone process_menu_sp->AddSubmenu(MenuSP(new Menu( 4536b9c1b51eSKate Stone "Detach", nullptr, 'd', ApplicationDelegate::eMenuID_ProcessDetach))); 4537b9c1b51eSKate Stone process_menu_sp->AddSubmenu(MenuSP(new Menu( 4538b9c1b51eSKate Stone "Launch", nullptr, 'l', ApplicationDelegate::eMenuID_ProcessLaunch))); 453944d93782SGreg Clayton process_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator))); 4540b9c1b51eSKate Stone process_menu_sp->AddSubmenu( 4541b9c1b51eSKate Stone MenuSP(new Menu("Continue", nullptr, 'c', 4542b9c1b51eSKate Stone ApplicationDelegate::eMenuID_ProcessContinue))); 4543b9c1b51eSKate Stone process_menu_sp->AddSubmenu(MenuSP(new Menu( 4544b9c1b51eSKate Stone "Halt", nullptr, 'h', ApplicationDelegate::eMenuID_ProcessHalt))); 4545b9c1b51eSKate Stone process_menu_sp->AddSubmenu(MenuSP(new Menu( 4546b9c1b51eSKate Stone "Kill", nullptr, 'k', ApplicationDelegate::eMenuID_ProcessKill))); 454744d93782SGreg Clayton 4548b9c1b51eSKate Stone MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4), 4549b9c1b51eSKate Stone ApplicationDelegate::eMenuID_Thread)); 4550b9c1b51eSKate Stone thread_menu_sp->AddSubmenu(MenuSP(new Menu( 4551b9c1b51eSKate Stone "Step In", nullptr, 'i', ApplicationDelegate::eMenuID_ThreadStepIn))); 4552b9c1b51eSKate Stone thread_menu_sp->AddSubmenu( 4553b9c1b51eSKate Stone MenuSP(new Menu("Step Over", nullptr, 'v', 4554b9c1b51eSKate Stone ApplicationDelegate::eMenuID_ThreadStepOver))); 4555b9c1b51eSKate Stone thread_menu_sp->AddSubmenu(MenuSP(new Menu( 4556b9c1b51eSKate Stone "Step Out", nullptr, 'o', ApplicationDelegate::eMenuID_ThreadStepOut))); 455744d93782SGreg Clayton 4558b9c1b51eSKate Stone MenuSP view_menu_sp( 4559b9c1b51eSKate Stone new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View)); 4560b9c1b51eSKate Stone view_menu_sp->AddSubmenu( 4561b9c1b51eSKate Stone MenuSP(new Menu("Backtrace", nullptr, 'b', 4562b9c1b51eSKate Stone ApplicationDelegate::eMenuID_ViewBacktrace))); 4563b9c1b51eSKate Stone view_menu_sp->AddSubmenu( 4564b9c1b51eSKate Stone MenuSP(new Menu("Registers", nullptr, 'r', 4565b9c1b51eSKate Stone ApplicationDelegate::eMenuID_ViewRegisters))); 4566b9c1b51eSKate Stone view_menu_sp->AddSubmenu(MenuSP(new Menu( 4567b9c1b51eSKate Stone "Source", nullptr, 's', ApplicationDelegate::eMenuID_ViewSource))); 4568b9c1b51eSKate Stone view_menu_sp->AddSubmenu( 4569b9c1b51eSKate Stone MenuSP(new Menu("Variables", nullptr, 'v', 4570b9c1b51eSKate Stone ApplicationDelegate::eMenuID_ViewVariables))); 457144d93782SGreg Clayton 4572b9c1b51eSKate Stone MenuSP help_menu_sp( 4573b9c1b51eSKate Stone new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help)); 4574b9c1b51eSKate Stone help_menu_sp->AddSubmenu(MenuSP(new Menu( 4575b9c1b51eSKate Stone "GUI Help", nullptr, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp))); 457644d93782SGreg Clayton 457744d93782SGreg Clayton m_app_ap->Initialize(); 457844d93782SGreg Clayton WindowSP &main_window_sp = m_app_ap->GetMainWindow(); 457944d93782SGreg Clayton 458044d93782SGreg Clayton MenuSP menubar_sp(new Menu(Menu::Type::Bar)); 458144d93782SGreg Clayton menubar_sp->AddSubmenu(lldb_menu_sp); 458244d93782SGreg Clayton menubar_sp->AddSubmenu(target_menu_sp); 458344d93782SGreg Clayton menubar_sp->AddSubmenu(process_menu_sp); 458444d93782SGreg Clayton menubar_sp->AddSubmenu(thread_menu_sp); 458544d93782SGreg Clayton menubar_sp->AddSubmenu(view_menu_sp); 458644d93782SGreg Clayton menubar_sp->AddSubmenu(help_menu_sp); 458744d93782SGreg Clayton menubar_sp->SetDelegate(app_menu_delegate_sp); 458844d93782SGreg Clayton 458944d93782SGreg Clayton Rect content_bounds = main_window_sp->GetFrame(); 459044d93782SGreg Clayton Rect menubar_bounds = content_bounds.MakeMenuBar(); 459144d93782SGreg Clayton Rect status_bounds = content_bounds.MakeStatusBar(); 459244d93782SGreg Clayton Rect source_bounds; 459344d93782SGreg Clayton Rect variables_bounds; 459444d93782SGreg Clayton Rect threads_bounds; 459544d93782SGreg Clayton Rect source_variables_bounds; 4596b9c1b51eSKate Stone content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds, 4597b9c1b51eSKate Stone threads_bounds); 4598b9c1b51eSKate Stone source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds, 4599b9c1b51eSKate Stone variables_bounds); 460044d93782SGreg Clayton 4601b9c1b51eSKate Stone WindowSP menubar_window_sp = 4602b9c1b51eSKate Stone main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false); 460305097246SAdrian Prantl // Let the menubar get keys if the active window doesn't handle the keys 460405097246SAdrian Prantl // that are typed so it can respond to menubar key presses. 4605b9c1b51eSKate Stone menubar_window_sp->SetCanBeActive( 4606b9c1b51eSKate Stone false); // Don't let the menubar become the active window 460744d93782SGreg Clayton menubar_window_sp->SetDelegate(menubar_sp); 460844d93782SGreg Clayton 4609b9c1b51eSKate Stone WindowSP source_window_sp( 4610b9c1b51eSKate Stone main_window_sp->CreateSubWindow("Source", source_bounds, true)); 4611b9c1b51eSKate Stone WindowSP variables_window_sp( 4612b9c1b51eSKate Stone main_window_sp->CreateSubWindow("Variables", variables_bounds, false)); 4613b9c1b51eSKate Stone WindowSP threads_window_sp( 4614b9c1b51eSKate Stone main_window_sp->CreateSubWindow("Threads", threads_bounds, false)); 4615b9c1b51eSKate Stone WindowSP status_window_sp( 46166bb7e21fSPavel Labath main_window_sp->CreateSubWindow("Status", status_bounds, false)); 4617b9c1b51eSKate Stone status_window_sp->SetCanBeActive( 4618b9c1b51eSKate Stone false); // Don't let the status bar become the active window 4619b9c1b51eSKate Stone main_window_sp->SetDelegate( 4620b9c1b51eSKate Stone std::static_pointer_cast<WindowDelegate>(app_delegate_sp)); 4621b9c1b51eSKate Stone source_window_sp->SetDelegate( 4622b9c1b51eSKate Stone WindowDelegateSP(new SourceFileWindowDelegate(m_debugger))); 4623b9c1b51eSKate Stone variables_window_sp->SetDelegate( 4624b9c1b51eSKate Stone WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger))); 4625ec990867SGreg Clayton TreeDelegateSP thread_delegate_sp(new ThreadsTreeDelegate(m_debugger)); 4626b9c1b51eSKate Stone threads_window_sp->SetDelegate(WindowDelegateSP( 4627b9c1b51eSKate Stone new TreeWindowDelegate(m_debugger, thread_delegate_sp))); 4628b9c1b51eSKate Stone status_window_sp->SetDelegate( 4629b9c1b51eSKate Stone WindowDelegateSP(new StatusBarWindowDelegate(m_debugger))); 463044d93782SGreg Clayton 46315fdb09bbSGreg Clayton // Show the main help window once the first time the curses GUI is launched 46325fdb09bbSGreg Clayton static bool g_showed_help = false; 4633b9c1b51eSKate Stone if (!g_showed_help) { 46345fdb09bbSGreg Clayton g_showed_help = true; 46355fdb09bbSGreg Clayton main_window_sp->CreateHelpSubwindow(); 46365fdb09bbSGreg Clayton } 46375fdb09bbSGreg Clayton 463844d93782SGreg Clayton init_pair(1, COLOR_WHITE, COLOR_BLUE); 463944d93782SGreg Clayton init_pair(2, COLOR_BLACK, COLOR_WHITE); 464044d93782SGreg Clayton init_pair(3, COLOR_MAGENTA, COLOR_WHITE); 464144d93782SGreg Clayton init_pair(4, COLOR_MAGENTA, COLOR_BLACK); 464244d93782SGreg Clayton init_pair(5, COLOR_RED, COLOR_BLACK); 464344d93782SGreg Clayton } 464444d93782SGreg Clayton } 464544d93782SGreg Clayton 4646b9c1b51eSKate Stone void IOHandlerCursesGUI::Deactivate() { m_app_ap->Terminate(); } 464744d93782SGreg Clayton 4648b9c1b51eSKate Stone void IOHandlerCursesGUI::Run() { 464944d93782SGreg Clayton m_app_ap->Run(m_debugger); 465044d93782SGreg Clayton SetIsDone(true); 465144d93782SGreg Clayton } 465244d93782SGreg Clayton 4653315b6884SEugene Zelenko IOHandlerCursesGUI::~IOHandlerCursesGUI() = default; 465444d93782SGreg Clayton 4655b9c1b51eSKate Stone void IOHandlerCursesGUI::Cancel() {} 465644d93782SGreg Clayton 4657b9c1b51eSKate Stone bool IOHandlerCursesGUI::Interrupt() { return false; } 465844d93782SGreg Clayton 4659b9c1b51eSKate Stone void IOHandlerCursesGUI::GotEOF() {} 466044d93782SGreg Clayton 4661315b6884SEugene Zelenko #endif // LLDB_DISABLE_CURSES 4662