112b93ac6SEd Maste //===-- IOHandler.cpp -------------------------------------------*- C++ -*-===//
212b93ac6SEd Maste //
312b93ac6SEd Maste //                     The LLVM Compiler Infrastructure
412b93ac6SEd Maste //
512b93ac6SEd Maste // This file is distributed under the University of Illinois Open Source
612b93ac6SEd Maste // License. See LICENSE.TXT for details.
712b93ac6SEd Maste //
812b93ac6SEd Maste //===----------------------------------------------------------------------===//
912b93ac6SEd Maste 
10f678e45dSDimitry Andric #include "lldb/Core/IOHandler.h"
11f678e45dSDimitry Andric 
129f2f44ceSEd Maste #ifndef LLDB_DISABLE_CURSES
139f2f44ceSEd Maste #include <curses.h>
149f2f44ceSEd Maste #include <panel.h>
159f2f44ceSEd Maste #endif
1612b93ac6SEd Maste 
179f2f44ceSEd Maste #if defined(__APPLE__)
189f2f44ceSEd Maste #include <deque>
199f2f44ceSEd Maste #endif
2012b93ac6SEd Maste #include <string>
2112b93ac6SEd Maste 
2212b93ac6SEd Maste #include "lldb/Core/Debugger.h"
2312b93ac6SEd Maste #include "lldb/Core/StreamFile.h"
24*b5893f02SDimitry Andric #include "lldb/Host/File.h"
25*b5893f02SDimitry Andric #include "lldb/Utility/Predicate.h"
26*b5893f02SDimitry Andric #include "lldb/Utility/Status.h"
27*b5893f02SDimitry Andric #include "lldb/Utility/StreamString.h"
28*b5893f02SDimitry Andric #include "lldb/Utility/StringList.h"
29*b5893f02SDimitry Andric #include "lldb/lldb-forward.h"
30f678e45dSDimitry Andric 
317aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
3212b93ac6SEd Maste #include "lldb/Host/Editline.h"
337aa51b79SEd Maste #endif
3412b93ac6SEd Maste #include "lldb/Interpreter/CommandCompletions.h"
3512b93ac6SEd Maste #include "lldb/Interpreter/CommandInterpreter.h"
36f678e45dSDimitry Andric #ifndef LLDB_DISABLE_CURSES
37f678e45dSDimitry Andric #include "lldb/Breakpoint/BreakpointLocation.h"
38f678e45dSDimitry Andric #include "lldb/Core/Module.h"
39f678e45dSDimitry Andric #include "lldb/Core/ValueObject.h"
40f678e45dSDimitry Andric #include "lldb/Core/ValueObjectRegister.h"
4112b93ac6SEd Maste #include "lldb/Symbol/Block.h"
4212b93ac6SEd Maste #include "lldb/Symbol/Function.h"
4312b93ac6SEd Maste #include "lldb/Symbol/Symbol.h"
444bb0738eSEd Maste #include "lldb/Symbol/VariableList.h"
454bb0738eSEd Maste #include "lldb/Target/Process.h"
46f678e45dSDimitry Andric #include "lldb/Target/RegisterContext.h"
474bb0738eSEd Maste #include "lldb/Target/StackFrame.h"
48f678e45dSDimitry Andric #include "lldb/Target/StopInfo.h"
49435933ddSDimitry Andric #include "lldb/Target/Target.h"
50435933ddSDimitry Andric #include "lldb/Target/Thread.h"
51*b5893f02SDimitry Andric #include "lldb/Utility/State.h"
524bb0738eSEd Maste #endif
5312b93ac6SEd Maste 
54*b5893f02SDimitry Andric #include "llvm/ADT/StringRef.h"
55f678e45dSDimitry Andric 
564bb0738eSEd Maste #ifdef _MSC_VER
57edd7eaddSDimitry Andric #include "lldb/Host/windows/windows.h"
584bb0738eSEd Maste #endif
5912b93ac6SEd Maste 
60*b5893f02SDimitry Andric #include <memory>
61*b5893f02SDimitry Andric #include <mutex>
62f678e45dSDimitry Andric 
63*b5893f02SDimitry Andric #include <assert.h>
64*b5893f02SDimitry Andric #include <ctype.h>
65*b5893f02SDimitry Andric #include <errno.h>
66*b5893f02SDimitry Andric #include <locale.h>
67*b5893f02SDimitry Andric #include <stdint.h>
68*b5893f02SDimitry Andric #include <stdio.h>
69*b5893f02SDimitry Andric #include <string.h>
70*b5893f02SDimitry Andric #include <type_traits>
71f678e45dSDimitry Andric 
7212b93ac6SEd Maste using namespace lldb;
7312b93ac6SEd Maste using namespace lldb_private;
7412b93ac6SEd Maste 
IOHandler(Debugger & debugger,IOHandler::Type type)75435933ddSDimitry Andric IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type)
76435933ddSDimitry Andric     : IOHandler(debugger, type,
7712b93ac6SEd Maste                 StreamFileSP(), // Adopt STDIN from top input reader
7812b93ac6SEd Maste                 StreamFileSP(), // Adopt STDOUT from top input reader
7912b93ac6SEd Maste                 StreamFileSP(), // Adopt STDERR from top input reader
8012b93ac6SEd Maste                 0)              // Flags
81435933ddSDimitry Andric {}
8212b93ac6SEd Maste 
IOHandler(Debugger & debugger,IOHandler::Type type,const lldb::StreamFileSP & input_sp,const lldb::StreamFileSP & output_sp,const lldb::StreamFileSP & error_sp,uint32_t flags)83435933ddSDimitry Andric IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type,
8412b93ac6SEd Maste                      const lldb::StreamFileSP &input_sp,
8512b93ac6SEd Maste                      const lldb::StreamFileSP &output_sp,
86435933ddSDimitry Andric                      const lldb::StreamFileSP &error_sp, uint32_t flags)
87435933ddSDimitry Andric     : m_debugger(debugger), m_input_sp(input_sp), m_output_sp(output_sp),
88435933ddSDimitry Andric       m_error_sp(error_sp), m_popped(false), m_flags(flags), m_type(type),
89435933ddSDimitry Andric       m_user_data(nullptr), m_done(false), m_active(false) {
9012b93ac6SEd Maste   // If any files are not specified, then adopt them from the top input reader.
9112b93ac6SEd Maste   if (!m_input_sp || !m_output_sp || !m_error_sp)
92435933ddSDimitry Andric     debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_sp, m_output_sp,
9312b93ac6SEd Maste                                              m_error_sp);
9412b93ac6SEd Maste }
9512b93ac6SEd Maste 
969f2f44ceSEd Maste IOHandler::~IOHandler() = default;
9712b93ac6SEd Maste 
GetInputFD()98435933ddSDimitry Andric int IOHandler::GetInputFD() {
994bb0738eSEd Maste   return (m_input_sp ? m_input_sp->GetFile().GetDescriptor() : -1);
10012b93ac6SEd Maste }
10112b93ac6SEd Maste 
GetOutputFD()102435933ddSDimitry Andric int IOHandler::GetOutputFD() {
1034bb0738eSEd Maste   return (m_output_sp ? m_output_sp->GetFile().GetDescriptor() : -1);
10412b93ac6SEd Maste }
10512b93ac6SEd Maste 
GetErrorFD()106435933ddSDimitry Andric int IOHandler::GetErrorFD() {
1074bb0738eSEd Maste   return (m_error_sp ? m_error_sp->GetFile().GetDescriptor() : -1);
10812b93ac6SEd Maste }
10912b93ac6SEd Maste 
GetInputFILE()110435933ddSDimitry Andric FILE *IOHandler::GetInputFILE() {
1114bb0738eSEd Maste   return (m_input_sp ? m_input_sp->GetFile().GetStream() : nullptr);
11212b93ac6SEd Maste }
11312b93ac6SEd Maste 
GetOutputFILE()114435933ddSDimitry Andric FILE *IOHandler::GetOutputFILE() {
1154bb0738eSEd Maste   return (m_output_sp ? m_output_sp->GetFile().GetStream() : nullptr);
11612b93ac6SEd Maste }
11712b93ac6SEd Maste 
GetErrorFILE()118435933ddSDimitry Andric FILE *IOHandler::GetErrorFILE() {
1194bb0738eSEd Maste   return (m_error_sp ? m_error_sp->GetFile().GetStream() : nullptr);
12012b93ac6SEd Maste }
12112b93ac6SEd Maste 
GetInputStreamFile()122435933ddSDimitry Andric StreamFileSP &IOHandler::GetInputStreamFile() { return m_input_sp; }
12312b93ac6SEd Maste 
GetOutputStreamFile()124435933ddSDimitry Andric StreamFileSP &IOHandler::GetOutputStreamFile() { return m_output_sp; }
12512b93ac6SEd Maste 
GetErrorStreamFile()126435933ddSDimitry Andric StreamFileSP &IOHandler::GetErrorStreamFile() { return m_error_sp; }
12712b93ac6SEd Maste 
GetIsInteractive()128435933ddSDimitry Andric bool IOHandler::GetIsInteractive() {
12912b93ac6SEd Maste   return GetInputStreamFile()->GetFile().GetIsInteractive();
13012b93ac6SEd Maste }
13112b93ac6SEd Maste 
GetIsRealTerminal()132435933ddSDimitry Andric bool IOHandler::GetIsRealTerminal() {
13312b93ac6SEd Maste   return GetInputStreamFile()->GetFile().GetIsRealTerminal();
13412b93ac6SEd Maste }
13512b93ac6SEd Maste 
SetPopped(bool b)136435933ddSDimitry Andric void IOHandler::SetPopped(bool b) { m_popped.SetValue(b, eBroadcastOnChange); }
1377aa51b79SEd Maste 
WaitForPop()138435933ddSDimitry Andric void IOHandler::WaitForPop() { m_popped.WaitForValueEqualTo(true); }
1397aa51b79SEd Maste 
PrintAsync(Stream * stream,const char * s,size_t len)140435933ddSDimitry Andric void IOHandlerStack::PrintAsync(Stream *stream, const char *s, size_t len) {
141435933ddSDimitry Andric   if (stream) {
1424bb0738eSEd Maste     std::lock_guard<std::recursive_mutex> guard(m_mutex);
1431c3bbb01SEd Maste     if (m_top)
1441c3bbb01SEd Maste       m_top->PrintAsync(stream, s, len);
1451c3bbb01SEd Maste   }
1461c3bbb01SEd Maste }
1471c3bbb01SEd Maste 
IOHandlerConfirm(Debugger & debugger,llvm::StringRef prompt,bool default_response)148435933ddSDimitry Andric IOHandlerConfirm::IOHandlerConfirm(Debugger &debugger, llvm::StringRef prompt,
149435933ddSDimitry Andric                                    bool default_response)
150435933ddSDimitry Andric     : IOHandlerEditline(
151435933ddSDimitry Andric           debugger, IOHandler::Type::Confirm,
1524bb0738eSEd Maste           nullptr, // nullptr editline_name means no history loaded/saved
153435933ddSDimitry Andric           llvm::StringRef(), // No prompt
154435933ddSDimitry Andric           llvm::StringRef(), // No continuation prompt
15512b93ac6SEd Maste           false,             // Multi-line
1567aa51b79SEd Maste           false, // Don't colorize the prompt (i.e. the confirm message.)
157435933ddSDimitry Andric           0, *this),
158435933ddSDimitry Andric       m_default_response(default_response), m_user_response(default_response) {
15912b93ac6SEd Maste   StreamString prompt_stream;
16012b93ac6SEd Maste   prompt_stream.PutCString(prompt);
16112b93ac6SEd Maste   if (m_default_response)
16212b93ac6SEd Maste     prompt_stream.Printf(": [Y/n] ");
16312b93ac6SEd Maste   else
16412b93ac6SEd Maste     prompt_stream.Printf(": [y/N] ");
16512b93ac6SEd Maste 
166435933ddSDimitry Andric   SetPrompt(prompt_stream.GetString());
16712b93ac6SEd Maste }
16812b93ac6SEd Maste 
1699f2f44ceSEd Maste IOHandlerConfirm::~IOHandlerConfirm() = default;
17012b93ac6SEd Maste 
IOHandlerComplete(IOHandler & io_handler,const char * current_line,const char * cursor,const char * last_char,int skip_first_n_matches,int max_matches,StringList & matches,StringList & descriptions)171*b5893f02SDimitry Andric int IOHandlerConfirm::IOHandlerComplete(
172*b5893f02SDimitry Andric     IOHandler &io_handler, const char *current_line, const char *cursor,
173*b5893f02SDimitry Andric     const char *last_char, int skip_first_n_matches, int max_matches,
174*b5893f02SDimitry Andric     StringList &matches, StringList &descriptions) {
175435933ddSDimitry Andric   if (current_line == cursor) {
176435933ddSDimitry Andric     if (m_default_response) {
17712b93ac6SEd Maste       matches.AppendString("y");
178435933ddSDimitry Andric     } else {
17912b93ac6SEd Maste       matches.AppendString("n");
18012b93ac6SEd Maste     }
18112b93ac6SEd Maste   }
18212b93ac6SEd Maste   return matches.GetSize();
18312b93ac6SEd Maste }
18412b93ac6SEd Maste 
IOHandlerInputComplete(IOHandler & io_handler,std::string & line)185435933ddSDimitry Andric void IOHandlerConfirm::IOHandlerInputComplete(IOHandler &io_handler,
186435933ddSDimitry Andric                                               std::string &line) {
187435933ddSDimitry Andric   if (line.empty()) {
18812b93ac6SEd Maste     // User just hit enter, set the response to the default
18912b93ac6SEd Maste     m_user_response = m_default_response;
19012b93ac6SEd Maste     io_handler.SetIsDone(true);
19112b93ac6SEd Maste     return;
19212b93ac6SEd Maste   }
19312b93ac6SEd Maste 
194435933ddSDimitry Andric   if (line.size() == 1) {
195435933ddSDimitry Andric     switch (line[0]) {
19612b93ac6SEd Maste     case 'y':
19712b93ac6SEd Maste     case 'Y':
19812b93ac6SEd Maste       m_user_response = true;
19912b93ac6SEd Maste       io_handler.SetIsDone(true);
20012b93ac6SEd Maste       return;
20112b93ac6SEd Maste     case 'n':
20212b93ac6SEd Maste     case 'N':
20312b93ac6SEd Maste       m_user_response = false;
20412b93ac6SEd Maste       io_handler.SetIsDone(true);
20512b93ac6SEd Maste       return;
20612b93ac6SEd Maste     default:
20712b93ac6SEd Maste       break;
20812b93ac6SEd Maste     }
20912b93ac6SEd Maste   }
21012b93ac6SEd Maste 
211435933ddSDimitry Andric   if (line == "yes" || line == "YES" || line == "Yes") {
21212b93ac6SEd Maste     m_user_response = true;
21312b93ac6SEd Maste     io_handler.SetIsDone(true);
214435933ddSDimitry Andric   } else if (line == "no" || line == "NO" || line == "No") {
21512b93ac6SEd Maste     m_user_response = false;
21612b93ac6SEd Maste     io_handler.SetIsDone(true);
21712b93ac6SEd Maste   }
21812b93ac6SEd Maste }
21912b93ac6SEd Maste 
IOHandlerComplete(IOHandler & io_handler,const char * current_line,const char * cursor,const char * last_char,int skip_first_n_matches,int max_matches,StringList & matches,StringList & descriptions)220*b5893f02SDimitry Andric int IOHandlerDelegate::IOHandlerComplete(
221*b5893f02SDimitry Andric     IOHandler &io_handler, const char *current_line, const char *cursor,
222*b5893f02SDimitry Andric     const char *last_char, int skip_first_n_matches, int max_matches,
223*b5893f02SDimitry Andric     StringList &matches, StringList &descriptions) {
224435933ddSDimitry Andric   switch (m_completion) {
22512b93ac6SEd Maste   case Completion::None:
22612b93ac6SEd Maste     break;
22712b93ac6SEd Maste 
22812b93ac6SEd Maste   case Completion::LLDBCommand:
229435933ddSDimitry Andric     return io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion(
230435933ddSDimitry Andric         current_line, cursor, last_char, skip_first_n_matches, max_matches,
231*b5893f02SDimitry Andric         matches, descriptions);
232435933ddSDimitry Andric   case Completion::Expression: {
233*b5893f02SDimitry Andric     CompletionResult result;
2344ba319b5SDimitry Andric     CompletionRequest request(current_line, current_line - cursor,
235*b5893f02SDimitry Andric                               skip_first_n_matches, max_matches, result);
236435933ddSDimitry Andric     CommandCompletions::InvokeCommonCompletionCallbacks(
237435933ddSDimitry Andric         io_handler.GetDebugger().GetCommandInterpreter(),
2384ba319b5SDimitry Andric         CommandCompletions::eVariablePathCompletion, request, nullptr);
239*b5893f02SDimitry Andric     result.GetMatches(matches);
240*b5893f02SDimitry Andric     result.GetDescriptions(descriptions);
24112b93ac6SEd Maste 
2424ba319b5SDimitry Andric     size_t num_matches = request.GetNumberOfMatches();
243435933ddSDimitry Andric     if (num_matches > 0) {
24412b93ac6SEd Maste       std::string common_prefix;
24512b93ac6SEd Maste       matches.LongestCommonPrefix(common_prefix);
2464ba319b5SDimitry Andric       const size_t partial_name_len = request.GetCursorArgumentPrefix().size();
24712b93ac6SEd Maste 
2484ba319b5SDimitry Andric       // If we matched a unique single command, add a space... Only do this if
2494ba319b5SDimitry Andric       // the completer told us this was a complete word, however...
2504ba319b5SDimitry Andric       if (num_matches == 1 && request.GetWordComplete()) {
25112b93ac6SEd Maste         common_prefix.push_back(' ');
25212b93ac6SEd Maste       }
25312b93ac6SEd Maste       common_prefix.erase(0, partial_name_len);
25412b93ac6SEd Maste       matches.InsertStringAtIndex(0, std::move(common_prefix));
25512b93ac6SEd Maste     }
25612b93ac6SEd Maste     return num_matches;
257435933ddSDimitry Andric   } break;
25812b93ac6SEd Maste   }
25912b93ac6SEd Maste 
26012b93ac6SEd Maste   return 0;
26112b93ac6SEd Maste }
26212b93ac6SEd Maste 
IOHandlerEditline(Debugger & debugger,IOHandler::Type type,const char * editline_name,llvm::StringRef prompt,llvm::StringRef continuation_prompt,bool multi_line,bool color_prompts,uint32_t line_number_start,IOHandlerDelegate & delegate)263435933ddSDimitry Andric IOHandlerEditline::IOHandlerEditline(
264435933ddSDimitry Andric     Debugger &debugger, IOHandler::Type type,
26512b93ac6SEd Maste     const char *editline_name, // Used for saving history files
266435933ddSDimitry Andric     llvm::StringRef prompt, llvm::StringRef continuation_prompt,
267435933ddSDimitry Andric     bool multi_line, bool color_prompts, uint32_t line_number_start,
268435933ddSDimitry Andric     IOHandlerDelegate &delegate)
269435933ddSDimitry Andric     : IOHandlerEditline(debugger, type,
27012b93ac6SEd Maste                         StreamFileSP(), // Inherit input from top input reader
27112b93ac6SEd Maste                         StreamFileSP(), // Inherit output from top input reader
27212b93ac6SEd Maste                         StreamFileSP(), // Inherit error from top input reader
27312b93ac6SEd Maste                         0,              // Flags
27412b93ac6SEd Maste                         editline_name,  // Used for saving history files
275435933ddSDimitry Andric                         prompt, continuation_prompt, multi_line, color_prompts,
276435933ddSDimitry Andric                         line_number_start, delegate) {}
27712b93ac6SEd Maste 
IOHandlerEditline(Debugger & debugger,IOHandler::Type type,const lldb::StreamFileSP & input_sp,const lldb::StreamFileSP & output_sp,const lldb::StreamFileSP & error_sp,uint32_t flags,const char * editline_name,llvm::StringRef prompt,llvm::StringRef continuation_prompt,bool multi_line,bool color_prompts,uint32_t line_number_start,IOHandlerDelegate & delegate)278435933ddSDimitry Andric IOHandlerEditline::IOHandlerEditline(
279435933ddSDimitry Andric     Debugger &debugger, IOHandler::Type type,
280435933ddSDimitry Andric     const lldb::StreamFileSP &input_sp, const lldb::StreamFileSP &output_sp,
281435933ddSDimitry Andric     const lldb::StreamFileSP &error_sp, uint32_t flags,
28212b93ac6SEd Maste     const char *editline_name, // Used for saving history files
283435933ddSDimitry Andric     llvm::StringRef prompt, llvm::StringRef continuation_prompt,
284435933ddSDimitry Andric     bool multi_line, bool color_prompts, uint32_t line_number_start,
285435933ddSDimitry Andric     IOHandlerDelegate &delegate)
286435933ddSDimitry Andric     : IOHandler(debugger, type, input_sp, output_sp, error_sp, flags),
2877aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
28812b93ac6SEd Maste       m_editline_ap(),
2897aa51b79SEd Maste #endif
290435933ddSDimitry Andric       m_delegate(delegate), m_prompt(), m_continuation_prompt(),
291435933ddSDimitry Andric       m_current_lines_ptr(nullptr), m_base_line_number(line_number_start),
292435933ddSDimitry Andric       m_curr_line_idx(UINT32_MAX), m_multi_line(multi_line),
293435933ddSDimitry Andric       m_color_prompts(color_prompts), m_interrupt_exits(true),
294435933ddSDimitry Andric       m_editing(false) {
29512b93ac6SEd Maste   SetPrompt(prompt);
29612b93ac6SEd Maste 
2977aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
29812b93ac6SEd Maste   bool use_editline = false;
29912b93ac6SEd Maste 
30012b93ac6SEd Maste   use_editline = m_input_sp->GetFile().GetIsRealTerminal();
30112b93ac6SEd Maste 
302435933ddSDimitry Andric   if (use_editline) {
303435933ddSDimitry Andric     m_editline_ap.reset(new Editline(editline_name, GetInputFILE(),
304435933ddSDimitry Andric                                      GetOutputFILE(), GetErrorFILE(),
3057aa51b79SEd Maste                                      m_color_prompts));
3067aa51b79SEd Maste     m_editline_ap->SetIsInputCompleteCallback(IsInputCompleteCallback, this);
30712b93ac6SEd Maste     m_editline_ap->SetAutoCompleteCallback(AutoCompleteCallback, this);
3087aa51b79SEd Maste     // See if the delegate supports fixing indentation
3097aa51b79SEd Maste     const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters();
310435933ddSDimitry Andric     if (indent_chars) {
311435933ddSDimitry Andric       // The delegate does support indentation, hook it up so when any
3124ba319b5SDimitry Andric       // indentation character is typed, the delegate gets a chance to fix it
313435933ddSDimitry Andric       m_editline_ap->SetFixIndentationCallback(FixIndentationCallback, this,
314435933ddSDimitry Andric                                                indent_chars);
31512b93ac6SEd Maste     }
3167aa51b79SEd Maste   }
3177aa51b79SEd Maste #endif
3187aa51b79SEd Maste   SetBaseLineNumber(m_base_line_number);
319435933ddSDimitry Andric   SetPrompt(prompt);
3207aa51b79SEd Maste   SetContinuationPrompt(continuation_prompt);
32112b93ac6SEd Maste }
32212b93ac6SEd Maste 
~IOHandlerEditline()323435933ddSDimitry Andric IOHandlerEditline::~IOHandlerEditline() {
3247aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
32512b93ac6SEd Maste   m_editline_ap.reset();
3267aa51b79SEd Maste #endif
3277aa51b79SEd Maste }
3287aa51b79SEd Maste 
Activate()329435933ddSDimitry Andric void IOHandlerEditline::Activate() {
3307aa51b79SEd Maste   IOHandler::Activate();
3317aa51b79SEd Maste   m_delegate.IOHandlerActivated(*this);
3327aa51b79SEd Maste }
3337aa51b79SEd Maste 
Deactivate()334435933ddSDimitry Andric void IOHandlerEditline::Deactivate() {
3357aa51b79SEd Maste   IOHandler::Deactivate();
3367aa51b79SEd Maste   m_delegate.IOHandlerDeactivated(*this);
33712b93ac6SEd Maste }
33812b93ac6SEd Maste 
GetLine(std::string & line,bool & interrupted)339435933ddSDimitry Andric bool IOHandlerEditline::GetLine(std::string &line, bool &interrupted) {
3407aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
341435933ddSDimitry Andric   if (m_editline_ap) {
3427aa51b79SEd Maste     return m_editline_ap->GetLine(line, interrupted);
343435933ddSDimitry Andric   } else {
3447aa51b79SEd Maste #endif
34512b93ac6SEd Maste     line.clear();
34612b93ac6SEd Maste 
34712b93ac6SEd Maste     FILE *in = GetInputFILE();
348435933ddSDimitry Andric     if (in) {
349435933ddSDimitry Andric       if (GetIsInteractive()) {
3504bb0738eSEd Maste         const char *prompt = nullptr;
3517aa51b79SEd Maste 
3527aa51b79SEd Maste         if (m_multi_line && m_curr_line_idx > 0)
3537aa51b79SEd Maste           prompt = GetContinuationPrompt();
3547aa51b79SEd Maste 
3554bb0738eSEd Maste         if (prompt == nullptr)
3567aa51b79SEd Maste           prompt = GetPrompt();
3577aa51b79SEd Maste 
358435933ddSDimitry Andric         if (prompt && prompt[0]) {
35912b93ac6SEd Maste           FILE *out = GetOutputFILE();
360435933ddSDimitry Andric           if (out) {
36112b93ac6SEd Maste             ::fprintf(out, "%s", prompt);
36212b93ac6SEd Maste             ::fflush(out);
36312b93ac6SEd Maste           }
36412b93ac6SEd Maste         }
36512b93ac6SEd Maste       }
36612b93ac6SEd Maste       char buffer[256];
36712b93ac6SEd Maste       bool done = false;
36812b93ac6SEd Maste       bool got_line = false;
3691c3bbb01SEd Maste       m_editing = true;
370435933ddSDimitry Andric       while (!done) {
371435933ddSDimitry Andric         if (fgets(buffer, sizeof(buffer), in) == nullptr) {
3720127ef0fSEd Maste           const int saved_errno = errno;
3730127ef0fSEd Maste           if (feof(in))
37412b93ac6SEd Maste             done = true;
375435933ddSDimitry Andric           else if (ferror(in)) {
3760127ef0fSEd Maste             if (saved_errno != EINTR)
3770127ef0fSEd Maste               done = true;
3780127ef0fSEd Maste           }
379435933ddSDimitry Andric         } else {
38012b93ac6SEd Maste           got_line = true;
38112b93ac6SEd Maste           size_t buffer_len = strlen(buffer);
38212b93ac6SEd Maste           assert(buffer[buffer_len] == '\0');
38312b93ac6SEd Maste           char last_char = buffer[buffer_len - 1];
384435933ddSDimitry Andric           if (last_char == '\r' || last_char == '\n') {
38512b93ac6SEd Maste             done = true;
38612b93ac6SEd Maste             // Strip trailing newlines
387435933ddSDimitry Andric             while (last_char == '\r' || last_char == '\n') {
38812b93ac6SEd Maste               --buffer_len;
38912b93ac6SEd Maste               if (buffer_len == 0)
39012b93ac6SEd Maste                 break;
39112b93ac6SEd Maste               last_char = buffer[buffer_len - 1];
39212b93ac6SEd Maste             }
39312b93ac6SEd Maste           }
39412b93ac6SEd Maste           line.append(buffer, buffer_len);
39512b93ac6SEd Maste         }
39612b93ac6SEd Maste       }
3971c3bbb01SEd Maste       m_editing = false;
3984ba319b5SDimitry Andric       // We might have gotten a newline on a line by itself make sure to return
3994ba319b5SDimitry Andric       // true in this case.
40012b93ac6SEd Maste       return got_line;
401435933ddSDimitry Andric     } else {
40212b93ac6SEd Maste       // No more input file, we are done...
40312b93ac6SEd Maste       SetIsDone(true);
40412b93ac6SEd Maste     }
40512b93ac6SEd Maste     return false;
4067aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
40712b93ac6SEd Maste   }
4087aa51b79SEd Maste #endif
40912b93ac6SEd Maste }
41012b93ac6SEd Maste 
4117aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
IsInputCompleteCallback(Editline * editline,StringList & lines,void * baton)412435933ddSDimitry Andric bool IOHandlerEditline::IsInputCompleteCallback(Editline *editline,
41312b93ac6SEd Maste                                                 StringList &lines,
414435933ddSDimitry Andric                                                 void *baton) {
41512b93ac6SEd Maste   IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton;
416435933ddSDimitry Andric   return editline_reader->m_delegate.IOHandlerIsInputComplete(*editline_reader,
417435933ddSDimitry Andric                                                               lines);
4187aa51b79SEd Maste }
4197aa51b79SEd Maste 
FixIndentationCallback(Editline * editline,const StringList & lines,int cursor_position,void * baton)420435933ddSDimitry Andric int IOHandlerEditline::FixIndentationCallback(Editline *editline,
4217aa51b79SEd Maste                                               const StringList &lines,
4227aa51b79SEd Maste                                               int cursor_position,
423435933ddSDimitry Andric                                               void *baton) {
4247aa51b79SEd Maste   IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton;
425435933ddSDimitry Andric   return editline_reader->m_delegate.IOHandlerFixIndentation(
426435933ddSDimitry Andric       *editline_reader, lines, cursor_position);
42712b93ac6SEd Maste }
42812b93ac6SEd Maste 
AutoCompleteCallback(const char * current_line,const char * cursor,const char * last_char,int skip_first_n_matches,int max_matches,StringList & matches,StringList & descriptions,void * baton)429*b5893f02SDimitry Andric int IOHandlerEditline::AutoCompleteCallback(
430*b5893f02SDimitry Andric     const char *current_line, const char *cursor, const char *last_char,
431*b5893f02SDimitry Andric     int skip_first_n_matches, int max_matches, StringList &matches,
432*b5893f02SDimitry Andric     StringList &descriptions, void *baton) {
43312b93ac6SEd Maste   IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton;
43412b93ac6SEd Maste   if (editline_reader)
435435933ddSDimitry Andric     return editline_reader->m_delegate.IOHandlerComplete(
436435933ddSDimitry Andric         *editline_reader, current_line, cursor, last_char, skip_first_n_matches,
437*b5893f02SDimitry Andric         max_matches, matches, descriptions);
43812b93ac6SEd Maste   return 0;
43912b93ac6SEd Maste }
4407aa51b79SEd Maste #endif
44112b93ac6SEd Maste 
GetPrompt()442435933ddSDimitry Andric const char *IOHandlerEditline::GetPrompt() {
4437aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
444435933ddSDimitry Andric   if (m_editline_ap) {
44512b93ac6SEd Maste     return m_editline_ap->GetPrompt();
446435933ddSDimitry Andric   } else {
4477aa51b79SEd Maste #endif
4487aa51b79SEd Maste     if (m_prompt.empty())
4494bb0738eSEd Maste       return nullptr;
4507aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
4517aa51b79SEd Maste   }
4527aa51b79SEd Maste #endif
45312b93ac6SEd Maste   return m_prompt.c_str();
45412b93ac6SEd Maste }
45512b93ac6SEd Maste 
SetPrompt(llvm::StringRef prompt)456435933ddSDimitry Andric bool IOHandlerEditline::SetPrompt(llvm::StringRef prompt) {
457435933ddSDimitry Andric   m_prompt = prompt;
458435933ddSDimitry Andric 
4597aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
46012b93ac6SEd Maste   if (m_editline_ap)
4614bb0738eSEd Maste     m_editline_ap->SetPrompt(m_prompt.empty() ? nullptr : m_prompt.c_str());
4627aa51b79SEd Maste #endif
46312b93ac6SEd Maste   return true;
46412b93ac6SEd Maste }
46512b93ac6SEd Maste 
GetContinuationPrompt()466435933ddSDimitry Andric const char *IOHandlerEditline::GetContinuationPrompt() {
467435933ddSDimitry Andric   return (m_continuation_prompt.empty() ? nullptr
468435933ddSDimitry Andric                                         : m_continuation_prompt.c_str());
4697aa51b79SEd Maste }
4707aa51b79SEd Maste 
SetContinuationPrompt(llvm::StringRef prompt)471435933ddSDimitry Andric void IOHandlerEditline::SetContinuationPrompt(llvm::StringRef prompt) {
472435933ddSDimitry Andric   m_continuation_prompt = prompt;
4737aa51b79SEd Maste 
4747aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
4757aa51b79SEd Maste   if (m_editline_ap)
476435933ddSDimitry Andric     m_editline_ap->SetContinuationPrompt(m_continuation_prompt.empty()
477435933ddSDimitry Andric                                              ? nullptr
478435933ddSDimitry Andric                                              : m_continuation_prompt.c_str());
4797aa51b79SEd Maste #endif
4807aa51b79SEd Maste }
4817aa51b79SEd Maste 
SetBaseLineNumber(uint32_t line)482435933ddSDimitry Andric void IOHandlerEditline::SetBaseLineNumber(uint32_t line) {
4830127ef0fSEd Maste   m_base_line_number = line;
4840127ef0fSEd Maste }
4857aa51b79SEd Maste 
GetCurrentLineIndex() const486435933ddSDimitry Andric uint32_t IOHandlerEditline::GetCurrentLineIndex() const {
4877aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
4887aa51b79SEd Maste   if (m_editline_ap)
4897aa51b79SEd Maste     return m_editline_ap->GetCurrentLine();
4907aa51b79SEd Maste #endif
4917aa51b79SEd Maste   return m_curr_line_idx;
4927aa51b79SEd Maste }
4937aa51b79SEd Maste 
GetLines(StringList & lines,bool & interrupted)494435933ddSDimitry Andric bool IOHandlerEditline::GetLines(StringList &lines, bool &interrupted) {
4957aa51b79SEd Maste   m_current_lines_ptr = &lines;
4967aa51b79SEd Maste 
49712b93ac6SEd Maste   bool success = false;
4987aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
499435933ddSDimitry Andric   if (m_editline_ap) {
5007aa51b79SEd Maste     return m_editline_ap->GetLines(m_base_line_number, lines, interrupted);
501435933ddSDimitry Andric   } else {
5027aa51b79SEd Maste #endif
5037aa51b79SEd Maste     bool done = false;
5045517e702SDimitry Andric     Status error;
50512b93ac6SEd Maste 
506435933ddSDimitry Andric     while (!done) {
5070127ef0fSEd Maste       // Show line numbers if we are asked to
50812b93ac6SEd Maste       std::string line;
509435933ddSDimitry Andric       if (m_base_line_number > 0 && GetIsInteractive()) {
5100127ef0fSEd Maste         FILE *out = GetOutputFILE();
5110127ef0fSEd Maste         if (out)
512435933ddSDimitry Andric           ::fprintf(out, "%u%s", m_base_line_number + (uint32_t)lines.GetSize(),
513435933ddSDimitry Andric                     GetPrompt() == nullptr ? " " : "");
5140127ef0fSEd Maste       }
5150127ef0fSEd Maste 
5167aa51b79SEd Maste       m_curr_line_idx = lines.GetSize();
5177aa51b79SEd Maste 
5180127ef0fSEd Maste       bool interrupted = false;
519435933ddSDimitry Andric       if (GetLine(line, interrupted) && !interrupted) {
52012b93ac6SEd Maste         lines.AppendString(line);
5217aa51b79SEd Maste         done = m_delegate.IOHandlerIsInputComplete(*this, lines);
522435933ddSDimitry Andric       } else {
5237aa51b79SEd Maste         done = true;
52412b93ac6SEd Maste       }
52512b93ac6SEd Maste     }
52612b93ac6SEd Maste     success = lines.GetSize() > 0;
5277aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
52812b93ac6SEd Maste   }
5297aa51b79SEd Maste #endif
53012b93ac6SEd Maste   return success;
53112b93ac6SEd Maste }
53212b93ac6SEd Maste 
5334ba319b5SDimitry Andric // Each IOHandler gets to run until it is done. It should read data from the
5344ba319b5SDimitry Andric // "in" and place output into "out" and "err and return when done.
Run()535435933ddSDimitry Andric void IOHandlerEditline::Run() {
53612b93ac6SEd Maste   std::string line;
537435933ddSDimitry Andric   while (IsActive()) {
5380127ef0fSEd Maste     bool interrupted = false;
539435933ddSDimitry Andric     if (m_multi_line) {
54012b93ac6SEd Maste       StringList lines;
541435933ddSDimitry Andric       if (GetLines(lines, interrupted)) {
542435933ddSDimitry Andric         if (interrupted) {
5437aa51b79SEd Maste           m_done = m_interrupt_exits;
5447aa51b79SEd Maste           m_delegate.IOHandlerInputInterrupted(*this, line);
5457aa51b79SEd Maste 
546435933ddSDimitry Andric         } else {
54712b93ac6SEd Maste           line = lines.CopyList();
54812b93ac6SEd Maste           m_delegate.IOHandlerInputComplete(*this, line);
54912b93ac6SEd Maste         }
550435933ddSDimitry Andric       } else {
55112b93ac6SEd Maste         m_done = true;
55212b93ac6SEd Maste       }
553435933ddSDimitry Andric     } else {
554435933ddSDimitry Andric       if (GetLine(line, interrupted)) {
5557aa51b79SEd Maste         if (interrupted)
5567aa51b79SEd Maste           m_delegate.IOHandlerInputInterrupted(*this, line);
5577aa51b79SEd Maste         else
55812b93ac6SEd Maste           m_delegate.IOHandlerInputComplete(*this, line);
559435933ddSDimitry Andric       } else {
56012b93ac6SEd Maste         m_done = true;
56112b93ac6SEd Maste       }
56212b93ac6SEd Maste     }
56312b93ac6SEd Maste   }
56412b93ac6SEd Maste }
56512b93ac6SEd Maste 
Cancel()566435933ddSDimitry Andric void IOHandlerEditline::Cancel() {
5677aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
5686fcb8242SEd Maste   if (m_editline_ap)
5691c3bbb01SEd Maste     m_editline_ap->Cancel();
5707aa51b79SEd Maste #endif
5716fcb8242SEd Maste }
5726fcb8242SEd Maste 
Interrupt()573435933ddSDimitry Andric bool IOHandlerEditline::Interrupt() {
5740127ef0fSEd Maste   // Let the delgate handle it first
5750127ef0fSEd Maste   if (m_delegate.IOHandlerInterrupt(*this))
5760127ef0fSEd Maste     return true;
5770127ef0fSEd Maste 
5787aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
57912b93ac6SEd Maste   if (m_editline_ap)
5800127ef0fSEd Maste     return m_editline_ap->Interrupt();
5817aa51b79SEd Maste #endif
5820127ef0fSEd Maste   return false;
58312b93ac6SEd Maste }
58412b93ac6SEd Maste 
GotEOF()585435933ddSDimitry Andric void IOHandlerEditline::GotEOF() {
5867aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
58712b93ac6SEd Maste   if (m_editline_ap)
58812b93ac6SEd Maste     m_editline_ap->Interrupt();
5897aa51b79SEd Maste #endif
59012b93ac6SEd Maste }
59112b93ac6SEd Maste 
PrintAsync(Stream * stream,const char * s,size_t len)592435933ddSDimitry Andric void IOHandlerEditline::PrintAsync(Stream *stream, const char *s, size_t len) {
5931c3bbb01SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
5941c3bbb01SEd Maste   if (m_editline_ap)
5951c3bbb01SEd Maste     m_editline_ap->PrintAsync(stream, s, len);
5961c3bbb01SEd Maste   else
5971c3bbb01SEd Maste #endif
5984bb0738eSEd Maste   {
5994bb0738eSEd Maste #ifdef _MSC_VER
600435933ddSDimitry Andric     const char *prompt = GetPrompt();
601435933ddSDimitry Andric     if (prompt) {
6024bb0738eSEd Maste       // Back up over previous prompt using Windows API
6034bb0738eSEd Maste       CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info;
6044bb0738eSEd Maste       HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
6054bb0738eSEd Maste       GetConsoleScreenBufferInfo(console_handle, &screen_buffer_info);
6064bb0738eSEd Maste       COORD coord = screen_buffer_info.dwCursorPosition;
6074bb0738eSEd Maste       coord.X -= strlen(prompt);
6084bb0738eSEd Maste       if (coord.X < 0)
6094bb0738eSEd Maste         coord.X = 0;
6104bb0738eSEd Maste       SetConsoleCursorPosition(console_handle, coord);
6114bb0738eSEd Maste     }
6124bb0738eSEd Maste #endif
6131c3bbb01SEd Maste     IOHandler::PrintAsync(stream, s, len);
614435933ddSDimitry Andric #ifdef _MSC_VER
6154bb0738eSEd Maste     if (prompt)
616435933ddSDimitry Andric       IOHandler::PrintAsync(GetOutputStreamFile().get(), prompt,
617435933ddSDimitry Andric                             strlen(prompt));
618435933ddSDimitry Andric #endif
6194bb0738eSEd Maste   }
6201c3bbb01SEd Maste }
6211c3bbb01SEd Maste 
6224ba319b5SDimitry Andric // we may want curses to be disabled for some builds for instance, windows
62312b93ac6SEd Maste #ifndef LLDB_DISABLE_CURSES
62412b93ac6SEd Maste 
62512b93ac6SEd Maste #define KEY_RETURN 10
62612b93ac6SEd Maste #define KEY_ESCAPE 27
62712b93ac6SEd Maste 
628435933ddSDimitry Andric namespace curses {
62912b93ac6SEd Maste class Menu;
63012b93ac6SEd Maste class MenuDelegate;
63112b93ac6SEd Maste class Window;
63212b93ac6SEd Maste class WindowDelegate;
63312b93ac6SEd Maste typedef std::shared_ptr<Menu> MenuSP;
63412b93ac6SEd Maste typedef std::shared_ptr<MenuDelegate> MenuDelegateSP;
63512b93ac6SEd Maste typedef std::shared_ptr<Window> WindowSP;
63612b93ac6SEd Maste typedef std::shared_ptr<WindowDelegate> WindowDelegateSP;
63712b93ac6SEd Maste typedef std::vector<MenuSP> Menus;
63812b93ac6SEd Maste typedef std::vector<WindowSP> Windows;
63912b93ac6SEd Maste typedef std::vector<WindowDelegateSP> WindowDelegates;
64012b93ac6SEd Maste 
64112b93ac6SEd Maste #if 0
64212b93ac6SEd Maste type summary add -s "x=${var.x}, y=${var.y}" curses::Point
64312b93ac6SEd Maste type summary add -s "w=${var.width}, h=${var.height}" curses::Size
64412b93ac6SEd Maste type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect
64512b93ac6SEd Maste #endif
6469f2f44ceSEd Maste 
647435933ddSDimitry Andric struct Point {
64812b93ac6SEd Maste   int x;
64912b93ac6SEd Maste   int y;
65012b93ac6SEd Maste 
Pointcurses::Point651435933ddSDimitry Andric   Point(int _x = 0, int _y = 0) : x(_x), y(_y) {}
65212b93ac6SEd Maste 
Clearcurses::Point653435933ddSDimitry Andric   void Clear() {
65412b93ac6SEd Maste     x = 0;
65512b93ac6SEd Maste     y = 0;
65612b93ac6SEd Maste   }
65712b93ac6SEd Maste 
operator +=curses::Point658435933ddSDimitry Andric   Point &operator+=(const Point &rhs) {
65912b93ac6SEd Maste     x += rhs.x;
66012b93ac6SEd Maste     y += rhs.y;
66112b93ac6SEd Maste     return *this;
66212b93ac6SEd Maste   }
66312b93ac6SEd Maste 
Dumpcurses::Point664435933ddSDimitry Andric   void Dump() { printf("(x=%i, y=%i)\n", x, y); }
66512b93ac6SEd Maste };
66612b93ac6SEd Maste 
operator ==(const Point & lhs,const Point & rhs)667435933ddSDimitry Andric bool operator==(const Point &lhs, const Point &rhs) {
66812b93ac6SEd Maste   return lhs.x == rhs.x && lhs.y == rhs.y;
66912b93ac6SEd Maste }
6709f2f44ceSEd Maste 
operator !=(const Point & lhs,const Point & rhs)671435933ddSDimitry Andric bool operator!=(const Point &lhs, const Point &rhs) {
67212b93ac6SEd Maste   return lhs.x != rhs.x || lhs.y != rhs.y;
67312b93ac6SEd Maste }
67412b93ac6SEd Maste 
675435933ddSDimitry Andric struct Size {
67612b93ac6SEd Maste   int width;
67712b93ac6SEd Maste   int height;
Sizecurses::Size678435933ddSDimitry Andric   Size(int w = 0, int h = 0) : width(w), height(h) {}
67912b93ac6SEd Maste 
Clearcurses::Size680435933ddSDimitry Andric   void Clear() {
68112b93ac6SEd Maste     width = 0;
68212b93ac6SEd Maste     height = 0;
68312b93ac6SEd Maste   }
68412b93ac6SEd Maste 
Dumpcurses::Size685435933ddSDimitry Andric   void Dump() { printf("(w=%i, h=%i)\n", width, height); }
68612b93ac6SEd Maste };
68712b93ac6SEd Maste 
operator ==(const Size & lhs,const Size & rhs)688435933ddSDimitry Andric bool operator==(const Size &lhs, const Size &rhs) {
68912b93ac6SEd Maste   return lhs.width == rhs.width && lhs.height == rhs.height;
69012b93ac6SEd Maste }
6919f2f44ceSEd Maste 
operator !=(const Size & lhs,const Size & rhs)692435933ddSDimitry Andric bool operator!=(const Size &lhs, const Size &rhs) {
69312b93ac6SEd Maste   return lhs.width != rhs.width || lhs.height != rhs.height;
69412b93ac6SEd Maste }
69512b93ac6SEd Maste 
696435933ddSDimitry Andric struct Rect {
69712b93ac6SEd Maste   Point origin;
69812b93ac6SEd Maste   Size size;
69912b93ac6SEd Maste 
Rectcurses::Rect700435933ddSDimitry Andric   Rect() : origin(), size() {}
70112b93ac6SEd Maste 
Rectcurses::Rect702435933ddSDimitry Andric   Rect(const Point &p, const Size &s) : origin(p), size(s) {}
70312b93ac6SEd Maste 
Clearcurses::Rect704435933ddSDimitry Andric   void Clear() {
70512b93ac6SEd Maste     origin.Clear();
70612b93ac6SEd Maste     size.Clear();
70712b93ac6SEd Maste   }
70812b93ac6SEd Maste 
Dumpcurses::Rect709435933ddSDimitry Andric   void Dump() {
710435933ddSDimitry Andric     printf("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width,
711435933ddSDimitry Andric            size.height);
71212b93ac6SEd Maste   }
71312b93ac6SEd Maste 
Insetcurses::Rect714435933ddSDimitry Andric   void Inset(int w, int h) {
71512b93ac6SEd Maste     if (size.width > w * 2)
71612b93ac6SEd Maste       size.width -= w * 2;
71712b93ac6SEd Maste     origin.x += w;
71812b93ac6SEd Maste 
71912b93ac6SEd Maste     if (size.height > h * 2)
72012b93ac6SEd Maste       size.height -= h * 2;
72112b93ac6SEd Maste     origin.y += h;
72212b93ac6SEd Maste   }
7239f2f44ceSEd Maste 
7244ba319b5SDimitry Andric   // Return a status bar rectangle which is the last line of this rectangle.
7254ba319b5SDimitry Andric   // This rectangle will be modified to not include the status bar area.
MakeStatusBarcurses::Rect726435933ddSDimitry Andric   Rect MakeStatusBar() {
72712b93ac6SEd Maste     Rect status_bar;
728435933ddSDimitry Andric     if (size.height > 1) {
72912b93ac6SEd Maste       status_bar.origin.x = origin.x;
73012b93ac6SEd Maste       status_bar.origin.y = size.height;
73112b93ac6SEd Maste       status_bar.size.width = size.width;
73212b93ac6SEd Maste       status_bar.size.height = 1;
73312b93ac6SEd Maste       --size.height;
73412b93ac6SEd Maste     }
73512b93ac6SEd Maste     return status_bar;
73612b93ac6SEd Maste   }
73712b93ac6SEd Maste 
7384ba319b5SDimitry Andric   // Return a menubar rectangle which is the first line of this rectangle. This
7394ba319b5SDimitry Andric   // rectangle will be modified to not include the menubar area.
MakeMenuBarcurses::Rect740435933ddSDimitry Andric   Rect MakeMenuBar() {
74112b93ac6SEd Maste     Rect menubar;
742435933ddSDimitry Andric     if (size.height > 1) {
74312b93ac6SEd Maste       menubar.origin.x = origin.x;
74412b93ac6SEd Maste       menubar.origin.y = origin.y;
74512b93ac6SEd Maste       menubar.size.width = size.width;
74612b93ac6SEd Maste       menubar.size.height = 1;
74712b93ac6SEd Maste       ++origin.y;
74812b93ac6SEd Maste       --size.height;
74912b93ac6SEd Maste     }
75012b93ac6SEd Maste     return menubar;
75112b93ac6SEd Maste   }
75212b93ac6SEd Maste 
HorizontalSplitPercentagecurses::Rect753435933ddSDimitry Andric   void HorizontalSplitPercentage(float top_percentage, Rect &top,
754435933ddSDimitry Andric                                  Rect &bottom) const {
75512b93ac6SEd Maste     float top_height = top_percentage * size.height;
75612b93ac6SEd Maste     HorizontalSplit(top_height, top, bottom);
75712b93ac6SEd Maste   }
75812b93ac6SEd Maste 
HorizontalSplitcurses::Rect759435933ddSDimitry Andric   void HorizontalSplit(int top_height, Rect &top, Rect &bottom) const {
76012b93ac6SEd Maste     top = *this;
761435933ddSDimitry Andric     if (top_height < size.height) {
76212b93ac6SEd Maste       top.size.height = top_height;
76312b93ac6SEd Maste       bottom.origin.x = origin.x;
76412b93ac6SEd Maste       bottom.origin.y = origin.y + top.size.height;
76512b93ac6SEd Maste       bottom.size.width = size.width;
76612b93ac6SEd Maste       bottom.size.height = size.height - top.size.height;
767435933ddSDimitry Andric     } else {
76812b93ac6SEd Maste       bottom.Clear();
76912b93ac6SEd Maste     }
77012b93ac6SEd Maste   }
77112b93ac6SEd Maste 
VerticalSplitPercentagecurses::Rect772435933ddSDimitry Andric   void VerticalSplitPercentage(float left_percentage, Rect &left,
773435933ddSDimitry Andric                                Rect &right) const {
77412b93ac6SEd Maste     float left_width = left_percentage * size.width;
77512b93ac6SEd Maste     VerticalSplit(left_width, left, right);
77612b93ac6SEd Maste   }
77712b93ac6SEd Maste 
VerticalSplitcurses::Rect778435933ddSDimitry Andric   void VerticalSplit(int left_width, Rect &left, Rect &right) const {
77912b93ac6SEd Maste     left = *this;
780435933ddSDimitry Andric     if (left_width < size.width) {
78112b93ac6SEd Maste       left.size.width = left_width;
78212b93ac6SEd Maste       right.origin.x = origin.x + left.size.width;
78312b93ac6SEd Maste       right.origin.y = origin.y;
78412b93ac6SEd Maste       right.size.width = size.width - left.size.width;
78512b93ac6SEd Maste       right.size.height = size.height;
786435933ddSDimitry Andric     } else {
78712b93ac6SEd Maste       right.Clear();
78812b93ac6SEd Maste     }
78912b93ac6SEd Maste   }
79012b93ac6SEd Maste };
79112b93ac6SEd Maste 
operator ==(const Rect & lhs,const Rect & rhs)792435933ddSDimitry Andric bool operator==(const Rect &lhs, const Rect &rhs) {
79312b93ac6SEd Maste   return lhs.origin == rhs.origin && lhs.size == rhs.size;
79412b93ac6SEd Maste }
7959f2f44ceSEd Maste 
operator !=(const Rect & lhs,const Rect & rhs)796435933ddSDimitry Andric bool operator!=(const Rect &lhs, const Rect &rhs) {
79712b93ac6SEd Maste   return lhs.origin != rhs.origin || lhs.size != rhs.size;
79812b93ac6SEd Maste }
79912b93ac6SEd Maste 
800435933ddSDimitry Andric enum HandleCharResult {
80112b93ac6SEd Maste   eKeyNotHandled = 0,
80212b93ac6SEd Maste   eKeyHandled = 1,
80312b93ac6SEd Maste   eQuitApplication = 2
80412b93ac6SEd Maste };
80512b93ac6SEd Maste 
806435933ddSDimitry Andric enum class MenuActionResult {
80712b93ac6SEd Maste   Handled,
80812b93ac6SEd Maste   NotHandled,
80912b93ac6SEd Maste   Quit // Exit all menus and quit
81012b93ac6SEd Maste };
81112b93ac6SEd Maste 
812435933ddSDimitry Andric struct KeyHelp {
81312b93ac6SEd Maste   int ch;
81412b93ac6SEd Maste   const char *description;
81512b93ac6SEd Maste };
81612b93ac6SEd Maste 
817435933ddSDimitry Andric class WindowDelegate {
81812b93ac6SEd Maste public:
819435933ddSDimitry Andric   virtual ~WindowDelegate() = default;
82012b93ac6SEd Maste 
WindowDelegateDraw(Window & window,bool force)821435933ddSDimitry Andric   virtual bool WindowDelegateDraw(Window &window, bool force) {
82212b93ac6SEd Maste     return false; // Drawing not handled
82312b93ac6SEd Maste   }
82412b93ac6SEd Maste 
WindowDelegateHandleChar(Window & window,int key)825435933ddSDimitry Andric   virtual HandleCharResult WindowDelegateHandleChar(Window &window, int key) {
82612b93ac6SEd Maste     return eKeyNotHandled;
82712b93ac6SEd Maste   }
82812b93ac6SEd Maste 
WindowDelegateGetHelpText()829435933ddSDimitry Andric   virtual const char *WindowDelegateGetHelpText() { return nullptr; }
83012b93ac6SEd Maste 
WindowDelegateGetKeyHelp()831435933ddSDimitry Andric   virtual KeyHelp *WindowDelegateGetKeyHelp() { return nullptr; }
83212b93ac6SEd Maste };
83312b93ac6SEd Maste 
834435933ddSDimitry Andric class HelpDialogDelegate : public WindowDelegate {
83512b93ac6SEd Maste public:
83612b93ac6SEd Maste   HelpDialogDelegate(const char *text, KeyHelp *key_help_array);
83712b93ac6SEd Maste 
8381c3bbb01SEd Maste   ~HelpDialogDelegate() override;
83912b93ac6SEd Maste 
840435933ddSDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override;
84112b93ac6SEd Maste 
842435933ddSDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override;
84312b93ac6SEd Maste 
GetNumLines() const844435933ddSDimitry Andric   size_t GetNumLines() const { return m_text.GetSize(); }
84512b93ac6SEd Maste 
GetMaxLineLength() const846435933ddSDimitry Andric   size_t GetMaxLineLength() const { return m_text.GetMaxStringLength(); }
84712b93ac6SEd Maste 
84812b93ac6SEd Maste protected:
84912b93ac6SEd Maste   StringList m_text;
85012b93ac6SEd Maste   int m_first_visible_line;
85112b93ac6SEd Maste };
85212b93ac6SEd Maste 
853435933ddSDimitry Andric class Window {
85412b93ac6SEd Maste public:
Window(const char * name)855435933ddSDimitry Andric   Window(const char *name)
856435933ddSDimitry Andric       : m_name(name), m_window(nullptr), m_panel(nullptr), m_parent(nullptr),
857435933ddSDimitry Andric         m_subwindows(), m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX),
858435933ddSDimitry Andric         m_prev_active_window_idx(UINT32_MAX), m_delete(false),
859435933ddSDimitry Andric         m_needs_update(true), m_can_activate(true), m_is_subwin(false) {}
86012b93ac6SEd Maste 
Window(const char * name,WINDOW * w,bool del=true)861435933ddSDimitry Andric   Window(const char *name, WINDOW *w, bool del = true)
862435933ddSDimitry Andric       : m_name(name), m_window(nullptr), m_panel(nullptr), m_parent(nullptr),
863435933ddSDimitry Andric         m_subwindows(), m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX),
864435933ddSDimitry Andric         m_prev_active_window_idx(UINT32_MAX), m_delete(del),
865435933ddSDimitry Andric         m_needs_update(true), m_can_activate(true), m_is_subwin(false) {
86612b93ac6SEd Maste     if (w)
86712b93ac6SEd Maste       Reset(w);
86812b93ac6SEd Maste   }
86912b93ac6SEd Maste 
Window(const char * name,const Rect & bounds)870435933ddSDimitry Andric   Window(const char *name, const Rect &bounds)
871435933ddSDimitry Andric       : m_name(name), m_window(nullptr), m_parent(nullptr), m_subwindows(),
872435933ddSDimitry Andric         m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX),
873435933ddSDimitry Andric         m_prev_active_window_idx(UINT32_MAX), m_delete(true),
874435933ddSDimitry Andric         m_needs_update(true), m_can_activate(true), m_is_subwin(false) {
875435933ddSDimitry Andric     Reset(::newwin(bounds.size.height, bounds.size.width, bounds.origin.y,
876435933ddSDimitry Andric                    bounds.origin.y));
87712b93ac6SEd Maste   }
87812b93ac6SEd Maste 
~Window()879435933ddSDimitry Andric   virtual ~Window() {
88012b93ac6SEd Maste     RemoveSubWindows();
88112b93ac6SEd Maste     Reset();
88212b93ac6SEd Maste   }
88312b93ac6SEd Maste 
Reset(WINDOW * w=nullptr,bool del=true)884435933ddSDimitry Andric   void Reset(WINDOW *w = nullptr, bool del = true) {
88512b93ac6SEd Maste     if (m_window == w)
88612b93ac6SEd Maste       return;
88712b93ac6SEd Maste 
888435933ddSDimitry Andric     if (m_panel) {
88912b93ac6SEd Maste       ::del_panel(m_panel);
8904bb0738eSEd Maste       m_panel = nullptr;
89112b93ac6SEd Maste     }
892435933ddSDimitry Andric     if (m_window && m_delete) {
89312b93ac6SEd Maste       ::delwin(m_window);
8944bb0738eSEd Maste       m_window = nullptr;
89512b93ac6SEd Maste       m_delete = false;
89612b93ac6SEd Maste     }
897435933ddSDimitry Andric     if (w) {
89812b93ac6SEd Maste       m_window = w;
89912b93ac6SEd Maste       m_panel = ::new_panel(m_window);
90012b93ac6SEd Maste       m_delete = del;
90112b93ac6SEd Maste     }
90212b93ac6SEd Maste   }
90312b93ac6SEd Maste 
AttributeOn(attr_t attr)90412b93ac6SEd Maste   void AttributeOn(attr_t attr) { ::wattron(m_window, attr); }
AttributeOff(attr_t attr)90512b93ac6SEd Maste   void AttributeOff(attr_t attr) { ::wattroff(m_window, attr); }
Box(chtype v_char=ACS_VLINE,chtype h_char=ACS_HLINE)906435933ddSDimitry Andric   void Box(chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) {
907435933ddSDimitry Andric     ::box(m_window, v_char, h_char);
908435933ddSDimitry Andric   }
Clear()90912b93ac6SEd Maste   void Clear() { ::wclear(m_window); }
Erase()91012b93ac6SEd Maste   void Erase() { ::werase(m_window); }
GetBounds()911435933ddSDimitry Andric   Rect GetBounds() {
912435933ddSDimitry Andric     return Rect(GetParentOrigin(), GetSize());
913435933ddSDimitry Andric   } // Get the rectangle in our parent window
GetChar()91412b93ac6SEd Maste   int GetChar() { return ::wgetch(m_window); }
GetCursorX()91512b93ac6SEd Maste   int GetCursorX() { return getcurx(m_window); }
GetCursorY()91612b93ac6SEd Maste   int GetCursorY() { return getcury(m_window); }
GetFrame()917435933ddSDimitry Andric   Rect GetFrame() {
918435933ddSDimitry Andric     return Rect(Point(), GetSize());
919435933ddSDimitry Andric   } // Get our rectangle in our own coordinate system
GetParentOrigin()92012b93ac6SEd Maste   Point GetParentOrigin() { return Point(GetParentX(), GetParentY()); }
GetSize()92112b93ac6SEd Maste   Size GetSize() { return Size(GetWidth(), GetHeight()); }
GetParentX()92212b93ac6SEd Maste   int GetParentX() { return getparx(m_window); }
GetParentY()92312b93ac6SEd Maste   int GetParentY() { return getpary(m_window); }
GetMaxX()92412b93ac6SEd Maste   int GetMaxX() { return getmaxx(m_window); }
GetMaxY()92512b93ac6SEd Maste   int GetMaxY() { return getmaxy(m_window); }
GetWidth()92612b93ac6SEd Maste   int GetWidth() { return GetMaxX(); }
GetHeight()92712b93ac6SEd Maste   int GetHeight() { return GetMaxY(); }
MoveCursor(int x,int y)92812b93ac6SEd Maste   void MoveCursor(int x, int y) { ::wmove(m_window, y, x); }
MoveWindow(int x,int y)92912b93ac6SEd Maste   void MoveWindow(int x, int y) { MoveWindow(Point(x, y)); }
Resize(int w,int h)93012b93ac6SEd Maste   void Resize(int w, int h) { ::wresize(m_window, h, w); }
Resize(const Size & size)931435933ddSDimitry Andric   void Resize(const Size &size) {
932435933ddSDimitry Andric     ::wresize(m_window, size.height, size.width);
933435933ddSDimitry Andric   }
PutChar(int ch)93412b93ac6SEd Maste   void PutChar(int ch) { ::waddch(m_window, ch); }
PutCString(const char * s,int len=-1)93512b93ac6SEd Maste   void PutCString(const char *s, int len = -1) { ::waddnstr(m_window, s, len); }
Refresh()93612b93ac6SEd Maste   void Refresh() { ::wrefresh(m_window); }
DeferredRefresh()937435933ddSDimitry Andric   void DeferredRefresh() {
93812b93ac6SEd Maste     // We are using panels, so we don't need to call this...
93912b93ac6SEd Maste     //::wnoutrefresh(m_window);
94012b93ac6SEd Maste   }
SetBackground(int color_pair_idx)941435933ddSDimitry Andric   void SetBackground(int color_pair_idx) {
942435933ddSDimitry Andric     ::wbkgd(m_window, COLOR_PAIR(color_pair_idx));
943435933ddSDimitry Andric   }
UnderlineOn()94412b93ac6SEd Maste   void UnderlineOn() { AttributeOn(A_UNDERLINE); }
UnderlineOff()94512b93ac6SEd Maste   void UnderlineOff() { AttributeOff(A_UNDERLINE); }
94612b93ac6SEd Maste 
PutCStringTruncated(const char * s,int right_pad)947435933ddSDimitry Andric   void PutCStringTruncated(const char *s, int right_pad) {
94812b93ac6SEd Maste     int bytes_left = GetWidth() - GetCursorX();
949435933ddSDimitry Andric     if (bytes_left > right_pad) {
95012b93ac6SEd Maste       bytes_left -= right_pad;
95112b93ac6SEd Maste       ::waddnstr(m_window, s, bytes_left);
95212b93ac6SEd Maste     }
95312b93ac6SEd Maste   }
95412b93ac6SEd Maste 
MoveWindow(const Point & origin)955435933ddSDimitry Andric   void MoveWindow(const Point &origin) {
95612b93ac6SEd Maste     const bool moving_window = origin != GetParentOrigin();
957435933ddSDimitry Andric     if (m_is_subwin && moving_window) {
95812b93ac6SEd Maste       // Can't move subwindows, must delete and re-create
95912b93ac6SEd Maste       Size size = GetSize();
960435933ddSDimitry Andric       Reset(::subwin(m_parent->m_window, size.height, size.width, origin.y,
961435933ddSDimitry Andric                      origin.x),
962435933ddSDimitry Andric             true);
963435933ddSDimitry Andric     } else {
96412b93ac6SEd Maste       ::mvwin(m_window, origin.y, origin.x);
96512b93ac6SEd Maste     }
96612b93ac6SEd Maste   }
96712b93ac6SEd Maste 
SetBounds(const Rect & bounds)968435933ddSDimitry Andric   void SetBounds(const Rect &bounds) {
96912b93ac6SEd Maste     const bool moving_window = bounds.origin != GetParentOrigin();
970435933ddSDimitry Andric     if (m_is_subwin && moving_window) {
97112b93ac6SEd Maste       // Can't move subwindows, must delete and re-create
972435933ddSDimitry Andric       Reset(::subwin(m_parent->m_window, bounds.size.height, bounds.size.width,
973435933ddSDimitry Andric                      bounds.origin.y, bounds.origin.x),
974435933ddSDimitry Andric             true);
975435933ddSDimitry Andric     } else {
97612b93ac6SEd Maste       if (moving_window)
97712b93ac6SEd Maste         MoveWindow(bounds.origin);
97812b93ac6SEd Maste       Resize(bounds.size);
97912b93ac6SEd Maste     }
98012b93ac6SEd Maste   }
98112b93ac6SEd Maste 
Printf(const char * format,...)982435933ddSDimitry Andric   void Printf(const char *format, ...) __attribute__((format(printf, 2, 3))) {
98312b93ac6SEd Maste     va_list args;
98412b93ac6SEd Maste     va_start(args, format);
98512b93ac6SEd Maste     vwprintw(m_window, format, args);
98612b93ac6SEd Maste     va_end(args);
98712b93ac6SEd Maste   }
98812b93ac6SEd Maste 
Touch()989435933ddSDimitry Andric   void Touch() {
99012b93ac6SEd Maste     ::touchwin(m_window);
99112b93ac6SEd Maste     if (m_parent)
99212b93ac6SEd Maste       m_parent->Touch();
99312b93ac6SEd Maste   }
99412b93ac6SEd Maste 
CreateSubWindow(const char * name,const Rect & bounds,bool make_active)995435933ddSDimitry Andric   WindowSP CreateSubWindow(const char *name, const Rect &bounds,
996435933ddSDimitry Andric                            bool make_active) {
99712b93ac6SEd Maste     WindowSP subwindow_sp;
998435933ddSDimitry Andric     if (m_window) {
999435933ddSDimitry Andric       subwindow_sp.reset(new Window(
1000435933ddSDimitry Andric           name, ::subwin(m_window, bounds.size.height, bounds.size.width,
1001435933ddSDimitry Andric                          bounds.origin.y, bounds.origin.x),
1002435933ddSDimitry Andric           true));
100312b93ac6SEd Maste       subwindow_sp->m_is_subwin = true;
1004435933ddSDimitry Andric     } else {
1005435933ddSDimitry Andric       subwindow_sp.reset(
1006435933ddSDimitry Andric           new Window(name, ::newwin(bounds.size.height, bounds.size.width,
1007435933ddSDimitry Andric                                     bounds.origin.y, bounds.origin.x),
1008435933ddSDimitry Andric                      true));
100912b93ac6SEd Maste       subwindow_sp->m_is_subwin = false;
101012b93ac6SEd Maste     }
101112b93ac6SEd Maste     subwindow_sp->m_parent = this;
1012435933ddSDimitry Andric     if (make_active) {
101312b93ac6SEd Maste       m_prev_active_window_idx = m_curr_active_window_idx;
101412b93ac6SEd Maste       m_curr_active_window_idx = m_subwindows.size();
101512b93ac6SEd Maste     }
101612b93ac6SEd Maste     m_subwindows.push_back(subwindow_sp);
101712b93ac6SEd Maste     ::top_panel(subwindow_sp->m_panel);
101812b93ac6SEd Maste     m_needs_update = true;
101912b93ac6SEd Maste     return subwindow_sp;
102012b93ac6SEd Maste   }
102112b93ac6SEd Maste 
RemoveSubWindow(Window * window)1022435933ddSDimitry Andric   bool RemoveSubWindow(Window *window) {
102312b93ac6SEd Maste     Windows::iterator pos, end = m_subwindows.end();
102412b93ac6SEd Maste     size_t i = 0;
1025435933ddSDimitry Andric     for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) {
1026435933ddSDimitry Andric       if ((*pos).get() == window) {
102712b93ac6SEd Maste         if (m_prev_active_window_idx == i)
102812b93ac6SEd Maste           m_prev_active_window_idx = UINT32_MAX;
1029435933ddSDimitry Andric         else if (m_prev_active_window_idx != UINT32_MAX &&
1030435933ddSDimitry Andric                  m_prev_active_window_idx > i)
103112b93ac6SEd Maste           --m_prev_active_window_idx;
103212b93ac6SEd Maste 
103312b93ac6SEd Maste         if (m_curr_active_window_idx == i)
103412b93ac6SEd Maste           m_curr_active_window_idx = UINT32_MAX;
1035435933ddSDimitry Andric         else if (m_curr_active_window_idx != UINT32_MAX &&
1036435933ddSDimitry Andric                  m_curr_active_window_idx > i)
103712b93ac6SEd Maste           --m_curr_active_window_idx;
103812b93ac6SEd Maste         window->Erase();
103912b93ac6SEd Maste         m_subwindows.erase(pos);
104012b93ac6SEd Maste         m_needs_update = true;
104112b93ac6SEd Maste         if (m_parent)
104212b93ac6SEd Maste           m_parent->Touch();
104312b93ac6SEd Maste         else
104412b93ac6SEd Maste           ::touchwin(stdscr);
104512b93ac6SEd Maste         return true;
104612b93ac6SEd Maste       }
104712b93ac6SEd Maste     }
104812b93ac6SEd Maste     return false;
104912b93ac6SEd Maste   }
105012b93ac6SEd Maste 
FindSubWindow(const char * name)1051435933ddSDimitry Andric   WindowSP FindSubWindow(const char *name) {
105212b93ac6SEd Maste     Windows::iterator pos, end = m_subwindows.end();
105312b93ac6SEd Maste     size_t i = 0;
1054435933ddSDimitry Andric     for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) {
1055*b5893f02SDimitry Andric       if ((*pos)->m_name == name)
105612b93ac6SEd Maste         return *pos;
105712b93ac6SEd Maste     }
105812b93ac6SEd Maste     return WindowSP();
105912b93ac6SEd Maste   }
106012b93ac6SEd Maste 
RemoveSubWindows()1061435933ddSDimitry Andric   void RemoveSubWindows() {
106212b93ac6SEd Maste     m_curr_active_window_idx = UINT32_MAX;
106312b93ac6SEd Maste     m_prev_active_window_idx = UINT32_MAX;
106412b93ac6SEd Maste     for (Windows::iterator pos = m_subwindows.begin();
1065435933ddSDimitry Andric          pos != m_subwindows.end(); pos = m_subwindows.erase(pos)) {
106612b93ac6SEd Maste       (*pos)->Erase();
106712b93ac6SEd Maste     }
106812b93ac6SEd Maste     if (m_parent)
106912b93ac6SEd Maste       m_parent->Touch();
107012b93ac6SEd Maste     else
107112b93ac6SEd Maste       ::touchwin(stdscr);
107212b93ac6SEd Maste   }
107312b93ac6SEd Maste 
get()1074435933ddSDimitry Andric   WINDOW *get() { return m_window; }
107512b93ac6SEd Maste 
operator WINDOW*()1076435933ddSDimitry Andric   operator WINDOW *() { return m_window; }
107712b93ac6SEd Maste 
107812b93ac6SEd Maste   //----------------------------------------------------------------------
107912b93ac6SEd Maste   // Window drawing utilities
108012b93ac6SEd Maste   //----------------------------------------------------------------------
DrawTitleBox(const char * title,const char * bottom_message=nullptr)1081435933ddSDimitry Andric   void DrawTitleBox(const char *title, const char *bottom_message = nullptr) {
108212b93ac6SEd Maste     attr_t attr = 0;
108312b93ac6SEd Maste     if (IsActive())
108412b93ac6SEd Maste       attr = A_BOLD | COLOR_PAIR(2);
108512b93ac6SEd Maste     else
108612b93ac6SEd Maste       attr = 0;
108712b93ac6SEd Maste     if (attr)
108812b93ac6SEd Maste       AttributeOn(attr);
108912b93ac6SEd Maste 
109012b93ac6SEd Maste     Box();
109112b93ac6SEd Maste     MoveCursor(3, 0);
109212b93ac6SEd Maste 
1093435933ddSDimitry Andric     if (title && title[0]) {
109412b93ac6SEd Maste       PutChar('<');
109512b93ac6SEd Maste       PutCString(title);
109612b93ac6SEd Maste       PutChar('>');
109712b93ac6SEd Maste     }
109812b93ac6SEd Maste 
1099435933ddSDimitry Andric     if (bottom_message && bottom_message[0]) {
110012b93ac6SEd Maste       int bottom_message_length = strlen(bottom_message);
110112b93ac6SEd Maste       int x = GetWidth() - 3 - (bottom_message_length + 2);
110212b93ac6SEd Maste 
1103435933ddSDimitry Andric       if (x > 0) {
110412b93ac6SEd Maste         MoveCursor(x, GetHeight() - 1);
110512b93ac6SEd Maste         PutChar('[');
110612b93ac6SEd Maste         PutCString(bottom_message);
110712b93ac6SEd Maste         PutChar(']');
1108435933ddSDimitry Andric       } else {
110912b93ac6SEd Maste         MoveCursor(1, GetHeight() - 1);
111012b93ac6SEd Maste         PutChar('[');
111112b93ac6SEd Maste         PutCStringTruncated(bottom_message, 1);
111212b93ac6SEd Maste       }
111312b93ac6SEd Maste     }
111412b93ac6SEd Maste     if (attr)
111512b93ac6SEd Maste       AttributeOff(attr);
111612b93ac6SEd Maste   }
111712b93ac6SEd Maste 
Draw(bool force)1118435933ddSDimitry Andric   virtual void Draw(bool force) {
111912b93ac6SEd Maste     if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw(*this, force))
112012b93ac6SEd Maste       return;
112112b93ac6SEd Maste 
112212b93ac6SEd Maste     for (auto &subwindow_sp : m_subwindows)
112312b93ac6SEd Maste       subwindow_sp->Draw(force);
112412b93ac6SEd Maste   }
112512b93ac6SEd Maste 
CreateHelpSubwindow()1126435933ddSDimitry Andric   bool CreateHelpSubwindow() {
1127435933ddSDimitry Andric     if (m_delegate_sp) {
112812b93ac6SEd Maste       const char *text = m_delegate_sp->WindowDelegateGetHelpText();
112912b93ac6SEd Maste       KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp();
1130435933ddSDimitry Andric       if ((text && text[0]) || key_help) {
1131acac075bSDimitry Andric         std::unique_ptr<HelpDialogDelegate> help_delegate_ap(
1132435933ddSDimitry Andric             new HelpDialogDelegate(text, key_help));
113312b93ac6SEd Maste         const size_t num_lines = help_delegate_ap->GetNumLines();
113412b93ac6SEd Maste         const size_t max_length = help_delegate_ap->GetMaxLineLength();
113512b93ac6SEd Maste         Rect bounds = GetBounds();
113612b93ac6SEd Maste         bounds.Inset(1, 1);
1137435933ddSDimitry Andric         if (max_length + 4 < static_cast<size_t>(bounds.size.width)) {
113812b93ac6SEd Maste           bounds.origin.x += (bounds.size.width - max_length + 4) / 2;
113912b93ac6SEd Maste           bounds.size.width = max_length + 4;
1140435933ddSDimitry Andric         } else {
1141435933ddSDimitry Andric           if (bounds.size.width > 100) {
114212b93ac6SEd Maste             const int inset_w = bounds.size.width / 4;
114312b93ac6SEd Maste             bounds.origin.x += inset_w;
114412b93ac6SEd Maste             bounds.size.width -= 2 * inset_w;
114512b93ac6SEd Maste           }
114612b93ac6SEd Maste         }
114712b93ac6SEd Maste 
1148435933ddSDimitry Andric         if (num_lines + 2 < static_cast<size_t>(bounds.size.height)) {
114912b93ac6SEd Maste           bounds.origin.y += (bounds.size.height - num_lines + 2) / 2;
115012b93ac6SEd Maste           bounds.size.height = num_lines + 2;
1151435933ddSDimitry Andric         } else {
1152435933ddSDimitry Andric           if (bounds.size.height > 100) {
115312b93ac6SEd Maste             const int inset_h = bounds.size.height / 4;
115412b93ac6SEd Maste             bounds.origin.y += inset_h;
115512b93ac6SEd Maste             bounds.size.height -= 2 * inset_h;
115612b93ac6SEd Maste           }
115712b93ac6SEd Maste         }
115812b93ac6SEd Maste         WindowSP help_window_sp;
115912b93ac6SEd Maste         Window *parent_window = GetParent();
116012b93ac6SEd Maste         if (parent_window)
116112b93ac6SEd Maste           help_window_sp = parent_window->CreateSubWindow("Help", bounds, true);
116212b93ac6SEd Maste         else
116312b93ac6SEd Maste           help_window_sp = CreateSubWindow("Help", bounds, true);
1164435933ddSDimitry Andric         help_window_sp->SetDelegate(
1165435933ddSDimitry Andric             WindowDelegateSP(help_delegate_ap.release()));
116612b93ac6SEd Maste         return true;
116712b93ac6SEd Maste       }
116812b93ac6SEd Maste     }
116912b93ac6SEd Maste     return false;
117012b93ac6SEd Maste   }
117112b93ac6SEd Maste 
HandleChar(int key)1172435933ddSDimitry Andric   virtual HandleCharResult HandleChar(int key) {
117312b93ac6SEd Maste     // Always check the active window first
117412b93ac6SEd Maste     HandleCharResult result = eKeyNotHandled;
117512b93ac6SEd Maste     WindowSP active_window_sp = GetActiveWindow();
1176435933ddSDimitry Andric     if (active_window_sp) {
117712b93ac6SEd Maste       result = active_window_sp->HandleChar(key);
117812b93ac6SEd Maste       if (result != eKeyNotHandled)
117912b93ac6SEd Maste         return result;
118012b93ac6SEd Maste     }
118112b93ac6SEd Maste 
1182435933ddSDimitry Andric     if (m_delegate_sp) {
118312b93ac6SEd Maste       result = m_delegate_sp->WindowDelegateHandleChar(*this, key);
118412b93ac6SEd Maste       if (result != eKeyNotHandled)
118512b93ac6SEd Maste         return result;
118612b93ac6SEd Maste     }
118712b93ac6SEd Maste 
11884ba319b5SDimitry Andric     // Then check for any windows that want any keys that weren't handled. This
11894ba319b5SDimitry Andric     // is typically only for a menubar. Make a copy of the subwindows in case
11904ba319b5SDimitry Andric     // any HandleChar() functions muck with the subwindows. If we don't do
11914ba319b5SDimitry Andric     // this, we can crash when iterating over the subwindows.
119212b93ac6SEd Maste     Windows subwindows(m_subwindows);
1193435933ddSDimitry Andric     for (auto subwindow_sp : subwindows) {
1194435933ddSDimitry Andric       if (!subwindow_sp->m_can_activate) {
119512b93ac6SEd Maste         HandleCharResult result = subwindow_sp->HandleChar(key);
119612b93ac6SEd Maste         if (result != eKeyNotHandled)
119712b93ac6SEd Maste           return result;
119812b93ac6SEd Maste       }
119912b93ac6SEd Maste     }
120012b93ac6SEd Maste 
120112b93ac6SEd Maste     return eKeyNotHandled;
120212b93ac6SEd Maste   }
120312b93ac6SEd Maste 
SetActiveWindow(Window * window)1204435933ddSDimitry Andric   bool SetActiveWindow(Window *window) {
120512b93ac6SEd Maste     const size_t num_subwindows = m_subwindows.size();
1206435933ddSDimitry Andric     for (size_t i = 0; i < num_subwindows; ++i) {
1207435933ddSDimitry Andric       if (m_subwindows[i].get() == window) {
120812b93ac6SEd Maste         m_prev_active_window_idx = m_curr_active_window_idx;
120912b93ac6SEd Maste         ::top_panel(window->m_panel);
121012b93ac6SEd Maste         m_curr_active_window_idx = i;
121112b93ac6SEd Maste         return true;
121212b93ac6SEd Maste       }
121312b93ac6SEd Maste     }
121412b93ac6SEd Maste     return false;
121512b93ac6SEd Maste   }
121612b93ac6SEd Maste 
GetActiveWindow()1217435933ddSDimitry Andric   WindowSP GetActiveWindow() {
1218435933ddSDimitry Andric     if (!m_subwindows.empty()) {
1219435933ddSDimitry Andric       if (m_curr_active_window_idx >= m_subwindows.size()) {
1220435933ddSDimitry Andric         if (m_prev_active_window_idx < m_subwindows.size()) {
122112b93ac6SEd Maste           m_curr_active_window_idx = m_prev_active_window_idx;
122212b93ac6SEd Maste           m_prev_active_window_idx = UINT32_MAX;
1223435933ddSDimitry Andric         } else if (IsActive()) {
122412b93ac6SEd Maste           m_prev_active_window_idx = UINT32_MAX;
122512b93ac6SEd Maste           m_curr_active_window_idx = UINT32_MAX;
122612b93ac6SEd Maste 
122712b93ac6SEd Maste           // Find first window that wants to be active if this window is active
122812b93ac6SEd Maste           const size_t num_subwindows = m_subwindows.size();
1229435933ddSDimitry Andric           for (size_t i = 0; i < num_subwindows; ++i) {
1230435933ddSDimitry Andric             if (m_subwindows[i]->GetCanBeActive()) {
123112b93ac6SEd Maste               m_curr_active_window_idx = i;
123212b93ac6SEd Maste               break;
123312b93ac6SEd Maste             }
123412b93ac6SEd Maste           }
123512b93ac6SEd Maste         }
123612b93ac6SEd Maste       }
123712b93ac6SEd Maste 
123812b93ac6SEd Maste       if (m_curr_active_window_idx < m_subwindows.size())
123912b93ac6SEd Maste         return m_subwindows[m_curr_active_window_idx];
124012b93ac6SEd Maste     }
124112b93ac6SEd Maste     return WindowSP();
124212b93ac6SEd Maste   }
124312b93ac6SEd Maste 
GetCanBeActive() const1244435933ddSDimitry Andric   bool GetCanBeActive() const { return m_can_activate; }
124512b93ac6SEd Maste 
SetCanBeActive(bool b)1246435933ddSDimitry Andric   void SetCanBeActive(bool b) { m_can_activate = b; }
124712b93ac6SEd Maste 
GetDelegate() const1248435933ddSDimitry Andric   const WindowDelegateSP &GetDelegate() const { return m_delegate_sp; }
124912b93ac6SEd Maste 
SetDelegate(const WindowDelegateSP & delegate_sp)1250435933ddSDimitry Andric   void SetDelegate(const WindowDelegateSP &delegate_sp) {
125112b93ac6SEd Maste     m_delegate_sp = delegate_sp;
125212b93ac6SEd Maste   }
125312b93ac6SEd Maste 
GetParent() const1254435933ddSDimitry Andric   Window *GetParent() const { return m_parent; }
125512b93ac6SEd Maste 
IsActive() const1256435933ddSDimitry Andric   bool IsActive() const {
125712b93ac6SEd Maste     if (m_parent)
125812b93ac6SEd Maste       return m_parent->GetActiveWindow().get() == this;
125912b93ac6SEd Maste     else
126012b93ac6SEd Maste       return true; // Top level window is always active
126112b93ac6SEd Maste   }
126212b93ac6SEd Maste 
SelectNextWindowAsActive()1263435933ddSDimitry Andric   void SelectNextWindowAsActive() {
126412b93ac6SEd Maste     // Move active focus to next window
126512b93ac6SEd Maste     const size_t num_subwindows = m_subwindows.size();
1266435933ddSDimitry Andric     if (m_curr_active_window_idx == UINT32_MAX) {
126712b93ac6SEd Maste       uint32_t idx = 0;
1268435933ddSDimitry Andric       for (auto subwindow_sp : m_subwindows) {
1269435933ddSDimitry Andric         if (subwindow_sp->GetCanBeActive()) {
127012b93ac6SEd Maste           m_curr_active_window_idx = idx;
127112b93ac6SEd Maste           break;
127212b93ac6SEd Maste         }
127312b93ac6SEd Maste         ++idx;
127412b93ac6SEd Maste       }
1275435933ddSDimitry Andric     } else if (m_curr_active_window_idx + 1 < num_subwindows) {
127612b93ac6SEd Maste       bool handled = false;
127712b93ac6SEd Maste       m_prev_active_window_idx = m_curr_active_window_idx;
1278435933ddSDimitry Andric       for (size_t idx = m_curr_active_window_idx + 1; idx < num_subwindows;
1279435933ddSDimitry Andric            ++idx) {
1280435933ddSDimitry Andric         if (m_subwindows[idx]->GetCanBeActive()) {
128112b93ac6SEd Maste           m_curr_active_window_idx = idx;
128212b93ac6SEd Maste           handled = true;
128312b93ac6SEd Maste           break;
128412b93ac6SEd Maste         }
128512b93ac6SEd Maste       }
1286435933ddSDimitry Andric       if (!handled) {
1287435933ddSDimitry Andric         for (size_t idx = 0; idx <= m_prev_active_window_idx; ++idx) {
1288435933ddSDimitry Andric           if (m_subwindows[idx]->GetCanBeActive()) {
128912b93ac6SEd Maste             m_curr_active_window_idx = idx;
129012b93ac6SEd Maste             break;
129112b93ac6SEd Maste           }
129212b93ac6SEd Maste         }
129312b93ac6SEd Maste       }
1294435933ddSDimitry Andric     } else {
129512b93ac6SEd Maste       m_prev_active_window_idx = m_curr_active_window_idx;
1296435933ddSDimitry Andric       for (size_t idx = 0; idx < num_subwindows; ++idx) {
1297435933ddSDimitry Andric         if (m_subwindows[idx]->GetCanBeActive()) {
129812b93ac6SEd Maste           m_curr_active_window_idx = idx;
129912b93ac6SEd Maste           break;
130012b93ac6SEd Maste         }
130112b93ac6SEd Maste       }
130212b93ac6SEd Maste     }
130312b93ac6SEd Maste   }
130412b93ac6SEd Maste 
GetName() const1305435933ddSDimitry Andric   const char *GetName() const { return m_name.c_str(); }
13069f2f44ceSEd Maste 
130712b93ac6SEd Maste protected:
130812b93ac6SEd Maste   std::string m_name;
130912b93ac6SEd Maste   WINDOW *m_window;
131012b93ac6SEd Maste   PANEL *m_panel;
131112b93ac6SEd Maste   Window *m_parent;
131212b93ac6SEd Maste   Windows m_subwindows;
131312b93ac6SEd Maste   WindowDelegateSP m_delegate_sp;
131412b93ac6SEd Maste   uint32_t m_curr_active_window_idx;
131512b93ac6SEd Maste   uint32_t m_prev_active_window_idx;
131612b93ac6SEd Maste   bool m_delete;
131712b93ac6SEd Maste   bool m_needs_update;
131812b93ac6SEd Maste   bool m_can_activate;
131912b93ac6SEd Maste   bool m_is_subwin;
132012b93ac6SEd Maste 
132112b93ac6SEd Maste private:
132212b93ac6SEd Maste   DISALLOW_COPY_AND_ASSIGN(Window);
132312b93ac6SEd Maste };
132412b93ac6SEd Maste 
1325435933ddSDimitry Andric class MenuDelegate {
132612b93ac6SEd Maste public:
13279f2f44ceSEd Maste   virtual ~MenuDelegate() = default;
132812b93ac6SEd Maste 
1329435933ddSDimitry Andric   virtual MenuActionResult MenuDelegateAction(Menu &menu) = 0;
133012b93ac6SEd Maste };
133112b93ac6SEd Maste 
1332435933ddSDimitry Andric class Menu : public WindowDelegate {
133312b93ac6SEd Maste public:
1334435933ddSDimitry Andric   enum class Type { Invalid, Bar, Item, Separator };
133512b93ac6SEd Maste 
133612b93ac6SEd Maste   // Menubar or separator constructor
133712b93ac6SEd Maste   Menu(Type type);
133812b93ac6SEd Maste 
133912b93ac6SEd Maste   // Menuitem constructor
1340435933ddSDimitry Andric   Menu(const char *name, const char *key_name, int key_value,
134112b93ac6SEd Maste        uint64_t identifier);
134212b93ac6SEd Maste 
13439f2f44ceSEd Maste   ~Menu() override = default;
134412b93ac6SEd Maste 
GetDelegate() const1345435933ddSDimitry Andric   const MenuDelegateSP &GetDelegate() const { return m_delegate_sp; }
134612b93ac6SEd Maste 
SetDelegate(const MenuDelegateSP & delegate_sp)1347435933ddSDimitry Andric   void SetDelegate(const MenuDelegateSP &delegate_sp) {
134812b93ac6SEd Maste     m_delegate_sp = delegate_sp;
134912b93ac6SEd Maste   }
135012b93ac6SEd Maste 
1351435933ddSDimitry Andric   void RecalculateNameLengths();
135212b93ac6SEd Maste 
1353435933ddSDimitry Andric   void AddSubmenu(const MenuSP &menu_sp);
135412b93ac6SEd Maste 
1355435933ddSDimitry Andric   int DrawAndRunMenu(Window &window);
135612b93ac6SEd Maste 
1357435933ddSDimitry Andric   void DrawMenuTitle(Window &window, bool highlight);
135812b93ac6SEd Maste 
1359435933ddSDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override;
136012b93ac6SEd Maste 
1361435933ddSDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override;
136212b93ac6SEd Maste 
ActionPrivate(Menu & menu)1363435933ddSDimitry Andric   MenuActionResult ActionPrivate(Menu &menu) {
136412b93ac6SEd Maste     MenuActionResult result = MenuActionResult::NotHandled;
1365435933ddSDimitry Andric     if (m_delegate_sp) {
136612b93ac6SEd Maste       result = m_delegate_sp->MenuDelegateAction(menu);
136712b93ac6SEd Maste       if (result != MenuActionResult::NotHandled)
136812b93ac6SEd Maste         return result;
1369435933ddSDimitry Andric     } else if (m_parent) {
137012b93ac6SEd Maste       result = m_parent->ActionPrivate(menu);
137112b93ac6SEd Maste       if (result != MenuActionResult::NotHandled)
137212b93ac6SEd Maste         return result;
137312b93ac6SEd Maste     }
137412b93ac6SEd Maste     return m_canned_result;
137512b93ac6SEd Maste   }
137612b93ac6SEd Maste 
Action()1377435933ddSDimitry Andric   MenuActionResult Action() {
13784ba319b5SDimitry Andric     // Call the recursive action so it can try to handle it with the menu
13794ba319b5SDimitry Andric     // delegate, and if not, try our parent menu
138012b93ac6SEd Maste     return ActionPrivate(*this);
138112b93ac6SEd Maste   }
138212b93ac6SEd Maste 
SetCannedResult(MenuActionResult result)1383435933ddSDimitry Andric   void SetCannedResult(MenuActionResult result) { m_canned_result = result; }
138412b93ac6SEd Maste 
GetSubmenus()1385435933ddSDimitry Andric   Menus &GetSubmenus() { return m_submenus; }
138612b93ac6SEd Maste 
GetSubmenus() const1387435933ddSDimitry Andric   const Menus &GetSubmenus() const { return m_submenus; }
138812b93ac6SEd Maste 
GetSelectedSubmenuIndex() const1389435933ddSDimitry Andric   int GetSelectedSubmenuIndex() const { return m_selected; }
139012b93ac6SEd Maste 
SetSelectedSubmenuIndex(int idx)1391435933ddSDimitry Andric   void SetSelectedSubmenuIndex(int idx) { m_selected = idx; }
139212b93ac6SEd Maste 
GetType() const1393435933ddSDimitry Andric   Type GetType() const { return m_type; }
139412b93ac6SEd Maste 
GetStartingColumn() const1395435933ddSDimitry Andric   int GetStartingColumn() const { return m_start_col; }
139612b93ac6SEd Maste 
SetStartingColumn(int col)1397435933ddSDimitry Andric   void SetStartingColumn(int col) { m_start_col = col; }
139812b93ac6SEd Maste 
GetKeyValue() const1399435933ddSDimitry Andric   int GetKeyValue() const { return m_key_value; }
140012b93ac6SEd Maste 
SetKeyValue(int key_value)1401435933ddSDimitry Andric   void SetKeyValue(int key_value) { m_key_value = key_value; }
140212b93ac6SEd Maste 
GetName()1403435933ddSDimitry Andric   std::string &GetName() { return m_name; }
140412b93ac6SEd Maste 
GetKeyName()1405435933ddSDimitry Andric   std::string &GetKeyName() { return m_key_name; }
140612b93ac6SEd Maste 
GetDrawWidth() const1407435933ddSDimitry Andric   int GetDrawWidth() const {
140812b93ac6SEd Maste     return m_max_submenu_name_length + m_max_submenu_key_name_length + 8;
140912b93ac6SEd Maste   }
141012b93ac6SEd Maste 
GetIdentifier() const1411435933ddSDimitry Andric   uint64_t GetIdentifier() const { return m_identifier; }
141212b93ac6SEd Maste 
SetIdentifier(uint64_t identifier)1413435933ddSDimitry Andric   void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
141412b93ac6SEd Maste 
141512b93ac6SEd Maste protected:
141612b93ac6SEd Maste   std::string m_name;
141712b93ac6SEd Maste   std::string m_key_name;
141812b93ac6SEd Maste   uint64_t m_identifier;
141912b93ac6SEd Maste   Type m_type;
142012b93ac6SEd Maste   int m_key_value;
142112b93ac6SEd Maste   int m_start_col;
142212b93ac6SEd Maste   int m_max_submenu_name_length;
142312b93ac6SEd Maste   int m_max_submenu_key_name_length;
142412b93ac6SEd Maste   int m_selected;
142512b93ac6SEd Maste   Menu *m_parent;
142612b93ac6SEd Maste   Menus m_submenus;
142712b93ac6SEd Maste   WindowSP m_menu_window_sp;
142812b93ac6SEd Maste   MenuActionResult m_canned_result;
142912b93ac6SEd Maste   MenuDelegateSP m_delegate_sp;
143012b93ac6SEd Maste };
143112b93ac6SEd Maste 
143212b93ac6SEd Maste // Menubar or separator constructor
Menu(Type type)1433435933ddSDimitry Andric Menu::Menu(Type type)
1434435933ddSDimitry Andric     : m_name(), m_key_name(), m_identifier(0), m_type(type), m_key_value(0),
1435435933ddSDimitry Andric       m_start_col(0), m_max_submenu_name_length(0),
1436435933ddSDimitry Andric       m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr),
1437435933ddSDimitry Andric       m_submenus(), m_canned_result(MenuActionResult::NotHandled),
1438435933ddSDimitry Andric       m_delegate_sp() {}
143912b93ac6SEd Maste 
144012b93ac6SEd Maste // Menuitem constructor
Menu(const char * name,const char * key_name,int key_value,uint64_t identifier)1441435933ddSDimitry Andric Menu::Menu(const char *name, const char *key_name, int key_value,
1442435933ddSDimitry Andric            uint64_t identifier)
1443435933ddSDimitry Andric     : m_name(), m_key_name(), m_identifier(identifier), m_type(Type::Invalid),
1444435933ddSDimitry Andric       m_key_value(key_value), m_start_col(0), m_max_submenu_name_length(0),
1445435933ddSDimitry Andric       m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr),
1446435933ddSDimitry Andric       m_submenus(), m_canned_result(MenuActionResult::NotHandled),
1447435933ddSDimitry Andric       m_delegate_sp() {
1448435933ddSDimitry Andric   if (name && name[0]) {
144912b93ac6SEd Maste     m_name = name;
145012b93ac6SEd Maste     m_type = Type::Item;
145112b93ac6SEd Maste     if (key_name && key_name[0])
145212b93ac6SEd Maste       m_key_name = key_name;
1453435933ddSDimitry Andric   } else {
145412b93ac6SEd Maste     m_type = Type::Separator;
145512b93ac6SEd Maste   }
145612b93ac6SEd Maste }
145712b93ac6SEd Maste 
RecalculateNameLengths()1458435933ddSDimitry Andric void Menu::RecalculateNameLengths() {
145912b93ac6SEd Maste   m_max_submenu_name_length = 0;
146012b93ac6SEd Maste   m_max_submenu_key_name_length = 0;
146112b93ac6SEd Maste   Menus &submenus = GetSubmenus();
146212b93ac6SEd Maste   const size_t num_submenus = submenus.size();
1463435933ddSDimitry Andric   for (size_t i = 0; i < num_submenus; ++i) {
146412b93ac6SEd Maste     Menu *submenu = submenus[i].get();
14650127ef0fSEd Maste     if (static_cast<size_t>(m_max_submenu_name_length) < submenu->m_name.size())
146612b93ac6SEd Maste       m_max_submenu_name_length = submenu->m_name.size();
1467435933ddSDimitry Andric     if (static_cast<size_t>(m_max_submenu_key_name_length) <
1468435933ddSDimitry Andric         submenu->m_key_name.size())
146912b93ac6SEd Maste       m_max_submenu_key_name_length = submenu->m_key_name.size();
147012b93ac6SEd Maste   }
147112b93ac6SEd Maste }
147212b93ac6SEd Maste 
AddSubmenu(const MenuSP & menu_sp)1473435933ddSDimitry Andric void Menu::AddSubmenu(const MenuSP &menu_sp) {
147412b93ac6SEd Maste   menu_sp->m_parent = this;
14750127ef0fSEd Maste   if (static_cast<size_t>(m_max_submenu_name_length) < menu_sp->m_name.size())
147612b93ac6SEd Maste     m_max_submenu_name_length = menu_sp->m_name.size();
1477435933ddSDimitry Andric   if (static_cast<size_t>(m_max_submenu_key_name_length) <
1478435933ddSDimitry Andric       menu_sp->m_key_name.size())
147912b93ac6SEd Maste     m_max_submenu_key_name_length = menu_sp->m_key_name.size();
148012b93ac6SEd Maste   m_submenus.push_back(menu_sp);
148112b93ac6SEd Maste }
148212b93ac6SEd Maste 
DrawMenuTitle(Window & window,bool highlight)1483435933ddSDimitry Andric void Menu::DrawMenuTitle(Window &window, bool highlight) {
1484435933ddSDimitry Andric   if (m_type == Type::Separator) {
148512b93ac6SEd Maste     window.MoveCursor(0, window.GetCursorY());
148612b93ac6SEd Maste     window.PutChar(ACS_LTEE);
148712b93ac6SEd Maste     int width = window.GetWidth();
1488435933ddSDimitry Andric     if (width > 2) {
148912b93ac6SEd Maste       width -= 2;
14900127ef0fSEd Maste       for (int i = 0; i < width; ++i)
149112b93ac6SEd Maste         window.PutChar(ACS_HLINE);
149212b93ac6SEd Maste     }
149312b93ac6SEd Maste     window.PutChar(ACS_RTEE);
1494435933ddSDimitry Andric   } else {
149512b93ac6SEd Maste     const int shortcut_key = m_key_value;
149612b93ac6SEd Maste     bool underlined_shortcut = false;
149712b93ac6SEd Maste     const attr_t hilgight_attr = A_REVERSE;
149812b93ac6SEd Maste     if (highlight)
149912b93ac6SEd Maste       window.AttributeOn(hilgight_attr);
1500435933ddSDimitry Andric     if (isprint(shortcut_key)) {
150112b93ac6SEd Maste       size_t lower_pos = m_name.find(tolower(shortcut_key));
150212b93ac6SEd Maste       size_t upper_pos = m_name.find(toupper(shortcut_key));
150312b93ac6SEd Maste       const char *name = m_name.c_str();
150412b93ac6SEd Maste       size_t pos = std::min<size_t>(lower_pos, upper_pos);
1505435933ddSDimitry Andric       if (pos != std::string::npos) {
150612b93ac6SEd Maste         underlined_shortcut = true;
1507435933ddSDimitry Andric         if (pos > 0) {
150812b93ac6SEd Maste           window.PutCString(name, pos);
150912b93ac6SEd Maste           name += pos;
151012b93ac6SEd Maste         }
151112b93ac6SEd Maste         const attr_t shortcut_attr = A_UNDERLINE | A_BOLD;
151212b93ac6SEd Maste         window.AttributeOn(shortcut_attr);
151312b93ac6SEd Maste         window.PutChar(name[0]);
151412b93ac6SEd Maste         window.AttributeOff(shortcut_attr);
151512b93ac6SEd Maste         name++;
151612b93ac6SEd Maste         if (name[0])
151712b93ac6SEd Maste           window.PutCString(name);
151812b93ac6SEd Maste       }
151912b93ac6SEd Maste     }
152012b93ac6SEd Maste 
1521435933ddSDimitry Andric     if (!underlined_shortcut) {
152212b93ac6SEd Maste       window.PutCString(m_name.c_str());
152312b93ac6SEd Maste     }
152412b93ac6SEd Maste 
152512b93ac6SEd Maste     if (highlight)
152612b93ac6SEd Maste       window.AttributeOff(hilgight_attr);
152712b93ac6SEd Maste 
1528435933ddSDimitry Andric     if (m_key_name.empty()) {
1529435933ddSDimitry Andric       if (!underlined_shortcut && isprint(m_key_value)) {
153012b93ac6SEd Maste         window.AttributeOn(COLOR_PAIR(3));
153112b93ac6SEd Maste         window.Printf(" (%c)", m_key_value);
153212b93ac6SEd Maste         window.AttributeOff(COLOR_PAIR(3));
153312b93ac6SEd Maste       }
1534435933ddSDimitry Andric     } else {
153512b93ac6SEd Maste       window.AttributeOn(COLOR_PAIR(3));
153612b93ac6SEd Maste       window.Printf(" (%s)", m_key_name.c_str());
153712b93ac6SEd Maste       window.AttributeOff(COLOR_PAIR(3));
153812b93ac6SEd Maste     }
153912b93ac6SEd Maste   }
154012b93ac6SEd Maste }
154112b93ac6SEd Maste 
WindowDelegateDraw(Window & window,bool force)1542435933ddSDimitry Andric bool Menu::WindowDelegateDraw(Window &window, bool force) {
154312b93ac6SEd Maste   Menus &submenus = GetSubmenus();
154412b93ac6SEd Maste   const size_t num_submenus = submenus.size();
154512b93ac6SEd Maste   const int selected_idx = GetSelectedSubmenuIndex();
154612b93ac6SEd Maste   Menu::Type menu_type = GetType();
1547435933ddSDimitry Andric   switch (menu_type) {
1548435933ddSDimitry Andric   case Menu::Type::Bar: {
154912b93ac6SEd Maste     window.SetBackground(2);
155012b93ac6SEd Maste     window.MoveCursor(0, 0);
1551435933ddSDimitry Andric     for (size_t i = 0; i < num_submenus; ++i) {
155212b93ac6SEd Maste       Menu *menu = submenus[i].get();
155312b93ac6SEd Maste       if (i > 0)
155412b93ac6SEd Maste         window.PutChar(' ');
155512b93ac6SEd Maste       menu->SetStartingColumn(window.GetCursorX());
155612b93ac6SEd Maste       window.PutCString("| ");
155712b93ac6SEd Maste       menu->DrawMenuTitle(window, false);
155812b93ac6SEd Maste     }
155912b93ac6SEd Maste     window.PutCString(" |");
156012b93ac6SEd Maste     window.DeferredRefresh();
1561435933ddSDimitry Andric   } break;
156212b93ac6SEd Maste 
1563435933ddSDimitry Andric   case Menu::Type::Item: {
156412b93ac6SEd Maste     int y = 1;
156512b93ac6SEd Maste     int x = 3;
156612b93ac6SEd Maste     // Draw the menu
156712b93ac6SEd Maste     int cursor_x = 0;
156812b93ac6SEd Maste     int cursor_y = 0;
156912b93ac6SEd Maste     window.Erase();
157012b93ac6SEd Maste     window.SetBackground(2);
157112b93ac6SEd Maste     window.Box();
1572435933ddSDimitry Andric     for (size_t i = 0; i < num_submenus; ++i) {
1573435933ddSDimitry Andric       const bool is_selected = (i == static_cast<size_t>(selected_idx));
157412b93ac6SEd Maste       window.MoveCursor(x, y + i);
1575435933ddSDimitry Andric       if (is_selected) {
157612b93ac6SEd Maste         // Remember where we want the cursor to be
157712b93ac6SEd Maste         cursor_x = x - 1;
157812b93ac6SEd Maste         cursor_y = y + i;
157912b93ac6SEd Maste       }
158012b93ac6SEd Maste       submenus[i]->DrawMenuTitle(window, is_selected);
158112b93ac6SEd Maste     }
158212b93ac6SEd Maste     window.MoveCursor(cursor_x, cursor_y);
158312b93ac6SEd Maste     window.DeferredRefresh();
1584435933ddSDimitry Andric   } break;
158512b93ac6SEd Maste 
158612b93ac6SEd Maste   default:
158712b93ac6SEd Maste   case Menu::Type::Separator:
158812b93ac6SEd Maste     break;
158912b93ac6SEd Maste   }
159012b93ac6SEd Maste   return true; // Drawing handled...
159112b93ac6SEd Maste }
159212b93ac6SEd Maste 
WindowDelegateHandleChar(Window & window,int key)1593435933ddSDimitry Andric HandleCharResult Menu::WindowDelegateHandleChar(Window &window, int key) {
159412b93ac6SEd Maste   HandleCharResult result = eKeyNotHandled;
159512b93ac6SEd Maste 
159612b93ac6SEd Maste   Menus &submenus = GetSubmenus();
159712b93ac6SEd Maste   const size_t num_submenus = submenus.size();
159812b93ac6SEd Maste   const int selected_idx = GetSelectedSubmenuIndex();
159912b93ac6SEd Maste   Menu::Type menu_type = GetType();
1600435933ddSDimitry Andric   if (menu_type == Menu::Type::Bar) {
160112b93ac6SEd Maste     MenuSP run_menu_sp;
1602435933ddSDimitry Andric     switch (key) {
160312b93ac6SEd Maste     case KEY_DOWN:
160412b93ac6SEd Maste     case KEY_UP:
160512b93ac6SEd Maste       // Show last menu or first menu
16060127ef0fSEd Maste       if (selected_idx < static_cast<int>(num_submenus))
160712b93ac6SEd Maste         run_menu_sp = submenus[selected_idx];
160812b93ac6SEd Maste       else if (!submenus.empty())
160912b93ac6SEd Maste         run_menu_sp = submenus.front();
161012b93ac6SEd Maste       result = eKeyHandled;
161112b93ac6SEd Maste       break;
161212b93ac6SEd Maste 
161312b93ac6SEd Maste     case KEY_RIGHT:
161412b93ac6SEd Maste       ++m_selected;
16150127ef0fSEd Maste       if (m_selected >= static_cast<int>(num_submenus))
161612b93ac6SEd Maste         m_selected = 0;
16170127ef0fSEd Maste       if (m_selected < static_cast<int>(num_submenus))
161812b93ac6SEd Maste         run_menu_sp = submenus[m_selected];
161912b93ac6SEd Maste       else if (!submenus.empty())
162012b93ac6SEd Maste         run_menu_sp = submenus.front();
162112b93ac6SEd Maste       result = eKeyHandled;
162212b93ac6SEd Maste       break;
162312b93ac6SEd Maste 
162412b93ac6SEd Maste     case KEY_LEFT:
162512b93ac6SEd Maste       --m_selected;
162612b93ac6SEd Maste       if (m_selected < 0)
162712b93ac6SEd Maste         m_selected = num_submenus - 1;
16280127ef0fSEd Maste       if (m_selected < static_cast<int>(num_submenus))
162912b93ac6SEd Maste         run_menu_sp = submenus[m_selected];
163012b93ac6SEd Maste       else if (!submenus.empty())
163112b93ac6SEd Maste         run_menu_sp = submenus.front();
163212b93ac6SEd Maste       result = eKeyHandled;
163312b93ac6SEd Maste       break;
163412b93ac6SEd Maste 
163512b93ac6SEd Maste     default:
1636435933ddSDimitry Andric       for (size_t i = 0; i < num_submenus; ++i) {
1637435933ddSDimitry Andric         if (submenus[i]->GetKeyValue() == key) {
163812b93ac6SEd Maste           SetSelectedSubmenuIndex(i);
163912b93ac6SEd Maste           run_menu_sp = submenus[i];
164012b93ac6SEd Maste           result = eKeyHandled;
164112b93ac6SEd Maste           break;
164212b93ac6SEd Maste         }
164312b93ac6SEd Maste       }
164412b93ac6SEd Maste       break;
164512b93ac6SEd Maste     }
164612b93ac6SEd Maste 
1647435933ddSDimitry Andric     if (run_menu_sp) {
16484ba319b5SDimitry Andric       // Run the action on this menu in case we need to populate the menu with
16494ba319b5SDimitry Andric       // dynamic content and also in case check marks, and any other menu
16504ba319b5SDimitry Andric       // decorations need to be calculated
165112b93ac6SEd Maste       if (run_menu_sp->Action() == MenuActionResult::Quit)
165212b93ac6SEd Maste         return eQuitApplication;
165312b93ac6SEd Maste 
165412b93ac6SEd Maste       Rect menu_bounds;
165512b93ac6SEd Maste       menu_bounds.origin.x = run_menu_sp->GetStartingColumn();
165612b93ac6SEd Maste       menu_bounds.origin.y = 1;
165712b93ac6SEd Maste       menu_bounds.size.width = run_menu_sp->GetDrawWidth();
165812b93ac6SEd Maste       menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2;
165912b93ac6SEd Maste       if (m_menu_window_sp)
166012b93ac6SEd Maste         window.GetParent()->RemoveSubWindow(m_menu_window_sp.get());
166112b93ac6SEd Maste 
1662435933ddSDimitry Andric       m_menu_window_sp = window.GetParent()->CreateSubWindow(
1663435933ddSDimitry Andric           run_menu_sp->GetName().c_str(), menu_bounds, true);
166412b93ac6SEd Maste       m_menu_window_sp->SetDelegate(run_menu_sp);
166512b93ac6SEd Maste     }
1666435933ddSDimitry Andric   } else if (menu_type == Menu::Type::Item) {
1667435933ddSDimitry Andric     switch (key) {
166812b93ac6SEd Maste     case KEY_DOWN:
1669435933ddSDimitry Andric       if (m_submenus.size() > 1) {
167012b93ac6SEd Maste         const int start_select = m_selected;
1671435933ddSDimitry Andric         while (++m_selected != start_select) {
16720127ef0fSEd Maste           if (static_cast<size_t>(m_selected) >= num_submenus)
167312b93ac6SEd Maste             m_selected = 0;
167412b93ac6SEd Maste           if (m_submenus[m_selected]->GetType() == Type::Separator)
167512b93ac6SEd Maste             continue;
167612b93ac6SEd Maste           else
167712b93ac6SEd Maste             break;
167812b93ac6SEd Maste         }
167912b93ac6SEd Maste         return eKeyHandled;
168012b93ac6SEd Maste       }
168112b93ac6SEd Maste       break;
168212b93ac6SEd Maste 
168312b93ac6SEd Maste     case KEY_UP:
1684435933ddSDimitry Andric       if (m_submenus.size() > 1) {
168512b93ac6SEd Maste         const int start_select = m_selected;
1686435933ddSDimitry Andric         while (--m_selected != start_select) {
16870127ef0fSEd Maste           if (m_selected < static_cast<int>(0))
168812b93ac6SEd Maste             m_selected = num_submenus - 1;
168912b93ac6SEd Maste           if (m_submenus[m_selected]->GetType() == Type::Separator)
169012b93ac6SEd Maste             continue;
169112b93ac6SEd Maste           else
169212b93ac6SEd Maste             break;
169312b93ac6SEd Maste         }
169412b93ac6SEd Maste         return eKeyHandled;
169512b93ac6SEd Maste       }
169612b93ac6SEd Maste       break;
169712b93ac6SEd Maste 
169812b93ac6SEd Maste     case KEY_RETURN:
1699435933ddSDimitry Andric       if (static_cast<size_t>(selected_idx) < num_submenus) {
170012b93ac6SEd Maste         if (submenus[selected_idx]->Action() == MenuActionResult::Quit)
170112b93ac6SEd Maste           return eQuitApplication;
170212b93ac6SEd Maste         window.GetParent()->RemoveSubWindow(&window);
170312b93ac6SEd Maste         return eKeyHandled;
170412b93ac6SEd Maste       }
170512b93ac6SEd Maste       break;
170612b93ac6SEd Maste 
1707435933ddSDimitry Andric     case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in
1708435933ddSDimitry Andric                      // case other chars are entered for escaped sequences
170912b93ac6SEd Maste       window.GetParent()->RemoveSubWindow(&window);
171012b93ac6SEd Maste       return eKeyHandled;
171112b93ac6SEd Maste 
171212b93ac6SEd Maste     default:
1713435933ddSDimitry Andric       for (size_t i = 0; i < num_submenus; ++i) {
171412b93ac6SEd Maste         Menu *menu = submenus[i].get();
1715435933ddSDimitry Andric         if (menu->GetKeyValue() == key) {
171612b93ac6SEd Maste           SetSelectedSubmenuIndex(i);
171712b93ac6SEd Maste           window.GetParent()->RemoveSubWindow(&window);
171812b93ac6SEd Maste           if (menu->Action() == MenuActionResult::Quit)
171912b93ac6SEd Maste             return eQuitApplication;
172012b93ac6SEd Maste           return eKeyHandled;
172112b93ac6SEd Maste         }
172212b93ac6SEd Maste       }
172312b93ac6SEd Maste       break;
172412b93ac6SEd Maste     }
1725435933ddSDimitry Andric   } else if (menu_type == Menu::Type::Separator) {
172612b93ac6SEd Maste   }
172712b93ac6SEd Maste   return result;
172812b93ac6SEd Maste }
172912b93ac6SEd Maste 
1730435933ddSDimitry Andric class Application {
173112b93ac6SEd Maste public:
Application(FILE * in,FILE * out)1732435933ddSDimitry Andric   Application(FILE *in, FILE *out)
1733435933ddSDimitry Andric       : m_window_sp(), m_screen(nullptr), m_in(in), m_out(out) {}
173412b93ac6SEd Maste 
~Application()1735435933ddSDimitry Andric   ~Application() {
173612b93ac6SEd Maste     m_window_delegates.clear();
173712b93ac6SEd Maste     m_window_sp.reset();
1738435933ddSDimitry Andric     if (m_screen) {
173912b93ac6SEd Maste       ::delscreen(m_screen);
17404bb0738eSEd Maste       m_screen = nullptr;
174112b93ac6SEd Maste     }
174212b93ac6SEd Maste   }
174312b93ac6SEd Maste 
Initialize()1744435933ddSDimitry Andric   void Initialize() {
174512b93ac6SEd Maste     ::setlocale(LC_ALL, "");
174612b93ac6SEd Maste     ::setlocale(LC_CTYPE, "");
17474bb0738eSEd Maste     m_screen = ::newterm(nullptr, m_out, m_in);
174812b93ac6SEd Maste     ::start_color();
174912b93ac6SEd Maste     ::curs_set(0);
175012b93ac6SEd Maste     ::noecho();
175112b93ac6SEd Maste     ::keypad(stdscr, TRUE);
175212b93ac6SEd Maste   }
175312b93ac6SEd Maste 
Terminate()1754435933ddSDimitry Andric   void Terminate() { ::endwin(); }
175512b93ac6SEd Maste 
Run(Debugger & debugger)1756435933ddSDimitry Andric   void Run(Debugger &debugger) {
175712b93ac6SEd Maste     bool done = false;
175812b93ac6SEd Maste     int delay_in_tenths_of_a_second = 1;
175912b93ac6SEd Maste 
17604ba319b5SDimitry Andric     // Alas the threading model in curses is a bit lame so we need to resort to
17614ba319b5SDimitry Andric     // polling every 0.5 seconds. We could poll for stdin ourselves and then
17624ba319b5SDimitry Andric     // pass the keys down but then we need to translate all of the escape
17634ba319b5SDimitry Andric     // sequences ourselves. So we resort to polling for input because we need
17644ba319b5SDimitry Andric     // to receive async process events while in this loop.
176512b93ac6SEd Maste 
1766435933ddSDimitry Andric     halfdelay(delay_in_tenths_of_a_second); // Poll using some number of tenths
1767435933ddSDimitry Andric                                             // of seconds seconds when calling
1768435933ddSDimitry Andric                                             // Window::GetChar()
176912b93ac6SEd Maste 
1770435933ddSDimitry Andric     ListenerSP listener_sp(
1771435933ddSDimitry Andric         Listener::MakeListener("lldb.IOHandler.curses.Application"));
177212b93ac6SEd Maste     ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass());
177312b93ac6SEd Maste     ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass());
177412b93ac6SEd Maste     ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass());
177512b93ac6SEd Maste     debugger.EnableForwardEvents(listener_sp);
177612b93ac6SEd Maste 
177712b93ac6SEd Maste     bool update = true;
177812b93ac6SEd Maste #if defined(__APPLE__)
177912b93ac6SEd Maste     std::deque<int> escape_chars;
178012b93ac6SEd Maste #endif
178112b93ac6SEd Maste 
1782435933ddSDimitry Andric     while (!done) {
1783435933ddSDimitry Andric       if (update) {
178412b93ac6SEd Maste         m_window_sp->Draw(false);
17854ba319b5SDimitry Andric         // All windows should be calling Window::DeferredRefresh() instead of
17864ba319b5SDimitry Andric         // Window::Refresh() so we can do a single update and avoid any screen
17874ba319b5SDimitry Andric         // blinking
178812b93ac6SEd Maste         update_panels();
178912b93ac6SEd Maste 
1790435933ddSDimitry Andric         // Cursor hiding isn't working on MacOSX, so hide it in the top left
1791435933ddSDimitry Andric         // corner
179212b93ac6SEd Maste         m_window_sp->MoveCursor(0, 0);
179312b93ac6SEd Maste 
179412b93ac6SEd Maste         doupdate();
179512b93ac6SEd Maste         update = false;
179612b93ac6SEd Maste       }
179712b93ac6SEd Maste 
179812b93ac6SEd Maste #if defined(__APPLE__)
17994ba319b5SDimitry Andric       // Terminal.app doesn't map its function keys correctly, F1-F4 default
18004ba319b5SDimitry Andric       // to: \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if
1801435933ddSDimitry Andric       // possible
180212b93ac6SEd Maste       int ch;
180312b93ac6SEd Maste       if (escape_chars.empty())
180412b93ac6SEd Maste         ch = m_window_sp->GetChar();
1805435933ddSDimitry Andric       else {
180612b93ac6SEd Maste         ch = escape_chars.front();
180712b93ac6SEd Maste         escape_chars.pop_front();
180812b93ac6SEd Maste       }
1809435933ddSDimitry Andric       if (ch == KEY_ESCAPE) {
181012b93ac6SEd Maste         int ch2 = m_window_sp->GetChar();
1811435933ddSDimitry Andric         if (ch2 == 'O') {
181212b93ac6SEd Maste           int ch3 = m_window_sp->GetChar();
1813435933ddSDimitry Andric           switch (ch3) {
1814435933ddSDimitry Andric           case 'P':
1815435933ddSDimitry Andric             ch = KEY_F(1);
1816435933ddSDimitry Andric             break;
1817435933ddSDimitry Andric           case 'Q':
1818435933ddSDimitry Andric             ch = KEY_F(2);
1819435933ddSDimitry Andric             break;
1820435933ddSDimitry Andric           case 'R':
1821435933ddSDimitry Andric             ch = KEY_F(3);
1822435933ddSDimitry Andric             break;
1823435933ddSDimitry Andric           case 'S':
1824435933ddSDimitry Andric             ch = KEY_F(4);
1825435933ddSDimitry Andric             break;
182612b93ac6SEd Maste           default:
182712b93ac6SEd Maste             escape_chars.push_back(ch2);
182812b93ac6SEd Maste             if (ch3 != -1)
182912b93ac6SEd Maste               escape_chars.push_back(ch3);
183012b93ac6SEd Maste             break;
183112b93ac6SEd Maste           }
1832435933ddSDimitry Andric         } else if (ch2 != -1)
183312b93ac6SEd Maste           escape_chars.push_back(ch2);
183412b93ac6SEd Maste       }
183512b93ac6SEd Maste #else
183612b93ac6SEd Maste       int ch = m_window_sp->GetChar();
183712b93ac6SEd Maste 
183812b93ac6SEd Maste #endif
1839435933ddSDimitry Andric       if (ch == -1) {
1840435933ddSDimitry Andric         if (feof(m_in) || ferror(m_in)) {
184112b93ac6SEd Maste           done = true;
1842435933ddSDimitry Andric         } else {
184312b93ac6SEd Maste           // Just a timeout from using halfdelay(), check for events
184412b93ac6SEd Maste           EventSP event_sp;
1845435933ddSDimitry Andric           while (listener_sp->PeekAtNextEvent()) {
1846435933ddSDimitry Andric             listener_sp->GetEvent(event_sp, std::chrono::seconds(0));
184712b93ac6SEd Maste 
1848435933ddSDimitry Andric             if (event_sp) {
184912b93ac6SEd Maste               Broadcaster *broadcaster = event_sp->GetBroadcaster();
1850435933ddSDimitry Andric               if (broadcaster) {
185112b93ac6SEd Maste                 // uint32_t event_type = event_sp->GetType();
1852435933ddSDimitry Andric                 ConstString broadcaster_class(
1853435933ddSDimitry Andric                     broadcaster->GetBroadcasterClass());
1854435933ddSDimitry Andric                 if (broadcaster_class == broadcaster_class_process) {
1855435933ddSDimitry Andric                   debugger.GetCommandInterpreter().UpdateExecutionContext(
1856435933ddSDimitry Andric                       nullptr);
185712b93ac6SEd Maste                   update = true;
185812b93ac6SEd Maste                   continue; // Don't get any key, just update our view
185912b93ac6SEd Maste                 }
186012b93ac6SEd Maste               }
186112b93ac6SEd Maste             }
186212b93ac6SEd Maste           }
186312b93ac6SEd Maste         }
1864435933ddSDimitry Andric       } else {
186512b93ac6SEd Maste         HandleCharResult key_result = m_window_sp->HandleChar(ch);
1866435933ddSDimitry Andric         switch (key_result) {
186712b93ac6SEd Maste         case eKeyHandled:
18684bb0738eSEd Maste           debugger.GetCommandInterpreter().UpdateExecutionContext(nullptr);
186912b93ac6SEd Maste           update = true;
187012b93ac6SEd Maste           break;
187112b93ac6SEd Maste         case eKeyNotHandled:
187212b93ac6SEd Maste           break;
187312b93ac6SEd Maste         case eQuitApplication:
187412b93ac6SEd Maste           done = true;
187512b93ac6SEd Maste           break;
187612b93ac6SEd Maste         }
187712b93ac6SEd Maste       }
187812b93ac6SEd Maste     }
187912b93ac6SEd Maste 
188012b93ac6SEd Maste     debugger.CancelForwardEvents(listener_sp);
188112b93ac6SEd Maste   }
188212b93ac6SEd Maste 
GetMainWindow()1883435933ddSDimitry Andric   WindowSP &GetMainWindow() {
188412b93ac6SEd Maste     if (!m_window_sp)
188512b93ac6SEd Maste       m_window_sp.reset(new Window("main", stdscr, false));
188612b93ac6SEd Maste     return m_window_sp;
188712b93ac6SEd Maste   }
188812b93ac6SEd Maste 
GetWindowDelegates()1889435933ddSDimitry Andric   WindowDelegates &GetWindowDelegates() { return m_window_delegates; }
189012b93ac6SEd Maste 
189112b93ac6SEd Maste protected:
189212b93ac6SEd Maste   WindowSP m_window_sp;
189312b93ac6SEd Maste   WindowDelegates m_window_delegates;
189412b93ac6SEd Maste   SCREEN *m_screen;
189512b93ac6SEd Maste   FILE *m_in;
189612b93ac6SEd Maste   FILE *m_out;
189712b93ac6SEd Maste };
189812b93ac6SEd Maste 
189912b93ac6SEd Maste } // namespace curses
190012b93ac6SEd Maste 
190112b93ac6SEd Maste using namespace curses;
190212b93ac6SEd Maste 
1903435933ddSDimitry Andric struct Row {
1904435933ddSDimitry Andric   ValueObjectManager value;
190512b93ac6SEd Maste   Row *parent;
1906435933ddSDimitry Andric   // The process stop ID when the children were calculated.
1907435933ddSDimitry Andric   uint32_t children_stop_id;
190812b93ac6SEd Maste   int row_idx;
190912b93ac6SEd Maste   int x;
191012b93ac6SEd Maste   int y;
191112b93ac6SEd Maste   bool might_have_children;
191212b93ac6SEd Maste   bool expanded;
191312b93ac6SEd Maste   bool calculated_children;
191412b93ac6SEd Maste   std::vector<Row> children;
191512b93ac6SEd Maste 
RowRow1916435933ddSDimitry Andric   Row(const ValueObjectSP &v, Row *p)
1917435933ddSDimitry Andric       : value(v, lldb::eDynamicDontRunTarget, true), parent(p), row_idx(0),
1918435933ddSDimitry Andric         x(1), y(1), might_have_children(v ? v->MightHaveChildren() : false),
1919435933ddSDimitry Andric         expanded(false), calculated_children(false), children() {}
192012b93ac6SEd Maste 
GetDepthRow1921435933ddSDimitry Andric   size_t GetDepth() const {
192212b93ac6SEd Maste     if (parent)
192312b93ac6SEd Maste       return 1 + parent->GetDepth();
192412b93ac6SEd Maste     return 0;
192512b93ac6SEd Maste   }
192612b93ac6SEd Maste 
ExpandRow1927435933ddSDimitry Andric   void Expand() {
192812b93ac6SEd Maste     expanded = true;
1929435933ddSDimitry Andric   }
1930435933ddSDimitry Andric 
GetChildrenRow1931435933ddSDimitry Andric   std::vector<Row> &GetChildren() {
1932435933ddSDimitry Andric     ProcessSP process_sp = value.GetProcessSP();
1933435933ddSDimitry Andric     auto stop_id = process_sp->GetStopID();
1934435933ddSDimitry Andric     if (process_sp && stop_id != children_stop_id) {
1935435933ddSDimitry Andric       children_stop_id = stop_id;
1936435933ddSDimitry Andric       calculated_children = false;
1937435933ddSDimitry Andric     }
1938435933ddSDimitry Andric     if (!calculated_children) {
1939435933ddSDimitry Andric       children.clear();
194012b93ac6SEd Maste       calculated_children = true;
1941435933ddSDimitry Andric       ValueObjectSP valobj = value.GetSP();
1942435933ddSDimitry Andric       if (valobj) {
194312b93ac6SEd Maste         const size_t num_children = valobj->GetNumChildren();
1944435933ddSDimitry Andric         for (size_t i = 0; i < num_children; ++i) {
194512b93ac6SEd Maste           children.push_back(Row(valobj->GetChildAtIndex(i, true), this));
194612b93ac6SEd Maste         }
194712b93ac6SEd Maste       }
194812b93ac6SEd Maste     }
1949435933ddSDimitry Andric     return children;
195012b93ac6SEd Maste   }
195112b93ac6SEd Maste 
UnexpandRow1952435933ddSDimitry Andric   void Unexpand() {
195312b93ac6SEd Maste     expanded = false;
1954435933ddSDimitry Andric     calculated_children = false;
1955435933ddSDimitry Andric     children.clear();
195612b93ac6SEd Maste   }
195712b93ac6SEd Maste 
DrawTreeRow1958435933ddSDimitry Andric   void DrawTree(Window &window) {
195912b93ac6SEd Maste     if (parent)
196012b93ac6SEd Maste       parent->DrawTreeForChild(window, this, 0);
196112b93ac6SEd Maste 
1962435933ddSDimitry Andric     if (might_have_children) {
19634ba319b5SDimitry Andric       // It we can get UTF8 characters to work we should try to use the
19644ba319b5SDimitry Andric       // "symbol" UTF8 string below
196512b93ac6SEd Maste       //            const char *symbol = "";
196612b93ac6SEd Maste       //            if (row.expanded)
196712b93ac6SEd Maste       //                symbol = "\xe2\x96\xbd ";
196812b93ac6SEd Maste       //            else
196912b93ac6SEd Maste       //                symbol = "\xe2\x96\xb7 ";
197012b93ac6SEd Maste       //            window.PutCString (symbol);
197112b93ac6SEd Maste 
19724ba319b5SDimitry Andric       // The ACS_DARROW and ACS_RARROW don't look very nice they are just a 'v'
19734ba319b5SDimitry Andric       // or '>' character...
197412b93ac6SEd Maste       //            if (expanded)
197512b93ac6SEd Maste       //                window.PutChar (ACS_DARROW);
197612b93ac6SEd Maste       //            else
197712b93ac6SEd Maste       //                window.PutChar (ACS_RARROW);
19784ba319b5SDimitry Andric       // Since we can't find any good looking right arrow/down arrow symbols,
19794ba319b5SDimitry Andric       // just use a diamond...
198012b93ac6SEd Maste       window.PutChar(ACS_DIAMOND);
198112b93ac6SEd Maste       window.PutChar(ACS_HLINE);
198212b93ac6SEd Maste     }
198312b93ac6SEd Maste   }
198412b93ac6SEd Maste 
DrawTreeForChildRow1985435933ddSDimitry Andric   void DrawTreeForChild(Window &window, Row *child, uint32_t reverse_depth) {
198612b93ac6SEd Maste     if (parent)
198712b93ac6SEd Maste       parent->DrawTreeForChild(window, this, reverse_depth + 1);
198812b93ac6SEd Maste 
1989435933ddSDimitry Andric     if (&GetChildren().back() == child) {
199012b93ac6SEd Maste       // Last child
1991435933ddSDimitry Andric       if (reverse_depth == 0) {
199212b93ac6SEd Maste         window.PutChar(ACS_LLCORNER);
199312b93ac6SEd Maste         window.PutChar(ACS_HLINE);
1994435933ddSDimitry Andric       } else {
199512b93ac6SEd Maste         window.PutChar(' ');
199612b93ac6SEd Maste         window.PutChar(' ');
199712b93ac6SEd Maste       }
1998435933ddSDimitry Andric     } else {
1999435933ddSDimitry Andric       if (reverse_depth == 0) {
200012b93ac6SEd Maste         window.PutChar(ACS_LTEE);
200112b93ac6SEd Maste         window.PutChar(ACS_HLINE);
2002435933ddSDimitry Andric       } else {
200312b93ac6SEd Maste         window.PutChar(ACS_VLINE);
200412b93ac6SEd Maste         window.PutChar(' ');
200512b93ac6SEd Maste       }
200612b93ac6SEd Maste     }
200712b93ac6SEd Maste   }
200812b93ac6SEd Maste };
200912b93ac6SEd Maste 
2010435933ddSDimitry Andric struct DisplayOptions {
201112b93ac6SEd Maste   bool show_types;
201212b93ac6SEd Maste };
201312b93ac6SEd Maste 
201412b93ac6SEd Maste class TreeItem;
201512b93ac6SEd Maste 
2016435933ddSDimitry Andric class TreeDelegate {
201712b93ac6SEd Maste public:
20184bb0738eSEd Maste   TreeDelegate() = default;
20199f2f44ceSEd Maste   virtual ~TreeDelegate() = default;
20209f2f44ceSEd Maste 
202112b93ac6SEd Maste   virtual void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) = 0;
202212b93ac6SEd Maste   virtual void TreeDelegateGenerateChildren(TreeItem &item) = 0;
2023435933ddSDimitry Andric   virtual bool TreeDelegateItemSelected(
2024435933ddSDimitry Andric       TreeItem &item) = 0; // Return true if we need to update views
202512b93ac6SEd Maste };
20269f2f44ceSEd Maste 
202712b93ac6SEd Maste typedef std::shared_ptr<TreeDelegate> TreeDelegateSP;
202812b93ac6SEd Maste 
2029435933ddSDimitry Andric class TreeItem {
203012b93ac6SEd Maste public:
TreeItem(TreeItem * parent,TreeDelegate & delegate,bool might_have_children)2031435933ddSDimitry Andric   TreeItem(TreeItem *parent, TreeDelegate &delegate, bool might_have_children)
2032435933ddSDimitry Andric       : m_parent(parent), m_delegate(delegate), m_user_data(nullptr),
2033435933ddSDimitry Andric         m_identifier(0), m_row_idx(-1), m_children(),
2034435933ddSDimitry Andric         m_might_have_children(might_have_children), m_is_expanded(false) {}
203512b93ac6SEd Maste 
operator =(const TreeItem & rhs)2036435933ddSDimitry Andric   TreeItem &operator=(const TreeItem &rhs) {
2037435933ddSDimitry Andric     if (this != &rhs) {
203812b93ac6SEd Maste       m_parent = rhs.m_parent;
203912b93ac6SEd Maste       m_delegate = rhs.m_delegate;
20400127ef0fSEd Maste       m_user_data = rhs.m_user_data;
204112b93ac6SEd Maste       m_identifier = rhs.m_identifier;
204212b93ac6SEd Maste       m_row_idx = rhs.m_row_idx;
204312b93ac6SEd Maste       m_children = rhs.m_children;
204412b93ac6SEd Maste       m_might_have_children = rhs.m_might_have_children;
204512b93ac6SEd Maste       m_is_expanded = rhs.m_is_expanded;
204612b93ac6SEd Maste     }
204712b93ac6SEd Maste     return *this;
204812b93ac6SEd Maste   }
204912b93ac6SEd Maste 
GetDepth() const2050435933ddSDimitry Andric   size_t GetDepth() const {
205112b93ac6SEd Maste     if (m_parent)
205212b93ac6SEd Maste       return 1 + m_parent->GetDepth();
205312b93ac6SEd Maste     return 0;
205412b93ac6SEd Maste   }
205512b93ac6SEd Maste 
GetRowIndex() const2056435933ddSDimitry Andric   int GetRowIndex() const { return m_row_idx; }
205712b93ac6SEd Maste 
ClearChildren()2058435933ddSDimitry Andric   void ClearChildren() { m_children.clear(); }
205912b93ac6SEd Maste 
Resize(size_t n,const TreeItem & t)2060435933ddSDimitry Andric   void Resize(size_t n, const TreeItem &t) { m_children.resize(n, t); }
206112b93ac6SEd Maste 
operator [](size_t i)2062435933ddSDimitry Andric   TreeItem &operator[](size_t i) { return m_children[i]; }
206312b93ac6SEd Maste 
SetRowIndex(int row_idx)2064435933ddSDimitry Andric   void SetRowIndex(int row_idx) { m_row_idx = row_idx; }
206512b93ac6SEd Maste 
GetNumChildren()2066435933ddSDimitry Andric   size_t GetNumChildren() {
206712b93ac6SEd Maste     m_delegate.TreeDelegateGenerateChildren(*this);
206812b93ac6SEd Maste     return m_children.size();
206912b93ac6SEd Maste   }
207012b93ac6SEd Maste 
ItemWasSelected()2071435933ddSDimitry Andric   void ItemWasSelected() { m_delegate.TreeDelegateItemSelected(*this); }
20729f2f44ceSEd Maste 
CalculateRowIndexes(int & row_idx)2073435933ddSDimitry Andric   void CalculateRowIndexes(int &row_idx) {
207412b93ac6SEd Maste     SetRowIndex(row_idx);
207512b93ac6SEd Maste     ++row_idx;
207612b93ac6SEd Maste 
20770127ef0fSEd Maste     const bool expanded = IsExpanded();
20780127ef0fSEd Maste 
20794ba319b5SDimitry Andric     // The root item must calculate its children, or we must calculate the
20804ba319b5SDimitry Andric     // number of children if the item is expanded
20814bb0738eSEd Maste     if (m_parent == nullptr || expanded)
208212b93ac6SEd Maste       GetNumChildren();
208312b93ac6SEd Maste 
2084435933ddSDimitry Andric     for (auto &item : m_children) {
208512b93ac6SEd Maste       if (expanded)
208612b93ac6SEd Maste         item.CalculateRowIndexes(row_idx);
208712b93ac6SEd Maste       else
208812b93ac6SEd Maste         item.SetRowIndex(-1);
208912b93ac6SEd Maste     }
209012b93ac6SEd Maste   }
209112b93ac6SEd Maste 
GetParent()2092435933ddSDimitry Andric   TreeItem *GetParent() { return m_parent; }
209312b93ac6SEd Maste 
IsExpanded() const2094435933ddSDimitry Andric   bool IsExpanded() const { return m_is_expanded; }
209512b93ac6SEd Maste 
Expand()2096435933ddSDimitry Andric   void Expand() { m_is_expanded = true; }
209712b93ac6SEd Maste 
Unexpand()2098435933ddSDimitry Andric   void Unexpand() { m_is_expanded = false; }
209912b93ac6SEd Maste 
Draw(Window & window,const int first_visible_row,const uint32_t selected_row_idx,int & row_idx,int & num_rows_left)2100435933ddSDimitry Andric   bool Draw(Window &window, const int first_visible_row,
2101435933ddSDimitry Andric             const uint32_t selected_row_idx, int &row_idx, int &num_rows_left) {
210212b93ac6SEd Maste     if (num_rows_left <= 0)
210312b93ac6SEd Maste       return false;
210412b93ac6SEd Maste 
2105435933ddSDimitry Andric     if (m_row_idx >= first_visible_row) {
210612b93ac6SEd Maste       window.MoveCursor(2, row_idx + 1);
210712b93ac6SEd Maste 
210812b93ac6SEd Maste       if (m_parent)
210912b93ac6SEd Maste         m_parent->DrawTreeForChild(window, this, 0);
211012b93ac6SEd Maste 
2111435933ddSDimitry Andric       if (m_might_have_children) {
2112435933ddSDimitry Andric         // It we can get UTF8 characters to work we should try to use the
21134ba319b5SDimitry Andric         // "symbol" UTF8 string below
211412b93ac6SEd Maste         //            const char *symbol = "";
211512b93ac6SEd Maste         //            if (row.expanded)
211612b93ac6SEd Maste         //                symbol = "\xe2\x96\xbd ";
211712b93ac6SEd Maste         //            else
211812b93ac6SEd Maste         //                symbol = "\xe2\x96\xb7 ";
211912b93ac6SEd Maste         //            window.PutCString (symbol);
212012b93ac6SEd Maste 
212112b93ac6SEd Maste         // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
212212b93ac6SEd Maste         // 'v' or '>' character...
212312b93ac6SEd Maste         //            if (expanded)
212412b93ac6SEd Maste         //                window.PutChar (ACS_DARROW);
212512b93ac6SEd Maste         //            else
212612b93ac6SEd Maste         //                window.PutChar (ACS_RARROW);
21274ba319b5SDimitry Andric         // Since we can't find any good looking right arrow/down arrow symbols,
21284ba319b5SDimitry Andric         // just use a diamond...
212912b93ac6SEd Maste         window.PutChar(ACS_DIAMOND);
213012b93ac6SEd Maste         window.PutChar(ACS_HLINE);
213112b93ac6SEd Maste       }
2132435933ddSDimitry Andric       bool highlight = (selected_row_idx == static_cast<size_t>(m_row_idx)) &&
2133435933ddSDimitry Andric                        window.IsActive();
213412b93ac6SEd Maste 
213512b93ac6SEd Maste       if (highlight)
213612b93ac6SEd Maste         window.AttributeOn(A_REVERSE);
213712b93ac6SEd Maste 
213812b93ac6SEd Maste       m_delegate.TreeDelegateDrawTreeItem(*this, window);
213912b93ac6SEd Maste 
214012b93ac6SEd Maste       if (highlight)
214112b93ac6SEd Maste         window.AttributeOff(A_REVERSE);
214212b93ac6SEd Maste       ++row_idx;
214312b93ac6SEd Maste       --num_rows_left;
214412b93ac6SEd Maste     }
214512b93ac6SEd Maste 
214612b93ac6SEd Maste     if (num_rows_left <= 0)
214712b93ac6SEd Maste       return false; // We are done drawing...
214812b93ac6SEd Maste 
2149435933ddSDimitry Andric     if (IsExpanded()) {
2150435933ddSDimitry Andric       for (auto &item : m_children) {
21514ba319b5SDimitry Andric         // If we displayed all the rows and item.Draw() returns false we are
21524ba319b5SDimitry Andric         // done drawing and can exit this for loop
2153435933ddSDimitry Andric         if (!item.Draw(window, first_visible_row, selected_row_idx, row_idx,
2154435933ddSDimitry Andric                        num_rows_left))
215512b93ac6SEd Maste           break;
215612b93ac6SEd Maste       }
215712b93ac6SEd Maste     }
215812b93ac6SEd Maste     return num_rows_left >= 0; // Return true if not done drawing yet
215912b93ac6SEd Maste   }
216012b93ac6SEd Maste 
DrawTreeForChild(Window & window,TreeItem * child,uint32_t reverse_depth)2161435933ddSDimitry Andric   void DrawTreeForChild(Window &window, TreeItem *child,
2162435933ddSDimitry Andric                         uint32_t reverse_depth) {
216312b93ac6SEd Maste     if (m_parent)
216412b93ac6SEd Maste       m_parent->DrawTreeForChild(window, this, reverse_depth + 1);
216512b93ac6SEd Maste 
2166435933ddSDimitry Andric     if (&m_children.back() == child) {
216712b93ac6SEd Maste       // Last child
2168435933ddSDimitry Andric       if (reverse_depth == 0) {
216912b93ac6SEd Maste         window.PutChar(ACS_LLCORNER);
217012b93ac6SEd Maste         window.PutChar(ACS_HLINE);
2171435933ddSDimitry Andric       } else {
217212b93ac6SEd Maste         window.PutChar(' ');
217312b93ac6SEd Maste         window.PutChar(' ');
217412b93ac6SEd Maste       }
2175435933ddSDimitry Andric     } else {
2176435933ddSDimitry Andric       if (reverse_depth == 0) {
217712b93ac6SEd Maste         window.PutChar(ACS_LTEE);
217812b93ac6SEd Maste         window.PutChar(ACS_HLINE);
2179435933ddSDimitry Andric       } else {
218012b93ac6SEd Maste         window.PutChar(ACS_VLINE);
218112b93ac6SEd Maste         window.PutChar(' ');
218212b93ac6SEd Maste       }
218312b93ac6SEd Maste     }
218412b93ac6SEd Maste   }
218512b93ac6SEd Maste 
GetItemForRowIndex(uint32_t row_idx)2186435933ddSDimitry Andric   TreeItem *GetItemForRowIndex(uint32_t row_idx) {
21870127ef0fSEd Maste     if (static_cast<uint32_t>(m_row_idx) == row_idx)
218812b93ac6SEd Maste       return this;
218912b93ac6SEd Maste     if (m_children.empty())
21904bb0738eSEd Maste       return nullptr;
2191435933ddSDimitry Andric     if (IsExpanded()) {
2192435933ddSDimitry Andric       for (auto &item : m_children) {
219312b93ac6SEd Maste         TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx);
219412b93ac6SEd Maste         if (selected_item_ptr)
219512b93ac6SEd Maste           return selected_item_ptr;
219612b93ac6SEd Maste       }
219712b93ac6SEd Maste     }
21984bb0738eSEd Maste     return nullptr;
219912b93ac6SEd Maste   }
220012b93ac6SEd Maste 
GetUserData() const2201435933ddSDimitry Andric   void *GetUserData() const { return m_user_data; }
22020127ef0fSEd Maste 
SetUserData(void * user_data)2203435933ddSDimitry Andric   void SetUserData(void *user_data) { m_user_data = user_data; }
22040127ef0fSEd Maste 
GetIdentifier() const2205435933ddSDimitry Andric   uint64_t GetIdentifier() const { return m_identifier; }
220612b93ac6SEd Maste 
SetIdentifier(uint64_t identifier)2207435933ddSDimitry Andric   void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
220812b93ac6SEd Maste 
SetMightHaveChildren(bool b)2209435933ddSDimitry Andric   void SetMightHaveChildren(bool b) { m_might_have_children = b; }
22100127ef0fSEd Maste 
221112b93ac6SEd Maste protected:
221212b93ac6SEd Maste   TreeItem *m_parent;
221312b93ac6SEd Maste   TreeDelegate &m_delegate;
22140127ef0fSEd Maste   void *m_user_data;
221512b93ac6SEd Maste   uint64_t m_identifier;
2216435933ddSDimitry Andric   int m_row_idx; // Zero based visible row index, -1 if not visible or for the
2217435933ddSDimitry Andric                  // root item
221812b93ac6SEd Maste   std::vector<TreeItem> m_children;
221912b93ac6SEd Maste   bool m_might_have_children;
222012b93ac6SEd Maste   bool m_is_expanded;
222112b93ac6SEd Maste };
222212b93ac6SEd Maste 
2223435933ddSDimitry Andric class TreeWindowDelegate : public WindowDelegate {
222412b93ac6SEd Maste public:
TreeWindowDelegate(Debugger & debugger,const TreeDelegateSP & delegate_sp)2225435933ddSDimitry Andric   TreeWindowDelegate(Debugger &debugger, const TreeDelegateSP &delegate_sp)
2226435933ddSDimitry Andric       : m_debugger(debugger), m_delegate_sp(delegate_sp),
2227435933ddSDimitry Andric         m_root(nullptr, *delegate_sp, true), m_selected_item(nullptr),
2228435933ddSDimitry Andric         m_num_rows(0), m_selected_row_idx(0), m_first_visible_row(0),
2229435933ddSDimitry Andric         m_min_x(0), m_min_y(0), m_max_x(0), m_max_y(0) {}
223012b93ac6SEd Maste 
NumVisibleRows() const2231435933ddSDimitry Andric   int NumVisibleRows() const { return m_max_y - m_min_y; }
223212b93ac6SEd Maste 
WindowDelegateDraw(Window & window,bool force)2233435933ddSDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
2234435933ddSDimitry Andric     ExecutionContext exe_ctx(
2235435933ddSDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext());
223612b93ac6SEd Maste     Process *process = exe_ctx.GetProcessPtr();
223712b93ac6SEd Maste 
223812b93ac6SEd Maste     bool display_content = false;
2239435933ddSDimitry Andric     if (process) {
224012b93ac6SEd Maste       StateType state = process->GetState();
2241435933ddSDimitry Andric       if (StateIsStoppedState(state, true)) {
224212b93ac6SEd Maste         // We are stopped, so it is ok to
224312b93ac6SEd Maste         display_content = true;
2244435933ddSDimitry Andric       } else if (StateIsRunningState(state)) {
224512b93ac6SEd Maste         return true; // Don't do any updating when we are running
224612b93ac6SEd Maste       }
224712b93ac6SEd Maste     }
224812b93ac6SEd Maste 
224912b93ac6SEd Maste     m_min_x = 2;
225012b93ac6SEd Maste     m_min_y = 1;
225112b93ac6SEd Maste     m_max_x = window.GetWidth() - 1;
225212b93ac6SEd Maste     m_max_y = window.GetHeight() - 1;
225312b93ac6SEd Maste 
225412b93ac6SEd Maste     window.Erase();
225512b93ac6SEd Maste     window.DrawTitleBox(window.GetName());
225612b93ac6SEd Maste 
2257435933ddSDimitry Andric     if (display_content) {
225812b93ac6SEd Maste       const int num_visible_rows = NumVisibleRows();
225912b93ac6SEd Maste       m_num_rows = 0;
226012b93ac6SEd Maste       m_root.CalculateRowIndexes(m_num_rows);
226112b93ac6SEd Maste 
22624ba319b5SDimitry Andric       // If we unexpanded while having something selected our total number of
22634ba319b5SDimitry Andric       // rows is less than the num visible rows, then make sure we show all the
22644ba319b5SDimitry Andric       // rows by setting the first visible row accordingly.
226512b93ac6SEd Maste       if (m_first_visible_row > 0 && m_num_rows < num_visible_rows)
226612b93ac6SEd Maste         m_first_visible_row = 0;
226712b93ac6SEd Maste 
226812b93ac6SEd Maste       // Make sure the selected row is always visible
226912b93ac6SEd Maste       if (m_selected_row_idx < m_first_visible_row)
227012b93ac6SEd Maste         m_first_visible_row = m_selected_row_idx;
227112b93ac6SEd Maste       else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
227212b93ac6SEd Maste         m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
227312b93ac6SEd Maste 
227412b93ac6SEd Maste       int row_idx = 0;
227512b93ac6SEd Maste       int num_rows_left = num_visible_rows;
2276435933ddSDimitry Andric       m_root.Draw(window, m_first_visible_row, m_selected_row_idx, row_idx,
2277435933ddSDimitry Andric                   num_rows_left);
227812b93ac6SEd Maste       // Get the selected row
227912b93ac6SEd Maste       m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
2280435933ddSDimitry Andric     } else {
22814bb0738eSEd Maste       m_selected_item = nullptr;
228212b93ac6SEd Maste     }
228312b93ac6SEd Maste 
228412b93ac6SEd Maste     window.DeferredRefresh();
228512b93ac6SEd Maste 
228612b93ac6SEd Maste     return true; // Drawing handled
228712b93ac6SEd Maste   }
228812b93ac6SEd Maste 
WindowDelegateGetHelpText()2289435933ddSDimitry Andric   const char *WindowDelegateGetHelpText() override {
229012b93ac6SEd Maste     return "Thread window keyboard shortcuts:";
229112b93ac6SEd Maste   }
229212b93ac6SEd Maste 
WindowDelegateGetKeyHelp()2293435933ddSDimitry Andric   KeyHelp *WindowDelegateGetKeyHelp() override {
229412b93ac6SEd Maste     static curses::KeyHelp g_source_view_key_help[] = {
229512b93ac6SEd Maste         {KEY_UP, "Select previous item"},
229612b93ac6SEd Maste         {KEY_DOWN, "Select next item"},
229712b93ac6SEd Maste         {KEY_RIGHT, "Expand the selected item"},
2298435933ddSDimitry Andric         {KEY_LEFT,
2299435933ddSDimitry Andric          "Unexpand the selected item or select parent if not expanded"},
230012b93ac6SEd Maste         {KEY_PPAGE, "Page up"},
230112b93ac6SEd Maste         {KEY_NPAGE, "Page down"},
230212b93ac6SEd Maste         {'h', "Show help dialog"},
230312b93ac6SEd Maste         {' ', "Toggle item expansion"},
230412b93ac6SEd Maste         {',', "Page up"},
230512b93ac6SEd Maste         {'.', "Page down"},
2306435933ddSDimitry Andric         {'\0', nullptr}};
230712b93ac6SEd Maste     return g_source_view_key_help;
230812b93ac6SEd Maste   }
230912b93ac6SEd Maste 
WindowDelegateHandleChar(Window & window,int c)2310435933ddSDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
2311435933ddSDimitry Andric     switch (c) {
231212b93ac6SEd Maste     case ',':
231312b93ac6SEd Maste     case KEY_PPAGE:
231412b93ac6SEd Maste       // Page up key
2315435933ddSDimitry Andric       if (m_first_visible_row > 0) {
231612b93ac6SEd Maste         if (m_first_visible_row > m_max_y)
231712b93ac6SEd Maste           m_first_visible_row -= m_max_y;
231812b93ac6SEd Maste         else
231912b93ac6SEd Maste           m_first_visible_row = 0;
232012b93ac6SEd Maste         m_selected_row_idx = m_first_visible_row;
232112b93ac6SEd Maste         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
232212b93ac6SEd Maste         if (m_selected_item)
232312b93ac6SEd Maste           m_selected_item->ItemWasSelected();
232412b93ac6SEd Maste       }
232512b93ac6SEd Maste       return eKeyHandled;
232612b93ac6SEd Maste 
232712b93ac6SEd Maste     case '.':
232812b93ac6SEd Maste     case KEY_NPAGE:
232912b93ac6SEd Maste       // Page down key
2330435933ddSDimitry Andric       if (m_num_rows > m_max_y) {
2331435933ddSDimitry Andric         if (m_first_visible_row + m_max_y < m_num_rows) {
233212b93ac6SEd Maste           m_first_visible_row += m_max_y;
233312b93ac6SEd Maste           m_selected_row_idx = m_first_visible_row;
233412b93ac6SEd Maste           m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
233512b93ac6SEd Maste           if (m_selected_item)
233612b93ac6SEd Maste             m_selected_item->ItemWasSelected();
233712b93ac6SEd Maste         }
233812b93ac6SEd Maste       }
233912b93ac6SEd Maste       return eKeyHandled;
234012b93ac6SEd Maste 
234112b93ac6SEd Maste     case KEY_UP:
2342435933ddSDimitry Andric       if (m_selected_row_idx > 0) {
234312b93ac6SEd Maste         --m_selected_row_idx;
234412b93ac6SEd Maste         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
234512b93ac6SEd Maste         if (m_selected_item)
234612b93ac6SEd Maste           m_selected_item->ItemWasSelected();
234712b93ac6SEd Maste       }
234812b93ac6SEd Maste       return eKeyHandled;
23499f2f44ceSEd Maste 
235012b93ac6SEd Maste     case KEY_DOWN:
2351435933ddSDimitry Andric       if (m_selected_row_idx + 1 < m_num_rows) {
235212b93ac6SEd Maste         ++m_selected_row_idx;
235312b93ac6SEd Maste         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
235412b93ac6SEd Maste         if (m_selected_item)
235512b93ac6SEd Maste           m_selected_item->ItemWasSelected();
235612b93ac6SEd Maste       }
235712b93ac6SEd Maste       return eKeyHandled;
235812b93ac6SEd Maste 
235912b93ac6SEd Maste     case KEY_RIGHT:
2360435933ddSDimitry Andric       if (m_selected_item) {
236112b93ac6SEd Maste         if (!m_selected_item->IsExpanded())
236212b93ac6SEd Maste           m_selected_item->Expand();
236312b93ac6SEd Maste       }
236412b93ac6SEd Maste       return eKeyHandled;
236512b93ac6SEd Maste 
236612b93ac6SEd Maste     case KEY_LEFT:
2367435933ddSDimitry Andric       if (m_selected_item) {
236812b93ac6SEd Maste         if (m_selected_item->IsExpanded())
236912b93ac6SEd Maste           m_selected_item->Unexpand();
2370435933ddSDimitry Andric         else if (m_selected_item->GetParent()) {
237112b93ac6SEd Maste           m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex();
237212b93ac6SEd Maste           m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
237312b93ac6SEd Maste           if (m_selected_item)
237412b93ac6SEd Maste             m_selected_item->ItemWasSelected();
237512b93ac6SEd Maste         }
237612b93ac6SEd Maste       }
237712b93ac6SEd Maste       return eKeyHandled;
237812b93ac6SEd Maste 
237912b93ac6SEd Maste     case ' ':
238012b93ac6SEd Maste       // Toggle expansion state when SPACE is pressed
2381435933ddSDimitry Andric       if (m_selected_item) {
238212b93ac6SEd Maste         if (m_selected_item->IsExpanded())
238312b93ac6SEd Maste           m_selected_item->Unexpand();
238412b93ac6SEd Maste         else
238512b93ac6SEd Maste           m_selected_item->Expand();
238612b93ac6SEd Maste       }
238712b93ac6SEd Maste       return eKeyHandled;
238812b93ac6SEd Maste 
238912b93ac6SEd Maste     case 'h':
239012b93ac6SEd Maste       window.CreateHelpSubwindow();
239112b93ac6SEd Maste       return eKeyHandled;
239212b93ac6SEd Maste 
239312b93ac6SEd Maste     default:
239412b93ac6SEd Maste       break;
239512b93ac6SEd Maste     }
239612b93ac6SEd Maste     return eKeyNotHandled;
239712b93ac6SEd Maste   }
239812b93ac6SEd Maste 
239912b93ac6SEd Maste protected:
240012b93ac6SEd Maste   Debugger &m_debugger;
240112b93ac6SEd Maste   TreeDelegateSP m_delegate_sp;
240212b93ac6SEd Maste   TreeItem m_root;
240312b93ac6SEd Maste   TreeItem *m_selected_item;
240412b93ac6SEd Maste   int m_num_rows;
240512b93ac6SEd Maste   int m_selected_row_idx;
240612b93ac6SEd Maste   int m_first_visible_row;
240712b93ac6SEd Maste   int m_min_x;
240812b93ac6SEd Maste   int m_min_y;
240912b93ac6SEd Maste   int m_max_x;
241012b93ac6SEd Maste   int m_max_y;
241112b93ac6SEd Maste };
241212b93ac6SEd Maste 
2413435933ddSDimitry Andric class FrameTreeDelegate : public TreeDelegate {
241412b93ac6SEd Maste public:
FrameTreeDelegate()2415435933ddSDimitry Andric   FrameTreeDelegate() : TreeDelegate() {
2416435933ddSDimitry Andric     FormatEntity::Parse(
2417435933ddSDimitry Andric         "frame #${frame.index}: {${function.name}${function.pc-offset}}}",
24181c3bbb01SEd Maste         m_format);
241912b93ac6SEd Maste   }
242012b93ac6SEd Maste 
24219f2f44ceSEd Maste   ~FrameTreeDelegate() override = default;
242212b93ac6SEd Maste 
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)2423435933ddSDimitry Andric   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
24240127ef0fSEd Maste     Thread *thread = (Thread *)item.GetUserData();
2425435933ddSDimitry Andric     if (thread) {
242612b93ac6SEd Maste       const uint64_t frame_idx = item.GetIdentifier();
24270127ef0fSEd Maste       StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_idx);
2428435933ddSDimitry Andric       if (frame_sp) {
242912b93ac6SEd Maste         StreamString strm;
2430435933ddSDimitry Andric         const SymbolContext &sc =
2431435933ddSDimitry Andric             frame_sp->GetSymbolContext(eSymbolContextEverything);
243212b93ac6SEd Maste         ExecutionContext exe_ctx(frame_sp);
2433435933ddSDimitry Andric         if (FormatEntity::Format(m_format, strm, &sc, &exe_ctx, nullptr,
2434435933ddSDimitry Andric                                  nullptr, false, false)) {
243512b93ac6SEd Maste           int right_pad = 1;
2436435933ddSDimitry Andric           window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad);
243712b93ac6SEd Maste         }
243812b93ac6SEd Maste       }
243912b93ac6SEd Maste     }
244012b93ac6SEd Maste   }
24419f2f44ceSEd Maste 
TreeDelegateGenerateChildren(TreeItem & item)2442435933ddSDimitry Andric   void TreeDelegateGenerateChildren(TreeItem &item) override {
244312b93ac6SEd Maste     // No children for frames yet...
244412b93ac6SEd Maste   }
244512b93ac6SEd Maste 
TreeDelegateItemSelected(TreeItem & item)2446435933ddSDimitry Andric   bool TreeDelegateItemSelected(TreeItem &item) override {
24470127ef0fSEd Maste     Thread *thread = (Thread *)item.GetUserData();
2448435933ddSDimitry Andric     if (thread) {
2449435933ddSDimitry Andric       thread->GetProcess()->GetThreadList().SetSelectedThreadByID(
2450435933ddSDimitry Andric           thread->GetID());
245112b93ac6SEd Maste       const uint64_t frame_idx = item.GetIdentifier();
24520127ef0fSEd Maste       thread->SetSelectedFrameByIndex(frame_idx);
245312b93ac6SEd Maste       return true;
245412b93ac6SEd Maste     }
245512b93ac6SEd Maste     return false;
245612b93ac6SEd Maste   }
24579f2f44ceSEd Maste 
24581c3bbb01SEd Maste protected:
24591c3bbb01SEd Maste   FormatEntity::Entry m_format;
246012b93ac6SEd Maste };
246112b93ac6SEd Maste 
2462435933ddSDimitry Andric class ThreadTreeDelegate : public TreeDelegate {
246312b93ac6SEd Maste public:
ThreadTreeDelegate(Debugger & debugger)2464435933ddSDimitry Andric   ThreadTreeDelegate(Debugger &debugger)
2465435933ddSDimitry Andric       : TreeDelegate(), m_debugger(debugger), m_tid(LLDB_INVALID_THREAD_ID),
2466435933ddSDimitry Andric         m_stop_id(UINT32_MAX) {
2467435933ddSDimitry Andric     FormatEntity::Parse("thread #${thread.index}: tid = ${thread.id}{, stop "
2468435933ddSDimitry Andric                         "reason = ${thread.stop-reason}}",
24691c3bbb01SEd Maste                         m_format);
247012b93ac6SEd Maste   }
247112b93ac6SEd Maste 
24729f2f44ceSEd Maste   ~ThreadTreeDelegate() override = default;
247312b93ac6SEd Maste 
GetProcess()2474435933ddSDimitry Andric   ProcessSP GetProcess() {
2475435933ddSDimitry Andric     return m_debugger.GetCommandInterpreter()
2476435933ddSDimitry Andric         .GetExecutionContext()
2477435933ddSDimitry Andric         .GetProcessSP();
24780127ef0fSEd Maste   }
24790127ef0fSEd Maste 
GetThread(const TreeItem & item)2480435933ddSDimitry Andric   ThreadSP GetThread(const TreeItem &item) {
24810127ef0fSEd Maste     ProcessSP process_sp = GetProcess();
24820127ef0fSEd Maste     if (process_sp)
24830127ef0fSEd Maste       return process_sp->GetThreadList().FindThreadByID(item.GetIdentifier());
24840127ef0fSEd Maste     return ThreadSP();
24850127ef0fSEd Maste   }
24860127ef0fSEd Maste 
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)2487435933ddSDimitry Andric   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
24880127ef0fSEd Maste     ThreadSP thread_sp = GetThread(item);
2489435933ddSDimitry Andric     if (thread_sp) {
249012b93ac6SEd Maste       StreamString strm;
249112b93ac6SEd Maste       ExecutionContext exe_ctx(thread_sp);
2492435933ddSDimitry Andric       if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
2493435933ddSDimitry Andric                                nullptr, false, false)) {
249412b93ac6SEd Maste         int right_pad = 1;
2495435933ddSDimitry Andric         window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad);
249612b93ac6SEd Maste       }
249712b93ac6SEd Maste     }
249812b93ac6SEd Maste   }
24999f2f44ceSEd Maste 
TreeDelegateGenerateChildren(TreeItem & item)2500435933ddSDimitry Andric   void TreeDelegateGenerateChildren(TreeItem &item) override {
25010127ef0fSEd Maste     ProcessSP process_sp = GetProcess();
2502435933ddSDimitry Andric     if (process_sp && process_sp->IsAlive()) {
250312b93ac6SEd Maste       StateType state = process_sp->GetState();
2504435933ddSDimitry Andric       if (StateIsStoppedState(state, true)) {
25050127ef0fSEd Maste         ThreadSP thread_sp = GetThread(item);
2506435933ddSDimitry Andric         if (thread_sp) {
2507435933ddSDimitry Andric           if (m_stop_id == process_sp->GetStopID() &&
2508435933ddSDimitry Andric               thread_sp->GetID() == m_tid)
250912b93ac6SEd Maste             return; // Children are already up to date
2510435933ddSDimitry Andric           if (!m_frame_delegate_sp) {
251112b93ac6SEd Maste             // Always expand the thread item the first time we show it
25120127ef0fSEd Maste             m_frame_delegate_sp.reset(new FrameTreeDelegate());
251312b93ac6SEd Maste           }
251412b93ac6SEd Maste 
251512b93ac6SEd Maste           m_stop_id = process_sp->GetStopID();
251612b93ac6SEd Maste           m_tid = thread_sp->GetID();
251712b93ac6SEd Maste 
251812b93ac6SEd Maste           TreeItem t(&item, *m_frame_delegate_sp, false);
251912b93ac6SEd Maste           size_t num_frames = thread_sp->GetStackFrameCount();
252012b93ac6SEd Maste           item.Resize(num_frames, t);
2521435933ddSDimitry Andric           for (size_t i = 0; i < num_frames; ++i) {
25220127ef0fSEd Maste             item[i].SetUserData(thread_sp.get());
252312b93ac6SEd Maste             item[i].SetIdentifier(i);
252412b93ac6SEd Maste           }
252512b93ac6SEd Maste         }
252612b93ac6SEd Maste         return;
252712b93ac6SEd Maste       }
252812b93ac6SEd Maste     }
252912b93ac6SEd Maste     item.ClearChildren();
253012b93ac6SEd Maste   }
253112b93ac6SEd Maste 
TreeDelegateItemSelected(TreeItem & item)2532435933ddSDimitry Andric   bool TreeDelegateItemSelected(TreeItem &item) override {
25330127ef0fSEd Maste     ProcessSP process_sp = GetProcess();
2534435933ddSDimitry Andric     if (process_sp && process_sp->IsAlive()) {
25350127ef0fSEd Maste       StateType state = process_sp->GetState();
2536435933ddSDimitry Andric       if (StateIsStoppedState(state, true)) {
25370127ef0fSEd Maste         ThreadSP thread_sp = GetThread(item);
2538435933ddSDimitry Andric         if (thread_sp) {
253912b93ac6SEd Maste           ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList();
25404bb0738eSEd Maste           std::lock_guard<std::recursive_mutex> guard(thread_list.GetMutex());
254112b93ac6SEd Maste           ThreadSP selected_thread_sp = thread_list.GetSelectedThread();
2542435933ddSDimitry Andric           if (selected_thread_sp->GetID() != thread_sp->GetID()) {
254312b93ac6SEd Maste             thread_list.SetSelectedThreadByID(thread_sp->GetID());
254412b93ac6SEd Maste             return true;
254512b93ac6SEd Maste           }
254612b93ac6SEd Maste         }
25470127ef0fSEd Maste       }
25480127ef0fSEd Maste     }
254912b93ac6SEd Maste     return false;
255012b93ac6SEd Maste   }
255112b93ac6SEd Maste 
255212b93ac6SEd Maste protected:
255312b93ac6SEd Maste   Debugger &m_debugger;
255412b93ac6SEd Maste   std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp;
255512b93ac6SEd Maste   lldb::user_id_t m_tid;
255612b93ac6SEd Maste   uint32_t m_stop_id;
25571c3bbb01SEd Maste   FormatEntity::Entry m_format;
255812b93ac6SEd Maste };
255912b93ac6SEd Maste 
2560435933ddSDimitry Andric class ThreadsTreeDelegate : public TreeDelegate {
25610127ef0fSEd Maste public:
ThreadsTreeDelegate(Debugger & debugger)2562435933ddSDimitry Andric   ThreadsTreeDelegate(Debugger &debugger)
2563435933ddSDimitry Andric       : TreeDelegate(), m_thread_delegate_sp(), m_debugger(debugger),
2564435933ddSDimitry Andric         m_stop_id(UINT32_MAX) {
25651c3bbb01SEd Maste     FormatEntity::Parse("process ${process.id}{, name = ${process.name}}",
25661c3bbb01SEd Maste                         m_format);
25670127ef0fSEd Maste   }
25680127ef0fSEd Maste 
25699f2f44ceSEd Maste   ~ThreadsTreeDelegate() override = default;
25700127ef0fSEd Maste 
GetProcess()2571435933ddSDimitry Andric   ProcessSP GetProcess() {
2572435933ddSDimitry Andric     return m_debugger.GetCommandInterpreter()
2573435933ddSDimitry Andric         .GetExecutionContext()
2574435933ddSDimitry Andric         .GetProcessSP();
25750127ef0fSEd Maste   }
25760127ef0fSEd Maste 
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)2577435933ddSDimitry Andric   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
25780127ef0fSEd Maste     ProcessSP process_sp = GetProcess();
2579435933ddSDimitry Andric     if (process_sp && process_sp->IsAlive()) {
25800127ef0fSEd Maste       StreamString strm;
25810127ef0fSEd Maste       ExecutionContext exe_ctx(process_sp);
2582435933ddSDimitry Andric       if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
2583435933ddSDimitry Andric                                nullptr, false, false)) {
25840127ef0fSEd Maste         int right_pad = 1;
2585435933ddSDimitry Andric         window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad);
25860127ef0fSEd Maste       }
25870127ef0fSEd Maste     }
25880127ef0fSEd Maste   }
25890127ef0fSEd Maste 
TreeDelegateGenerateChildren(TreeItem & item)2590435933ddSDimitry Andric   void TreeDelegateGenerateChildren(TreeItem &item) override {
25910127ef0fSEd Maste     ProcessSP process_sp = GetProcess();
2592435933ddSDimitry Andric     if (process_sp && process_sp->IsAlive()) {
25930127ef0fSEd Maste       StateType state = process_sp->GetState();
2594435933ddSDimitry Andric       if (StateIsStoppedState(state, true)) {
25950127ef0fSEd Maste         const uint32_t stop_id = process_sp->GetStopID();
25960127ef0fSEd Maste         if (m_stop_id == stop_id)
25970127ef0fSEd Maste           return; // Children are already up to date
25980127ef0fSEd Maste 
25990127ef0fSEd Maste         m_stop_id = stop_id;
26000127ef0fSEd Maste 
2601435933ddSDimitry Andric         if (!m_thread_delegate_sp) {
26020127ef0fSEd Maste           // Always expand the thread item the first time we show it
26030127ef0fSEd Maste           // item.Expand();
26040127ef0fSEd Maste           m_thread_delegate_sp.reset(new ThreadTreeDelegate(m_debugger));
26050127ef0fSEd Maste         }
26060127ef0fSEd Maste 
26070127ef0fSEd Maste         TreeItem t(&item, *m_thread_delegate_sp, false);
26080127ef0fSEd Maste         ThreadList &threads = process_sp->GetThreadList();
26094bb0738eSEd Maste         std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
26100127ef0fSEd Maste         size_t num_threads = threads.GetSize();
26110127ef0fSEd Maste         item.Resize(num_threads, t);
2612435933ddSDimitry Andric         for (size_t i = 0; i < num_threads; ++i) {
26130127ef0fSEd Maste           item[i].SetIdentifier(threads.GetThreadAtIndex(i)->GetID());
26140127ef0fSEd Maste           item[i].SetMightHaveChildren(true);
26150127ef0fSEd Maste         }
26160127ef0fSEd Maste         return;
26170127ef0fSEd Maste       }
26180127ef0fSEd Maste     }
26190127ef0fSEd Maste     item.ClearChildren();
26200127ef0fSEd Maste   }
26210127ef0fSEd Maste 
TreeDelegateItemSelected(TreeItem & item)2622435933ddSDimitry Andric   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
26230127ef0fSEd Maste 
26240127ef0fSEd Maste protected:
26250127ef0fSEd Maste   std::shared_ptr<ThreadTreeDelegate> m_thread_delegate_sp;
26260127ef0fSEd Maste   Debugger &m_debugger;
26270127ef0fSEd Maste   uint32_t m_stop_id;
26281c3bbb01SEd Maste   FormatEntity::Entry m_format;
26290127ef0fSEd Maste };
26300127ef0fSEd Maste 
2631435933ddSDimitry Andric class ValueObjectListDelegate : public WindowDelegate {
263212b93ac6SEd Maste public:
ValueObjectListDelegate()2633435933ddSDimitry Andric   ValueObjectListDelegate()
2634435933ddSDimitry Andric       : m_rows(), m_selected_row(nullptr),
2635435933ddSDimitry Andric         m_selected_row_idx(0), m_first_visible_row(0), m_num_rows(0),
2636435933ddSDimitry Andric         m_max_x(0), m_max_y(0) {}
263712b93ac6SEd Maste 
ValueObjectListDelegate(ValueObjectList & valobj_list)2638435933ddSDimitry Andric   ValueObjectListDelegate(ValueObjectList &valobj_list)
2639435933ddSDimitry Andric       : m_rows(), m_selected_row(nullptr),
2640435933ddSDimitry Andric         m_selected_row_idx(0), m_first_visible_row(0), m_num_rows(0),
2641435933ddSDimitry Andric         m_max_x(0), m_max_y(0) {
264212b93ac6SEd Maste     SetValues(valobj_list);
264312b93ac6SEd Maste   }
264412b93ac6SEd Maste 
26459f2f44ceSEd Maste   ~ValueObjectListDelegate() override = default;
264612b93ac6SEd Maste 
SetValues(ValueObjectList & valobj_list)2647435933ddSDimitry Andric   void SetValues(ValueObjectList &valobj_list) {
26484bb0738eSEd Maste     m_selected_row = nullptr;
264912b93ac6SEd Maste     m_selected_row_idx = 0;
265012b93ac6SEd Maste     m_first_visible_row = 0;
265112b93ac6SEd Maste     m_num_rows = 0;
265212b93ac6SEd Maste     m_rows.clear();
2653435933ddSDimitry Andric     for (auto &valobj_sp : valobj_list.GetObjects())
2654435933ddSDimitry Andric       m_rows.push_back(Row(valobj_sp, nullptr));
265512b93ac6SEd Maste   }
265612b93ac6SEd Maste 
WindowDelegateDraw(Window & window,bool force)2657435933ddSDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
265812b93ac6SEd Maste     m_num_rows = 0;
265912b93ac6SEd Maste     m_min_x = 2;
266012b93ac6SEd Maste     m_min_y = 1;
266112b93ac6SEd Maste     m_max_x = window.GetWidth() - 1;
266212b93ac6SEd Maste     m_max_y = window.GetHeight() - 1;
266312b93ac6SEd Maste 
266412b93ac6SEd Maste     window.Erase();
266512b93ac6SEd Maste     window.DrawTitleBox(window.GetName());
266612b93ac6SEd Maste 
266712b93ac6SEd Maste     const int num_visible_rows = NumVisibleRows();
266812b93ac6SEd Maste     const int num_rows = CalculateTotalNumberRows(m_rows);
266912b93ac6SEd Maste 
26704ba319b5SDimitry Andric     // If we unexpanded while having something selected our total number of
26714ba319b5SDimitry Andric     // rows is less than the num visible rows, then make sure we show all the
26724ba319b5SDimitry Andric     // rows by setting the first visible row accordingly.
267312b93ac6SEd Maste     if (m_first_visible_row > 0 && num_rows < num_visible_rows)
267412b93ac6SEd Maste       m_first_visible_row = 0;
267512b93ac6SEd Maste 
267612b93ac6SEd Maste     // Make sure the selected row is always visible
267712b93ac6SEd Maste     if (m_selected_row_idx < m_first_visible_row)
267812b93ac6SEd Maste       m_first_visible_row = m_selected_row_idx;
267912b93ac6SEd Maste     else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
268012b93ac6SEd Maste       m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
268112b93ac6SEd Maste 
268212b93ac6SEd Maste     DisplayRows(window, m_rows, g_options);
268312b93ac6SEd Maste 
268412b93ac6SEd Maste     window.DeferredRefresh();
268512b93ac6SEd Maste 
268612b93ac6SEd Maste     // Get the selected row
268712b93ac6SEd Maste     m_selected_row = GetRowForRowIndex(m_selected_row_idx);
26884ba319b5SDimitry Andric     // Keep the cursor on the selected row so the highlight and the cursor are
26894ba319b5SDimitry Andric     // always on the same line
269012b93ac6SEd Maste     if (m_selected_row)
2691435933ddSDimitry Andric       window.MoveCursor(m_selected_row->x, m_selected_row->y);
269212b93ac6SEd Maste 
269312b93ac6SEd Maste     return true; // Drawing handled
269412b93ac6SEd Maste   }
269512b93ac6SEd Maste 
WindowDelegateGetKeyHelp()2696435933ddSDimitry Andric   KeyHelp *WindowDelegateGetKeyHelp() override {
269712b93ac6SEd Maste     static curses::KeyHelp g_source_view_key_help[] = {
269812b93ac6SEd Maste         {KEY_UP, "Select previous item"},
269912b93ac6SEd Maste         {KEY_DOWN, "Select next item"},
270012b93ac6SEd Maste         {KEY_RIGHT, "Expand selected item"},
270112b93ac6SEd Maste         {KEY_LEFT, "Unexpand selected item or select parent if not expanded"},
270212b93ac6SEd Maste         {KEY_PPAGE, "Page up"},
270312b93ac6SEd Maste         {KEY_NPAGE, "Page down"},
270412b93ac6SEd Maste         {'A', "Format as annotated address"},
270512b93ac6SEd Maste         {'b', "Format as binary"},
270612b93ac6SEd Maste         {'B', "Format as hex bytes with ASCII"},
270712b93ac6SEd Maste         {'c', "Format as character"},
270812b93ac6SEd Maste         {'d', "Format as a signed integer"},
270912b93ac6SEd Maste         {'D', "Format selected value using the default format for the type"},
271012b93ac6SEd Maste         {'f', "Format as float"},
271112b93ac6SEd Maste         {'h', "Show help dialog"},
271212b93ac6SEd Maste         {'i', "Format as instructions"},
271312b93ac6SEd Maste         {'o', "Format as octal"},
271412b93ac6SEd Maste         {'p', "Format as pointer"},
271512b93ac6SEd Maste         {'s', "Format as C string"},
271612b93ac6SEd Maste         {'t', "Toggle showing/hiding type names"},
271712b93ac6SEd Maste         {'u', "Format as an unsigned integer"},
271812b93ac6SEd Maste         {'x', "Format as hex"},
271912b93ac6SEd Maste         {'X', "Format as uppercase hex"},
272012b93ac6SEd Maste         {' ', "Toggle item expansion"},
272112b93ac6SEd Maste         {',', "Page up"},
272212b93ac6SEd Maste         {'.', "Page down"},
2723435933ddSDimitry Andric         {'\0', nullptr}};
272412b93ac6SEd Maste     return g_source_view_key_help;
272512b93ac6SEd Maste   }
272612b93ac6SEd Maste 
WindowDelegateHandleChar(Window & window,int c)2727435933ddSDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
2728435933ddSDimitry Andric     switch (c) {
272912b93ac6SEd Maste     case 'x':
273012b93ac6SEd Maste     case 'X':
273112b93ac6SEd Maste     case 'o':
273212b93ac6SEd Maste     case 's':
273312b93ac6SEd Maste     case 'u':
273412b93ac6SEd Maste     case 'd':
273512b93ac6SEd Maste     case 'D':
273612b93ac6SEd Maste     case 'i':
273712b93ac6SEd Maste     case 'A':
273812b93ac6SEd Maste     case 'p':
273912b93ac6SEd Maste     case 'c':
274012b93ac6SEd Maste     case 'b':
274112b93ac6SEd Maste     case 'B':
274212b93ac6SEd Maste     case 'f':
274312b93ac6SEd Maste       // Change the format for the currently selected item
2744435933ddSDimitry Andric       if (m_selected_row) {
2745435933ddSDimitry Andric         auto valobj_sp = m_selected_row->value.GetSP();
2746435933ddSDimitry Andric         if (valobj_sp)
2747435933ddSDimitry Andric           valobj_sp->SetFormat(FormatForChar(c));
2748435933ddSDimitry Andric       }
274912b93ac6SEd Maste       return eKeyHandled;
275012b93ac6SEd Maste 
275112b93ac6SEd Maste     case 't':
275212b93ac6SEd Maste       // Toggle showing type names
275312b93ac6SEd Maste       g_options.show_types = !g_options.show_types;
275412b93ac6SEd Maste       return eKeyHandled;
275512b93ac6SEd Maste 
275612b93ac6SEd Maste     case ',':
275712b93ac6SEd Maste     case KEY_PPAGE:
275812b93ac6SEd Maste       // Page up key
2759435933ddSDimitry Andric       if (m_first_visible_row > 0) {
27600127ef0fSEd Maste         if (static_cast<int>(m_first_visible_row) > m_max_y)
276112b93ac6SEd Maste           m_first_visible_row -= m_max_y;
276212b93ac6SEd Maste         else
276312b93ac6SEd Maste           m_first_visible_row = 0;
276412b93ac6SEd Maste         m_selected_row_idx = m_first_visible_row;
276512b93ac6SEd Maste       }
276612b93ac6SEd Maste       return eKeyHandled;
276712b93ac6SEd Maste 
276812b93ac6SEd Maste     case '.':
276912b93ac6SEd Maste     case KEY_NPAGE:
277012b93ac6SEd Maste       // Page down key
2771435933ddSDimitry Andric       if (m_num_rows > static_cast<size_t>(m_max_y)) {
2772435933ddSDimitry Andric         if (m_first_visible_row + m_max_y < m_num_rows) {
277312b93ac6SEd Maste           m_first_visible_row += m_max_y;
277412b93ac6SEd Maste           m_selected_row_idx = m_first_visible_row;
277512b93ac6SEd Maste         }
277612b93ac6SEd Maste       }
277712b93ac6SEd Maste       return eKeyHandled;
277812b93ac6SEd Maste 
277912b93ac6SEd Maste     case KEY_UP:
278012b93ac6SEd Maste       if (m_selected_row_idx > 0)
278112b93ac6SEd Maste         --m_selected_row_idx;
278212b93ac6SEd Maste       return eKeyHandled;
27839f2f44ceSEd Maste 
278412b93ac6SEd Maste     case KEY_DOWN:
278512b93ac6SEd Maste       if (m_selected_row_idx + 1 < m_num_rows)
278612b93ac6SEd Maste         ++m_selected_row_idx;
278712b93ac6SEd Maste       return eKeyHandled;
278812b93ac6SEd Maste 
278912b93ac6SEd Maste     case KEY_RIGHT:
2790435933ddSDimitry Andric       if (m_selected_row) {
279112b93ac6SEd Maste         if (!m_selected_row->expanded)
279212b93ac6SEd Maste           m_selected_row->Expand();
279312b93ac6SEd Maste       }
279412b93ac6SEd Maste       return eKeyHandled;
279512b93ac6SEd Maste 
279612b93ac6SEd Maste     case KEY_LEFT:
2797435933ddSDimitry Andric       if (m_selected_row) {
279812b93ac6SEd Maste         if (m_selected_row->expanded)
279912b93ac6SEd Maste           m_selected_row->Unexpand();
280012b93ac6SEd Maste         else if (m_selected_row->parent)
280112b93ac6SEd Maste           m_selected_row_idx = m_selected_row->parent->row_idx;
280212b93ac6SEd Maste       }
280312b93ac6SEd Maste       return eKeyHandled;
280412b93ac6SEd Maste 
280512b93ac6SEd Maste     case ' ':
280612b93ac6SEd Maste       // Toggle expansion state when SPACE is pressed
2807435933ddSDimitry Andric       if (m_selected_row) {
280812b93ac6SEd Maste         if (m_selected_row->expanded)
280912b93ac6SEd Maste           m_selected_row->Unexpand();
281012b93ac6SEd Maste         else
281112b93ac6SEd Maste           m_selected_row->Expand();
281212b93ac6SEd Maste       }
281312b93ac6SEd Maste       return eKeyHandled;
281412b93ac6SEd Maste 
281512b93ac6SEd Maste     case 'h':
281612b93ac6SEd Maste       window.CreateHelpSubwindow();
281712b93ac6SEd Maste       return eKeyHandled;
281812b93ac6SEd Maste 
281912b93ac6SEd Maste     default:
282012b93ac6SEd Maste       break;
282112b93ac6SEd Maste     }
282212b93ac6SEd Maste     return eKeyNotHandled;
282312b93ac6SEd Maste   }
282412b93ac6SEd Maste 
282512b93ac6SEd Maste protected:
282612b93ac6SEd Maste   std::vector<Row> m_rows;
282712b93ac6SEd Maste   Row *m_selected_row;
282812b93ac6SEd Maste   uint32_t m_selected_row_idx;
282912b93ac6SEd Maste   uint32_t m_first_visible_row;
283012b93ac6SEd Maste   uint32_t m_num_rows;
283112b93ac6SEd Maste   int m_min_x;
283212b93ac6SEd Maste   int m_min_y;
283312b93ac6SEd Maste   int m_max_x;
283412b93ac6SEd Maste   int m_max_y;
283512b93ac6SEd Maste 
FormatForChar(int c)2836435933ddSDimitry Andric   static Format FormatForChar(int c) {
2837435933ddSDimitry Andric     switch (c) {
2838435933ddSDimitry Andric     case 'x':
2839435933ddSDimitry Andric       return eFormatHex;
2840435933ddSDimitry Andric     case 'X':
2841435933ddSDimitry Andric       return eFormatHexUppercase;
2842435933ddSDimitry Andric     case 'o':
2843435933ddSDimitry Andric       return eFormatOctal;
2844435933ddSDimitry Andric     case 's':
2845435933ddSDimitry Andric       return eFormatCString;
2846435933ddSDimitry Andric     case 'u':
2847435933ddSDimitry Andric       return eFormatUnsigned;
2848435933ddSDimitry Andric     case 'd':
2849435933ddSDimitry Andric       return eFormatDecimal;
2850435933ddSDimitry Andric     case 'D':
2851435933ddSDimitry Andric       return eFormatDefault;
2852435933ddSDimitry Andric     case 'i':
2853435933ddSDimitry Andric       return eFormatInstruction;
2854435933ddSDimitry Andric     case 'A':
2855435933ddSDimitry Andric       return eFormatAddressInfo;
2856435933ddSDimitry Andric     case 'p':
2857435933ddSDimitry Andric       return eFormatPointer;
2858435933ddSDimitry Andric     case 'c':
2859435933ddSDimitry Andric       return eFormatChar;
2860435933ddSDimitry Andric     case 'b':
2861435933ddSDimitry Andric       return eFormatBinary;
2862435933ddSDimitry Andric     case 'B':
2863435933ddSDimitry Andric       return eFormatBytesWithASCII;
2864435933ddSDimitry Andric     case 'f':
2865435933ddSDimitry Andric       return eFormatFloat;
286612b93ac6SEd Maste     }
286712b93ac6SEd Maste     return eFormatDefault;
286812b93ac6SEd Maste   }
286912b93ac6SEd Maste 
DisplayRowObject(Window & window,Row & row,DisplayOptions & options,bool highlight,bool last_child)2870435933ddSDimitry Andric   bool DisplayRowObject(Window &window, Row &row, DisplayOptions &options,
2871435933ddSDimitry Andric                         bool highlight, bool last_child) {
2872435933ddSDimitry Andric     ValueObject *valobj = row.value.GetSP().get();
287312b93ac6SEd Maste 
28744bb0738eSEd Maste     if (valobj == nullptr)
287512b93ac6SEd Maste       return false;
287612b93ac6SEd Maste 
2877435933ddSDimitry Andric     const char *type_name =
2878435933ddSDimitry Andric         options.show_types ? valobj->GetTypeName().GetCString() : nullptr;
287912b93ac6SEd Maste     const char *name = valobj->GetName().GetCString();
288012b93ac6SEd Maste     const char *value = valobj->GetValueAsCString();
288112b93ac6SEd Maste     const char *summary = valobj->GetSummaryAsCString();
288212b93ac6SEd Maste 
288312b93ac6SEd Maste     window.MoveCursor(row.x, row.y);
288412b93ac6SEd Maste 
288512b93ac6SEd Maste     row.DrawTree(window);
288612b93ac6SEd Maste 
288712b93ac6SEd Maste     if (highlight)
288812b93ac6SEd Maste       window.AttributeOn(A_REVERSE);
288912b93ac6SEd Maste 
289012b93ac6SEd Maste     if (type_name && type_name[0])
289112b93ac6SEd Maste       window.Printf("(%s) ", type_name);
289212b93ac6SEd Maste 
289312b93ac6SEd Maste     if (name && name[0])
289412b93ac6SEd Maste       window.PutCString(name);
289512b93ac6SEd Maste 
289612b93ac6SEd Maste     attr_t changd_attr = 0;
289712b93ac6SEd Maste     if (valobj->GetValueDidChange())
289812b93ac6SEd Maste       changd_attr = COLOR_PAIR(5) | A_BOLD;
289912b93ac6SEd Maste 
2900435933ddSDimitry Andric     if (value && value[0]) {
290112b93ac6SEd Maste       window.PutCString(" = ");
290212b93ac6SEd Maste       if (changd_attr)
290312b93ac6SEd Maste         window.AttributeOn(changd_attr);
290412b93ac6SEd Maste       window.PutCString(value);
290512b93ac6SEd Maste       if (changd_attr)
290612b93ac6SEd Maste         window.AttributeOff(changd_attr);
290712b93ac6SEd Maste     }
290812b93ac6SEd Maste 
2909435933ddSDimitry Andric     if (summary && summary[0]) {
291012b93ac6SEd Maste       window.PutChar(' ');
291112b93ac6SEd Maste       if (changd_attr)
291212b93ac6SEd Maste         window.AttributeOn(changd_attr);
291312b93ac6SEd Maste       window.PutCString(summary);
291412b93ac6SEd Maste       if (changd_attr)
291512b93ac6SEd Maste         window.AttributeOff(changd_attr);
291612b93ac6SEd Maste     }
291712b93ac6SEd Maste 
291812b93ac6SEd Maste     if (highlight)
291912b93ac6SEd Maste       window.AttributeOff(A_REVERSE);
292012b93ac6SEd Maste 
292112b93ac6SEd Maste     return true;
292212b93ac6SEd Maste   }
29239f2f44ceSEd Maste 
DisplayRows(Window & window,std::vector<Row> & rows,DisplayOptions & options)2924435933ddSDimitry Andric   void DisplayRows(Window &window, std::vector<Row> &rows,
2925435933ddSDimitry Andric                    DisplayOptions &options) {
292612b93ac6SEd Maste     // >   0x25B7
292712b93ac6SEd Maste     // \/  0x25BD
292812b93ac6SEd Maste 
292912b93ac6SEd Maste     bool window_is_active = window.IsActive();
2930435933ddSDimitry Andric     for (auto &row : rows) {
293112b93ac6SEd Maste       const bool last_child = row.parent && &rows[rows.size() - 1] == &row;
293212b93ac6SEd Maste       // Save the row index in each Row structure
293312b93ac6SEd Maste       row.row_idx = m_num_rows;
293412b93ac6SEd Maste       if ((m_num_rows >= m_first_visible_row) &&
2935435933ddSDimitry Andric           ((m_num_rows - m_first_visible_row) <
2936435933ddSDimitry Andric            static_cast<size_t>(NumVisibleRows()))) {
293712b93ac6SEd Maste         row.x = m_min_x;
293812b93ac6SEd Maste         row.y = m_num_rows - m_first_visible_row + 1;
2939435933ddSDimitry Andric         if (DisplayRowObject(window, row, options,
2940435933ddSDimitry Andric                              window_is_active &&
2941435933ddSDimitry Andric                                  m_num_rows == m_selected_row_idx,
2942435933ddSDimitry Andric                              last_child)) {
294312b93ac6SEd Maste           ++m_num_rows;
2944435933ddSDimitry Andric         } else {
294512b93ac6SEd Maste           row.x = 0;
294612b93ac6SEd Maste           row.y = 0;
294712b93ac6SEd Maste         }
2948435933ddSDimitry Andric       } else {
294912b93ac6SEd Maste         row.x = 0;
295012b93ac6SEd Maste         row.y = 0;
295112b93ac6SEd Maste         ++m_num_rows;
295212b93ac6SEd Maste       }
295312b93ac6SEd Maste 
2954435933ddSDimitry Andric       auto &children = row.GetChildren();
2955435933ddSDimitry Andric       if (row.expanded && !children.empty()) {
2956435933ddSDimitry Andric         DisplayRows(window, children, options);
295712b93ac6SEd Maste       }
295812b93ac6SEd Maste     }
295912b93ac6SEd Maste   }
296012b93ac6SEd Maste 
CalculateTotalNumberRows(std::vector<Row> & rows)2961435933ddSDimitry Andric   int CalculateTotalNumberRows(std::vector<Row> &rows) {
296212b93ac6SEd Maste     int row_count = 0;
2963435933ddSDimitry Andric     for (auto &row : rows) {
296412b93ac6SEd Maste       ++row_count;
296512b93ac6SEd Maste       if (row.expanded)
2966435933ddSDimitry Andric         row_count += CalculateTotalNumberRows(row.GetChildren());
296712b93ac6SEd Maste     }
296812b93ac6SEd Maste     return row_count;
296912b93ac6SEd Maste   }
29709f2f44ceSEd Maste 
GetRowForRowIndexImpl(std::vector<Row> & rows,size_t & row_index)2971435933ddSDimitry Andric   static Row *GetRowForRowIndexImpl(std::vector<Row> &rows, size_t &row_index) {
2972435933ddSDimitry Andric     for (auto &row : rows) {
297312b93ac6SEd Maste       if (row_index == 0)
297412b93ac6SEd Maste         return &row;
2975435933ddSDimitry Andric       else {
297612b93ac6SEd Maste         --row_index;
2977435933ddSDimitry Andric         auto &children = row.GetChildren();
2978435933ddSDimitry Andric         if (row.expanded && !children.empty()) {
2979435933ddSDimitry Andric           Row *result = GetRowForRowIndexImpl(children, row_index);
298012b93ac6SEd Maste           if (result)
298112b93ac6SEd Maste             return result;
298212b93ac6SEd Maste         }
298312b93ac6SEd Maste       }
298412b93ac6SEd Maste     }
29854bb0738eSEd Maste     return nullptr;
298612b93ac6SEd Maste   }
298712b93ac6SEd Maste 
GetRowForRowIndex(size_t row_index)2988435933ddSDimitry Andric   Row *GetRowForRowIndex(size_t row_index) {
298912b93ac6SEd Maste     return GetRowForRowIndexImpl(m_rows, row_index);
299012b93ac6SEd Maste   }
299112b93ac6SEd Maste 
NumVisibleRows() const2992435933ddSDimitry Andric   int NumVisibleRows() const { return m_max_y - m_min_y; }
299312b93ac6SEd Maste 
299412b93ac6SEd Maste   static DisplayOptions g_options;
299512b93ac6SEd Maste };
299612b93ac6SEd Maste 
2997435933ddSDimitry Andric class FrameVariablesWindowDelegate : public ValueObjectListDelegate {
299812b93ac6SEd Maste public:
FrameVariablesWindowDelegate(Debugger & debugger)2999435933ddSDimitry Andric   FrameVariablesWindowDelegate(Debugger &debugger)
3000435933ddSDimitry Andric       : ValueObjectListDelegate(), m_debugger(debugger),
3001435933ddSDimitry Andric         m_frame_block(nullptr) {}
300212b93ac6SEd Maste 
30039f2f44ceSEd Maste   ~FrameVariablesWindowDelegate() override = default;
300412b93ac6SEd Maste 
WindowDelegateGetHelpText()3005435933ddSDimitry Andric   const char *WindowDelegateGetHelpText() override {
300612b93ac6SEd Maste     return "Frame variable window keyboard shortcuts:";
300712b93ac6SEd Maste   }
300812b93ac6SEd Maste 
WindowDelegateDraw(Window & window,bool force)3009435933ddSDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
3010435933ddSDimitry Andric     ExecutionContext exe_ctx(
3011435933ddSDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext());
301212b93ac6SEd Maste     Process *process = exe_ctx.GetProcessPtr();
30134bb0738eSEd Maste     Block *frame_block = nullptr;
30144bb0738eSEd Maste     StackFrame *frame = nullptr;
301512b93ac6SEd Maste 
3016435933ddSDimitry Andric     if (process) {
301712b93ac6SEd Maste       StateType state = process->GetState();
3018435933ddSDimitry Andric       if (StateIsStoppedState(state, true)) {
301912b93ac6SEd Maste         frame = exe_ctx.GetFramePtr();
302012b93ac6SEd Maste         if (frame)
302112b93ac6SEd Maste           frame_block = frame->GetFrameBlock();
3022435933ddSDimitry Andric       } else if (StateIsRunningState(state)) {
302312b93ac6SEd Maste         return true; // Don't do any updating when we are running
302412b93ac6SEd Maste       }
302512b93ac6SEd Maste     }
302612b93ac6SEd Maste 
302712b93ac6SEd Maste     ValueObjectList local_values;
3028435933ddSDimitry Andric     if (frame_block) {
302912b93ac6SEd Maste       // Only update the variables if they have changed
3030435933ddSDimitry Andric       if (m_frame_block != frame_block) {
303112b93ac6SEd Maste         m_frame_block = frame_block;
303212b93ac6SEd Maste 
303312b93ac6SEd Maste         VariableList *locals = frame->GetVariableList(true);
3034435933ddSDimitry Andric         if (locals) {
303512b93ac6SEd Maste           const DynamicValueType use_dynamic = eDynamicDontRunTarget;
303612b93ac6SEd Maste           const size_t num_locals = locals->GetSize();
3037435933ddSDimitry Andric           for (size_t i = 0; i < num_locals; ++i) {
3038435933ddSDimitry Andric             ValueObjectSP value_sp = frame->GetValueObjectForFrameVariable(
3039435933ddSDimitry Andric                 locals->GetVariableAtIndex(i), use_dynamic);
3040435933ddSDimitry Andric             if (value_sp) {
30411c3bbb01SEd Maste               ValueObjectSP synthetic_value_sp = value_sp->GetSyntheticValue();
30421c3bbb01SEd Maste               if (synthetic_value_sp)
30431c3bbb01SEd Maste                 local_values.Append(synthetic_value_sp);
30441c3bbb01SEd Maste               else
30451c3bbb01SEd Maste                 local_values.Append(value_sp);
30461c3bbb01SEd Maste             }
30471c3bbb01SEd Maste           }
304812b93ac6SEd Maste           // Update the values
304912b93ac6SEd Maste           SetValues(local_values);
305012b93ac6SEd Maste         }
305112b93ac6SEd Maste       }
3052435933ddSDimitry Andric     } else {
30534bb0738eSEd Maste       m_frame_block = nullptr;
305412b93ac6SEd Maste       // Update the values with an empty list if there is no frame
305512b93ac6SEd Maste       SetValues(local_values);
305612b93ac6SEd Maste     }
305712b93ac6SEd Maste 
305812b93ac6SEd Maste     return ValueObjectListDelegate::WindowDelegateDraw(window, force);
305912b93ac6SEd Maste   }
306012b93ac6SEd Maste 
306112b93ac6SEd Maste protected:
306212b93ac6SEd Maste   Debugger &m_debugger;
306312b93ac6SEd Maste   Block *m_frame_block;
306412b93ac6SEd Maste };
306512b93ac6SEd Maste 
3066435933ddSDimitry Andric class RegistersWindowDelegate : public ValueObjectListDelegate {
306712b93ac6SEd Maste public:
RegistersWindowDelegate(Debugger & debugger)3068435933ddSDimitry Andric   RegistersWindowDelegate(Debugger &debugger)
3069435933ddSDimitry Andric       : ValueObjectListDelegate(), m_debugger(debugger) {}
307012b93ac6SEd Maste 
30719f2f44ceSEd Maste   ~RegistersWindowDelegate() override = default;
307212b93ac6SEd Maste 
WindowDelegateGetHelpText()3073435933ddSDimitry Andric   const char *WindowDelegateGetHelpText() override {
307412b93ac6SEd Maste     return "Register window keyboard shortcuts:";
307512b93ac6SEd Maste   }
307612b93ac6SEd Maste 
WindowDelegateDraw(Window & window,bool force)3077435933ddSDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
3078435933ddSDimitry Andric     ExecutionContext exe_ctx(
3079435933ddSDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext());
308012b93ac6SEd Maste     StackFrame *frame = exe_ctx.GetFramePtr();
308112b93ac6SEd Maste 
308212b93ac6SEd Maste     ValueObjectList value_list;
3083435933ddSDimitry Andric     if (frame) {
3084435933ddSDimitry Andric       if (frame->GetStackID() != m_stack_id) {
308512b93ac6SEd Maste         m_stack_id = frame->GetStackID();
308612b93ac6SEd Maste         RegisterContextSP reg_ctx(frame->GetRegisterContext());
3087435933ddSDimitry Andric         if (reg_ctx) {
308812b93ac6SEd Maste           const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
3089435933ddSDimitry Andric           for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) {
3090435933ddSDimitry Andric             value_list.Append(
3091435933ddSDimitry Andric                 ValueObjectRegisterSet::Create(frame, reg_ctx, set_idx));
309212b93ac6SEd Maste           }
309312b93ac6SEd Maste         }
309412b93ac6SEd Maste         SetValues(value_list);
309512b93ac6SEd Maste       }
3096435933ddSDimitry Andric     } else {
309712b93ac6SEd Maste       Process *process = exe_ctx.GetProcessPtr();
309812b93ac6SEd Maste       if (process && process->IsAlive())
309912b93ac6SEd Maste         return true; // Don't do any updating if we are running
3100435933ddSDimitry Andric       else {
31014ba319b5SDimitry Andric         // Update the values with an empty list if there is no process or the
31024ba319b5SDimitry Andric         // process isn't alive anymore
310312b93ac6SEd Maste         SetValues(value_list);
310412b93ac6SEd Maste       }
310512b93ac6SEd Maste     }
310612b93ac6SEd Maste     return ValueObjectListDelegate::WindowDelegateDraw(window, force);
310712b93ac6SEd Maste   }
310812b93ac6SEd Maste 
310912b93ac6SEd Maste protected:
311012b93ac6SEd Maste   Debugger &m_debugger;
311112b93ac6SEd Maste   StackID m_stack_id;
311212b93ac6SEd Maste };
311312b93ac6SEd Maste 
CursesKeyToCString(int ch)3114435933ddSDimitry Andric static const char *CursesKeyToCString(int ch) {
311512b93ac6SEd Maste   static char g_desc[32];
3116435933ddSDimitry Andric   if (ch >= KEY_F0 && ch < KEY_F0 + 64) {
311712b93ac6SEd Maste     snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0);
311812b93ac6SEd Maste     return g_desc;
311912b93ac6SEd Maste   }
3120435933ddSDimitry Andric   switch (ch) {
3121435933ddSDimitry Andric   case KEY_DOWN:
3122435933ddSDimitry Andric     return "down";
3123435933ddSDimitry Andric   case KEY_UP:
3124435933ddSDimitry Andric     return "up";
3125435933ddSDimitry Andric   case KEY_LEFT:
3126435933ddSDimitry Andric     return "left";
3127435933ddSDimitry Andric   case KEY_RIGHT:
3128435933ddSDimitry Andric     return "right";
3129435933ddSDimitry Andric   case KEY_HOME:
3130435933ddSDimitry Andric     return "home";
3131435933ddSDimitry Andric   case KEY_BACKSPACE:
3132435933ddSDimitry Andric     return "backspace";
3133435933ddSDimitry Andric   case KEY_DL:
3134435933ddSDimitry Andric     return "delete-line";
3135435933ddSDimitry Andric   case KEY_IL:
3136435933ddSDimitry Andric     return "insert-line";
3137435933ddSDimitry Andric   case KEY_DC:
3138435933ddSDimitry Andric     return "delete-char";
3139435933ddSDimitry Andric   case KEY_IC:
3140435933ddSDimitry Andric     return "insert-char";
3141435933ddSDimitry Andric   case KEY_CLEAR:
3142435933ddSDimitry Andric     return "clear";
3143435933ddSDimitry Andric   case KEY_EOS:
3144435933ddSDimitry Andric     return "clear-to-eos";
3145435933ddSDimitry Andric   case KEY_EOL:
3146435933ddSDimitry Andric     return "clear-to-eol";
3147435933ddSDimitry Andric   case KEY_SF:
3148435933ddSDimitry Andric     return "scroll-forward";
3149435933ddSDimitry Andric   case KEY_SR:
3150435933ddSDimitry Andric     return "scroll-backward";
3151435933ddSDimitry Andric   case KEY_NPAGE:
3152435933ddSDimitry Andric     return "page-down";
3153435933ddSDimitry Andric   case KEY_PPAGE:
3154435933ddSDimitry Andric     return "page-up";
3155435933ddSDimitry Andric   case KEY_STAB:
3156435933ddSDimitry Andric     return "set-tab";
3157435933ddSDimitry Andric   case KEY_CTAB:
3158435933ddSDimitry Andric     return "clear-tab";
3159435933ddSDimitry Andric   case KEY_CATAB:
3160435933ddSDimitry Andric     return "clear-all-tabs";
3161435933ddSDimitry Andric   case KEY_ENTER:
3162435933ddSDimitry Andric     return "enter";
3163435933ddSDimitry Andric   case KEY_PRINT:
3164435933ddSDimitry Andric     return "print";
3165435933ddSDimitry Andric   case KEY_LL:
3166435933ddSDimitry Andric     return "lower-left key";
3167435933ddSDimitry Andric   case KEY_A1:
3168435933ddSDimitry Andric     return "upper left of keypad";
3169435933ddSDimitry Andric   case KEY_A3:
3170435933ddSDimitry Andric     return "upper right of keypad";
3171435933ddSDimitry Andric   case KEY_B2:
3172435933ddSDimitry Andric     return "center of keypad";
3173435933ddSDimitry Andric   case KEY_C1:
3174435933ddSDimitry Andric     return "lower left of keypad";
3175435933ddSDimitry Andric   case KEY_C3:
3176435933ddSDimitry Andric     return "lower right of keypad";
3177435933ddSDimitry Andric   case KEY_BTAB:
3178435933ddSDimitry Andric     return "back-tab key";
3179435933ddSDimitry Andric   case KEY_BEG:
3180435933ddSDimitry Andric     return "begin key";
3181435933ddSDimitry Andric   case KEY_CANCEL:
3182435933ddSDimitry Andric     return "cancel key";
3183435933ddSDimitry Andric   case KEY_CLOSE:
3184435933ddSDimitry Andric     return "close key";
3185435933ddSDimitry Andric   case KEY_COMMAND:
3186435933ddSDimitry Andric     return "command key";
3187435933ddSDimitry Andric   case KEY_COPY:
3188435933ddSDimitry Andric     return "copy key";
3189435933ddSDimitry Andric   case KEY_CREATE:
3190435933ddSDimitry Andric     return "create key";
3191435933ddSDimitry Andric   case KEY_END:
3192435933ddSDimitry Andric     return "end key";
3193435933ddSDimitry Andric   case KEY_EXIT:
3194435933ddSDimitry Andric     return "exit key";
3195435933ddSDimitry Andric   case KEY_FIND:
3196435933ddSDimitry Andric     return "find key";
3197435933ddSDimitry Andric   case KEY_HELP:
3198435933ddSDimitry Andric     return "help key";
3199435933ddSDimitry Andric   case KEY_MARK:
3200435933ddSDimitry Andric     return "mark key";
3201435933ddSDimitry Andric   case KEY_MESSAGE:
3202435933ddSDimitry Andric     return "message key";
3203435933ddSDimitry Andric   case KEY_MOVE:
3204435933ddSDimitry Andric     return "move key";
3205435933ddSDimitry Andric   case KEY_NEXT:
3206435933ddSDimitry Andric     return "next key";
3207435933ddSDimitry Andric   case KEY_OPEN:
3208435933ddSDimitry Andric     return "open key";
3209435933ddSDimitry Andric   case KEY_OPTIONS:
3210435933ddSDimitry Andric     return "options key";
3211435933ddSDimitry Andric   case KEY_PREVIOUS:
3212435933ddSDimitry Andric     return "previous key";
3213435933ddSDimitry Andric   case KEY_REDO:
3214435933ddSDimitry Andric     return "redo key";
3215435933ddSDimitry Andric   case KEY_REFERENCE:
3216435933ddSDimitry Andric     return "reference key";
3217435933ddSDimitry Andric   case KEY_REFRESH:
3218435933ddSDimitry Andric     return "refresh key";
3219435933ddSDimitry Andric   case KEY_REPLACE:
3220435933ddSDimitry Andric     return "replace key";
3221435933ddSDimitry Andric   case KEY_RESTART:
3222435933ddSDimitry Andric     return "restart key";
3223435933ddSDimitry Andric   case KEY_RESUME:
3224435933ddSDimitry Andric     return "resume key";
3225435933ddSDimitry Andric   case KEY_SAVE:
3226435933ddSDimitry Andric     return "save key";
3227435933ddSDimitry Andric   case KEY_SBEG:
3228435933ddSDimitry Andric     return "shifted begin key";
3229435933ddSDimitry Andric   case KEY_SCANCEL:
3230435933ddSDimitry Andric     return "shifted cancel key";
3231435933ddSDimitry Andric   case KEY_SCOMMAND:
3232435933ddSDimitry Andric     return "shifted command key";
3233435933ddSDimitry Andric   case KEY_SCOPY:
3234435933ddSDimitry Andric     return "shifted copy key";
3235435933ddSDimitry Andric   case KEY_SCREATE:
3236435933ddSDimitry Andric     return "shifted create key";
3237435933ddSDimitry Andric   case KEY_SDC:
3238435933ddSDimitry Andric     return "shifted delete-character key";
3239435933ddSDimitry Andric   case KEY_SDL:
3240435933ddSDimitry Andric     return "shifted delete-line key";
3241435933ddSDimitry Andric   case KEY_SELECT:
3242435933ddSDimitry Andric     return "select key";
3243435933ddSDimitry Andric   case KEY_SEND:
3244435933ddSDimitry Andric     return "shifted end key";
3245435933ddSDimitry Andric   case KEY_SEOL:
3246435933ddSDimitry Andric     return "shifted clear-to-end-of-line key";
3247435933ddSDimitry Andric   case KEY_SEXIT:
3248435933ddSDimitry Andric     return "shifted exit key";
3249435933ddSDimitry Andric   case KEY_SFIND:
3250435933ddSDimitry Andric     return "shifted find key";
3251435933ddSDimitry Andric   case KEY_SHELP:
3252435933ddSDimitry Andric     return "shifted help key";
3253435933ddSDimitry Andric   case KEY_SHOME:
3254435933ddSDimitry Andric     return "shifted home key";
3255435933ddSDimitry Andric   case KEY_SIC:
3256435933ddSDimitry Andric     return "shifted insert-character key";
3257435933ddSDimitry Andric   case KEY_SLEFT:
3258435933ddSDimitry Andric     return "shifted left-arrow key";
3259435933ddSDimitry Andric   case KEY_SMESSAGE:
3260435933ddSDimitry Andric     return "shifted message key";
3261435933ddSDimitry Andric   case KEY_SMOVE:
3262435933ddSDimitry Andric     return "shifted move key";
3263435933ddSDimitry Andric   case KEY_SNEXT:
3264435933ddSDimitry Andric     return "shifted next key";
3265435933ddSDimitry Andric   case KEY_SOPTIONS:
3266435933ddSDimitry Andric     return "shifted options key";
3267435933ddSDimitry Andric   case KEY_SPREVIOUS:
3268435933ddSDimitry Andric     return "shifted previous key";
3269435933ddSDimitry Andric   case KEY_SPRINT:
3270435933ddSDimitry Andric     return "shifted print key";
3271435933ddSDimitry Andric   case KEY_SREDO:
3272435933ddSDimitry Andric     return "shifted redo key";
3273435933ddSDimitry Andric   case KEY_SREPLACE:
3274435933ddSDimitry Andric     return "shifted replace key";
3275435933ddSDimitry Andric   case KEY_SRIGHT:
3276435933ddSDimitry Andric     return "shifted right-arrow key";
3277435933ddSDimitry Andric   case KEY_SRSUME:
3278435933ddSDimitry Andric     return "shifted resume key";
3279435933ddSDimitry Andric   case KEY_SSAVE:
3280435933ddSDimitry Andric     return "shifted save key";
3281435933ddSDimitry Andric   case KEY_SSUSPEND:
3282435933ddSDimitry Andric     return "shifted suspend key";
3283435933ddSDimitry Andric   case KEY_SUNDO:
3284435933ddSDimitry Andric     return "shifted undo key";
3285435933ddSDimitry Andric   case KEY_SUSPEND:
3286435933ddSDimitry Andric     return "suspend key";
3287435933ddSDimitry Andric   case KEY_UNDO:
3288435933ddSDimitry Andric     return "undo key";
3289435933ddSDimitry Andric   case KEY_MOUSE:
3290435933ddSDimitry Andric     return "Mouse event has occurred";
3291435933ddSDimitry Andric   case KEY_RESIZE:
3292435933ddSDimitry Andric     return "Terminal resize event";
32939f2f44ceSEd Maste #ifdef KEY_EVENT
3294435933ddSDimitry Andric   case KEY_EVENT:
3295435933ddSDimitry Andric     return "We were interrupted by an event";
32969f2f44ceSEd Maste #endif
3297435933ddSDimitry Andric   case KEY_RETURN:
3298435933ddSDimitry Andric     return "return";
3299435933ddSDimitry Andric   case ' ':
3300435933ddSDimitry Andric     return "space";
3301435933ddSDimitry Andric   case '\t':
3302435933ddSDimitry Andric     return "tab";
3303435933ddSDimitry Andric   case KEY_ESCAPE:
3304435933ddSDimitry Andric     return "escape";
330512b93ac6SEd Maste   default:
330612b93ac6SEd Maste     if (isprint(ch))
330712b93ac6SEd Maste       snprintf(g_desc, sizeof(g_desc), "%c", ch);
330812b93ac6SEd Maste     else
330912b93ac6SEd Maste       snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch);
331012b93ac6SEd Maste     return g_desc;
331112b93ac6SEd Maste   }
33124bb0738eSEd Maste   return nullptr;
331312b93ac6SEd Maste }
331412b93ac6SEd Maste 
HelpDialogDelegate(const char * text,KeyHelp * key_help_array)3315435933ddSDimitry Andric HelpDialogDelegate::HelpDialogDelegate(const char *text,
3316435933ddSDimitry Andric                                        KeyHelp *key_help_array)
3317435933ddSDimitry Andric     : m_text(), m_first_visible_line(0) {
3318435933ddSDimitry Andric   if (text && text[0]) {
331912b93ac6SEd Maste     m_text.SplitIntoLines(text);
332012b93ac6SEd Maste     m_text.AppendString("");
332112b93ac6SEd Maste   }
3322435933ddSDimitry Andric   if (key_help_array) {
3323435933ddSDimitry Andric     for (KeyHelp *key = key_help_array; key->ch; ++key) {
332412b93ac6SEd Maste       StreamString key_description;
3325435933ddSDimitry Andric       key_description.Printf("%10s - %s", CursesKeyToCString(key->ch),
3326435933ddSDimitry Andric                              key->description);
3327435933ddSDimitry Andric       m_text.AppendString(key_description.GetString());
332812b93ac6SEd Maste     }
332912b93ac6SEd Maste   }
333012b93ac6SEd Maste }
333112b93ac6SEd Maste 
33329f2f44ceSEd Maste HelpDialogDelegate::~HelpDialogDelegate() = default;
333312b93ac6SEd Maste 
WindowDelegateDraw(Window & window,bool force)3334435933ddSDimitry Andric bool HelpDialogDelegate::WindowDelegateDraw(Window &window, bool force) {
333512b93ac6SEd Maste   window.Erase();
333612b93ac6SEd Maste   const int window_height = window.GetHeight();
333712b93ac6SEd Maste   int x = 2;
333812b93ac6SEd Maste   int y = 1;
333912b93ac6SEd Maste   const int min_y = y;
334012b93ac6SEd Maste   const int max_y = window_height - 1 - y;
33410127ef0fSEd Maste   const size_t num_visible_lines = max_y - min_y + 1;
334212b93ac6SEd Maste   const size_t num_lines = m_text.GetSize();
334312b93ac6SEd Maste   const char *bottom_message;
334412b93ac6SEd Maste   if (num_lines <= num_visible_lines)
334512b93ac6SEd Maste     bottom_message = "Press any key to exit";
334612b93ac6SEd Maste   else
334712b93ac6SEd Maste     bottom_message = "Use arrows to scroll, any other key to exit";
334812b93ac6SEd Maste   window.DrawTitleBox(window.GetName(), bottom_message);
3349435933ddSDimitry Andric   while (y <= max_y) {
335012b93ac6SEd Maste     window.MoveCursor(x, y);
3351435933ddSDimitry Andric     window.PutCStringTruncated(
3352435933ddSDimitry Andric         m_text.GetStringAtIndex(m_first_visible_line + y - min_y), 1);
335312b93ac6SEd Maste     ++y;
335412b93ac6SEd Maste   }
335512b93ac6SEd Maste   return true;
335612b93ac6SEd Maste }
335712b93ac6SEd Maste 
WindowDelegateHandleChar(Window & window,int key)3358435933ddSDimitry Andric HandleCharResult HelpDialogDelegate::WindowDelegateHandleChar(Window &window,
3359435933ddSDimitry Andric                                                               int key) {
336012b93ac6SEd Maste   bool done = false;
336112b93ac6SEd Maste   const size_t num_lines = m_text.GetSize();
336212b93ac6SEd Maste   const size_t num_visible_lines = window.GetHeight() - 2;
336312b93ac6SEd Maste 
3364435933ddSDimitry Andric   if (num_lines <= num_visible_lines) {
336512b93ac6SEd Maste     done = true;
33664ba319b5SDimitry Andric     // If we have all lines visible and don't need scrolling, then any key
33674ba319b5SDimitry Andric     // press will cause us to exit
3368435933ddSDimitry Andric   } else {
3369435933ddSDimitry Andric     switch (key) {
337012b93ac6SEd Maste     case KEY_UP:
337112b93ac6SEd Maste       if (m_first_visible_line > 0)
337212b93ac6SEd Maste         --m_first_visible_line;
337312b93ac6SEd Maste       break;
337412b93ac6SEd Maste 
337512b93ac6SEd Maste     case KEY_DOWN:
337612b93ac6SEd Maste       if (m_first_visible_line + num_visible_lines < num_lines)
337712b93ac6SEd Maste         ++m_first_visible_line;
337812b93ac6SEd Maste       break;
337912b93ac6SEd Maste 
338012b93ac6SEd Maste     case KEY_PPAGE:
338112b93ac6SEd Maste     case ',':
3382435933ddSDimitry Andric       if (m_first_visible_line > 0) {
33830127ef0fSEd Maste         if (static_cast<size_t>(m_first_visible_line) >= num_visible_lines)
338412b93ac6SEd Maste           m_first_visible_line -= num_visible_lines;
338512b93ac6SEd Maste         else
338612b93ac6SEd Maste           m_first_visible_line = 0;
338712b93ac6SEd Maste       }
338812b93ac6SEd Maste       break;
33899f2f44ceSEd Maste 
339012b93ac6SEd Maste     case KEY_NPAGE:
339112b93ac6SEd Maste     case '.':
3392435933ddSDimitry Andric       if (m_first_visible_line + num_visible_lines < num_lines) {
339312b93ac6SEd Maste         m_first_visible_line += num_visible_lines;
33940127ef0fSEd Maste         if (static_cast<size_t>(m_first_visible_line) > num_lines)
339512b93ac6SEd Maste           m_first_visible_line = num_lines - num_visible_lines;
339612b93ac6SEd Maste       }
339712b93ac6SEd Maste       break;
33989f2f44ceSEd Maste 
339912b93ac6SEd Maste     default:
340012b93ac6SEd Maste       done = true;
340112b93ac6SEd Maste       break;
340212b93ac6SEd Maste     }
340312b93ac6SEd Maste   }
340412b93ac6SEd Maste   if (done)
340512b93ac6SEd Maste     window.GetParent()->RemoveSubWindow(&window);
340612b93ac6SEd Maste   return eKeyHandled;
340712b93ac6SEd Maste }
340812b93ac6SEd Maste 
3409435933ddSDimitry Andric class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
341012b93ac6SEd Maste public:
341112b93ac6SEd Maste   enum {
341212b93ac6SEd Maste     eMenuID_LLDB = 1,
341312b93ac6SEd Maste     eMenuID_LLDBAbout,
341412b93ac6SEd Maste     eMenuID_LLDBExit,
341512b93ac6SEd Maste 
341612b93ac6SEd Maste     eMenuID_Target,
341712b93ac6SEd Maste     eMenuID_TargetCreate,
341812b93ac6SEd Maste     eMenuID_TargetDelete,
341912b93ac6SEd Maste 
342012b93ac6SEd Maste     eMenuID_Process,
342112b93ac6SEd Maste     eMenuID_ProcessAttach,
342212b93ac6SEd Maste     eMenuID_ProcessDetach,
342312b93ac6SEd Maste     eMenuID_ProcessLaunch,
342412b93ac6SEd Maste     eMenuID_ProcessContinue,
342512b93ac6SEd Maste     eMenuID_ProcessHalt,
342612b93ac6SEd Maste     eMenuID_ProcessKill,
342712b93ac6SEd Maste 
342812b93ac6SEd Maste     eMenuID_Thread,
342912b93ac6SEd Maste     eMenuID_ThreadStepIn,
343012b93ac6SEd Maste     eMenuID_ThreadStepOver,
343112b93ac6SEd Maste     eMenuID_ThreadStepOut,
343212b93ac6SEd Maste 
343312b93ac6SEd Maste     eMenuID_View,
343412b93ac6SEd Maste     eMenuID_ViewBacktrace,
343512b93ac6SEd Maste     eMenuID_ViewRegisters,
343612b93ac6SEd Maste     eMenuID_ViewSource,
343712b93ac6SEd Maste     eMenuID_ViewVariables,
343812b93ac6SEd Maste 
343912b93ac6SEd Maste     eMenuID_Help,
344012b93ac6SEd Maste     eMenuID_HelpGUIHelp
344112b93ac6SEd Maste   };
344212b93ac6SEd Maste 
ApplicationDelegate(Application & app,Debugger & debugger)3443435933ddSDimitry Andric   ApplicationDelegate(Application &app, Debugger &debugger)
3444435933ddSDimitry Andric       : WindowDelegate(), MenuDelegate(), m_app(app), m_debugger(debugger) {}
344512b93ac6SEd Maste 
34469f2f44ceSEd Maste   ~ApplicationDelegate() override = default;
34471c3bbb01SEd Maste 
WindowDelegateDraw(Window & window,bool force)3448435933ddSDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
344912b93ac6SEd Maste     return false; // Drawing not handled, let standard window drawing happen
345012b93ac6SEd Maste   }
345112b93ac6SEd Maste 
WindowDelegateHandleChar(Window & window,int key)3452435933ddSDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
3453435933ddSDimitry Andric     switch (key) {
345412b93ac6SEd Maste     case '\t':
345512b93ac6SEd Maste       window.SelectNextWindowAsActive();
345612b93ac6SEd Maste       return eKeyHandled;
345712b93ac6SEd Maste 
345812b93ac6SEd Maste     case 'h':
345912b93ac6SEd Maste       window.CreateHelpSubwindow();
346012b93ac6SEd Maste       return eKeyHandled;
346112b93ac6SEd Maste 
346212b93ac6SEd Maste     case KEY_ESCAPE:
346312b93ac6SEd Maste       return eQuitApplication;
346412b93ac6SEd Maste 
346512b93ac6SEd Maste     default:
346612b93ac6SEd Maste       break;
346712b93ac6SEd Maste     }
346812b93ac6SEd Maste     return eKeyNotHandled;
346912b93ac6SEd Maste   }
347012b93ac6SEd Maste 
WindowDelegateGetHelpText()3471435933ddSDimitry Andric   const char *WindowDelegateGetHelpText() override {
347212b93ac6SEd Maste     return "Welcome to the LLDB curses GUI.\n\n"
347312b93ac6SEd Maste            "Press the TAB key to change the selected view.\n"
3474435933ddSDimitry Andric            "Each view has its own keyboard shortcuts, press 'h' to open a "
3475435933ddSDimitry Andric            "dialog to display them.\n\n"
347612b93ac6SEd Maste            "Common key bindings for all views:";
347712b93ac6SEd Maste   }
347812b93ac6SEd Maste 
WindowDelegateGetKeyHelp()3479435933ddSDimitry Andric   KeyHelp *WindowDelegateGetKeyHelp() override {
348012b93ac6SEd Maste     static curses::KeyHelp g_source_view_key_help[] = {
348112b93ac6SEd Maste         {'\t', "Select next view"},
348212b93ac6SEd Maste         {'h', "Show help dialog with view specific key bindings"},
348312b93ac6SEd Maste         {',', "Page up"},
348412b93ac6SEd Maste         {'.', "Page down"},
348512b93ac6SEd Maste         {KEY_UP, "Select previous"},
348612b93ac6SEd Maste         {KEY_DOWN, "Select next"},
348712b93ac6SEd Maste         {KEY_LEFT, "Unexpand or select parent"},
348812b93ac6SEd Maste         {KEY_RIGHT, "Expand"},
348912b93ac6SEd Maste         {KEY_PPAGE, "Page up"},
349012b93ac6SEd Maste         {KEY_NPAGE, "Page down"},
3491435933ddSDimitry Andric         {'\0', nullptr}};
349212b93ac6SEd Maste     return g_source_view_key_help;
349312b93ac6SEd Maste   }
349412b93ac6SEd Maste 
MenuDelegateAction(Menu & menu)3495435933ddSDimitry Andric   MenuActionResult MenuDelegateAction(Menu &menu) override {
3496435933ddSDimitry Andric     switch (menu.GetIdentifier()) {
3497435933ddSDimitry Andric     case eMenuID_ThreadStepIn: {
3498435933ddSDimitry Andric       ExecutionContext exe_ctx =
3499435933ddSDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
3500435933ddSDimitry Andric       if (exe_ctx.HasThreadScope()) {
350112b93ac6SEd Maste         Process *process = exe_ctx.GetProcessPtr();
3502435933ddSDimitry Andric         if (process && process->IsAlive() &&
3503435933ddSDimitry Andric             StateIsStoppedState(process->GetState(), true))
35040127ef0fSEd Maste           exe_ctx.GetThreadRef().StepIn(true);
350512b93ac6SEd Maste       }
350612b93ac6SEd Maste     }
350712b93ac6SEd Maste       return MenuActionResult::Handled;
350812b93ac6SEd Maste 
3509435933ddSDimitry Andric     case eMenuID_ThreadStepOut: {
3510435933ddSDimitry Andric       ExecutionContext exe_ctx =
3511435933ddSDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
3512435933ddSDimitry Andric       if (exe_ctx.HasThreadScope()) {
351312b93ac6SEd Maste         Process *process = exe_ctx.GetProcessPtr();
3514435933ddSDimitry Andric         if (process && process->IsAlive() &&
3515435933ddSDimitry Andric             StateIsStoppedState(process->GetState(), true))
351612b93ac6SEd Maste           exe_ctx.GetThreadRef().StepOut();
351712b93ac6SEd Maste       }
351812b93ac6SEd Maste     }
351912b93ac6SEd Maste       return MenuActionResult::Handled;
352012b93ac6SEd Maste 
3521435933ddSDimitry Andric     case eMenuID_ThreadStepOver: {
3522435933ddSDimitry Andric       ExecutionContext exe_ctx =
3523435933ddSDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
3524435933ddSDimitry Andric       if (exe_ctx.HasThreadScope()) {
352512b93ac6SEd Maste         Process *process = exe_ctx.GetProcessPtr();
3526435933ddSDimitry Andric         if (process && process->IsAlive() &&
3527435933ddSDimitry Andric             StateIsStoppedState(process->GetState(), true))
352812b93ac6SEd Maste           exe_ctx.GetThreadRef().StepOver(true);
352912b93ac6SEd Maste       }
353012b93ac6SEd Maste     }
353112b93ac6SEd Maste       return MenuActionResult::Handled;
353212b93ac6SEd Maste 
3533435933ddSDimitry Andric     case eMenuID_ProcessContinue: {
3534435933ddSDimitry Andric       ExecutionContext exe_ctx =
3535435933ddSDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
3536435933ddSDimitry Andric       if (exe_ctx.HasProcessScope()) {
353712b93ac6SEd Maste         Process *process = exe_ctx.GetProcessPtr();
3538435933ddSDimitry Andric         if (process && process->IsAlive() &&
3539435933ddSDimitry Andric             StateIsStoppedState(process->GetState(), true))
354012b93ac6SEd Maste           process->Resume();
354112b93ac6SEd Maste       }
354212b93ac6SEd Maste     }
354312b93ac6SEd Maste       return MenuActionResult::Handled;
354412b93ac6SEd Maste 
3545435933ddSDimitry Andric     case eMenuID_ProcessKill: {
3546435933ddSDimitry Andric       ExecutionContext exe_ctx =
3547435933ddSDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
3548435933ddSDimitry Andric       if (exe_ctx.HasProcessScope()) {
354912b93ac6SEd Maste         Process *process = exe_ctx.GetProcessPtr();
355012b93ac6SEd Maste         if (process && process->IsAlive())
35511c3bbb01SEd Maste           process->Destroy(false);
355212b93ac6SEd Maste       }
355312b93ac6SEd Maste     }
355412b93ac6SEd Maste       return MenuActionResult::Handled;
355512b93ac6SEd Maste 
3556435933ddSDimitry Andric     case eMenuID_ProcessHalt: {
3557435933ddSDimitry Andric       ExecutionContext exe_ctx =
3558435933ddSDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
3559435933ddSDimitry Andric       if (exe_ctx.HasProcessScope()) {
356012b93ac6SEd Maste         Process *process = exe_ctx.GetProcessPtr();
356112b93ac6SEd Maste         if (process && process->IsAlive())
356212b93ac6SEd Maste           process->Halt();
356312b93ac6SEd Maste       }
356412b93ac6SEd Maste     }
356512b93ac6SEd Maste       return MenuActionResult::Handled;
356612b93ac6SEd Maste 
3567435933ddSDimitry Andric     case eMenuID_ProcessDetach: {
3568435933ddSDimitry Andric       ExecutionContext exe_ctx =
3569435933ddSDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
3570435933ddSDimitry Andric       if (exe_ctx.HasProcessScope()) {
357112b93ac6SEd Maste         Process *process = exe_ctx.GetProcessPtr();
357212b93ac6SEd Maste         if (process && process->IsAlive())
357312b93ac6SEd Maste           process->Detach(false);
357412b93ac6SEd Maste       }
357512b93ac6SEd Maste     }
357612b93ac6SEd Maste       return MenuActionResult::Handled;
357712b93ac6SEd Maste 
3578435933ddSDimitry Andric     case eMenuID_Process: {
3579435933ddSDimitry Andric       // Populate the menu with all of the threads if the process is stopped
35804ba319b5SDimitry Andric       // when the Process menu gets selected and is about to display its
35814ba319b5SDimitry Andric       // submenu.
358212b93ac6SEd Maste       Menus &submenus = menu.GetSubmenus();
3583435933ddSDimitry Andric       ExecutionContext exe_ctx =
3584435933ddSDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
358512b93ac6SEd Maste       Process *process = exe_ctx.GetProcessPtr();
3586435933ddSDimitry Andric       if (process && process->IsAlive() &&
3587435933ddSDimitry Andric           StateIsStoppedState(process->GetState(), true)) {
358812b93ac6SEd Maste         if (submenus.size() == 7)
358912b93ac6SEd Maste           menu.AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
359012b93ac6SEd Maste         else if (submenus.size() > 8)
359112b93ac6SEd Maste           submenus.erase(submenus.begin() + 8, submenus.end());
359212b93ac6SEd Maste 
359312b93ac6SEd Maste         ThreadList &threads = process->GetThreadList();
35944bb0738eSEd Maste         std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
359512b93ac6SEd Maste         size_t num_threads = threads.GetSize();
3596435933ddSDimitry Andric         for (size_t i = 0; i < num_threads; ++i) {
359712b93ac6SEd Maste           ThreadSP thread_sp = threads.GetThreadAtIndex(i);
359812b93ac6SEd Maste           char menu_char = '\0';
359912b93ac6SEd Maste           if (i < 9)
360012b93ac6SEd Maste             menu_char = '1' + i;
360112b93ac6SEd Maste           StreamString thread_menu_title;
360212b93ac6SEd Maste           thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID());
360312b93ac6SEd Maste           const char *thread_name = thread_sp->GetName();
360412b93ac6SEd Maste           if (thread_name && thread_name[0])
360512b93ac6SEd Maste             thread_menu_title.Printf(" %s", thread_name);
3606435933ddSDimitry Andric           else {
360712b93ac6SEd Maste             const char *queue_name = thread_sp->GetQueueName();
360812b93ac6SEd Maste             if (queue_name && queue_name[0])
360912b93ac6SEd Maste               thread_menu_title.Printf(" %s", queue_name);
361012b93ac6SEd Maste           }
3611435933ddSDimitry Andric           menu.AddSubmenu(
3612435933ddSDimitry Andric               MenuSP(new Menu(thread_menu_title.GetString().str().c_str(),
3613435933ddSDimitry Andric                               nullptr, menu_char, thread_sp->GetID())));
361412b93ac6SEd Maste         }
3615435933ddSDimitry Andric       } else if (submenus.size() > 7) {
36164ba319b5SDimitry Andric         // Remove the separator and any other thread submenu items that were
36174ba319b5SDimitry Andric         // previously added
361812b93ac6SEd Maste         submenus.erase(submenus.begin() + 7, submenus.end());
361912b93ac6SEd Maste       }
3620435933ddSDimitry Andric       // Since we are adding and removing items we need to recalculate the name
3621435933ddSDimitry Andric       // lengths
362212b93ac6SEd Maste       menu.RecalculateNameLengths();
362312b93ac6SEd Maste     }
362412b93ac6SEd Maste       return MenuActionResult::Handled;
362512b93ac6SEd Maste 
3626435933ddSDimitry Andric     case eMenuID_ViewVariables: {
362712b93ac6SEd Maste       WindowSP main_window_sp = m_app.GetMainWindow();
362812b93ac6SEd Maste       WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
362912b93ac6SEd Maste       WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
363012b93ac6SEd Maste       WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
363112b93ac6SEd Maste       const Rect source_bounds = source_window_sp->GetBounds();
363212b93ac6SEd Maste 
3633435933ddSDimitry Andric       if (variables_window_sp) {
363412b93ac6SEd Maste         const Rect variables_bounds = variables_window_sp->GetBounds();
363512b93ac6SEd Maste 
363612b93ac6SEd Maste         main_window_sp->RemoveSubWindow(variables_window_sp.get());
363712b93ac6SEd Maste 
3638435933ddSDimitry Andric         if (registers_window_sp) {
3639435933ddSDimitry Andric           // We have a registers window, so give all the area back to the
3640435933ddSDimitry Andric           // registers window
364112b93ac6SEd Maste           Rect registers_bounds = variables_bounds;
364212b93ac6SEd Maste           registers_bounds.size.width = source_bounds.size.width;
364312b93ac6SEd Maste           registers_window_sp->SetBounds(registers_bounds);
3644435933ddSDimitry Andric         } else {
36454ba319b5SDimitry Andric           // We have no registers window showing so give the bottom area back
36464ba319b5SDimitry Andric           // to the source view
364712b93ac6SEd Maste           source_window_sp->Resize(source_bounds.size.width,
3648435933ddSDimitry Andric                                    source_bounds.size.height +
3649435933ddSDimitry Andric                                        variables_bounds.size.height);
365012b93ac6SEd Maste         }
3651435933ddSDimitry Andric       } else {
365212b93ac6SEd Maste         Rect new_variables_rect;
3653435933ddSDimitry Andric         if (registers_window_sp) {
365412b93ac6SEd Maste           // We have a registers window so split the area of the registers
365512b93ac6SEd Maste           // window into two columns where the left hand side will be the
365612b93ac6SEd Maste           // variables and the right hand side will be the registers
365712b93ac6SEd Maste           const Rect variables_bounds = registers_window_sp->GetBounds();
365812b93ac6SEd Maste           Rect new_registers_rect;
3659435933ddSDimitry Andric           variables_bounds.VerticalSplitPercentage(0.50, new_variables_rect,
3660435933ddSDimitry Andric                                                    new_registers_rect);
366112b93ac6SEd Maste           registers_window_sp->SetBounds(new_registers_rect);
3662435933ddSDimitry Andric         } else {
366312b93ac6SEd Maste           // No variables window, grab the bottom part of the source window
366412b93ac6SEd Maste           Rect new_source_rect;
3665435933ddSDimitry Andric           source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
3666435933ddSDimitry Andric                                                   new_variables_rect);
366712b93ac6SEd Maste           source_window_sp->SetBounds(new_source_rect);
366812b93ac6SEd Maste         }
3669435933ddSDimitry Andric         WindowSP new_window_sp = main_window_sp->CreateSubWindow(
3670435933ddSDimitry Andric             "Variables", new_variables_rect, false);
3671435933ddSDimitry Andric         new_window_sp->SetDelegate(
3672435933ddSDimitry Andric             WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
367312b93ac6SEd Maste       }
367412b93ac6SEd Maste       touchwin(stdscr);
367512b93ac6SEd Maste     }
367612b93ac6SEd Maste       return MenuActionResult::Handled;
367712b93ac6SEd Maste 
3678435933ddSDimitry Andric     case eMenuID_ViewRegisters: {
367912b93ac6SEd Maste       WindowSP main_window_sp = m_app.GetMainWindow();
368012b93ac6SEd Maste       WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
368112b93ac6SEd Maste       WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
368212b93ac6SEd Maste       WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
368312b93ac6SEd Maste       const Rect source_bounds = source_window_sp->GetBounds();
368412b93ac6SEd Maste 
3685435933ddSDimitry Andric       if (registers_window_sp) {
3686435933ddSDimitry Andric         if (variables_window_sp) {
368712b93ac6SEd Maste           const Rect variables_bounds = variables_window_sp->GetBounds();
368812b93ac6SEd Maste 
3689435933ddSDimitry Andric           // We have a variables window, so give all the area back to the
3690435933ddSDimitry Andric           // variables window
3691435933ddSDimitry Andric           variables_window_sp->Resize(variables_bounds.size.width +
3692435933ddSDimitry Andric                                           registers_window_sp->GetWidth(),
369312b93ac6SEd Maste                                       variables_bounds.size.height);
3694435933ddSDimitry Andric         } else {
36954ba319b5SDimitry Andric           // We have no variables window showing so give the bottom area back
36964ba319b5SDimitry Andric           // to the source view
369712b93ac6SEd Maste           source_window_sp->Resize(source_bounds.size.width,
3698435933ddSDimitry Andric                                    source_bounds.size.height +
3699435933ddSDimitry Andric                                        registers_window_sp->GetHeight());
370012b93ac6SEd Maste         }
370112b93ac6SEd Maste         main_window_sp->RemoveSubWindow(registers_window_sp.get());
3702435933ddSDimitry Andric       } else {
370312b93ac6SEd Maste         Rect new_regs_rect;
3704435933ddSDimitry Andric         if (variables_window_sp) {
37054ba319b5SDimitry Andric           // We have a variables window, split it into two columns where the
37064ba319b5SDimitry Andric           // left hand side will be the variables and the right hand side will
37074ba319b5SDimitry Andric           // be the registers
370812b93ac6SEd Maste           const Rect variables_bounds = variables_window_sp->GetBounds();
370912b93ac6SEd Maste           Rect new_vars_rect;
3710435933ddSDimitry Andric           variables_bounds.VerticalSplitPercentage(0.50, new_vars_rect,
3711435933ddSDimitry Andric                                                    new_regs_rect);
371212b93ac6SEd Maste           variables_window_sp->SetBounds(new_vars_rect);
3713435933ddSDimitry Andric         } else {
371412b93ac6SEd Maste           // No registers window, grab the bottom part of the source window
371512b93ac6SEd Maste           Rect new_source_rect;
3716435933ddSDimitry Andric           source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
3717435933ddSDimitry Andric                                                   new_regs_rect);
371812b93ac6SEd Maste           source_window_sp->SetBounds(new_source_rect);
371912b93ac6SEd Maste         }
3720435933ddSDimitry Andric         WindowSP new_window_sp =
3721435933ddSDimitry Andric             main_window_sp->CreateSubWindow("Registers", new_regs_rect, false);
3722435933ddSDimitry Andric         new_window_sp->SetDelegate(
3723435933ddSDimitry Andric             WindowDelegateSP(new RegistersWindowDelegate(m_debugger)));
372412b93ac6SEd Maste       }
372512b93ac6SEd Maste       touchwin(stdscr);
372612b93ac6SEd Maste     }
372712b93ac6SEd Maste       return MenuActionResult::Handled;
372812b93ac6SEd Maste 
372912b93ac6SEd Maste     case eMenuID_HelpGUIHelp:
373012b93ac6SEd Maste       m_app.GetMainWindow()->CreateHelpSubwindow();
373112b93ac6SEd Maste       return MenuActionResult::Handled;
373212b93ac6SEd Maste 
373312b93ac6SEd Maste     default:
373412b93ac6SEd Maste       break;
373512b93ac6SEd Maste     }
373612b93ac6SEd Maste 
373712b93ac6SEd Maste     return MenuActionResult::NotHandled;
373812b93ac6SEd Maste   }
3739435933ddSDimitry Andric 
374012b93ac6SEd Maste protected:
374112b93ac6SEd Maste   Application &m_app;
374212b93ac6SEd Maste   Debugger &m_debugger;
374312b93ac6SEd Maste };
374412b93ac6SEd Maste 
3745435933ddSDimitry Andric class StatusBarWindowDelegate : public WindowDelegate {
374612b93ac6SEd Maste public:
StatusBarWindowDelegate(Debugger & debugger)3747435933ddSDimitry Andric   StatusBarWindowDelegate(Debugger &debugger) : m_debugger(debugger) {
3748435933ddSDimitry Andric     FormatEntity::Parse("Thread: ${thread.id%tid}", m_format);
374912b93ac6SEd Maste   }
375012b93ac6SEd Maste 
37519f2f44ceSEd Maste   ~StatusBarWindowDelegate() override = default;
37521c3bbb01SEd Maste 
WindowDelegateDraw(Window & window,bool force)3753435933ddSDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
3754435933ddSDimitry Andric     ExecutionContext exe_ctx =
3755435933ddSDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext();
375612b93ac6SEd Maste     Process *process = exe_ctx.GetProcessPtr();
375712b93ac6SEd Maste     Thread *thread = exe_ctx.GetThreadPtr();
375812b93ac6SEd Maste     StackFrame *frame = exe_ctx.GetFramePtr();
375912b93ac6SEd Maste     window.Erase();
376012b93ac6SEd Maste     window.SetBackground(2);
376112b93ac6SEd Maste     window.MoveCursor(0, 0);
3762435933ddSDimitry Andric     if (process) {
376312b93ac6SEd Maste       const StateType state = process->GetState();
3764435933ddSDimitry Andric       window.Printf("Process: %5" PRIu64 " %10s", process->GetID(),
3765435933ddSDimitry Andric                     StateAsCString(state));
376612b93ac6SEd Maste 
3767435933ddSDimitry Andric       if (StateIsStoppedState(state, true)) {
37680127ef0fSEd Maste         StreamString strm;
3769435933ddSDimitry Andric         if (thread && FormatEntity::Format(m_format, strm, nullptr, &exe_ctx,
3770435933ddSDimitry Andric                                            nullptr, nullptr, false, false)) {
377112b93ac6SEd Maste           window.MoveCursor(40, 0);
3772435933ddSDimitry Andric           window.PutCStringTruncated(strm.GetString().str().c_str(), 1);
37730127ef0fSEd Maste         }
377412b93ac6SEd Maste 
377512b93ac6SEd Maste         window.MoveCursor(60, 0);
377612b93ac6SEd Maste         if (frame)
3777435933ddSDimitry Andric           window.Printf("Frame: %3u  PC = 0x%16.16" PRIx64,
3778435933ddSDimitry Andric                         frame->GetFrameIndex(),
3779435933ddSDimitry Andric                         frame->GetFrameCodeAddress().GetOpcodeLoadAddress(
3780435933ddSDimitry Andric                             exe_ctx.GetTargetPtr()));
3781435933ddSDimitry Andric       } else if (state == eStateExited) {
378212b93ac6SEd Maste         const char *exit_desc = process->GetExitDescription();
378312b93ac6SEd Maste         const int exit_status = process->GetExitStatus();
378412b93ac6SEd Maste         if (exit_desc && exit_desc[0])
378512b93ac6SEd Maste           window.Printf(" with status = %i (%s)", exit_status, exit_desc);
378612b93ac6SEd Maste         else
378712b93ac6SEd Maste           window.Printf(" with status = %i", exit_status);
378812b93ac6SEd Maste       }
378912b93ac6SEd Maste     }
379012b93ac6SEd Maste     window.DeferredRefresh();
379112b93ac6SEd Maste     return true;
379212b93ac6SEd Maste   }
379312b93ac6SEd Maste 
379412b93ac6SEd Maste protected:
379512b93ac6SEd Maste   Debugger &m_debugger;
37961c3bbb01SEd Maste   FormatEntity::Entry m_format;
379712b93ac6SEd Maste };
379812b93ac6SEd Maste 
3799435933ddSDimitry Andric class SourceFileWindowDelegate : public WindowDelegate {
380012b93ac6SEd Maste public:
SourceFileWindowDelegate(Debugger & debugger)3801435933ddSDimitry Andric   SourceFileWindowDelegate(Debugger &debugger)
3802435933ddSDimitry Andric       : WindowDelegate(), m_debugger(debugger), m_sc(), m_file_sp(),
3803435933ddSDimitry Andric         m_disassembly_scope(nullptr), m_disassembly_sp(), m_disassembly_range(),
3804435933ddSDimitry Andric         m_title(), m_line_width(4), m_selected_line(0), m_pc_line(0),
3805435933ddSDimitry Andric         m_stop_id(0), m_frame_idx(UINT32_MAX), m_first_visible_line(0),
3806435933ddSDimitry Andric         m_min_x(0), m_min_y(0), m_max_x(0), m_max_y(0) {}
380712b93ac6SEd Maste 
38089f2f44ceSEd Maste   ~SourceFileWindowDelegate() override = default;
380912b93ac6SEd Maste 
Update(const SymbolContext & sc)3810435933ddSDimitry Andric   void Update(const SymbolContext &sc) { m_sc = sc; }
381112b93ac6SEd Maste 
NumVisibleLines() const3812435933ddSDimitry Andric   uint32_t NumVisibleLines() const { return m_max_y - m_min_y; }
381312b93ac6SEd Maste 
WindowDelegateGetHelpText()3814435933ddSDimitry Andric   const char *WindowDelegateGetHelpText() override {
381512b93ac6SEd Maste     return "Source/Disassembly window keyboard shortcuts:";
381612b93ac6SEd Maste   }
381712b93ac6SEd Maste 
WindowDelegateGetKeyHelp()3818435933ddSDimitry Andric   KeyHelp *WindowDelegateGetKeyHelp() override {
381912b93ac6SEd Maste     static curses::KeyHelp g_source_view_key_help[] = {
382012b93ac6SEd Maste         {KEY_RETURN, "Run to selected line with one shot breakpoint"},
382112b93ac6SEd Maste         {KEY_UP, "Select previous source line"},
382212b93ac6SEd Maste         {KEY_DOWN, "Select next source line"},
382312b93ac6SEd Maste         {KEY_PPAGE, "Page up"},
382412b93ac6SEd Maste         {KEY_NPAGE, "Page down"},
382512b93ac6SEd Maste         {'b', "Set breakpoint on selected source/disassembly line"},
382612b93ac6SEd Maste         {'c', "Continue process"},
382712b93ac6SEd Maste         {'d', "Detach and resume process"},
382812b93ac6SEd Maste         {'D', "Detach with process suspended"},
382912b93ac6SEd Maste         {'h', "Show help dialog"},
383012b93ac6SEd Maste         {'k', "Kill process"},
383112b93ac6SEd Maste         {'n', "Step over (source line)"},
383212b93ac6SEd Maste         {'N', "Step over (single instruction)"},
383312b93ac6SEd Maste         {'o', "Step out"},
383412b93ac6SEd Maste         {'s', "Step in (source line)"},
383512b93ac6SEd Maste         {'S', "Step in (single instruction)"},
383612b93ac6SEd Maste         {',', "Page up"},
383712b93ac6SEd Maste         {'.', "Page down"},
3838435933ddSDimitry Andric         {'\0', nullptr}};
383912b93ac6SEd Maste     return g_source_view_key_help;
384012b93ac6SEd Maste   }
384112b93ac6SEd Maste 
WindowDelegateDraw(Window & window,bool force)3842435933ddSDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
3843435933ddSDimitry Andric     ExecutionContext exe_ctx =
3844435933ddSDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext();
384512b93ac6SEd Maste     Process *process = exe_ctx.GetProcessPtr();
38464bb0738eSEd Maste     Thread *thread = nullptr;
384712b93ac6SEd Maste 
384812b93ac6SEd Maste     bool update_location = false;
3849435933ddSDimitry Andric     if (process) {
385012b93ac6SEd Maste       StateType state = process->GetState();
3851435933ddSDimitry Andric       if (StateIsStoppedState(state, true)) {
385212b93ac6SEd Maste         // We are stopped, so it is ok to
385312b93ac6SEd Maste         update_location = true;
385412b93ac6SEd Maste       }
385512b93ac6SEd Maste     }
385612b93ac6SEd Maste 
385712b93ac6SEd Maste     m_min_x = 1;
38580127ef0fSEd Maste     m_min_y = 2;
385912b93ac6SEd Maste     m_max_x = window.GetMaxX() - 1;
386012b93ac6SEd Maste     m_max_y = window.GetMaxY() - 1;
386112b93ac6SEd Maste 
386212b93ac6SEd Maste     const uint32_t num_visible_lines = NumVisibleLines();
386312b93ac6SEd Maste     StackFrameSP frame_sp;
386412b93ac6SEd Maste     bool set_selected_line_to_pc = false;
386512b93ac6SEd Maste 
3866435933ddSDimitry Andric     if (update_location) {
386712b93ac6SEd Maste       const bool process_alive = process ? process->IsAlive() : false;
386812b93ac6SEd Maste       bool thread_changed = false;
3869435933ddSDimitry Andric       if (process_alive) {
387012b93ac6SEd Maste         thread = exe_ctx.GetThreadPtr();
3871435933ddSDimitry Andric         if (thread) {
387212b93ac6SEd Maste           frame_sp = thread->GetSelectedFrame();
387312b93ac6SEd Maste           auto tid = thread->GetID();
387412b93ac6SEd Maste           thread_changed = tid != m_tid;
387512b93ac6SEd Maste           m_tid = tid;
3876435933ddSDimitry Andric         } else {
3877435933ddSDimitry Andric           if (m_tid != LLDB_INVALID_THREAD_ID) {
387812b93ac6SEd Maste             thread_changed = true;
387912b93ac6SEd Maste             m_tid = LLDB_INVALID_THREAD_ID;
388012b93ac6SEd Maste           }
388112b93ac6SEd Maste         }
388212b93ac6SEd Maste       }
388312b93ac6SEd Maste       const uint32_t stop_id = process ? process->GetStopID() : 0;
388412b93ac6SEd Maste       const bool stop_id_changed = stop_id != m_stop_id;
388512b93ac6SEd Maste       bool frame_changed = false;
388612b93ac6SEd Maste       m_stop_id = stop_id;
38870127ef0fSEd Maste       m_title.Clear();
3888435933ddSDimitry Andric       if (frame_sp) {
388912b93ac6SEd Maste         m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
3890435933ddSDimitry Andric         if (m_sc.module_sp) {
3891435933ddSDimitry Andric           m_title.Printf(
3892435933ddSDimitry Andric               "%s", m_sc.module_sp->GetFileSpec().GetFilename().GetCString());
38930127ef0fSEd Maste           ConstString func_name = m_sc.GetFunctionName();
38940127ef0fSEd Maste           if (func_name)
38950127ef0fSEd Maste             m_title.Printf("`%s", func_name.GetCString());
38960127ef0fSEd Maste         }
389712b93ac6SEd Maste         const uint32_t frame_idx = frame_sp->GetFrameIndex();
389812b93ac6SEd Maste         frame_changed = frame_idx != m_frame_idx;
389912b93ac6SEd Maste         m_frame_idx = frame_idx;
3900435933ddSDimitry Andric       } else {
390112b93ac6SEd Maste         m_sc.Clear(true);
390212b93ac6SEd Maste         frame_changed = m_frame_idx != UINT32_MAX;
390312b93ac6SEd Maste         m_frame_idx = UINT32_MAX;
390412b93ac6SEd Maste       }
390512b93ac6SEd Maste 
3906435933ddSDimitry Andric       const bool context_changed =
3907435933ddSDimitry Andric           thread_changed || frame_changed || stop_id_changed;
390812b93ac6SEd Maste 
3909435933ddSDimitry Andric       if (process_alive) {
3910435933ddSDimitry Andric         if (m_sc.line_entry.IsValid()) {
391112b93ac6SEd Maste           m_pc_line = m_sc.line_entry.line;
391212b93ac6SEd Maste           if (m_pc_line != UINT32_MAX)
391312b93ac6SEd Maste             --m_pc_line; // Convert to zero based line number...
391412b93ac6SEd Maste           // Update the selected line if the stop ID changed...
391512b93ac6SEd Maste           if (context_changed)
391612b93ac6SEd Maste             m_selected_line = m_pc_line;
391712b93ac6SEd Maste 
3918435933ddSDimitry Andric           if (m_file_sp && m_file_sp->FileSpecMatches(m_sc.line_entry.file)) {
39194ba319b5SDimitry Andric             // Same file, nothing to do, we should either have the lines or not
39204ba319b5SDimitry Andric             // (source file missing)
3921435933ddSDimitry Andric             if (m_selected_line >= static_cast<size_t>(m_first_visible_line)) {
392212b93ac6SEd Maste               if (m_selected_line >= m_first_visible_line + num_visible_lines)
392312b93ac6SEd Maste                 m_first_visible_line = m_selected_line - 10;
3924435933ddSDimitry Andric             } else {
392512b93ac6SEd Maste               if (m_selected_line > 10)
392612b93ac6SEd Maste                 m_first_visible_line = m_selected_line - 10;
392712b93ac6SEd Maste               else
392812b93ac6SEd Maste                 m_first_visible_line = 0;
392912b93ac6SEd Maste             }
3930435933ddSDimitry Andric           } else {
393112b93ac6SEd Maste             // File changed, set selected line to the line with the PC
393212b93ac6SEd Maste             m_selected_line = m_pc_line;
3933435933ddSDimitry Andric             m_file_sp =
3934435933ddSDimitry Andric                 m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file);
3935435933ddSDimitry Andric             if (m_file_sp) {
393612b93ac6SEd Maste               const size_t num_lines = m_file_sp->GetNumLines();
393712b93ac6SEd Maste               int m_line_width = 1;
393812b93ac6SEd Maste               for (size_t n = num_lines; n >= 10; n = n / 10)
393912b93ac6SEd Maste                 ++m_line_width;
394012b93ac6SEd Maste 
3941435933ddSDimitry Andric               snprintf(m_line_format, sizeof(m_line_format), " %%%iu ",
3942435933ddSDimitry Andric                        m_line_width);
3943435933ddSDimitry Andric               if (num_lines < num_visible_lines ||
3944435933ddSDimitry Andric                   m_selected_line < num_visible_lines)
394512b93ac6SEd Maste                 m_first_visible_line = 0;
394612b93ac6SEd Maste               else
394712b93ac6SEd Maste                 m_first_visible_line = m_selected_line - 10;
394812b93ac6SEd Maste             }
394912b93ac6SEd Maste           }
3950435933ddSDimitry Andric         } else {
395112b93ac6SEd Maste           m_file_sp.reset();
395212b93ac6SEd Maste         }
395312b93ac6SEd Maste 
3954435933ddSDimitry Andric         if (!m_file_sp || m_file_sp->GetNumLines() == 0) {
395512b93ac6SEd Maste           // Show disassembly
395612b93ac6SEd Maste           bool prefer_file_cache = false;
3957435933ddSDimitry Andric           if (m_sc.function) {
3958435933ddSDimitry Andric             if (m_disassembly_scope != m_sc.function) {
395912b93ac6SEd Maste               m_disassembly_scope = m_sc.function;
3960435933ddSDimitry Andric               m_disassembly_sp = m_sc.function->GetInstructions(
3961435933ddSDimitry Andric                   exe_ctx, nullptr, prefer_file_cache);
3962435933ddSDimitry Andric               if (m_disassembly_sp) {
396312b93ac6SEd Maste                 set_selected_line_to_pc = true;
396412b93ac6SEd Maste                 m_disassembly_range = m_sc.function->GetAddressRange();
3965435933ddSDimitry Andric               } else {
396612b93ac6SEd Maste                 m_disassembly_range.Clear();
396712b93ac6SEd Maste               }
3968435933ddSDimitry Andric             } else {
396912b93ac6SEd Maste               set_selected_line_to_pc = context_changed;
397012b93ac6SEd Maste             }
3971435933ddSDimitry Andric           } else if (m_sc.symbol) {
3972435933ddSDimitry Andric             if (m_disassembly_scope != m_sc.symbol) {
397312b93ac6SEd Maste               m_disassembly_scope = m_sc.symbol;
3974435933ddSDimitry Andric               m_disassembly_sp = m_sc.symbol->GetInstructions(
3975435933ddSDimitry Andric                   exe_ctx, nullptr, prefer_file_cache);
3976435933ddSDimitry Andric               if (m_disassembly_sp) {
397712b93ac6SEd Maste                 set_selected_line_to_pc = true;
3978435933ddSDimitry Andric                 m_disassembly_range.GetBaseAddress() =
3979435933ddSDimitry Andric                     m_sc.symbol->GetAddress();
398012b93ac6SEd Maste                 m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize());
3981435933ddSDimitry Andric               } else {
398212b93ac6SEd Maste                 m_disassembly_range.Clear();
398312b93ac6SEd Maste               }
3984435933ddSDimitry Andric             } else {
398512b93ac6SEd Maste               set_selected_line_to_pc = context_changed;
398612b93ac6SEd Maste             }
398712b93ac6SEd Maste           }
398812b93ac6SEd Maste         }
3989435933ddSDimitry Andric       } else {
399012b93ac6SEd Maste         m_pc_line = UINT32_MAX;
399112b93ac6SEd Maste       }
399212b93ac6SEd Maste     }
399312b93ac6SEd Maste 
39940127ef0fSEd Maste     const int window_width = window.GetWidth();
399512b93ac6SEd Maste     window.Erase();
399612b93ac6SEd Maste     window.DrawTitleBox("Sources");
3997435933ddSDimitry Andric     if (!m_title.GetString().empty()) {
39980127ef0fSEd Maste       window.AttributeOn(A_REVERSE);
39990127ef0fSEd Maste       window.MoveCursor(1, 1);
40000127ef0fSEd Maste       window.PutChar(' ');
4001435933ddSDimitry Andric       window.PutCStringTruncated(m_title.GetString().str().c_str(), 1);
40020127ef0fSEd Maste       int x = window.GetCursorX();
4003435933ddSDimitry Andric       if (x < window_width - 1) {
40040127ef0fSEd Maste         window.Printf("%*s", window_width - x - 1, "");
40050127ef0fSEd Maste       }
40060127ef0fSEd Maste       window.AttributeOff(A_REVERSE);
40070127ef0fSEd Maste     }
400812b93ac6SEd Maste 
400912b93ac6SEd Maste     Target *target = exe_ctx.GetTargetPtr();
401012b93ac6SEd Maste     const size_t num_source_lines = GetNumSourceLines();
4011435933ddSDimitry Andric     if (num_source_lines > 0) {
401212b93ac6SEd Maste       // Display source
401312b93ac6SEd Maste       BreakpointLines bp_lines;
4014435933ddSDimitry Andric       if (target) {
401512b93ac6SEd Maste         BreakpointList &bp_list = target->GetBreakpointList();
401612b93ac6SEd Maste         const size_t num_bps = bp_list.GetSize();
4017435933ddSDimitry Andric         for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
401812b93ac6SEd Maste           BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
401912b93ac6SEd Maste           const size_t num_bps_locs = bp_sp->GetNumLocations();
4020435933ddSDimitry Andric           for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
4021435933ddSDimitry Andric             BreakpointLocationSP bp_loc_sp =
4022435933ddSDimitry Andric                 bp_sp->GetLocationAtIndex(bp_loc_idx);
402312b93ac6SEd Maste             LineEntry bp_loc_line_entry;
4024435933ddSDimitry Andric             if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry(
4025435933ddSDimitry Andric                     bp_loc_line_entry)) {
4026435933ddSDimitry Andric               if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file) {
402712b93ac6SEd Maste                 bp_lines.insert(bp_loc_line_entry.line);
402812b93ac6SEd Maste               }
402912b93ac6SEd Maste             }
403012b93ac6SEd Maste           }
403112b93ac6SEd Maste         }
403212b93ac6SEd Maste       }
403312b93ac6SEd Maste 
403412b93ac6SEd Maste       const attr_t selected_highlight_attr = A_REVERSE;
403512b93ac6SEd Maste       const attr_t pc_highlight_attr = COLOR_PAIR(1);
403612b93ac6SEd Maste 
4037435933ddSDimitry Andric       for (size_t i = 0; i < num_visible_lines; ++i) {
403812b93ac6SEd Maste         const uint32_t curr_line = m_first_visible_line + i;
4039435933ddSDimitry Andric         if (curr_line < num_source_lines) {
40400127ef0fSEd Maste           const int line_y = m_min_y + i;
404112b93ac6SEd Maste           window.MoveCursor(1, line_y);
404212b93ac6SEd Maste           const bool is_pc_line = curr_line == m_pc_line;
404312b93ac6SEd Maste           const bool line_is_selected = m_selected_line == curr_line;
404412b93ac6SEd Maste           // Highlight the line as the PC line first, then if the selected line
404512b93ac6SEd Maste           // isn't the same as the PC line, highlight it differently
404612b93ac6SEd Maste           attr_t highlight_attr = 0;
404712b93ac6SEd Maste           attr_t bp_attr = 0;
404812b93ac6SEd Maste           if (is_pc_line)
404912b93ac6SEd Maste             highlight_attr = pc_highlight_attr;
405012b93ac6SEd Maste           else if (line_is_selected)
405112b93ac6SEd Maste             highlight_attr = selected_highlight_attr;
405212b93ac6SEd Maste 
405312b93ac6SEd Maste           if (bp_lines.find(curr_line + 1) != bp_lines.end())
405412b93ac6SEd Maste             bp_attr = COLOR_PAIR(2);
405512b93ac6SEd Maste 
405612b93ac6SEd Maste           if (bp_attr)
405712b93ac6SEd Maste             window.AttributeOn(bp_attr);
405812b93ac6SEd Maste 
405912b93ac6SEd Maste           window.Printf(m_line_format, curr_line + 1);
406012b93ac6SEd Maste 
406112b93ac6SEd Maste           if (bp_attr)
406212b93ac6SEd Maste             window.AttributeOff(bp_attr);
406312b93ac6SEd Maste 
406412b93ac6SEd Maste           window.PutChar(ACS_VLINE);
406512b93ac6SEd Maste           // Mark the line with the PC with a diamond
406612b93ac6SEd Maste           if (is_pc_line)
406712b93ac6SEd Maste             window.PutChar(ACS_DIAMOND);
406812b93ac6SEd Maste           else
406912b93ac6SEd Maste             window.PutChar(' ');
407012b93ac6SEd Maste 
407112b93ac6SEd Maste           if (highlight_attr)
407212b93ac6SEd Maste             window.AttributeOn(highlight_attr);
4073435933ddSDimitry Andric           const uint32_t line_len =
4074435933ddSDimitry Andric               m_file_sp->GetLineLength(curr_line + 1, false);
407512b93ac6SEd Maste           if (line_len > 0)
407612b93ac6SEd Maste             window.PutCString(m_file_sp->PeekLineData(curr_line + 1), line_len);
407712b93ac6SEd Maste 
4078435933ddSDimitry Andric           if (is_pc_line && frame_sp &&
4079435933ddSDimitry Andric               frame_sp->GetConcreteFrameIndex() == 0) {
408012b93ac6SEd Maste             StopInfoSP stop_info_sp;
408112b93ac6SEd Maste             if (thread)
408212b93ac6SEd Maste               stop_info_sp = thread->GetStopInfo();
4083435933ddSDimitry Andric             if (stop_info_sp) {
408412b93ac6SEd Maste               const char *stop_description = stop_info_sp->GetDescription();
4085435933ddSDimitry Andric               if (stop_description && stop_description[0]) {
408612b93ac6SEd Maste                 size_t stop_description_len = strlen(stop_description);
40870127ef0fSEd Maste                 int desc_x = window_width - stop_description_len - 16;
408812b93ac6SEd Maste                 window.Printf("%*s", desc_x - window.GetCursorX(), "");
4089435933ddSDimitry Andric                 // window.MoveCursor(window_width - stop_description_len - 15,
4090435933ddSDimitry Andric                 // line_y);
4091435933ddSDimitry Andric                 window.Printf("<<< Thread %u: %s ", thread->GetIndexID(),
4092435933ddSDimitry Andric                               stop_description);
409312b93ac6SEd Maste               }
4094435933ddSDimitry Andric             } else {
40950127ef0fSEd Maste               window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
409612b93ac6SEd Maste             }
409712b93ac6SEd Maste           }
409812b93ac6SEd Maste           if (highlight_attr)
409912b93ac6SEd Maste             window.AttributeOff(highlight_attr);
4100435933ddSDimitry Andric         } else {
410112b93ac6SEd Maste           break;
410212b93ac6SEd Maste         }
410312b93ac6SEd Maste       }
4104435933ddSDimitry Andric     } else {
410512b93ac6SEd Maste       size_t num_disassembly_lines = GetNumDisassemblyLines();
4106435933ddSDimitry Andric       if (num_disassembly_lines > 0) {
410712b93ac6SEd Maste         // Display disassembly
410812b93ac6SEd Maste         BreakpointAddrs bp_file_addrs;
410912b93ac6SEd Maste         Target *target = exe_ctx.GetTargetPtr();
4110435933ddSDimitry Andric         if (target) {
411112b93ac6SEd Maste           BreakpointList &bp_list = target->GetBreakpointList();
411212b93ac6SEd Maste           const size_t num_bps = bp_list.GetSize();
4113435933ddSDimitry Andric           for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
411412b93ac6SEd Maste             BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
411512b93ac6SEd Maste             const size_t num_bps_locs = bp_sp->GetNumLocations();
4116435933ddSDimitry Andric             for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs;
4117435933ddSDimitry Andric                  ++bp_loc_idx) {
4118435933ddSDimitry Andric               BreakpointLocationSP bp_loc_sp =
4119435933ddSDimitry Andric                   bp_sp->GetLocationAtIndex(bp_loc_idx);
412012b93ac6SEd Maste               LineEntry bp_loc_line_entry;
4121435933ddSDimitry Andric               const lldb::addr_t file_addr =
4122435933ddSDimitry Andric                   bp_loc_sp->GetAddress().GetFileAddress();
4123435933ddSDimitry Andric               if (file_addr != LLDB_INVALID_ADDRESS) {
412412b93ac6SEd Maste                 if (m_disassembly_range.ContainsFileAddress(file_addr))
412512b93ac6SEd Maste                   bp_file_addrs.insert(file_addr);
412612b93ac6SEd Maste               }
412712b93ac6SEd Maste             }
412812b93ac6SEd Maste           }
412912b93ac6SEd Maste         }
413012b93ac6SEd Maste 
413112b93ac6SEd Maste         const attr_t selected_highlight_attr = A_REVERSE;
413212b93ac6SEd Maste         const attr_t pc_highlight_attr = COLOR_PAIR(1);
413312b93ac6SEd Maste 
413412b93ac6SEd Maste         StreamString strm;
413512b93ac6SEd Maste 
413612b93ac6SEd Maste         InstructionList &insts = m_disassembly_sp->GetInstructionList();
413712b93ac6SEd Maste         Address pc_address;
413812b93ac6SEd Maste 
413912b93ac6SEd Maste         if (frame_sp)
414012b93ac6SEd Maste           pc_address = frame_sp->GetFrameCodeAddress();
4141435933ddSDimitry Andric         const uint32_t pc_idx =
4142435933ddSDimitry Andric             pc_address.IsValid()
4143435933ddSDimitry Andric                 ? insts.GetIndexOfInstructionAtAddress(pc_address)
4144435933ddSDimitry Andric                 : UINT32_MAX;
4145435933ddSDimitry Andric         if (set_selected_line_to_pc) {
414612b93ac6SEd Maste           m_selected_line = pc_idx;
414712b93ac6SEd Maste         }
414812b93ac6SEd Maste 
414912b93ac6SEd Maste         const uint32_t non_visible_pc_offset = (num_visible_lines / 5);
41500127ef0fSEd Maste         if (static_cast<size_t>(m_first_visible_line) >= num_disassembly_lines)
415112b93ac6SEd Maste           m_first_visible_line = 0;
415212b93ac6SEd Maste 
4153435933ddSDimitry Andric         if (pc_idx < num_disassembly_lines) {
41540127ef0fSEd Maste           if (pc_idx < static_cast<uint32_t>(m_first_visible_line) ||
415512b93ac6SEd Maste               pc_idx >= m_first_visible_line + num_visible_lines)
415612b93ac6SEd Maste             m_first_visible_line = pc_idx - non_visible_pc_offset;
415712b93ac6SEd Maste         }
415812b93ac6SEd Maste 
4159435933ddSDimitry Andric         for (size_t i = 0; i < num_visible_lines; ++i) {
416012b93ac6SEd Maste           const uint32_t inst_idx = m_first_visible_line + i;
416112b93ac6SEd Maste           Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get();
416212b93ac6SEd Maste           if (!inst)
416312b93ac6SEd Maste             break;
416412b93ac6SEd Maste 
41650127ef0fSEd Maste           const int line_y = m_min_y + i;
41660127ef0fSEd Maste           window.MoveCursor(1, line_y);
416712b93ac6SEd Maste           const bool is_pc_line = frame_sp && inst_idx == pc_idx;
416812b93ac6SEd Maste           const bool line_is_selected = m_selected_line == inst_idx;
416912b93ac6SEd Maste           // Highlight the line as the PC line first, then if the selected line
417012b93ac6SEd Maste           // isn't the same as the PC line, highlight it differently
417112b93ac6SEd Maste           attr_t highlight_attr = 0;
417212b93ac6SEd Maste           attr_t bp_attr = 0;
417312b93ac6SEd Maste           if (is_pc_line)
417412b93ac6SEd Maste             highlight_attr = pc_highlight_attr;
417512b93ac6SEd Maste           else if (line_is_selected)
417612b93ac6SEd Maste             highlight_attr = selected_highlight_attr;
417712b93ac6SEd Maste 
4178435933ddSDimitry Andric           if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) !=
4179435933ddSDimitry Andric               bp_file_addrs.end())
418012b93ac6SEd Maste             bp_attr = COLOR_PAIR(2);
418112b93ac6SEd Maste 
418212b93ac6SEd Maste           if (bp_attr)
418312b93ac6SEd Maste             window.AttributeOn(bp_attr);
418412b93ac6SEd Maste 
41850127ef0fSEd Maste           window.Printf(" 0x%16.16llx ",
4186435933ddSDimitry Andric                         static_cast<unsigned long long>(
4187435933ddSDimitry Andric                             inst->GetAddress().GetLoadAddress(target)));
418812b93ac6SEd Maste 
418912b93ac6SEd Maste           if (bp_attr)
419012b93ac6SEd Maste             window.AttributeOff(bp_attr);
419112b93ac6SEd Maste 
419212b93ac6SEd Maste           window.PutChar(ACS_VLINE);
419312b93ac6SEd Maste           // Mark the line with the PC with a diamond
419412b93ac6SEd Maste           if (is_pc_line)
419512b93ac6SEd Maste             window.PutChar(ACS_DIAMOND);
419612b93ac6SEd Maste           else
419712b93ac6SEd Maste             window.PutChar(' ');
419812b93ac6SEd Maste 
419912b93ac6SEd Maste           if (highlight_attr)
420012b93ac6SEd Maste             window.AttributeOn(highlight_attr);
420112b93ac6SEd Maste 
420212b93ac6SEd Maste           const char *mnemonic = inst->GetMnemonic(&exe_ctx);
420312b93ac6SEd Maste           const char *operands = inst->GetOperands(&exe_ctx);
420412b93ac6SEd Maste           const char *comment = inst->GetComment(&exe_ctx);
420512b93ac6SEd Maste 
42064bb0738eSEd Maste           if (mnemonic != nullptr && mnemonic[0] == '\0')
42074bb0738eSEd Maste             mnemonic = nullptr;
42084bb0738eSEd Maste           if (operands != nullptr && operands[0] == '\0')
42094bb0738eSEd Maste             operands = nullptr;
42104bb0738eSEd Maste           if (comment != nullptr && comment[0] == '\0')
42114bb0738eSEd Maste             comment = nullptr;
421212b93ac6SEd Maste 
421312b93ac6SEd Maste           strm.Clear();
421412b93ac6SEd Maste 
42154bb0738eSEd Maste           if (mnemonic != nullptr && operands != nullptr && comment != nullptr)
421612b93ac6SEd Maste             strm.Printf("%-8s %-25s ; %s", mnemonic, operands, comment);
42174bb0738eSEd Maste           else if (mnemonic != nullptr && operands != nullptr)
421812b93ac6SEd Maste             strm.Printf("%-8s %s", mnemonic, operands);
42194bb0738eSEd Maste           else if (mnemonic != nullptr)
422012b93ac6SEd Maste             strm.Printf("%s", mnemonic);
422112b93ac6SEd Maste 
422212b93ac6SEd Maste           int right_pad = 1;
4223435933ddSDimitry Andric           window.PutCStringTruncated(strm.GetData(), right_pad);
422412b93ac6SEd Maste 
4225435933ddSDimitry Andric           if (is_pc_line && frame_sp &&
4226435933ddSDimitry Andric               frame_sp->GetConcreteFrameIndex() == 0) {
422712b93ac6SEd Maste             StopInfoSP stop_info_sp;
422812b93ac6SEd Maste             if (thread)
422912b93ac6SEd Maste               stop_info_sp = thread->GetStopInfo();
4230435933ddSDimitry Andric             if (stop_info_sp) {
423112b93ac6SEd Maste               const char *stop_description = stop_info_sp->GetDescription();
4232435933ddSDimitry Andric               if (stop_description && stop_description[0]) {
423312b93ac6SEd Maste                 size_t stop_description_len = strlen(stop_description);
42340127ef0fSEd Maste                 int desc_x = window_width - stop_description_len - 16;
423512b93ac6SEd Maste                 window.Printf("%*s", desc_x - window.GetCursorX(), "");
4236435933ddSDimitry Andric                 // window.MoveCursor(window_width - stop_description_len - 15,
4237435933ddSDimitry Andric                 // line_y);
4238435933ddSDimitry Andric                 window.Printf("<<< Thread %u: %s ", thread->GetIndexID(),
4239435933ddSDimitry Andric                               stop_description);
424012b93ac6SEd Maste               }
4241435933ddSDimitry Andric             } else {
42420127ef0fSEd Maste               window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
424312b93ac6SEd Maste             }
424412b93ac6SEd Maste           }
424512b93ac6SEd Maste           if (highlight_attr)
424612b93ac6SEd Maste             window.AttributeOff(highlight_attr);
424712b93ac6SEd Maste         }
424812b93ac6SEd Maste       }
424912b93ac6SEd Maste     }
425012b93ac6SEd Maste     window.DeferredRefresh();
425112b93ac6SEd Maste     return true; // Drawing handled
425212b93ac6SEd Maste   }
425312b93ac6SEd Maste 
GetNumLines()4254435933ddSDimitry Andric   size_t GetNumLines() {
425512b93ac6SEd Maste     size_t num_lines = GetNumSourceLines();
425612b93ac6SEd Maste     if (num_lines == 0)
425712b93ac6SEd Maste       num_lines = GetNumDisassemblyLines();
425812b93ac6SEd Maste     return num_lines;
425912b93ac6SEd Maste   }
426012b93ac6SEd Maste 
GetNumSourceLines() const4261435933ddSDimitry Andric   size_t GetNumSourceLines() const {
426212b93ac6SEd Maste     if (m_file_sp)
426312b93ac6SEd Maste       return m_file_sp->GetNumLines();
426412b93ac6SEd Maste     return 0;
426512b93ac6SEd Maste   }
42669f2f44ceSEd Maste 
GetNumDisassemblyLines() const4267435933ddSDimitry Andric   size_t GetNumDisassemblyLines() const {
426812b93ac6SEd Maste     if (m_disassembly_sp)
426912b93ac6SEd Maste       return m_disassembly_sp->GetInstructionList().GetSize();
427012b93ac6SEd Maste     return 0;
427112b93ac6SEd Maste   }
427212b93ac6SEd Maste 
WindowDelegateHandleChar(Window & window,int c)4273435933ddSDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
427412b93ac6SEd Maste     const uint32_t num_visible_lines = NumVisibleLines();
427512b93ac6SEd Maste     const size_t num_lines = GetNumLines();
427612b93ac6SEd Maste 
4277435933ddSDimitry Andric     switch (c) {
427812b93ac6SEd Maste     case ',':
427912b93ac6SEd Maste     case KEY_PPAGE:
428012b93ac6SEd Maste       // Page up key
42810127ef0fSEd Maste       if (static_cast<uint32_t>(m_first_visible_line) > num_visible_lines)
428212b93ac6SEd Maste         m_first_visible_line -= num_visible_lines;
428312b93ac6SEd Maste       else
428412b93ac6SEd Maste         m_first_visible_line = 0;
428512b93ac6SEd Maste       m_selected_line = m_first_visible_line;
428612b93ac6SEd Maste       return eKeyHandled;
428712b93ac6SEd Maste 
428812b93ac6SEd Maste     case '.':
428912b93ac6SEd Maste     case KEY_NPAGE:
429012b93ac6SEd Maste       // Page down key
429112b93ac6SEd Maste       {
429212b93ac6SEd Maste         if (m_first_visible_line + num_visible_lines < num_lines)
429312b93ac6SEd Maste           m_first_visible_line += num_visible_lines;
429412b93ac6SEd Maste         else if (num_lines < num_visible_lines)
429512b93ac6SEd Maste           m_first_visible_line = 0;
429612b93ac6SEd Maste         else
429712b93ac6SEd Maste           m_first_visible_line = num_lines - num_visible_lines;
429812b93ac6SEd Maste         m_selected_line = m_first_visible_line;
429912b93ac6SEd Maste       }
430012b93ac6SEd Maste       return eKeyHandled;
430112b93ac6SEd Maste 
430212b93ac6SEd Maste     case KEY_UP:
4303435933ddSDimitry Andric       if (m_selected_line > 0) {
430412b93ac6SEd Maste         m_selected_line--;
43050127ef0fSEd Maste         if (static_cast<size_t>(m_first_visible_line) > m_selected_line)
430612b93ac6SEd Maste           m_first_visible_line = m_selected_line;
430712b93ac6SEd Maste       }
430812b93ac6SEd Maste       return eKeyHandled;
430912b93ac6SEd Maste 
431012b93ac6SEd Maste     case KEY_DOWN:
4311435933ddSDimitry Andric       if (m_selected_line + 1 < num_lines) {
431212b93ac6SEd Maste         m_selected_line++;
431312b93ac6SEd Maste         if (m_first_visible_line + num_visible_lines < m_selected_line)
431412b93ac6SEd Maste           m_first_visible_line++;
431512b93ac6SEd Maste       }
431612b93ac6SEd Maste       return eKeyHandled;
431712b93ac6SEd Maste 
431812b93ac6SEd Maste     case '\r':
431912b93ac6SEd Maste     case '\n':
432012b93ac6SEd Maste     case KEY_ENTER:
432112b93ac6SEd Maste       // Set a breakpoint and run to the line using a one shot breakpoint
4322435933ddSDimitry Andric       if (GetNumSourceLines() > 0) {
4323435933ddSDimitry Andric         ExecutionContext exe_ctx =
4324435933ddSDimitry Andric             m_debugger.GetCommandInterpreter().GetExecutionContext();
4325435933ddSDimitry Andric         if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive()) {
4326435933ddSDimitry Andric           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
4327435933ddSDimitry Andric               nullptr, // Don't limit the breakpoint to certain modules
432812b93ac6SEd Maste               m_file_sp->GetFileSpec(), // Source file
4329435933ddSDimitry Andric               m_selected_line +
4330435933ddSDimitry Andric                   1, // Source line number (m_selected_line is zero based)
4331*b5893f02SDimitry Andric               0,     // Unspecified column.
43324bb0738eSEd Maste               0,     // No offset
433312b93ac6SEd Maste               eLazyBoolCalculate,  // Check inlines using global setting
433412b93ac6SEd Maste               eLazyBoolCalculate,  // Skip prologue using global setting,
433512b93ac6SEd Maste               false,               // internal
43361c3bbb01SEd Maste               false,               // request_hardware
43371c3bbb01SEd Maste               eLazyBoolCalculate); // move_to_nearest_code
433812b93ac6SEd Maste           // Make breakpoint one shot
433912b93ac6SEd Maste           bp_sp->GetOptions()->SetOneShot(true);
434012b93ac6SEd Maste           exe_ctx.GetProcessRef().Resume();
434112b93ac6SEd Maste         }
4342435933ddSDimitry Andric       } else if (m_selected_line < GetNumDisassemblyLines()) {
4343435933ddSDimitry Andric         const Instruction *inst = m_disassembly_sp->GetInstructionList()
4344435933ddSDimitry Andric                                       .GetInstructionAtIndex(m_selected_line)
4345435933ddSDimitry Andric                                       .get();
4346435933ddSDimitry Andric         ExecutionContext exe_ctx =
4347435933ddSDimitry Andric             m_debugger.GetCommandInterpreter().GetExecutionContext();
4348435933ddSDimitry Andric         if (exe_ctx.HasTargetScope()) {
434912b93ac6SEd Maste           Address addr = inst->GetAddress();
4350435933ddSDimitry Andric           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
4351435933ddSDimitry Andric               addr,   // lldb_private::Address
435212b93ac6SEd Maste               false,  // internal
435312b93ac6SEd Maste               false); // request_hardware
435412b93ac6SEd Maste           // Make breakpoint one shot
435512b93ac6SEd Maste           bp_sp->GetOptions()->SetOneShot(true);
435612b93ac6SEd Maste           exe_ctx.GetProcessRef().Resume();
435712b93ac6SEd Maste         }
435812b93ac6SEd Maste       }
435912b93ac6SEd Maste       return eKeyHandled;
436012b93ac6SEd Maste 
436112b93ac6SEd Maste     case 'b': // 'b' == toggle breakpoint on currently selected line
4362435933ddSDimitry Andric       if (m_selected_line < GetNumSourceLines()) {
4363435933ddSDimitry Andric         ExecutionContext exe_ctx =
4364435933ddSDimitry Andric             m_debugger.GetCommandInterpreter().GetExecutionContext();
4365435933ddSDimitry Andric         if (exe_ctx.HasTargetScope()) {
4366435933ddSDimitry Andric           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
4367435933ddSDimitry Andric               nullptr, // Don't limit the breakpoint to certain modules
436812b93ac6SEd Maste               m_file_sp->GetFileSpec(), // Source file
4369435933ddSDimitry Andric               m_selected_line +
4370435933ddSDimitry Andric                   1, // Source line number (m_selected_line is zero based)
4371*b5893f02SDimitry Andric               0,     // No column specified.
43724bb0738eSEd Maste               0,     // No offset
437312b93ac6SEd Maste               eLazyBoolCalculate,  // Check inlines using global setting
437412b93ac6SEd Maste               eLazyBoolCalculate,  // Skip prologue using global setting,
437512b93ac6SEd Maste               false,               // internal
43761c3bbb01SEd Maste               false,               // request_hardware
43771c3bbb01SEd Maste               eLazyBoolCalculate); // move_to_nearest_code
437812b93ac6SEd Maste         }
4379435933ddSDimitry Andric       } else if (m_selected_line < GetNumDisassemblyLines()) {
4380435933ddSDimitry Andric         const Instruction *inst = m_disassembly_sp->GetInstructionList()
4381435933ddSDimitry Andric                                       .GetInstructionAtIndex(m_selected_line)
4382435933ddSDimitry Andric                                       .get();
4383435933ddSDimitry Andric         ExecutionContext exe_ctx =
4384435933ddSDimitry Andric             m_debugger.GetCommandInterpreter().GetExecutionContext();
4385435933ddSDimitry Andric         if (exe_ctx.HasTargetScope()) {
438612b93ac6SEd Maste           Address addr = inst->GetAddress();
4387435933ddSDimitry Andric           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
4388435933ddSDimitry Andric               addr,   // lldb_private::Address
438912b93ac6SEd Maste               false,  // internal
439012b93ac6SEd Maste               false); // request_hardware
439112b93ac6SEd Maste         }
439212b93ac6SEd Maste       }
439312b93ac6SEd Maste       return eKeyHandled;
439412b93ac6SEd Maste 
439512b93ac6SEd Maste     case 'd': // 'd' == detach and let run
439612b93ac6SEd Maste     case 'D': // 'D' == detach and keep stopped
439712b93ac6SEd Maste     {
4398435933ddSDimitry Andric       ExecutionContext exe_ctx =
4399435933ddSDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
440012b93ac6SEd Maste       if (exe_ctx.HasProcessScope())
440112b93ac6SEd Maste         exe_ctx.GetProcessRef().Detach(c == 'D');
440212b93ac6SEd Maste     }
440312b93ac6SEd Maste       return eKeyHandled;
440412b93ac6SEd Maste 
440512b93ac6SEd Maste     case 'k':
440612b93ac6SEd Maste       // 'k' == kill
440712b93ac6SEd Maste       {
4408435933ddSDimitry Andric         ExecutionContext exe_ctx =
4409435933ddSDimitry Andric             m_debugger.GetCommandInterpreter().GetExecutionContext();
441012b93ac6SEd Maste         if (exe_ctx.HasProcessScope())
44111c3bbb01SEd Maste           exe_ctx.GetProcessRef().Destroy(false);
441212b93ac6SEd Maste       }
441312b93ac6SEd Maste       return eKeyHandled;
441412b93ac6SEd Maste 
441512b93ac6SEd Maste     case 'c':
441612b93ac6SEd Maste       // 'c' == continue
441712b93ac6SEd Maste       {
4418435933ddSDimitry Andric         ExecutionContext exe_ctx =
4419435933ddSDimitry Andric             m_debugger.GetCommandInterpreter().GetExecutionContext();
442012b93ac6SEd Maste         if (exe_ctx.HasProcessScope())
442112b93ac6SEd Maste           exe_ctx.GetProcessRef().Resume();
442212b93ac6SEd Maste       }
442312b93ac6SEd Maste       return eKeyHandled;
442412b93ac6SEd Maste 
442512b93ac6SEd Maste     case 'o':
442612b93ac6SEd Maste       // 'o' == step out
442712b93ac6SEd Maste       {
4428435933ddSDimitry Andric         ExecutionContext exe_ctx =
4429435933ddSDimitry Andric             m_debugger.GetCommandInterpreter().GetExecutionContext();
4430435933ddSDimitry Andric         if (exe_ctx.HasThreadScope() &&
4431435933ddSDimitry Andric             StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
443212b93ac6SEd Maste           exe_ctx.GetThreadRef().StepOut();
443312b93ac6SEd Maste         }
443412b93ac6SEd Maste       }
443512b93ac6SEd Maste       return eKeyHandled;
44369f2f44ceSEd Maste 
443712b93ac6SEd Maste     case 'n': // 'n' == step over
443812b93ac6SEd Maste     case 'N': // 'N' == step over instruction
443912b93ac6SEd Maste     {
4440435933ddSDimitry Andric       ExecutionContext exe_ctx =
4441435933ddSDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
4442435933ddSDimitry Andric       if (exe_ctx.HasThreadScope() &&
4443435933ddSDimitry Andric           StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
444412b93ac6SEd Maste         bool source_step = (c == 'n');
444512b93ac6SEd Maste         exe_ctx.GetThreadRef().StepOver(source_step);
444612b93ac6SEd Maste       }
444712b93ac6SEd Maste     }
444812b93ac6SEd Maste       return eKeyHandled;
44499f2f44ceSEd Maste 
445012b93ac6SEd Maste     case 's': // 's' == step into
445112b93ac6SEd Maste     case 'S': // 'S' == step into instruction
445212b93ac6SEd Maste     {
4453435933ddSDimitry Andric       ExecutionContext exe_ctx =
4454435933ddSDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
4455435933ddSDimitry Andric       if (exe_ctx.HasThreadScope() &&
4456435933ddSDimitry Andric           StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
445712b93ac6SEd Maste         bool source_step = (c == 's');
44580127ef0fSEd Maste         exe_ctx.GetThreadRef().StepIn(source_step);
445912b93ac6SEd Maste       }
446012b93ac6SEd Maste     }
446112b93ac6SEd Maste       return eKeyHandled;
446212b93ac6SEd Maste 
446312b93ac6SEd Maste     case 'h':
446412b93ac6SEd Maste       window.CreateHelpSubwindow();
446512b93ac6SEd Maste       return eKeyHandled;
446612b93ac6SEd Maste 
446712b93ac6SEd Maste     default:
446812b93ac6SEd Maste       break;
446912b93ac6SEd Maste     }
447012b93ac6SEd Maste     return eKeyNotHandled;
447112b93ac6SEd Maste   }
447212b93ac6SEd Maste 
447312b93ac6SEd Maste protected:
447412b93ac6SEd Maste   typedef std::set<uint32_t> BreakpointLines;
447512b93ac6SEd Maste   typedef std::set<lldb::addr_t> BreakpointAddrs;
447612b93ac6SEd Maste 
447712b93ac6SEd Maste   Debugger &m_debugger;
447812b93ac6SEd Maste   SymbolContext m_sc;
447912b93ac6SEd Maste   SourceManager::FileSP m_file_sp;
448012b93ac6SEd Maste   SymbolContextScope *m_disassembly_scope;
448112b93ac6SEd Maste   lldb::DisassemblerSP m_disassembly_sp;
448212b93ac6SEd Maste   AddressRange m_disassembly_range;
44830127ef0fSEd Maste   StreamString m_title;
448412b93ac6SEd Maste   lldb::user_id_t m_tid;
448512b93ac6SEd Maste   char m_line_format[8];
448612b93ac6SEd Maste   int m_line_width;
448712b93ac6SEd Maste   uint32_t m_selected_line; // The selected line
448812b93ac6SEd Maste   uint32_t m_pc_line;       // The line with the PC
448912b93ac6SEd Maste   uint32_t m_stop_id;
449012b93ac6SEd Maste   uint32_t m_frame_idx;
449112b93ac6SEd Maste   int m_first_visible_line;
449212b93ac6SEd Maste   int m_min_x;
449312b93ac6SEd Maste   int m_min_y;
449412b93ac6SEd Maste   int m_max_x;
449512b93ac6SEd Maste   int m_max_y;
449612b93ac6SEd Maste };
449712b93ac6SEd Maste 
449812b93ac6SEd Maste DisplayOptions ValueObjectListDelegate::g_options = {true};
449912b93ac6SEd Maste 
IOHandlerCursesGUI(Debugger & debugger)4500435933ddSDimitry Andric IOHandlerCursesGUI::IOHandlerCursesGUI(Debugger &debugger)
4501435933ddSDimitry Andric     : IOHandler(debugger, IOHandler::Type::Curses) {}
450212b93ac6SEd Maste 
Activate()4503435933ddSDimitry Andric void IOHandlerCursesGUI::Activate() {
450412b93ac6SEd Maste   IOHandler::Activate();
4505435933ddSDimitry Andric   if (!m_app_ap) {
450612b93ac6SEd Maste     m_app_ap.reset(new Application(GetInputFILE(), GetOutputFILE()));
450712b93ac6SEd Maste 
450812b93ac6SEd Maste     // This is both a window and a menu delegate
4509435933ddSDimitry Andric     std::shared_ptr<ApplicationDelegate> app_delegate_sp(
4510435933ddSDimitry Andric         new ApplicationDelegate(*m_app_ap, m_debugger));
451112b93ac6SEd Maste 
4512435933ddSDimitry Andric     MenuDelegateSP app_menu_delegate_sp =
4513435933ddSDimitry Andric         std::static_pointer_cast<MenuDelegate>(app_delegate_sp);
4514435933ddSDimitry Andric     MenuSP lldb_menu_sp(
4515435933ddSDimitry Andric         new Menu("LLDB", "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB));
4516435933ddSDimitry Andric     MenuSP exit_menuitem_sp(
4517435933ddSDimitry Andric         new Menu("Exit", nullptr, 'x', ApplicationDelegate::eMenuID_LLDBExit));
451812b93ac6SEd Maste     exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit);
4519435933ddSDimitry Andric     lldb_menu_sp->AddSubmenu(MenuSP(new Menu(
4520435933ddSDimitry Andric         "About LLDB", nullptr, 'a', ApplicationDelegate::eMenuID_LLDBAbout)));
452112b93ac6SEd Maste     lldb_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
452212b93ac6SEd Maste     lldb_menu_sp->AddSubmenu(exit_menuitem_sp);
452312b93ac6SEd Maste 
4524435933ddSDimitry Andric     MenuSP target_menu_sp(new Menu("Target", "F2", KEY_F(2),
4525435933ddSDimitry Andric                                    ApplicationDelegate::eMenuID_Target));
4526435933ddSDimitry Andric     target_menu_sp->AddSubmenu(MenuSP(new Menu(
4527435933ddSDimitry Andric         "Create", nullptr, 'c', ApplicationDelegate::eMenuID_TargetCreate)));
4528435933ddSDimitry Andric     target_menu_sp->AddSubmenu(MenuSP(new Menu(
4529435933ddSDimitry Andric         "Delete", nullptr, 'd', ApplicationDelegate::eMenuID_TargetDelete)));
453012b93ac6SEd Maste 
4531435933ddSDimitry Andric     MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3),
4532435933ddSDimitry Andric                                     ApplicationDelegate::eMenuID_Process));
4533435933ddSDimitry Andric     process_menu_sp->AddSubmenu(MenuSP(new Menu(
4534435933ddSDimitry Andric         "Attach", nullptr, 'a', ApplicationDelegate::eMenuID_ProcessAttach)));
4535435933ddSDimitry Andric     process_menu_sp->AddSubmenu(MenuSP(new Menu(
4536435933ddSDimitry Andric         "Detach", nullptr, 'd', ApplicationDelegate::eMenuID_ProcessDetach)));
4537435933ddSDimitry Andric     process_menu_sp->AddSubmenu(MenuSP(new Menu(
4538435933ddSDimitry Andric         "Launch", nullptr, 'l', ApplicationDelegate::eMenuID_ProcessLaunch)));
453912b93ac6SEd Maste     process_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
4540435933ddSDimitry Andric     process_menu_sp->AddSubmenu(
4541435933ddSDimitry Andric         MenuSP(new Menu("Continue", nullptr, 'c',
4542435933ddSDimitry Andric                         ApplicationDelegate::eMenuID_ProcessContinue)));
4543435933ddSDimitry Andric     process_menu_sp->AddSubmenu(MenuSP(new Menu(
4544435933ddSDimitry Andric         "Halt", nullptr, 'h', ApplicationDelegate::eMenuID_ProcessHalt)));
4545435933ddSDimitry Andric     process_menu_sp->AddSubmenu(MenuSP(new Menu(
4546435933ddSDimitry Andric         "Kill", nullptr, 'k', ApplicationDelegate::eMenuID_ProcessKill)));
454712b93ac6SEd Maste 
4548435933ddSDimitry Andric     MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4),
4549435933ddSDimitry Andric                                    ApplicationDelegate::eMenuID_Thread));
4550435933ddSDimitry Andric     thread_menu_sp->AddSubmenu(MenuSP(new Menu(
4551435933ddSDimitry Andric         "Step In", nullptr, 'i', ApplicationDelegate::eMenuID_ThreadStepIn)));
4552435933ddSDimitry Andric     thread_menu_sp->AddSubmenu(
4553435933ddSDimitry Andric         MenuSP(new Menu("Step Over", nullptr, 'v',
4554435933ddSDimitry Andric                         ApplicationDelegate::eMenuID_ThreadStepOver)));
4555435933ddSDimitry Andric     thread_menu_sp->AddSubmenu(MenuSP(new Menu(
4556435933ddSDimitry Andric         "Step Out", nullptr, 'o', ApplicationDelegate::eMenuID_ThreadStepOut)));
455712b93ac6SEd Maste 
4558435933ddSDimitry Andric     MenuSP view_menu_sp(
4559435933ddSDimitry Andric         new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View));
4560435933ddSDimitry Andric     view_menu_sp->AddSubmenu(
4561435933ddSDimitry Andric         MenuSP(new Menu("Backtrace", nullptr, 'b',
4562435933ddSDimitry Andric                         ApplicationDelegate::eMenuID_ViewBacktrace)));
4563435933ddSDimitry Andric     view_menu_sp->AddSubmenu(
4564435933ddSDimitry Andric         MenuSP(new Menu("Registers", nullptr, 'r',
4565435933ddSDimitry Andric                         ApplicationDelegate::eMenuID_ViewRegisters)));
4566435933ddSDimitry Andric     view_menu_sp->AddSubmenu(MenuSP(new Menu(
4567435933ddSDimitry Andric         "Source", nullptr, 's', ApplicationDelegate::eMenuID_ViewSource)));
4568435933ddSDimitry Andric     view_menu_sp->AddSubmenu(
4569435933ddSDimitry Andric         MenuSP(new Menu("Variables", nullptr, 'v',
4570435933ddSDimitry Andric                         ApplicationDelegate::eMenuID_ViewVariables)));
457112b93ac6SEd Maste 
4572435933ddSDimitry Andric     MenuSP help_menu_sp(
4573435933ddSDimitry Andric         new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help));
4574435933ddSDimitry Andric     help_menu_sp->AddSubmenu(MenuSP(new Menu(
4575435933ddSDimitry Andric         "GUI Help", nullptr, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp)));
457612b93ac6SEd Maste 
457712b93ac6SEd Maste     m_app_ap->Initialize();
457812b93ac6SEd Maste     WindowSP &main_window_sp = m_app_ap->GetMainWindow();
457912b93ac6SEd Maste 
458012b93ac6SEd Maste     MenuSP menubar_sp(new Menu(Menu::Type::Bar));
458112b93ac6SEd Maste     menubar_sp->AddSubmenu(lldb_menu_sp);
458212b93ac6SEd Maste     menubar_sp->AddSubmenu(target_menu_sp);
458312b93ac6SEd Maste     menubar_sp->AddSubmenu(process_menu_sp);
458412b93ac6SEd Maste     menubar_sp->AddSubmenu(thread_menu_sp);
458512b93ac6SEd Maste     menubar_sp->AddSubmenu(view_menu_sp);
458612b93ac6SEd Maste     menubar_sp->AddSubmenu(help_menu_sp);
458712b93ac6SEd Maste     menubar_sp->SetDelegate(app_menu_delegate_sp);
458812b93ac6SEd Maste 
458912b93ac6SEd Maste     Rect content_bounds = main_window_sp->GetFrame();
459012b93ac6SEd Maste     Rect menubar_bounds = content_bounds.MakeMenuBar();
459112b93ac6SEd Maste     Rect status_bounds = content_bounds.MakeStatusBar();
459212b93ac6SEd Maste     Rect source_bounds;
459312b93ac6SEd Maste     Rect variables_bounds;
459412b93ac6SEd Maste     Rect threads_bounds;
459512b93ac6SEd Maste     Rect source_variables_bounds;
4596435933ddSDimitry Andric     content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds,
4597435933ddSDimitry Andric                                            threads_bounds);
4598435933ddSDimitry Andric     source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds,
4599435933ddSDimitry Andric                                                       variables_bounds);
460012b93ac6SEd Maste 
4601435933ddSDimitry Andric     WindowSP menubar_window_sp =
4602435933ddSDimitry Andric         main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false);
46034ba319b5SDimitry Andric     // Let the menubar get keys if the active window doesn't handle the keys
46044ba319b5SDimitry Andric     // that are typed so it can respond to menubar key presses.
4605435933ddSDimitry Andric     menubar_window_sp->SetCanBeActive(
4606435933ddSDimitry Andric         false); // Don't let the menubar become the active window
460712b93ac6SEd Maste     menubar_window_sp->SetDelegate(menubar_sp);
460812b93ac6SEd Maste 
4609435933ddSDimitry Andric     WindowSP source_window_sp(
4610435933ddSDimitry Andric         main_window_sp->CreateSubWindow("Source", source_bounds, true));
4611435933ddSDimitry Andric     WindowSP variables_window_sp(
4612435933ddSDimitry Andric         main_window_sp->CreateSubWindow("Variables", variables_bounds, false));
4613435933ddSDimitry Andric     WindowSP threads_window_sp(
4614435933ddSDimitry Andric         main_window_sp->CreateSubWindow("Threads", threads_bounds, false));
4615435933ddSDimitry Andric     WindowSP status_window_sp(
4616d8866befSDimitry Andric         main_window_sp->CreateSubWindow("Status", status_bounds, false));
4617435933ddSDimitry Andric     status_window_sp->SetCanBeActive(
4618435933ddSDimitry Andric         false); // Don't let the status bar become the active window
4619435933ddSDimitry Andric     main_window_sp->SetDelegate(
4620435933ddSDimitry Andric         std::static_pointer_cast<WindowDelegate>(app_delegate_sp));
4621435933ddSDimitry Andric     source_window_sp->SetDelegate(
4622435933ddSDimitry Andric         WindowDelegateSP(new SourceFileWindowDelegate(m_debugger)));
4623435933ddSDimitry Andric     variables_window_sp->SetDelegate(
4624435933ddSDimitry Andric         WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
46250127ef0fSEd Maste     TreeDelegateSP thread_delegate_sp(new ThreadsTreeDelegate(m_debugger));
4626435933ddSDimitry Andric     threads_window_sp->SetDelegate(WindowDelegateSP(
4627435933ddSDimitry Andric         new TreeWindowDelegate(m_debugger, thread_delegate_sp)));
4628435933ddSDimitry Andric     status_window_sp->SetDelegate(
4629435933ddSDimitry Andric         WindowDelegateSP(new StatusBarWindowDelegate(m_debugger)));
463012b93ac6SEd Maste 
463112b93ac6SEd Maste     // Show the main help window once the first time the curses GUI is launched
463212b93ac6SEd Maste     static bool g_showed_help = false;
4633435933ddSDimitry Andric     if (!g_showed_help) {
463412b93ac6SEd Maste       g_showed_help = true;
463512b93ac6SEd Maste       main_window_sp->CreateHelpSubwindow();
463612b93ac6SEd Maste     }
463712b93ac6SEd Maste 
463812b93ac6SEd Maste     init_pair(1, COLOR_WHITE, COLOR_BLUE);
463912b93ac6SEd Maste     init_pair(2, COLOR_BLACK, COLOR_WHITE);
464012b93ac6SEd Maste     init_pair(3, COLOR_MAGENTA, COLOR_WHITE);
464112b93ac6SEd Maste     init_pair(4, COLOR_MAGENTA, COLOR_BLACK);
464212b93ac6SEd Maste     init_pair(5, COLOR_RED, COLOR_BLACK);
464312b93ac6SEd Maste   }
464412b93ac6SEd Maste }
464512b93ac6SEd Maste 
Deactivate()4646435933ddSDimitry Andric void IOHandlerCursesGUI::Deactivate() { m_app_ap->Terminate(); }
464712b93ac6SEd Maste 
Run()4648435933ddSDimitry Andric void IOHandlerCursesGUI::Run() {
464912b93ac6SEd Maste   m_app_ap->Run(m_debugger);
465012b93ac6SEd Maste   SetIsDone(true);
465112b93ac6SEd Maste }
465212b93ac6SEd Maste 
46539f2f44ceSEd Maste IOHandlerCursesGUI::~IOHandlerCursesGUI() = default;
465412b93ac6SEd Maste 
Cancel()4655435933ddSDimitry Andric void IOHandlerCursesGUI::Cancel() {}
465612b93ac6SEd Maste 
Interrupt()4657435933ddSDimitry Andric bool IOHandlerCursesGUI::Interrupt() { return false; }
465812b93ac6SEd Maste 
GotEOF()4659435933ddSDimitry Andric void IOHandlerCursesGUI::GotEOF() {}
466012b93ac6SEd Maste 
46619f2f44ceSEd Maste #endif // LLDB_DISABLE_CURSES
4662