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