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 10*2f3df613SZachary Turner #include "lldb/Core/IOHandler.h" 11*2f3df613SZachary Turner 12315b6884SEugene Zelenko // C Includes 13315b6884SEugene Zelenko #ifndef LLDB_DISABLE_CURSES 1427801f4fSBruce Mitchener #include <curses.h> 15315b6884SEugene Zelenko #include <panel.h> 16315b6884SEugene Zelenko #endif 1744d93782SGreg Clayton 18315b6884SEugene Zelenko // C++ Includes 197c9aa073STodd Fiala #if defined(__APPLE__) 207c9aa073STodd Fiala #include <deque> 217c9aa073STodd Fiala #endif 2244d93782SGreg Clayton #include <string> 2344d93782SGreg Clayton 24315b6884SEugene Zelenko // Other libraries and framework includes 25315b6884SEugene Zelenko // Project includes 2644d93782SGreg Clayton #include "lldb/Core/Debugger.h" 2744d93782SGreg Clayton #include "lldb/Core/StreamFile.h" 28*2f3df613SZachary Turner #include "lldb/Host/File.h" // for File 29*2f3df613SZachary Turner #include "lldb/Host/Predicate.h" // for Predicate, ::eBroad... 30*2f3df613SZachary Turner #include "lldb/Utility/Error.h" // for Error 31*2f3df613SZachary Turner #include "lldb/Utility/StreamString.h" // for StreamString 32*2f3df613SZachary Turner #include "lldb/Utility/StringList.h" // for StringList 33*2f3df613SZachary Turner #include "lldb/lldb-forward.h" // for StreamFileSP 34*2f3df613SZachary Turner 35cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 3644d93782SGreg Clayton #include "lldb/Host/Editline.h" 37cacde7dfSTodd Fiala #endif 3844d93782SGreg Clayton #include "lldb/Interpreter/CommandCompletions.h" 3944d93782SGreg Clayton #include "lldb/Interpreter/CommandInterpreter.h" 40*2f3df613SZachary Turner #ifndef LLDB_DISABLE_CURSES 41*2f3df613SZachary Turner #include "lldb/Breakpoint/BreakpointLocation.h" 42*2f3df613SZachary Turner #include "lldb/Core/Module.h" 43*2f3df613SZachary Turner #include "lldb/Core/State.h" 44*2f3df613SZachary Turner #include "lldb/Core/ValueObject.h" 45*2f3df613SZachary Turner #include "lldb/Core/ValueObjectRegister.h" 4644d93782SGreg Clayton #include "lldb/Symbol/Block.h" 4744d93782SGreg Clayton #include "lldb/Symbol/Function.h" 4844d93782SGreg Clayton #include "lldb/Symbol/Symbol.h" 49c5dac77aSEugene Zelenko #include "lldb/Symbol/VariableList.h" 50c5dac77aSEugene Zelenko #include "lldb/Target/Process.h" 51*2f3df613SZachary Turner #include "lldb/Target/RegisterContext.h" 52c5dac77aSEugene Zelenko #include "lldb/Target/StackFrame.h" 53*2f3df613SZachary Turner #include "lldb/Target/StopInfo.h" 54b9c1b51eSKate Stone #include "lldb/Target/Target.h" 55b9c1b51eSKate Stone #include "lldb/Target/Thread.h" 56c5dac77aSEugene Zelenko #endif 577c9aa073STodd Fiala 58*2f3df613SZachary Turner #include "llvm/ADT/StringRef.h" // for StringRef 59*2f3df613SZachary Turner 60fab31220STed Woodward #ifdef _MSC_VER 61*2f3df613SZachary Turner #include <windows.h> 62fab31220STed Woodward #endif 63fab31220STed Woodward 64*2f3df613SZachary Turner #include <memory> // for shared_ptr 65*2f3df613SZachary Turner #include <mutex> // for recursive_mutex 66*2f3df613SZachary Turner 67*2f3df613SZachary Turner #include <assert.h> // for assert 68*2f3df613SZachary Turner #include <ctype.h> // for isspace 69*2f3df613SZachary Turner #include <errno.h> // for EINTR, errno 70*2f3df613SZachary Turner #include <stdint.h> // for uint32_t, UINT32_MAX 71*2f3df613SZachary Turner #include <stdio.h> // for size_t, fprintf, feof 72*2f3df613SZachary Turner #include <string.h> // for strlen 73*2f3df613SZachary Turner #include <type_traits> // for move 74*2f3df613SZachary Turner 7544d93782SGreg Clayton using namespace lldb; 7644d93782SGreg Clayton using namespace lldb_private; 7744d93782SGreg Clayton 78b9c1b51eSKate Stone IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type) 79b9c1b51eSKate Stone : IOHandler(debugger, type, 8044d93782SGreg Clayton StreamFileSP(), // Adopt STDIN from top input reader 8144d93782SGreg Clayton StreamFileSP(), // Adopt STDOUT from top input reader 82340b0309SGreg Clayton StreamFileSP(), // Adopt STDERR from top input reader 83340b0309SGreg Clayton 0) // Flags 84b9c1b51eSKate Stone {} 8544d93782SGreg Clayton 86b9c1b51eSKate Stone IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type, 8744d93782SGreg Clayton const lldb::StreamFileSP &input_sp, 8844d93782SGreg Clayton const lldb::StreamFileSP &output_sp, 89b9c1b51eSKate Stone const lldb::StreamFileSP &error_sp, uint32_t flags) 90b9c1b51eSKate Stone : m_debugger(debugger), m_input_sp(input_sp), m_output_sp(output_sp), 91b9c1b51eSKate Stone m_error_sp(error_sp), m_popped(false), m_flags(flags), m_type(type), 92b9c1b51eSKate Stone m_user_data(nullptr), m_done(false), m_active(false) { 9344d93782SGreg Clayton // If any files are not specified, then adopt them from the top input reader. 9444d93782SGreg Clayton if (!m_input_sp || !m_output_sp || !m_error_sp) 95b9c1b51eSKate Stone debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_sp, m_output_sp, 9644d93782SGreg Clayton m_error_sp); 9744d93782SGreg Clayton } 9844d93782SGreg Clayton 99315b6884SEugene Zelenko IOHandler::~IOHandler() = default; 10044d93782SGreg Clayton 101b9c1b51eSKate Stone int IOHandler::GetInputFD() { 102c5dac77aSEugene Zelenko return (m_input_sp ? m_input_sp->GetFile().GetDescriptor() : -1); 10344d93782SGreg Clayton } 10444d93782SGreg Clayton 105b9c1b51eSKate Stone int IOHandler::GetOutputFD() { 106c5dac77aSEugene Zelenko return (m_output_sp ? m_output_sp->GetFile().GetDescriptor() : -1); 10744d93782SGreg Clayton } 10844d93782SGreg Clayton 109b9c1b51eSKate Stone int IOHandler::GetErrorFD() { 110c5dac77aSEugene Zelenko return (m_error_sp ? m_error_sp->GetFile().GetDescriptor() : -1); 11144d93782SGreg Clayton } 11244d93782SGreg Clayton 113b9c1b51eSKate Stone FILE *IOHandler::GetInputFILE() { 114c5dac77aSEugene Zelenko return (m_input_sp ? m_input_sp->GetFile().GetStream() : nullptr); 11544d93782SGreg Clayton } 11644d93782SGreg Clayton 117b9c1b51eSKate Stone FILE *IOHandler::GetOutputFILE() { 118c5dac77aSEugene Zelenko return (m_output_sp ? m_output_sp->GetFile().GetStream() : nullptr); 11944d93782SGreg Clayton } 12044d93782SGreg Clayton 121b9c1b51eSKate Stone FILE *IOHandler::GetErrorFILE() { 122c5dac77aSEugene Zelenko return (m_error_sp ? m_error_sp->GetFile().GetStream() : nullptr); 12344d93782SGreg Clayton } 12444d93782SGreg Clayton 125b9c1b51eSKate Stone StreamFileSP &IOHandler::GetInputStreamFile() { return m_input_sp; } 12644d93782SGreg Clayton 127b9c1b51eSKate Stone StreamFileSP &IOHandler::GetOutputStreamFile() { return m_output_sp; } 12844d93782SGreg Clayton 129b9c1b51eSKate Stone StreamFileSP &IOHandler::GetErrorStreamFile() { return m_error_sp; } 13044d93782SGreg Clayton 131b9c1b51eSKate Stone bool IOHandler::GetIsInteractive() { 132340b0309SGreg Clayton return GetInputStreamFile()->GetFile().GetIsInteractive(); 133340b0309SGreg Clayton } 134340b0309SGreg Clayton 135b9c1b51eSKate Stone bool IOHandler::GetIsRealTerminal() { 136340b0309SGreg Clayton return GetInputStreamFile()->GetFile().GetIsRealTerminal(); 137340b0309SGreg Clayton } 13844d93782SGreg Clayton 139b9c1b51eSKate Stone void IOHandler::SetPopped(bool b) { m_popped.SetValue(b, eBroadcastOnChange); } 140e30f11d9SKate Stone 141b9c1b51eSKate Stone void IOHandler::WaitForPop() { m_popped.WaitForValueEqualTo(true); } 142e30f11d9SKate Stone 143b9c1b51eSKate Stone void IOHandlerStack::PrintAsync(Stream *stream, const char *s, size_t len) { 144b9c1b51eSKate Stone if (stream) { 14516ff8604SSaleem Abdulrasool std::lock_guard<std::recursive_mutex> guard(m_mutex); 1464446487dSPavel Labath if (m_top) 1474446487dSPavel Labath m_top->PrintAsync(stream, s, len); 1484446487dSPavel Labath } 1494446487dSPavel Labath } 1504446487dSPavel Labath 1517a120c8bSZachary Turner IOHandlerConfirm::IOHandlerConfirm(Debugger &debugger, llvm::StringRef prompt, 152b9c1b51eSKate Stone bool default_response) 153b9c1b51eSKate Stone : IOHandlerEditline( 154b9c1b51eSKate Stone debugger, IOHandler::Type::Confirm, 155c5dac77aSEugene Zelenko nullptr, // nullptr editline_name means no history loaded/saved 156514d8cd8SZachary Turner llvm::StringRef(), // No prompt 157514d8cd8SZachary Turner llvm::StringRef(), // No continuation prompt 15844d93782SGreg Clayton false, // Multi-line 159e30f11d9SKate Stone false, // Don't colorize the prompt (i.e. the confirm message.) 160b9c1b51eSKate Stone 0, *this), 161b9c1b51eSKate Stone m_default_response(default_response), m_user_response(default_response) { 16244d93782SGreg Clayton StreamString prompt_stream; 16344d93782SGreg Clayton prompt_stream.PutCString(prompt); 16444d93782SGreg Clayton if (m_default_response) 16544d93782SGreg Clayton prompt_stream.Printf(": [Y/n] "); 16644d93782SGreg Clayton else 16744d93782SGreg Clayton prompt_stream.Printf(": [y/N] "); 16844d93782SGreg Clayton 169514d8cd8SZachary Turner SetPrompt(prompt_stream.GetString()); 17044d93782SGreg Clayton } 17144d93782SGreg Clayton 172315b6884SEugene Zelenko IOHandlerConfirm::~IOHandlerConfirm() = default; 17344d93782SGreg Clayton 174b9c1b51eSKate Stone int IOHandlerConfirm::IOHandlerComplete(IOHandler &io_handler, 17544d93782SGreg Clayton const char *current_line, 17644d93782SGreg Clayton const char *cursor, 17744d93782SGreg Clayton const char *last_char, 17844d93782SGreg Clayton int skip_first_n_matches, 179b9c1b51eSKate Stone int max_matches, StringList &matches) { 180b9c1b51eSKate Stone if (current_line == cursor) { 181b9c1b51eSKate Stone if (m_default_response) { 18244d93782SGreg Clayton matches.AppendString("y"); 183b9c1b51eSKate Stone } else { 18444d93782SGreg Clayton matches.AppendString("n"); 18544d93782SGreg Clayton } 18644d93782SGreg Clayton } 18744d93782SGreg Clayton return matches.GetSize(); 18844d93782SGreg Clayton } 18944d93782SGreg Clayton 190b9c1b51eSKate Stone void IOHandlerConfirm::IOHandlerInputComplete(IOHandler &io_handler, 191b9c1b51eSKate Stone std::string &line) { 192b9c1b51eSKate Stone if (line.empty()) { 19344d93782SGreg Clayton // User just hit enter, set the response to the default 19444d93782SGreg Clayton m_user_response = m_default_response; 19544d93782SGreg Clayton io_handler.SetIsDone(true); 19644d93782SGreg Clayton return; 19744d93782SGreg Clayton } 19844d93782SGreg Clayton 199b9c1b51eSKate Stone if (line.size() == 1) { 200b9c1b51eSKate Stone switch (line[0]) { 20144d93782SGreg Clayton case 'y': 20244d93782SGreg Clayton case 'Y': 20344d93782SGreg Clayton m_user_response = true; 20444d93782SGreg Clayton io_handler.SetIsDone(true); 20544d93782SGreg Clayton return; 20644d93782SGreg Clayton case 'n': 20744d93782SGreg Clayton case 'N': 20844d93782SGreg Clayton m_user_response = false; 20944d93782SGreg Clayton io_handler.SetIsDone(true); 21044d93782SGreg Clayton return; 21144d93782SGreg Clayton default: 21244d93782SGreg Clayton break; 21344d93782SGreg Clayton } 21444d93782SGreg Clayton } 21544d93782SGreg Clayton 216b9c1b51eSKate Stone if (line == "yes" || line == "YES" || line == "Yes") { 21744d93782SGreg Clayton m_user_response = true; 21844d93782SGreg Clayton io_handler.SetIsDone(true); 219b9c1b51eSKate Stone } else if (line == "no" || line == "NO" || line == "No") { 22044d93782SGreg Clayton m_user_response = false; 22144d93782SGreg Clayton io_handler.SetIsDone(true); 22244d93782SGreg Clayton } 22344d93782SGreg Clayton } 22444d93782SGreg Clayton 225b9c1b51eSKate Stone int IOHandlerDelegate::IOHandlerComplete(IOHandler &io_handler, 22644d93782SGreg Clayton const char *current_line, 22744d93782SGreg Clayton const char *cursor, 22844d93782SGreg Clayton const char *last_char, 22944d93782SGreg Clayton int skip_first_n_matches, 230b9c1b51eSKate Stone int max_matches, StringList &matches) { 231b9c1b51eSKate Stone switch (m_completion) { 23244d93782SGreg Clayton case Completion::None: 23344d93782SGreg Clayton break; 23444d93782SGreg Clayton 23544d93782SGreg Clayton case Completion::LLDBCommand: 236b9c1b51eSKate Stone return io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion( 237b9c1b51eSKate Stone current_line, cursor, last_char, skip_first_n_matches, max_matches, 23844d93782SGreg Clayton matches); 23944d93782SGreg Clayton 240b9c1b51eSKate Stone case Completion::Expression: { 24144d93782SGreg Clayton bool word_complete = false; 24244d93782SGreg Clayton const char *word_start = cursor; 24344d93782SGreg Clayton if (cursor > current_line) 24444d93782SGreg Clayton --word_start; 24544d93782SGreg Clayton while (word_start > current_line && !isspace(*word_start)) 24644d93782SGreg Clayton --word_start; 247b9c1b51eSKate Stone CommandCompletions::InvokeCommonCompletionCallbacks( 248b9c1b51eSKate Stone io_handler.GetDebugger().GetCommandInterpreter(), 249b9c1b51eSKate Stone CommandCompletions::eVariablePathCompletion, word_start, 250b9c1b51eSKate Stone skip_first_n_matches, max_matches, nullptr, word_complete, matches); 25144d93782SGreg Clayton 25244d93782SGreg Clayton size_t num_matches = matches.GetSize(); 253b9c1b51eSKate Stone if (num_matches > 0) { 25444d93782SGreg Clayton std::string common_prefix; 25544d93782SGreg Clayton matches.LongestCommonPrefix(common_prefix); 25644d93782SGreg Clayton const size_t partial_name_len = strlen(word_start); 25744d93782SGreg Clayton 25844d93782SGreg Clayton // If we matched a unique single command, add a space... 259b9c1b51eSKate Stone // Only do this if the completer told us this was a complete word, 260b9c1b51eSKate Stone // however... 261b9c1b51eSKate Stone if (num_matches == 1 && word_complete) { 26244d93782SGreg Clayton common_prefix.push_back(' '); 26344d93782SGreg Clayton } 26444d93782SGreg Clayton common_prefix.erase(0, partial_name_len); 26544d93782SGreg Clayton matches.InsertStringAtIndex(0, std::move(common_prefix)); 26644d93782SGreg Clayton } 26744d93782SGreg Clayton return num_matches; 268b9c1b51eSKate Stone } break; 26944d93782SGreg Clayton } 27044d93782SGreg Clayton 27144d93782SGreg Clayton return 0; 27244d93782SGreg Clayton } 27344d93782SGreg Clayton 274b9c1b51eSKate Stone IOHandlerEditline::IOHandlerEditline( 275b9c1b51eSKate Stone Debugger &debugger, IOHandler::Type type, 27644d93782SGreg Clayton const char *editline_name, // Used for saving history files 277514d8cd8SZachary Turner llvm::StringRef prompt, llvm::StringRef continuation_prompt, 278514d8cd8SZachary Turner bool multi_line, bool color_prompts, uint32_t line_number_start, 279514d8cd8SZachary Turner IOHandlerDelegate &delegate) 280b9c1b51eSKate Stone : IOHandlerEditline(debugger, type, 28144d93782SGreg Clayton StreamFileSP(), // Inherit input from top input reader 28244d93782SGreg Clayton StreamFileSP(), // Inherit output from top input reader 28344d93782SGreg Clayton StreamFileSP(), // Inherit error from top input reader 284340b0309SGreg Clayton 0, // Flags 28544d93782SGreg Clayton editline_name, // Used for saving history files 286b9c1b51eSKate Stone prompt, continuation_prompt, multi_line, color_prompts, 287b9c1b51eSKate Stone line_number_start, delegate) {} 28844d93782SGreg Clayton 289b9c1b51eSKate Stone IOHandlerEditline::IOHandlerEditline( 290b9c1b51eSKate Stone Debugger &debugger, IOHandler::Type type, 291b9c1b51eSKate Stone const lldb::StreamFileSP &input_sp, const lldb::StreamFileSP &output_sp, 292b9c1b51eSKate Stone const lldb::StreamFileSP &error_sp, uint32_t flags, 29344d93782SGreg Clayton const char *editline_name, // Used for saving history files 294514d8cd8SZachary Turner llvm::StringRef prompt, llvm::StringRef continuation_prompt, 295514d8cd8SZachary Turner bool multi_line, bool color_prompts, uint32_t line_number_start, 296514d8cd8SZachary Turner IOHandlerDelegate &delegate) 297b9c1b51eSKate Stone : IOHandler(debugger, type, input_sp, output_sp, error_sp, flags), 298cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 29944d93782SGreg Clayton m_editline_ap(), 300cacde7dfSTodd Fiala #endif 301b9c1b51eSKate Stone m_delegate(delegate), m_prompt(), m_continuation_prompt(), 302b9c1b51eSKate Stone m_current_lines_ptr(nullptr), m_base_line_number(line_number_start), 303b9c1b51eSKate Stone m_curr_line_idx(UINT32_MAX), m_multi_line(multi_line), 304b9c1b51eSKate Stone m_color_prompts(color_prompts), m_interrupt_exits(true), 305b9c1b51eSKate Stone m_editing(false) { 30644d93782SGreg Clayton SetPrompt(prompt); 30744d93782SGreg Clayton 308cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 309914b8d98SDeepak Panickal bool use_editline = false; 310340b0309SGreg Clayton 311340b0309SGreg Clayton use_editline = m_input_sp->GetFile().GetIsRealTerminal(); 31244d93782SGreg Clayton 313b9c1b51eSKate Stone if (use_editline) { 314b9c1b51eSKate Stone m_editline_ap.reset(new Editline(editline_name, GetInputFILE(), 315b9c1b51eSKate Stone GetOutputFILE(), GetErrorFILE(), 316e30f11d9SKate Stone m_color_prompts)); 317e30f11d9SKate Stone m_editline_ap->SetIsInputCompleteCallback(IsInputCompleteCallback, this); 31844d93782SGreg Clayton m_editline_ap->SetAutoCompleteCallback(AutoCompleteCallback, this); 319e30f11d9SKate Stone // See if the delegate supports fixing indentation 320e30f11d9SKate Stone const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters(); 321b9c1b51eSKate Stone if (indent_chars) { 322b9c1b51eSKate Stone // The delegate does support indentation, hook it up so when any 323b9c1b51eSKate Stone // indentation 324e30f11d9SKate Stone // character is typed, the delegate gets a chance to fix it 325b9c1b51eSKate Stone m_editline_ap->SetFixIndentationCallback(FixIndentationCallback, this, 326b9c1b51eSKate Stone indent_chars); 327e30f11d9SKate Stone } 32844d93782SGreg Clayton } 329cacde7dfSTodd Fiala #endif 330e30f11d9SKate Stone SetBaseLineNumber(m_base_line_number); 331514d8cd8SZachary Turner SetPrompt(prompt); 332e30f11d9SKate Stone SetContinuationPrompt(continuation_prompt); 33344d93782SGreg Clayton } 33444d93782SGreg Clayton 335b9c1b51eSKate Stone IOHandlerEditline::~IOHandlerEditline() { 336cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 33744d93782SGreg Clayton m_editline_ap.reset(); 338cacde7dfSTodd Fiala #endif 33944d93782SGreg Clayton } 34044d93782SGreg Clayton 341b9c1b51eSKate Stone void IOHandlerEditline::Activate() { 342e30f11d9SKate Stone IOHandler::Activate(); 343e30f11d9SKate Stone m_delegate.IOHandlerActivated(*this); 344e30f11d9SKate Stone } 345e30f11d9SKate Stone 346b9c1b51eSKate Stone void IOHandlerEditline::Deactivate() { 347e30f11d9SKate Stone IOHandler::Deactivate(); 348e30f11d9SKate Stone m_delegate.IOHandlerDeactivated(*this); 349e30f11d9SKate Stone } 350e30f11d9SKate Stone 351b9c1b51eSKate Stone bool IOHandlerEditline::GetLine(std::string &line, bool &interrupted) { 352cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 353b9c1b51eSKate Stone if (m_editline_ap) { 354e30f11d9SKate Stone return m_editline_ap->GetLine(line, interrupted); 355b9c1b51eSKate Stone } else { 356cacde7dfSTodd Fiala #endif 35744d93782SGreg Clayton line.clear(); 35844d93782SGreg Clayton 35944d93782SGreg Clayton FILE *in = GetInputFILE(); 360b9c1b51eSKate Stone if (in) { 361b9c1b51eSKate Stone if (GetIsInteractive()) { 362c5dac77aSEugene Zelenko const char *prompt = nullptr; 363e30f11d9SKate Stone 364e30f11d9SKate Stone if (m_multi_line && m_curr_line_idx > 0) 365e30f11d9SKate Stone prompt = GetContinuationPrompt(); 366e30f11d9SKate Stone 367c5dac77aSEugene Zelenko if (prompt == nullptr) 368e30f11d9SKate Stone prompt = GetPrompt(); 369e30f11d9SKate Stone 370b9c1b51eSKate Stone if (prompt && prompt[0]) { 37144d93782SGreg Clayton FILE *out = GetOutputFILE(); 372b9c1b51eSKate Stone if (out) { 37344d93782SGreg Clayton ::fprintf(out, "%s", prompt); 37444d93782SGreg Clayton ::fflush(out); 37544d93782SGreg Clayton } 37644d93782SGreg Clayton } 37744d93782SGreg Clayton } 37844d93782SGreg Clayton char buffer[256]; 37944d93782SGreg Clayton bool done = false; 3800f86e6e7SGreg Clayton bool got_line = false; 381e034a04eSGreg Clayton m_editing = true; 382b9c1b51eSKate Stone while (!done) { 383b9c1b51eSKate Stone if (fgets(buffer, sizeof(buffer), in) == nullptr) { 384c7797accSGreg Clayton const int saved_errno = errno; 385c9cf5798SGreg Clayton if (feof(in)) 38644d93782SGreg Clayton done = true; 387b9c1b51eSKate Stone else if (ferror(in)) { 388c7797accSGreg Clayton if (saved_errno != EINTR) 389c7797accSGreg Clayton done = true; 390c7797accSGreg Clayton } 391b9c1b51eSKate Stone } else { 3920f86e6e7SGreg Clayton got_line = true; 39344d93782SGreg Clayton size_t buffer_len = strlen(buffer); 39444d93782SGreg Clayton assert(buffer[buffer_len] == '\0'); 39544d93782SGreg Clayton char last_char = buffer[buffer_len - 1]; 396b9c1b51eSKate Stone if (last_char == '\r' || last_char == '\n') { 39744d93782SGreg Clayton done = true; 39844d93782SGreg Clayton // Strip trailing newlines 399b9c1b51eSKate Stone while (last_char == '\r' || last_char == '\n') { 40044d93782SGreg Clayton --buffer_len; 40144d93782SGreg Clayton if (buffer_len == 0) 40244d93782SGreg Clayton break; 40344d93782SGreg Clayton last_char = buffer[buffer_len - 1]; 40444d93782SGreg Clayton } 40544d93782SGreg Clayton } 40644d93782SGreg Clayton line.append(buffer, buffer_len); 40744d93782SGreg Clayton } 40844d93782SGreg Clayton } 409e034a04eSGreg Clayton m_editing = false; 4100f86e6e7SGreg Clayton // We might have gotten a newline on a line by itself 4110f86e6e7SGreg Clayton // make sure to return true in this case. 4120f86e6e7SGreg Clayton return got_line; 413b9c1b51eSKate Stone } else { 41444d93782SGreg Clayton // No more input file, we are done... 41544d93782SGreg Clayton SetIsDone(true); 41644d93782SGreg Clayton } 417340b0309SGreg Clayton return false; 418cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 41944d93782SGreg Clayton } 420cacde7dfSTodd Fiala #endif 42144d93782SGreg Clayton } 42244d93782SGreg Clayton 423cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 424b9c1b51eSKate Stone bool IOHandlerEditline::IsInputCompleteCallback(Editline *editline, 42544d93782SGreg Clayton StringList &lines, 426b9c1b51eSKate Stone void *baton) { 42744d93782SGreg Clayton IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton; 428b9c1b51eSKate Stone return editline_reader->m_delegate.IOHandlerIsInputComplete(*editline_reader, 429b9c1b51eSKate Stone lines); 430e30f11d9SKate Stone } 431e30f11d9SKate Stone 432b9c1b51eSKate Stone int IOHandlerEditline::FixIndentationCallback(Editline *editline, 433e30f11d9SKate Stone const StringList &lines, 434e30f11d9SKate Stone int cursor_position, 435b9c1b51eSKate Stone void *baton) { 436e30f11d9SKate Stone IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton; 437b9c1b51eSKate Stone return editline_reader->m_delegate.IOHandlerFixIndentation( 438b9c1b51eSKate Stone *editline_reader, lines, cursor_position); 43944d93782SGreg Clayton } 44044d93782SGreg Clayton 441b9c1b51eSKate Stone int IOHandlerEditline::AutoCompleteCallback(const char *current_line, 44244d93782SGreg Clayton const char *cursor, 44344d93782SGreg Clayton const char *last_char, 44444d93782SGreg Clayton int skip_first_n_matches, 44544d93782SGreg Clayton int max_matches, 446b9c1b51eSKate Stone StringList &matches, void *baton) { 44744d93782SGreg Clayton IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton; 44844d93782SGreg Clayton if (editline_reader) 449b9c1b51eSKate Stone return editline_reader->m_delegate.IOHandlerComplete( 450b9c1b51eSKate Stone *editline_reader, current_line, cursor, last_char, skip_first_n_matches, 451b9c1b51eSKate Stone max_matches, matches); 45244d93782SGreg Clayton return 0; 45344d93782SGreg Clayton } 454cacde7dfSTodd Fiala #endif 45544d93782SGreg Clayton 456b9c1b51eSKate Stone const char *IOHandlerEditline::GetPrompt() { 457cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 458b9c1b51eSKate Stone if (m_editline_ap) { 45944d93782SGreg Clayton return m_editline_ap->GetPrompt(); 460b9c1b51eSKate Stone } else { 461cacde7dfSTodd Fiala #endif 462cacde7dfSTodd Fiala if (m_prompt.empty()) 463c5dac77aSEugene Zelenko return nullptr; 464cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 465cacde7dfSTodd Fiala } 466cacde7dfSTodd Fiala #endif 46744d93782SGreg Clayton return m_prompt.c_str(); 46844d93782SGreg Clayton } 46944d93782SGreg Clayton 470514d8cd8SZachary Turner bool IOHandlerEditline::SetPrompt(llvm::StringRef prompt) { 471514d8cd8SZachary Turner m_prompt = prompt; 472514d8cd8SZachary Turner 473cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 47444d93782SGreg Clayton if (m_editline_ap) 475c5dac77aSEugene Zelenko m_editline_ap->SetPrompt(m_prompt.empty() ? nullptr : m_prompt.c_str()); 476cacde7dfSTodd Fiala #endif 47744d93782SGreg Clayton return true; 47844d93782SGreg Clayton } 47944d93782SGreg Clayton 480b9c1b51eSKate Stone const char *IOHandlerEditline::GetContinuationPrompt() { 481b9c1b51eSKate Stone return (m_continuation_prompt.empty() ? nullptr 482b9c1b51eSKate Stone : m_continuation_prompt.c_str()); 483e30f11d9SKate Stone } 484e30f11d9SKate Stone 485514d8cd8SZachary Turner void IOHandlerEditline::SetContinuationPrompt(llvm::StringRef prompt) { 486514d8cd8SZachary Turner m_continuation_prompt = prompt; 487e30f11d9SKate Stone 488d553d00cSZachary Turner #ifndef LLDB_DISABLE_LIBEDIT 489e30f11d9SKate Stone if (m_editline_ap) 490b9c1b51eSKate Stone m_editline_ap->SetContinuationPrompt(m_continuation_prompt.empty() 491b9c1b51eSKate Stone ? nullptr 492b9c1b51eSKate Stone : m_continuation_prompt.c_str()); 493d553d00cSZachary Turner #endif 494e30f11d9SKate Stone } 495e30f11d9SKate Stone 496b9c1b51eSKate Stone void IOHandlerEditline::SetBaseLineNumber(uint32_t line) { 497f6913cd7SGreg Clayton m_base_line_number = line; 498f6913cd7SGreg Clayton } 499e30f11d9SKate Stone 500b9c1b51eSKate Stone uint32_t IOHandlerEditline::GetCurrentLineIndex() const { 501d553d00cSZachary Turner #ifndef LLDB_DISABLE_LIBEDIT 502e30f11d9SKate Stone if (m_editline_ap) 503e30f11d9SKate Stone return m_editline_ap->GetCurrentLine(); 504e30f11d9SKate Stone #endif 505e30f11d9SKate Stone return m_curr_line_idx; 506e30f11d9SKate Stone } 507e30f11d9SKate Stone 508b9c1b51eSKate Stone bool IOHandlerEditline::GetLines(StringList &lines, bool &interrupted) { 509e30f11d9SKate Stone m_current_lines_ptr = &lines; 510e30f11d9SKate Stone 51144d93782SGreg Clayton bool success = false; 512cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 513b9c1b51eSKate Stone if (m_editline_ap) { 514e30f11d9SKate Stone return m_editline_ap->GetLines(m_base_line_number, lines, interrupted); 515b9c1b51eSKate Stone } else { 516cacde7dfSTodd Fiala #endif 517e30f11d9SKate Stone bool done = false; 518c3d874a5SGreg Clayton Error error; 51944d93782SGreg Clayton 520b9c1b51eSKate Stone while (!done) { 521f6913cd7SGreg Clayton // Show line numbers if we are asked to 52244d93782SGreg Clayton std::string line; 523b9c1b51eSKate Stone if (m_base_line_number > 0 && GetIsInteractive()) { 524f6913cd7SGreg Clayton FILE *out = GetOutputFILE(); 525f6913cd7SGreg Clayton if (out) 526b9c1b51eSKate Stone ::fprintf(out, "%u%s", m_base_line_number + (uint32_t)lines.GetSize(), 527b9c1b51eSKate Stone GetPrompt() == nullptr ? " " : ""); 528f6913cd7SGreg Clayton } 529f6913cd7SGreg Clayton 530e30f11d9SKate Stone m_curr_line_idx = lines.GetSize(); 531e30f11d9SKate Stone 532f0066ad0SGreg Clayton bool interrupted = false; 533b9c1b51eSKate Stone if (GetLine(line, interrupted) && !interrupted) { 53444d93782SGreg Clayton lines.AppendString(line); 535e30f11d9SKate Stone done = m_delegate.IOHandlerIsInputComplete(*this, lines); 536b9c1b51eSKate Stone } else { 537e30f11d9SKate Stone done = true; 53844d93782SGreg Clayton } 53944d93782SGreg Clayton } 54044d93782SGreg Clayton success = lines.GetSize() > 0; 541cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 54244d93782SGreg Clayton } 543cacde7dfSTodd Fiala #endif 54444d93782SGreg Clayton return success; 54544d93782SGreg Clayton } 54644d93782SGreg Clayton 54744d93782SGreg Clayton // Each IOHandler gets to run until it is done. It should read data 54844d93782SGreg Clayton // from the "in" and place output into "out" and "err and return 54944d93782SGreg Clayton // when done. 550b9c1b51eSKate Stone void IOHandlerEditline::Run() { 55144d93782SGreg Clayton std::string line; 552b9c1b51eSKate Stone while (IsActive()) { 553f0066ad0SGreg Clayton bool interrupted = false; 554b9c1b51eSKate Stone if (m_multi_line) { 55544d93782SGreg Clayton StringList lines; 556b9c1b51eSKate Stone if (GetLines(lines, interrupted)) { 557b9c1b51eSKate Stone if (interrupted) { 558e30f11d9SKate Stone m_done = m_interrupt_exits; 559e30f11d9SKate Stone m_delegate.IOHandlerInputInterrupted(*this, line); 560e30f11d9SKate Stone 561b9c1b51eSKate Stone } else { 56244d93782SGreg Clayton line = lines.CopyList(); 56344d93782SGreg Clayton m_delegate.IOHandlerInputComplete(*this, line); 56444d93782SGreg Clayton } 565b9c1b51eSKate Stone } else { 56644d93782SGreg Clayton m_done = true; 56744d93782SGreg Clayton } 568b9c1b51eSKate Stone } else { 569b9c1b51eSKate Stone if (GetLine(line, interrupted)) { 570e30f11d9SKate Stone if (interrupted) 571e30f11d9SKate Stone m_delegate.IOHandlerInputInterrupted(*this, line); 572e30f11d9SKate Stone else 57344d93782SGreg Clayton m_delegate.IOHandlerInputComplete(*this, line); 574b9c1b51eSKate Stone } else { 57544d93782SGreg Clayton m_done = true; 57644d93782SGreg Clayton } 57744d93782SGreg Clayton } 57844d93782SGreg Clayton } 57944d93782SGreg Clayton } 58044d93782SGreg Clayton 581b9c1b51eSKate Stone void IOHandlerEditline::Cancel() { 582cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 583e68f5d6bSGreg Clayton if (m_editline_ap) 5844446487dSPavel Labath m_editline_ap->Cancel(); 585cacde7dfSTodd Fiala #endif 586e68f5d6bSGreg Clayton } 587e68f5d6bSGreg Clayton 588b9c1b51eSKate Stone bool IOHandlerEditline::Interrupt() { 589f0066ad0SGreg Clayton // Let the delgate handle it first 590f0066ad0SGreg Clayton if (m_delegate.IOHandlerInterrupt(*this)) 591f0066ad0SGreg Clayton return true; 592f0066ad0SGreg Clayton 593cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 59444d93782SGreg Clayton if (m_editline_ap) 595f0066ad0SGreg Clayton return m_editline_ap->Interrupt(); 596cacde7dfSTodd Fiala #endif 597f0066ad0SGreg Clayton return false; 59844d93782SGreg Clayton } 59944d93782SGreg Clayton 600b9c1b51eSKate Stone void IOHandlerEditline::GotEOF() { 601cacde7dfSTodd Fiala #ifndef LLDB_DISABLE_LIBEDIT 60244d93782SGreg Clayton if (m_editline_ap) 60344d93782SGreg Clayton m_editline_ap->Interrupt(); 604cacde7dfSTodd Fiala #endif 60544d93782SGreg Clayton } 60644d93782SGreg Clayton 607b9c1b51eSKate Stone void IOHandlerEditline::PrintAsync(Stream *stream, const char *s, size_t len) { 6084446487dSPavel Labath #ifndef LLDB_DISABLE_LIBEDIT 6094446487dSPavel Labath if (m_editline_ap) 6104446487dSPavel Labath m_editline_ap->PrintAsync(stream, s, len); 6114446487dSPavel Labath else 6124446487dSPavel Labath #endif 613fab31220STed Woodward { 614fab31220STed Woodward #ifdef _MSC_VER 615341e4789SDawn Perchik const char *prompt = GetPrompt(); 616b9c1b51eSKate Stone if (prompt) { 617fab31220STed Woodward // Back up over previous prompt using Windows API 618fab31220STed Woodward CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info; 619fab31220STed Woodward HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE); 620fab31220STed Woodward GetConsoleScreenBufferInfo(console_handle, &screen_buffer_info); 621fab31220STed Woodward COORD coord = screen_buffer_info.dwCursorPosition; 622fab31220STed Woodward coord.X -= strlen(prompt); 623fab31220STed Woodward if (coord.X < 0) 624fab31220STed Woodward coord.X = 0; 625fab31220STed Woodward SetConsoleCursorPosition(console_handle, coord); 626fab31220STed Woodward } 627fab31220STed Woodward #endif 6284446487dSPavel Labath IOHandler::PrintAsync(stream, s, len); 629341e4789SDawn Perchik #ifdef _MSC_VER 630fab31220STed Woodward if (prompt) 631b9c1b51eSKate Stone IOHandler::PrintAsync(GetOutputStreamFile().get(), prompt, 632b9c1b51eSKate Stone strlen(prompt)); 633341e4789SDawn Perchik #endif 634fab31220STed Woodward } 6354446487dSPavel Labath } 6364446487dSPavel Labath 637914b8d98SDeepak Panickal // we may want curses to be disabled for some builds 638914b8d98SDeepak Panickal // for instance, windows 639914b8d98SDeepak Panickal #ifndef LLDB_DISABLE_CURSES 640914b8d98SDeepak Panickal 64144d93782SGreg Clayton #define KEY_RETURN 10 64244d93782SGreg Clayton #define KEY_ESCAPE 27 64344d93782SGreg Clayton 644b9c1b51eSKate Stone namespace curses { 64544d93782SGreg Clayton class Menu; 64644d93782SGreg Clayton class MenuDelegate; 64744d93782SGreg Clayton class Window; 64844d93782SGreg Clayton class WindowDelegate; 64944d93782SGreg Clayton typedef std::shared_ptr<Menu> MenuSP; 65044d93782SGreg Clayton typedef std::shared_ptr<MenuDelegate> MenuDelegateSP; 65144d93782SGreg Clayton typedef std::shared_ptr<Window> WindowSP; 65244d93782SGreg Clayton typedef std::shared_ptr<WindowDelegate> WindowDelegateSP; 65344d93782SGreg Clayton typedef std::vector<MenuSP> Menus; 65444d93782SGreg Clayton typedef std::vector<WindowSP> Windows; 65544d93782SGreg Clayton typedef std::vector<WindowDelegateSP> WindowDelegates; 65644d93782SGreg Clayton 65744d93782SGreg Clayton #if 0 65844d93782SGreg Clayton type summary add -s "x=${var.x}, y=${var.y}" curses::Point 65944d93782SGreg Clayton type summary add -s "w=${var.width}, h=${var.height}" curses::Size 66044d93782SGreg Clayton type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect 66144d93782SGreg Clayton #endif 662315b6884SEugene Zelenko 663b9c1b51eSKate Stone struct Point { 66444d93782SGreg Clayton int x; 66544d93782SGreg Clayton int y; 66644d93782SGreg Clayton 667b9c1b51eSKate Stone Point(int _x = 0, int _y = 0) : x(_x), y(_y) {} 66844d93782SGreg Clayton 669b9c1b51eSKate Stone void Clear() { 67044d93782SGreg Clayton x = 0; 67144d93782SGreg Clayton y = 0; 67244d93782SGreg Clayton } 67344d93782SGreg Clayton 674b9c1b51eSKate Stone Point &operator+=(const Point &rhs) { 67544d93782SGreg Clayton x += rhs.x; 67644d93782SGreg Clayton y += rhs.y; 67744d93782SGreg Clayton return *this; 67844d93782SGreg Clayton } 67944d93782SGreg Clayton 680b9c1b51eSKate Stone void Dump() { printf("(x=%i, y=%i)\n", x, y); } 68144d93782SGreg Clayton }; 68244d93782SGreg Clayton 683b9c1b51eSKate Stone bool operator==(const Point &lhs, const Point &rhs) { 68444d93782SGreg Clayton return lhs.x == rhs.x && lhs.y == rhs.y; 68544d93782SGreg Clayton } 686315b6884SEugene Zelenko 687b9c1b51eSKate Stone bool operator!=(const Point &lhs, const Point &rhs) { 68844d93782SGreg Clayton return lhs.x != rhs.x || lhs.y != rhs.y; 68944d93782SGreg Clayton } 69044d93782SGreg Clayton 691b9c1b51eSKate Stone struct Size { 69244d93782SGreg Clayton int width; 69344d93782SGreg Clayton int height; 694b9c1b51eSKate Stone Size(int w = 0, int h = 0) : width(w), height(h) {} 69544d93782SGreg Clayton 696b9c1b51eSKate Stone void Clear() { 69744d93782SGreg Clayton width = 0; 69844d93782SGreg Clayton height = 0; 69944d93782SGreg Clayton } 70044d93782SGreg Clayton 701b9c1b51eSKate Stone void Dump() { printf("(w=%i, h=%i)\n", width, height); } 70244d93782SGreg Clayton }; 70344d93782SGreg Clayton 704b9c1b51eSKate Stone bool operator==(const Size &lhs, const Size &rhs) { 70544d93782SGreg Clayton return lhs.width == rhs.width && lhs.height == rhs.height; 70644d93782SGreg Clayton } 707315b6884SEugene Zelenko 708b9c1b51eSKate Stone bool operator!=(const Size &lhs, const Size &rhs) { 70944d93782SGreg Clayton return lhs.width != rhs.width || lhs.height != rhs.height; 71044d93782SGreg Clayton } 71144d93782SGreg Clayton 712b9c1b51eSKate Stone struct Rect { 71344d93782SGreg Clayton Point origin; 71444d93782SGreg Clayton Size size; 71544d93782SGreg Clayton 716b9c1b51eSKate Stone Rect() : origin(), size() {} 71744d93782SGreg Clayton 718b9c1b51eSKate Stone Rect(const Point &p, const Size &s) : origin(p), size(s) {} 71944d93782SGreg Clayton 720b9c1b51eSKate Stone void Clear() { 72144d93782SGreg Clayton origin.Clear(); 72244d93782SGreg Clayton size.Clear(); 72344d93782SGreg Clayton } 72444d93782SGreg Clayton 725b9c1b51eSKate Stone void Dump() { 726b9c1b51eSKate Stone printf("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width, 727b9c1b51eSKate Stone size.height); 72844d93782SGreg Clayton } 72944d93782SGreg Clayton 730b9c1b51eSKate Stone void Inset(int w, int h) { 73144d93782SGreg Clayton if (size.width > w * 2) 73244d93782SGreg Clayton size.width -= w * 2; 73344d93782SGreg Clayton origin.x += w; 73444d93782SGreg Clayton 73544d93782SGreg Clayton if (size.height > h * 2) 73644d93782SGreg Clayton size.height -= h * 2; 73744d93782SGreg Clayton origin.y += h; 73844d93782SGreg Clayton } 739315b6884SEugene Zelenko 74044d93782SGreg Clayton // Return a status bar rectangle which is the last line of 74144d93782SGreg Clayton // this rectangle. This rectangle will be modified to not 74244d93782SGreg Clayton // include the status bar area. 743b9c1b51eSKate Stone Rect MakeStatusBar() { 74444d93782SGreg Clayton Rect status_bar; 745b9c1b51eSKate Stone if (size.height > 1) { 74644d93782SGreg Clayton status_bar.origin.x = origin.x; 74744d93782SGreg Clayton status_bar.origin.y = size.height; 74844d93782SGreg Clayton status_bar.size.width = size.width; 74944d93782SGreg Clayton status_bar.size.height = 1; 75044d93782SGreg Clayton --size.height; 75144d93782SGreg Clayton } 75244d93782SGreg Clayton return status_bar; 75344d93782SGreg Clayton } 75444d93782SGreg Clayton 75544d93782SGreg Clayton // Return a menubar rectangle which is the first line of 75644d93782SGreg Clayton // this rectangle. This rectangle will be modified to not 75744d93782SGreg Clayton // include the menubar area. 758b9c1b51eSKate Stone Rect MakeMenuBar() { 75944d93782SGreg Clayton Rect menubar; 760b9c1b51eSKate Stone if (size.height > 1) { 76144d93782SGreg Clayton menubar.origin.x = origin.x; 76244d93782SGreg Clayton menubar.origin.y = origin.y; 76344d93782SGreg Clayton menubar.size.width = size.width; 76444d93782SGreg Clayton menubar.size.height = 1; 76544d93782SGreg Clayton ++origin.y; 76644d93782SGreg Clayton --size.height; 76744d93782SGreg Clayton } 76844d93782SGreg Clayton return menubar; 76944d93782SGreg Clayton } 77044d93782SGreg Clayton 771b9c1b51eSKate Stone void HorizontalSplitPercentage(float top_percentage, Rect &top, 772b9c1b51eSKate Stone Rect &bottom) const { 77344d93782SGreg Clayton float top_height = top_percentage * size.height; 77444d93782SGreg Clayton HorizontalSplit(top_height, top, bottom); 77544d93782SGreg Clayton } 77644d93782SGreg Clayton 777b9c1b51eSKate Stone void HorizontalSplit(int top_height, Rect &top, Rect &bottom) const { 77844d93782SGreg Clayton top = *this; 779b9c1b51eSKate Stone if (top_height < size.height) { 78044d93782SGreg Clayton top.size.height = top_height; 78144d93782SGreg Clayton bottom.origin.x = origin.x; 78244d93782SGreg Clayton bottom.origin.y = origin.y + top.size.height; 78344d93782SGreg Clayton bottom.size.width = size.width; 78444d93782SGreg Clayton bottom.size.height = size.height - top.size.height; 785b9c1b51eSKate Stone } else { 78644d93782SGreg Clayton bottom.Clear(); 78744d93782SGreg Clayton } 78844d93782SGreg Clayton } 78944d93782SGreg Clayton 790b9c1b51eSKate Stone void VerticalSplitPercentage(float left_percentage, Rect &left, 791b9c1b51eSKate Stone Rect &right) const { 79244d93782SGreg Clayton float left_width = left_percentage * size.width; 79344d93782SGreg Clayton VerticalSplit(left_width, left, right); 79444d93782SGreg Clayton } 79544d93782SGreg Clayton 796b9c1b51eSKate Stone void VerticalSplit(int left_width, Rect &left, Rect &right) const { 79744d93782SGreg Clayton left = *this; 798b9c1b51eSKate Stone if (left_width < size.width) { 79944d93782SGreg Clayton left.size.width = left_width; 80044d93782SGreg Clayton right.origin.x = origin.x + left.size.width; 80144d93782SGreg Clayton right.origin.y = origin.y; 80244d93782SGreg Clayton right.size.width = size.width - left.size.width; 80344d93782SGreg Clayton right.size.height = size.height; 804b9c1b51eSKate Stone } else { 80544d93782SGreg Clayton right.Clear(); 80644d93782SGreg Clayton } 80744d93782SGreg Clayton } 80844d93782SGreg Clayton }; 80944d93782SGreg Clayton 810b9c1b51eSKate Stone bool operator==(const Rect &lhs, const Rect &rhs) { 81144d93782SGreg Clayton return lhs.origin == rhs.origin && lhs.size == rhs.size; 81244d93782SGreg Clayton } 813315b6884SEugene Zelenko 814b9c1b51eSKate Stone bool operator!=(const Rect &lhs, const Rect &rhs) { 81544d93782SGreg Clayton return lhs.origin != rhs.origin || lhs.size != rhs.size; 81644d93782SGreg Clayton } 81744d93782SGreg Clayton 818b9c1b51eSKate Stone enum HandleCharResult { 81944d93782SGreg Clayton eKeyNotHandled = 0, 82044d93782SGreg Clayton eKeyHandled = 1, 82144d93782SGreg Clayton eQuitApplication = 2 82244d93782SGreg Clayton }; 82344d93782SGreg Clayton 824b9c1b51eSKate Stone enum class MenuActionResult { 82544d93782SGreg Clayton Handled, 82644d93782SGreg Clayton NotHandled, 82744d93782SGreg Clayton Quit // Exit all menus and quit 82844d93782SGreg Clayton }; 82944d93782SGreg Clayton 830b9c1b51eSKate Stone struct KeyHelp { 83144d93782SGreg Clayton int ch; 83244d93782SGreg Clayton const char *description; 83344d93782SGreg Clayton }; 83444d93782SGreg Clayton 835b9c1b51eSKate Stone class WindowDelegate { 83644d93782SGreg Clayton public: 837b9c1b51eSKate Stone virtual ~WindowDelegate() = default; 83844d93782SGreg Clayton 839b9c1b51eSKate Stone virtual bool WindowDelegateDraw(Window &window, bool force) { 84044d93782SGreg Clayton return false; // Drawing not handled 84144d93782SGreg Clayton } 84244d93782SGreg Clayton 843b9c1b51eSKate Stone virtual HandleCharResult WindowDelegateHandleChar(Window &window, int key) { 84444d93782SGreg Clayton return eKeyNotHandled; 84544d93782SGreg Clayton } 84644d93782SGreg Clayton 847b9c1b51eSKate Stone virtual const char *WindowDelegateGetHelpText() { return nullptr; } 84844d93782SGreg Clayton 849b9c1b51eSKate Stone virtual KeyHelp *WindowDelegateGetKeyHelp() { return nullptr; } 85044d93782SGreg Clayton }; 85144d93782SGreg Clayton 852b9c1b51eSKate Stone class HelpDialogDelegate : public WindowDelegate { 85344d93782SGreg Clayton public: 85444d93782SGreg Clayton HelpDialogDelegate(const char *text, KeyHelp *key_help_array); 85544d93782SGreg Clayton 856bd5ae6b4SGreg Clayton ~HelpDialogDelegate() override; 85744d93782SGreg Clayton 858b9c1b51eSKate Stone bool WindowDelegateDraw(Window &window, bool force) override; 85944d93782SGreg Clayton 860b9c1b51eSKate Stone HandleCharResult WindowDelegateHandleChar(Window &window, int key) override; 86144d93782SGreg Clayton 862b9c1b51eSKate Stone size_t GetNumLines() const { return m_text.GetSize(); } 86344d93782SGreg Clayton 864b9c1b51eSKate Stone size_t GetMaxLineLength() const { return m_text.GetMaxStringLength(); } 86544d93782SGreg Clayton 86644d93782SGreg Clayton protected: 86744d93782SGreg Clayton StringList m_text; 86844d93782SGreg Clayton int m_first_visible_line; 86944d93782SGreg Clayton }; 87044d93782SGreg Clayton 871b9c1b51eSKate Stone class Window { 87244d93782SGreg Clayton public: 873b9c1b51eSKate Stone Window(const char *name) 874b9c1b51eSKate Stone : m_name(name), m_window(nullptr), m_panel(nullptr), m_parent(nullptr), 875b9c1b51eSKate Stone m_subwindows(), m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX), 876b9c1b51eSKate Stone m_prev_active_window_idx(UINT32_MAX), m_delete(false), 877b9c1b51eSKate Stone m_needs_update(true), m_can_activate(true), m_is_subwin(false) {} 87844d93782SGreg Clayton 879b9c1b51eSKate Stone Window(const char *name, WINDOW *w, bool del = true) 880b9c1b51eSKate Stone : m_name(name), m_window(nullptr), m_panel(nullptr), m_parent(nullptr), 881b9c1b51eSKate Stone m_subwindows(), m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX), 882b9c1b51eSKate Stone m_prev_active_window_idx(UINT32_MAX), m_delete(del), 883b9c1b51eSKate Stone m_needs_update(true), m_can_activate(true), m_is_subwin(false) { 88444d93782SGreg Clayton if (w) 88544d93782SGreg Clayton Reset(w); 88644d93782SGreg Clayton } 88744d93782SGreg Clayton 888b9c1b51eSKate Stone Window(const char *name, const Rect &bounds) 889b9c1b51eSKate Stone : m_name(name), m_window(nullptr), m_parent(nullptr), m_subwindows(), 890b9c1b51eSKate Stone m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX), 891b9c1b51eSKate Stone m_prev_active_window_idx(UINT32_MAX), m_delete(true), 892b9c1b51eSKate Stone m_needs_update(true), m_can_activate(true), m_is_subwin(false) { 893b9c1b51eSKate Stone Reset(::newwin(bounds.size.height, bounds.size.width, bounds.origin.y, 894b9c1b51eSKate Stone bounds.origin.y)); 89544d93782SGreg Clayton } 89644d93782SGreg Clayton 897b9c1b51eSKate Stone virtual ~Window() { 89844d93782SGreg Clayton RemoveSubWindows(); 89944d93782SGreg Clayton Reset(); 90044d93782SGreg Clayton } 90144d93782SGreg Clayton 902b9c1b51eSKate Stone void Reset(WINDOW *w = nullptr, bool del = true) { 90344d93782SGreg Clayton if (m_window == w) 90444d93782SGreg Clayton return; 90544d93782SGreg Clayton 906b9c1b51eSKate Stone if (m_panel) { 90744d93782SGreg Clayton ::del_panel(m_panel); 908c5dac77aSEugene Zelenko m_panel = nullptr; 90944d93782SGreg Clayton } 910b9c1b51eSKate Stone if (m_window && m_delete) { 91144d93782SGreg Clayton ::delwin(m_window); 912c5dac77aSEugene Zelenko m_window = nullptr; 91344d93782SGreg Clayton m_delete = false; 91444d93782SGreg Clayton } 915b9c1b51eSKate Stone if (w) { 91644d93782SGreg Clayton m_window = w; 91744d93782SGreg Clayton m_panel = ::new_panel(m_window); 91844d93782SGreg Clayton m_delete = del; 91944d93782SGreg Clayton } 92044d93782SGreg Clayton } 92144d93782SGreg Clayton 92244d93782SGreg Clayton void AttributeOn(attr_t attr) { ::wattron(m_window, attr); } 92344d93782SGreg Clayton void AttributeOff(attr_t attr) { ::wattroff(m_window, attr); } 924b9c1b51eSKate Stone void Box(chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) { 925b9c1b51eSKate Stone ::box(m_window, v_char, h_char); 926b9c1b51eSKate Stone } 92744d93782SGreg Clayton void Clear() { ::wclear(m_window); } 92844d93782SGreg Clayton void Erase() { ::werase(m_window); } 929b9c1b51eSKate Stone Rect GetBounds() { 930b9c1b51eSKate Stone return Rect(GetParentOrigin(), GetSize()); 931b9c1b51eSKate Stone } // Get the rectangle in our parent window 93244d93782SGreg Clayton int GetChar() { return ::wgetch(m_window); } 93344d93782SGreg Clayton int GetCursorX() { return getcurx(m_window); } 93444d93782SGreg Clayton int GetCursorY() { return getcury(m_window); } 935b9c1b51eSKate Stone Rect GetFrame() { 936b9c1b51eSKate Stone return Rect(Point(), GetSize()); 937b9c1b51eSKate Stone } // Get our rectangle in our own coordinate system 93844d93782SGreg Clayton Point GetParentOrigin() { return Point(GetParentX(), GetParentY()); } 93944d93782SGreg Clayton Size GetSize() { return Size(GetWidth(), GetHeight()); } 94044d93782SGreg Clayton int GetParentX() { return getparx(m_window); } 94144d93782SGreg Clayton int GetParentY() { return getpary(m_window); } 94244d93782SGreg Clayton int GetMaxX() { return getmaxx(m_window); } 94344d93782SGreg Clayton int GetMaxY() { return getmaxy(m_window); } 94444d93782SGreg Clayton int GetWidth() { return GetMaxX(); } 94544d93782SGreg Clayton int GetHeight() { return GetMaxY(); } 94644d93782SGreg Clayton void MoveCursor(int x, int y) { ::wmove(m_window, y, x); } 94744d93782SGreg Clayton void MoveWindow(int x, int y) { MoveWindow(Point(x, y)); } 94844d93782SGreg Clayton void Resize(int w, int h) { ::wresize(m_window, h, w); } 949b9c1b51eSKate Stone void Resize(const Size &size) { 950b9c1b51eSKate Stone ::wresize(m_window, size.height, size.width); 951b9c1b51eSKate Stone } 95244d93782SGreg Clayton void PutChar(int ch) { ::waddch(m_window, ch); } 95344d93782SGreg Clayton void PutCString(const char *s, int len = -1) { ::waddnstr(m_window, s, len); } 95444d93782SGreg Clayton void Refresh() { ::wrefresh(m_window); } 955b9c1b51eSKate Stone void DeferredRefresh() { 95644d93782SGreg Clayton // We are using panels, so we don't need to call this... 95744d93782SGreg Clayton //::wnoutrefresh(m_window); 95844d93782SGreg Clayton } 959b9c1b51eSKate Stone void SetBackground(int color_pair_idx) { 960b9c1b51eSKate Stone ::wbkgd(m_window, COLOR_PAIR(color_pair_idx)); 961b9c1b51eSKate Stone } 96244d93782SGreg Clayton void UnderlineOn() { AttributeOn(A_UNDERLINE); } 96344d93782SGreg Clayton void UnderlineOff() { AttributeOff(A_UNDERLINE); } 96444d93782SGreg Clayton 965b9c1b51eSKate Stone void PutCStringTruncated(const char *s, int right_pad) { 96644d93782SGreg Clayton int bytes_left = GetWidth() - GetCursorX(); 967b9c1b51eSKate Stone if (bytes_left > right_pad) { 96844d93782SGreg Clayton bytes_left -= right_pad; 96944d93782SGreg Clayton ::waddnstr(m_window, s, bytes_left); 97044d93782SGreg Clayton } 97144d93782SGreg Clayton } 97244d93782SGreg Clayton 973b9c1b51eSKate Stone void MoveWindow(const Point &origin) { 97444d93782SGreg Clayton const bool moving_window = origin != GetParentOrigin(); 975b9c1b51eSKate Stone if (m_is_subwin && moving_window) { 97644d93782SGreg Clayton // Can't move subwindows, must delete and re-create 97744d93782SGreg Clayton Size size = GetSize(); 978b9c1b51eSKate Stone Reset(::subwin(m_parent->m_window, size.height, size.width, origin.y, 979b9c1b51eSKate Stone origin.x), 980b9c1b51eSKate Stone true); 981b9c1b51eSKate Stone } else { 98244d93782SGreg Clayton ::mvwin(m_window, origin.y, origin.x); 98344d93782SGreg Clayton } 98444d93782SGreg Clayton } 98544d93782SGreg Clayton 986b9c1b51eSKate Stone void SetBounds(const Rect &bounds) { 98744d93782SGreg Clayton const bool moving_window = bounds.origin != GetParentOrigin(); 988b9c1b51eSKate Stone if (m_is_subwin && moving_window) { 98944d93782SGreg Clayton // Can't move subwindows, must delete and re-create 990b9c1b51eSKate Stone Reset(::subwin(m_parent->m_window, bounds.size.height, bounds.size.width, 991b9c1b51eSKate Stone bounds.origin.y, bounds.origin.x), 992b9c1b51eSKate Stone true); 993b9c1b51eSKate Stone } else { 99444d93782SGreg Clayton if (moving_window) 99544d93782SGreg Clayton MoveWindow(bounds.origin); 99644d93782SGreg Clayton Resize(bounds.size); 99744d93782SGreg Clayton } 99844d93782SGreg Clayton } 99944d93782SGreg Clayton 1000b9c1b51eSKate Stone void Printf(const char *format, ...) __attribute__((format(printf, 2, 3))) { 100144d93782SGreg Clayton va_list args; 100244d93782SGreg Clayton va_start(args, format); 100344d93782SGreg Clayton vwprintw(m_window, format, args); 100444d93782SGreg Clayton va_end(args); 100544d93782SGreg Clayton } 100644d93782SGreg Clayton 1007b9c1b51eSKate Stone void Touch() { 100844d93782SGreg Clayton ::touchwin(m_window); 100944d93782SGreg Clayton if (m_parent) 101044d93782SGreg Clayton m_parent->Touch(); 101144d93782SGreg Clayton } 101244d93782SGreg Clayton 1013b9c1b51eSKate Stone WindowSP CreateSubWindow(const char *name, const Rect &bounds, 1014b9c1b51eSKate Stone bool make_active) { 101544d93782SGreg Clayton WindowSP subwindow_sp; 1016b9c1b51eSKate Stone if (m_window) { 1017b9c1b51eSKate Stone subwindow_sp.reset(new Window( 1018b9c1b51eSKate Stone name, ::subwin(m_window, bounds.size.height, bounds.size.width, 1019b9c1b51eSKate Stone bounds.origin.y, bounds.origin.x), 1020b9c1b51eSKate Stone true)); 102144d93782SGreg Clayton subwindow_sp->m_is_subwin = true; 1022b9c1b51eSKate Stone } else { 1023b9c1b51eSKate Stone subwindow_sp.reset( 1024b9c1b51eSKate Stone new Window(name, ::newwin(bounds.size.height, bounds.size.width, 1025b9c1b51eSKate Stone bounds.origin.y, bounds.origin.x), 1026b9c1b51eSKate Stone true)); 102744d93782SGreg Clayton subwindow_sp->m_is_subwin = false; 102844d93782SGreg Clayton } 102944d93782SGreg Clayton subwindow_sp->m_parent = this; 1030b9c1b51eSKate Stone if (make_active) { 103144d93782SGreg Clayton m_prev_active_window_idx = m_curr_active_window_idx; 103244d93782SGreg Clayton m_curr_active_window_idx = m_subwindows.size(); 103344d93782SGreg Clayton } 103444d93782SGreg Clayton m_subwindows.push_back(subwindow_sp); 103544d93782SGreg Clayton ::top_panel(subwindow_sp->m_panel); 103644d93782SGreg Clayton m_needs_update = true; 103744d93782SGreg Clayton return subwindow_sp; 103844d93782SGreg Clayton } 103944d93782SGreg Clayton 1040b9c1b51eSKate Stone bool RemoveSubWindow(Window *window) { 104144d93782SGreg Clayton Windows::iterator pos, end = m_subwindows.end(); 104244d93782SGreg Clayton size_t i = 0; 1043b9c1b51eSKate Stone for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) { 1044b9c1b51eSKate Stone if ((*pos).get() == window) { 104544d93782SGreg Clayton if (m_prev_active_window_idx == i) 104644d93782SGreg Clayton m_prev_active_window_idx = UINT32_MAX; 1047b9c1b51eSKate Stone else if (m_prev_active_window_idx != UINT32_MAX && 1048b9c1b51eSKate Stone m_prev_active_window_idx > i) 104944d93782SGreg Clayton --m_prev_active_window_idx; 105044d93782SGreg Clayton 105144d93782SGreg Clayton if (m_curr_active_window_idx == i) 105244d93782SGreg Clayton m_curr_active_window_idx = UINT32_MAX; 1053b9c1b51eSKate Stone else if (m_curr_active_window_idx != UINT32_MAX && 1054b9c1b51eSKate Stone m_curr_active_window_idx > i) 105544d93782SGreg Clayton --m_curr_active_window_idx; 105644d93782SGreg Clayton window->Erase(); 105744d93782SGreg Clayton m_subwindows.erase(pos); 105844d93782SGreg Clayton m_needs_update = true; 105944d93782SGreg Clayton if (m_parent) 106044d93782SGreg Clayton m_parent->Touch(); 106144d93782SGreg Clayton else 106244d93782SGreg Clayton ::touchwin(stdscr); 106344d93782SGreg Clayton return true; 106444d93782SGreg Clayton } 106544d93782SGreg Clayton } 106644d93782SGreg Clayton return false; 106744d93782SGreg Clayton } 106844d93782SGreg Clayton 1069b9c1b51eSKate Stone WindowSP FindSubWindow(const char *name) { 107044d93782SGreg Clayton Windows::iterator pos, end = m_subwindows.end(); 107144d93782SGreg Clayton size_t i = 0; 1072b9c1b51eSKate Stone for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) { 107344d93782SGreg Clayton if ((*pos)->m_name.compare(name) == 0) 107444d93782SGreg Clayton return *pos; 107544d93782SGreg Clayton } 107644d93782SGreg Clayton return WindowSP(); 107744d93782SGreg Clayton } 107844d93782SGreg Clayton 1079b9c1b51eSKate Stone void RemoveSubWindows() { 108044d93782SGreg Clayton m_curr_active_window_idx = UINT32_MAX; 108144d93782SGreg Clayton m_prev_active_window_idx = UINT32_MAX; 108244d93782SGreg Clayton for (Windows::iterator pos = m_subwindows.begin(); 1083b9c1b51eSKate Stone pos != m_subwindows.end(); pos = m_subwindows.erase(pos)) { 108444d93782SGreg Clayton (*pos)->Erase(); 108544d93782SGreg Clayton } 108644d93782SGreg Clayton if (m_parent) 108744d93782SGreg Clayton m_parent->Touch(); 108844d93782SGreg Clayton else 108944d93782SGreg Clayton ::touchwin(stdscr); 109044d93782SGreg Clayton } 109144d93782SGreg Clayton 1092b9c1b51eSKate Stone WINDOW *get() { return m_window; } 109344d93782SGreg Clayton 1094b9c1b51eSKate Stone operator WINDOW *() { return m_window; } 109544d93782SGreg Clayton 109644d93782SGreg Clayton //---------------------------------------------------------------------- 109744d93782SGreg Clayton // Window drawing utilities 109844d93782SGreg Clayton //---------------------------------------------------------------------- 1099b9c1b51eSKate Stone void DrawTitleBox(const char *title, const char *bottom_message = nullptr) { 110044d93782SGreg Clayton attr_t attr = 0; 110144d93782SGreg Clayton if (IsActive()) 110244d93782SGreg Clayton attr = A_BOLD | COLOR_PAIR(2); 110344d93782SGreg Clayton else 110444d93782SGreg Clayton attr = 0; 110544d93782SGreg Clayton if (attr) 110644d93782SGreg Clayton AttributeOn(attr); 110744d93782SGreg Clayton 110844d93782SGreg Clayton Box(); 110944d93782SGreg Clayton MoveCursor(3, 0); 111044d93782SGreg Clayton 1111b9c1b51eSKate Stone if (title && title[0]) { 111244d93782SGreg Clayton PutChar('<'); 111344d93782SGreg Clayton PutCString(title); 111444d93782SGreg Clayton PutChar('>'); 111544d93782SGreg Clayton } 111644d93782SGreg Clayton 1117b9c1b51eSKate Stone if (bottom_message && bottom_message[0]) { 111844d93782SGreg Clayton int bottom_message_length = strlen(bottom_message); 111944d93782SGreg Clayton int x = GetWidth() - 3 - (bottom_message_length + 2); 112044d93782SGreg Clayton 1121b9c1b51eSKate Stone if (x > 0) { 112244d93782SGreg Clayton MoveCursor(x, GetHeight() - 1); 112344d93782SGreg Clayton PutChar('['); 112444d93782SGreg Clayton PutCString(bottom_message); 112544d93782SGreg Clayton PutChar(']'); 1126b9c1b51eSKate Stone } else { 112744d93782SGreg Clayton MoveCursor(1, GetHeight() - 1); 112844d93782SGreg Clayton PutChar('['); 112944d93782SGreg Clayton PutCStringTruncated(bottom_message, 1); 113044d93782SGreg Clayton } 113144d93782SGreg Clayton } 113244d93782SGreg Clayton if (attr) 113344d93782SGreg Clayton AttributeOff(attr); 113444d93782SGreg Clayton } 113544d93782SGreg Clayton 1136b9c1b51eSKate Stone virtual void Draw(bool force) { 113744d93782SGreg Clayton if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw(*this, force)) 113844d93782SGreg Clayton return; 113944d93782SGreg Clayton 114044d93782SGreg Clayton for (auto &subwindow_sp : m_subwindows) 114144d93782SGreg Clayton subwindow_sp->Draw(force); 114244d93782SGreg Clayton } 114344d93782SGreg Clayton 1144b9c1b51eSKate Stone bool CreateHelpSubwindow() { 1145b9c1b51eSKate Stone if (m_delegate_sp) { 114644d93782SGreg Clayton const char *text = m_delegate_sp->WindowDelegateGetHelpText(); 114744d93782SGreg Clayton KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp(); 1148b9c1b51eSKate Stone if ((text && text[0]) || key_help) { 1149b9c1b51eSKate Stone std::auto_ptr<HelpDialogDelegate> help_delegate_ap( 1150b9c1b51eSKate Stone new HelpDialogDelegate(text, key_help)); 115144d93782SGreg Clayton const size_t num_lines = help_delegate_ap->GetNumLines(); 115244d93782SGreg Clayton const size_t max_length = help_delegate_ap->GetMaxLineLength(); 115344d93782SGreg Clayton Rect bounds = GetBounds(); 115444d93782SGreg Clayton bounds.Inset(1, 1); 1155b9c1b51eSKate Stone if (max_length + 4 < static_cast<size_t>(bounds.size.width)) { 115644d93782SGreg Clayton bounds.origin.x += (bounds.size.width - max_length + 4) / 2; 115744d93782SGreg Clayton bounds.size.width = max_length + 4; 1158b9c1b51eSKate Stone } else { 1159b9c1b51eSKate Stone if (bounds.size.width > 100) { 116044d93782SGreg Clayton const int inset_w = bounds.size.width / 4; 116144d93782SGreg Clayton bounds.origin.x += inset_w; 116244d93782SGreg Clayton bounds.size.width -= 2 * inset_w; 116344d93782SGreg Clayton } 116444d93782SGreg Clayton } 116544d93782SGreg Clayton 1166b9c1b51eSKate Stone if (num_lines + 2 < static_cast<size_t>(bounds.size.height)) { 116744d93782SGreg Clayton bounds.origin.y += (bounds.size.height - num_lines + 2) / 2; 116844d93782SGreg Clayton bounds.size.height = num_lines + 2; 1169b9c1b51eSKate Stone } else { 1170b9c1b51eSKate Stone if (bounds.size.height > 100) { 117144d93782SGreg Clayton const int inset_h = bounds.size.height / 4; 117244d93782SGreg Clayton bounds.origin.y += inset_h; 117344d93782SGreg Clayton bounds.size.height -= 2 * inset_h; 117444d93782SGreg Clayton } 117544d93782SGreg Clayton } 11765fdb09bbSGreg Clayton WindowSP help_window_sp; 11775fdb09bbSGreg Clayton Window *parent_window = GetParent(); 11785fdb09bbSGreg Clayton if (parent_window) 11795fdb09bbSGreg Clayton help_window_sp = parent_window->CreateSubWindow("Help", bounds, true); 11805fdb09bbSGreg Clayton else 11815fdb09bbSGreg Clayton help_window_sp = CreateSubWindow("Help", bounds, true); 1182b9c1b51eSKate Stone help_window_sp->SetDelegate( 1183b9c1b51eSKate Stone WindowDelegateSP(help_delegate_ap.release())); 118444d93782SGreg Clayton return true; 118544d93782SGreg Clayton } 118644d93782SGreg Clayton } 118744d93782SGreg Clayton return false; 118844d93782SGreg Clayton } 118944d93782SGreg Clayton 1190b9c1b51eSKate Stone virtual HandleCharResult HandleChar(int key) { 119144d93782SGreg Clayton // Always check the active window first 119244d93782SGreg Clayton HandleCharResult result = eKeyNotHandled; 119344d93782SGreg Clayton WindowSP active_window_sp = GetActiveWindow(); 1194b9c1b51eSKate Stone if (active_window_sp) { 119544d93782SGreg Clayton result = active_window_sp->HandleChar(key); 119644d93782SGreg Clayton if (result != eKeyNotHandled) 119744d93782SGreg Clayton return result; 119844d93782SGreg Clayton } 119944d93782SGreg Clayton 1200b9c1b51eSKate Stone if (m_delegate_sp) { 120144d93782SGreg Clayton result = m_delegate_sp->WindowDelegateHandleChar(*this, key); 120244d93782SGreg Clayton if (result != eKeyNotHandled) 120344d93782SGreg Clayton return result; 120444d93782SGreg Clayton } 120544d93782SGreg Clayton 120644d93782SGreg Clayton // Then check for any windows that want any keys 120744d93782SGreg Clayton // that weren't handled. This is typically only 120844d93782SGreg Clayton // for a menubar. 120944d93782SGreg Clayton // Make a copy of the subwindows in case any HandleChar() 121044d93782SGreg Clayton // functions muck with the subwindows. If we don't do this, 121144d93782SGreg Clayton // we can crash when iterating over the subwindows. 121244d93782SGreg Clayton Windows subwindows(m_subwindows); 1213b9c1b51eSKate Stone for (auto subwindow_sp : subwindows) { 1214b9c1b51eSKate Stone if (!subwindow_sp->m_can_activate) { 121544d93782SGreg Clayton HandleCharResult result = subwindow_sp->HandleChar(key); 121644d93782SGreg Clayton if (result != eKeyNotHandled) 121744d93782SGreg Clayton return result; 121844d93782SGreg Clayton } 121944d93782SGreg Clayton } 122044d93782SGreg Clayton 122144d93782SGreg Clayton return eKeyNotHandled; 122244d93782SGreg Clayton } 122344d93782SGreg Clayton 1224b9c1b51eSKate Stone bool SetActiveWindow(Window *window) { 122544d93782SGreg Clayton const size_t num_subwindows = m_subwindows.size(); 1226b9c1b51eSKate Stone for (size_t i = 0; i < num_subwindows; ++i) { 1227b9c1b51eSKate Stone if (m_subwindows[i].get() == window) { 122844d93782SGreg Clayton m_prev_active_window_idx = m_curr_active_window_idx; 122944d93782SGreg Clayton ::top_panel(window->m_panel); 123044d93782SGreg Clayton m_curr_active_window_idx = i; 123144d93782SGreg Clayton return true; 123244d93782SGreg Clayton } 123344d93782SGreg Clayton } 123444d93782SGreg Clayton return false; 123544d93782SGreg Clayton } 123644d93782SGreg Clayton 1237b9c1b51eSKate Stone WindowSP GetActiveWindow() { 1238b9c1b51eSKate Stone if (!m_subwindows.empty()) { 1239b9c1b51eSKate Stone if (m_curr_active_window_idx >= m_subwindows.size()) { 1240b9c1b51eSKate Stone if (m_prev_active_window_idx < m_subwindows.size()) { 124144d93782SGreg Clayton m_curr_active_window_idx = m_prev_active_window_idx; 124244d93782SGreg Clayton m_prev_active_window_idx = UINT32_MAX; 1243b9c1b51eSKate Stone } else if (IsActive()) { 124444d93782SGreg Clayton m_prev_active_window_idx = UINT32_MAX; 124544d93782SGreg Clayton m_curr_active_window_idx = UINT32_MAX; 124644d93782SGreg Clayton 124744d93782SGreg Clayton // Find first window that wants to be active if this window is active 124844d93782SGreg Clayton const size_t num_subwindows = m_subwindows.size(); 1249b9c1b51eSKate Stone for (size_t i = 0; i < num_subwindows; ++i) { 1250b9c1b51eSKate Stone if (m_subwindows[i]->GetCanBeActive()) { 125144d93782SGreg Clayton m_curr_active_window_idx = i; 125244d93782SGreg Clayton break; 125344d93782SGreg Clayton } 125444d93782SGreg Clayton } 125544d93782SGreg Clayton } 125644d93782SGreg Clayton } 125744d93782SGreg Clayton 125844d93782SGreg Clayton if (m_curr_active_window_idx < m_subwindows.size()) 125944d93782SGreg Clayton return m_subwindows[m_curr_active_window_idx]; 126044d93782SGreg Clayton } 126144d93782SGreg Clayton return WindowSP(); 126244d93782SGreg Clayton } 126344d93782SGreg Clayton 1264b9c1b51eSKate Stone bool GetCanBeActive() const { return m_can_activate; } 126544d93782SGreg Clayton 1266b9c1b51eSKate Stone void SetCanBeActive(bool b) { m_can_activate = b; } 126744d93782SGreg Clayton 1268b9c1b51eSKate Stone const WindowDelegateSP &GetDelegate() const { return m_delegate_sp; } 126944d93782SGreg Clayton 1270b9c1b51eSKate Stone void SetDelegate(const WindowDelegateSP &delegate_sp) { 127144d93782SGreg Clayton m_delegate_sp = delegate_sp; 127244d93782SGreg Clayton } 127344d93782SGreg Clayton 1274b9c1b51eSKate Stone Window *GetParent() const { return m_parent; } 127544d93782SGreg Clayton 1276b9c1b51eSKate Stone bool IsActive() const { 127744d93782SGreg Clayton if (m_parent) 127844d93782SGreg Clayton return m_parent->GetActiveWindow().get() == this; 127944d93782SGreg Clayton else 128044d93782SGreg Clayton return true; // Top level window is always active 128144d93782SGreg Clayton } 128244d93782SGreg Clayton 1283b9c1b51eSKate Stone void SelectNextWindowAsActive() { 128444d93782SGreg Clayton // Move active focus to next window 128544d93782SGreg Clayton const size_t num_subwindows = m_subwindows.size(); 1286b9c1b51eSKate Stone if (m_curr_active_window_idx == UINT32_MAX) { 128744d93782SGreg Clayton uint32_t idx = 0; 1288b9c1b51eSKate Stone for (auto subwindow_sp : m_subwindows) { 1289b9c1b51eSKate Stone if (subwindow_sp->GetCanBeActive()) { 129044d93782SGreg Clayton m_curr_active_window_idx = idx; 129144d93782SGreg Clayton break; 129244d93782SGreg Clayton } 129344d93782SGreg Clayton ++idx; 129444d93782SGreg Clayton } 1295b9c1b51eSKate Stone } else if (m_curr_active_window_idx + 1 < num_subwindows) { 129644d93782SGreg Clayton bool handled = false; 129744d93782SGreg Clayton m_prev_active_window_idx = m_curr_active_window_idx; 1298b9c1b51eSKate Stone for (size_t idx = m_curr_active_window_idx + 1; idx < num_subwindows; 1299b9c1b51eSKate Stone ++idx) { 1300b9c1b51eSKate Stone if (m_subwindows[idx]->GetCanBeActive()) { 130144d93782SGreg Clayton m_curr_active_window_idx = idx; 130244d93782SGreg Clayton handled = true; 130344d93782SGreg Clayton break; 130444d93782SGreg Clayton } 130544d93782SGreg Clayton } 1306b9c1b51eSKate Stone if (!handled) { 1307b9c1b51eSKate Stone for (size_t idx = 0; idx <= m_prev_active_window_idx; ++idx) { 1308b9c1b51eSKate Stone if (m_subwindows[idx]->GetCanBeActive()) { 130944d93782SGreg Clayton m_curr_active_window_idx = idx; 131044d93782SGreg Clayton break; 131144d93782SGreg Clayton } 131244d93782SGreg Clayton } 131344d93782SGreg Clayton } 1314b9c1b51eSKate Stone } else { 131544d93782SGreg Clayton m_prev_active_window_idx = m_curr_active_window_idx; 1316b9c1b51eSKate Stone for (size_t idx = 0; idx < num_subwindows; ++idx) { 1317b9c1b51eSKate Stone if (m_subwindows[idx]->GetCanBeActive()) { 131844d93782SGreg Clayton m_curr_active_window_idx = idx; 131944d93782SGreg Clayton break; 132044d93782SGreg Clayton } 132144d93782SGreg Clayton } 132244d93782SGreg Clayton } 132344d93782SGreg Clayton } 132444d93782SGreg Clayton 1325b9c1b51eSKate Stone const char *GetName() const { return m_name.c_str(); } 1326315b6884SEugene Zelenko 132744d93782SGreg Clayton protected: 132844d93782SGreg Clayton std::string m_name; 132944d93782SGreg Clayton WINDOW *m_window; 133044d93782SGreg Clayton PANEL *m_panel; 133144d93782SGreg Clayton Window *m_parent; 133244d93782SGreg Clayton Windows m_subwindows; 133344d93782SGreg Clayton WindowDelegateSP m_delegate_sp; 133444d93782SGreg Clayton uint32_t m_curr_active_window_idx; 133544d93782SGreg Clayton uint32_t m_prev_active_window_idx; 133644d93782SGreg Clayton bool m_delete; 133744d93782SGreg Clayton bool m_needs_update; 133844d93782SGreg Clayton bool m_can_activate; 133944d93782SGreg Clayton bool m_is_subwin; 134044d93782SGreg Clayton 134144d93782SGreg Clayton private: 134244d93782SGreg Clayton DISALLOW_COPY_AND_ASSIGN(Window); 134344d93782SGreg Clayton }; 134444d93782SGreg Clayton 1345b9c1b51eSKate Stone class MenuDelegate { 134644d93782SGreg Clayton public: 1347315b6884SEugene Zelenko virtual ~MenuDelegate() = default; 134844d93782SGreg Clayton 1349b9c1b51eSKate Stone virtual MenuActionResult MenuDelegateAction(Menu &menu) = 0; 135044d93782SGreg Clayton }; 135144d93782SGreg Clayton 1352b9c1b51eSKate Stone class Menu : public WindowDelegate { 135344d93782SGreg Clayton public: 1354b9c1b51eSKate Stone enum class Type { Invalid, Bar, Item, Separator }; 135544d93782SGreg Clayton 135644d93782SGreg Clayton // Menubar or separator constructor 135744d93782SGreg Clayton Menu(Type type); 135844d93782SGreg Clayton 135944d93782SGreg Clayton // Menuitem constructor 1360b9c1b51eSKate Stone Menu(const char *name, const char *key_name, int key_value, 136144d93782SGreg Clayton uint64_t identifier); 136244d93782SGreg Clayton 1363315b6884SEugene Zelenko ~Menu() override = default; 136444d93782SGreg Clayton 1365b9c1b51eSKate Stone const MenuDelegateSP &GetDelegate() const { return m_delegate_sp; } 136644d93782SGreg Clayton 1367b9c1b51eSKate Stone void SetDelegate(const MenuDelegateSP &delegate_sp) { 136844d93782SGreg Clayton m_delegate_sp = delegate_sp; 136944d93782SGreg Clayton } 137044d93782SGreg Clayton 1371b9c1b51eSKate Stone void RecalculateNameLengths(); 137244d93782SGreg Clayton 1373b9c1b51eSKate Stone void AddSubmenu(const MenuSP &menu_sp); 137444d93782SGreg Clayton 1375b9c1b51eSKate Stone int DrawAndRunMenu(Window &window); 137644d93782SGreg Clayton 1377b9c1b51eSKate Stone void DrawMenuTitle(Window &window, bool highlight); 137844d93782SGreg Clayton 1379b9c1b51eSKate Stone bool WindowDelegateDraw(Window &window, bool force) override; 138044d93782SGreg Clayton 1381b9c1b51eSKate Stone HandleCharResult WindowDelegateHandleChar(Window &window, int key) override; 138244d93782SGreg Clayton 1383b9c1b51eSKate Stone MenuActionResult ActionPrivate(Menu &menu) { 138444d93782SGreg Clayton MenuActionResult result = MenuActionResult::NotHandled; 1385b9c1b51eSKate Stone if (m_delegate_sp) { 138644d93782SGreg Clayton result = m_delegate_sp->MenuDelegateAction(menu); 138744d93782SGreg Clayton if (result != MenuActionResult::NotHandled) 138844d93782SGreg Clayton return result; 1389b9c1b51eSKate Stone } else if (m_parent) { 139044d93782SGreg Clayton result = m_parent->ActionPrivate(menu); 139144d93782SGreg Clayton if (result != MenuActionResult::NotHandled) 139244d93782SGreg Clayton return result; 139344d93782SGreg Clayton } 139444d93782SGreg Clayton return m_canned_result; 139544d93782SGreg Clayton } 139644d93782SGreg Clayton 1397b9c1b51eSKate Stone MenuActionResult Action() { 139844d93782SGreg Clayton // Call the recursive action so it can try to handle it 139944d93782SGreg Clayton // with the menu delegate, and if not, try our parent menu 140044d93782SGreg Clayton return ActionPrivate(*this); 140144d93782SGreg Clayton } 140244d93782SGreg Clayton 1403b9c1b51eSKate Stone void SetCannedResult(MenuActionResult result) { m_canned_result = result; } 140444d93782SGreg Clayton 1405b9c1b51eSKate Stone Menus &GetSubmenus() { return m_submenus; } 140644d93782SGreg Clayton 1407b9c1b51eSKate Stone const Menus &GetSubmenus() const { return m_submenus; } 140844d93782SGreg Clayton 1409b9c1b51eSKate Stone int GetSelectedSubmenuIndex() const { return m_selected; } 141044d93782SGreg Clayton 1411b9c1b51eSKate Stone void SetSelectedSubmenuIndex(int idx) { m_selected = idx; } 141244d93782SGreg Clayton 1413b9c1b51eSKate Stone Type GetType() const { return m_type; } 141444d93782SGreg Clayton 1415b9c1b51eSKate Stone int GetStartingColumn() const { return m_start_col; } 141644d93782SGreg Clayton 1417b9c1b51eSKate Stone void SetStartingColumn(int col) { m_start_col = col; } 141844d93782SGreg Clayton 1419b9c1b51eSKate Stone int GetKeyValue() const { return m_key_value; } 142044d93782SGreg Clayton 1421b9c1b51eSKate Stone void SetKeyValue(int key_value) { m_key_value = key_value; } 142244d93782SGreg Clayton 1423b9c1b51eSKate Stone std::string &GetName() { return m_name; } 142444d93782SGreg Clayton 1425b9c1b51eSKate Stone std::string &GetKeyName() { return m_key_name; } 142644d93782SGreg Clayton 1427b9c1b51eSKate Stone int GetDrawWidth() const { 142844d93782SGreg Clayton return m_max_submenu_name_length + m_max_submenu_key_name_length + 8; 142944d93782SGreg Clayton } 143044d93782SGreg Clayton 1431b9c1b51eSKate Stone uint64_t GetIdentifier() const { return m_identifier; } 143244d93782SGreg Clayton 1433b9c1b51eSKate Stone void SetIdentifier(uint64_t identifier) { m_identifier = identifier; } 143444d93782SGreg Clayton 143544d93782SGreg Clayton protected: 143644d93782SGreg Clayton std::string m_name; 143744d93782SGreg Clayton std::string m_key_name; 143844d93782SGreg Clayton uint64_t m_identifier; 143944d93782SGreg Clayton Type m_type; 144044d93782SGreg Clayton int m_key_value; 144144d93782SGreg Clayton int m_start_col; 144244d93782SGreg Clayton int m_max_submenu_name_length; 144344d93782SGreg Clayton int m_max_submenu_key_name_length; 144444d93782SGreg Clayton int m_selected; 144544d93782SGreg Clayton Menu *m_parent; 144644d93782SGreg Clayton Menus m_submenus; 144744d93782SGreg Clayton WindowSP m_menu_window_sp; 144844d93782SGreg Clayton MenuActionResult m_canned_result; 144944d93782SGreg Clayton MenuDelegateSP m_delegate_sp; 145044d93782SGreg Clayton }; 145144d93782SGreg Clayton 145244d93782SGreg Clayton // Menubar or separator constructor 1453b9c1b51eSKate Stone Menu::Menu(Type type) 1454b9c1b51eSKate Stone : m_name(), m_key_name(), m_identifier(0), m_type(type), m_key_value(0), 1455b9c1b51eSKate Stone m_start_col(0), m_max_submenu_name_length(0), 1456b9c1b51eSKate Stone m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr), 1457b9c1b51eSKate Stone m_submenus(), m_canned_result(MenuActionResult::NotHandled), 1458b9c1b51eSKate Stone m_delegate_sp() {} 145944d93782SGreg Clayton 146044d93782SGreg Clayton // Menuitem constructor 1461b9c1b51eSKate Stone Menu::Menu(const char *name, const char *key_name, int key_value, 1462b9c1b51eSKate Stone uint64_t identifier) 1463b9c1b51eSKate Stone : m_name(), m_key_name(), m_identifier(identifier), m_type(Type::Invalid), 1464b9c1b51eSKate Stone m_key_value(key_value), m_start_col(0), m_max_submenu_name_length(0), 1465b9c1b51eSKate Stone m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr), 1466b9c1b51eSKate Stone m_submenus(), m_canned_result(MenuActionResult::NotHandled), 1467b9c1b51eSKate Stone m_delegate_sp() { 1468b9c1b51eSKate Stone if (name && name[0]) { 146944d93782SGreg Clayton m_name = name; 147044d93782SGreg Clayton m_type = Type::Item; 147144d93782SGreg Clayton if (key_name && key_name[0]) 147244d93782SGreg Clayton m_key_name = key_name; 1473b9c1b51eSKate Stone } else { 147444d93782SGreg Clayton m_type = Type::Separator; 147544d93782SGreg Clayton } 147644d93782SGreg Clayton } 147744d93782SGreg Clayton 1478b9c1b51eSKate Stone void Menu::RecalculateNameLengths() { 147944d93782SGreg Clayton m_max_submenu_name_length = 0; 148044d93782SGreg Clayton m_max_submenu_key_name_length = 0; 148144d93782SGreg Clayton Menus &submenus = GetSubmenus(); 148244d93782SGreg Clayton const size_t num_submenus = submenus.size(); 1483b9c1b51eSKate Stone for (size_t i = 0; i < num_submenus; ++i) { 148444d93782SGreg Clayton Menu *submenu = submenus[i].get(); 14853985c8c6SSaleem Abdulrasool if (static_cast<size_t>(m_max_submenu_name_length) < submenu->m_name.size()) 148644d93782SGreg Clayton m_max_submenu_name_length = submenu->m_name.size(); 1487b9c1b51eSKate Stone if (static_cast<size_t>(m_max_submenu_key_name_length) < 1488b9c1b51eSKate Stone submenu->m_key_name.size()) 148944d93782SGreg Clayton m_max_submenu_key_name_length = submenu->m_key_name.size(); 149044d93782SGreg Clayton } 149144d93782SGreg Clayton } 149244d93782SGreg Clayton 1493b9c1b51eSKate Stone void Menu::AddSubmenu(const MenuSP &menu_sp) { 149444d93782SGreg Clayton menu_sp->m_parent = this; 14953985c8c6SSaleem Abdulrasool if (static_cast<size_t>(m_max_submenu_name_length) < menu_sp->m_name.size()) 149644d93782SGreg Clayton m_max_submenu_name_length = menu_sp->m_name.size(); 1497b9c1b51eSKate Stone if (static_cast<size_t>(m_max_submenu_key_name_length) < 1498b9c1b51eSKate Stone menu_sp->m_key_name.size()) 149944d93782SGreg Clayton m_max_submenu_key_name_length = menu_sp->m_key_name.size(); 150044d93782SGreg Clayton m_submenus.push_back(menu_sp); 150144d93782SGreg Clayton } 150244d93782SGreg Clayton 1503b9c1b51eSKate Stone void Menu::DrawMenuTitle(Window &window, bool highlight) { 1504b9c1b51eSKate Stone if (m_type == Type::Separator) { 150544d93782SGreg Clayton window.MoveCursor(0, window.GetCursorY()); 150644d93782SGreg Clayton window.PutChar(ACS_LTEE); 150744d93782SGreg Clayton int width = window.GetWidth(); 1508b9c1b51eSKate Stone if (width > 2) { 150944d93782SGreg Clayton width -= 2; 15103985c8c6SSaleem Abdulrasool for (int i = 0; i < width; ++i) 151144d93782SGreg Clayton window.PutChar(ACS_HLINE); 151244d93782SGreg Clayton } 151344d93782SGreg Clayton window.PutChar(ACS_RTEE); 1514b9c1b51eSKate Stone } else { 151544d93782SGreg Clayton const int shortcut_key = m_key_value; 151644d93782SGreg Clayton bool underlined_shortcut = false; 151744d93782SGreg Clayton const attr_t hilgight_attr = A_REVERSE; 151844d93782SGreg Clayton if (highlight) 151944d93782SGreg Clayton window.AttributeOn(hilgight_attr); 1520b9c1b51eSKate Stone if (isprint(shortcut_key)) { 152144d93782SGreg Clayton size_t lower_pos = m_name.find(tolower(shortcut_key)); 152244d93782SGreg Clayton size_t upper_pos = m_name.find(toupper(shortcut_key)); 152344d93782SGreg Clayton const char *name = m_name.c_str(); 152444d93782SGreg Clayton size_t pos = std::min<size_t>(lower_pos, upper_pos); 1525b9c1b51eSKate Stone if (pos != std::string::npos) { 152644d93782SGreg Clayton underlined_shortcut = true; 1527b9c1b51eSKate Stone if (pos > 0) { 152844d93782SGreg Clayton window.PutCString(name, pos); 152944d93782SGreg Clayton name += pos; 153044d93782SGreg Clayton } 153144d93782SGreg Clayton const attr_t shortcut_attr = A_UNDERLINE | A_BOLD; 153244d93782SGreg Clayton window.AttributeOn(shortcut_attr); 153344d93782SGreg Clayton window.PutChar(name[0]); 153444d93782SGreg Clayton window.AttributeOff(shortcut_attr); 153544d93782SGreg Clayton name++; 153644d93782SGreg Clayton if (name[0]) 153744d93782SGreg Clayton window.PutCString(name); 153844d93782SGreg Clayton } 153944d93782SGreg Clayton } 154044d93782SGreg Clayton 1541b9c1b51eSKate Stone if (!underlined_shortcut) { 154244d93782SGreg Clayton window.PutCString(m_name.c_str()); 154344d93782SGreg Clayton } 154444d93782SGreg Clayton 154544d93782SGreg Clayton if (highlight) 154644d93782SGreg Clayton window.AttributeOff(hilgight_attr); 154744d93782SGreg Clayton 1548b9c1b51eSKate Stone if (m_key_name.empty()) { 1549b9c1b51eSKate Stone if (!underlined_shortcut && isprint(m_key_value)) { 155044d93782SGreg Clayton window.AttributeOn(COLOR_PAIR(3)); 155144d93782SGreg Clayton window.Printf(" (%c)", m_key_value); 155244d93782SGreg Clayton window.AttributeOff(COLOR_PAIR(3)); 155344d93782SGreg Clayton } 1554b9c1b51eSKate Stone } else { 155544d93782SGreg Clayton window.AttributeOn(COLOR_PAIR(3)); 155644d93782SGreg Clayton window.Printf(" (%s)", m_key_name.c_str()); 155744d93782SGreg Clayton window.AttributeOff(COLOR_PAIR(3)); 155844d93782SGreg Clayton } 155944d93782SGreg Clayton } 156044d93782SGreg Clayton } 156144d93782SGreg Clayton 1562b9c1b51eSKate Stone bool Menu::WindowDelegateDraw(Window &window, bool force) { 156344d93782SGreg Clayton Menus &submenus = GetSubmenus(); 156444d93782SGreg Clayton const size_t num_submenus = submenus.size(); 156544d93782SGreg Clayton const int selected_idx = GetSelectedSubmenuIndex(); 156644d93782SGreg Clayton Menu::Type menu_type = GetType(); 1567b9c1b51eSKate Stone switch (menu_type) { 1568b9c1b51eSKate Stone case Menu::Type::Bar: { 156944d93782SGreg Clayton window.SetBackground(2); 157044d93782SGreg Clayton window.MoveCursor(0, 0); 1571b9c1b51eSKate Stone for (size_t i = 0; i < num_submenus; ++i) { 157244d93782SGreg Clayton Menu *menu = submenus[i].get(); 157344d93782SGreg Clayton if (i > 0) 157444d93782SGreg Clayton window.PutChar(' '); 157544d93782SGreg Clayton menu->SetStartingColumn(window.GetCursorX()); 157644d93782SGreg Clayton window.PutCString("| "); 157744d93782SGreg Clayton menu->DrawMenuTitle(window, false); 157844d93782SGreg Clayton } 157944d93782SGreg Clayton window.PutCString(" |"); 158044d93782SGreg Clayton window.DeferredRefresh(); 1581b9c1b51eSKate Stone } break; 158244d93782SGreg Clayton 1583b9c1b51eSKate Stone case Menu::Type::Item: { 158444d93782SGreg Clayton int y = 1; 158544d93782SGreg Clayton int x = 3; 158644d93782SGreg Clayton // Draw the menu 158744d93782SGreg Clayton int cursor_x = 0; 158844d93782SGreg Clayton int cursor_y = 0; 158944d93782SGreg Clayton window.Erase(); 159044d93782SGreg Clayton window.SetBackground(2); 159144d93782SGreg Clayton window.Box(); 1592b9c1b51eSKate Stone for (size_t i = 0; i < num_submenus; ++i) { 1593b9c1b51eSKate Stone const bool is_selected = (i == static_cast<size_t>(selected_idx)); 159444d93782SGreg Clayton window.MoveCursor(x, y + i); 1595b9c1b51eSKate Stone if (is_selected) { 159644d93782SGreg Clayton // Remember where we want the cursor to be 159744d93782SGreg Clayton cursor_x = x - 1; 159844d93782SGreg Clayton cursor_y = y + i; 159944d93782SGreg Clayton } 160044d93782SGreg Clayton submenus[i]->DrawMenuTitle(window, is_selected); 160144d93782SGreg Clayton } 160244d93782SGreg Clayton window.MoveCursor(cursor_x, cursor_y); 160344d93782SGreg Clayton window.DeferredRefresh(); 1604b9c1b51eSKate Stone } break; 160544d93782SGreg Clayton 160644d93782SGreg Clayton default: 160744d93782SGreg Clayton case Menu::Type::Separator: 160844d93782SGreg Clayton break; 160944d93782SGreg Clayton } 161044d93782SGreg Clayton return true; // Drawing handled... 161144d93782SGreg Clayton } 161244d93782SGreg Clayton 1613b9c1b51eSKate Stone HandleCharResult Menu::WindowDelegateHandleChar(Window &window, int key) { 161444d93782SGreg Clayton HandleCharResult result = eKeyNotHandled; 161544d93782SGreg Clayton 161644d93782SGreg Clayton Menus &submenus = GetSubmenus(); 161744d93782SGreg Clayton const size_t num_submenus = submenus.size(); 161844d93782SGreg Clayton const int selected_idx = GetSelectedSubmenuIndex(); 161944d93782SGreg Clayton Menu::Type menu_type = GetType(); 1620b9c1b51eSKate Stone if (menu_type == Menu::Type::Bar) { 162144d93782SGreg Clayton MenuSP run_menu_sp; 1622b9c1b51eSKate Stone switch (key) { 162344d93782SGreg Clayton case KEY_DOWN: 162444d93782SGreg Clayton case KEY_UP: 162544d93782SGreg Clayton // Show last menu or first menu 16263985c8c6SSaleem Abdulrasool if (selected_idx < static_cast<int>(num_submenus)) 162744d93782SGreg Clayton run_menu_sp = submenus[selected_idx]; 162844d93782SGreg Clayton else if (!submenus.empty()) 162944d93782SGreg Clayton run_menu_sp = submenus.front(); 163044d93782SGreg Clayton result = eKeyHandled; 163144d93782SGreg Clayton break; 163244d93782SGreg Clayton 163344d93782SGreg Clayton case KEY_RIGHT: 163444d93782SGreg Clayton ++m_selected; 16353985c8c6SSaleem Abdulrasool if (m_selected >= static_cast<int>(num_submenus)) 163644d93782SGreg Clayton m_selected = 0; 16373985c8c6SSaleem Abdulrasool if (m_selected < static_cast<int>(num_submenus)) 163844d93782SGreg Clayton run_menu_sp = submenus[m_selected]; 163944d93782SGreg Clayton else if (!submenus.empty()) 164044d93782SGreg Clayton run_menu_sp = submenus.front(); 164144d93782SGreg Clayton result = eKeyHandled; 164244d93782SGreg Clayton break; 164344d93782SGreg Clayton 164444d93782SGreg Clayton case KEY_LEFT: 164544d93782SGreg Clayton --m_selected; 164644d93782SGreg Clayton if (m_selected < 0) 164744d93782SGreg Clayton m_selected = num_submenus - 1; 16483985c8c6SSaleem Abdulrasool if (m_selected < static_cast<int>(num_submenus)) 164944d93782SGreg Clayton run_menu_sp = submenus[m_selected]; 165044d93782SGreg Clayton else if (!submenus.empty()) 165144d93782SGreg Clayton run_menu_sp = submenus.front(); 165244d93782SGreg Clayton result = eKeyHandled; 165344d93782SGreg Clayton break; 165444d93782SGreg Clayton 165544d93782SGreg Clayton default: 1656b9c1b51eSKate Stone for (size_t i = 0; i < num_submenus; ++i) { 1657b9c1b51eSKate Stone if (submenus[i]->GetKeyValue() == key) { 165844d93782SGreg Clayton SetSelectedSubmenuIndex(i); 165944d93782SGreg Clayton run_menu_sp = submenus[i]; 166044d93782SGreg Clayton result = eKeyHandled; 166144d93782SGreg Clayton break; 166244d93782SGreg Clayton } 166344d93782SGreg Clayton } 166444d93782SGreg Clayton break; 166544d93782SGreg Clayton } 166644d93782SGreg Clayton 1667b9c1b51eSKate Stone if (run_menu_sp) { 166844d93782SGreg Clayton // Run the action on this menu in case we need to populate the 166944d93782SGreg Clayton // menu with dynamic content and also in case check marks, and 167085025451SKamil Rytarowski // any other menu decorations need to be calculated 167144d93782SGreg Clayton if (run_menu_sp->Action() == MenuActionResult::Quit) 167244d93782SGreg Clayton return eQuitApplication; 167344d93782SGreg Clayton 167444d93782SGreg Clayton Rect menu_bounds; 167544d93782SGreg Clayton menu_bounds.origin.x = run_menu_sp->GetStartingColumn(); 167644d93782SGreg Clayton menu_bounds.origin.y = 1; 167744d93782SGreg Clayton menu_bounds.size.width = run_menu_sp->GetDrawWidth(); 167844d93782SGreg Clayton menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2; 167944d93782SGreg Clayton if (m_menu_window_sp) 168044d93782SGreg Clayton window.GetParent()->RemoveSubWindow(m_menu_window_sp.get()); 168144d93782SGreg Clayton 1682b9c1b51eSKate Stone m_menu_window_sp = window.GetParent()->CreateSubWindow( 1683b9c1b51eSKate Stone run_menu_sp->GetName().c_str(), menu_bounds, true); 168444d93782SGreg Clayton m_menu_window_sp->SetDelegate(run_menu_sp); 168544d93782SGreg Clayton } 1686b9c1b51eSKate Stone } else if (menu_type == Menu::Type::Item) { 1687b9c1b51eSKate Stone switch (key) { 168844d93782SGreg Clayton case KEY_DOWN: 1689b9c1b51eSKate Stone if (m_submenus.size() > 1) { 169044d93782SGreg Clayton const int start_select = m_selected; 1691b9c1b51eSKate Stone while (++m_selected != start_select) { 16923985c8c6SSaleem Abdulrasool if (static_cast<size_t>(m_selected) >= num_submenus) 169344d93782SGreg Clayton m_selected = 0; 169444d93782SGreg Clayton if (m_submenus[m_selected]->GetType() == Type::Separator) 169544d93782SGreg Clayton continue; 169644d93782SGreg Clayton else 169744d93782SGreg Clayton break; 169844d93782SGreg Clayton } 169944d93782SGreg Clayton return eKeyHandled; 170044d93782SGreg Clayton } 170144d93782SGreg Clayton break; 170244d93782SGreg Clayton 170344d93782SGreg Clayton case KEY_UP: 1704b9c1b51eSKate Stone if (m_submenus.size() > 1) { 170544d93782SGreg Clayton const int start_select = m_selected; 1706b9c1b51eSKate Stone while (--m_selected != start_select) { 17073985c8c6SSaleem Abdulrasool if (m_selected < static_cast<int>(0)) 170844d93782SGreg Clayton m_selected = num_submenus - 1; 170944d93782SGreg Clayton if (m_submenus[m_selected]->GetType() == Type::Separator) 171044d93782SGreg Clayton continue; 171144d93782SGreg Clayton else 171244d93782SGreg Clayton break; 171344d93782SGreg Clayton } 171444d93782SGreg Clayton return eKeyHandled; 171544d93782SGreg Clayton } 171644d93782SGreg Clayton break; 171744d93782SGreg Clayton 171844d93782SGreg Clayton case KEY_RETURN: 1719b9c1b51eSKate Stone if (static_cast<size_t>(selected_idx) < num_submenus) { 172044d93782SGreg Clayton if (submenus[selected_idx]->Action() == MenuActionResult::Quit) 172144d93782SGreg Clayton return eQuitApplication; 172244d93782SGreg Clayton window.GetParent()->RemoveSubWindow(&window); 172344d93782SGreg Clayton return eKeyHandled; 172444d93782SGreg Clayton } 172544d93782SGreg Clayton break; 172644d93782SGreg Clayton 1727b9c1b51eSKate Stone case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in 1728b9c1b51eSKate Stone // case other chars are entered for escaped sequences 172944d93782SGreg Clayton window.GetParent()->RemoveSubWindow(&window); 173044d93782SGreg Clayton return eKeyHandled; 173144d93782SGreg Clayton 173244d93782SGreg Clayton default: 1733b9c1b51eSKate Stone for (size_t i = 0; i < num_submenus; ++i) { 173444d93782SGreg Clayton Menu *menu = submenus[i].get(); 1735b9c1b51eSKate Stone if (menu->GetKeyValue() == key) { 173644d93782SGreg Clayton SetSelectedSubmenuIndex(i); 173744d93782SGreg Clayton window.GetParent()->RemoveSubWindow(&window); 173844d93782SGreg Clayton if (menu->Action() == MenuActionResult::Quit) 173944d93782SGreg Clayton return eQuitApplication; 174044d93782SGreg Clayton return eKeyHandled; 174144d93782SGreg Clayton } 174244d93782SGreg Clayton } 174344d93782SGreg Clayton break; 174444d93782SGreg Clayton } 1745b9c1b51eSKate Stone } else if (menu_type == Menu::Type::Separator) { 174644d93782SGreg Clayton } 174744d93782SGreg Clayton return result; 174844d93782SGreg Clayton } 174944d93782SGreg Clayton 1750b9c1b51eSKate Stone class Application { 175144d93782SGreg Clayton public: 1752b9c1b51eSKate Stone Application(FILE *in, FILE *out) 1753b9c1b51eSKate Stone : m_window_sp(), m_screen(nullptr), m_in(in), m_out(out) {} 175444d93782SGreg Clayton 1755b9c1b51eSKate Stone ~Application() { 175644d93782SGreg Clayton m_window_delegates.clear(); 175744d93782SGreg Clayton m_window_sp.reset(); 1758b9c1b51eSKate Stone if (m_screen) { 175944d93782SGreg Clayton ::delscreen(m_screen); 1760c5dac77aSEugene Zelenko m_screen = nullptr; 176144d93782SGreg Clayton } 176244d93782SGreg Clayton } 176344d93782SGreg Clayton 1764b9c1b51eSKate Stone void Initialize() { 176544d93782SGreg Clayton ::setlocale(LC_ALL, ""); 176644d93782SGreg Clayton ::setlocale(LC_CTYPE, ""); 176744d93782SGreg Clayton #if 0 176844d93782SGreg Clayton ::initscr(); 176944d93782SGreg Clayton #else 1770c5dac77aSEugene Zelenko m_screen = ::newterm(nullptr, m_out, m_in); 177144d93782SGreg Clayton #endif 177244d93782SGreg Clayton ::start_color(); 177344d93782SGreg Clayton ::curs_set(0); 177444d93782SGreg Clayton ::noecho(); 177544d93782SGreg Clayton ::keypad(stdscr, TRUE); 177644d93782SGreg Clayton } 177744d93782SGreg Clayton 1778b9c1b51eSKate Stone void Terminate() { ::endwin(); } 177944d93782SGreg Clayton 1780b9c1b51eSKate Stone void Run(Debugger &debugger) { 178144d93782SGreg Clayton bool done = false; 178244d93782SGreg Clayton int delay_in_tenths_of_a_second = 1; 178344d93782SGreg Clayton 178444d93782SGreg Clayton // Alas the threading model in curses is a bit lame so we need to 178544d93782SGreg Clayton // resort to polling every 0.5 seconds. We could poll for stdin 178644d93782SGreg Clayton // ourselves and then pass the keys down but then we need to 178744d93782SGreg Clayton // translate all of the escape sequences ourselves. So we resort to 178844d93782SGreg Clayton // polling for input because we need to receive async process events 178944d93782SGreg Clayton // while in this loop. 179044d93782SGreg Clayton 1791b9c1b51eSKate Stone halfdelay(delay_in_tenths_of_a_second); // Poll using some number of tenths 1792b9c1b51eSKate Stone // of seconds seconds when calling 1793b9c1b51eSKate Stone // Window::GetChar() 179444d93782SGreg Clayton 1795b9c1b51eSKate Stone ListenerSP listener_sp( 1796b9c1b51eSKate Stone Listener::MakeListener("lldb.IOHandler.curses.Application")); 179744d93782SGreg Clayton ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass()); 179844d93782SGreg Clayton ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass()); 179944d93782SGreg Clayton ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass()); 180044d93782SGreg Clayton debugger.EnableForwardEvents(listener_sp); 180144d93782SGreg Clayton 180244d93782SGreg Clayton bool update = true; 180344d93782SGreg Clayton #if defined(__APPLE__) 180444d93782SGreg Clayton std::deque<int> escape_chars; 180544d93782SGreg Clayton #endif 180644d93782SGreg Clayton 1807b9c1b51eSKate Stone while (!done) { 1808b9c1b51eSKate Stone if (update) { 180944d93782SGreg Clayton m_window_sp->Draw(false); 181044d93782SGreg Clayton // All windows should be calling Window::DeferredRefresh() instead 181144d93782SGreg Clayton // of Window::Refresh() so we can do a single update and avoid 181244d93782SGreg Clayton // any screen blinking 181344d93782SGreg Clayton update_panels(); 181444d93782SGreg Clayton 1815b9c1b51eSKate Stone // Cursor hiding isn't working on MacOSX, so hide it in the top left 1816b9c1b51eSKate Stone // corner 181744d93782SGreg Clayton m_window_sp->MoveCursor(0, 0); 181844d93782SGreg Clayton 181944d93782SGreg Clayton doupdate(); 182044d93782SGreg Clayton update = false; 182144d93782SGreg Clayton } 182244d93782SGreg Clayton 182344d93782SGreg Clayton #if defined(__APPLE__) 182444d93782SGreg Clayton // Terminal.app doesn't map its function keys correctly, F1-F4 default to: 1825b9c1b51eSKate Stone // \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if 1826b9c1b51eSKate Stone // possible 182744d93782SGreg Clayton int ch; 182844d93782SGreg Clayton if (escape_chars.empty()) 182944d93782SGreg Clayton ch = m_window_sp->GetChar(); 1830b9c1b51eSKate Stone else { 183144d93782SGreg Clayton ch = escape_chars.front(); 183244d93782SGreg Clayton escape_chars.pop_front(); 183344d93782SGreg Clayton } 1834b9c1b51eSKate Stone if (ch == KEY_ESCAPE) { 183544d93782SGreg Clayton int ch2 = m_window_sp->GetChar(); 1836b9c1b51eSKate Stone if (ch2 == 'O') { 183744d93782SGreg Clayton int ch3 = m_window_sp->GetChar(); 1838b9c1b51eSKate Stone switch (ch3) { 1839b9c1b51eSKate Stone case 'P': 1840b9c1b51eSKate Stone ch = KEY_F(1); 1841b9c1b51eSKate Stone break; 1842b9c1b51eSKate Stone case 'Q': 1843b9c1b51eSKate Stone ch = KEY_F(2); 1844b9c1b51eSKate Stone break; 1845b9c1b51eSKate Stone case 'R': 1846b9c1b51eSKate Stone ch = KEY_F(3); 1847b9c1b51eSKate Stone break; 1848b9c1b51eSKate Stone case 'S': 1849b9c1b51eSKate Stone ch = KEY_F(4); 1850b9c1b51eSKate Stone break; 185144d93782SGreg Clayton default: 185244d93782SGreg Clayton escape_chars.push_back(ch2); 185344d93782SGreg Clayton if (ch3 != -1) 185444d93782SGreg Clayton escape_chars.push_back(ch3); 185544d93782SGreg Clayton break; 185644d93782SGreg Clayton } 1857b9c1b51eSKate Stone } else if (ch2 != -1) 185844d93782SGreg Clayton escape_chars.push_back(ch2); 185944d93782SGreg Clayton } 186044d93782SGreg Clayton #else 186144d93782SGreg Clayton int ch = m_window_sp->GetChar(); 186244d93782SGreg Clayton 186344d93782SGreg Clayton #endif 1864b9c1b51eSKate Stone if (ch == -1) { 1865b9c1b51eSKate Stone if (feof(m_in) || ferror(m_in)) { 186644d93782SGreg Clayton done = true; 1867b9c1b51eSKate Stone } else { 186844d93782SGreg Clayton // Just a timeout from using halfdelay(), check for events 186944d93782SGreg Clayton EventSP event_sp; 1870b9c1b51eSKate Stone while (listener_sp->PeekAtNextEvent()) { 1871d35031e1SPavel Labath listener_sp->GetEvent(event_sp, std::chrono::seconds(0)); 187244d93782SGreg Clayton 1873b9c1b51eSKate Stone if (event_sp) { 187444d93782SGreg Clayton Broadcaster *broadcaster = event_sp->GetBroadcaster(); 1875b9c1b51eSKate Stone if (broadcaster) { 187644d93782SGreg Clayton // uint32_t event_type = event_sp->GetType(); 1877b9c1b51eSKate Stone ConstString broadcaster_class( 1878b9c1b51eSKate Stone broadcaster->GetBroadcasterClass()); 1879b9c1b51eSKate Stone if (broadcaster_class == broadcaster_class_process) { 1880b9c1b51eSKate Stone debugger.GetCommandInterpreter().UpdateExecutionContext( 1881b9c1b51eSKate Stone nullptr); 188244d93782SGreg Clayton update = true; 188344d93782SGreg Clayton continue; // Don't get any key, just update our view 188444d93782SGreg Clayton } 188544d93782SGreg Clayton } 188644d93782SGreg Clayton } 188744d93782SGreg Clayton } 188844d93782SGreg Clayton } 1889b9c1b51eSKate Stone } else { 189044d93782SGreg Clayton HandleCharResult key_result = m_window_sp->HandleChar(ch); 1891b9c1b51eSKate Stone switch (key_result) { 189244d93782SGreg Clayton case eKeyHandled: 1893c5dac77aSEugene Zelenko debugger.GetCommandInterpreter().UpdateExecutionContext(nullptr); 189444d93782SGreg Clayton update = true; 189544d93782SGreg Clayton break; 189644d93782SGreg Clayton case eKeyNotHandled: 189744d93782SGreg Clayton break; 189844d93782SGreg Clayton case eQuitApplication: 189944d93782SGreg Clayton done = true; 190044d93782SGreg Clayton break; 190144d93782SGreg Clayton } 190244d93782SGreg Clayton } 190344d93782SGreg Clayton } 190444d93782SGreg Clayton 190544d93782SGreg Clayton debugger.CancelForwardEvents(listener_sp); 190644d93782SGreg Clayton } 190744d93782SGreg Clayton 1908b9c1b51eSKate Stone WindowSP &GetMainWindow() { 190944d93782SGreg Clayton if (!m_window_sp) 191044d93782SGreg Clayton m_window_sp.reset(new Window("main", stdscr, false)); 191144d93782SGreg Clayton return m_window_sp; 191244d93782SGreg Clayton } 191344d93782SGreg Clayton 1914b9c1b51eSKate Stone WindowDelegates &GetWindowDelegates() { return m_window_delegates; } 191544d93782SGreg Clayton 191644d93782SGreg Clayton protected: 191744d93782SGreg Clayton WindowSP m_window_sp; 191844d93782SGreg Clayton WindowDelegates m_window_delegates; 191944d93782SGreg Clayton SCREEN *m_screen; 192044d93782SGreg Clayton FILE *m_in; 192144d93782SGreg Clayton FILE *m_out; 192244d93782SGreg Clayton }; 192344d93782SGreg Clayton 192444d93782SGreg Clayton } // namespace curses 192544d93782SGreg Clayton 192644d93782SGreg Clayton using namespace curses; 192744d93782SGreg Clayton 1928b9c1b51eSKate Stone struct Row { 19298369b28dSGreg Clayton ValueObjectManager value; 193044d93782SGreg Clayton Row *parent; 19318369b28dSGreg Clayton // The process stop ID when the children were calculated. 19328369b28dSGreg Clayton uint32_t children_stop_id; 193344d93782SGreg Clayton int row_idx; 193444d93782SGreg Clayton int x; 193544d93782SGreg Clayton int y; 193644d93782SGreg Clayton bool might_have_children; 193744d93782SGreg Clayton bool expanded; 193844d93782SGreg Clayton bool calculated_children; 193944d93782SGreg Clayton std::vector<Row> children; 194044d93782SGreg Clayton 1941b9c1b51eSKate Stone Row(const ValueObjectSP &v, Row *p) 19428369b28dSGreg Clayton : value(v, lldb::eDynamicDontRunTarget, true), parent(p), row_idx(0), 19438369b28dSGreg Clayton x(1), y(1), might_have_children(v ? v->MightHaveChildren() : false), 1944b9c1b51eSKate Stone expanded(false), calculated_children(false), children() {} 194544d93782SGreg Clayton 1946b9c1b51eSKate Stone size_t GetDepth() const { 194744d93782SGreg Clayton if (parent) 194844d93782SGreg Clayton return 1 + parent->GetDepth(); 194944d93782SGreg Clayton return 0; 195044d93782SGreg Clayton } 195144d93782SGreg Clayton 1952b9c1b51eSKate Stone void Expand() { 195344d93782SGreg Clayton expanded = true; 19548369b28dSGreg Clayton } 19558369b28dSGreg Clayton 19568369b28dSGreg Clayton std::vector<Row> &GetChildren() { 19578369b28dSGreg Clayton ProcessSP process_sp = value.GetProcessSP(); 19588369b28dSGreg Clayton auto stop_id = process_sp->GetStopID(); 19598369b28dSGreg Clayton if (process_sp && stop_id != children_stop_id) { 19608369b28dSGreg Clayton children_stop_id = stop_id; 19618369b28dSGreg Clayton calculated_children = false; 19628369b28dSGreg Clayton } 1963b9c1b51eSKate Stone if (!calculated_children) { 19648369b28dSGreg Clayton children.clear(); 196544d93782SGreg Clayton calculated_children = true; 19668369b28dSGreg Clayton ValueObjectSP valobj = value.GetSP(); 1967b9c1b51eSKate Stone if (valobj) { 196844d93782SGreg Clayton const size_t num_children = valobj->GetNumChildren(); 1969b9c1b51eSKate Stone for (size_t i = 0; i < num_children; ++i) { 197044d93782SGreg Clayton children.push_back(Row(valobj->GetChildAtIndex(i, true), this)); 197144d93782SGreg Clayton } 197244d93782SGreg Clayton } 197344d93782SGreg Clayton } 19748369b28dSGreg Clayton return children; 197544d93782SGreg Clayton } 197644d93782SGreg Clayton 19778369b28dSGreg Clayton void Unexpand() { 19788369b28dSGreg Clayton expanded = false; 19798369b28dSGreg Clayton calculated_children = false; 19808369b28dSGreg Clayton children.clear(); 19818369b28dSGreg Clayton } 198244d93782SGreg Clayton 1983b9c1b51eSKate Stone void DrawTree(Window &window) { 198444d93782SGreg Clayton if (parent) 198544d93782SGreg Clayton parent->DrawTreeForChild(window, this, 0); 198644d93782SGreg Clayton 1987b9c1b51eSKate Stone if (might_have_children) { 198844d93782SGreg Clayton // It we can get UTF8 characters to work we should try to use the "symbol" 198944d93782SGreg Clayton // UTF8 string below 199044d93782SGreg Clayton // const char *symbol = ""; 199144d93782SGreg Clayton // if (row.expanded) 199244d93782SGreg Clayton // symbol = "\xe2\x96\xbd "; 199344d93782SGreg Clayton // else 199444d93782SGreg Clayton // symbol = "\xe2\x96\xb7 "; 199544d93782SGreg Clayton // window.PutCString (symbol); 199644d93782SGreg Clayton 199744d93782SGreg Clayton // The ACS_DARROW and ACS_RARROW don't look very nice they are just a 199844d93782SGreg Clayton // 'v' or '>' character... 199944d93782SGreg Clayton // if (expanded) 200044d93782SGreg Clayton // window.PutChar (ACS_DARROW); 200144d93782SGreg Clayton // else 200244d93782SGreg Clayton // window.PutChar (ACS_RARROW); 200344d93782SGreg Clayton // Since we can't find any good looking right arrow/down arrow 200444d93782SGreg Clayton // symbols, just use a diamond... 200544d93782SGreg Clayton window.PutChar(ACS_DIAMOND); 200644d93782SGreg Clayton window.PutChar(ACS_HLINE); 200744d93782SGreg Clayton } 200844d93782SGreg Clayton } 200944d93782SGreg Clayton 2010b9c1b51eSKate Stone void DrawTreeForChild(Window &window, Row *child, uint32_t reverse_depth) { 201144d93782SGreg Clayton if (parent) 201244d93782SGreg Clayton parent->DrawTreeForChild(window, this, reverse_depth + 1); 201344d93782SGreg Clayton 20148369b28dSGreg Clayton if (&GetChildren().back() == child) { 201544d93782SGreg Clayton // Last child 2016b9c1b51eSKate Stone if (reverse_depth == 0) { 201744d93782SGreg Clayton window.PutChar(ACS_LLCORNER); 201844d93782SGreg Clayton window.PutChar(ACS_HLINE); 2019b9c1b51eSKate Stone } else { 202044d93782SGreg Clayton window.PutChar(' '); 202144d93782SGreg Clayton window.PutChar(' '); 202244d93782SGreg Clayton } 2023b9c1b51eSKate Stone } else { 2024b9c1b51eSKate Stone if (reverse_depth == 0) { 202544d93782SGreg Clayton window.PutChar(ACS_LTEE); 202644d93782SGreg Clayton window.PutChar(ACS_HLINE); 2027b9c1b51eSKate Stone } else { 202844d93782SGreg Clayton window.PutChar(ACS_VLINE); 202944d93782SGreg Clayton window.PutChar(' '); 203044d93782SGreg Clayton } 203144d93782SGreg Clayton } 203244d93782SGreg Clayton } 203344d93782SGreg Clayton }; 203444d93782SGreg Clayton 2035b9c1b51eSKate Stone struct DisplayOptions { 203644d93782SGreg Clayton bool show_types; 203744d93782SGreg Clayton }; 203844d93782SGreg Clayton 203944d93782SGreg Clayton class TreeItem; 204044d93782SGreg Clayton 2041b9c1b51eSKate Stone class TreeDelegate { 204244d93782SGreg Clayton public: 2043c5dac77aSEugene Zelenko TreeDelegate() = default; 2044315b6884SEugene Zelenko virtual ~TreeDelegate() = default; 2045315b6884SEugene Zelenko 204644d93782SGreg Clayton virtual void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) = 0; 204744d93782SGreg Clayton virtual void TreeDelegateGenerateChildren(TreeItem &item) = 0; 2048b9c1b51eSKate Stone virtual bool TreeDelegateItemSelected( 2049b9c1b51eSKate Stone TreeItem &item) = 0; // Return true if we need to update views 205044d93782SGreg Clayton }; 2051315b6884SEugene Zelenko 205244d93782SGreg Clayton typedef std::shared_ptr<TreeDelegate> TreeDelegateSP; 205344d93782SGreg Clayton 2054b9c1b51eSKate Stone class TreeItem { 205544d93782SGreg Clayton public: 2056b9c1b51eSKate Stone TreeItem(TreeItem *parent, TreeDelegate &delegate, bool might_have_children) 2057b9c1b51eSKate Stone : m_parent(parent), m_delegate(delegate), m_user_data(nullptr), 2058b9c1b51eSKate Stone m_identifier(0), m_row_idx(-1), m_children(), 2059b9c1b51eSKate Stone m_might_have_children(might_have_children), m_is_expanded(false) {} 206044d93782SGreg Clayton 2061b9c1b51eSKate Stone TreeItem &operator=(const TreeItem &rhs) { 2062b9c1b51eSKate Stone if (this != &rhs) { 206344d93782SGreg Clayton m_parent = rhs.m_parent; 206444d93782SGreg Clayton m_delegate = rhs.m_delegate; 2065ec990867SGreg Clayton m_user_data = rhs.m_user_data; 206644d93782SGreg Clayton m_identifier = rhs.m_identifier; 206744d93782SGreg Clayton m_row_idx = rhs.m_row_idx; 206844d93782SGreg Clayton m_children = rhs.m_children; 206944d93782SGreg Clayton m_might_have_children = rhs.m_might_have_children; 207044d93782SGreg Clayton m_is_expanded = rhs.m_is_expanded; 207144d93782SGreg Clayton } 207244d93782SGreg Clayton return *this; 207344d93782SGreg Clayton } 207444d93782SGreg Clayton 2075b9c1b51eSKate Stone size_t GetDepth() const { 207644d93782SGreg Clayton if (m_parent) 207744d93782SGreg Clayton return 1 + m_parent->GetDepth(); 207844d93782SGreg Clayton return 0; 207944d93782SGreg Clayton } 208044d93782SGreg Clayton 2081b9c1b51eSKate Stone int GetRowIndex() const { return m_row_idx; } 208244d93782SGreg Clayton 2083b9c1b51eSKate Stone void ClearChildren() { m_children.clear(); } 208444d93782SGreg Clayton 2085b9c1b51eSKate Stone void Resize(size_t n, const TreeItem &t) { m_children.resize(n, t); } 208644d93782SGreg Clayton 2087b9c1b51eSKate Stone TreeItem &operator[](size_t i) { return m_children[i]; } 208844d93782SGreg Clayton 2089b9c1b51eSKate Stone void SetRowIndex(int row_idx) { m_row_idx = row_idx; } 209044d93782SGreg Clayton 2091b9c1b51eSKate Stone size_t GetNumChildren() { 209244d93782SGreg Clayton m_delegate.TreeDelegateGenerateChildren(*this); 209344d93782SGreg Clayton return m_children.size(); 209444d93782SGreg Clayton } 209544d93782SGreg Clayton 2096b9c1b51eSKate Stone void ItemWasSelected() { m_delegate.TreeDelegateItemSelected(*this); } 2097315b6884SEugene Zelenko 2098b9c1b51eSKate Stone void CalculateRowIndexes(int &row_idx) { 209944d93782SGreg Clayton SetRowIndex(row_idx); 210044d93782SGreg Clayton ++row_idx; 210144d93782SGreg Clayton 2102ec990867SGreg Clayton const bool expanded = IsExpanded(); 2103ec990867SGreg Clayton 2104ec990867SGreg Clayton // The root item must calculate its children, 2105ec990867SGreg Clayton // or we must calculate the number of children 2106ec990867SGreg Clayton // if the item is expanded 2107c5dac77aSEugene Zelenko if (m_parent == nullptr || expanded) 210844d93782SGreg Clayton GetNumChildren(); 210944d93782SGreg Clayton 2110b9c1b51eSKate Stone for (auto &item : m_children) { 211144d93782SGreg Clayton if (expanded) 211244d93782SGreg Clayton item.CalculateRowIndexes(row_idx); 211344d93782SGreg Clayton else 211444d93782SGreg Clayton item.SetRowIndex(-1); 211544d93782SGreg Clayton } 211644d93782SGreg Clayton } 211744d93782SGreg Clayton 2118b9c1b51eSKate Stone TreeItem *GetParent() { return m_parent; } 211944d93782SGreg Clayton 2120b9c1b51eSKate Stone bool IsExpanded() const { return m_is_expanded; } 212144d93782SGreg Clayton 2122b9c1b51eSKate Stone void Expand() { m_is_expanded = true; } 212344d93782SGreg Clayton 2124b9c1b51eSKate Stone void Unexpand() { m_is_expanded = false; } 212544d93782SGreg Clayton 2126b9c1b51eSKate Stone bool Draw(Window &window, const int first_visible_row, 2127b9c1b51eSKate Stone const uint32_t selected_row_idx, int &row_idx, int &num_rows_left) { 212844d93782SGreg Clayton if (num_rows_left <= 0) 212944d93782SGreg Clayton return false; 213044d93782SGreg Clayton 2131b9c1b51eSKate Stone if (m_row_idx >= first_visible_row) { 213244d93782SGreg Clayton window.MoveCursor(2, row_idx + 1); 213344d93782SGreg Clayton 213444d93782SGreg Clayton if (m_parent) 213544d93782SGreg Clayton m_parent->DrawTreeForChild(window, this, 0); 213644d93782SGreg Clayton 2137b9c1b51eSKate Stone if (m_might_have_children) { 2138b9c1b51eSKate Stone // It we can get UTF8 characters to work we should try to use the 2139b9c1b51eSKate Stone // "symbol" 214044d93782SGreg Clayton // UTF8 string below 214144d93782SGreg Clayton // const char *symbol = ""; 214244d93782SGreg Clayton // if (row.expanded) 214344d93782SGreg Clayton // symbol = "\xe2\x96\xbd "; 214444d93782SGreg Clayton // else 214544d93782SGreg Clayton // symbol = "\xe2\x96\xb7 "; 214644d93782SGreg Clayton // window.PutCString (symbol); 214744d93782SGreg Clayton 214844d93782SGreg Clayton // The ACS_DARROW and ACS_RARROW don't look very nice they are just a 214944d93782SGreg Clayton // 'v' or '>' character... 215044d93782SGreg Clayton // if (expanded) 215144d93782SGreg Clayton // window.PutChar (ACS_DARROW); 215244d93782SGreg Clayton // else 215344d93782SGreg Clayton // window.PutChar (ACS_RARROW); 215444d93782SGreg Clayton // Since we can't find any good looking right arrow/down arrow 215544d93782SGreg Clayton // symbols, just use a diamond... 215644d93782SGreg Clayton window.PutChar(ACS_DIAMOND); 215744d93782SGreg Clayton window.PutChar(ACS_HLINE); 215844d93782SGreg Clayton } 2159b9c1b51eSKate Stone bool highlight = (selected_row_idx == static_cast<size_t>(m_row_idx)) && 2160b9c1b51eSKate Stone window.IsActive(); 216144d93782SGreg Clayton 216244d93782SGreg Clayton if (highlight) 216344d93782SGreg Clayton window.AttributeOn(A_REVERSE); 216444d93782SGreg Clayton 216544d93782SGreg Clayton m_delegate.TreeDelegateDrawTreeItem(*this, window); 216644d93782SGreg Clayton 216744d93782SGreg Clayton if (highlight) 216844d93782SGreg Clayton window.AttributeOff(A_REVERSE); 216944d93782SGreg Clayton ++row_idx; 217044d93782SGreg Clayton --num_rows_left; 217144d93782SGreg Clayton } 217244d93782SGreg Clayton 217344d93782SGreg Clayton if (num_rows_left <= 0) 217444d93782SGreg Clayton return false; // We are done drawing... 217544d93782SGreg Clayton 2176b9c1b51eSKate Stone if (IsExpanded()) { 2177b9c1b51eSKate Stone for (auto &item : m_children) { 217844d93782SGreg Clayton // If we displayed all the rows and item.Draw() returns 217944d93782SGreg Clayton // false we are done drawing and can exit this for loop 2180b9c1b51eSKate Stone if (!item.Draw(window, first_visible_row, selected_row_idx, row_idx, 2181b9c1b51eSKate Stone num_rows_left)) 218244d93782SGreg Clayton break; 218344d93782SGreg Clayton } 218444d93782SGreg Clayton } 218544d93782SGreg Clayton return num_rows_left >= 0; // Return true if not done drawing yet 218644d93782SGreg Clayton } 218744d93782SGreg Clayton 2188b9c1b51eSKate Stone void DrawTreeForChild(Window &window, TreeItem *child, 2189b9c1b51eSKate Stone uint32_t reverse_depth) { 219044d93782SGreg Clayton if (m_parent) 219144d93782SGreg Clayton m_parent->DrawTreeForChild(window, this, reverse_depth + 1); 219244d93782SGreg Clayton 2193b9c1b51eSKate Stone if (&m_children.back() == child) { 219444d93782SGreg Clayton // Last child 2195b9c1b51eSKate Stone if (reverse_depth == 0) { 219644d93782SGreg Clayton window.PutChar(ACS_LLCORNER); 219744d93782SGreg Clayton window.PutChar(ACS_HLINE); 2198b9c1b51eSKate Stone } else { 219944d93782SGreg Clayton window.PutChar(' '); 220044d93782SGreg Clayton window.PutChar(' '); 220144d93782SGreg Clayton } 2202b9c1b51eSKate Stone } else { 2203b9c1b51eSKate Stone if (reverse_depth == 0) { 220444d93782SGreg Clayton window.PutChar(ACS_LTEE); 220544d93782SGreg Clayton window.PutChar(ACS_HLINE); 2206b9c1b51eSKate Stone } else { 220744d93782SGreg Clayton window.PutChar(ACS_VLINE); 220844d93782SGreg Clayton window.PutChar(' '); 220944d93782SGreg Clayton } 221044d93782SGreg Clayton } 221144d93782SGreg Clayton } 221244d93782SGreg Clayton 2213b9c1b51eSKate Stone TreeItem *GetItemForRowIndex(uint32_t row_idx) { 22143985c8c6SSaleem Abdulrasool if (static_cast<uint32_t>(m_row_idx) == row_idx) 221544d93782SGreg Clayton return this; 221644d93782SGreg Clayton if (m_children.empty()) 2217c5dac77aSEugene Zelenko return nullptr; 2218b9c1b51eSKate Stone if (IsExpanded()) { 2219b9c1b51eSKate Stone for (auto &item : m_children) { 222044d93782SGreg Clayton TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx); 222144d93782SGreg Clayton if (selected_item_ptr) 222244d93782SGreg Clayton return selected_item_ptr; 222344d93782SGreg Clayton } 222444d93782SGreg Clayton } 2225c5dac77aSEugene Zelenko return nullptr; 222644d93782SGreg Clayton } 222744d93782SGreg Clayton 2228b9c1b51eSKate Stone void *GetUserData() const { return m_user_data; } 2229ec990867SGreg Clayton 2230b9c1b51eSKate Stone void SetUserData(void *user_data) { m_user_data = user_data; } 2231ec990867SGreg Clayton 2232b9c1b51eSKate Stone uint64_t GetIdentifier() const { return m_identifier; } 223344d93782SGreg Clayton 2234b9c1b51eSKate Stone void SetIdentifier(uint64_t identifier) { m_identifier = identifier; } 223544d93782SGreg Clayton 2236b9c1b51eSKate Stone void SetMightHaveChildren(bool b) { m_might_have_children = b; } 2237ec990867SGreg Clayton 223844d93782SGreg Clayton protected: 223944d93782SGreg Clayton TreeItem *m_parent; 224044d93782SGreg Clayton TreeDelegate &m_delegate; 2241ec990867SGreg Clayton void *m_user_data; 224244d93782SGreg Clayton uint64_t m_identifier; 2243b9c1b51eSKate Stone int m_row_idx; // Zero based visible row index, -1 if not visible or for the 2244b9c1b51eSKate Stone // root item 224544d93782SGreg Clayton std::vector<TreeItem> m_children; 224644d93782SGreg Clayton bool m_might_have_children; 224744d93782SGreg Clayton bool m_is_expanded; 224844d93782SGreg Clayton }; 224944d93782SGreg Clayton 2250b9c1b51eSKate Stone class TreeWindowDelegate : public WindowDelegate { 225144d93782SGreg Clayton public: 2252b9c1b51eSKate Stone TreeWindowDelegate(Debugger &debugger, const TreeDelegateSP &delegate_sp) 2253b9c1b51eSKate Stone : m_debugger(debugger), m_delegate_sp(delegate_sp), 2254b9c1b51eSKate Stone m_root(nullptr, *delegate_sp, true), m_selected_item(nullptr), 2255b9c1b51eSKate Stone m_num_rows(0), m_selected_row_idx(0), m_first_visible_row(0), 2256b9c1b51eSKate Stone m_min_x(0), m_min_y(0), m_max_x(0), m_max_y(0) {} 225744d93782SGreg Clayton 2258b9c1b51eSKate Stone int NumVisibleRows() const { return m_max_y - m_min_y; } 225944d93782SGreg Clayton 2260b9c1b51eSKate Stone bool WindowDelegateDraw(Window &window, bool force) override { 2261b9c1b51eSKate Stone ExecutionContext exe_ctx( 2262b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext()); 226344d93782SGreg Clayton Process *process = exe_ctx.GetProcessPtr(); 226444d93782SGreg Clayton 226544d93782SGreg Clayton bool display_content = false; 2266b9c1b51eSKate Stone if (process) { 226744d93782SGreg Clayton StateType state = process->GetState(); 2268b9c1b51eSKate Stone if (StateIsStoppedState(state, true)) { 226944d93782SGreg Clayton // We are stopped, so it is ok to 227044d93782SGreg Clayton display_content = true; 2271b9c1b51eSKate Stone } else if (StateIsRunningState(state)) { 227244d93782SGreg Clayton return true; // Don't do any updating when we are running 227344d93782SGreg Clayton } 227444d93782SGreg Clayton } 227544d93782SGreg Clayton 227644d93782SGreg Clayton m_min_x = 2; 227744d93782SGreg Clayton m_min_y = 1; 227844d93782SGreg Clayton m_max_x = window.GetWidth() - 1; 227944d93782SGreg Clayton m_max_y = window.GetHeight() - 1; 228044d93782SGreg Clayton 228144d93782SGreg Clayton window.Erase(); 228244d93782SGreg Clayton window.DrawTitleBox(window.GetName()); 228344d93782SGreg Clayton 2284b9c1b51eSKate Stone if (display_content) { 228544d93782SGreg Clayton const int num_visible_rows = NumVisibleRows(); 228644d93782SGreg Clayton m_num_rows = 0; 228744d93782SGreg Clayton m_root.CalculateRowIndexes(m_num_rows); 228844d93782SGreg Clayton 228944d93782SGreg Clayton // If we unexpanded while having something selected our 229044d93782SGreg Clayton // total number of rows is less than the num visible rows, 229144d93782SGreg Clayton // then make sure we show all the rows by setting the first 229244d93782SGreg Clayton // visible row accordingly. 229344d93782SGreg Clayton if (m_first_visible_row > 0 && m_num_rows < num_visible_rows) 229444d93782SGreg Clayton m_first_visible_row = 0; 229544d93782SGreg Clayton 229644d93782SGreg Clayton // Make sure the selected row is always visible 229744d93782SGreg Clayton if (m_selected_row_idx < m_first_visible_row) 229844d93782SGreg Clayton m_first_visible_row = m_selected_row_idx; 229944d93782SGreg Clayton else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx) 230044d93782SGreg Clayton m_first_visible_row = m_selected_row_idx - num_visible_rows + 1; 230144d93782SGreg Clayton 230244d93782SGreg Clayton int row_idx = 0; 230344d93782SGreg Clayton int num_rows_left = num_visible_rows; 2304b9c1b51eSKate Stone m_root.Draw(window, m_first_visible_row, m_selected_row_idx, row_idx, 2305b9c1b51eSKate Stone num_rows_left); 230644d93782SGreg Clayton // Get the selected row 230744d93782SGreg Clayton m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); 2308b9c1b51eSKate Stone } else { 2309c5dac77aSEugene Zelenko m_selected_item = nullptr; 231044d93782SGreg Clayton } 231144d93782SGreg Clayton 231244d93782SGreg Clayton window.DeferredRefresh(); 231344d93782SGreg Clayton 231444d93782SGreg Clayton return true; // Drawing handled 231544d93782SGreg Clayton } 231644d93782SGreg Clayton 2317b9c1b51eSKate Stone const char *WindowDelegateGetHelpText() override { 231844d93782SGreg Clayton return "Thread window keyboard shortcuts:"; 231944d93782SGreg Clayton } 232044d93782SGreg Clayton 2321b9c1b51eSKate Stone KeyHelp *WindowDelegateGetKeyHelp() override { 232244d93782SGreg Clayton static curses::KeyHelp g_source_view_key_help[] = { 232344d93782SGreg Clayton {KEY_UP, "Select previous item"}, 232444d93782SGreg Clayton {KEY_DOWN, "Select next item"}, 232544d93782SGreg Clayton {KEY_RIGHT, "Expand the selected item"}, 2326b9c1b51eSKate Stone {KEY_LEFT, 2327b9c1b51eSKate Stone "Unexpand the selected item or select parent if not expanded"}, 232844d93782SGreg Clayton {KEY_PPAGE, "Page up"}, 232944d93782SGreg Clayton {KEY_NPAGE, "Page down"}, 233044d93782SGreg Clayton {'h', "Show help dialog"}, 233144d93782SGreg Clayton {' ', "Toggle item expansion"}, 233244d93782SGreg Clayton {',', "Page up"}, 233344d93782SGreg Clayton {'.', "Page down"}, 2334b9c1b51eSKate Stone {'\0', nullptr}}; 233544d93782SGreg Clayton return g_source_view_key_help; 233644d93782SGreg Clayton } 233744d93782SGreg Clayton 2338b9c1b51eSKate Stone HandleCharResult WindowDelegateHandleChar(Window &window, int c) override { 2339b9c1b51eSKate Stone switch (c) { 234044d93782SGreg Clayton case ',': 234144d93782SGreg Clayton case KEY_PPAGE: 234244d93782SGreg Clayton // Page up key 2343b9c1b51eSKate Stone if (m_first_visible_row > 0) { 234444d93782SGreg Clayton if (m_first_visible_row > m_max_y) 234544d93782SGreg Clayton m_first_visible_row -= m_max_y; 234644d93782SGreg Clayton else 234744d93782SGreg Clayton m_first_visible_row = 0; 234844d93782SGreg Clayton m_selected_row_idx = m_first_visible_row; 234944d93782SGreg Clayton m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); 235044d93782SGreg Clayton if (m_selected_item) 235144d93782SGreg Clayton m_selected_item->ItemWasSelected(); 235244d93782SGreg Clayton } 235344d93782SGreg Clayton return eKeyHandled; 235444d93782SGreg Clayton 235544d93782SGreg Clayton case '.': 235644d93782SGreg Clayton case KEY_NPAGE: 235744d93782SGreg Clayton // Page down key 2358b9c1b51eSKate Stone if (m_num_rows > m_max_y) { 2359b9c1b51eSKate Stone if (m_first_visible_row + m_max_y < m_num_rows) { 236044d93782SGreg Clayton m_first_visible_row += m_max_y; 236144d93782SGreg Clayton m_selected_row_idx = m_first_visible_row; 236244d93782SGreg Clayton m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); 236344d93782SGreg Clayton if (m_selected_item) 236444d93782SGreg Clayton m_selected_item->ItemWasSelected(); 236544d93782SGreg Clayton } 236644d93782SGreg Clayton } 236744d93782SGreg Clayton return eKeyHandled; 236844d93782SGreg Clayton 236944d93782SGreg Clayton case KEY_UP: 2370b9c1b51eSKate Stone if (m_selected_row_idx > 0) { 237144d93782SGreg Clayton --m_selected_row_idx; 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 return eKeyHandled; 2377315b6884SEugene Zelenko 237844d93782SGreg Clayton case KEY_DOWN: 2379b9c1b51eSKate Stone if (m_selected_row_idx + 1 < m_num_rows) { 238044d93782SGreg Clayton ++m_selected_row_idx; 238144d93782SGreg Clayton m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); 238244d93782SGreg Clayton if (m_selected_item) 238344d93782SGreg Clayton m_selected_item->ItemWasSelected(); 238444d93782SGreg Clayton } 238544d93782SGreg Clayton return eKeyHandled; 238644d93782SGreg Clayton 238744d93782SGreg Clayton case KEY_RIGHT: 2388b9c1b51eSKate Stone if (m_selected_item) { 238944d93782SGreg Clayton if (!m_selected_item->IsExpanded()) 239044d93782SGreg Clayton m_selected_item->Expand(); 239144d93782SGreg Clayton } 239244d93782SGreg Clayton return eKeyHandled; 239344d93782SGreg Clayton 239444d93782SGreg Clayton case KEY_LEFT: 2395b9c1b51eSKate Stone if (m_selected_item) { 239644d93782SGreg Clayton if (m_selected_item->IsExpanded()) 239744d93782SGreg Clayton m_selected_item->Unexpand(); 2398b9c1b51eSKate Stone else if (m_selected_item->GetParent()) { 239944d93782SGreg Clayton m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex(); 240044d93782SGreg Clayton m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); 240144d93782SGreg Clayton if (m_selected_item) 240244d93782SGreg Clayton m_selected_item->ItemWasSelected(); 240344d93782SGreg Clayton } 240444d93782SGreg Clayton } 240544d93782SGreg Clayton return eKeyHandled; 240644d93782SGreg Clayton 240744d93782SGreg Clayton case ' ': 240844d93782SGreg Clayton // Toggle expansion state when SPACE is pressed 2409b9c1b51eSKate Stone if (m_selected_item) { 241044d93782SGreg Clayton if (m_selected_item->IsExpanded()) 241144d93782SGreg Clayton m_selected_item->Unexpand(); 241244d93782SGreg Clayton else 241344d93782SGreg Clayton m_selected_item->Expand(); 241444d93782SGreg Clayton } 241544d93782SGreg Clayton return eKeyHandled; 241644d93782SGreg Clayton 241744d93782SGreg Clayton case 'h': 241844d93782SGreg Clayton window.CreateHelpSubwindow(); 241944d93782SGreg Clayton return eKeyHandled; 242044d93782SGreg Clayton 242144d93782SGreg Clayton default: 242244d93782SGreg Clayton break; 242344d93782SGreg Clayton } 242444d93782SGreg Clayton return eKeyNotHandled; 242544d93782SGreg Clayton } 242644d93782SGreg Clayton 242744d93782SGreg Clayton protected: 242844d93782SGreg Clayton Debugger &m_debugger; 242944d93782SGreg Clayton TreeDelegateSP m_delegate_sp; 243044d93782SGreg Clayton TreeItem m_root; 243144d93782SGreg Clayton TreeItem *m_selected_item; 243244d93782SGreg Clayton int m_num_rows; 243344d93782SGreg Clayton int m_selected_row_idx; 243444d93782SGreg Clayton int m_first_visible_row; 243544d93782SGreg Clayton int m_min_x; 243644d93782SGreg Clayton int m_min_y; 243744d93782SGreg Clayton int m_max_x; 243844d93782SGreg Clayton int m_max_y; 243944d93782SGreg Clayton }; 244044d93782SGreg Clayton 2441b9c1b51eSKate Stone class FrameTreeDelegate : public TreeDelegate { 244244d93782SGreg Clayton public: 2443b9c1b51eSKate Stone FrameTreeDelegate() : TreeDelegate() { 2444b9c1b51eSKate Stone FormatEntity::Parse( 2445b9c1b51eSKate Stone "frame #${frame.index}: {${function.name}${function.pc-offset}}}", 2446554f68d3SGreg Clayton m_format); 244744d93782SGreg Clayton } 244844d93782SGreg Clayton 2449315b6884SEugene Zelenko ~FrameTreeDelegate() override = default; 245044d93782SGreg Clayton 2451b9c1b51eSKate Stone void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override { 2452ec990867SGreg Clayton Thread *thread = (Thread *)item.GetUserData(); 2453b9c1b51eSKate Stone if (thread) { 245444d93782SGreg Clayton const uint64_t frame_idx = item.GetIdentifier(); 2455ec990867SGreg Clayton StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_idx); 2456b9c1b51eSKate Stone if (frame_sp) { 245744d93782SGreg Clayton StreamString strm; 2458b9c1b51eSKate Stone const SymbolContext &sc = 2459b9c1b51eSKate Stone frame_sp->GetSymbolContext(eSymbolContextEverything); 246044d93782SGreg Clayton ExecutionContext exe_ctx(frame_sp); 2461b9c1b51eSKate Stone if (FormatEntity::Format(m_format, strm, &sc, &exe_ctx, nullptr, 2462b9c1b51eSKate Stone nullptr, false, false)) { 246344d93782SGreg Clayton int right_pad = 1; 2464c156427dSZachary Turner window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad); 246544d93782SGreg Clayton } 246644d93782SGreg Clayton } 246744d93782SGreg Clayton } 246844d93782SGreg Clayton } 2469315b6884SEugene Zelenko 2470b9c1b51eSKate Stone void TreeDelegateGenerateChildren(TreeItem &item) override { 247144d93782SGreg Clayton // No children for frames yet... 247244d93782SGreg Clayton } 247344d93782SGreg Clayton 2474b9c1b51eSKate Stone bool TreeDelegateItemSelected(TreeItem &item) override { 2475ec990867SGreg Clayton Thread *thread = (Thread *)item.GetUserData(); 2476b9c1b51eSKate Stone if (thread) { 2477b9c1b51eSKate Stone thread->GetProcess()->GetThreadList().SetSelectedThreadByID( 2478b9c1b51eSKate Stone thread->GetID()); 247944d93782SGreg Clayton const uint64_t frame_idx = item.GetIdentifier(); 2480ec990867SGreg Clayton thread->SetSelectedFrameByIndex(frame_idx); 248144d93782SGreg Clayton return true; 248244d93782SGreg Clayton } 248344d93782SGreg Clayton return false; 248444d93782SGreg Clayton } 2485315b6884SEugene Zelenko 2486554f68d3SGreg Clayton protected: 2487554f68d3SGreg Clayton FormatEntity::Entry m_format; 248844d93782SGreg Clayton }; 248944d93782SGreg Clayton 2490b9c1b51eSKate Stone class ThreadTreeDelegate : public TreeDelegate { 249144d93782SGreg Clayton public: 2492b9c1b51eSKate Stone ThreadTreeDelegate(Debugger &debugger) 2493b9c1b51eSKate Stone : TreeDelegate(), m_debugger(debugger), m_tid(LLDB_INVALID_THREAD_ID), 2494b9c1b51eSKate Stone m_stop_id(UINT32_MAX) { 2495b9c1b51eSKate Stone FormatEntity::Parse("thread #${thread.index}: tid = ${thread.id}{, stop " 2496b9c1b51eSKate Stone "reason = ${thread.stop-reason}}", 2497554f68d3SGreg Clayton m_format); 249844d93782SGreg Clayton } 249944d93782SGreg Clayton 2500315b6884SEugene Zelenko ~ThreadTreeDelegate() override = default; 250144d93782SGreg Clayton 2502b9c1b51eSKate Stone ProcessSP GetProcess() { 2503b9c1b51eSKate Stone return m_debugger.GetCommandInterpreter() 2504b9c1b51eSKate Stone .GetExecutionContext() 2505b9c1b51eSKate Stone .GetProcessSP(); 2506ec990867SGreg Clayton } 2507ec990867SGreg Clayton 2508b9c1b51eSKate Stone ThreadSP GetThread(const TreeItem &item) { 2509ec990867SGreg Clayton ProcessSP process_sp = GetProcess(); 2510ec990867SGreg Clayton if (process_sp) 2511ec990867SGreg Clayton return process_sp->GetThreadList().FindThreadByID(item.GetIdentifier()); 2512ec990867SGreg Clayton return ThreadSP(); 2513ec990867SGreg Clayton } 2514ec990867SGreg Clayton 2515b9c1b51eSKate Stone void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override { 2516ec990867SGreg Clayton ThreadSP thread_sp = GetThread(item); 2517b9c1b51eSKate Stone if (thread_sp) { 251844d93782SGreg Clayton StreamString strm; 251944d93782SGreg Clayton ExecutionContext exe_ctx(thread_sp); 2520b9c1b51eSKate Stone if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr, 2521b9c1b51eSKate Stone nullptr, false, false)) { 252244d93782SGreg Clayton int right_pad = 1; 2523c156427dSZachary Turner window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad); 252444d93782SGreg Clayton } 252544d93782SGreg Clayton } 252644d93782SGreg Clayton } 2527315b6884SEugene Zelenko 2528b9c1b51eSKate Stone void TreeDelegateGenerateChildren(TreeItem &item) override { 2529ec990867SGreg Clayton ProcessSP process_sp = GetProcess(); 2530b9c1b51eSKate Stone if (process_sp && process_sp->IsAlive()) { 253144d93782SGreg Clayton StateType state = process_sp->GetState(); 2532b9c1b51eSKate Stone if (StateIsStoppedState(state, true)) { 2533ec990867SGreg Clayton ThreadSP thread_sp = GetThread(item); 2534b9c1b51eSKate Stone if (thread_sp) { 2535b9c1b51eSKate Stone if (m_stop_id == process_sp->GetStopID() && 2536b9c1b51eSKate Stone thread_sp->GetID() == m_tid) 253744d93782SGreg Clayton return; // Children are already up to date 2538b9c1b51eSKate Stone if (!m_frame_delegate_sp) { 253944d93782SGreg Clayton // Always expand the thread item the first time we show it 2540ec990867SGreg Clayton m_frame_delegate_sp.reset(new FrameTreeDelegate()); 254144d93782SGreg Clayton } 254244d93782SGreg Clayton 254344d93782SGreg Clayton m_stop_id = process_sp->GetStopID(); 254444d93782SGreg Clayton m_tid = thread_sp->GetID(); 254544d93782SGreg Clayton 254644d93782SGreg Clayton TreeItem t(&item, *m_frame_delegate_sp, false); 254744d93782SGreg Clayton size_t num_frames = thread_sp->GetStackFrameCount(); 254844d93782SGreg Clayton item.Resize(num_frames, t); 2549b9c1b51eSKate Stone for (size_t i = 0; i < num_frames; ++i) { 2550ec990867SGreg Clayton item[i].SetUserData(thread_sp.get()); 255144d93782SGreg Clayton item[i].SetIdentifier(i); 255244d93782SGreg Clayton } 255344d93782SGreg Clayton } 255444d93782SGreg Clayton return; 255544d93782SGreg Clayton } 255644d93782SGreg Clayton } 255744d93782SGreg Clayton item.ClearChildren(); 255844d93782SGreg Clayton } 255944d93782SGreg Clayton 2560b9c1b51eSKate Stone bool TreeDelegateItemSelected(TreeItem &item) override { 2561ec990867SGreg Clayton ProcessSP process_sp = GetProcess(); 2562b9c1b51eSKate Stone if (process_sp && process_sp->IsAlive()) { 2563ec990867SGreg Clayton StateType state = process_sp->GetState(); 2564b9c1b51eSKate Stone if (StateIsStoppedState(state, true)) { 2565ec990867SGreg Clayton ThreadSP thread_sp = GetThread(item); 2566b9c1b51eSKate Stone if (thread_sp) { 256744d93782SGreg Clayton ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList(); 2568bb19a13cSSaleem Abdulrasool std::lock_guard<std::recursive_mutex> guard(thread_list.GetMutex()); 256944d93782SGreg Clayton ThreadSP selected_thread_sp = thread_list.GetSelectedThread(); 2570b9c1b51eSKate Stone if (selected_thread_sp->GetID() != thread_sp->GetID()) { 257144d93782SGreg Clayton thread_list.SetSelectedThreadByID(thread_sp->GetID()); 257244d93782SGreg Clayton return true; 257344d93782SGreg Clayton } 257444d93782SGreg Clayton } 2575ec990867SGreg Clayton } 2576ec990867SGreg Clayton } 257744d93782SGreg Clayton return false; 257844d93782SGreg Clayton } 257944d93782SGreg Clayton 258044d93782SGreg Clayton protected: 258144d93782SGreg Clayton Debugger &m_debugger; 258244d93782SGreg Clayton std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp; 258344d93782SGreg Clayton lldb::user_id_t m_tid; 258444d93782SGreg Clayton uint32_t m_stop_id; 2585554f68d3SGreg Clayton FormatEntity::Entry m_format; 258644d93782SGreg Clayton }; 258744d93782SGreg Clayton 2588b9c1b51eSKate Stone class ThreadsTreeDelegate : public TreeDelegate { 2589ec990867SGreg Clayton public: 2590b9c1b51eSKate Stone ThreadsTreeDelegate(Debugger &debugger) 2591b9c1b51eSKate Stone : TreeDelegate(), m_thread_delegate_sp(), m_debugger(debugger), 2592b9c1b51eSKate Stone m_stop_id(UINT32_MAX) { 2593554f68d3SGreg Clayton FormatEntity::Parse("process ${process.id}{, name = ${process.name}}", 2594554f68d3SGreg Clayton m_format); 2595ec990867SGreg Clayton } 2596ec990867SGreg Clayton 2597315b6884SEugene Zelenko ~ThreadsTreeDelegate() override = default; 2598ec990867SGreg Clayton 2599b9c1b51eSKate Stone ProcessSP GetProcess() { 2600b9c1b51eSKate Stone return m_debugger.GetCommandInterpreter() 2601b9c1b51eSKate Stone .GetExecutionContext() 2602b9c1b51eSKate Stone .GetProcessSP(); 2603ec990867SGreg Clayton } 2604ec990867SGreg Clayton 2605b9c1b51eSKate Stone void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override { 2606ec990867SGreg Clayton ProcessSP process_sp = GetProcess(); 2607b9c1b51eSKate Stone if (process_sp && process_sp->IsAlive()) { 2608ec990867SGreg Clayton StreamString strm; 2609ec990867SGreg Clayton ExecutionContext exe_ctx(process_sp); 2610b9c1b51eSKate Stone if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr, 2611b9c1b51eSKate Stone nullptr, false, false)) { 2612ec990867SGreg Clayton int right_pad = 1; 2613c156427dSZachary Turner window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad); 2614ec990867SGreg Clayton } 2615ec990867SGreg Clayton } 2616ec990867SGreg Clayton } 2617ec990867SGreg Clayton 2618b9c1b51eSKate Stone void TreeDelegateGenerateChildren(TreeItem &item) override { 2619ec990867SGreg Clayton ProcessSP process_sp = GetProcess(); 2620b9c1b51eSKate Stone if (process_sp && process_sp->IsAlive()) { 2621ec990867SGreg Clayton StateType state = process_sp->GetState(); 2622b9c1b51eSKate Stone if (StateIsStoppedState(state, true)) { 2623ec990867SGreg Clayton const uint32_t stop_id = process_sp->GetStopID(); 2624ec990867SGreg Clayton if (m_stop_id == stop_id) 2625ec990867SGreg Clayton return; // Children are already up to date 2626ec990867SGreg Clayton 2627ec990867SGreg Clayton m_stop_id = stop_id; 2628ec990867SGreg Clayton 2629b9c1b51eSKate Stone if (!m_thread_delegate_sp) { 2630ec990867SGreg Clayton // Always expand the thread item the first time we show it 2631ec990867SGreg Clayton // item.Expand(); 2632ec990867SGreg Clayton m_thread_delegate_sp.reset(new ThreadTreeDelegate(m_debugger)); 2633ec990867SGreg Clayton } 2634ec990867SGreg Clayton 2635ec990867SGreg Clayton TreeItem t(&item, *m_thread_delegate_sp, false); 2636ec990867SGreg Clayton ThreadList &threads = process_sp->GetThreadList(); 2637bb19a13cSSaleem Abdulrasool std::lock_guard<std::recursive_mutex> guard(threads.GetMutex()); 2638ec990867SGreg Clayton size_t num_threads = threads.GetSize(); 2639ec990867SGreg Clayton item.Resize(num_threads, t); 2640b9c1b51eSKate Stone for (size_t i = 0; i < num_threads; ++i) { 2641ec990867SGreg Clayton item[i].SetIdentifier(threads.GetThreadAtIndex(i)->GetID()); 2642ec990867SGreg Clayton item[i].SetMightHaveChildren(true); 2643ec990867SGreg Clayton } 2644ec990867SGreg Clayton return; 2645ec990867SGreg Clayton } 2646ec990867SGreg Clayton } 2647ec990867SGreg Clayton item.ClearChildren(); 2648ec990867SGreg Clayton } 2649ec990867SGreg Clayton 2650b9c1b51eSKate Stone bool TreeDelegateItemSelected(TreeItem &item) override { return false; } 2651ec990867SGreg Clayton 2652ec990867SGreg Clayton protected: 2653ec990867SGreg Clayton std::shared_ptr<ThreadTreeDelegate> m_thread_delegate_sp; 2654ec990867SGreg Clayton Debugger &m_debugger; 2655ec990867SGreg Clayton uint32_t m_stop_id; 2656554f68d3SGreg Clayton FormatEntity::Entry m_format; 2657ec990867SGreg Clayton }; 2658ec990867SGreg Clayton 2659b9c1b51eSKate Stone class ValueObjectListDelegate : public WindowDelegate { 266044d93782SGreg Clayton public: 2661b9c1b51eSKate Stone ValueObjectListDelegate() 26628369b28dSGreg Clayton : m_rows(), m_selected_row(nullptr), 2663b9c1b51eSKate Stone m_selected_row_idx(0), m_first_visible_row(0), m_num_rows(0), 2664b9c1b51eSKate Stone m_max_x(0), m_max_y(0) {} 266544d93782SGreg Clayton 2666b9c1b51eSKate Stone ValueObjectListDelegate(ValueObjectList &valobj_list) 26678369b28dSGreg Clayton : m_rows(), m_selected_row(nullptr), 2668b9c1b51eSKate Stone m_selected_row_idx(0), m_first_visible_row(0), m_num_rows(0), 2669b9c1b51eSKate Stone m_max_x(0), m_max_y(0) { 267044d93782SGreg Clayton SetValues(valobj_list); 267144d93782SGreg Clayton } 267244d93782SGreg Clayton 2673315b6884SEugene Zelenko ~ValueObjectListDelegate() override = default; 267444d93782SGreg Clayton 2675b9c1b51eSKate Stone void SetValues(ValueObjectList &valobj_list) { 2676c5dac77aSEugene Zelenko m_selected_row = nullptr; 267744d93782SGreg Clayton m_selected_row_idx = 0; 267844d93782SGreg Clayton m_first_visible_row = 0; 267944d93782SGreg Clayton m_num_rows = 0; 268044d93782SGreg Clayton m_rows.clear(); 26818369b28dSGreg Clayton for (auto &valobj_sp : valobj_list.GetObjects()) 26828369b28dSGreg Clayton m_rows.push_back(Row(valobj_sp, nullptr)); 268344d93782SGreg Clayton } 268444d93782SGreg Clayton 2685b9c1b51eSKate Stone bool WindowDelegateDraw(Window &window, bool force) override { 268644d93782SGreg Clayton m_num_rows = 0; 268744d93782SGreg Clayton m_min_x = 2; 268844d93782SGreg Clayton m_min_y = 1; 268944d93782SGreg Clayton m_max_x = window.GetWidth() - 1; 269044d93782SGreg Clayton m_max_y = window.GetHeight() - 1; 269144d93782SGreg Clayton 269244d93782SGreg Clayton window.Erase(); 269344d93782SGreg Clayton window.DrawTitleBox(window.GetName()); 269444d93782SGreg Clayton 269544d93782SGreg Clayton const int num_visible_rows = NumVisibleRows(); 269644d93782SGreg Clayton const int num_rows = CalculateTotalNumberRows(m_rows); 269744d93782SGreg Clayton 269844d93782SGreg Clayton // If we unexpanded while having something selected our 269944d93782SGreg Clayton // total number of rows is less than the num visible rows, 270044d93782SGreg Clayton // then make sure we show all the rows by setting the first 270144d93782SGreg Clayton // visible row accordingly. 270244d93782SGreg Clayton if (m_first_visible_row > 0 && num_rows < num_visible_rows) 270344d93782SGreg Clayton m_first_visible_row = 0; 270444d93782SGreg Clayton 270544d93782SGreg Clayton // Make sure the selected row is always visible 270644d93782SGreg Clayton if (m_selected_row_idx < m_first_visible_row) 270744d93782SGreg Clayton m_first_visible_row = m_selected_row_idx; 270844d93782SGreg Clayton else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx) 270944d93782SGreg Clayton m_first_visible_row = m_selected_row_idx - num_visible_rows + 1; 271044d93782SGreg Clayton 271144d93782SGreg Clayton DisplayRows(window, m_rows, g_options); 271244d93782SGreg Clayton 271344d93782SGreg Clayton window.DeferredRefresh(); 271444d93782SGreg Clayton 271544d93782SGreg Clayton // Get the selected row 271644d93782SGreg Clayton m_selected_row = GetRowForRowIndex(m_selected_row_idx); 271744d93782SGreg Clayton // Keep the cursor on the selected row so the highlight and the cursor 271844d93782SGreg Clayton // are always on the same line 271944d93782SGreg Clayton if (m_selected_row) 2720b9c1b51eSKate Stone window.MoveCursor(m_selected_row->x, m_selected_row->y); 272144d93782SGreg Clayton 272244d93782SGreg Clayton return true; // Drawing handled 272344d93782SGreg Clayton } 272444d93782SGreg Clayton 2725b9c1b51eSKate Stone KeyHelp *WindowDelegateGetKeyHelp() override { 272644d93782SGreg Clayton static curses::KeyHelp g_source_view_key_help[] = { 272744d93782SGreg Clayton {KEY_UP, "Select previous item"}, 272844d93782SGreg Clayton {KEY_DOWN, "Select next item"}, 272944d93782SGreg Clayton {KEY_RIGHT, "Expand selected item"}, 273044d93782SGreg Clayton {KEY_LEFT, "Unexpand selected item or select parent if not expanded"}, 273144d93782SGreg Clayton {KEY_PPAGE, "Page up"}, 273244d93782SGreg Clayton {KEY_NPAGE, "Page down"}, 273344d93782SGreg Clayton {'A', "Format as annotated address"}, 273444d93782SGreg Clayton {'b', "Format as binary"}, 273544d93782SGreg Clayton {'B', "Format as hex bytes with ASCII"}, 273644d93782SGreg Clayton {'c', "Format as character"}, 273744d93782SGreg Clayton {'d', "Format as a signed integer"}, 273844d93782SGreg Clayton {'D', "Format selected value using the default format for the type"}, 273944d93782SGreg Clayton {'f', "Format as float"}, 274044d93782SGreg Clayton {'h', "Show help dialog"}, 274144d93782SGreg Clayton {'i', "Format as instructions"}, 274244d93782SGreg Clayton {'o', "Format as octal"}, 274344d93782SGreg Clayton {'p', "Format as pointer"}, 274444d93782SGreg Clayton {'s', "Format as C string"}, 274544d93782SGreg Clayton {'t', "Toggle showing/hiding type names"}, 274644d93782SGreg Clayton {'u', "Format as an unsigned integer"}, 274744d93782SGreg Clayton {'x', "Format as hex"}, 274844d93782SGreg Clayton {'X', "Format as uppercase hex"}, 274944d93782SGreg Clayton {' ', "Toggle item expansion"}, 275044d93782SGreg Clayton {',', "Page up"}, 275144d93782SGreg Clayton {'.', "Page down"}, 2752b9c1b51eSKate Stone {'\0', nullptr}}; 275344d93782SGreg Clayton return g_source_view_key_help; 275444d93782SGreg Clayton } 275544d93782SGreg Clayton 2756b9c1b51eSKate Stone HandleCharResult WindowDelegateHandleChar(Window &window, int c) override { 2757b9c1b51eSKate Stone switch (c) { 275844d93782SGreg Clayton case 'x': 275944d93782SGreg Clayton case 'X': 276044d93782SGreg Clayton case 'o': 276144d93782SGreg Clayton case 's': 276244d93782SGreg Clayton case 'u': 276344d93782SGreg Clayton case 'd': 276444d93782SGreg Clayton case 'D': 276544d93782SGreg Clayton case 'i': 276644d93782SGreg Clayton case 'A': 276744d93782SGreg Clayton case 'p': 276844d93782SGreg Clayton case 'c': 276944d93782SGreg Clayton case 'b': 277044d93782SGreg Clayton case 'B': 277144d93782SGreg Clayton case 'f': 277244d93782SGreg Clayton // Change the format for the currently selected item 27738369b28dSGreg Clayton if (m_selected_row) { 27748369b28dSGreg Clayton auto valobj_sp = m_selected_row->value.GetSP(); 27758369b28dSGreg Clayton if (valobj_sp) 27768369b28dSGreg Clayton valobj_sp->SetFormat(FormatForChar(c)); 27778369b28dSGreg Clayton } 277844d93782SGreg Clayton return eKeyHandled; 277944d93782SGreg Clayton 278044d93782SGreg Clayton case 't': 278144d93782SGreg Clayton // Toggle showing type names 278244d93782SGreg Clayton g_options.show_types = !g_options.show_types; 278344d93782SGreg Clayton return eKeyHandled; 278444d93782SGreg Clayton 278544d93782SGreg Clayton case ',': 278644d93782SGreg Clayton case KEY_PPAGE: 278744d93782SGreg Clayton // Page up key 2788b9c1b51eSKate Stone if (m_first_visible_row > 0) { 27893985c8c6SSaleem Abdulrasool if (static_cast<int>(m_first_visible_row) > m_max_y) 279044d93782SGreg Clayton m_first_visible_row -= m_max_y; 279144d93782SGreg Clayton else 279244d93782SGreg Clayton m_first_visible_row = 0; 279344d93782SGreg Clayton m_selected_row_idx = m_first_visible_row; 279444d93782SGreg Clayton } 279544d93782SGreg Clayton return eKeyHandled; 279644d93782SGreg Clayton 279744d93782SGreg Clayton case '.': 279844d93782SGreg Clayton case KEY_NPAGE: 279944d93782SGreg Clayton // Page down key 2800b9c1b51eSKate Stone if (m_num_rows > static_cast<size_t>(m_max_y)) { 2801b9c1b51eSKate Stone if (m_first_visible_row + m_max_y < m_num_rows) { 280244d93782SGreg Clayton m_first_visible_row += m_max_y; 280344d93782SGreg Clayton m_selected_row_idx = m_first_visible_row; 280444d93782SGreg Clayton } 280544d93782SGreg Clayton } 280644d93782SGreg Clayton return eKeyHandled; 280744d93782SGreg Clayton 280844d93782SGreg Clayton case KEY_UP: 280944d93782SGreg Clayton if (m_selected_row_idx > 0) 281044d93782SGreg Clayton --m_selected_row_idx; 281144d93782SGreg Clayton return eKeyHandled; 2812315b6884SEugene Zelenko 281344d93782SGreg Clayton case KEY_DOWN: 281444d93782SGreg Clayton if (m_selected_row_idx + 1 < m_num_rows) 281544d93782SGreg Clayton ++m_selected_row_idx; 281644d93782SGreg Clayton return eKeyHandled; 281744d93782SGreg Clayton 281844d93782SGreg Clayton case KEY_RIGHT: 2819b9c1b51eSKate Stone if (m_selected_row) { 282044d93782SGreg Clayton if (!m_selected_row->expanded) 282144d93782SGreg Clayton m_selected_row->Expand(); 282244d93782SGreg Clayton } 282344d93782SGreg Clayton return eKeyHandled; 282444d93782SGreg Clayton 282544d93782SGreg Clayton case KEY_LEFT: 2826b9c1b51eSKate Stone if (m_selected_row) { 282744d93782SGreg Clayton if (m_selected_row->expanded) 282844d93782SGreg Clayton m_selected_row->Unexpand(); 282944d93782SGreg Clayton else if (m_selected_row->parent) 283044d93782SGreg Clayton m_selected_row_idx = m_selected_row->parent->row_idx; 283144d93782SGreg Clayton } 283244d93782SGreg Clayton return eKeyHandled; 283344d93782SGreg Clayton 283444d93782SGreg Clayton case ' ': 283544d93782SGreg Clayton // Toggle expansion state when SPACE is pressed 2836b9c1b51eSKate Stone if (m_selected_row) { 283744d93782SGreg Clayton if (m_selected_row->expanded) 283844d93782SGreg Clayton m_selected_row->Unexpand(); 283944d93782SGreg Clayton else 284044d93782SGreg Clayton m_selected_row->Expand(); 284144d93782SGreg Clayton } 284244d93782SGreg Clayton return eKeyHandled; 284344d93782SGreg Clayton 284444d93782SGreg Clayton case 'h': 284544d93782SGreg Clayton window.CreateHelpSubwindow(); 284644d93782SGreg Clayton return eKeyHandled; 284744d93782SGreg Clayton 284844d93782SGreg Clayton default: 284944d93782SGreg Clayton break; 285044d93782SGreg Clayton } 285144d93782SGreg Clayton return eKeyNotHandled; 285244d93782SGreg Clayton } 285344d93782SGreg Clayton 285444d93782SGreg Clayton protected: 285544d93782SGreg Clayton std::vector<Row> m_rows; 285644d93782SGreg Clayton Row *m_selected_row; 285744d93782SGreg Clayton uint32_t m_selected_row_idx; 285844d93782SGreg Clayton uint32_t m_first_visible_row; 285944d93782SGreg Clayton uint32_t m_num_rows; 286044d93782SGreg Clayton int m_min_x; 286144d93782SGreg Clayton int m_min_y; 286244d93782SGreg Clayton int m_max_x; 286344d93782SGreg Clayton int m_max_y; 286444d93782SGreg Clayton 2865b9c1b51eSKate Stone static Format FormatForChar(int c) { 2866b9c1b51eSKate Stone switch (c) { 2867b9c1b51eSKate Stone case 'x': 2868b9c1b51eSKate Stone return eFormatHex; 2869b9c1b51eSKate Stone case 'X': 2870b9c1b51eSKate Stone return eFormatHexUppercase; 2871b9c1b51eSKate Stone case 'o': 2872b9c1b51eSKate Stone return eFormatOctal; 2873b9c1b51eSKate Stone case 's': 2874b9c1b51eSKate Stone return eFormatCString; 2875b9c1b51eSKate Stone case 'u': 2876b9c1b51eSKate Stone return eFormatUnsigned; 2877b9c1b51eSKate Stone case 'd': 2878b9c1b51eSKate Stone return eFormatDecimal; 2879b9c1b51eSKate Stone case 'D': 2880b9c1b51eSKate Stone return eFormatDefault; 2881b9c1b51eSKate Stone case 'i': 2882b9c1b51eSKate Stone return eFormatInstruction; 2883b9c1b51eSKate Stone case 'A': 2884b9c1b51eSKate Stone return eFormatAddressInfo; 2885b9c1b51eSKate Stone case 'p': 2886b9c1b51eSKate Stone return eFormatPointer; 2887b9c1b51eSKate Stone case 'c': 2888b9c1b51eSKate Stone return eFormatChar; 2889b9c1b51eSKate Stone case 'b': 2890b9c1b51eSKate Stone return eFormatBinary; 2891b9c1b51eSKate Stone case 'B': 2892b9c1b51eSKate Stone return eFormatBytesWithASCII; 2893b9c1b51eSKate Stone case 'f': 2894b9c1b51eSKate Stone return eFormatFloat; 289544d93782SGreg Clayton } 289644d93782SGreg Clayton return eFormatDefault; 289744d93782SGreg Clayton } 289844d93782SGreg Clayton 2899b9c1b51eSKate Stone bool DisplayRowObject(Window &window, Row &row, DisplayOptions &options, 2900b9c1b51eSKate Stone bool highlight, bool last_child) { 29018369b28dSGreg Clayton ValueObject *valobj = row.value.GetSP().get(); 290244d93782SGreg Clayton 2903c5dac77aSEugene Zelenko if (valobj == nullptr) 290444d93782SGreg Clayton return false; 290544d93782SGreg Clayton 2906b9c1b51eSKate Stone const char *type_name = 2907b9c1b51eSKate Stone options.show_types ? valobj->GetTypeName().GetCString() : nullptr; 290844d93782SGreg Clayton const char *name = valobj->GetName().GetCString(); 290944d93782SGreg Clayton const char *value = valobj->GetValueAsCString(); 291044d93782SGreg Clayton const char *summary = valobj->GetSummaryAsCString(); 291144d93782SGreg Clayton 291244d93782SGreg Clayton window.MoveCursor(row.x, row.y); 291344d93782SGreg Clayton 291444d93782SGreg Clayton row.DrawTree(window); 291544d93782SGreg Clayton 291644d93782SGreg Clayton if (highlight) 291744d93782SGreg Clayton window.AttributeOn(A_REVERSE); 291844d93782SGreg Clayton 291944d93782SGreg Clayton if (type_name && type_name[0]) 292044d93782SGreg Clayton window.Printf("(%s) ", type_name); 292144d93782SGreg Clayton 292244d93782SGreg Clayton if (name && name[0]) 292344d93782SGreg Clayton window.PutCString(name); 292444d93782SGreg Clayton 292544d93782SGreg Clayton attr_t changd_attr = 0; 292644d93782SGreg Clayton if (valobj->GetValueDidChange()) 292744d93782SGreg Clayton changd_attr = COLOR_PAIR(5) | A_BOLD; 292844d93782SGreg Clayton 2929b9c1b51eSKate Stone if (value && value[0]) { 293044d93782SGreg Clayton window.PutCString(" = "); 293144d93782SGreg Clayton if (changd_attr) 293244d93782SGreg Clayton window.AttributeOn(changd_attr); 293344d93782SGreg Clayton window.PutCString(value); 293444d93782SGreg Clayton if (changd_attr) 293544d93782SGreg Clayton window.AttributeOff(changd_attr); 293644d93782SGreg Clayton } 293744d93782SGreg Clayton 2938b9c1b51eSKate Stone if (summary && summary[0]) { 293944d93782SGreg Clayton window.PutChar(' '); 294044d93782SGreg Clayton if (changd_attr) 294144d93782SGreg Clayton window.AttributeOn(changd_attr); 294244d93782SGreg Clayton window.PutCString(summary); 294344d93782SGreg Clayton if (changd_attr) 294444d93782SGreg Clayton window.AttributeOff(changd_attr); 294544d93782SGreg Clayton } 294644d93782SGreg Clayton 294744d93782SGreg Clayton if (highlight) 294844d93782SGreg Clayton window.AttributeOff(A_REVERSE); 294944d93782SGreg Clayton 295044d93782SGreg Clayton return true; 295144d93782SGreg Clayton } 2952315b6884SEugene Zelenko 2953b9c1b51eSKate Stone void DisplayRows(Window &window, std::vector<Row> &rows, 2954b9c1b51eSKate Stone DisplayOptions &options) { 295544d93782SGreg Clayton // > 0x25B7 295644d93782SGreg Clayton // \/ 0x25BD 295744d93782SGreg Clayton 295844d93782SGreg Clayton bool window_is_active = window.IsActive(); 2959b9c1b51eSKate Stone for (auto &row : rows) { 296044d93782SGreg Clayton const bool last_child = row.parent && &rows[rows.size() - 1] == &row; 296144d93782SGreg Clayton // Save the row index in each Row structure 296244d93782SGreg Clayton row.row_idx = m_num_rows; 296344d93782SGreg Clayton if ((m_num_rows >= m_first_visible_row) && 2964b9c1b51eSKate Stone ((m_num_rows - m_first_visible_row) < 2965b9c1b51eSKate Stone static_cast<size_t>(NumVisibleRows()))) { 296644d93782SGreg Clayton row.x = m_min_x; 296744d93782SGreg Clayton row.y = m_num_rows - m_first_visible_row + 1; 2968b9c1b51eSKate Stone if (DisplayRowObject(window, row, options, 2969b9c1b51eSKate Stone window_is_active && 2970b9c1b51eSKate Stone m_num_rows == m_selected_row_idx, 2971b9c1b51eSKate Stone last_child)) { 297244d93782SGreg Clayton ++m_num_rows; 2973b9c1b51eSKate Stone } else { 297444d93782SGreg Clayton row.x = 0; 297544d93782SGreg Clayton row.y = 0; 297644d93782SGreg Clayton } 2977b9c1b51eSKate Stone } else { 297844d93782SGreg Clayton row.x = 0; 297944d93782SGreg Clayton row.y = 0; 298044d93782SGreg Clayton ++m_num_rows; 298144d93782SGreg Clayton } 298244d93782SGreg Clayton 29838369b28dSGreg Clayton auto &children = row.GetChildren(); 29848369b28dSGreg Clayton if (row.expanded && !children.empty()) { 29858369b28dSGreg Clayton DisplayRows(window, children, options); 298644d93782SGreg Clayton } 298744d93782SGreg Clayton } 298844d93782SGreg Clayton } 298944d93782SGreg Clayton 29908369b28dSGreg Clayton int CalculateTotalNumberRows(std::vector<Row> &rows) { 299144d93782SGreg Clayton int row_count = 0; 29928369b28dSGreg Clayton for (auto &row : rows) { 299344d93782SGreg Clayton ++row_count; 299444d93782SGreg Clayton if (row.expanded) 29958369b28dSGreg Clayton row_count += CalculateTotalNumberRows(row.GetChildren()); 299644d93782SGreg Clayton } 299744d93782SGreg Clayton return row_count; 299844d93782SGreg Clayton } 2999315b6884SEugene Zelenko 3000b9c1b51eSKate Stone static Row *GetRowForRowIndexImpl(std::vector<Row> &rows, size_t &row_index) { 3001b9c1b51eSKate Stone for (auto &row : rows) { 300244d93782SGreg Clayton if (row_index == 0) 300344d93782SGreg Clayton return &row; 3004b9c1b51eSKate Stone else { 300544d93782SGreg Clayton --row_index; 30068369b28dSGreg Clayton auto &children = row.GetChildren(); 30078369b28dSGreg Clayton if (row.expanded && !children.empty()) { 30088369b28dSGreg Clayton Row *result = GetRowForRowIndexImpl(children, row_index); 300944d93782SGreg Clayton if (result) 301044d93782SGreg Clayton return result; 301144d93782SGreg Clayton } 301244d93782SGreg Clayton } 301344d93782SGreg Clayton } 3014c5dac77aSEugene Zelenko return nullptr; 301544d93782SGreg Clayton } 301644d93782SGreg Clayton 3017b9c1b51eSKate Stone Row *GetRowForRowIndex(size_t row_index) { 301844d93782SGreg Clayton return GetRowForRowIndexImpl(m_rows, row_index); 301944d93782SGreg Clayton } 302044d93782SGreg Clayton 3021b9c1b51eSKate Stone int NumVisibleRows() const { return m_max_y - m_min_y; } 302244d93782SGreg Clayton 302344d93782SGreg Clayton static DisplayOptions g_options; 302444d93782SGreg Clayton }; 302544d93782SGreg Clayton 3026b9c1b51eSKate Stone class FrameVariablesWindowDelegate : public ValueObjectListDelegate { 302744d93782SGreg Clayton public: 3028b9c1b51eSKate Stone FrameVariablesWindowDelegate(Debugger &debugger) 3029b9c1b51eSKate Stone : ValueObjectListDelegate(), m_debugger(debugger), 3030b9c1b51eSKate Stone m_frame_block(nullptr) {} 303144d93782SGreg Clayton 3032315b6884SEugene Zelenko ~FrameVariablesWindowDelegate() override = default; 303344d93782SGreg Clayton 3034b9c1b51eSKate Stone const char *WindowDelegateGetHelpText() override { 303544d93782SGreg Clayton return "Frame variable window keyboard shortcuts:"; 303644d93782SGreg Clayton } 303744d93782SGreg Clayton 3038b9c1b51eSKate Stone bool WindowDelegateDraw(Window &window, bool force) override { 3039b9c1b51eSKate Stone ExecutionContext exe_ctx( 3040b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext()); 304144d93782SGreg Clayton Process *process = exe_ctx.GetProcessPtr(); 3042c5dac77aSEugene Zelenko Block *frame_block = nullptr; 3043c5dac77aSEugene Zelenko StackFrame *frame = nullptr; 304444d93782SGreg Clayton 3045b9c1b51eSKate Stone if (process) { 304644d93782SGreg Clayton StateType state = process->GetState(); 3047b9c1b51eSKate Stone if (StateIsStoppedState(state, true)) { 304844d93782SGreg Clayton frame = exe_ctx.GetFramePtr(); 304944d93782SGreg Clayton if (frame) 305044d93782SGreg Clayton frame_block = frame->GetFrameBlock(); 3051b9c1b51eSKate Stone } else if (StateIsRunningState(state)) { 305244d93782SGreg Clayton return true; // Don't do any updating when we are running 305344d93782SGreg Clayton } 305444d93782SGreg Clayton } 305544d93782SGreg Clayton 305644d93782SGreg Clayton ValueObjectList local_values; 3057b9c1b51eSKate Stone if (frame_block) { 305844d93782SGreg Clayton // Only update the variables if they have changed 3059b9c1b51eSKate Stone if (m_frame_block != frame_block) { 306044d93782SGreg Clayton m_frame_block = frame_block; 306144d93782SGreg Clayton 306244d93782SGreg Clayton VariableList *locals = frame->GetVariableList(true); 3063b9c1b51eSKate Stone if (locals) { 306444d93782SGreg Clayton const DynamicValueType use_dynamic = eDynamicDontRunTarget; 306544d93782SGreg Clayton const size_t num_locals = locals->GetSize(); 3066b9c1b51eSKate Stone for (size_t i = 0; i < num_locals; ++i) { 3067b9c1b51eSKate Stone ValueObjectSP value_sp = frame->GetValueObjectForFrameVariable( 3068b9c1b51eSKate Stone locals->GetVariableAtIndex(i), use_dynamic); 3069b9c1b51eSKate Stone if (value_sp) { 3070eb72dc7dSGreg Clayton ValueObjectSP synthetic_value_sp = value_sp->GetSyntheticValue(); 3071eb72dc7dSGreg Clayton if (synthetic_value_sp) 3072eb72dc7dSGreg Clayton local_values.Append(synthetic_value_sp); 3073eb72dc7dSGreg Clayton else 3074eb72dc7dSGreg Clayton local_values.Append(value_sp); 3075eb72dc7dSGreg Clayton } 3076eb72dc7dSGreg Clayton } 307744d93782SGreg Clayton // Update the values 307844d93782SGreg Clayton SetValues(local_values); 307944d93782SGreg Clayton } 308044d93782SGreg Clayton } 3081b9c1b51eSKate Stone } else { 3082c5dac77aSEugene Zelenko m_frame_block = nullptr; 308344d93782SGreg Clayton // Update the values with an empty list if there is no frame 308444d93782SGreg Clayton SetValues(local_values); 308544d93782SGreg Clayton } 308644d93782SGreg Clayton 308744d93782SGreg Clayton return ValueObjectListDelegate::WindowDelegateDraw(window, force); 308844d93782SGreg Clayton } 308944d93782SGreg Clayton 309044d93782SGreg Clayton protected: 309144d93782SGreg Clayton Debugger &m_debugger; 309244d93782SGreg Clayton Block *m_frame_block; 309344d93782SGreg Clayton }; 309444d93782SGreg Clayton 3095b9c1b51eSKate Stone class RegistersWindowDelegate : public ValueObjectListDelegate { 309644d93782SGreg Clayton public: 3097b9c1b51eSKate Stone RegistersWindowDelegate(Debugger &debugger) 3098b9c1b51eSKate Stone : ValueObjectListDelegate(), m_debugger(debugger) {} 309944d93782SGreg Clayton 3100315b6884SEugene Zelenko ~RegistersWindowDelegate() override = default; 310144d93782SGreg Clayton 3102b9c1b51eSKate Stone const char *WindowDelegateGetHelpText() override { 310344d93782SGreg Clayton return "Register window keyboard shortcuts:"; 310444d93782SGreg Clayton } 310544d93782SGreg Clayton 3106b9c1b51eSKate Stone bool WindowDelegateDraw(Window &window, bool force) override { 3107b9c1b51eSKate Stone ExecutionContext exe_ctx( 3108b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext()); 310944d93782SGreg Clayton StackFrame *frame = exe_ctx.GetFramePtr(); 311044d93782SGreg Clayton 311144d93782SGreg Clayton ValueObjectList value_list; 3112b9c1b51eSKate Stone if (frame) { 3113b9c1b51eSKate Stone if (frame->GetStackID() != m_stack_id) { 311444d93782SGreg Clayton m_stack_id = frame->GetStackID(); 311544d93782SGreg Clayton RegisterContextSP reg_ctx(frame->GetRegisterContext()); 3116b9c1b51eSKate Stone if (reg_ctx) { 311744d93782SGreg Clayton const uint32_t num_sets = reg_ctx->GetRegisterSetCount(); 3118b9c1b51eSKate Stone for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) { 3119b9c1b51eSKate Stone value_list.Append( 3120b9c1b51eSKate Stone ValueObjectRegisterSet::Create(frame, reg_ctx, set_idx)); 312144d93782SGreg Clayton } 312244d93782SGreg Clayton } 312344d93782SGreg Clayton SetValues(value_list); 312444d93782SGreg Clayton } 3125b9c1b51eSKate Stone } else { 312644d93782SGreg Clayton Process *process = exe_ctx.GetProcessPtr(); 312744d93782SGreg Clayton if (process && process->IsAlive()) 312844d93782SGreg Clayton return true; // Don't do any updating if we are running 3129b9c1b51eSKate Stone else { 313044d93782SGreg Clayton // Update the values with an empty list if there 313144d93782SGreg Clayton // is no process or the process isn't alive anymore 313244d93782SGreg Clayton SetValues(value_list); 313344d93782SGreg Clayton } 313444d93782SGreg Clayton } 313544d93782SGreg Clayton return ValueObjectListDelegate::WindowDelegateDraw(window, force); 313644d93782SGreg Clayton } 313744d93782SGreg Clayton 313844d93782SGreg Clayton protected: 313944d93782SGreg Clayton Debugger &m_debugger; 314044d93782SGreg Clayton StackID m_stack_id; 314144d93782SGreg Clayton }; 314244d93782SGreg Clayton 3143b9c1b51eSKate Stone static const char *CursesKeyToCString(int ch) { 314444d93782SGreg Clayton static char g_desc[32]; 3145b9c1b51eSKate Stone if (ch >= KEY_F0 && ch < KEY_F0 + 64) { 314644d93782SGreg Clayton snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0); 314744d93782SGreg Clayton return g_desc; 314844d93782SGreg Clayton } 3149b9c1b51eSKate Stone switch (ch) { 3150b9c1b51eSKate Stone case KEY_DOWN: 3151b9c1b51eSKate Stone return "down"; 3152b9c1b51eSKate Stone case KEY_UP: 3153b9c1b51eSKate Stone return "up"; 3154b9c1b51eSKate Stone case KEY_LEFT: 3155b9c1b51eSKate Stone return "left"; 3156b9c1b51eSKate Stone case KEY_RIGHT: 3157b9c1b51eSKate Stone return "right"; 3158b9c1b51eSKate Stone case KEY_HOME: 3159b9c1b51eSKate Stone return "home"; 3160b9c1b51eSKate Stone case KEY_BACKSPACE: 3161b9c1b51eSKate Stone return "backspace"; 3162b9c1b51eSKate Stone case KEY_DL: 3163b9c1b51eSKate Stone return "delete-line"; 3164b9c1b51eSKate Stone case KEY_IL: 3165b9c1b51eSKate Stone return "insert-line"; 3166b9c1b51eSKate Stone case KEY_DC: 3167b9c1b51eSKate Stone return "delete-char"; 3168b9c1b51eSKate Stone case KEY_IC: 3169b9c1b51eSKate Stone return "insert-char"; 3170b9c1b51eSKate Stone case KEY_CLEAR: 3171b9c1b51eSKate Stone return "clear"; 3172b9c1b51eSKate Stone case KEY_EOS: 3173b9c1b51eSKate Stone return "clear-to-eos"; 3174b9c1b51eSKate Stone case KEY_EOL: 3175b9c1b51eSKate Stone return "clear-to-eol"; 3176b9c1b51eSKate Stone case KEY_SF: 3177b9c1b51eSKate Stone return "scroll-forward"; 3178b9c1b51eSKate Stone case KEY_SR: 3179b9c1b51eSKate Stone return "scroll-backward"; 3180b9c1b51eSKate Stone case KEY_NPAGE: 3181b9c1b51eSKate Stone return "page-down"; 3182b9c1b51eSKate Stone case KEY_PPAGE: 3183b9c1b51eSKate Stone return "page-up"; 3184b9c1b51eSKate Stone case KEY_STAB: 3185b9c1b51eSKate Stone return "set-tab"; 3186b9c1b51eSKate Stone case KEY_CTAB: 3187b9c1b51eSKate Stone return "clear-tab"; 3188b9c1b51eSKate Stone case KEY_CATAB: 3189b9c1b51eSKate Stone return "clear-all-tabs"; 3190b9c1b51eSKate Stone case KEY_ENTER: 3191b9c1b51eSKate Stone return "enter"; 3192b9c1b51eSKate Stone case KEY_PRINT: 3193b9c1b51eSKate Stone return "print"; 3194b9c1b51eSKate Stone case KEY_LL: 3195b9c1b51eSKate Stone return "lower-left key"; 3196b9c1b51eSKate Stone case KEY_A1: 3197b9c1b51eSKate Stone return "upper left of keypad"; 3198b9c1b51eSKate Stone case KEY_A3: 3199b9c1b51eSKate Stone return "upper right of keypad"; 3200b9c1b51eSKate Stone case KEY_B2: 3201b9c1b51eSKate Stone return "center of keypad"; 3202b9c1b51eSKate Stone case KEY_C1: 3203b9c1b51eSKate Stone return "lower left of keypad"; 3204b9c1b51eSKate Stone case KEY_C3: 3205b9c1b51eSKate Stone return "lower right of keypad"; 3206b9c1b51eSKate Stone case KEY_BTAB: 3207b9c1b51eSKate Stone return "back-tab key"; 3208b9c1b51eSKate Stone case KEY_BEG: 3209b9c1b51eSKate Stone return "begin key"; 3210b9c1b51eSKate Stone case KEY_CANCEL: 3211b9c1b51eSKate Stone return "cancel key"; 3212b9c1b51eSKate Stone case KEY_CLOSE: 3213b9c1b51eSKate Stone return "close key"; 3214b9c1b51eSKate Stone case KEY_COMMAND: 3215b9c1b51eSKate Stone return "command key"; 3216b9c1b51eSKate Stone case KEY_COPY: 3217b9c1b51eSKate Stone return "copy key"; 3218b9c1b51eSKate Stone case KEY_CREATE: 3219b9c1b51eSKate Stone return "create key"; 3220b9c1b51eSKate Stone case KEY_END: 3221b9c1b51eSKate Stone return "end key"; 3222b9c1b51eSKate Stone case KEY_EXIT: 3223b9c1b51eSKate Stone return "exit key"; 3224b9c1b51eSKate Stone case KEY_FIND: 3225b9c1b51eSKate Stone return "find key"; 3226b9c1b51eSKate Stone case KEY_HELP: 3227b9c1b51eSKate Stone return "help key"; 3228b9c1b51eSKate Stone case KEY_MARK: 3229b9c1b51eSKate Stone return "mark key"; 3230b9c1b51eSKate Stone case KEY_MESSAGE: 3231b9c1b51eSKate Stone return "message key"; 3232b9c1b51eSKate Stone case KEY_MOVE: 3233b9c1b51eSKate Stone return "move key"; 3234b9c1b51eSKate Stone case KEY_NEXT: 3235b9c1b51eSKate Stone return "next key"; 3236b9c1b51eSKate Stone case KEY_OPEN: 3237b9c1b51eSKate Stone return "open key"; 3238b9c1b51eSKate Stone case KEY_OPTIONS: 3239b9c1b51eSKate Stone return "options key"; 3240b9c1b51eSKate Stone case KEY_PREVIOUS: 3241b9c1b51eSKate Stone return "previous key"; 3242b9c1b51eSKate Stone case KEY_REDO: 3243b9c1b51eSKate Stone return "redo key"; 3244b9c1b51eSKate Stone case KEY_REFERENCE: 3245b9c1b51eSKate Stone return "reference key"; 3246b9c1b51eSKate Stone case KEY_REFRESH: 3247b9c1b51eSKate Stone return "refresh key"; 3248b9c1b51eSKate Stone case KEY_REPLACE: 3249b9c1b51eSKate Stone return "replace key"; 3250b9c1b51eSKate Stone case KEY_RESTART: 3251b9c1b51eSKate Stone return "restart key"; 3252b9c1b51eSKate Stone case KEY_RESUME: 3253b9c1b51eSKate Stone return "resume key"; 3254b9c1b51eSKate Stone case KEY_SAVE: 3255b9c1b51eSKate Stone return "save key"; 3256b9c1b51eSKate Stone case KEY_SBEG: 3257b9c1b51eSKate Stone return "shifted begin key"; 3258b9c1b51eSKate Stone case KEY_SCANCEL: 3259b9c1b51eSKate Stone return "shifted cancel key"; 3260b9c1b51eSKate Stone case KEY_SCOMMAND: 3261b9c1b51eSKate Stone return "shifted command key"; 3262b9c1b51eSKate Stone case KEY_SCOPY: 3263b9c1b51eSKate Stone return "shifted copy key"; 3264b9c1b51eSKate Stone case KEY_SCREATE: 3265b9c1b51eSKate Stone return "shifted create key"; 3266b9c1b51eSKate Stone case KEY_SDC: 3267b9c1b51eSKate Stone return "shifted delete-character key"; 3268b9c1b51eSKate Stone case KEY_SDL: 3269b9c1b51eSKate Stone return "shifted delete-line key"; 3270b9c1b51eSKate Stone case KEY_SELECT: 3271b9c1b51eSKate Stone return "select key"; 3272b9c1b51eSKate Stone case KEY_SEND: 3273b9c1b51eSKate Stone return "shifted end key"; 3274b9c1b51eSKate Stone case KEY_SEOL: 3275b9c1b51eSKate Stone return "shifted clear-to-end-of-line key"; 3276b9c1b51eSKate Stone case KEY_SEXIT: 3277b9c1b51eSKate Stone return "shifted exit key"; 3278b9c1b51eSKate Stone case KEY_SFIND: 3279b9c1b51eSKate Stone return "shifted find key"; 3280b9c1b51eSKate Stone case KEY_SHELP: 3281b9c1b51eSKate Stone return "shifted help key"; 3282b9c1b51eSKate Stone case KEY_SHOME: 3283b9c1b51eSKate Stone return "shifted home key"; 3284b9c1b51eSKate Stone case KEY_SIC: 3285b9c1b51eSKate Stone return "shifted insert-character key"; 3286b9c1b51eSKate Stone case KEY_SLEFT: 3287b9c1b51eSKate Stone return "shifted left-arrow key"; 3288b9c1b51eSKate Stone case KEY_SMESSAGE: 3289b9c1b51eSKate Stone return "shifted message key"; 3290b9c1b51eSKate Stone case KEY_SMOVE: 3291b9c1b51eSKate Stone return "shifted move key"; 3292b9c1b51eSKate Stone case KEY_SNEXT: 3293b9c1b51eSKate Stone return "shifted next key"; 3294b9c1b51eSKate Stone case KEY_SOPTIONS: 3295b9c1b51eSKate Stone return "shifted options key"; 3296b9c1b51eSKate Stone case KEY_SPREVIOUS: 3297b9c1b51eSKate Stone return "shifted previous key"; 3298b9c1b51eSKate Stone case KEY_SPRINT: 3299b9c1b51eSKate Stone return "shifted print key"; 3300b9c1b51eSKate Stone case KEY_SREDO: 3301b9c1b51eSKate Stone return "shifted redo key"; 3302b9c1b51eSKate Stone case KEY_SREPLACE: 3303b9c1b51eSKate Stone return "shifted replace key"; 3304b9c1b51eSKate Stone case KEY_SRIGHT: 3305b9c1b51eSKate Stone return "shifted right-arrow key"; 3306b9c1b51eSKate Stone case KEY_SRSUME: 3307b9c1b51eSKate Stone return "shifted resume key"; 3308b9c1b51eSKate Stone case KEY_SSAVE: 3309b9c1b51eSKate Stone return "shifted save key"; 3310b9c1b51eSKate Stone case KEY_SSUSPEND: 3311b9c1b51eSKate Stone return "shifted suspend key"; 3312b9c1b51eSKate Stone case KEY_SUNDO: 3313b9c1b51eSKate Stone return "shifted undo key"; 3314b9c1b51eSKate Stone case KEY_SUSPEND: 3315b9c1b51eSKate Stone return "suspend key"; 3316b9c1b51eSKate Stone case KEY_UNDO: 3317b9c1b51eSKate Stone return "undo key"; 3318b9c1b51eSKate Stone case KEY_MOUSE: 3319b9c1b51eSKate Stone return "Mouse event has occurred"; 3320b9c1b51eSKate Stone case KEY_RESIZE: 3321b9c1b51eSKate Stone return "Terminal resize event"; 332227801f4fSBruce Mitchener #ifdef KEY_EVENT 3323b9c1b51eSKate Stone case KEY_EVENT: 3324b9c1b51eSKate Stone return "We were interrupted by an event"; 332527801f4fSBruce Mitchener #endif 3326b9c1b51eSKate Stone case KEY_RETURN: 3327b9c1b51eSKate Stone return "return"; 3328b9c1b51eSKate Stone case ' ': 3329b9c1b51eSKate Stone return "space"; 3330b9c1b51eSKate Stone case '\t': 3331b9c1b51eSKate Stone return "tab"; 3332b9c1b51eSKate Stone case KEY_ESCAPE: 3333b9c1b51eSKate Stone return "escape"; 333444d93782SGreg Clayton default: 333544d93782SGreg Clayton if (isprint(ch)) 333644d93782SGreg Clayton snprintf(g_desc, sizeof(g_desc), "%c", ch); 333744d93782SGreg Clayton else 333844d93782SGreg Clayton snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch); 333944d93782SGreg Clayton return g_desc; 334044d93782SGreg Clayton } 3341c5dac77aSEugene Zelenko return nullptr; 334244d93782SGreg Clayton } 334344d93782SGreg Clayton 3344b9c1b51eSKate Stone HelpDialogDelegate::HelpDialogDelegate(const char *text, 3345b9c1b51eSKate Stone KeyHelp *key_help_array) 3346b9c1b51eSKate Stone : m_text(), m_first_visible_line(0) { 3347b9c1b51eSKate Stone if (text && text[0]) { 334844d93782SGreg Clayton m_text.SplitIntoLines(text); 334944d93782SGreg Clayton m_text.AppendString(""); 335044d93782SGreg Clayton } 3351b9c1b51eSKate Stone if (key_help_array) { 3352b9c1b51eSKate Stone for (KeyHelp *key = key_help_array; key->ch; ++key) { 335344d93782SGreg Clayton StreamString key_description; 3354b9c1b51eSKate Stone key_description.Printf("%10s - %s", CursesKeyToCString(key->ch), 3355b9c1b51eSKate Stone key->description); 3356c156427dSZachary Turner m_text.AppendString(key_description.GetString()); 335744d93782SGreg Clayton } 335844d93782SGreg Clayton } 335944d93782SGreg Clayton } 336044d93782SGreg Clayton 3361315b6884SEugene Zelenko HelpDialogDelegate::~HelpDialogDelegate() = default; 336244d93782SGreg Clayton 3363b9c1b51eSKate Stone bool HelpDialogDelegate::WindowDelegateDraw(Window &window, bool force) { 336444d93782SGreg Clayton window.Erase(); 336544d93782SGreg Clayton const int window_height = window.GetHeight(); 336644d93782SGreg Clayton int x = 2; 336744d93782SGreg Clayton int y = 1; 336844d93782SGreg Clayton const int min_y = y; 336944d93782SGreg Clayton const int max_y = window_height - 1 - y; 33703985c8c6SSaleem Abdulrasool const size_t num_visible_lines = max_y - min_y + 1; 337144d93782SGreg Clayton const size_t num_lines = m_text.GetSize(); 337244d93782SGreg Clayton const char *bottom_message; 337344d93782SGreg Clayton if (num_lines <= num_visible_lines) 337444d93782SGreg Clayton bottom_message = "Press any key to exit"; 337544d93782SGreg Clayton else 337644d93782SGreg Clayton bottom_message = "Use arrows to scroll, any other key to exit"; 337744d93782SGreg Clayton window.DrawTitleBox(window.GetName(), bottom_message); 3378b9c1b51eSKate Stone while (y <= max_y) { 337944d93782SGreg Clayton window.MoveCursor(x, y); 3380b9c1b51eSKate Stone window.PutCStringTruncated( 3381b9c1b51eSKate Stone m_text.GetStringAtIndex(m_first_visible_line + y - min_y), 1); 338244d93782SGreg Clayton ++y; 338344d93782SGreg Clayton } 338444d93782SGreg Clayton return true; 338544d93782SGreg Clayton } 338644d93782SGreg Clayton 3387b9c1b51eSKate Stone HandleCharResult HelpDialogDelegate::WindowDelegateHandleChar(Window &window, 3388b9c1b51eSKate Stone int key) { 338944d93782SGreg Clayton bool done = false; 339044d93782SGreg Clayton const size_t num_lines = m_text.GetSize(); 339144d93782SGreg Clayton const size_t num_visible_lines = window.GetHeight() - 2; 339244d93782SGreg Clayton 3393b9c1b51eSKate Stone if (num_lines <= num_visible_lines) { 339444d93782SGreg Clayton done = true; 339544d93782SGreg Clayton // If we have all lines visible and don't need scrolling, then any 339644d93782SGreg Clayton // key press will cause us to exit 3397b9c1b51eSKate Stone } else { 3398b9c1b51eSKate Stone switch (key) { 339944d93782SGreg Clayton case KEY_UP: 340044d93782SGreg Clayton if (m_first_visible_line > 0) 340144d93782SGreg Clayton --m_first_visible_line; 340244d93782SGreg Clayton break; 340344d93782SGreg Clayton 340444d93782SGreg Clayton case KEY_DOWN: 340544d93782SGreg Clayton if (m_first_visible_line + num_visible_lines < num_lines) 340644d93782SGreg Clayton ++m_first_visible_line; 340744d93782SGreg Clayton break; 340844d93782SGreg Clayton 340944d93782SGreg Clayton case KEY_PPAGE: 341044d93782SGreg Clayton case ',': 3411b9c1b51eSKate Stone if (m_first_visible_line > 0) { 34123985c8c6SSaleem Abdulrasool if (static_cast<size_t>(m_first_visible_line) >= num_visible_lines) 341344d93782SGreg Clayton m_first_visible_line -= num_visible_lines; 341444d93782SGreg Clayton else 341544d93782SGreg Clayton m_first_visible_line = 0; 341644d93782SGreg Clayton } 341744d93782SGreg Clayton break; 3418315b6884SEugene Zelenko 341944d93782SGreg Clayton case KEY_NPAGE: 342044d93782SGreg Clayton case '.': 3421b9c1b51eSKate Stone if (m_first_visible_line + num_visible_lines < num_lines) { 342244d93782SGreg Clayton m_first_visible_line += num_visible_lines; 34233985c8c6SSaleem Abdulrasool if (static_cast<size_t>(m_first_visible_line) > num_lines) 342444d93782SGreg Clayton m_first_visible_line = num_lines - num_visible_lines; 342544d93782SGreg Clayton } 342644d93782SGreg Clayton break; 3427315b6884SEugene Zelenko 342844d93782SGreg Clayton default: 342944d93782SGreg Clayton done = true; 343044d93782SGreg Clayton break; 343144d93782SGreg Clayton } 343244d93782SGreg Clayton } 343344d93782SGreg Clayton if (done) 343444d93782SGreg Clayton window.GetParent()->RemoveSubWindow(&window); 343544d93782SGreg Clayton return eKeyHandled; 343644d93782SGreg Clayton } 343744d93782SGreg Clayton 3438b9c1b51eSKate Stone class ApplicationDelegate : public WindowDelegate, public MenuDelegate { 343944d93782SGreg Clayton public: 344044d93782SGreg Clayton enum { 344144d93782SGreg Clayton eMenuID_LLDB = 1, 344244d93782SGreg Clayton eMenuID_LLDBAbout, 344344d93782SGreg Clayton eMenuID_LLDBExit, 344444d93782SGreg Clayton 344544d93782SGreg Clayton eMenuID_Target, 344644d93782SGreg Clayton eMenuID_TargetCreate, 344744d93782SGreg Clayton eMenuID_TargetDelete, 344844d93782SGreg Clayton 344944d93782SGreg Clayton eMenuID_Process, 345044d93782SGreg Clayton eMenuID_ProcessAttach, 345144d93782SGreg Clayton eMenuID_ProcessDetach, 345244d93782SGreg Clayton eMenuID_ProcessLaunch, 345344d93782SGreg Clayton eMenuID_ProcessContinue, 345444d93782SGreg Clayton eMenuID_ProcessHalt, 345544d93782SGreg Clayton eMenuID_ProcessKill, 345644d93782SGreg Clayton 345744d93782SGreg Clayton eMenuID_Thread, 345844d93782SGreg Clayton eMenuID_ThreadStepIn, 345944d93782SGreg Clayton eMenuID_ThreadStepOver, 346044d93782SGreg Clayton eMenuID_ThreadStepOut, 346144d93782SGreg Clayton 346244d93782SGreg Clayton eMenuID_View, 346344d93782SGreg Clayton eMenuID_ViewBacktrace, 346444d93782SGreg Clayton eMenuID_ViewRegisters, 346544d93782SGreg Clayton eMenuID_ViewSource, 346644d93782SGreg Clayton eMenuID_ViewVariables, 346744d93782SGreg Clayton 346844d93782SGreg Clayton eMenuID_Help, 346944d93782SGreg Clayton eMenuID_HelpGUIHelp 347044d93782SGreg Clayton }; 347144d93782SGreg Clayton 3472b9c1b51eSKate Stone ApplicationDelegate(Application &app, Debugger &debugger) 3473b9c1b51eSKate Stone : WindowDelegate(), MenuDelegate(), m_app(app), m_debugger(debugger) {} 347444d93782SGreg Clayton 3475315b6884SEugene Zelenko ~ApplicationDelegate() override = default; 3476bd5ae6b4SGreg Clayton 3477b9c1b51eSKate Stone bool WindowDelegateDraw(Window &window, bool force) override { 347844d93782SGreg Clayton return false; // Drawing not handled, let standard window drawing happen 347944d93782SGreg Clayton } 348044d93782SGreg Clayton 3481b9c1b51eSKate Stone HandleCharResult WindowDelegateHandleChar(Window &window, int key) override { 3482b9c1b51eSKate Stone switch (key) { 34835fdb09bbSGreg Clayton case '\t': 348444d93782SGreg Clayton window.SelectNextWindowAsActive(); 348544d93782SGreg Clayton return eKeyHandled; 34865fdb09bbSGreg Clayton 34875fdb09bbSGreg Clayton case 'h': 34885fdb09bbSGreg Clayton window.CreateHelpSubwindow(); 34895fdb09bbSGreg Clayton return eKeyHandled; 34905fdb09bbSGreg Clayton 34915fdb09bbSGreg Clayton case KEY_ESCAPE: 34925fdb09bbSGreg Clayton return eQuitApplication; 34935fdb09bbSGreg Clayton 34945fdb09bbSGreg Clayton default: 34955fdb09bbSGreg Clayton break; 349644d93782SGreg Clayton } 349744d93782SGreg Clayton return eKeyNotHandled; 349844d93782SGreg Clayton } 349944d93782SGreg Clayton 3500b9c1b51eSKate Stone const char *WindowDelegateGetHelpText() override { 35015fdb09bbSGreg Clayton return "Welcome to the LLDB curses GUI.\n\n" 35025fdb09bbSGreg Clayton "Press the TAB key to change the selected view.\n" 3503b9c1b51eSKate Stone "Each view has its own keyboard shortcuts, press 'h' to open a " 3504b9c1b51eSKate Stone "dialog to display them.\n\n" 35055fdb09bbSGreg Clayton "Common key bindings for all views:"; 35065fdb09bbSGreg Clayton } 35075fdb09bbSGreg Clayton 3508b9c1b51eSKate Stone KeyHelp *WindowDelegateGetKeyHelp() override { 35095fdb09bbSGreg Clayton static curses::KeyHelp g_source_view_key_help[] = { 35105fdb09bbSGreg Clayton {'\t', "Select next view"}, 35115fdb09bbSGreg Clayton {'h', "Show help dialog with view specific key bindings"}, 35125fdb09bbSGreg Clayton {',', "Page up"}, 35135fdb09bbSGreg Clayton {'.', "Page down"}, 35145fdb09bbSGreg Clayton {KEY_UP, "Select previous"}, 35155fdb09bbSGreg Clayton {KEY_DOWN, "Select next"}, 35165fdb09bbSGreg Clayton {KEY_LEFT, "Unexpand or select parent"}, 35175fdb09bbSGreg Clayton {KEY_RIGHT, "Expand"}, 35185fdb09bbSGreg Clayton {KEY_PPAGE, "Page up"}, 35195fdb09bbSGreg Clayton {KEY_NPAGE, "Page down"}, 3520b9c1b51eSKate Stone {'\0', nullptr}}; 35215fdb09bbSGreg Clayton return g_source_view_key_help; 35225fdb09bbSGreg Clayton } 35235fdb09bbSGreg Clayton 3524b9c1b51eSKate Stone MenuActionResult MenuDelegateAction(Menu &menu) override { 3525b9c1b51eSKate Stone switch (menu.GetIdentifier()) { 3526b9c1b51eSKate Stone case eMenuID_ThreadStepIn: { 3527b9c1b51eSKate Stone ExecutionContext exe_ctx = 3528b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 3529b9c1b51eSKate Stone if (exe_ctx.HasThreadScope()) { 353044d93782SGreg Clayton Process *process = exe_ctx.GetProcessPtr(); 3531b9c1b51eSKate Stone if (process && process->IsAlive() && 3532b9c1b51eSKate Stone StateIsStoppedState(process->GetState(), true)) 35334b4b2478SJim Ingham exe_ctx.GetThreadRef().StepIn(true); 353444d93782SGreg Clayton } 353544d93782SGreg Clayton } 353644d93782SGreg Clayton return MenuActionResult::Handled; 353744d93782SGreg Clayton 3538b9c1b51eSKate Stone case eMenuID_ThreadStepOut: { 3539b9c1b51eSKate Stone ExecutionContext exe_ctx = 3540b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 3541b9c1b51eSKate Stone if (exe_ctx.HasThreadScope()) { 354244d93782SGreg Clayton Process *process = exe_ctx.GetProcessPtr(); 3543b9c1b51eSKate Stone if (process && process->IsAlive() && 3544b9c1b51eSKate Stone StateIsStoppedState(process->GetState(), true)) 354544d93782SGreg Clayton exe_ctx.GetThreadRef().StepOut(); 354644d93782SGreg Clayton } 354744d93782SGreg Clayton } 354844d93782SGreg Clayton return MenuActionResult::Handled; 354944d93782SGreg Clayton 3550b9c1b51eSKate Stone case eMenuID_ThreadStepOver: { 3551b9c1b51eSKate Stone ExecutionContext exe_ctx = 3552b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 3553b9c1b51eSKate Stone if (exe_ctx.HasThreadScope()) { 355444d93782SGreg Clayton Process *process = exe_ctx.GetProcessPtr(); 3555b9c1b51eSKate Stone if (process && process->IsAlive() && 3556b9c1b51eSKate Stone StateIsStoppedState(process->GetState(), true)) 355744d93782SGreg Clayton exe_ctx.GetThreadRef().StepOver(true); 355844d93782SGreg Clayton } 355944d93782SGreg Clayton } 356044d93782SGreg Clayton return MenuActionResult::Handled; 356144d93782SGreg Clayton 3562b9c1b51eSKate Stone case eMenuID_ProcessContinue: { 3563b9c1b51eSKate Stone ExecutionContext exe_ctx = 3564b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 3565b9c1b51eSKate Stone if (exe_ctx.HasProcessScope()) { 356644d93782SGreg Clayton Process *process = exe_ctx.GetProcessPtr(); 3567b9c1b51eSKate Stone if (process && process->IsAlive() && 3568b9c1b51eSKate Stone StateIsStoppedState(process->GetState(), true)) 356944d93782SGreg Clayton process->Resume(); 357044d93782SGreg Clayton } 357144d93782SGreg Clayton } 357244d93782SGreg Clayton return MenuActionResult::Handled; 357344d93782SGreg Clayton 3574b9c1b51eSKate Stone case eMenuID_ProcessKill: { 3575b9c1b51eSKate Stone ExecutionContext exe_ctx = 3576b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 3577b9c1b51eSKate Stone if (exe_ctx.HasProcessScope()) { 357844d93782SGreg Clayton Process *process = exe_ctx.GetProcessPtr(); 357944d93782SGreg Clayton if (process && process->IsAlive()) 3580ede3193bSJason Molenda process->Destroy(false); 358144d93782SGreg Clayton } 358244d93782SGreg Clayton } 358344d93782SGreg Clayton return MenuActionResult::Handled; 358444d93782SGreg Clayton 3585b9c1b51eSKate Stone case eMenuID_ProcessHalt: { 3586b9c1b51eSKate Stone ExecutionContext exe_ctx = 3587b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 3588b9c1b51eSKate Stone if (exe_ctx.HasProcessScope()) { 358944d93782SGreg Clayton Process *process = exe_ctx.GetProcessPtr(); 359044d93782SGreg Clayton if (process && process->IsAlive()) 359144d93782SGreg Clayton process->Halt(); 359244d93782SGreg Clayton } 359344d93782SGreg Clayton } 359444d93782SGreg Clayton return MenuActionResult::Handled; 359544d93782SGreg Clayton 3596b9c1b51eSKate Stone case eMenuID_ProcessDetach: { 3597b9c1b51eSKate Stone ExecutionContext exe_ctx = 3598b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 3599b9c1b51eSKate Stone if (exe_ctx.HasProcessScope()) { 360044d93782SGreg Clayton Process *process = exe_ctx.GetProcessPtr(); 360144d93782SGreg Clayton if (process && process->IsAlive()) 360244d93782SGreg Clayton process->Detach(false); 360344d93782SGreg Clayton } 360444d93782SGreg Clayton } 360544d93782SGreg Clayton return MenuActionResult::Handled; 360644d93782SGreg Clayton 3607b9c1b51eSKate Stone case eMenuID_Process: { 3608b9c1b51eSKate Stone // Populate the menu with all of the threads if the process is stopped 3609b9c1b51eSKate Stone // when 361044d93782SGreg Clayton // the Process menu gets selected and is about to display its submenu. 361144d93782SGreg Clayton Menus &submenus = menu.GetSubmenus(); 3612b9c1b51eSKate Stone ExecutionContext exe_ctx = 3613b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 361444d93782SGreg Clayton Process *process = exe_ctx.GetProcessPtr(); 3615b9c1b51eSKate Stone if (process && process->IsAlive() && 3616b9c1b51eSKate Stone StateIsStoppedState(process->GetState(), true)) { 361744d93782SGreg Clayton if (submenus.size() == 7) 361844d93782SGreg Clayton menu.AddSubmenu(MenuSP(new Menu(Menu::Type::Separator))); 361944d93782SGreg Clayton else if (submenus.size() > 8) 362044d93782SGreg Clayton submenus.erase(submenus.begin() + 8, submenus.end()); 362144d93782SGreg Clayton 362244d93782SGreg Clayton ThreadList &threads = process->GetThreadList(); 3623bb19a13cSSaleem Abdulrasool std::lock_guard<std::recursive_mutex> guard(threads.GetMutex()); 362444d93782SGreg Clayton size_t num_threads = threads.GetSize(); 3625b9c1b51eSKate Stone for (size_t i = 0; i < num_threads; ++i) { 362644d93782SGreg Clayton ThreadSP thread_sp = threads.GetThreadAtIndex(i); 362744d93782SGreg Clayton char menu_char = '\0'; 362844d93782SGreg Clayton if (i < 9) 362944d93782SGreg Clayton menu_char = '1' + i; 363044d93782SGreg Clayton StreamString thread_menu_title; 363144d93782SGreg Clayton thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID()); 363244d93782SGreg Clayton const char *thread_name = thread_sp->GetName(); 363344d93782SGreg Clayton if (thread_name && thread_name[0]) 363444d93782SGreg Clayton thread_menu_title.Printf(" %s", thread_name); 3635b9c1b51eSKate Stone else { 363644d93782SGreg Clayton const char *queue_name = thread_sp->GetQueueName(); 363744d93782SGreg Clayton if (queue_name && queue_name[0]) 363844d93782SGreg Clayton thread_menu_title.Printf(" %s", queue_name); 363944d93782SGreg Clayton } 3640b9c1b51eSKate Stone menu.AddSubmenu( 3641c156427dSZachary Turner MenuSP(new Menu(thread_menu_title.GetString().str().c_str(), 3642c156427dSZachary Turner nullptr, menu_char, thread_sp->GetID()))); 364344d93782SGreg Clayton } 3644b9c1b51eSKate Stone } else if (submenus.size() > 7) { 364544d93782SGreg Clayton // Remove the separator and any other thread submenu items 364644d93782SGreg Clayton // that were previously added 364744d93782SGreg Clayton submenus.erase(submenus.begin() + 7, submenus.end()); 364844d93782SGreg Clayton } 3649b9c1b51eSKate Stone // Since we are adding and removing items we need to recalculate the name 3650b9c1b51eSKate Stone // lengths 365144d93782SGreg Clayton menu.RecalculateNameLengths(); 365244d93782SGreg Clayton } 365344d93782SGreg Clayton return MenuActionResult::Handled; 365444d93782SGreg Clayton 3655b9c1b51eSKate Stone case eMenuID_ViewVariables: { 365644d93782SGreg Clayton WindowSP main_window_sp = m_app.GetMainWindow(); 365744d93782SGreg Clayton WindowSP source_window_sp = main_window_sp->FindSubWindow("Source"); 365844d93782SGreg Clayton WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables"); 365944d93782SGreg Clayton WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers"); 366044d93782SGreg Clayton const Rect source_bounds = source_window_sp->GetBounds(); 366144d93782SGreg Clayton 3662b9c1b51eSKate Stone if (variables_window_sp) { 366344d93782SGreg Clayton const Rect variables_bounds = variables_window_sp->GetBounds(); 366444d93782SGreg Clayton 366544d93782SGreg Clayton main_window_sp->RemoveSubWindow(variables_window_sp.get()); 366644d93782SGreg Clayton 3667b9c1b51eSKate Stone if (registers_window_sp) { 3668b9c1b51eSKate Stone // We have a registers window, so give all the area back to the 3669b9c1b51eSKate Stone // registers window 367044d93782SGreg Clayton Rect registers_bounds = variables_bounds; 367144d93782SGreg Clayton registers_bounds.size.width = source_bounds.size.width; 367244d93782SGreg Clayton registers_window_sp->SetBounds(registers_bounds); 3673b9c1b51eSKate Stone } else { 367444d93782SGreg Clayton // We have no registers window showing so give the bottom 367544d93782SGreg Clayton // area back to the source view 367644d93782SGreg Clayton source_window_sp->Resize(source_bounds.size.width, 3677b9c1b51eSKate Stone source_bounds.size.height + 3678b9c1b51eSKate Stone variables_bounds.size.height); 367944d93782SGreg Clayton } 3680b9c1b51eSKate Stone } else { 368144d93782SGreg Clayton Rect new_variables_rect; 3682b9c1b51eSKate Stone if (registers_window_sp) { 368344d93782SGreg Clayton // We have a registers window so split the area of the registers 368444d93782SGreg Clayton // window into two columns where the left hand side will be the 368544d93782SGreg Clayton // variables and the right hand side will be the registers 368644d93782SGreg Clayton const Rect variables_bounds = registers_window_sp->GetBounds(); 368744d93782SGreg Clayton Rect new_registers_rect; 3688b9c1b51eSKate Stone variables_bounds.VerticalSplitPercentage(0.50, new_variables_rect, 3689b9c1b51eSKate Stone new_registers_rect); 369044d93782SGreg Clayton registers_window_sp->SetBounds(new_registers_rect); 3691b9c1b51eSKate Stone } else { 369244d93782SGreg Clayton // No variables window, grab the bottom part of the source window 369344d93782SGreg Clayton Rect new_source_rect; 3694b9c1b51eSKate Stone source_bounds.HorizontalSplitPercentage(0.70, new_source_rect, 3695b9c1b51eSKate Stone new_variables_rect); 369644d93782SGreg Clayton source_window_sp->SetBounds(new_source_rect); 369744d93782SGreg Clayton } 3698b9c1b51eSKate Stone WindowSP new_window_sp = main_window_sp->CreateSubWindow( 3699b9c1b51eSKate Stone "Variables", new_variables_rect, false); 3700b9c1b51eSKate Stone new_window_sp->SetDelegate( 3701b9c1b51eSKate Stone WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger))); 370244d93782SGreg Clayton } 370344d93782SGreg Clayton touchwin(stdscr); 370444d93782SGreg Clayton } 370544d93782SGreg Clayton return MenuActionResult::Handled; 370644d93782SGreg Clayton 3707b9c1b51eSKate Stone case eMenuID_ViewRegisters: { 370844d93782SGreg Clayton WindowSP main_window_sp = m_app.GetMainWindow(); 370944d93782SGreg Clayton WindowSP source_window_sp = main_window_sp->FindSubWindow("Source"); 371044d93782SGreg Clayton WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables"); 371144d93782SGreg Clayton WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers"); 371244d93782SGreg Clayton const Rect source_bounds = source_window_sp->GetBounds(); 371344d93782SGreg Clayton 3714b9c1b51eSKate Stone if (registers_window_sp) { 3715b9c1b51eSKate Stone if (variables_window_sp) { 371644d93782SGreg Clayton const Rect variables_bounds = variables_window_sp->GetBounds(); 371744d93782SGreg Clayton 3718b9c1b51eSKate Stone // We have a variables window, so give all the area back to the 3719b9c1b51eSKate Stone // variables window 3720b9c1b51eSKate Stone variables_window_sp->Resize(variables_bounds.size.width + 3721b9c1b51eSKate Stone registers_window_sp->GetWidth(), 372244d93782SGreg Clayton variables_bounds.size.height); 3723b9c1b51eSKate Stone } else { 372444d93782SGreg Clayton // We have no variables window showing so give the bottom 372544d93782SGreg Clayton // area back to the source view 372644d93782SGreg Clayton source_window_sp->Resize(source_bounds.size.width, 3727b9c1b51eSKate Stone source_bounds.size.height + 3728b9c1b51eSKate Stone registers_window_sp->GetHeight()); 372944d93782SGreg Clayton } 373044d93782SGreg Clayton main_window_sp->RemoveSubWindow(registers_window_sp.get()); 3731b9c1b51eSKate Stone } else { 373244d93782SGreg Clayton Rect new_regs_rect; 3733b9c1b51eSKate Stone if (variables_window_sp) { 373444d93782SGreg Clayton // We have a variables window, split it into two columns 373544d93782SGreg Clayton // where the left hand side will be the variables and the 373644d93782SGreg Clayton // right hand side will be the registers 373744d93782SGreg Clayton const Rect variables_bounds = variables_window_sp->GetBounds(); 373844d93782SGreg Clayton Rect new_vars_rect; 3739b9c1b51eSKate Stone variables_bounds.VerticalSplitPercentage(0.50, new_vars_rect, 3740b9c1b51eSKate Stone new_regs_rect); 374144d93782SGreg Clayton variables_window_sp->SetBounds(new_vars_rect); 3742b9c1b51eSKate Stone } else { 374344d93782SGreg Clayton // No registers window, grab the bottom part of the source window 374444d93782SGreg Clayton Rect new_source_rect; 3745b9c1b51eSKate Stone source_bounds.HorizontalSplitPercentage(0.70, new_source_rect, 3746b9c1b51eSKate Stone new_regs_rect); 374744d93782SGreg Clayton source_window_sp->SetBounds(new_source_rect); 374844d93782SGreg Clayton } 3749b9c1b51eSKate Stone WindowSP new_window_sp = 3750b9c1b51eSKate Stone main_window_sp->CreateSubWindow("Registers", new_regs_rect, false); 3751b9c1b51eSKate Stone new_window_sp->SetDelegate( 3752b9c1b51eSKate Stone WindowDelegateSP(new RegistersWindowDelegate(m_debugger))); 375344d93782SGreg Clayton } 375444d93782SGreg Clayton touchwin(stdscr); 375544d93782SGreg Clayton } 375644d93782SGreg Clayton return MenuActionResult::Handled; 375744d93782SGreg Clayton 375844d93782SGreg Clayton case eMenuID_HelpGUIHelp: 37595fdb09bbSGreg Clayton m_app.GetMainWindow()->CreateHelpSubwindow(); 376044d93782SGreg Clayton return MenuActionResult::Handled; 376144d93782SGreg Clayton 376244d93782SGreg Clayton default: 376344d93782SGreg Clayton break; 376444d93782SGreg Clayton } 376544d93782SGreg Clayton 376644d93782SGreg Clayton return MenuActionResult::NotHandled; 376744d93782SGreg Clayton } 3768b9c1b51eSKate Stone 376944d93782SGreg Clayton protected: 377044d93782SGreg Clayton Application &m_app; 377144d93782SGreg Clayton Debugger &m_debugger; 377244d93782SGreg Clayton }; 377344d93782SGreg Clayton 3774b9c1b51eSKate Stone class StatusBarWindowDelegate : public WindowDelegate { 377544d93782SGreg Clayton public: 3776b9c1b51eSKate Stone StatusBarWindowDelegate(Debugger &debugger) : m_debugger(debugger) { 3777b9c1b51eSKate Stone FormatEntity::Parse("Thread: ${thread.id%tid}", m_format); 377844d93782SGreg Clayton } 377944d93782SGreg Clayton 3780315b6884SEugene Zelenko ~StatusBarWindowDelegate() override = default; 3781bd5ae6b4SGreg Clayton 3782b9c1b51eSKate Stone bool WindowDelegateDraw(Window &window, bool force) override { 3783b9c1b51eSKate Stone ExecutionContext exe_ctx = 3784b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 378544d93782SGreg Clayton Process *process = exe_ctx.GetProcessPtr(); 378644d93782SGreg Clayton Thread *thread = exe_ctx.GetThreadPtr(); 378744d93782SGreg Clayton StackFrame *frame = exe_ctx.GetFramePtr(); 378844d93782SGreg Clayton window.Erase(); 378944d93782SGreg Clayton window.SetBackground(2); 379044d93782SGreg Clayton window.MoveCursor(0, 0); 3791b9c1b51eSKate Stone if (process) { 379244d93782SGreg Clayton const StateType state = process->GetState(); 3793b9c1b51eSKate Stone window.Printf("Process: %5" PRIu64 " %10s", process->GetID(), 3794b9c1b51eSKate Stone StateAsCString(state)); 379544d93782SGreg Clayton 3796b9c1b51eSKate Stone if (StateIsStoppedState(state, true)) { 37975b031ebcSEd Maste StreamString strm; 3798b9c1b51eSKate Stone if (thread && FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, 3799b9c1b51eSKate Stone nullptr, nullptr, false, false)) { 380044d93782SGreg Clayton window.MoveCursor(40, 0); 3801c156427dSZachary Turner window.PutCStringTruncated(strm.GetString().str().c_str(), 1); 38025b031ebcSEd Maste } 380344d93782SGreg Clayton 380444d93782SGreg Clayton window.MoveCursor(60, 0); 380544d93782SGreg Clayton if (frame) 3806b9c1b51eSKate Stone window.Printf("Frame: %3u PC = 0x%16.16" PRIx64, 3807b9c1b51eSKate Stone frame->GetFrameIndex(), 3808b9c1b51eSKate Stone frame->GetFrameCodeAddress().GetOpcodeLoadAddress( 3809b9c1b51eSKate Stone exe_ctx.GetTargetPtr())); 3810b9c1b51eSKate Stone } else if (state == eStateExited) { 381144d93782SGreg Clayton const char *exit_desc = process->GetExitDescription(); 381244d93782SGreg Clayton const int exit_status = process->GetExitStatus(); 381344d93782SGreg Clayton if (exit_desc && exit_desc[0]) 381444d93782SGreg Clayton window.Printf(" with status = %i (%s)", exit_status, exit_desc); 381544d93782SGreg Clayton else 381644d93782SGreg Clayton window.Printf(" with status = %i", exit_status); 381744d93782SGreg Clayton } 381844d93782SGreg Clayton } 381944d93782SGreg Clayton window.DeferredRefresh(); 382044d93782SGreg Clayton return true; 382144d93782SGreg Clayton } 382244d93782SGreg Clayton 382344d93782SGreg Clayton protected: 382444d93782SGreg Clayton Debugger &m_debugger; 3825554f68d3SGreg Clayton FormatEntity::Entry m_format; 382644d93782SGreg Clayton }; 382744d93782SGreg Clayton 3828b9c1b51eSKate Stone class SourceFileWindowDelegate : public WindowDelegate { 382944d93782SGreg Clayton public: 3830b9c1b51eSKate Stone SourceFileWindowDelegate(Debugger &debugger) 3831b9c1b51eSKate Stone : WindowDelegate(), m_debugger(debugger), m_sc(), m_file_sp(), 3832b9c1b51eSKate Stone m_disassembly_scope(nullptr), m_disassembly_sp(), m_disassembly_range(), 3833b9c1b51eSKate Stone m_title(), m_line_width(4), m_selected_line(0), m_pc_line(0), 3834b9c1b51eSKate Stone m_stop_id(0), m_frame_idx(UINT32_MAX), m_first_visible_line(0), 3835b9c1b51eSKate Stone m_min_x(0), m_min_y(0), m_max_x(0), m_max_y(0) {} 383644d93782SGreg Clayton 3837315b6884SEugene Zelenko ~SourceFileWindowDelegate() override = default; 383844d93782SGreg Clayton 3839b9c1b51eSKate Stone void Update(const SymbolContext &sc) { m_sc = sc; } 384044d93782SGreg Clayton 3841b9c1b51eSKate Stone uint32_t NumVisibleLines() const { return m_max_y - m_min_y; } 384244d93782SGreg Clayton 3843b9c1b51eSKate Stone const char *WindowDelegateGetHelpText() override { 384444d93782SGreg Clayton return "Source/Disassembly window keyboard shortcuts:"; 384544d93782SGreg Clayton } 384644d93782SGreg Clayton 3847b9c1b51eSKate Stone KeyHelp *WindowDelegateGetKeyHelp() override { 384844d93782SGreg Clayton static curses::KeyHelp g_source_view_key_help[] = { 384944d93782SGreg Clayton {KEY_RETURN, "Run to selected line with one shot breakpoint"}, 385044d93782SGreg Clayton {KEY_UP, "Select previous source line"}, 385144d93782SGreg Clayton {KEY_DOWN, "Select next source line"}, 385244d93782SGreg Clayton {KEY_PPAGE, "Page up"}, 385344d93782SGreg Clayton {KEY_NPAGE, "Page down"}, 385444d93782SGreg Clayton {'b', "Set breakpoint on selected source/disassembly line"}, 385544d93782SGreg Clayton {'c', "Continue process"}, 385644d93782SGreg Clayton {'d', "Detach and resume process"}, 385744d93782SGreg Clayton {'D', "Detach with process suspended"}, 385844d93782SGreg Clayton {'h', "Show help dialog"}, 385944d93782SGreg Clayton {'k', "Kill process"}, 386044d93782SGreg Clayton {'n', "Step over (source line)"}, 386144d93782SGreg Clayton {'N', "Step over (single instruction)"}, 386244d93782SGreg Clayton {'o', "Step out"}, 386344d93782SGreg Clayton {'s', "Step in (source line)"}, 386444d93782SGreg Clayton {'S', "Step in (single instruction)"}, 386544d93782SGreg Clayton {',', "Page up"}, 386644d93782SGreg Clayton {'.', "Page down"}, 3867b9c1b51eSKate Stone {'\0', nullptr}}; 386844d93782SGreg Clayton return g_source_view_key_help; 386944d93782SGreg Clayton } 387044d93782SGreg Clayton 3871b9c1b51eSKate Stone bool WindowDelegateDraw(Window &window, bool force) override { 3872b9c1b51eSKate Stone ExecutionContext exe_ctx = 3873b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 387444d93782SGreg Clayton Process *process = exe_ctx.GetProcessPtr(); 3875c5dac77aSEugene Zelenko Thread *thread = nullptr; 387644d93782SGreg Clayton 387744d93782SGreg Clayton bool update_location = false; 3878b9c1b51eSKate Stone if (process) { 387944d93782SGreg Clayton StateType state = process->GetState(); 3880b9c1b51eSKate Stone if (StateIsStoppedState(state, true)) { 388144d93782SGreg Clayton // We are stopped, so it is ok to 388244d93782SGreg Clayton update_location = true; 388344d93782SGreg Clayton } 388444d93782SGreg Clayton } 388544d93782SGreg Clayton 388644d93782SGreg Clayton m_min_x = 1; 3887ec990867SGreg Clayton m_min_y = 2; 388844d93782SGreg Clayton m_max_x = window.GetMaxX() - 1; 388944d93782SGreg Clayton m_max_y = window.GetMaxY() - 1; 389044d93782SGreg Clayton 389144d93782SGreg Clayton const uint32_t num_visible_lines = NumVisibleLines(); 389244d93782SGreg Clayton StackFrameSP frame_sp; 389344d93782SGreg Clayton bool set_selected_line_to_pc = false; 389444d93782SGreg Clayton 3895b9c1b51eSKate Stone if (update_location) { 389644d93782SGreg Clayton const bool process_alive = process ? process->IsAlive() : false; 389744d93782SGreg Clayton bool thread_changed = false; 3898b9c1b51eSKate Stone if (process_alive) { 389944d93782SGreg Clayton thread = exe_ctx.GetThreadPtr(); 3900b9c1b51eSKate Stone if (thread) { 390144d93782SGreg Clayton frame_sp = thread->GetSelectedFrame(); 390244d93782SGreg Clayton auto tid = thread->GetID(); 390344d93782SGreg Clayton thread_changed = tid != m_tid; 390444d93782SGreg Clayton m_tid = tid; 3905b9c1b51eSKate Stone } else { 3906b9c1b51eSKate Stone if (m_tid != LLDB_INVALID_THREAD_ID) { 390744d93782SGreg Clayton thread_changed = true; 390844d93782SGreg Clayton m_tid = LLDB_INVALID_THREAD_ID; 390944d93782SGreg Clayton } 391044d93782SGreg Clayton } 391144d93782SGreg Clayton } 391244d93782SGreg Clayton const uint32_t stop_id = process ? process->GetStopID() : 0; 391344d93782SGreg Clayton const bool stop_id_changed = stop_id != m_stop_id; 391444d93782SGreg Clayton bool frame_changed = false; 391544d93782SGreg Clayton m_stop_id = stop_id; 3916ec990867SGreg Clayton m_title.Clear(); 3917b9c1b51eSKate Stone if (frame_sp) { 391844d93782SGreg Clayton m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything); 3919b9c1b51eSKate Stone if (m_sc.module_sp) { 3920b9c1b51eSKate Stone m_title.Printf( 3921b9c1b51eSKate Stone "%s", m_sc.module_sp->GetFileSpec().GetFilename().GetCString()); 3922ec990867SGreg Clayton ConstString func_name = m_sc.GetFunctionName(); 3923ec990867SGreg Clayton if (func_name) 3924ec990867SGreg Clayton m_title.Printf("`%s", func_name.GetCString()); 3925ec990867SGreg Clayton } 392644d93782SGreg Clayton const uint32_t frame_idx = frame_sp->GetFrameIndex(); 392744d93782SGreg Clayton frame_changed = frame_idx != m_frame_idx; 392844d93782SGreg Clayton m_frame_idx = frame_idx; 3929b9c1b51eSKate Stone } else { 393044d93782SGreg Clayton m_sc.Clear(true); 393144d93782SGreg Clayton frame_changed = m_frame_idx != UINT32_MAX; 393244d93782SGreg Clayton m_frame_idx = UINT32_MAX; 393344d93782SGreg Clayton } 393444d93782SGreg Clayton 3935b9c1b51eSKate Stone const bool context_changed = 3936b9c1b51eSKate Stone thread_changed || frame_changed || stop_id_changed; 393744d93782SGreg Clayton 3938b9c1b51eSKate Stone if (process_alive) { 3939b9c1b51eSKate Stone if (m_sc.line_entry.IsValid()) { 394044d93782SGreg Clayton m_pc_line = m_sc.line_entry.line; 394144d93782SGreg Clayton if (m_pc_line != UINT32_MAX) 394244d93782SGreg Clayton --m_pc_line; // Convert to zero based line number... 394344d93782SGreg Clayton // Update the selected line if the stop ID changed... 394444d93782SGreg Clayton if (context_changed) 394544d93782SGreg Clayton m_selected_line = m_pc_line; 394644d93782SGreg Clayton 3947b9c1b51eSKate Stone if (m_file_sp && m_file_sp->FileSpecMatches(m_sc.line_entry.file)) { 394844d93782SGreg Clayton // Same file, nothing to do, we should either have the 394944d93782SGreg Clayton // lines or not (source file missing) 3950b9c1b51eSKate Stone if (m_selected_line >= static_cast<size_t>(m_first_visible_line)) { 395144d93782SGreg Clayton if (m_selected_line >= m_first_visible_line + num_visible_lines) 395244d93782SGreg Clayton m_first_visible_line = m_selected_line - 10; 3953b9c1b51eSKate Stone } else { 395444d93782SGreg Clayton if (m_selected_line > 10) 395544d93782SGreg Clayton m_first_visible_line = m_selected_line - 10; 395644d93782SGreg Clayton else 395744d93782SGreg Clayton m_first_visible_line = 0; 395844d93782SGreg Clayton } 3959b9c1b51eSKate Stone } else { 396044d93782SGreg Clayton // File changed, set selected line to the line with the PC 396144d93782SGreg Clayton m_selected_line = m_pc_line; 3962b9c1b51eSKate Stone m_file_sp = 3963b9c1b51eSKate Stone m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file); 3964b9c1b51eSKate Stone if (m_file_sp) { 396544d93782SGreg Clayton const size_t num_lines = m_file_sp->GetNumLines(); 396644d93782SGreg Clayton int m_line_width = 1; 396744d93782SGreg Clayton for (size_t n = num_lines; n >= 10; n = n / 10) 396844d93782SGreg Clayton ++m_line_width; 396944d93782SGreg Clayton 3970b9c1b51eSKate Stone snprintf(m_line_format, sizeof(m_line_format), " %%%iu ", 3971b9c1b51eSKate Stone m_line_width); 3972b9c1b51eSKate Stone if (num_lines < num_visible_lines || 3973b9c1b51eSKate Stone m_selected_line < num_visible_lines) 397444d93782SGreg Clayton m_first_visible_line = 0; 397544d93782SGreg Clayton else 397644d93782SGreg Clayton m_first_visible_line = m_selected_line - 10; 397744d93782SGreg Clayton } 397844d93782SGreg Clayton } 3979b9c1b51eSKate Stone } else { 398044d93782SGreg Clayton m_file_sp.reset(); 398144d93782SGreg Clayton } 398244d93782SGreg Clayton 3983b9c1b51eSKate Stone if (!m_file_sp || m_file_sp->GetNumLines() == 0) { 398444d93782SGreg Clayton // Show disassembly 398544d93782SGreg Clayton bool prefer_file_cache = false; 3986b9c1b51eSKate Stone if (m_sc.function) { 3987b9c1b51eSKate Stone if (m_disassembly_scope != m_sc.function) { 398844d93782SGreg Clayton m_disassembly_scope = m_sc.function; 3989b9c1b51eSKate Stone m_disassembly_sp = m_sc.function->GetInstructions( 3990b9c1b51eSKate Stone exe_ctx, nullptr, prefer_file_cache); 3991b9c1b51eSKate Stone if (m_disassembly_sp) { 399244d93782SGreg Clayton set_selected_line_to_pc = true; 399344d93782SGreg Clayton m_disassembly_range = m_sc.function->GetAddressRange(); 3994b9c1b51eSKate Stone } else { 399544d93782SGreg Clayton m_disassembly_range.Clear(); 399644d93782SGreg Clayton } 3997b9c1b51eSKate Stone } else { 399844d93782SGreg Clayton set_selected_line_to_pc = context_changed; 399944d93782SGreg Clayton } 4000b9c1b51eSKate Stone } else if (m_sc.symbol) { 4001b9c1b51eSKate Stone if (m_disassembly_scope != m_sc.symbol) { 400244d93782SGreg Clayton m_disassembly_scope = m_sc.symbol; 4003b9c1b51eSKate Stone m_disassembly_sp = m_sc.symbol->GetInstructions( 4004b9c1b51eSKate Stone exe_ctx, nullptr, prefer_file_cache); 4005b9c1b51eSKate Stone if (m_disassembly_sp) { 400644d93782SGreg Clayton set_selected_line_to_pc = true; 4007b9c1b51eSKate Stone m_disassembly_range.GetBaseAddress() = 4008b9c1b51eSKate Stone m_sc.symbol->GetAddress(); 400944d93782SGreg Clayton m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize()); 4010b9c1b51eSKate Stone } else { 401144d93782SGreg Clayton m_disassembly_range.Clear(); 401244d93782SGreg Clayton } 4013b9c1b51eSKate Stone } else { 401444d93782SGreg Clayton set_selected_line_to_pc = context_changed; 401544d93782SGreg Clayton } 401644d93782SGreg Clayton } 401744d93782SGreg Clayton } 4018b9c1b51eSKate Stone } else { 401944d93782SGreg Clayton m_pc_line = UINT32_MAX; 402044d93782SGreg Clayton } 402144d93782SGreg Clayton } 402244d93782SGreg Clayton 4023ec990867SGreg Clayton const int window_width = window.GetWidth(); 402444d93782SGreg Clayton window.Erase(); 402544d93782SGreg Clayton window.DrawTitleBox("Sources"); 4026b9c1b51eSKate Stone if (!m_title.GetString().empty()) { 4027ec990867SGreg Clayton window.AttributeOn(A_REVERSE); 4028ec990867SGreg Clayton window.MoveCursor(1, 1); 4029ec990867SGreg Clayton window.PutChar(' '); 4030c156427dSZachary Turner window.PutCStringTruncated(m_title.GetString().str().c_str(), 1); 4031ec990867SGreg Clayton int x = window.GetCursorX(); 4032b9c1b51eSKate Stone if (x < window_width - 1) { 4033ec990867SGreg Clayton window.Printf("%*s", window_width - x - 1, ""); 4034ec990867SGreg Clayton } 4035ec990867SGreg Clayton window.AttributeOff(A_REVERSE); 4036ec990867SGreg Clayton } 403744d93782SGreg Clayton 403844d93782SGreg Clayton Target *target = exe_ctx.GetTargetPtr(); 403944d93782SGreg Clayton const size_t num_source_lines = GetNumSourceLines(); 4040b9c1b51eSKate Stone if (num_source_lines > 0) { 404144d93782SGreg Clayton // Display source 404244d93782SGreg Clayton BreakpointLines bp_lines; 4043b9c1b51eSKate Stone if (target) { 404444d93782SGreg Clayton BreakpointList &bp_list = target->GetBreakpointList(); 404544d93782SGreg Clayton const size_t num_bps = bp_list.GetSize(); 4046b9c1b51eSKate Stone for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) { 404744d93782SGreg Clayton BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx); 404844d93782SGreg Clayton const size_t num_bps_locs = bp_sp->GetNumLocations(); 4049b9c1b51eSKate Stone for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) { 4050b9c1b51eSKate Stone BreakpointLocationSP bp_loc_sp = 4051b9c1b51eSKate Stone bp_sp->GetLocationAtIndex(bp_loc_idx); 405244d93782SGreg Clayton LineEntry bp_loc_line_entry; 4053b9c1b51eSKate Stone if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry( 4054b9c1b51eSKate Stone bp_loc_line_entry)) { 4055b9c1b51eSKate Stone if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file) { 405644d93782SGreg Clayton bp_lines.insert(bp_loc_line_entry.line); 405744d93782SGreg Clayton } 405844d93782SGreg Clayton } 405944d93782SGreg Clayton } 406044d93782SGreg Clayton } 406144d93782SGreg Clayton } 406244d93782SGreg Clayton 406344d93782SGreg Clayton const attr_t selected_highlight_attr = A_REVERSE; 406444d93782SGreg Clayton const attr_t pc_highlight_attr = COLOR_PAIR(1); 406544d93782SGreg Clayton 4066b9c1b51eSKate Stone for (size_t i = 0; i < num_visible_lines; ++i) { 406744d93782SGreg Clayton const uint32_t curr_line = m_first_visible_line + i; 4068b9c1b51eSKate Stone if (curr_line < num_source_lines) { 4069ec990867SGreg Clayton const int line_y = m_min_y + i; 407044d93782SGreg Clayton window.MoveCursor(1, line_y); 407144d93782SGreg Clayton const bool is_pc_line = curr_line == m_pc_line; 407244d93782SGreg Clayton const bool line_is_selected = m_selected_line == curr_line; 407344d93782SGreg Clayton // Highlight the line as the PC line first, then if the selected line 407444d93782SGreg Clayton // isn't the same as the PC line, highlight it differently 407544d93782SGreg Clayton attr_t highlight_attr = 0; 407644d93782SGreg Clayton attr_t bp_attr = 0; 407744d93782SGreg Clayton if (is_pc_line) 407844d93782SGreg Clayton highlight_attr = pc_highlight_attr; 407944d93782SGreg Clayton else if (line_is_selected) 408044d93782SGreg Clayton highlight_attr = selected_highlight_attr; 408144d93782SGreg Clayton 408244d93782SGreg Clayton if (bp_lines.find(curr_line + 1) != bp_lines.end()) 408344d93782SGreg Clayton bp_attr = COLOR_PAIR(2); 408444d93782SGreg Clayton 408544d93782SGreg Clayton if (bp_attr) 408644d93782SGreg Clayton window.AttributeOn(bp_attr); 408744d93782SGreg Clayton 408844d93782SGreg Clayton window.Printf(m_line_format, curr_line + 1); 408944d93782SGreg Clayton 409044d93782SGreg Clayton if (bp_attr) 409144d93782SGreg Clayton window.AttributeOff(bp_attr); 409244d93782SGreg Clayton 409344d93782SGreg Clayton window.PutChar(ACS_VLINE); 409444d93782SGreg Clayton // Mark the line with the PC with a diamond 409544d93782SGreg Clayton if (is_pc_line) 409644d93782SGreg Clayton window.PutChar(ACS_DIAMOND); 409744d93782SGreg Clayton else 409844d93782SGreg Clayton window.PutChar(' '); 409944d93782SGreg Clayton 410044d93782SGreg Clayton if (highlight_attr) 410144d93782SGreg Clayton window.AttributeOn(highlight_attr); 4102b9c1b51eSKate Stone const uint32_t line_len = 4103b9c1b51eSKate Stone m_file_sp->GetLineLength(curr_line + 1, false); 410444d93782SGreg Clayton if (line_len > 0) 410544d93782SGreg Clayton window.PutCString(m_file_sp->PeekLineData(curr_line + 1), line_len); 410644d93782SGreg Clayton 4107b9c1b51eSKate Stone if (is_pc_line && frame_sp && 4108b9c1b51eSKate Stone frame_sp->GetConcreteFrameIndex() == 0) { 410944d93782SGreg Clayton StopInfoSP stop_info_sp; 411044d93782SGreg Clayton if (thread) 411144d93782SGreg Clayton stop_info_sp = thread->GetStopInfo(); 4112b9c1b51eSKate Stone if (stop_info_sp) { 411344d93782SGreg Clayton const char *stop_description = stop_info_sp->GetDescription(); 4114b9c1b51eSKate Stone if (stop_description && stop_description[0]) { 411544d93782SGreg Clayton size_t stop_description_len = strlen(stop_description); 4116ec990867SGreg Clayton int desc_x = window_width - stop_description_len - 16; 411744d93782SGreg Clayton window.Printf("%*s", desc_x - window.GetCursorX(), ""); 4118b9c1b51eSKate Stone // window.MoveCursor(window_width - stop_description_len - 15, 4119b9c1b51eSKate Stone // line_y); 4120b9c1b51eSKate Stone window.Printf("<<< Thread %u: %s ", thread->GetIndexID(), 4121b9c1b51eSKate Stone stop_description); 412244d93782SGreg Clayton } 4123b9c1b51eSKate Stone } else { 4124ec990867SGreg Clayton window.Printf("%*s", window_width - window.GetCursorX() - 1, ""); 412544d93782SGreg Clayton } 412644d93782SGreg Clayton } 412744d93782SGreg Clayton if (highlight_attr) 412844d93782SGreg Clayton window.AttributeOff(highlight_attr); 4129b9c1b51eSKate Stone } else { 413044d93782SGreg Clayton break; 413144d93782SGreg Clayton } 413244d93782SGreg Clayton } 4133b9c1b51eSKate Stone } else { 413444d93782SGreg Clayton size_t num_disassembly_lines = GetNumDisassemblyLines(); 4135b9c1b51eSKate Stone if (num_disassembly_lines > 0) { 413644d93782SGreg Clayton // Display disassembly 413744d93782SGreg Clayton BreakpointAddrs bp_file_addrs; 413844d93782SGreg Clayton Target *target = exe_ctx.GetTargetPtr(); 4139b9c1b51eSKate Stone if (target) { 414044d93782SGreg Clayton BreakpointList &bp_list = target->GetBreakpointList(); 414144d93782SGreg Clayton const size_t num_bps = bp_list.GetSize(); 4142b9c1b51eSKate Stone for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) { 414344d93782SGreg Clayton BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx); 414444d93782SGreg Clayton const size_t num_bps_locs = bp_sp->GetNumLocations(); 4145b9c1b51eSKate Stone for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; 4146b9c1b51eSKate Stone ++bp_loc_idx) { 4147b9c1b51eSKate Stone BreakpointLocationSP bp_loc_sp = 4148b9c1b51eSKate Stone bp_sp->GetLocationAtIndex(bp_loc_idx); 414944d93782SGreg Clayton LineEntry bp_loc_line_entry; 4150b9c1b51eSKate Stone const lldb::addr_t file_addr = 4151b9c1b51eSKate Stone bp_loc_sp->GetAddress().GetFileAddress(); 4152b9c1b51eSKate Stone if (file_addr != LLDB_INVALID_ADDRESS) { 415344d93782SGreg Clayton if (m_disassembly_range.ContainsFileAddress(file_addr)) 415444d93782SGreg Clayton bp_file_addrs.insert(file_addr); 415544d93782SGreg Clayton } 415644d93782SGreg Clayton } 415744d93782SGreg Clayton } 415844d93782SGreg Clayton } 415944d93782SGreg Clayton 416044d93782SGreg Clayton const attr_t selected_highlight_attr = A_REVERSE; 416144d93782SGreg Clayton const attr_t pc_highlight_attr = COLOR_PAIR(1); 416244d93782SGreg Clayton 416344d93782SGreg Clayton StreamString strm; 416444d93782SGreg Clayton 416544d93782SGreg Clayton InstructionList &insts = m_disassembly_sp->GetInstructionList(); 416644d93782SGreg Clayton Address pc_address; 416744d93782SGreg Clayton 416844d93782SGreg Clayton if (frame_sp) 416944d93782SGreg Clayton pc_address = frame_sp->GetFrameCodeAddress(); 4170b9c1b51eSKate Stone const uint32_t pc_idx = 4171b9c1b51eSKate Stone pc_address.IsValid() 4172b9c1b51eSKate Stone ? insts.GetIndexOfInstructionAtAddress(pc_address) 4173b9c1b51eSKate Stone : UINT32_MAX; 4174b9c1b51eSKate Stone if (set_selected_line_to_pc) { 417544d93782SGreg Clayton m_selected_line = pc_idx; 417644d93782SGreg Clayton } 417744d93782SGreg Clayton 417844d93782SGreg Clayton const uint32_t non_visible_pc_offset = (num_visible_lines / 5); 41793985c8c6SSaleem Abdulrasool if (static_cast<size_t>(m_first_visible_line) >= num_disassembly_lines) 418044d93782SGreg Clayton m_first_visible_line = 0; 418144d93782SGreg Clayton 4182b9c1b51eSKate Stone if (pc_idx < num_disassembly_lines) { 41833985c8c6SSaleem Abdulrasool if (pc_idx < static_cast<uint32_t>(m_first_visible_line) || 418444d93782SGreg Clayton pc_idx >= m_first_visible_line + num_visible_lines) 418544d93782SGreg Clayton m_first_visible_line = pc_idx - non_visible_pc_offset; 418644d93782SGreg Clayton } 418744d93782SGreg Clayton 4188b9c1b51eSKate Stone for (size_t i = 0; i < num_visible_lines; ++i) { 418944d93782SGreg Clayton const uint32_t inst_idx = m_first_visible_line + i; 419044d93782SGreg Clayton Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get(); 419144d93782SGreg Clayton if (!inst) 419244d93782SGreg Clayton break; 419344d93782SGreg Clayton 4194ec990867SGreg Clayton const int line_y = m_min_y + i; 4195ec990867SGreg Clayton window.MoveCursor(1, line_y); 419644d93782SGreg Clayton const bool is_pc_line = frame_sp && inst_idx == pc_idx; 419744d93782SGreg Clayton const bool line_is_selected = m_selected_line == inst_idx; 419844d93782SGreg Clayton // Highlight the line as the PC line first, then if the selected line 419944d93782SGreg Clayton // isn't the same as the PC line, highlight it differently 420044d93782SGreg Clayton attr_t highlight_attr = 0; 420144d93782SGreg Clayton attr_t bp_attr = 0; 420244d93782SGreg Clayton if (is_pc_line) 420344d93782SGreg Clayton highlight_attr = pc_highlight_attr; 420444d93782SGreg Clayton else if (line_is_selected) 420544d93782SGreg Clayton highlight_attr = selected_highlight_attr; 420644d93782SGreg Clayton 4207b9c1b51eSKate Stone if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) != 4208b9c1b51eSKate Stone bp_file_addrs.end()) 420944d93782SGreg Clayton bp_attr = COLOR_PAIR(2); 421044d93782SGreg Clayton 421144d93782SGreg Clayton if (bp_attr) 421244d93782SGreg Clayton window.AttributeOn(bp_attr); 421344d93782SGreg Clayton 4214324a1036SSaleem Abdulrasool window.Printf(" 0x%16.16llx ", 4215b9c1b51eSKate Stone static_cast<unsigned long long>( 4216b9c1b51eSKate Stone inst->GetAddress().GetLoadAddress(target))); 421744d93782SGreg Clayton 421844d93782SGreg Clayton if (bp_attr) 421944d93782SGreg Clayton window.AttributeOff(bp_attr); 422044d93782SGreg Clayton 422144d93782SGreg Clayton window.PutChar(ACS_VLINE); 422244d93782SGreg Clayton // Mark the line with the PC with a diamond 422344d93782SGreg Clayton if (is_pc_line) 422444d93782SGreg Clayton window.PutChar(ACS_DIAMOND); 422544d93782SGreg Clayton else 422644d93782SGreg Clayton window.PutChar(' '); 422744d93782SGreg Clayton 422844d93782SGreg Clayton if (highlight_attr) 422944d93782SGreg Clayton window.AttributeOn(highlight_attr); 423044d93782SGreg Clayton 423144d93782SGreg Clayton const char *mnemonic = inst->GetMnemonic(&exe_ctx); 423244d93782SGreg Clayton const char *operands = inst->GetOperands(&exe_ctx); 423344d93782SGreg Clayton const char *comment = inst->GetComment(&exe_ctx); 423444d93782SGreg Clayton 4235c5dac77aSEugene Zelenko if (mnemonic != nullptr && mnemonic[0] == '\0') 4236c5dac77aSEugene Zelenko mnemonic = nullptr; 4237c5dac77aSEugene Zelenko if (operands != nullptr && operands[0] == '\0') 4238c5dac77aSEugene Zelenko operands = nullptr; 4239c5dac77aSEugene Zelenko if (comment != nullptr && comment[0] == '\0') 4240c5dac77aSEugene Zelenko comment = nullptr; 424144d93782SGreg Clayton 424244d93782SGreg Clayton strm.Clear(); 424344d93782SGreg Clayton 4244c5dac77aSEugene Zelenko if (mnemonic != nullptr && operands != nullptr && comment != nullptr) 424544d93782SGreg Clayton strm.Printf("%-8s %-25s ; %s", mnemonic, operands, comment); 4246c5dac77aSEugene Zelenko else if (mnemonic != nullptr && operands != nullptr) 424744d93782SGreg Clayton strm.Printf("%-8s %s", mnemonic, operands); 4248c5dac77aSEugene Zelenko else if (mnemonic != nullptr) 424944d93782SGreg Clayton strm.Printf("%s", mnemonic); 425044d93782SGreg Clayton 425144d93782SGreg Clayton int right_pad = 1; 4252c156427dSZachary Turner window.PutCStringTruncated(strm.GetData(), right_pad); 425344d93782SGreg Clayton 4254b9c1b51eSKate Stone if (is_pc_line && frame_sp && 4255b9c1b51eSKate Stone frame_sp->GetConcreteFrameIndex() == 0) { 425644d93782SGreg Clayton StopInfoSP stop_info_sp; 425744d93782SGreg Clayton if (thread) 425844d93782SGreg Clayton stop_info_sp = thread->GetStopInfo(); 4259b9c1b51eSKate Stone if (stop_info_sp) { 426044d93782SGreg Clayton const char *stop_description = stop_info_sp->GetDescription(); 4261b9c1b51eSKate Stone if (stop_description && stop_description[0]) { 426244d93782SGreg Clayton size_t stop_description_len = strlen(stop_description); 4263ec990867SGreg Clayton int desc_x = window_width - stop_description_len - 16; 426444d93782SGreg Clayton window.Printf("%*s", desc_x - window.GetCursorX(), ""); 4265b9c1b51eSKate Stone // window.MoveCursor(window_width - stop_description_len - 15, 4266b9c1b51eSKate Stone // line_y); 4267b9c1b51eSKate Stone window.Printf("<<< Thread %u: %s ", thread->GetIndexID(), 4268b9c1b51eSKate Stone stop_description); 426944d93782SGreg Clayton } 4270b9c1b51eSKate Stone } else { 4271ec990867SGreg Clayton window.Printf("%*s", window_width - window.GetCursorX() - 1, ""); 427244d93782SGreg Clayton } 427344d93782SGreg Clayton } 427444d93782SGreg Clayton if (highlight_attr) 427544d93782SGreg Clayton window.AttributeOff(highlight_attr); 427644d93782SGreg Clayton } 427744d93782SGreg Clayton } 427844d93782SGreg Clayton } 427944d93782SGreg Clayton window.DeferredRefresh(); 428044d93782SGreg Clayton return true; // Drawing handled 428144d93782SGreg Clayton } 428244d93782SGreg Clayton 4283b9c1b51eSKate Stone size_t GetNumLines() { 428444d93782SGreg Clayton size_t num_lines = GetNumSourceLines(); 428544d93782SGreg Clayton if (num_lines == 0) 428644d93782SGreg Clayton num_lines = GetNumDisassemblyLines(); 428744d93782SGreg Clayton return num_lines; 428844d93782SGreg Clayton } 428944d93782SGreg Clayton 4290b9c1b51eSKate Stone size_t GetNumSourceLines() const { 429144d93782SGreg Clayton if (m_file_sp) 429244d93782SGreg Clayton return m_file_sp->GetNumLines(); 429344d93782SGreg Clayton return 0; 429444d93782SGreg Clayton } 4295315b6884SEugene Zelenko 4296b9c1b51eSKate Stone size_t GetNumDisassemblyLines() const { 429744d93782SGreg Clayton if (m_disassembly_sp) 429844d93782SGreg Clayton return m_disassembly_sp->GetInstructionList().GetSize(); 429944d93782SGreg Clayton return 0; 430044d93782SGreg Clayton } 430144d93782SGreg Clayton 4302b9c1b51eSKate Stone HandleCharResult WindowDelegateHandleChar(Window &window, int c) override { 430344d93782SGreg Clayton const uint32_t num_visible_lines = NumVisibleLines(); 430444d93782SGreg Clayton const size_t num_lines = GetNumLines(); 430544d93782SGreg Clayton 4306b9c1b51eSKate Stone switch (c) { 430744d93782SGreg Clayton case ',': 430844d93782SGreg Clayton case KEY_PPAGE: 430944d93782SGreg Clayton // Page up key 43103985c8c6SSaleem Abdulrasool if (static_cast<uint32_t>(m_first_visible_line) > num_visible_lines) 431144d93782SGreg Clayton m_first_visible_line -= num_visible_lines; 431244d93782SGreg Clayton else 431344d93782SGreg Clayton m_first_visible_line = 0; 431444d93782SGreg Clayton m_selected_line = m_first_visible_line; 431544d93782SGreg Clayton return eKeyHandled; 431644d93782SGreg Clayton 431744d93782SGreg Clayton case '.': 431844d93782SGreg Clayton case KEY_NPAGE: 431944d93782SGreg Clayton // Page down key 432044d93782SGreg Clayton { 432144d93782SGreg Clayton if (m_first_visible_line + num_visible_lines < num_lines) 432244d93782SGreg Clayton m_first_visible_line += num_visible_lines; 432344d93782SGreg Clayton else if (num_lines < num_visible_lines) 432444d93782SGreg Clayton m_first_visible_line = 0; 432544d93782SGreg Clayton else 432644d93782SGreg Clayton m_first_visible_line = num_lines - num_visible_lines; 432744d93782SGreg Clayton m_selected_line = m_first_visible_line; 432844d93782SGreg Clayton } 432944d93782SGreg Clayton return eKeyHandled; 433044d93782SGreg Clayton 433144d93782SGreg Clayton case KEY_UP: 4332b9c1b51eSKate Stone if (m_selected_line > 0) { 433344d93782SGreg Clayton m_selected_line--; 43343985c8c6SSaleem Abdulrasool if (static_cast<size_t>(m_first_visible_line) > m_selected_line) 433544d93782SGreg Clayton m_first_visible_line = m_selected_line; 433644d93782SGreg Clayton } 433744d93782SGreg Clayton return eKeyHandled; 433844d93782SGreg Clayton 433944d93782SGreg Clayton case KEY_DOWN: 4340b9c1b51eSKate Stone if (m_selected_line + 1 < num_lines) { 434144d93782SGreg Clayton m_selected_line++; 434244d93782SGreg Clayton if (m_first_visible_line + num_visible_lines < m_selected_line) 434344d93782SGreg Clayton m_first_visible_line++; 434444d93782SGreg Clayton } 434544d93782SGreg Clayton return eKeyHandled; 434644d93782SGreg Clayton 434744d93782SGreg Clayton case '\r': 434844d93782SGreg Clayton case '\n': 434944d93782SGreg Clayton case KEY_ENTER: 435044d93782SGreg Clayton // Set a breakpoint and run to the line using a one shot breakpoint 4351b9c1b51eSKate Stone if (GetNumSourceLines() > 0) { 4352b9c1b51eSKate Stone ExecutionContext exe_ctx = 4353b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 4354b9c1b51eSKate Stone if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive()) { 4355b9c1b51eSKate Stone BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint( 4356b9c1b51eSKate Stone nullptr, // Don't limit the breakpoint to certain modules 435744d93782SGreg Clayton m_file_sp->GetFileSpec(), // Source file 4358b9c1b51eSKate Stone m_selected_line + 4359b9c1b51eSKate Stone 1, // Source line number (m_selected_line is zero based) 43602411167fSJim Ingham 0, // No offset 436144d93782SGreg Clayton eLazyBoolCalculate, // Check inlines using global setting 436244d93782SGreg Clayton eLazyBoolCalculate, // Skip prologue using global setting, 436344d93782SGreg Clayton false, // internal 4364055ad9beSIlia K false, // request_hardware 4365055ad9beSIlia K eLazyBoolCalculate); // move_to_nearest_code 436644d93782SGreg Clayton // Make breakpoint one shot 436744d93782SGreg Clayton bp_sp->GetOptions()->SetOneShot(true); 436844d93782SGreg Clayton exe_ctx.GetProcessRef().Resume(); 436944d93782SGreg Clayton } 4370b9c1b51eSKate Stone } else if (m_selected_line < GetNumDisassemblyLines()) { 4371b9c1b51eSKate Stone const Instruction *inst = m_disassembly_sp->GetInstructionList() 4372b9c1b51eSKate Stone .GetInstructionAtIndex(m_selected_line) 4373b9c1b51eSKate Stone .get(); 4374b9c1b51eSKate Stone ExecutionContext exe_ctx = 4375b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 4376b9c1b51eSKate Stone if (exe_ctx.HasTargetScope()) { 437744d93782SGreg Clayton Address addr = inst->GetAddress(); 4378b9c1b51eSKate Stone BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint( 4379b9c1b51eSKate Stone addr, // lldb_private::Address 438044d93782SGreg Clayton false, // internal 438144d93782SGreg Clayton false); // request_hardware 438244d93782SGreg Clayton // Make breakpoint one shot 438344d93782SGreg Clayton bp_sp->GetOptions()->SetOneShot(true); 438444d93782SGreg Clayton exe_ctx.GetProcessRef().Resume(); 438544d93782SGreg Clayton } 438644d93782SGreg Clayton } 438744d93782SGreg Clayton return eKeyHandled; 438844d93782SGreg Clayton 438944d93782SGreg Clayton case 'b': // 'b' == toggle breakpoint on currently selected line 4390b9c1b51eSKate Stone if (m_selected_line < GetNumSourceLines()) { 4391b9c1b51eSKate Stone ExecutionContext exe_ctx = 4392b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 4393b9c1b51eSKate Stone if (exe_ctx.HasTargetScope()) { 4394b9c1b51eSKate Stone BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint( 4395b9c1b51eSKate Stone nullptr, // Don't limit the breakpoint to certain modules 439644d93782SGreg Clayton m_file_sp->GetFileSpec(), // Source file 4397b9c1b51eSKate Stone m_selected_line + 4398b9c1b51eSKate Stone 1, // Source line number (m_selected_line is zero based) 43992411167fSJim Ingham 0, // No offset 440044d93782SGreg Clayton eLazyBoolCalculate, // Check inlines using global setting 440144d93782SGreg Clayton eLazyBoolCalculate, // Skip prologue using global setting, 440244d93782SGreg Clayton false, // internal 4403055ad9beSIlia K false, // request_hardware 4404055ad9beSIlia K eLazyBoolCalculate); // move_to_nearest_code 440544d93782SGreg Clayton } 4406b9c1b51eSKate Stone } else if (m_selected_line < GetNumDisassemblyLines()) { 4407b9c1b51eSKate Stone const Instruction *inst = m_disassembly_sp->GetInstructionList() 4408b9c1b51eSKate Stone .GetInstructionAtIndex(m_selected_line) 4409b9c1b51eSKate Stone .get(); 4410b9c1b51eSKate Stone ExecutionContext exe_ctx = 4411b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 4412b9c1b51eSKate Stone if (exe_ctx.HasTargetScope()) { 441344d93782SGreg Clayton Address addr = inst->GetAddress(); 4414b9c1b51eSKate Stone BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint( 4415b9c1b51eSKate Stone addr, // lldb_private::Address 441644d93782SGreg Clayton false, // internal 441744d93782SGreg Clayton false); // request_hardware 441844d93782SGreg Clayton } 441944d93782SGreg Clayton } 442044d93782SGreg Clayton return eKeyHandled; 442144d93782SGreg Clayton 442244d93782SGreg Clayton case 'd': // 'd' == detach and let run 442344d93782SGreg Clayton case 'D': // 'D' == detach and keep stopped 442444d93782SGreg Clayton { 4425b9c1b51eSKate Stone ExecutionContext exe_ctx = 4426b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 442744d93782SGreg Clayton if (exe_ctx.HasProcessScope()) 442844d93782SGreg Clayton exe_ctx.GetProcessRef().Detach(c == 'D'); 442944d93782SGreg Clayton } 443044d93782SGreg Clayton return eKeyHandled; 443144d93782SGreg Clayton 443244d93782SGreg Clayton case 'k': 443344d93782SGreg Clayton // 'k' == kill 443444d93782SGreg Clayton { 4435b9c1b51eSKate Stone ExecutionContext exe_ctx = 4436b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 443744d93782SGreg Clayton if (exe_ctx.HasProcessScope()) 4438ede3193bSJason Molenda exe_ctx.GetProcessRef().Destroy(false); 443944d93782SGreg Clayton } 444044d93782SGreg Clayton return eKeyHandled; 444144d93782SGreg Clayton 444244d93782SGreg Clayton case 'c': 444344d93782SGreg Clayton // 'c' == continue 444444d93782SGreg Clayton { 4445b9c1b51eSKate Stone ExecutionContext exe_ctx = 4446b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 444744d93782SGreg Clayton if (exe_ctx.HasProcessScope()) 444844d93782SGreg Clayton exe_ctx.GetProcessRef().Resume(); 444944d93782SGreg Clayton } 445044d93782SGreg Clayton return eKeyHandled; 445144d93782SGreg Clayton 445244d93782SGreg Clayton case 'o': 445344d93782SGreg Clayton // 'o' == step out 445444d93782SGreg Clayton { 4455b9c1b51eSKate Stone ExecutionContext exe_ctx = 4456b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 4457b9c1b51eSKate Stone if (exe_ctx.HasThreadScope() && 4458b9c1b51eSKate Stone StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) { 445944d93782SGreg Clayton exe_ctx.GetThreadRef().StepOut(); 446044d93782SGreg Clayton } 446144d93782SGreg Clayton } 446244d93782SGreg Clayton return eKeyHandled; 4463315b6884SEugene Zelenko 446444d93782SGreg Clayton case 'n': // 'n' == step over 446544d93782SGreg Clayton case 'N': // 'N' == step over instruction 446644d93782SGreg Clayton { 4467b9c1b51eSKate Stone ExecutionContext exe_ctx = 4468b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 4469b9c1b51eSKate Stone if (exe_ctx.HasThreadScope() && 4470b9c1b51eSKate Stone StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) { 447144d93782SGreg Clayton bool source_step = (c == 'n'); 447244d93782SGreg Clayton exe_ctx.GetThreadRef().StepOver(source_step); 447344d93782SGreg Clayton } 447444d93782SGreg Clayton } 447544d93782SGreg Clayton return eKeyHandled; 4476315b6884SEugene Zelenko 447744d93782SGreg Clayton case 's': // 's' == step into 447844d93782SGreg Clayton case 'S': // 'S' == step into instruction 447944d93782SGreg Clayton { 4480b9c1b51eSKate Stone ExecutionContext exe_ctx = 4481b9c1b51eSKate Stone m_debugger.GetCommandInterpreter().GetExecutionContext(); 4482b9c1b51eSKate Stone if (exe_ctx.HasThreadScope() && 4483b9c1b51eSKate Stone StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) { 448444d93782SGreg Clayton bool source_step = (c == 's'); 44854b4b2478SJim Ingham exe_ctx.GetThreadRef().StepIn(source_step); 448644d93782SGreg Clayton } 448744d93782SGreg Clayton } 448844d93782SGreg Clayton return eKeyHandled; 448944d93782SGreg Clayton 449044d93782SGreg Clayton case 'h': 449144d93782SGreg Clayton window.CreateHelpSubwindow(); 449244d93782SGreg Clayton return eKeyHandled; 449344d93782SGreg Clayton 449444d93782SGreg Clayton default: 449544d93782SGreg Clayton break; 449644d93782SGreg Clayton } 449744d93782SGreg Clayton return eKeyNotHandled; 449844d93782SGreg Clayton } 449944d93782SGreg Clayton 450044d93782SGreg Clayton protected: 450144d93782SGreg Clayton typedef std::set<uint32_t> BreakpointLines; 450244d93782SGreg Clayton typedef std::set<lldb::addr_t> BreakpointAddrs; 450344d93782SGreg Clayton 450444d93782SGreg Clayton Debugger &m_debugger; 450544d93782SGreg Clayton SymbolContext m_sc; 450644d93782SGreg Clayton SourceManager::FileSP m_file_sp; 450744d93782SGreg Clayton SymbolContextScope *m_disassembly_scope; 450844d93782SGreg Clayton lldb::DisassemblerSP m_disassembly_sp; 450944d93782SGreg Clayton AddressRange m_disassembly_range; 4510ec990867SGreg Clayton StreamString m_title; 451144d93782SGreg Clayton lldb::user_id_t m_tid; 451244d93782SGreg Clayton char m_line_format[8]; 451344d93782SGreg Clayton int m_line_width; 451444d93782SGreg Clayton uint32_t m_selected_line; // The selected line 451544d93782SGreg Clayton uint32_t m_pc_line; // The line with the PC 451644d93782SGreg Clayton uint32_t m_stop_id; 451744d93782SGreg Clayton uint32_t m_frame_idx; 451844d93782SGreg Clayton int m_first_visible_line; 451944d93782SGreg Clayton int m_min_x; 452044d93782SGreg Clayton int m_min_y; 452144d93782SGreg Clayton int m_max_x; 452244d93782SGreg Clayton int m_max_y; 452344d93782SGreg Clayton }; 452444d93782SGreg Clayton 452544d93782SGreg Clayton DisplayOptions ValueObjectListDelegate::g_options = {true}; 452644d93782SGreg Clayton 4527b9c1b51eSKate Stone IOHandlerCursesGUI::IOHandlerCursesGUI(Debugger &debugger) 4528b9c1b51eSKate Stone : IOHandler(debugger, IOHandler::Type::Curses) {} 452944d93782SGreg Clayton 4530b9c1b51eSKate Stone void IOHandlerCursesGUI::Activate() { 453144d93782SGreg Clayton IOHandler::Activate(); 4532b9c1b51eSKate Stone if (!m_app_ap) { 453344d93782SGreg Clayton m_app_ap.reset(new Application(GetInputFILE(), GetOutputFILE())); 453444d93782SGreg Clayton 453544d93782SGreg Clayton // This is both a window and a menu delegate 4536b9c1b51eSKate Stone std::shared_ptr<ApplicationDelegate> app_delegate_sp( 4537b9c1b51eSKate Stone new ApplicationDelegate(*m_app_ap, m_debugger)); 453844d93782SGreg Clayton 4539b9c1b51eSKate Stone MenuDelegateSP app_menu_delegate_sp = 4540b9c1b51eSKate Stone std::static_pointer_cast<MenuDelegate>(app_delegate_sp); 4541b9c1b51eSKate Stone MenuSP lldb_menu_sp( 4542b9c1b51eSKate Stone new Menu("LLDB", "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB)); 4543b9c1b51eSKate Stone MenuSP exit_menuitem_sp( 4544b9c1b51eSKate Stone new Menu("Exit", nullptr, 'x', ApplicationDelegate::eMenuID_LLDBExit)); 454544d93782SGreg Clayton exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit); 4546b9c1b51eSKate Stone lldb_menu_sp->AddSubmenu(MenuSP(new Menu( 4547b9c1b51eSKate Stone "About LLDB", nullptr, 'a', ApplicationDelegate::eMenuID_LLDBAbout))); 454844d93782SGreg Clayton lldb_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator))); 454944d93782SGreg Clayton lldb_menu_sp->AddSubmenu(exit_menuitem_sp); 455044d93782SGreg Clayton 4551b9c1b51eSKate Stone MenuSP target_menu_sp(new Menu("Target", "F2", KEY_F(2), 4552b9c1b51eSKate Stone ApplicationDelegate::eMenuID_Target)); 4553b9c1b51eSKate Stone target_menu_sp->AddSubmenu(MenuSP(new Menu( 4554b9c1b51eSKate Stone "Create", nullptr, 'c', ApplicationDelegate::eMenuID_TargetCreate))); 4555b9c1b51eSKate Stone target_menu_sp->AddSubmenu(MenuSP(new Menu( 4556b9c1b51eSKate Stone "Delete", nullptr, 'd', ApplicationDelegate::eMenuID_TargetDelete))); 455744d93782SGreg Clayton 4558b9c1b51eSKate Stone MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3), 4559b9c1b51eSKate Stone ApplicationDelegate::eMenuID_Process)); 4560b9c1b51eSKate Stone process_menu_sp->AddSubmenu(MenuSP(new Menu( 4561b9c1b51eSKate Stone "Attach", nullptr, 'a', ApplicationDelegate::eMenuID_ProcessAttach))); 4562b9c1b51eSKate Stone process_menu_sp->AddSubmenu(MenuSP(new Menu( 4563b9c1b51eSKate Stone "Detach", nullptr, 'd', ApplicationDelegate::eMenuID_ProcessDetach))); 4564b9c1b51eSKate Stone process_menu_sp->AddSubmenu(MenuSP(new Menu( 4565b9c1b51eSKate Stone "Launch", nullptr, 'l', ApplicationDelegate::eMenuID_ProcessLaunch))); 456644d93782SGreg Clayton process_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator))); 4567b9c1b51eSKate Stone process_menu_sp->AddSubmenu( 4568b9c1b51eSKate Stone MenuSP(new Menu("Continue", nullptr, 'c', 4569b9c1b51eSKate Stone ApplicationDelegate::eMenuID_ProcessContinue))); 4570b9c1b51eSKate Stone process_menu_sp->AddSubmenu(MenuSP(new Menu( 4571b9c1b51eSKate Stone "Halt", nullptr, 'h', ApplicationDelegate::eMenuID_ProcessHalt))); 4572b9c1b51eSKate Stone process_menu_sp->AddSubmenu(MenuSP(new Menu( 4573b9c1b51eSKate Stone "Kill", nullptr, 'k', ApplicationDelegate::eMenuID_ProcessKill))); 457444d93782SGreg Clayton 4575b9c1b51eSKate Stone MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4), 4576b9c1b51eSKate Stone ApplicationDelegate::eMenuID_Thread)); 4577b9c1b51eSKate Stone thread_menu_sp->AddSubmenu(MenuSP(new Menu( 4578b9c1b51eSKate Stone "Step In", nullptr, 'i', ApplicationDelegate::eMenuID_ThreadStepIn))); 4579b9c1b51eSKate Stone thread_menu_sp->AddSubmenu( 4580b9c1b51eSKate Stone MenuSP(new Menu("Step Over", nullptr, 'v', 4581b9c1b51eSKate Stone ApplicationDelegate::eMenuID_ThreadStepOver))); 4582b9c1b51eSKate Stone thread_menu_sp->AddSubmenu(MenuSP(new Menu( 4583b9c1b51eSKate Stone "Step Out", nullptr, 'o', ApplicationDelegate::eMenuID_ThreadStepOut))); 458444d93782SGreg Clayton 4585b9c1b51eSKate Stone MenuSP view_menu_sp( 4586b9c1b51eSKate Stone new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View)); 4587b9c1b51eSKate Stone view_menu_sp->AddSubmenu( 4588b9c1b51eSKate Stone MenuSP(new Menu("Backtrace", nullptr, 'b', 4589b9c1b51eSKate Stone ApplicationDelegate::eMenuID_ViewBacktrace))); 4590b9c1b51eSKate Stone view_menu_sp->AddSubmenu( 4591b9c1b51eSKate Stone MenuSP(new Menu("Registers", nullptr, 'r', 4592b9c1b51eSKate Stone ApplicationDelegate::eMenuID_ViewRegisters))); 4593b9c1b51eSKate Stone view_menu_sp->AddSubmenu(MenuSP(new Menu( 4594b9c1b51eSKate Stone "Source", nullptr, 's', ApplicationDelegate::eMenuID_ViewSource))); 4595b9c1b51eSKate Stone view_menu_sp->AddSubmenu( 4596b9c1b51eSKate Stone MenuSP(new Menu("Variables", nullptr, 'v', 4597b9c1b51eSKate Stone ApplicationDelegate::eMenuID_ViewVariables))); 459844d93782SGreg Clayton 4599b9c1b51eSKate Stone MenuSP help_menu_sp( 4600b9c1b51eSKate Stone new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help)); 4601b9c1b51eSKate Stone help_menu_sp->AddSubmenu(MenuSP(new Menu( 4602b9c1b51eSKate Stone "GUI Help", nullptr, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp))); 460344d93782SGreg Clayton 460444d93782SGreg Clayton m_app_ap->Initialize(); 460544d93782SGreg Clayton WindowSP &main_window_sp = m_app_ap->GetMainWindow(); 460644d93782SGreg Clayton 460744d93782SGreg Clayton MenuSP menubar_sp(new Menu(Menu::Type::Bar)); 460844d93782SGreg Clayton menubar_sp->AddSubmenu(lldb_menu_sp); 460944d93782SGreg Clayton menubar_sp->AddSubmenu(target_menu_sp); 461044d93782SGreg Clayton menubar_sp->AddSubmenu(process_menu_sp); 461144d93782SGreg Clayton menubar_sp->AddSubmenu(thread_menu_sp); 461244d93782SGreg Clayton menubar_sp->AddSubmenu(view_menu_sp); 461344d93782SGreg Clayton menubar_sp->AddSubmenu(help_menu_sp); 461444d93782SGreg Clayton menubar_sp->SetDelegate(app_menu_delegate_sp); 461544d93782SGreg Clayton 461644d93782SGreg Clayton Rect content_bounds = main_window_sp->GetFrame(); 461744d93782SGreg Clayton Rect menubar_bounds = content_bounds.MakeMenuBar(); 461844d93782SGreg Clayton Rect status_bounds = content_bounds.MakeStatusBar(); 461944d93782SGreg Clayton Rect source_bounds; 462044d93782SGreg Clayton Rect variables_bounds; 462144d93782SGreg Clayton Rect threads_bounds; 462244d93782SGreg Clayton Rect source_variables_bounds; 4623b9c1b51eSKate Stone content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds, 4624b9c1b51eSKate Stone threads_bounds); 4625b9c1b51eSKate Stone source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds, 4626b9c1b51eSKate Stone variables_bounds); 462744d93782SGreg Clayton 4628b9c1b51eSKate Stone WindowSP menubar_window_sp = 4629b9c1b51eSKate Stone main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false); 463044d93782SGreg Clayton // Let the menubar get keys if the active window doesn't handle the 463144d93782SGreg Clayton // keys that are typed so it can respond to menubar key presses. 4632b9c1b51eSKate Stone menubar_window_sp->SetCanBeActive( 4633b9c1b51eSKate Stone false); // Don't let the menubar become the active window 463444d93782SGreg Clayton menubar_window_sp->SetDelegate(menubar_sp); 463544d93782SGreg Clayton 4636b9c1b51eSKate Stone WindowSP source_window_sp( 4637b9c1b51eSKate Stone main_window_sp->CreateSubWindow("Source", source_bounds, true)); 4638b9c1b51eSKate Stone WindowSP variables_window_sp( 4639b9c1b51eSKate Stone main_window_sp->CreateSubWindow("Variables", variables_bounds, false)); 4640b9c1b51eSKate Stone WindowSP threads_window_sp( 4641b9c1b51eSKate Stone main_window_sp->CreateSubWindow("Threads", threads_bounds, false)); 4642b9c1b51eSKate Stone WindowSP status_window_sp( 4643b9c1b51eSKate Stone main_window_sp->CreateSubWindow("Status", status_bounds, false)); 4644b9c1b51eSKate Stone status_window_sp->SetCanBeActive( 4645b9c1b51eSKate Stone false); // Don't let the status bar become the active window 4646b9c1b51eSKate Stone main_window_sp->SetDelegate( 4647b9c1b51eSKate Stone std::static_pointer_cast<WindowDelegate>(app_delegate_sp)); 4648b9c1b51eSKate Stone source_window_sp->SetDelegate( 4649b9c1b51eSKate Stone WindowDelegateSP(new SourceFileWindowDelegate(m_debugger))); 4650b9c1b51eSKate Stone variables_window_sp->SetDelegate( 4651b9c1b51eSKate Stone WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger))); 4652ec990867SGreg Clayton TreeDelegateSP thread_delegate_sp(new ThreadsTreeDelegate(m_debugger)); 4653b9c1b51eSKate Stone threads_window_sp->SetDelegate(WindowDelegateSP( 4654b9c1b51eSKate Stone new TreeWindowDelegate(m_debugger, thread_delegate_sp))); 4655b9c1b51eSKate Stone status_window_sp->SetDelegate( 4656b9c1b51eSKate Stone WindowDelegateSP(new StatusBarWindowDelegate(m_debugger))); 465744d93782SGreg Clayton 46585fdb09bbSGreg Clayton // Show the main help window once the first time the curses GUI is launched 46595fdb09bbSGreg Clayton static bool g_showed_help = false; 4660b9c1b51eSKate Stone if (!g_showed_help) { 46615fdb09bbSGreg Clayton g_showed_help = true; 46625fdb09bbSGreg Clayton main_window_sp->CreateHelpSubwindow(); 46635fdb09bbSGreg Clayton } 46645fdb09bbSGreg Clayton 466544d93782SGreg Clayton init_pair(1, COLOR_WHITE, COLOR_BLUE); 466644d93782SGreg Clayton init_pair(2, COLOR_BLACK, COLOR_WHITE); 466744d93782SGreg Clayton init_pair(3, COLOR_MAGENTA, COLOR_WHITE); 466844d93782SGreg Clayton init_pair(4, COLOR_MAGENTA, COLOR_BLACK); 466944d93782SGreg Clayton init_pair(5, COLOR_RED, COLOR_BLACK); 467044d93782SGreg Clayton } 467144d93782SGreg Clayton } 467244d93782SGreg Clayton 4673b9c1b51eSKate Stone void IOHandlerCursesGUI::Deactivate() { m_app_ap->Terminate(); } 467444d93782SGreg Clayton 4675b9c1b51eSKate Stone void IOHandlerCursesGUI::Run() { 467644d93782SGreg Clayton m_app_ap->Run(m_debugger); 467744d93782SGreg Clayton SetIsDone(true); 467844d93782SGreg Clayton } 467944d93782SGreg Clayton 4680315b6884SEugene Zelenko IOHandlerCursesGUI::~IOHandlerCursesGUI() = default; 468144d93782SGreg Clayton 4682b9c1b51eSKate Stone void IOHandlerCursesGUI::Cancel() {} 468344d93782SGreg Clayton 4684b9c1b51eSKate Stone bool IOHandlerCursesGUI::Interrupt() { return false; } 468544d93782SGreg Clayton 4686b9c1b51eSKate Stone void IOHandlerCursesGUI::GotEOF() {} 468744d93782SGreg Clayton 4688315b6884SEugene Zelenko #endif // LLDB_DISABLE_CURSES 4689