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