]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/lldb/source/Core/IOHandler.cpp
Merge llvm, clang, compiler-rt, libc++, libunwind, lld, lldb and openmp
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / lldb / source / Core / IOHandler.cpp
1 //===-- IOHandler.cpp -------------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "lldb/Core/IOHandler.h"
11
12 #ifndef LLDB_DISABLE_CURSES
13 #include <curses.h>
14 #include <panel.h>
15 #endif
16
17 #if defined(__APPLE__)
18 #include <deque>
19 #endif
20 #include <string>
21
22 #include "lldb/Core/Debugger.h"
23 #include "lldb/Core/StreamFile.h"
24 #include "lldb/Host/File.h"
25 #include "lldb/Utility/Predicate.h"
26 #include "lldb/Utility/Status.h"
27 #include "lldb/Utility/StreamString.h"
28 #include "lldb/Utility/StringList.h"
29 #include "lldb/lldb-forward.h"
30
31 #ifndef LLDB_DISABLE_LIBEDIT
32 #include "lldb/Host/Editline.h"
33 #endif
34 #include "lldb/Interpreter/CommandCompletions.h"
35 #include "lldb/Interpreter/CommandInterpreter.h"
36 #ifndef LLDB_DISABLE_CURSES
37 #include "lldb/Breakpoint/BreakpointLocation.h"
38 #include "lldb/Core/Module.h"
39 #include "lldb/Core/ValueObject.h"
40 #include "lldb/Core/ValueObjectRegister.h"
41 #include "lldb/Symbol/Block.h"
42 #include "lldb/Symbol/Function.h"
43 #include "lldb/Symbol/Symbol.h"
44 #include "lldb/Symbol/VariableList.h"
45 #include "lldb/Target/Process.h"
46 #include "lldb/Target/RegisterContext.h"
47 #include "lldb/Target/StackFrame.h"
48 #include "lldb/Target/StopInfo.h"
49 #include "lldb/Target/Target.h"
50 #include "lldb/Target/Thread.h"
51 #include "lldb/Utility/State.h"
52 #endif
53
54 #include "llvm/ADT/StringRef.h"
55
56 #ifdef _MSC_VER
57 #include "lldb/Host/windows/windows.h"
58 #endif
59
60 #include <memory>
61 #include <mutex>
62
63 #include <assert.h>
64 #include <ctype.h>
65 #include <errno.h>
66 #include <locale.h>
67 #include <stdint.h>
68 #include <stdio.h>
69 #include <string.h>
70 #include <type_traits>
71
72 using namespace lldb;
73 using namespace lldb_private;
74
75 IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type)
76     : IOHandler(debugger, type,
77                 StreamFileSP(), // Adopt STDIN from top input reader
78                 StreamFileSP(), // Adopt STDOUT from top input reader
79                 StreamFileSP(), // Adopt STDERR from top input reader
80                 0)              // Flags
81 {}
82
83 IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type,
84                      const lldb::StreamFileSP &input_sp,
85                      const lldb::StreamFileSP &output_sp,
86                      const lldb::StreamFileSP &error_sp, uint32_t flags)
87     : m_debugger(debugger), m_input_sp(input_sp), m_output_sp(output_sp),
88       m_error_sp(error_sp), m_popped(false), m_flags(flags), m_type(type),
89       m_user_data(nullptr), m_done(false), m_active(false) {
90   // If any files are not specified, then adopt them from the top input reader.
91   if (!m_input_sp || !m_output_sp || !m_error_sp)
92     debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_sp, m_output_sp,
93                                              m_error_sp);
94 }
95
96 IOHandler::~IOHandler() = default;
97
98 int IOHandler::GetInputFD() {
99   return (m_input_sp ? m_input_sp->GetFile().GetDescriptor() : -1);
100 }
101
102 int IOHandler::GetOutputFD() {
103   return (m_output_sp ? m_output_sp->GetFile().GetDescriptor() : -1);
104 }
105
106 int IOHandler::GetErrorFD() {
107   return (m_error_sp ? m_error_sp->GetFile().GetDescriptor() : -1);
108 }
109
110 FILE *IOHandler::GetInputFILE() {
111   return (m_input_sp ? m_input_sp->GetFile().GetStream() : nullptr);
112 }
113
114 FILE *IOHandler::GetOutputFILE() {
115   return (m_output_sp ? m_output_sp->GetFile().GetStream() : nullptr);
116 }
117
118 FILE *IOHandler::GetErrorFILE() {
119   return (m_error_sp ? m_error_sp->GetFile().GetStream() : nullptr);
120 }
121
122 StreamFileSP &IOHandler::GetInputStreamFile() { return m_input_sp; }
123
124 StreamFileSP &IOHandler::GetOutputStreamFile() { return m_output_sp; }
125
126 StreamFileSP &IOHandler::GetErrorStreamFile() { return m_error_sp; }
127
128 bool IOHandler::GetIsInteractive() {
129   return GetInputStreamFile()->GetFile().GetIsInteractive();
130 }
131
132 bool IOHandler::GetIsRealTerminal() {
133   return GetInputStreamFile()->GetFile().GetIsRealTerminal();
134 }
135
136 void IOHandler::SetPopped(bool b) { m_popped.SetValue(b, eBroadcastOnChange); }
137
138 void IOHandler::WaitForPop() { m_popped.WaitForValueEqualTo(true); }
139
140 void IOHandlerStack::PrintAsync(Stream *stream, const char *s, size_t len) {
141   if (stream) {
142     std::lock_guard<std::recursive_mutex> guard(m_mutex);
143     if (m_top)
144       m_top->PrintAsync(stream, s, len);
145   }
146 }
147
148 IOHandlerConfirm::IOHandlerConfirm(Debugger &debugger, llvm::StringRef prompt,
149                                    bool default_response)
150     : IOHandlerEditline(
151           debugger, IOHandler::Type::Confirm,
152           nullptr, // nullptr editline_name means no history loaded/saved
153           llvm::StringRef(), // No prompt
154           llvm::StringRef(), // No continuation prompt
155           false,             // Multi-line
156           false, // Don't colorize the prompt (i.e. the confirm message.)
157           0, *this),
158       m_default_response(default_response), m_user_response(default_response) {
159   StreamString prompt_stream;
160   prompt_stream.PutCString(prompt);
161   if (m_default_response)
162     prompt_stream.Printf(": [Y/n] ");
163   else
164     prompt_stream.Printf(": [y/N] ");
165
166   SetPrompt(prompt_stream.GetString());
167 }
168
169 IOHandlerConfirm::~IOHandlerConfirm() = default;
170
171 int IOHandlerConfirm::IOHandlerComplete(
172     IOHandler &io_handler, const char *current_line, const char *cursor,
173     const char *last_char, int skip_first_n_matches, int max_matches,
174     StringList &matches, StringList &descriptions) {
175   if (current_line == cursor) {
176     if (m_default_response) {
177       matches.AppendString("y");
178     } else {
179       matches.AppendString("n");
180     }
181   }
182   return matches.GetSize();
183 }
184
185 void IOHandlerConfirm::IOHandlerInputComplete(IOHandler &io_handler,
186                                               std::string &line) {
187   if (line.empty()) {
188     // User just hit enter, set the response to the default
189     m_user_response = m_default_response;
190     io_handler.SetIsDone(true);
191     return;
192   }
193
194   if (line.size() == 1) {
195     switch (line[0]) {
196     case 'y':
197     case 'Y':
198       m_user_response = true;
199       io_handler.SetIsDone(true);
200       return;
201     case 'n':
202     case 'N':
203       m_user_response = false;
204       io_handler.SetIsDone(true);
205       return;
206     default:
207       break;
208     }
209   }
210
211   if (line == "yes" || line == "YES" || line == "Yes") {
212     m_user_response = true;
213     io_handler.SetIsDone(true);
214   } else if (line == "no" || line == "NO" || line == "No") {
215     m_user_response = false;
216     io_handler.SetIsDone(true);
217   }
218 }
219
220 int IOHandlerDelegate::IOHandlerComplete(
221     IOHandler &io_handler, const char *current_line, const char *cursor,
222     const char *last_char, int skip_first_n_matches, int max_matches,
223     StringList &matches, StringList &descriptions) {
224   switch (m_completion) {
225   case Completion::None:
226     break;
227
228   case Completion::LLDBCommand:
229     return io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion(
230         current_line, cursor, last_char, skip_first_n_matches, max_matches,
231         matches, descriptions);
232   case Completion::Expression: {
233     CompletionResult result;
234     CompletionRequest request(current_line, current_line - cursor,
235                               skip_first_n_matches, max_matches, result);
236     CommandCompletions::InvokeCommonCompletionCallbacks(
237         io_handler.GetDebugger().GetCommandInterpreter(),
238         CommandCompletions::eVariablePathCompletion, request, nullptr);
239     result.GetMatches(matches);
240     result.GetDescriptions(descriptions);
241
242     size_t num_matches = request.GetNumberOfMatches();
243     if (num_matches > 0) {
244       std::string common_prefix;
245       matches.LongestCommonPrefix(common_prefix);
246       const size_t partial_name_len = request.GetCursorArgumentPrefix().size();
247
248       // If we matched a unique single command, add a space... Only do this if
249       // the completer told us this was a complete word, however...
250       if (num_matches == 1 && request.GetWordComplete()) {
251         common_prefix.push_back(' ');
252       }
253       common_prefix.erase(0, partial_name_len);
254       matches.InsertStringAtIndex(0, std::move(common_prefix));
255     }
256     return num_matches;
257   } break;
258   }
259
260   return 0;
261 }
262
263 IOHandlerEditline::IOHandlerEditline(
264     Debugger &debugger, IOHandler::Type type,
265     const char *editline_name, // Used for saving history files
266     llvm::StringRef prompt, llvm::StringRef continuation_prompt,
267     bool multi_line, bool color_prompts, uint32_t line_number_start,
268     IOHandlerDelegate &delegate)
269     : IOHandlerEditline(debugger, type,
270                         StreamFileSP(), // Inherit input from top input reader
271                         StreamFileSP(), // Inherit output from top input reader
272                         StreamFileSP(), // Inherit error from top input reader
273                         0,              // Flags
274                         editline_name,  // Used for saving history files
275                         prompt, continuation_prompt, multi_line, color_prompts,
276                         line_number_start, delegate) {}
277
278 IOHandlerEditline::IOHandlerEditline(
279     Debugger &debugger, IOHandler::Type type,
280     const lldb::StreamFileSP &input_sp, const lldb::StreamFileSP &output_sp,
281     const lldb::StreamFileSP &error_sp, uint32_t flags,
282     const char *editline_name, // Used for saving history files
283     llvm::StringRef prompt, llvm::StringRef continuation_prompt,
284     bool multi_line, bool color_prompts, uint32_t line_number_start,
285     IOHandlerDelegate &delegate)
286     : IOHandler(debugger, type, input_sp, output_sp, error_sp, flags),
287 #ifndef LLDB_DISABLE_LIBEDIT
288       m_editline_ap(),
289 #endif
290       m_delegate(delegate), m_prompt(), m_continuation_prompt(),
291       m_current_lines_ptr(nullptr), m_base_line_number(line_number_start),
292       m_curr_line_idx(UINT32_MAX), m_multi_line(multi_line),
293       m_color_prompts(color_prompts), m_interrupt_exits(true),
294       m_editing(false) {
295   SetPrompt(prompt);
296
297 #ifndef LLDB_DISABLE_LIBEDIT
298   bool use_editline = false;
299
300   use_editline = m_input_sp->GetFile().GetIsRealTerminal();
301
302   if (use_editline) {
303     m_editline_ap.reset(new Editline(editline_name, GetInputFILE(),
304                                      GetOutputFILE(), GetErrorFILE(),
305                                      m_color_prompts));
306     m_editline_ap->SetIsInputCompleteCallback(IsInputCompleteCallback, this);
307     m_editline_ap->SetAutoCompleteCallback(AutoCompleteCallback, this);
308     // See if the delegate supports fixing indentation
309     const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters();
310     if (indent_chars) {
311       // The delegate does support indentation, hook it up so when any
312       // indentation character is typed, the delegate gets a chance to fix it
313       m_editline_ap->SetFixIndentationCallback(FixIndentationCallback, this,
314                                                indent_chars);
315     }
316   }
317 #endif
318   SetBaseLineNumber(m_base_line_number);
319   SetPrompt(prompt);
320   SetContinuationPrompt(continuation_prompt);
321 }
322
323 IOHandlerEditline::~IOHandlerEditline() {
324 #ifndef LLDB_DISABLE_LIBEDIT
325   m_editline_ap.reset();
326 #endif
327 }
328
329 void IOHandlerEditline::Activate() {
330   IOHandler::Activate();
331   m_delegate.IOHandlerActivated(*this);
332 }
333
334 void IOHandlerEditline::Deactivate() {
335   IOHandler::Deactivate();
336   m_delegate.IOHandlerDeactivated(*this);
337 }
338
339 bool IOHandlerEditline::GetLine(std::string &line, bool &interrupted) {
340 #ifndef LLDB_DISABLE_LIBEDIT
341   if (m_editline_ap) {
342     return m_editline_ap->GetLine(line, interrupted);
343   } else {
344 #endif
345     line.clear();
346
347     FILE *in = GetInputFILE();
348     if (in) {
349       if (GetIsInteractive()) {
350         const char *prompt = nullptr;
351
352         if (m_multi_line && m_curr_line_idx > 0)
353           prompt = GetContinuationPrompt();
354
355         if (prompt == nullptr)
356           prompt = GetPrompt();
357
358         if (prompt && prompt[0]) {
359           FILE *out = GetOutputFILE();
360           if (out) {
361             ::fprintf(out, "%s", prompt);
362             ::fflush(out);
363           }
364         }
365       }
366       char buffer[256];
367       bool done = false;
368       bool got_line = false;
369       m_editing = true;
370       while (!done) {
371         if (fgets(buffer, sizeof(buffer), in) == nullptr) {
372           const int saved_errno = errno;
373           if (feof(in))
374             done = true;
375           else if (ferror(in)) {
376             if (saved_errno != EINTR)
377               done = true;
378           }
379         } else {
380           got_line = true;
381           size_t buffer_len = strlen(buffer);
382           assert(buffer[buffer_len] == '\0');
383           char last_char = buffer[buffer_len - 1];
384           if (last_char == '\r' || last_char == '\n') {
385             done = true;
386             // Strip trailing newlines
387             while (last_char == '\r' || last_char == '\n') {
388               --buffer_len;
389               if (buffer_len == 0)
390                 break;
391               last_char = buffer[buffer_len - 1];
392             }
393           }
394           line.append(buffer, buffer_len);
395         }
396       }
397       m_editing = false;
398       // We might have gotten a newline on a line by itself make sure to return
399       // true in this case.
400       return got_line;
401     } else {
402       // No more input file, we are done...
403       SetIsDone(true);
404     }
405     return false;
406 #ifndef LLDB_DISABLE_LIBEDIT
407   }
408 #endif
409 }
410
411 #ifndef LLDB_DISABLE_LIBEDIT
412 bool IOHandlerEditline::IsInputCompleteCallback(Editline *editline,
413                                                 StringList &lines,
414                                                 void *baton) {
415   IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton;
416   return editline_reader->m_delegate.IOHandlerIsInputComplete(*editline_reader,
417                                                               lines);
418 }
419
420 int IOHandlerEditline::FixIndentationCallback(Editline *editline,
421                                               const StringList &lines,
422                                               int cursor_position,
423                                               void *baton) {
424   IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton;
425   return editline_reader->m_delegate.IOHandlerFixIndentation(
426       *editline_reader, lines, cursor_position);
427 }
428
429 int IOHandlerEditline::AutoCompleteCallback(
430     const char *current_line, const char *cursor, const char *last_char,
431     int skip_first_n_matches, int max_matches, StringList &matches,
432     StringList &descriptions, void *baton) {
433   IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton;
434   if (editline_reader)
435     return editline_reader->m_delegate.IOHandlerComplete(
436         *editline_reader, current_line, cursor, last_char, skip_first_n_matches,
437         max_matches, matches, descriptions);
438   return 0;
439 }
440 #endif
441
442 const char *IOHandlerEditline::GetPrompt() {
443 #ifndef LLDB_DISABLE_LIBEDIT
444   if (m_editline_ap) {
445     return m_editline_ap->GetPrompt();
446   } else {
447 #endif
448     if (m_prompt.empty())
449       return nullptr;
450 #ifndef LLDB_DISABLE_LIBEDIT
451   }
452 #endif
453   return m_prompt.c_str();
454 }
455
456 bool IOHandlerEditline::SetPrompt(llvm::StringRef prompt) {
457   m_prompt = prompt;
458
459 #ifndef LLDB_DISABLE_LIBEDIT
460   if (m_editline_ap)
461     m_editline_ap->SetPrompt(m_prompt.empty() ? nullptr : m_prompt.c_str());
462 #endif
463   return true;
464 }
465
466 const char *IOHandlerEditline::GetContinuationPrompt() {
467   return (m_continuation_prompt.empty() ? nullptr
468                                         : m_continuation_prompt.c_str());
469 }
470
471 void IOHandlerEditline::SetContinuationPrompt(llvm::StringRef prompt) {
472   m_continuation_prompt = prompt;
473
474 #ifndef LLDB_DISABLE_LIBEDIT
475   if (m_editline_ap)
476     m_editline_ap->SetContinuationPrompt(m_continuation_prompt.empty()
477                                              ? nullptr
478                                              : m_continuation_prompt.c_str());
479 #endif
480 }
481
482 void IOHandlerEditline::SetBaseLineNumber(uint32_t line) {
483   m_base_line_number = line;
484 }
485
486 uint32_t IOHandlerEditline::GetCurrentLineIndex() const {
487 #ifndef LLDB_DISABLE_LIBEDIT
488   if (m_editline_ap)
489     return m_editline_ap->GetCurrentLine();
490 #endif
491   return m_curr_line_idx;
492 }
493
494 bool IOHandlerEditline::GetLines(StringList &lines, bool &interrupted) {
495   m_current_lines_ptr = &lines;
496
497   bool success = false;
498 #ifndef LLDB_DISABLE_LIBEDIT
499   if (m_editline_ap) {
500     return m_editline_ap->GetLines(m_base_line_number, lines, interrupted);
501   } else {
502 #endif
503     bool done = false;
504     Status error;
505
506     while (!done) {
507       // Show line numbers if we are asked to
508       std::string line;
509       if (m_base_line_number > 0 && GetIsInteractive()) {
510         FILE *out = GetOutputFILE();
511         if (out)
512           ::fprintf(out, "%u%s", m_base_line_number + (uint32_t)lines.GetSize(),
513                     GetPrompt() == nullptr ? " " : "");
514       }
515
516       m_curr_line_idx = lines.GetSize();
517
518       bool interrupted = false;
519       if (GetLine(line, interrupted) && !interrupted) {
520         lines.AppendString(line);
521         done = m_delegate.IOHandlerIsInputComplete(*this, lines);
522       } else {
523         done = true;
524       }
525     }
526     success = lines.GetSize() > 0;
527 #ifndef LLDB_DISABLE_LIBEDIT
528   }
529 #endif
530   return success;
531 }
532
533 // Each IOHandler gets to run until it is done. It should read data from the
534 // "in" and place output into "out" and "err and return when done.
535 void IOHandlerEditline::Run() {
536   std::string line;
537   while (IsActive()) {
538     bool interrupted = false;
539     if (m_multi_line) {
540       StringList lines;
541       if (GetLines(lines, interrupted)) {
542         if (interrupted) {
543           m_done = m_interrupt_exits;
544           m_delegate.IOHandlerInputInterrupted(*this, line);
545
546         } else {
547           line = lines.CopyList();
548           m_delegate.IOHandlerInputComplete(*this, line);
549         }
550       } else {
551         m_done = true;
552       }
553     } else {
554       if (GetLine(line, interrupted)) {
555         if (interrupted)
556           m_delegate.IOHandlerInputInterrupted(*this, line);
557         else
558           m_delegate.IOHandlerInputComplete(*this, line);
559       } else {
560         m_done = true;
561       }
562     }
563   }
564 }
565
566 void IOHandlerEditline::Cancel() {
567 #ifndef LLDB_DISABLE_LIBEDIT
568   if (m_editline_ap)
569     m_editline_ap->Cancel();
570 #endif
571 }
572
573 bool IOHandlerEditline::Interrupt() {
574   // Let the delgate handle it first
575   if (m_delegate.IOHandlerInterrupt(*this))
576     return true;
577
578 #ifndef LLDB_DISABLE_LIBEDIT
579   if (m_editline_ap)
580     return m_editline_ap->Interrupt();
581 #endif
582   return false;
583 }
584
585 void IOHandlerEditline::GotEOF() {
586 #ifndef LLDB_DISABLE_LIBEDIT
587   if (m_editline_ap)
588     m_editline_ap->Interrupt();
589 #endif
590 }
591
592 void IOHandlerEditline::PrintAsync(Stream *stream, const char *s, size_t len) {
593 #ifndef LLDB_DISABLE_LIBEDIT
594   if (m_editline_ap)
595     m_editline_ap->PrintAsync(stream, s, len);
596   else
597 #endif
598   {
599 #ifdef _MSC_VER
600     const char *prompt = GetPrompt();
601     if (prompt) {
602       // Back up over previous prompt using Windows API
603       CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info;
604       HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
605       GetConsoleScreenBufferInfo(console_handle, &screen_buffer_info);
606       COORD coord = screen_buffer_info.dwCursorPosition;
607       coord.X -= strlen(prompt);
608       if (coord.X < 0)
609         coord.X = 0;
610       SetConsoleCursorPosition(console_handle, coord);
611     }
612 #endif
613     IOHandler::PrintAsync(stream, s, len);
614 #ifdef _MSC_VER
615     if (prompt)
616       IOHandler::PrintAsync(GetOutputStreamFile().get(), prompt,
617                             strlen(prompt));
618 #endif
619   }
620 }
621
622 // we may want curses to be disabled for some builds for instance, windows
623 #ifndef LLDB_DISABLE_CURSES
624
625 #define KEY_RETURN 10
626 #define KEY_ESCAPE 27
627
628 namespace curses {
629 class Menu;
630 class MenuDelegate;
631 class Window;
632 class WindowDelegate;
633 typedef std::shared_ptr<Menu> MenuSP;
634 typedef std::shared_ptr<MenuDelegate> MenuDelegateSP;
635 typedef std::shared_ptr<Window> WindowSP;
636 typedef std::shared_ptr<WindowDelegate> WindowDelegateSP;
637 typedef std::vector<MenuSP> Menus;
638 typedef std::vector<WindowSP> Windows;
639 typedef std::vector<WindowDelegateSP> WindowDelegates;
640
641 #if 0
642 type summary add -s "x=${var.x}, y=${var.y}" curses::Point
643 type summary add -s "w=${var.width}, h=${var.height}" curses::Size
644 type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect
645 #endif
646
647 struct Point {
648   int x;
649   int y;
650
651   Point(int _x = 0, int _y = 0) : x(_x), y(_y) {}
652
653   void Clear() {
654     x = 0;
655     y = 0;
656   }
657
658   Point &operator+=(const Point &rhs) {
659     x += rhs.x;
660     y += rhs.y;
661     return *this;
662   }
663
664   void Dump() { printf("(x=%i, y=%i)\n", x, y); }
665 };
666
667 bool operator==(const Point &lhs, const Point &rhs) {
668   return lhs.x == rhs.x && lhs.y == rhs.y;
669 }
670
671 bool operator!=(const Point &lhs, const Point &rhs) {
672   return lhs.x != rhs.x || lhs.y != rhs.y;
673 }
674
675 struct Size {
676   int width;
677   int height;
678   Size(int w = 0, int h = 0) : width(w), height(h) {}
679
680   void Clear() {
681     width = 0;
682     height = 0;
683   }
684
685   void Dump() { printf("(w=%i, h=%i)\n", width, height); }
686 };
687
688 bool operator==(const Size &lhs, const Size &rhs) {
689   return lhs.width == rhs.width && lhs.height == rhs.height;
690 }
691
692 bool operator!=(const Size &lhs, const Size &rhs) {
693   return lhs.width != rhs.width || lhs.height != rhs.height;
694 }
695
696 struct Rect {
697   Point origin;
698   Size size;
699
700   Rect() : origin(), size() {}
701
702   Rect(const Point &p, const Size &s) : origin(p), size(s) {}
703
704   void Clear() {
705     origin.Clear();
706     size.Clear();
707   }
708
709   void Dump() {
710     printf("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width,
711            size.height);
712   }
713
714   void Inset(int w, int h) {
715     if (size.width > w * 2)
716       size.width -= w * 2;
717     origin.x += w;
718
719     if (size.height > h * 2)
720       size.height -= h * 2;
721     origin.y += h;
722   }
723
724   // Return a status bar rectangle which is the last line of this rectangle.
725   // This rectangle will be modified to not include the status bar area.
726   Rect MakeStatusBar() {
727     Rect status_bar;
728     if (size.height > 1) {
729       status_bar.origin.x = origin.x;
730       status_bar.origin.y = size.height;
731       status_bar.size.width = size.width;
732       status_bar.size.height = 1;
733       --size.height;
734     }
735     return status_bar;
736   }
737
738   // Return a menubar rectangle which is the first line of this rectangle. This
739   // rectangle will be modified to not include the menubar area.
740   Rect MakeMenuBar() {
741     Rect menubar;
742     if (size.height > 1) {
743       menubar.origin.x = origin.x;
744       menubar.origin.y = origin.y;
745       menubar.size.width = size.width;
746       menubar.size.height = 1;
747       ++origin.y;
748       --size.height;
749     }
750     return menubar;
751   }
752
753   void HorizontalSplitPercentage(float top_percentage, Rect &top,
754                                  Rect &bottom) const {
755     float top_height = top_percentage * size.height;
756     HorizontalSplit(top_height, top, bottom);
757   }
758
759   void HorizontalSplit(int top_height, Rect &top, Rect &bottom) const {
760     top = *this;
761     if (top_height < size.height) {
762       top.size.height = top_height;
763       bottom.origin.x = origin.x;
764       bottom.origin.y = origin.y + top.size.height;
765       bottom.size.width = size.width;
766       bottom.size.height = size.height - top.size.height;
767     } else {
768       bottom.Clear();
769     }
770   }
771
772   void VerticalSplitPercentage(float left_percentage, Rect &left,
773                                Rect &right) const {
774     float left_width = left_percentage * size.width;
775     VerticalSplit(left_width, left, right);
776   }
777
778   void VerticalSplit(int left_width, Rect &left, Rect &right) const {
779     left = *this;
780     if (left_width < size.width) {
781       left.size.width = left_width;
782       right.origin.x = origin.x + left.size.width;
783       right.origin.y = origin.y;
784       right.size.width = size.width - left.size.width;
785       right.size.height = size.height;
786     } else {
787       right.Clear();
788     }
789   }
790 };
791
792 bool operator==(const Rect &lhs, const Rect &rhs) {
793   return lhs.origin == rhs.origin && lhs.size == rhs.size;
794 }
795
796 bool operator!=(const Rect &lhs, const Rect &rhs) {
797   return lhs.origin != rhs.origin || lhs.size != rhs.size;
798 }
799
800 enum HandleCharResult {
801   eKeyNotHandled = 0,
802   eKeyHandled = 1,
803   eQuitApplication = 2
804 };
805
806 enum class MenuActionResult {
807   Handled,
808   NotHandled,
809   Quit // Exit all menus and quit
810 };
811
812 struct KeyHelp {
813   int ch;
814   const char *description;
815 };
816
817 class WindowDelegate {
818 public:
819   virtual ~WindowDelegate() = default;
820
821   virtual bool WindowDelegateDraw(Window &window, bool force) {
822     return false; // Drawing not handled
823   }
824
825   virtual HandleCharResult WindowDelegateHandleChar(Window &window, int key) {
826     return eKeyNotHandled;
827   }
828
829   virtual const char *WindowDelegateGetHelpText() { return nullptr; }
830
831   virtual KeyHelp *WindowDelegateGetKeyHelp() { return nullptr; }
832 };
833
834 class HelpDialogDelegate : public WindowDelegate {
835 public:
836   HelpDialogDelegate(const char *text, KeyHelp *key_help_array);
837
838   ~HelpDialogDelegate() override;
839
840   bool WindowDelegateDraw(Window &window, bool force) override;
841
842   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override;
843
844   size_t GetNumLines() const { return m_text.GetSize(); }
845
846   size_t GetMaxLineLength() const { return m_text.GetMaxStringLength(); }
847
848 protected:
849   StringList m_text;
850   int m_first_visible_line;
851 };
852
853 class Window {
854 public:
855   Window(const char *name)
856       : m_name(name), m_window(nullptr), m_panel(nullptr), m_parent(nullptr),
857         m_subwindows(), m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX),
858         m_prev_active_window_idx(UINT32_MAX), m_delete(false),
859         m_needs_update(true), m_can_activate(true), m_is_subwin(false) {}
860
861   Window(const char *name, WINDOW *w, bool del = true)
862       : m_name(name), m_window(nullptr), m_panel(nullptr), m_parent(nullptr),
863         m_subwindows(), m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX),
864         m_prev_active_window_idx(UINT32_MAX), m_delete(del),
865         m_needs_update(true), m_can_activate(true), m_is_subwin(false) {
866     if (w)
867       Reset(w);
868   }
869
870   Window(const char *name, const Rect &bounds)
871       : m_name(name), m_window(nullptr), m_parent(nullptr), m_subwindows(),
872         m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX),
873         m_prev_active_window_idx(UINT32_MAX), m_delete(true),
874         m_needs_update(true), m_can_activate(true), m_is_subwin(false) {
875     Reset(::newwin(bounds.size.height, bounds.size.width, bounds.origin.y,
876                    bounds.origin.y));
877   }
878
879   virtual ~Window() {
880     RemoveSubWindows();
881     Reset();
882   }
883
884   void Reset(WINDOW *w = nullptr, bool del = true) {
885     if (m_window == w)
886       return;
887
888     if (m_panel) {
889       ::del_panel(m_panel);
890       m_panel = nullptr;
891     }
892     if (m_window && m_delete) {
893       ::delwin(m_window);
894       m_window = nullptr;
895       m_delete = false;
896     }
897     if (w) {
898       m_window = w;
899       m_panel = ::new_panel(m_window);
900       m_delete = del;
901     }
902   }
903
904   void AttributeOn(attr_t attr) { ::wattron(m_window, attr); }
905   void AttributeOff(attr_t attr) { ::wattroff(m_window, attr); }
906   void Box(chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) {
907     ::box(m_window, v_char, h_char);
908   }
909   void Clear() { ::wclear(m_window); }
910   void Erase() { ::werase(m_window); }
911   Rect GetBounds() {
912     return Rect(GetParentOrigin(), GetSize());
913   } // Get the rectangle in our parent window
914   int GetChar() { return ::wgetch(m_window); }
915   int GetCursorX() { return getcurx(m_window); }
916   int GetCursorY() { return getcury(m_window); }
917   Rect GetFrame() {
918     return Rect(Point(), GetSize());
919   } // Get our rectangle in our own coordinate system
920   Point GetParentOrigin() { return Point(GetParentX(), GetParentY()); }
921   Size GetSize() { return Size(GetWidth(), GetHeight()); }
922   int GetParentX() { return getparx(m_window); }
923   int GetParentY() { return getpary(m_window); }
924   int GetMaxX() { return getmaxx(m_window); }
925   int GetMaxY() { return getmaxy(m_window); }
926   int GetWidth() { return GetMaxX(); }
927   int GetHeight() { return GetMaxY(); }
928   void MoveCursor(int x, int y) { ::wmove(m_window, y, x); }
929   void MoveWindow(int x, int y) { MoveWindow(Point(x, y)); }
930   void Resize(int w, int h) { ::wresize(m_window, h, w); }
931   void Resize(const Size &size) {
932     ::wresize(m_window, size.height, size.width);
933   }
934   void PutChar(int ch) { ::waddch(m_window, ch); }
935   void PutCString(const char *s, int len = -1) { ::waddnstr(m_window, s, len); }
936   void Refresh() { ::wrefresh(m_window); }
937   void DeferredRefresh() {
938     // We are using panels, so we don't need to call this...
939     //::wnoutrefresh(m_window);
940   }
941   void SetBackground(int color_pair_idx) {
942     ::wbkgd(m_window, COLOR_PAIR(color_pair_idx));
943   }
944   void UnderlineOn() { AttributeOn(A_UNDERLINE); }
945   void UnderlineOff() { AttributeOff(A_UNDERLINE); }
946
947   void PutCStringTruncated(const char *s, int right_pad) {
948     int bytes_left = GetWidth() - GetCursorX();
949     if (bytes_left > right_pad) {
950       bytes_left -= right_pad;
951       ::waddnstr(m_window, s, bytes_left);
952     }
953   }
954
955   void MoveWindow(const Point &origin) {
956     const bool moving_window = origin != GetParentOrigin();
957     if (m_is_subwin && moving_window) {
958       // Can't move subwindows, must delete and re-create
959       Size size = GetSize();
960       Reset(::subwin(m_parent->m_window, size.height, size.width, origin.y,
961                      origin.x),
962             true);
963     } else {
964       ::mvwin(m_window, origin.y, origin.x);
965     }
966   }
967
968   void SetBounds(const Rect &bounds) {
969     const bool moving_window = bounds.origin != GetParentOrigin();
970     if (m_is_subwin && moving_window) {
971       // Can't move subwindows, must delete and re-create
972       Reset(::subwin(m_parent->m_window, bounds.size.height, bounds.size.width,
973                      bounds.origin.y, bounds.origin.x),
974             true);
975     } else {
976       if (moving_window)
977         MoveWindow(bounds.origin);
978       Resize(bounds.size);
979     }
980   }
981
982   void Printf(const char *format, ...) __attribute__((format(printf, 2, 3))) {
983     va_list args;
984     va_start(args, format);
985     vwprintw(m_window, format, args);
986     va_end(args);
987   }
988
989   void Touch() {
990     ::touchwin(m_window);
991     if (m_parent)
992       m_parent->Touch();
993   }
994
995   WindowSP CreateSubWindow(const char *name, const Rect &bounds,
996                            bool make_active) {
997     WindowSP subwindow_sp;
998     if (m_window) {
999       subwindow_sp.reset(new Window(
1000           name, ::subwin(m_window, bounds.size.height, bounds.size.width,
1001                          bounds.origin.y, bounds.origin.x),
1002           true));
1003       subwindow_sp->m_is_subwin = true;
1004     } else {
1005       subwindow_sp.reset(
1006           new Window(name, ::newwin(bounds.size.height, bounds.size.width,
1007                                     bounds.origin.y, bounds.origin.x),
1008                      true));
1009       subwindow_sp->m_is_subwin = false;
1010     }
1011     subwindow_sp->m_parent = this;
1012     if (make_active) {
1013       m_prev_active_window_idx = m_curr_active_window_idx;
1014       m_curr_active_window_idx = m_subwindows.size();
1015     }
1016     m_subwindows.push_back(subwindow_sp);
1017     ::top_panel(subwindow_sp->m_panel);
1018     m_needs_update = true;
1019     return subwindow_sp;
1020   }
1021
1022   bool RemoveSubWindow(Window *window) {
1023     Windows::iterator pos, end = m_subwindows.end();
1024     size_t i = 0;
1025     for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) {
1026       if ((*pos).get() == window) {
1027         if (m_prev_active_window_idx == i)
1028           m_prev_active_window_idx = UINT32_MAX;
1029         else if (m_prev_active_window_idx != UINT32_MAX &&
1030                  m_prev_active_window_idx > i)
1031           --m_prev_active_window_idx;
1032
1033         if (m_curr_active_window_idx == i)
1034           m_curr_active_window_idx = UINT32_MAX;
1035         else if (m_curr_active_window_idx != UINT32_MAX &&
1036                  m_curr_active_window_idx > i)
1037           --m_curr_active_window_idx;
1038         window->Erase();
1039         m_subwindows.erase(pos);
1040         m_needs_update = true;
1041         if (m_parent)
1042           m_parent->Touch();
1043         else
1044           ::touchwin(stdscr);
1045         return true;
1046       }
1047     }
1048     return false;
1049   }
1050
1051   WindowSP FindSubWindow(const char *name) {
1052     Windows::iterator pos, end = m_subwindows.end();
1053     size_t i = 0;
1054     for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) {
1055       if ((*pos)->m_name == name)
1056         return *pos;
1057     }
1058     return WindowSP();
1059   }
1060
1061   void RemoveSubWindows() {
1062     m_curr_active_window_idx = UINT32_MAX;
1063     m_prev_active_window_idx = UINT32_MAX;
1064     for (Windows::iterator pos = m_subwindows.begin();
1065          pos != m_subwindows.end(); pos = m_subwindows.erase(pos)) {
1066       (*pos)->Erase();
1067     }
1068     if (m_parent)
1069       m_parent->Touch();
1070     else
1071       ::touchwin(stdscr);
1072   }
1073
1074   WINDOW *get() { return m_window; }
1075
1076   operator WINDOW *() { return m_window; }
1077
1078   //----------------------------------------------------------------------
1079   // Window drawing utilities
1080   //----------------------------------------------------------------------
1081   void DrawTitleBox(const char *title, const char *bottom_message = nullptr) {
1082     attr_t attr = 0;
1083     if (IsActive())
1084       attr = A_BOLD | COLOR_PAIR(2);
1085     else
1086       attr = 0;
1087     if (attr)
1088       AttributeOn(attr);
1089
1090     Box();
1091     MoveCursor(3, 0);
1092
1093     if (title && title[0]) {
1094       PutChar('<');
1095       PutCString(title);
1096       PutChar('>');
1097     }
1098
1099     if (bottom_message && bottom_message[0]) {
1100       int bottom_message_length = strlen(bottom_message);
1101       int x = GetWidth() - 3 - (bottom_message_length + 2);
1102
1103       if (x > 0) {
1104         MoveCursor(x, GetHeight() - 1);
1105         PutChar('[');
1106         PutCString(bottom_message);
1107         PutChar(']');
1108       } else {
1109         MoveCursor(1, GetHeight() - 1);
1110         PutChar('[');
1111         PutCStringTruncated(bottom_message, 1);
1112       }
1113     }
1114     if (attr)
1115       AttributeOff(attr);
1116   }
1117
1118   virtual void Draw(bool force) {
1119     if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw(*this, force))
1120       return;
1121
1122     for (auto &subwindow_sp : m_subwindows)
1123       subwindow_sp->Draw(force);
1124   }
1125
1126   bool CreateHelpSubwindow() {
1127     if (m_delegate_sp) {
1128       const char *text = m_delegate_sp->WindowDelegateGetHelpText();
1129       KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp();
1130       if ((text && text[0]) || key_help) {
1131         std::unique_ptr<HelpDialogDelegate> help_delegate_ap(
1132             new HelpDialogDelegate(text, key_help));
1133         const size_t num_lines = help_delegate_ap->GetNumLines();
1134         const size_t max_length = help_delegate_ap->GetMaxLineLength();
1135         Rect bounds = GetBounds();
1136         bounds.Inset(1, 1);
1137         if (max_length + 4 < static_cast<size_t>(bounds.size.width)) {
1138           bounds.origin.x += (bounds.size.width - max_length + 4) / 2;
1139           bounds.size.width = max_length + 4;
1140         } else {
1141           if (bounds.size.width > 100) {
1142             const int inset_w = bounds.size.width / 4;
1143             bounds.origin.x += inset_w;
1144             bounds.size.width -= 2 * inset_w;
1145           }
1146         }
1147
1148         if (num_lines + 2 < static_cast<size_t>(bounds.size.height)) {
1149           bounds.origin.y += (bounds.size.height - num_lines + 2) / 2;
1150           bounds.size.height = num_lines + 2;
1151         } else {
1152           if (bounds.size.height > 100) {
1153             const int inset_h = bounds.size.height / 4;
1154             bounds.origin.y += inset_h;
1155             bounds.size.height -= 2 * inset_h;
1156           }
1157         }
1158         WindowSP help_window_sp;
1159         Window *parent_window = GetParent();
1160         if (parent_window)
1161           help_window_sp = parent_window->CreateSubWindow("Help", bounds, true);
1162         else
1163           help_window_sp = CreateSubWindow("Help", bounds, true);
1164         help_window_sp->SetDelegate(
1165             WindowDelegateSP(help_delegate_ap.release()));
1166         return true;
1167       }
1168     }
1169     return false;
1170   }
1171
1172   virtual HandleCharResult HandleChar(int key) {
1173     // Always check the active window first
1174     HandleCharResult result = eKeyNotHandled;
1175     WindowSP active_window_sp = GetActiveWindow();
1176     if (active_window_sp) {
1177       result = active_window_sp->HandleChar(key);
1178       if (result != eKeyNotHandled)
1179         return result;
1180     }
1181
1182     if (m_delegate_sp) {
1183       result = m_delegate_sp->WindowDelegateHandleChar(*this, key);
1184       if (result != eKeyNotHandled)
1185         return result;
1186     }
1187
1188     // Then check for any windows that want any keys that weren't handled. This
1189     // is typically only for a menubar. Make a copy of the subwindows in case
1190     // any HandleChar() functions muck with the subwindows. If we don't do
1191     // this, we can crash when iterating over the subwindows.
1192     Windows subwindows(m_subwindows);
1193     for (auto subwindow_sp : subwindows) {
1194       if (!subwindow_sp->m_can_activate) {
1195         HandleCharResult result = subwindow_sp->HandleChar(key);
1196         if (result != eKeyNotHandled)
1197           return result;
1198       }
1199     }
1200
1201     return eKeyNotHandled;
1202   }
1203
1204   bool SetActiveWindow(Window *window) {
1205     const size_t num_subwindows = m_subwindows.size();
1206     for (size_t i = 0; i < num_subwindows; ++i) {
1207       if (m_subwindows[i].get() == window) {
1208         m_prev_active_window_idx = m_curr_active_window_idx;
1209         ::top_panel(window->m_panel);
1210         m_curr_active_window_idx = i;
1211         return true;
1212       }
1213     }
1214     return false;
1215   }
1216
1217   WindowSP GetActiveWindow() {
1218     if (!m_subwindows.empty()) {
1219       if (m_curr_active_window_idx >= m_subwindows.size()) {
1220         if (m_prev_active_window_idx < m_subwindows.size()) {
1221           m_curr_active_window_idx = m_prev_active_window_idx;
1222           m_prev_active_window_idx = UINT32_MAX;
1223         } else if (IsActive()) {
1224           m_prev_active_window_idx = UINT32_MAX;
1225           m_curr_active_window_idx = UINT32_MAX;
1226
1227           // Find first window that wants to be active if this window is active
1228           const size_t num_subwindows = m_subwindows.size();
1229           for (size_t i = 0; i < num_subwindows; ++i) {
1230             if (m_subwindows[i]->GetCanBeActive()) {
1231               m_curr_active_window_idx = i;
1232               break;
1233             }
1234           }
1235         }
1236       }
1237
1238       if (m_curr_active_window_idx < m_subwindows.size())
1239         return m_subwindows[m_curr_active_window_idx];
1240     }
1241     return WindowSP();
1242   }
1243
1244   bool GetCanBeActive() const { return m_can_activate; }
1245
1246   void SetCanBeActive(bool b) { m_can_activate = b; }
1247
1248   const WindowDelegateSP &GetDelegate() const { return m_delegate_sp; }
1249
1250   void SetDelegate(const WindowDelegateSP &delegate_sp) {
1251     m_delegate_sp = delegate_sp;
1252   }
1253
1254   Window *GetParent() const { return m_parent; }
1255
1256   bool IsActive() const {
1257     if (m_parent)
1258       return m_parent->GetActiveWindow().get() == this;
1259     else
1260       return true; // Top level window is always active
1261   }
1262
1263   void SelectNextWindowAsActive() {
1264     // Move active focus to next window
1265     const size_t num_subwindows = m_subwindows.size();
1266     if (m_curr_active_window_idx == UINT32_MAX) {
1267       uint32_t idx = 0;
1268       for (auto subwindow_sp : m_subwindows) {
1269         if (subwindow_sp->GetCanBeActive()) {
1270           m_curr_active_window_idx = idx;
1271           break;
1272         }
1273         ++idx;
1274       }
1275     } else if (m_curr_active_window_idx + 1 < num_subwindows) {
1276       bool handled = false;
1277       m_prev_active_window_idx = m_curr_active_window_idx;
1278       for (size_t idx = m_curr_active_window_idx + 1; idx < num_subwindows;
1279            ++idx) {
1280         if (m_subwindows[idx]->GetCanBeActive()) {
1281           m_curr_active_window_idx = idx;
1282           handled = true;
1283           break;
1284         }
1285       }
1286       if (!handled) {
1287         for (size_t idx = 0; idx <= m_prev_active_window_idx; ++idx) {
1288           if (m_subwindows[idx]->GetCanBeActive()) {
1289             m_curr_active_window_idx = idx;
1290             break;
1291           }
1292         }
1293       }
1294     } else {
1295       m_prev_active_window_idx = m_curr_active_window_idx;
1296       for (size_t idx = 0; idx < num_subwindows; ++idx) {
1297         if (m_subwindows[idx]->GetCanBeActive()) {
1298           m_curr_active_window_idx = idx;
1299           break;
1300         }
1301       }
1302     }
1303   }
1304
1305   const char *GetName() const { return m_name.c_str(); }
1306
1307 protected:
1308   std::string m_name;
1309   WINDOW *m_window;
1310   PANEL *m_panel;
1311   Window *m_parent;
1312   Windows m_subwindows;
1313   WindowDelegateSP m_delegate_sp;
1314   uint32_t m_curr_active_window_idx;
1315   uint32_t m_prev_active_window_idx;
1316   bool m_delete;
1317   bool m_needs_update;
1318   bool m_can_activate;
1319   bool m_is_subwin;
1320
1321 private:
1322   DISALLOW_COPY_AND_ASSIGN(Window);
1323 };
1324
1325 class MenuDelegate {
1326 public:
1327   virtual ~MenuDelegate() = default;
1328
1329   virtual MenuActionResult MenuDelegateAction(Menu &menu) = 0;
1330 };
1331
1332 class Menu : public WindowDelegate {
1333 public:
1334   enum class Type { Invalid, Bar, Item, Separator };
1335
1336   // Menubar or separator constructor
1337   Menu(Type type);
1338
1339   // Menuitem constructor
1340   Menu(const char *name, const char *key_name, int key_value,
1341        uint64_t identifier);
1342
1343   ~Menu() override = default;
1344
1345   const MenuDelegateSP &GetDelegate() const { return m_delegate_sp; }
1346
1347   void SetDelegate(const MenuDelegateSP &delegate_sp) {
1348     m_delegate_sp = delegate_sp;
1349   }
1350
1351   void RecalculateNameLengths();
1352
1353   void AddSubmenu(const MenuSP &menu_sp);
1354
1355   int DrawAndRunMenu(Window &window);
1356
1357   void DrawMenuTitle(Window &window, bool highlight);
1358
1359   bool WindowDelegateDraw(Window &window, bool force) override;
1360
1361   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override;
1362
1363   MenuActionResult ActionPrivate(Menu &menu) {
1364     MenuActionResult result = MenuActionResult::NotHandled;
1365     if (m_delegate_sp) {
1366       result = m_delegate_sp->MenuDelegateAction(menu);
1367       if (result != MenuActionResult::NotHandled)
1368         return result;
1369     } else if (m_parent) {
1370       result = m_parent->ActionPrivate(menu);
1371       if (result != MenuActionResult::NotHandled)
1372         return result;
1373     }
1374     return m_canned_result;
1375   }
1376
1377   MenuActionResult Action() {
1378     // Call the recursive action so it can try to handle it with the menu
1379     // delegate, and if not, try our parent menu
1380     return ActionPrivate(*this);
1381   }
1382
1383   void SetCannedResult(MenuActionResult result) { m_canned_result = result; }
1384
1385   Menus &GetSubmenus() { return m_submenus; }
1386
1387   const Menus &GetSubmenus() const { return m_submenus; }
1388
1389   int GetSelectedSubmenuIndex() const { return m_selected; }
1390
1391   void SetSelectedSubmenuIndex(int idx) { m_selected = idx; }
1392
1393   Type GetType() const { return m_type; }
1394
1395   int GetStartingColumn() const { return m_start_col; }
1396
1397   void SetStartingColumn(int col) { m_start_col = col; }
1398
1399   int GetKeyValue() const { return m_key_value; }
1400
1401   void SetKeyValue(int key_value) { m_key_value = key_value; }
1402
1403   std::string &GetName() { return m_name; }
1404
1405   std::string &GetKeyName() { return m_key_name; }
1406
1407   int GetDrawWidth() const {
1408     return m_max_submenu_name_length + m_max_submenu_key_name_length + 8;
1409   }
1410
1411   uint64_t GetIdentifier() const { return m_identifier; }
1412
1413   void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
1414
1415 protected:
1416   std::string m_name;
1417   std::string m_key_name;
1418   uint64_t m_identifier;
1419   Type m_type;
1420   int m_key_value;
1421   int m_start_col;
1422   int m_max_submenu_name_length;
1423   int m_max_submenu_key_name_length;
1424   int m_selected;
1425   Menu *m_parent;
1426   Menus m_submenus;
1427   WindowSP m_menu_window_sp;
1428   MenuActionResult m_canned_result;
1429   MenuDelegateSP m_delegate_sp;
1430 };
1431
1432 // Menubar or separator constructor
1433 Menu::Menu(Type type)
1434     : m_name(), m_key_name(), m_identifier(0), m_type(type), m_key_value(0),
1435       m_start_col(0), m_max_submenu_name_length(0),
1436       m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr),
1437       m_submenus(), m_canned_result(MenuActionResult::NotHandled),
1438       m_delegate_sp() {}
1439
1440 // Menuitem constructor
1441 Menu::Menu(const char *name, const char *key_name, int key_value,
1442            uint64_t identifier)
1443     : m_name(), m_key_name(), m_identifier(identifier), m_type(Type::Invalid),
1444       m_key_value(key_value), m_start_col(0), m_max_submenu_name_length(0),
1445       m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr),
1446       m_submenus(), m_canned_result(MenuActionResult::NotHandled),
1447       m_delegate_sp() {
1448   if (name && name[0]) {
1449     m_name = name;
1450     m_type = Type::Item;
1451     if (key_name && key_name[0])
1452       m_key_name = key_name;
1453   } else {
1454     m_type = Type::Separator;
1455   }
1456 }
1457
1458 void Menu::RecalculateNameLengths() {
1459   m_max_submenu_name_length = 0;
1460   m_max_submenu_key_name_length = 0;
1461   Menus &submenus = GetSubmenus();
1462   const size_t num_submenus = submenus.size();
1463   for (size_t i = 0; i < num_submenus; ++i) {
1464     Menu *submenu = submenus[i].get();
1465     if (static_cast<size_t>(m_max_submenu_name_length) < submenu->m_name.size())
1466       m_max_submenu_name_length = submenu->m_name.size();
1467     if (static_cast<size_t>(m_max_submenu_key_name_length) <
1468         submenu->m_key_name.size())
1469       m_max_submenu_key_name_length = submenu->m_key_name.size();
1470   }
1471 }
1472
1473 void Menu::AddSubmenu(const MenuSP &menu_sp) {
1474   menu_sp->m_parent = this;
1475   if (static_cast<size_t>(m_max_submenu_name_length) < menu_sp->m_name.size())
1476     m_max_submenu_name_length = menu_sp->m_name.size();
1477   if (static_cast<size_t>(m_max_submenu_key_name_length) <
1478       menu_sp->m_key_name.size())
1479     m_max_submenu_key_name_length = menu_sp->m_key_name.size();
1480   m_submenus.push_back(menu_sp);
1481 }
1482
1483 void Menu::DrawMenuTitle(Window &window, bool highlight) {
1484   if (m_type == Type::Separator) {
1485     window.MoveCursor(0, window.GetCursorY());
1486     window.PutChar(ACS_LTEE);
1487     int width = window.GetWidth();
1488     if (width > 2) {
1489       width -= 2;
1490       for (int i = 0; i < width; ++i)
1491         window.PutChar(ACS_HLINE);
1492     }
1493     window.PutChar(ACS_RTEE);
1494   } else {
1495     const int shortcut_key = m_key_value;
1496     bool underlined_shortcut = false;
1497     const attr_t hilgight_attr = A_REVERSE;
1498     if (highlight)
1499       window.AttributeOn(hilgight_attr);
1500     if (isprint(shortcut_key)) {
1501       size_t lower_pos = m_name.find(tolower(shortcut_key));
1502       size_t upper_pos = m_name.find(toupper(shortcut_key));
1503       const char *name = m_name.c_str();
1504       size_t pos = std::min<size_t>(lower_pos, upper_pos);
1505       if (pos != std::string::npos) {
1506         underlined_shortcut = true;
1507         if (pos > 0) {
1508           window.PutCString(name, pos);
1509           name += pos;
1510         }
1511         const attr_t shortcut_attr = A_UNDERLINE | A_BOLD;
1512         window.AttributeOn(shortcut_attr);
1513         window.PutChar(name[0]);
1514         window.AttributeOff(shortcut_attr);
1515         name++;
1516         if (name[0])
1517           window.PutCString(name);
1518       }
1519     }
1520
1521     if (!underlined_shortcut) {
1522       window.PutCString(m_name.c_str());
1523     }
1524
1525     if (highlight)
1526       window.AttributeOff(hilgight_attr);
1527
1528     if (m_key_name.empty()) {
1529       if (!underlined_shortcut && isprint(m_key_value)) {
1530         window.AttributeOn(COLOR_PAIR(3));
1531         window.Printf(" (%c)", m_key_value);
1532         window.AttributeOff(COLOR_PAIR(3));
1533       }
1534     } else {
1535       window.AttributeOn(COLOR_PAIR(3));
1536       window.Printf(" (%s)", m_key_name.c_str());
1537       window.AttributeOff(COLOR_PAIR(3));
1538     }
1539   }
1540 }
1541
1542 bool Menu::WindowDelegateDraw(Window &window, bool force) {
1543   Menus &submenus = GetSubmenus();
1544   const size_t num_submenus = submenus.size();
1545   const int selected_idx = GetSelectedSubmenuIndex();
1546   Menu::Type menu_type = GetType();
1547   switch (menu_type) {
1548   case Menu::Type::Bar: {
1549     window.SetBackground(2);
1550     window.MoveCursor(0, 0);
1551     for (size_t i = 0; i < num_submenus; ++i) {
1552       Menu *menu = submenus[i].get();
1553       if (i > 0)
1554         window.PutChar(' ');
1555       menu->SetStartingColumn(window.GetCursorX());
1556       window.PutCString("| ");
1557       menu->DrawMenuTitle(window, false);
1558     }
1559     window.PutCString(" |");
1560     window.DeferredRefresh();
1561   } break;
1562
1563   case Menu::Type::Item: {
1564     int y = 1;
1565     int x = 3;
1566     // Draw the menu
1567     int cursor_x = 0;
1568     int cursor_y = 0;
1569     window.Erase();
1570     window.SetBackground(2);
1571     window.Box();
1572     for (size_t i = 0; i < num_submenus; ++i) {
1573       const bool is_selected = (i == static_cast<size_t>(selected_idx));
1574       window.MoveCursor(x, y + i);
1575       if (is_selected) {
1576         // Remember where we want the cursor to be
1577         cursor_x = x - 1;
1578         cursor_y = y + i;
1579       }
1580       submenus[i]->DrawMenuTitle(window, is_selected);
1581     }
1582     window.MoveCursor(cursor_x, cursor_y);
1583     window.DeferredRefresh();
1584   } break;
1585
1586   default:
1587   case Menu::Type::Separator:
1588     break;
1589   }
1590   return true; // Drawing handled...
1591 }
1592
1593 HandleCharResult Menu::WindowDelegateHandleChar(Window &window, int key) {
1594   HandleCharResult result = eKeyNotHandled;
1595
1596   Menus &submenus = GetSubmenus();
1597   const size_t num_submenus = submenus.size();
1598   const int selected_idx = GetSelectedSubmenuIndex();
1599   Menu::Type menu_type = GetType();
1600   if (menu_type == Menu::Type::Bar) {
1601     MenuSP run_menu_sp;
1602     switch (key) {
1603     case KEY_DOWN:
1604     case KEY_UP:
1605       // Show last menu or first menu
1606       if (selected_idx < static_cast<int>(num_submenus))
1607         run_menu_sp = submenus[selected_idx];
1608       else if (!submenus.empty())
1609         run_menu_sp = submenus.front();
1610       result = eKeyHandled;
1611       break;
1612
1613     case KEY_RIGHT:
1614       ++m_selected;
1615       if (m_selected >= static_cast<int>(num_submenus))
1616         m_selected = 0;
1617       if (m_selected < static_cast<int>(num_submenus))
1618         run_menu_sp = submenus[m_selected];
1619       else if (!submenus.empty())
1620         run_menu_sp = submenus.front();
1621       result = eKeyHandled;
1622       break;
1623
1624     case KEY_LEFT:
1625       --m_selected;
1626       if (m_selected < 0)
1627         m_selected = num_submenus - 1;
1628       if (m_selected < static_cast<int>(num_submenus))
1629         run_menu_sp = submenus[m_selected];
1630       else if (!submenus.empty())
1631         run_menu_sp = submenus.front();
1632       result = eKeyHandled;
1633       break;
1634
1635     default:
1636       for (size_t i = 0; i < num_submenus; ++i) {
1637         if (submenus[i]->GetKeyValue() == key) {
1638           SetSelectedSubmenuIndex(i);
1639           run_menu_sp = submenus[i];
1640           result = eKeyHandled;
1641           break;
1642         }
1643       }
1644       break;
1645     }
1646
1647     if (run_menu_sp) {
1648       // Run the action on this menu in case we need to populate the menu with
1649       // dynamic content and also in case check marks, and any other menu
1650       // decorations need to be calculated
1651       if (run_menu_sp->Action() == MenuActionResult::Quit)
1652         return eQuitApplication;
1653
1654       Rect menu_bounds;
1655       menu_bounds.origin.x = run_menu_sp->GetStartingColumn();
1656       menu_bounds.origin.y = 1;
1657       menu_bounds.size.width = run_menu_sp->GetDrawWidth();
1658       menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2;
1659       if (m_menu_window_sp)
1660         window.GetParent()->RemoveSubWindow(m_menu_window_sp.get());
1661
1662       m_menu_window_sp = window.GetParent()->CreateSubWindow(
1663           run_menu_sp->GetName().c_str(), menu_bounds, true);
1664       m_menu_window_sp->SetDelegate(run_menu_sp);
1665     }
1666   } else if (menu_type == Menu::Type::Item) {
1667     switch (key) {
1668     case KEY_DOWN:
1669       if (m_submenus.size() > 1) {
1670         const int start_select = m_selected;
1671         while (++m_selected != start_select) {
1672           if (static_cast<size_t>(m_selected) >= num_submenus)
1673             m_selected = 0;
1674           if (m_submenus[m_selected]->GetType() == Type::Separator)
1675             continue;
1676           else
1677             break;
1678         }
1679         return eKeyHandled;
1680       }
1681       break;
1682
1683     case KEY_UP:
1684       if (m_submenus.size() > 1) {
1685         const int start_select = m_selected;
1686         while (--m_selected != start_select) {
1687           if (m_selected < static_cast<int>(0))
1688             m_selected = num_submenus - 1;
1689           if (m_submenus[m_selected]->GetType() == Type::Separator)
1690             continue;
1691           else
1692             break;
1693         }
1694         return eKeyHandled;
1695       }
1696       break;
1697
1698     case KEY_RETURN:
1699       if (static_cast<size_t>(selected_idx) < num_submenus) {
1700         if (submenus[selected_idx]->Action() == MenuActionResult::Quit)
1701           return eQuitApplication;
1702         window.GetParent()->RemoveSubWindow(&window);
1703         return eKeyHandled;
1704       }
1705       break;
1706
1707     case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in
1708                      // case other chars are entered for escaped sequences
1709       window.GetParent()->RemoveSubWindow(&window);
1710       return eKeyHandled;
1711
1712     default:
1713       for (size_t i = 0; i < num_submenus; ++i) {
1714         Menu *menu = submenus[i].get();
1715         if (menu->GetKeyValue() == key) {
1716           SetSelectedSubmenuIndex(i);
1717           window.GetParent()->RemoveSubWindow(&window);
1718           if (menu->Action() == MenuActionResult::Quit)
1719             return eQuitApplication;
1720           return eKeyHandled;
1721         }
1722       }
1723       break;
1724     }
1725   } else if (menu_type == Menu::Type::Separator) {
1726   }
1727   return result;
1728 }
1729
1730 class Application {
1731 public:
1732   Application(FILE *in, FILE *out)
1733       : m_window_sp(), m_screen(nullptr), m_in(in), m_out(out) {}
1734
1735   ~Application() {
1736     m_window_delegates.clear();
1737     m_window_sp.reset();
1738     if (m_screen) {
1739       ::delscreen(m_screen);
1740       m_screen = nullptr;
1741     }
1742   }
1743
1744   void Initialize() {
1745     ::setlocale(LC_ALL, "");
1746     ::setlocale(LC_CTYPE, "");
1747     m_screen = ::newterm(nullptr, m_out, m_in);
1748     ::start_color();
1749     ::curs_set(0);
1750     ::noecho();
1751     ::keypad(stdscr, TRUE);
1752   }
1753
1754   void Terminate() { ::endwin(); }
1755
1756   void Run(Debugger &debugger) {
1757     bool done = false;
1758     int delay_in_tenths_of_a_second = 1;
1759
1760     // Alas the threading model in curses is a bit lame so we need to resort to
1761     // polling every 0.5 seconds. We could poll for stdin ourselves and then
1762     // pass the keys down but then we need to translate all of the escape
1763     // sequences ourselves. So we resort to polling for input because we need
1764     // to receive async process events while in this loop.
1765
1766     halfdelay(delay_in_tenths_of_a_second); // Poll using some number of tenths
1767                                             // of seconds seconds when calling
1768                                             // Window::GetChar()
1769
1770     ListenerSP listener_sp(
1771         Listener::MakeListener("lldb.IOHandler.curses.Application"));
1772     ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass());
1773     ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass());
1774     ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass());
1775     debugger.EnableForwardEvents(listener_sp);
1776
1777     bool update = true;
1778 #if defined(__APPLE__)
1779     std::deque<int> escape_chars;
1780 #endif
1781
1782     while (!done) {
1783       if (update) {
1784         m_window_sp->Draw(false);
1785         // All windows should be calling Window::DeferredRefresh() instead of
1786         // Window::Refresh() so we can do a single update and avoid any screen
1787         // blinking
1788         update_panels();
1789
1790         // Cursor hiding isn't working on MacOSX, so hide it in the top left
1791         // corner
1792         m_window_sp->MoveCursor(0, 0);
1793
1794         doupdate();
1795         update = false;
1796       }
1797
1798 #if defined(__APPLE__)
1799       // Terminal.app doesn't map its function keys correctly, F1-F4 default
1800       // to: \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if
1801       // possible
1802       int ch;
1803       if (escape_chars.empty())
1804         ch = m_window_sp->GetChar();
1805       else {
1806         ch = escape_chars.front();
1807         escape_chars.pop_front();
1808       }
1809       if (ch == KEY_ESCAPE) {
1810         int ch2 = m_window_sp->GetChar();
1811         if (ch2 == 'O') {
1812           int ch3 = m_window_sp->GetChar();
1813           switch (ch3) {
1814           case 'P':
1815             ch = KEY_F(1);
1816             break;
1817           case 'Q':
1818             ch = KEY_F(2);
1819             break;
1820           case 'R':
1821             ch = KEY_F(3);
1822             break;
1823           case 'S':
1824             ch = KEY_F(4);
1825             break;
1826           default:
1827             escape_chars.push_back(ch2);
1828             if (ch3 != -1)
1829               escape_chars.push_back(ch3);
1830             break;
1831           }
1832         } else if (ch2 != -1)
1833           escape_chars.push_back(ch2);
1834       }
1835 #else
1836       int ch = m_window_sp->GetChar();
1837
1838 #endif
1839       if (ch == -1) {
1840         if (feof(m_in) || ferror(m_in)) {
1841           done = true;
1842         } else {
1843           // Just a timeout from using halfdelay(), check for events
1844           EventSP event_sp;
1845           while (listener_sp->PeekAtNextEvent()) {
1846             listener_sp->GetEvent(event_sp, std::chrono::seconds(0));
1847
1848             if (event_sp) {
1849               Broadcaster *broadcaster = event_sp->GetBroadcaster();
1850               if (broadcaster) {
1851                 // uint32_t event_type = event_sp->GetType();
1852                 ConstString broadcaster_class(
1853                     broadcaster->GetBroadcasterClass());
1854                 if (broadcaster_class == broadcaster_class_process) {
1855                   debugger.GetCommandInterpreter().UpdateExecutionContext(
1856                       nullptr);
1857                   update = true;
1858                   continue; // Don't get any key, just update our view
1859                 }
1860               }
1861             }
1862           }
1863         }
1864       } else {
1865         HandleCharResult key_result = m_window_sp->HandleChar(ch);
1866         switch (key_result) {
1867         case eKeyHandled:
1868           debugger.GetCommandInterpreter().UpdateExecutionContext(nullptr);
1869           update = true;
1870           break;
1871         case eKeyNotHandled:
1872           break;
1873         case eQuitApplication:
1874           done = true;
1875           break;
1876         }
1877       }
1878     }
1879
1880     debugger.CancelForwardEvents(listener_sp);
1881   }
1882
1883   WindowSP &GetMainWindow() {
1884     if (!m_window_sp)
1885       m_window_sp.reset(new Window("main", stdscr, false));
1886     return m_window_sp;
1887   }
1888
1889   WindowDelegates &GetWindowDelegates() { return m_window_delegates; }
1890
1891 protected:
1892   WindowSP m_window_sp;
1893   WindowDelegates m_window_delegates;
1894   SCREEN *m_screen;
1895   FILE *m_in;
1896   FILE *m_out;
1897 };
1898
1899 } // namespace curses
1900
1901 using namespace curses;
1902
1903 struct Row {
1904   ValueObjectManager value;
1905   Row *parent;
1906   // The process stop ID when the children were calculated.
1907   uint32_t children_stop_id;
1908   int row_idx;
1909   int x;
1910   int y;
1911   bool might_have_children;
1912   bool expanded;
1913   bool calculated_children;
1914   std::vector<Row> children;
1915
1916   Row(const ValueObjectSP &v, Row *p)
1917       : value(v, lldb::eDynamicDontRunTarget, true), parent(p), row_idx(0),
1918         x(1), y(1), might_have_children(v ? v->MightHaveChildren() : false),
1919         expanded(false), calculated_children(false), children() {}
1920
1921   size_t GetDepth() const {
1922     if (parent)
1923       return 1 + parent->GetDepth();
1924     return 0;
1925   }
1926
1927   void Expand() {
1928     expanded = true;
1929   }
1930
1931   std::vector<Row> &GetChildren() {
1932     ProcessSP process_sp = value.GetProcessSP();
1933     auto stop_id = process_sp->GetStopID();
1934     if (process_sp && stop_id != children_stop_id) {
1935       children_stop_id = stop_id;
1936       calculated_children = false;
1937     }
1938     if (!calculated_children) {
1939       children.clear();
1940       calculated_children = true;
1941       ValueObjectSP valobj = value.GetSP();
1942       if (valobj) {
1943         const size_t num_children = valobj->GetNumChildren();
1944         for (size_t i = 0; i < num_children; ++i) {
1945           children.push_back(Row(valobj->GetChildAtIndex(i, true), this));
1946         }
1947       }
1948     }
1949     return children;
1950   }
1951
1952   void Unexpand() {
1953     expanded = false;
1954     calculated_children = false;
1955     children.clear();
1956   }
1957
1958   void DrawTree(Window &window) {
1959     if (parent)
1960       parent->DrawTreeForChild(window, this, 0);
1961
1962     if (might_have_children) {
1963       // It we can get UTF8 characters to work we should try to use the
1964       // "symbol" UTF8 string below
1965       //            const char *symbol = "";
1966       //            if (row.expanded)
1967       //                symbol = "\xe2\x96\xbd ";
1968       //            else
1969       //                symbol = "\xe2\x96\xb7 ";
1970       //            window.PutCString (symbol);
1971
1972       // The ACS_DARROW and ACS_RARROW don't look very nice they are just a 'v'
1973       // or '>' character...
1974       //            if (expanded)
1975       //                window.PutChar (ACS_DARROW);
1976       //            else
1977       //                window.PutChar (ACS_RARROW);
1978       // Since we can't find any good looking right arrow/down arrow symbols,
1979       // just use a diamond...
1980       window.PutChar(ACS_DIAMOND);
1981       window.PutChar(ACS_HLINE);
1982     }
1983   }
1984
1985   void DrawTreeForChild(Window &window, Row *child, uint32_t reverse_depth) {
1986     if (parent)
1987       parent->DrawTreeForChild(window, this, reverse_depth + 1);
1988
1989     if (&GetChildren().back() == child) {
1990       // Last child
1991       if (reverse_depth == 0) {
1992         window.PutChar(ACS_LLCORNER);
1993         window.PutChar(ACS_HLINE);
1994       } else {
1995         window.PutChar(' ');
1996         window.PutChar(' ');
1997       }
1998     } else {
1999       if (reverse_depth == 0) {
2000         window.PutChar(ACS_LTEE);
2001         window.PutChar(ACS_HLINE);
2002       } else {
2003         window.PutChar(ACS_VLINE);
2004         window.PutChar(' ');
2005       }
2006     }
2007   }
2008 };
2009
2010 struct DisplayOptions {
2011   bool show_types;
2012 };
2013
2014 class TreeItem;
2015
2016 class TreeDelegate {
2017 public:
2018   TreeDelegate() = default;
2019   virtual ~TreeDelegate() = default;
2020
2021   virtual void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) = 0;
2022   virtual void TreeDelegateGenerateChildren(TreeItem &item) = 0;
2023   virtual bool TreeDelegateItemSelected(
2024       TreeItem &item) = 0; // Return true if we need to update views
2025 };
2026
2027 typedef std::shared_ptr<TreeDelegate> TreeDelegateSP;
2028
2029 class TreeItem {
2030 public:
2031   TreeItem(TreeItem *parent, TreeDelegate &delegate, bool might_have_children)
2032       : m_parent(parent), m_delegate(delegate), m_user_data(nullptr),
2033         m_identifier(0), m_row_idx(-1), m_children(),
2034         m_might_have_children(might_have_children), m_is_expanded(false) {}
2035
2036   TreeItem &operator=(const TreeItem &rhs) {
2037     if (this != &rhs) {
2038       m_parent = rhs.m_parent;
2039       m_delegate = rhs.m_delegate;
2040       m_user_data = rhs.m_user_data;
2041       m_identifier = rhs.m_identifier;
2042       m_row_idx = rhs.m_row_idx;
2043       m_children = rhs.m_children;
2044       m_might_have_children = rhs.m_might_have_children;
2045       m_is_expanded = rhs.m_is_expanded;
2046     }
2047     return *this;
2048   }
2049
2050   size_t GetDepth() const {
2051     if (m_parent)
2052       return 1 + m_parent->GetDepth();
2053     return 0;
2054   }
2055
2056   int GetRowIndex() const { return m_row_idx; }
2057
2058   void ClearChildren() { m_children.clear(); }
2059
2060   void Resize(size_t n, const TreeItem &t) { m_children.resize(n, t); }
2061
2062   TreeItem &operator[](size_t i) { return m_children[i]; }
2063
2064   void SetRowIndex(int row_idx) { m_row_idx = row_idx; }
2065
2066   size_t GetNumChildren() {
2067     m_delegate.TreeDelegateGenerateChildren(*this);
2068     return m_children.size();
2069   }
2070
2071   void ItemWasSelected() { m_delegate.TreeDelegateItemSelected(*this); }
2072
2073   void CalculateRowIndexes(int &row_idx) {
2074     SetRowIndex(row_idx);
2075     ++row_idx;
2076
2077     const bool expanded = IsExpanded();
2078
2079     // The root item must calculate its children, or we must calculate the
2080     // number of children if the item is expanded
2081     if (m_parent == nullptr || expanded)
2082       GetNumChildren();
2083
2084     for (auto &item : m_children) {
2085       if (expanded)
2086         item.CalculateRowIndexes(row_idx);
2087       else
2088         item.SetRowIndex(-1);
2089     }
2090   }
2091
2092   TreeItem *GetParent() { return m_parent; }
2093
2094   bool IsExpanded() const { return m_is_expanded; }
2095
2096   void Expand() { m_is_expanded = true; }
2097
2098   void Unexpand() { m_is_expanded = false; }
2099
2100   bool Draw(Window &window, const int first_visible_row,
2101             const uint32_t selected_row_idx, int &row_idx, int &num_rows_left) {
2102     if (num_rows_left <= 0)
2103       return false;
2104
2105     if (m_row_idx >= first_visible_row) {
2106       window.MoveCursor(2, row_idx + 1);
2107
2108       if (m_parent)
2109         m_parent->DrawTreeForChild(window, this, 0);
2110
2111       if (m_might_have_children) {
2112         // It we can get UTF8 characters to work we should try to use the
2113         // "symbol" UTF8 string below
2114         //            const char *symbol = "";
2115         //            if (row.expanded)
2116         //                symbol = "\xe2\x96\xbd ";
2117         //            else
2118         //                symbol = "\xe2\x96\xb7 ";
2119         //            window.PutCString (symbol);
2120
2121         // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
2122         // 'v' or '>' character...
2123         //            if (expanded)
2124         //                window.PutChar (ACS_DARROW);
2125         //            else
2126         //                window.PutChar (ACS_RARROW);
2127         // Since we can't find any good looking right arrow/down arrow symbols,
2128         // just use a diamond...
2129         window.PutChar(ACS_DIAMOND);
2130         window.PutChar(ACS_HLINE);
2131       }
2132       bool highlight = (selected_row_idx == static_cast<size_t>(m_row_idx)) &&
2133                        window.IsActive();
2134
2135       if (highlight)
2136         window.AttributeOn(A_REVERSE);
2137
2138       m_delegate.TreeDelegateDrawTreeItem(*this, window);
2139
2140       if (highlight)
2141         window.AttributeOff(A_REVERSE);
2142       ++row_idx;
2143       --num_rows_left;
2144     }
2145
2146     if (num_rows_left <= 0)
2147       return false; // We are done drawing...
2148
2149     if (IsExpanded()) {
2150       for (auto &item : m_children) {
2151         // If we displayed all the rows and item.Draw() returns false we are
2152         // done drawing and can exit this for loop
2153         if (!item.Draw(window, first_visible_row, selected_row_idx, row_idx,
2154                        num_rows_left))
2155           break;
2156       }
2157     }
2158     return num_rows_left >= 0; // Return true if not done drawing yet
2159   }
2160
2161   void DrawTreeForChild(Window &window, TreeItem *child,
2162                         uint32_t reverse_depth) {
2163     if (m_parent)
2164       m_parent->DrawTreeForChild(window, this, reverse_depth + 1);
2165
2166     if (&m_children.back() == child) {
2167       // Last child
2168       if (reverse_depth == 0) {
2169         window.PutChar(ACS_LLCORNER);
2170         window.PutChar(ACS_HLINE);
2171       } else {
2172         window.PutChar(' ');
2173         window.PutChar(' ');
2174       }
2175     } else {
2176       if (reverse_depth == 0) {
2177         window.PutChar(ACS_LTEE);
2178         window.PutChar(ACS_HLINE);
2179       } else {
2180         window.PutChar(ACS_VLINE);
2181         window.PutChar(' ');
2182       }
2183     }
2184   }
2185
2186   TreeItem *GetItemForRowIndex(uint32_t row_idx) {
2187     if (static_cast<uint32_t>(m_row_idx) == row_idx)
2188       return this;
2189     if (m_children.empty())
2190       return nullptr;
2191     if (IsExpanded()) {
2192       for (auto &item : m_children) {
2193         TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx);
2194         if (selected_item_ptr)
2195           return selected_item_ptr;
2196       }
2197     }
2198     return nullptr;
2199   }
2200
2201   void *GetUserData() const { return m_user_data; }
2202
2203   void SetUserData(void *user_data) { m_user_data = user_data; }
2204
2205   uint64_t GetIdentifier() const { return m_identifier; }
2206
2207   void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
2208
2209   void SetMightHaveChildren(bool b) { m_might_have_children = b; }
2210
2211 protected:
2212   TreeItem *m_parent;
2213   TreeDelegate &m_delegate;
2214   void *m_user_data;
2215   uint64_t m_identifier;
2216   int m_row_idx; // Zero based visible row index, -1 if not visible or for the
2217                  // root item
2218   std::vector<TreeItem> m_children;
2219   bool m_might_have_children;
2220   bool m_is_expanded;
2221 };
2222
2223 class TreeWindowDelegate : public WindowDelegate {
2224 public:
2225   TreeWindowDelegate(Debugger &debugger, const TreeDelegateSP &delegate_sp)
2226       : m_debugger(debugger), m_delegate_sp(delegate_sp),
2227         m_root(nullptr, *delegate_sp, true), m_selected_item(nullptr),
2228         m_num_rows(0), m_selected_row_idx(0), m_first_visible_row(0),
2229         m_min_x(0), m_min_y(0), m_max_x(0), m_max_y(0) {}
2230
2231   int NumVisibleRows() const { return m_max_y - m_min_y; }
2232
2233   bool WindowDelegateDraw(Window &window, bool force) override {
2234     ExecutionContext exe_ctx(
2235         m_debugger.GetCommandInterpreter().GetExecutionContext());
2236     Process *process = exe_ctx.GetProcessPtr();
2237
2238     bool display_content = false;
2239     if (process) {
2240       StateType state = process->GetState();
2241       if (StateIsStoppedState(state, true)) {
2242         // We are stopped, so it is ok to
2243         display_content = true;
2244       } else if (StateIsRunningState(state)) {
2245         return true; // Don't do any updating when we are running
2246       }
2247     }
2248
2249     m_min_x = 2;
2250     m_min_y = 1;
2251     m_max_x = window.GetWidth() - 1;
2252     m_max_y = window.GetHeight() - 1;
2253
2254     window.Erase();
2255     window.DrawTitleBox(window.GetName());
2256
2257     if (display_content) {
2258       const int num_visible_rows = NumVisibleRows();
2259       m_num_rows = 0;
2260       m_root.CalculateRowIndexes(m_num_rows);
2261
2262       // If we unexpanded while having something selected our total number of
2263       // rows is less than the num visible rows, then make sure we show all the
2264       // rows by setting the first visible row accordingly.
2265       if (m_first_visible_row > 0 && m_num_rows < num_visible_rows)
2266         m_first_visible_row = 0;
2267
2268       // Make sure the selected row is always visible
2269       if (m_selected_row_idx < m_first_visible_row)
2270         m_first_visible_row = m_selected_row_idx;
2271       else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
2272         m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
2273
2274       int row_idx = 0;
2275       int num_rows_left = num_visible_rows;
2276       m_root.Draw(window, m_first_visible_row, m_selected_row_idx, row_idx,
2277                   num_rows_left);
2278       // Get the selected row
2279       m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
2280     } else {
2281       m_selected_item = nullptr;
2282     }
2283
2284     window.DeferredRefresh();
2285
2286     return true; // Drawing handled
2287   }
2288
2289   const char *WindowDelegateGetHelpText() override {
2290     return "Thread window keyboard shortcuts:";
2291   }
2292
2293   KeyHelp *WindowDelegateGetKeyHelp() override {
2294     static curses::KeyHelp g_source_view_key_help[] = {
2295         {KEY_UP, "Select previous item"},
2296         {KEY_DOWN, "Select next item"},
2297         {KEY_RIGHT, "Expand the selected item"},
2298         {KEY_LEFT,
2299          "Unexpand the selected item or select parent if not expanded"},
2300         {KEY_PPAGE, "Page up"},
2301         {KEY_NPAGE, "Page down"},
2302         {'h', "Show help dialog"},
2303         {' ', "Toggle item expansion"},
2304         {',', "Page up"},
2305         {'.', "Page down"},
2306         {'\0', nullptr}};
2307     return g_source_view_key_help;
2308   }
2309
2310   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
2311     switch (c) {
2312     case ',':
2313     case KEY_PPAGE:
2314       // Page up key
2315       if (m_first_visible_row > 0) {
2316         if (m_first_visible_row > m_max_y)
2317           m_first_visible_row -= m_max_y;
2318         else
2319           m_first_visible_row = 0;
2320         m_selected_row_idx = m_first_visible_row;
2321         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
2322         if (m_selected_item)
2323           m_selected_item->ItemWasSelected();
2324       }
2325       return eKeyHandled;
2326
2327     case '.':
2328     case KEY_NPAGE:
2329       // Page down key
2330       if (m_num_rows > m_max_y) {
2331         if (m_first_visible_row + m_max_y < m_num_rows) {
2332           m_first_visible_row += m_max_y;
2333           m_selected_row_idx = m_first_visible_row;
2334           m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
2335           if (m_selected_item)
2336             m_selected_item->ItemWasSelected();
2337         }
2338       }
2339       return eKeyHandled;
2340
2341     case KEY_UP:
2342       if (m_selected_row_idx > 0) {
2343         --m_selected_row_idx;
2344         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
2345         if (m_selected_item)
2346           m_selected_item->ItemWasSelected();
2347       }
2348       return eKeyHandled;
2349
2350     case KEY_DOWN:
2351       if (m_selected_row_idx + 1 < m_num_rows) {
2352         ++m_selected_row_idx;
2353         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
2354         if (m_selected_item)
2355           m_selected_item->ItemWasSelected();
2356       }
2357       return eKeyHandled;
2358
2359     case KEY_RIGHT:
2360       if (m_selected_item) {
2361         if (!m_selected_item->IsExpanded())
2362           m_selected_item->Expand();
2363       }
2364       return eKeyHandled;
2365
2366     case KEY_LEFT:
2367       if (m_selected_item) {
2368         if (m_selected_item->IsExpanded())
2369           m_selected_item->Unexpand();
2370         else if (m_selected_item->GetParent()) {
2371           m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex();
2372           m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
2373           if (m_selected_item)
2374             m_selected_item->ItemWasSelected();
2375         }
2376       }
2377       return eKeyHandled;
2378
2379     case ' ':
2380       // Toggle expansion state when SPACE is pressed
2381       if (m_selected_item) {
2382         if (m_selected_item->IsExpanded())
2383           m_selected_item->Unexpand();
2384         else
2385           m_selected_item->Expand();
2386       }
2387       return eKeyHandled;
2388
2389     case 'h':
2390       window.CreateHelpSubwindow();
2391       return eKeyHandled;
2392
2393     default:
2394       break;
2395     }
2396     return eKeyNotHandled;
2397   }
2398
2399 protected:
2400   Debugger &m_debugger;
2401   TreeDelegateSP m_delegate_sp;
2402   TreeItem m_root;
2403   TreeItem *m_selected_item;
2404   int m_num_rows;
2405   int m_selected_row_idx;
2406   int m_first_visible_row;
2407   int m_min_x;
2408   int m_min_y;
2409   int m_max_x;
2410   int m_max_y;
2411 };
2412
2413 class FrameTreeDelegate : public TreeDelegate {
2414 public:
2415   FrameTreeDelegate() : TreeDelegate() {
2416     FormatEntity::Parse(
2417         "frame #${frame.index}: {${function.name}${function.pc-offset}}}",
2418         m_format);
2419   }
2420
2421   ~FrameTreeDelegate() override = default;
2422
2423   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
2424     Thread *thread = (Thread *)item.GetUserData();
2425     if (thread) {
2426       const uint64_t frame_idx = item.GetIdentifier();
2427       StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_idx);
2428       if (frame_sp) {
2429         StreamString strm;
2430         const SymbolContext &sc =
2431             frame_sp->GetSymbolContext(eSymbolContextEverything);
2432         ExecutionContext exe_ctx(frame_sp);
2433         if (FormatEntity::Format(m_format, strm, &sc, &exe_ctx, nullptr,
2434                                  nullptr, false, false)) {
2435           int right_pad = 1;
2436           window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad);
2437         }
2438       }
2439     }
2440   }
2441
2442   void TreeDelegateGenerateChildren(TreeItem &item) override {
2443     // No children for frames yet...
2444   }
2445
2446   bool TreeDelegateItemSelected(TreeItem &item) override {
2447     Thread *thread = (Thread *)item.GetUserData();
2448     if (thread) {
2449       thread->GetProcess()->GetThreadList().SetSelectedThreadByID(
2450           thread->GetID());
2451       const uint64_t frame_idx = item.GetIdentifier();
2452       thread->SetSelectedFrameByIndex(frame_idx);
2453       return true;
2454     }
2455     return false;
2456   }
2457
2458 protected:
2459   FormatEntity::Entry m_format;
2460 };
2461
2462 class ThreadTreeDelegate : public TreeDelegate {
2463 public:
2464   ThreadTreeDelegate(Debugger &debugger)
2465       : TreeDelegate(), m_debugger(debugger), m_tid(LLDB_INVALID_THREAD_ID),
2466         m_stop_id(UINT32_MAX) {
2467     FormatEntity::Parse("thread #${thread.index}: tid = ${thread.id}{, stop "
2468                         "reason = ${thread.stop-reason}}",
2469                         m_format);
2470   }
2471
2472   ~ThreadTreeDelegate() override = default;
2473
2474   ProcessSP GetProcess() {
2475     return m_debugger.GetCommandInterpreter()
2476         .GetExecutionContext()
2477         .GetProcessSP();
2478   }
2479
2480   ThreadSP GetThread(const TreeItem &item) {
2481     ProcessSP process_sp = GetProcess();
2482     if (process_sp)
2483       return process_sp->GetThreadList().FindThreadByID(item.GetIdentifier());
2484     return ThreadSP();
2485   }
2486
2487   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
2488     ThreadSP thread_sp = GetThread(item);
2489     if (thread_sp) {
2490       StreamString strm;
2491       ExecutionContext exe_ctx(thread_sp);
2492       if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
2493                                nullptr, false, false)) {
2494         int right_pad = 1;
2495         window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad);
2496       }
2497     }
2498   }
2499
2500   void TreeDelegateGenerateChildren(TreeItem &item) override {
2501     ProcessSP process_sp = GetProcess();
2502     if (process_sp && process_sp->IsAlive()) {
2503       StateType state = process_sp->GetState();
2504       if (StateIsStoppedState(state, true)) {
2505         ThreadSP thread_sp = GetThread(item);
2506         if (thread_sp) {
2507           if (m_stop_id == process_sp->GetStopID() &&
2508               thread_sp->GetID() == m_tid)
2509             return; // Children are already up to date
2510           if (!m_frame_delegate_sp) {
2511             // Always expand the thread item the first time we show it
2512             m_frame_delegate_sp.reset(new FrameTreeDelegate());
2513           }
2514
2515           m_stop_id = process_sp->GetStopID();
2516           m_tid = thread_sp->GetID();
2517
2518           TreeItem t(&item, *m_frame_delegate_sp, false);
2519           size_t num_frames = thread_sp->GetStackFrameCount();
2520           item.Resize(num_frames, t);
2521           for (size_t i = 0; i < num_frames; ++i) {
2522             item[i].SetUserData(thread_sp.get());
2523             item[i].SetIdentifier(i);
2524           }
2525         }
2526         return;
2527       }
2528     }
2529     item.ClearChildren();
2530   }
2531
2532   bool TreeDelegateItemSelected(TreeItem &item) override {
2533     ProcessSP process_sp = GetProcess();
2534     if (process_sp && process_sp->IsAlive()) {
2535       StateType state = process_sp->GetState();
2536       if (StateIsStoppedState(state, true)) {
2537         ThreadSP thread_sp = GetThread(item);
2538         if (thread_sp) {
2539           ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList();
2540           std::lock_guard<std::recursive_mutex> guard(thread_list.GetMutex());
2541           ThreadSP selected_thread_sp = thread_list.GetSelectedThread();
2542           if (selected_thread_sp->GetID() != thread_sp->GetID()) {
2543             thread_list.SetSelectedThreadByID(thread_sp->GetID());
2544             return true;
2545           }
2546         }
2547       }
2548     }
2549     return false;
2550   }
2551
2552 protected:
2553   Debugger &m_debugger;
2554   std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp;
2555   lldb::user_id_t m_tid;
2556   uint32_t m_stop_id;
2557   FormatEntity::Entry m_format;
2558 };
2559
2560 class ThreadsTreeDelegate : public TreeDelegate {
2561 public:
2562   ThreadsTreeDelegate(Debugger &debugger)
2563       : TreeDelegate(), m_thread_delegate_sp(), m_debugger(debugger),
2564         m_stop_id(UINT32_MAX) {
2565     FormatEntity::Parse("process ${process.id}{, name = ${process.name}}",
2566                         m_format);
2567   }
2568
2569   ~ThreadsTreeDelegate() override = default;
2570
2571   ProcessSP GetProcess() {
2572     return m_debugger.GetCommandInterpreter()
2573         .GetExecutionContext()
2574         .GetProcessSP();
2575   }
2576
2577   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
2578     ProcessSP process_sp = GetProcess();
2579     if (process_sp && process_sp->IsAlive()) {
2580       StreamString strm;
2581       ExecutionContext exe_ctx(process_sp);
2582       if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
2583                                nullptr, false, false)) {
2584         int right_pad = 1;
2585         window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad);
2586       }
2587     }
2588   }
2589
2590   void TreeDelegateGenerateChildren(TreeItem &item) override {
2591     ProcessSP process_sp = GetProcess();
2592     if (process_sp && process_sp->IsAlive()) {
2593       StateType state = process_sp->GetState();
2594       if (StateIsStoppedState(state, true)) {
2595         const uint32_t stop_id = process_sp->GetStopID();
2596         if (m_stop_id == stop_id)
2597           return; // Children are already up to date
2598
2599         m_stop_id = stop_id;
2600
2601         if (!m_thread_delegate_sp) {
2602           // Always expand the thread item the first time we show it
2603           // item.Expand();
2604           m_thread_delegate_sp.reset(new ThreadTreeDelegate(m_debugger));
2605         }
2606
2607         TreeItem t(&item, *m_thread_delegate_sp, false);
2608         ThreadList &threads = process_sp->GetThreadList();
2609         std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
2610         size_t num_threads = threads.GetSize();
2611         item.Resize(num_threads, t);
2612         for (size_t i = 0; i < num_threads; ++i) {
2613           item[i].SetIdentifier(threads.GetThreadAtIndex(i)->GetID());
2614           item[i].SetMightHaveChildren(true);
2615         }
2616         return;
2617       }
2618     }
2619     item.ClearChildren();
2620   }
2621
2622   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
2623
2624 protected:
2625   std::shared_ptr<ThreadTreeDelegate> m_thread_delegate_sp;
2626   Debugger &m_debugger;
2627   uint32_t m_stop_id;
2628   FormatEntity::Entry m_format;
2629 };
2630
2631 class ValueObjectListDelegate : public WindowDelegate {
2632 public:
2633   ValueObjectListDelegate()
2634       : m_rows(), m_selected_row(nullptr),
2635         m_selected_row_idx(0), m_first_visible_row(0), m_num_rows(0),
2636         m_max_x(0), m_max_y(0) {}
2637
2638   ValueObjectListDelegate(ValueObjectList &valobj_list)
2639       : m_rows(), m_selected_row(nullptr),
2640         m_selected_row_idx(0), m_first_visible_row(0), m_num_rows(0),
2641         m_max_x(0), m_max_y(0) {
2642     SetValues(valobj_list);
2643   }
2644
2645   ~ValueObjectListDelegate() override = default;
2646
2647   void SetValues(ValueObjectList &valobj_list) {
2648     m_selected_row = nullptr;
2649     m_selected_row_idx = 0;
2650     m_first_visible_row = 0;
2651     m_num_rows = 0;
2652     m_rows.clear();
2653     for (auto &valobj_sp : valobj_list.GetObjects())
2654       m_rows.push_back(Row(valobj_sp, nullptr));
2655   }
2656
2657   bool WindowDelegateDraw(Window &window, bool force) override {
2658     m_num_rows = 0;
2659     m_min_x = 2;
2660     m_min_y = 1;
2661     m_max_x = window.GetWidth() - 1;
2662     m_max_y = window.GetHeight() - 1;
2663
2664     window.Erase();
2665     window.DrawTitleBox(window.GetName());
2666
2667     const int num_visible_rows = NumVisibleRows();
2668     const int num_rows = CalculateTotalNumberRows(m_rows);
2669
2670     // If we unexpanded while having something selected our total number of
2671     // rows is less than the num visible rows, then make sure we show all the
2672     // rows by setting the first visible row accordingly.
2673     if (m_first_visible_row > 0 && num_rows < num_visible_rows)
2674       m_first_visible_row = 0;
2675
2676     // Make sure the selected row is always visible
2677     if (m_selected_row_idx < m_first_visible_row)
2678       m_first_visible_row = m_selected_row_idx;
2679     else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
2680       m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
2681
2682     DisplayRows(window, m_rows, g_options);
2683
2684     window.DeferredRefresh();
2685
2686     // Get the selected row
2687     m_selected_row = GetRowForRowIndex(m_selected_row_idx);
2688     // Keep the cursor on the selected row so the highlight and the cursor are
2689     // always on the same line
2690     if (m_selected_row)
2691       window.MoveCursor(m_selected_row->x, m_selected_row->y);
2692
2693     return true; // Drawing handled
2694   }
2695
2696   KeyHelp *WindowDelegateGetKeyHelp() override {
2697     static curses::KeyHelp g_source_view_key_help[] = {
2698         {KEY_UP, "Select previous item"},
2699         {KEY_DOWN, "Select next item"},
2700         {KEY_RIGHT, "Expand selected item"},
2701         {KEY_LEFT, "Unexpand selected item or select parent if not expanded"},
2702         {KEY_PPAGE, "Page up"},
2703         {KEY_NPAGE, "Page down"},
2704         {'A', "Format as annotated address"},
2705         {'b', "Format as binary"},
2706         {'B', "Format as hex bytes with ASCII"},
2707         {'c', "Format as character"},
2708         {'d', "Format as a signed integer"},
2709         {'D', "Format selected value using the default format for the type"},
2710         {'f', "Format as float"},
2711         {'h', "Show help dialog"},
2712         {'i', "Format as instructions"},
2713         {'o', "Format as octal"},
2714         {'p', "Format as pointer"},
2715         {'s', "Format as C string"},
2716         {'t', "Toggle showing/hiding type names"},
2717         {'u', "Format as an unsigned integer"},
2718         {'x', "Format as hex"},
2719         {'X', "Format as uppercase hex"},
2720         {' ', "Toggle item expansion"},
2721         {',', "Page up"},
2722         {'.', "Page down"},
2723         {'\0', nullptr}};
2724     return g_source_view_key_help;
2725   }
2726
2727   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
2728     switch (c) {
2729     case 'x':
2730     case 'X':
2731     case 'o':
2732     case 's':
2733     case 'u':
2734     case 'd':
2735     case 'D':
2736     case 'i':
2737     case 'A':
2738     case 'p':
2739     case 'c':
2740     case 'b':
2741     case 'B':
2742     case 'f':
2743       // Change the format for the currently selected item
2744       if (m_selected_row) {
2745         auto valobj_sp = m_selected_row->value.GetSP();
2746         if (valobj_sp)
2747           valobj_sp->SetFormat(FormatForChar(c));
2748       }
2749       return eKeyHandled;
2750
2751     case 't':
2752       // Toggle showing type names
2753       g_options.show_types = !g_options.show_types;
2754       return eKeyHandled;
2755
2756     case ',':
2757     case KEY_PPAGE:
2758       // Page up key
2759       if (m_first_visible_row > 0) {
2760         if (static_cast<int>(m_first_visible_row) > m_max_y)
2761           m_first_visible_row -= m_max_y;
2762         else
2763           m_first_visible_row = 0;
2764         m_selected_row_idx = m_first_visible_row;
2765       }
2766       return eKeyHandled;
2767
2768     case '.':
2769     case KEY_NPAGE:
2770       // Page down key
2771       if (m_num_rows > static_cast<size_t>(m_max_y)) {
2772         if (m_first_visible_row + m_max_y < m_num_rows) {
2773           m_first_visible_row += m_max_y;
2774           m_selected_row_idx = m_first_visible_row;
2775         }
2776       }
2777       return eKeyHandled;
2778
2779     case KEY_UP:
2780       if (m_selected_row_idx > 0)
2781         --m_selected_row_idx;
2782       return eKeyHandled;
2783
2784     case KEY_DOWN:
2785       if (m_selected_row_idx + 1 < m_num_rows)
2786         ++m_selected_row_idx;
2787       return eKeyHandled;
2788
2789     case KEY_RIGHT:
2790       if (m_selected_row) {
2791         if (!m_selected_row->expanded)
2792           m_selected_row->Expand();
2793       }
2794       return eKeyHandled;
2795
2796     case KEY_LEFT:
2797       if (m_selected_row) {
2798         if (m_selected_row->expanded)
2799           m_selected_row->Unexpand();
2800         else if (m_selected_row->parent)
2801           m_selected_row_idx = m_selected_row->parent->row_idx;
2802       }
2803       return eKeyHandled;
2804
2805     case ' ':
2806       // Toggle expansion state when SPACE is pressed
2807       if (m_selected_row) {
2808         if (m_selected_row->expanded)
2809           m_selected_row->Unexpand();
2810         else
2811           m_selected_row->Expand();
2812       }
2813       return eKeyHandled;
2814
2815     case 'h':
2816       window.CreateHelpSubwindow();
2817       return eKeyHandled;
2818
2819     default:
2820       break;
2821     }
2822     return eKeyNotHandled;
2823   }
2824
2825 protected:
2826   std::vector<Row> m_rows;
2827   Row *m_selected_row;
2828   uint32_t m_selected_row_idx;
2829   uint32_t m_first_visible_row;
2830   uint32_t m_num_rows;
2831   int m_min_x;
2832   int m_min_y;
2833   int m_max_x;
2834   int m_max_y;
2835
2836   static Format FormatForChar(int c) {
2837     switch (c) {
2838     case 'x':
2839       return eFormatHex;
2840     case 'X':
2841       return eFormatHexUppercase;
2842     case 'o':
2843       return eFormatOctal;
2844     case 's':
2845       return eFormatCString;
2846     case 'u':
2847       return eFormatUnsigned;
2848     case 'd':
2849       return eFormatDecimal;
2850     case 'D':
2851       return eFormatDefault;
2852     case 'i':
2853       return eFormatInstruction;
2854     case 'A':
2855       return eFormatAddressInfo;
2856     case 'p':
2857       return eFormatPointer;
2858     case 'c':
2859       return eFormatChar;
2860     case 'b':
2861       return eFormatBinary;
2862     case 'B':
2863       return eFormatBytesWithASCII;
2864     case 'f':
2865       return eFormatFloat;
2866     }
2867     return eFormatDefault;
2868   }
2869
2870   bool DisplayRowObject(Window &window, Row &row, DisplayOptions &options,
2871                         bool highlight, bool last_child) {
2872     ValueObject *valobj = row.value.GetSP().get();
2873
2874     if (valobj == nullptr)
2875       return false;
2876
2877     const char *type_name =
2878         options.show_types ? valobj->GetTypeName().GetCString() : nullptr;
2879     const char *name = valobj->GetName().GetCString();
2880     const char *value = valobj->GetValueAsCString();
2881     const char *summary = valobj->GetSummaryAsCString();
2882
2883     window.MoveCursor(row.x, row.y);
2884
2885     row.DrawTree(window);
2886
2887     if (highlight)
2888       window.AttributeOn(A_REVERSE);
2889
2890     if (type_name && type_name[0])
2891       window.Printf("(%s) ", type_name);
2892
2893     if (name && name[0])
2894       window.PutCString(name);
2895
2896     attr_t changd_attr = 0;
2897     if (valobj->GetValueDidChange())
2898       changd_attr = COLOR_PAIR(5) | A_BOLD;
2899
2900     if (value && value[0]) {
2901       window.PutCString(" = ");
2902       if (changd_attr)
2903         window.AttributeOn(changd_attr);
2904       window.PutCString(value);
2905       if (changd_attr)
2906         window.AttributeOff(changd_attr);
2907     }
2908
2909     if (summary && summary[0]) {
2910       window.PutChar(' ');
2911       if (changd_attr)
2912         window.AttributeOn(changd_attr);
2913       window.PutCString(summary);
2914       if (changd_attr)
2915         window.AttributeOff(changd_attr);
2916     }
2917
2918     if (highlight)
2919       window.AttributeOff(A_REVERSE);
2920
2921     return true;
2922   }
2923
2924   void DisplayRows(Window &window, std::vector<Row> &rows,
2925                    DisplayOptions &options) {
2926     // >   0x25B7
2927     // \/  0x25BD
2928
2929     bool window_is_active = window.IsActive();
2930     for (auto &row : rows) {
2931       const bool last_child = row.parent && &rows[rows.size() - 1] == &row;
2932       // Save the row index in each Row structure
2933       row.row_idx = m_num_rows;
2934       if ((m_num_rows >= m_first_visible_row) &&
2935           ((m_num_rows - m_first_visible_row) <
2936            static_cast<size_t>(NumVisibleRows()))) {
2937         row.x = m_min_x;
2938         row.y = m_num_rows - m_first_visible_row + 1;
2939         if (DisplayRowObject(window, row, options,
2940                              window_is_active &&
2941                                  m_num_rows == m_selected_row_idx,
2942                              last_child)) {
2943           ++m_num_rows;
2944         } else {
2945           row.x = 0;
2946           row.y = 0;
2947         }
2948       } else {
2949         row.x = 0;
2950         row.y = 0;
2951         ++m_num_rows;
2952       }
2953
2954       auto &children = row.GetChildren();
2955       if (row.expanded && !children.empty()) {
2956         DisplayRows(window, children, options);
2957       }
2958     }
2959   }
2960
2961   int CalculateTotalNumberRows(std::vector<Row> &rows) {
2962     int row_count = 0;
2963     for (auto &row : rows) {
2964       ++row_count;
2965       if (row.expanded)
2966         row_count += CalculateTotalNumberRows(row.GetChildren());
2967     }
2968     return row_count;
2969   }
2970
2971   static Row *GetRowForRowIndexImpl(std::vector<Row> &rows, size_t &row_index) {
2972     for (auto &row : rows) {
2973       if (row_index == 0)
2974         return &row;
2975       else {
2976         --row_index;
2977         auto &children = row.GetChildren();
2978         if (row.expanded && !children.empty()) {
2979           Row *result = GetRowForRowIndexImpl(children, row_index);
2980           if (result)
2981             return result;
2982         }
2983       }
2984     }
2985     return nullptr;
2986   }
2987
2988   Row *GetRowForRowIndex(size_t row_index) {
2989     return GetRowForRowIndexImpl(m_rows, row_index);
2990   }
2991
2992   int NumVisibleRows() const { return m_max_y - m_min_y; }
2993
2994   static DisplayOptions g_options;
2995 };
2996
2997 class FrameVariablesWindowDelegate : public ValueObjectListDelegate {
2998 public:
2999   FrameVariablesWindowDelegate(Debugger &debugger)
3000       : ValueObjectListDelegate(), m_debugger(debugger),
3001         m_frame_block(nullptr) {}
3002
3003   ~FrameVariablesWindowDelegate() override = default;
3004
3005   const char *WindowDelegateGetHelpText() override {
3006     return "Frame variable window keyboard shortcuts:";
3007   }
3008
3009   bool WindowDelegateDraw(Window &window, bool force) override {
3010     ExecutionContext exe_ctx(
3011         m_debugger.GetCommandInterpreter().GetExecutionContext());
3012     Process *process = exe_ctx.GetProcessPtr();
3013     Block *frame_block = nullptr;
3014     StackFrame *frame = nullptr;
3015
3016     if (process) {
3017       StateType state = process->GetState();
3018       if (StateIsStoppedState(state, true)) {
3019         frame = exe_ctx.GetFramePtr();
3020         if (frame)
3021           frame_block = frame->GetFrameBlock();
3022       } else if (StateIsRunningState(state)) {
3023         return true; // Don't do any updating when we are running
3024       }
3025     }
3026
3027     ValueObjectList local_values;
3028     if (frame_block) {
3029       // Only update the variables if they have changed
3030       if (m_frame_block != frame_block) {
3031         m_frame_block = frame_block;
3032
3033         VariableList *locals = frame->GetVariableList(true);
3034         if (locals) {
3035           const DynamicValueType use_dynamic = eDynamicDontRunTarget;
3036           const size_t num_locals = locals->GetSize();
3037           for (size_t i = 0; i < num_locals; ++i) {
3038             ValueObjectSP value_sp = frame->GetValueObjectForFrameVariable(
3039                 locals->GetVariableAtIndex(i), use_dynamic);
3040             if (value_sp) {
3041               ValueObjectSP synthetic_value_sp = value_sp->GetSyntheticValue();
3042               if (synthetic_value_sp)
3043                 local_values.Append(synthetic_value_sp);
3044               else
3045                 local_values.Append(value_sp);
3046             }
3047           }
3048           // Update the values
3049           SetValues(local_values);
3050         }
3051       }
3052     } else {
3053       m_frame_block = nullptr;
3054       // Update the values with an empty list if there is no frame
3055       SetValues(local_values);
3056     }
3057
3058     return ValueObjectListDelegate::WindowDelegateDraw(window, force);
3059   }
3060
3061 protected:
3062   Debugger &m_debugger;
3063   Block *m_frame_block;
3064 };
3065
3066 class RegistersWindowDelegate : public ValueObjectListDelegate {
3067 public:
3068   RegistersWindowDelegate(Debugger &debugger)
3069       : ValueObjectListDelegate(), m_debugger(debugger) {}
3070
3071   ~RegistersWindowDelegate() override = default;
3072
3073   const char *WindowDelegateGetHelpText() override {
3074     return "Register window keyboard shortcuts:";
3075   }
3076
3077   bool WindowDelegateDraw(Window &window, bool force) override {
3078     ExecutionContext exe_ctx(
3079         m_debugger.GetCommandInterpreter().GetExecutionContext());
3080     StackFrame *frame = exe_ctx.GetFramePtr();
3081
3082     ValueObjectList value_list;
3083     if (frame) {
3084       if (frame->GetStackID() != m_stack_id) {
3085         m_stack_id = frame->GetStackID();
3086         RegisterContextSP reg_ctx(frame->GetRegisterContext());
3087         if (reg_ctx) {
3088           const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
3089           for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) {
3090             value_list.Append(
3091                 ValueObjectRegisterSet::Create(frame, reg_ctx, set_idx));
3092           }
3093         }
3094         SetValues(value_list);
3095       }
3096     } else {
3097       Process *process = exe_ctx.GetProcessPtr();
3098       if (process && process->IsAlive())
3099         return true; // Don't do any updating if we are running
3100       else {
3101         // Update the values with an empty list if there is no process or the
3102         // process isn't alive anymore
3103         SetValues(value_list);
3104       }
3105     }
3106     return ValueObjectListDelegate::WindowDelegateDraw(window, force);
3107   }
3108
3109 protected:
3110   Debugger &m_debugger;
3111   StackID m_stack_id;
3112 };
3113
3114 static const char *CursesKeyToCString(int ch) {
3115   static char g_desc[32];
3116   if (ch >= KEY_F0 && ch < KEY_F0 + 64) {
3117     snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0);
3118     return g_desc;
3119   }
3120   switch (ch) {
3121   case KEY_DOWN:
3122     return "down";
3123   case KEY_UP:
3124     return "up";
3125   case KEY_LEFT:
3126     return "left";
3127   case KEY_RIGHT:
3128     return "right";
3129   case KEY_HOME:
3130     return "home";
3131   case KEY_BACKSPACE:
3132     return "backspace";
3133   case KEY_DL:
3134     return "delete-line";
3135   case KEY_IL:
3136     return "insert-line";
3137   case KEY_DC:
3138     return "delete-char";
3139   case KEY_IC:
3140     return "insert-char";
3141   case KEY_CLEAR:
3142     return "clear";
3143   case KEY_EOS:
3144     return "clear-to-eos";
3145   case KEY_EOL:
3146     return "clear-to-eol";
3147   case KEY_SF:
3148     return "scroll-forward";
3149   case KEY_SR:
3150     return "scroll-backward";
3151   case KEY_NPAGE:
3152     return "page-down";
3153   case KEY_PPAGE:
3154     return "page-up";
3155   case KEY_STAB:
3156     return "set-tab";
3157   case KEY_CTAB:
3158     return "clear-tab";
3159   case KEY_CATAB:
3160     return "clear-all-tabs";
3161   case KEY_ENTER:
3162     return "enter";
3163   case KEY_PRINT:
3164     return "print";
3165   case KEY_LL:
3166     return "lower-left key";
3167   case KEY_A1:
3168     return "upper left of keypad";
3169   case KEY_A3:
3170     return "upper right of keypad";
3171   case KEY_B2:
3172     return "center of keypad";
3173   case KEY_C1:
3174     return "lower left of keypad";
3175   case KEY_C3:
3176     return "lower right of keypad";
3177   case KEY_BTAB:
3178     return "back-tab key";
3179   case KEY_BEG:
3180     return "begin key";
3181   case KEY_CANCEL:
3182     return "cancel key";
3183   case KEY_CLOSE:
3184     return "close key";
3185   case KEY_COMMAND:
3186     return "command key";
3187   case KEY_COPY:
3188     return "copy key";
3189   case KEY_CREATE:
3190     return "create key";
3191   case KEY_END:
3192     return "end key";
3193   case KEY_EXIT:
3194     return "exit key";
3195   case KEY_FIND:
3196     return "find key";
3197   case KEY_HELP:
3198     return "help key";
3199   case KEY_MARK:
3200     return "mark key";
3201   case KEY_MESSAGE:
3202     return "message key";
3203   case KEY_MOVE:
3204     return "move key";
3205   case KEY_NEXT:
3206     return "next key";
3207   case KEY_OPEN:
3208     return "open key";
3209   case KEY_OPTIONS:
3210     return "options key";
3211   case KEY_PREVIOUS:
3212     return "previous key";
3213   case KEY_REDO:
3214     return "redo key";
3215   case KEY_REFERENCE:
3216     return "reference key";
3217   case KEY_REFRESH:
3218     return "refresh key";
3219   case KEY_REPLACE:
3220     return "replace key";
3221   case KEY_RESTART:
3222     return "restart key";
3223   case KEY_RESUME:
3224     return "resume key";
3225   case KEY_SAVE:
3226     return "save key";
3227   case KEY_SBEG:
3228     return "shifted begin key";
3229   case KEY_SCANCEL:
3230     return "shifted cancel key";
3231   case KEY_SCOMMAND:
3232     return "shifted command key";
3233   case KEY_SCOPY:
3234     return "shifted copy key";
3235   case KEY_SCREATE:
3236     return "shifted create key";
3237   case KEY_SDC:
3238     return "shifted delete-character key";
3239   case KEY_SDL:
3240     return "shifted delete-line key";
3241   case KEY_SELECT:
3242     return "select key";
3243   case KEY_SEND:
3244     return "shifted end key";
3245   case KEY_SEOL:
3246     return "shifted clear-to-end-of-line key";
3247   case KEY_SEXIT:
3248     return "shifted exit key";
3249   case KEY_SFIND:
3250     return "shifted find key";
3251   case KEY_SHELP:
3252     return "shifted help key";
3253   case KEY_SHOME:
3254     return "shifted home key";
3255   case KEY_SIC:
3256     return "shifted insert-character key";
3257   case KEY_SLEFT:
3258     return "shifted left-arrow key";
3259   case KEY_SMESSAGE:
3260     return "shifted message key";
3261   case KEY_SMOVE:
3262     return "shifted move key";
3263   case KEY_SNEXT:
3264     return "shifted next key";
3265   case KEY_SOPTIONS:
3266     return "shifted options key";
3267   case KEY_SPREVIOUS:
3268     return "shifted previous key";
3269   case KEY_SPRINT:
3270     return "shifted print key";
3271   case KEY_SREDO:
3272     return "shifted redo key";
3273   case KEY_SREPLACE:
3274     return "shifted replace key";
3275   case KEY_SRIGHT:
3276     return "shifted right-arrow key";
3277   case KEY_SRSUME:
3278     return "shifted resume key";
3279   case KEY_SSAVE:
3280     return "shifted save key";
3281   case KEY_SSUSPEND:
3282     return "shifted suspend key";
3283   case KEY_SUNDO:
3284     return "shifted undo key";
3285   case KEY_SUSPEND:
3286     return "suspend key";
3287   case KEY_UNDO:
3288     return "undo key";
3289   case KEY_MOUSE:
3290     return "Mouse event has occurred";
3291   case KEY_RESIZE:
3292     return "Terminal resize event";
3293 #ifdef KEY_EVENT
3294   case KEY_EVENT:
3295     return "We were interrupted by an event";
3296 #endif
3297   case KEY_RETURN:
3298     return "return";
3299   case ' ':
3300     return "space";
3301   case '\t':
3302     return "tab";
3303   case KEY_ESCAPE:
3304     return "escape";
3305   default:
3306     if (isprint(ch))
3307       snprintf(g_desc, sizeof(g_desc), "%c", ch);
3308     else
3309       snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch);
3310     return g_desc;
3311   }
3312   return nullptr;
3313 }
3314
3315 HelpDialogDelegate::HelpDialogDelegate(const char *text,
3316                                        KeyHelp *key_help_array)
3317     : m_text(), m_first_visible_line(0) {
3318   if (text && text[0]) {
3319     m_text.SplitIntoLines(text);
3320     m_text.AppendString("");
3321   }
3322   if (key_help_array) {
3323     for (KeyHelp *key = key_help_array; key->ch; ++key) {
3324       StreamString key_description;
3325       key_description.Printf("%10s - %s", CursesKeyToCString(key->ch),
3326                              key->description);
3327       m_text.AppendString(key_description.GetString());
3328     }
3329   }
3330 }
3331
3332 HelpDialogDelegate::~HelpDialogDelegate() = default;
3333
3334 bool HelpDialogDelegate::WindowDelegateDraw(Window &window, bool force) {
3335   window.Erase();
3336   const int window_height = window.GetHeight();
3337   int x = 2;
3338   int y = 1;
3339   const int min_y = y;
3340   const int max_y = window_height - 1 - y;
3341   const size_t num_visible_lines = max_y - min_y + 1;
3342   const size_t num_lines = m_text.GetSize();
3343   const char *bottom_message;
3344   if (num_lines <= num_visible_lines)
3345     bottom_message = "Press any key to exit";
3346   else
3347     bottom_message = "Use arrows to scroll, any other key to exit";
3348   window.DrawTitleBox(window.GetName(), bottom_message);
3349   while (y <= max_y) {
3350     window.MoveCursor(x, y);
3351     window.PutCStringTruncated(
3352         m_text.GetStringAtIndex(m_first_visible_line + y - min_y), 1);
3353     ++y;
3354   }
3355   return true;
3356 }
3357
3358 HandleCharResult HelpDialogDelegate::WindowDelegateHandleChar(Window &window,
3359                                                               int key) {
3360   bool done = false;
3361   const size_t num_lines = m_text.GetSize();
3362   const size_t num_visible_lines = window.GetHeight() - 2;
3363
3364   if (num_lines <= num_visible_lines) {
3365     done = true;
3366     // If we have all lines visible and don't need scrolling, then any key
3367     // press will cause us to exit
3368   } else {
3369     switch (key) {
3370     case KEY_UP:
3371       if (m_first_visible_line > 0)
3372         --m_first_visible_line;
3373       break;
3374
3375     case KEY_DOWN:
3376       if (m_first_visible_line + num_visible_lines < num_lines)
3377         ++m_first_visible_line;
3378       break;
3379
3380     case KEY_PPAGE:
3381     case ',':
3382       if (m_first_visible_line > 0) {
3383         if (static_cast<size_t>(m_first_visible_line) >= num_visible_lines)
3384           m_first_visible_line -= num_visible_lines;
3385         else
3386           m_first_visible_line = 0;
3387       }
3388       break;
3389
3390     case KEY_NPAGE:
3391     case '.':
3392       if (m_first_visible_line + num_visible_lines < num_lines) {
3393         m_first_visible_line += num_visible_lines;
3394         if (static_cast<size_t>(m_first_visible_line) > num_lines)
3395           m_first_visible_line = num_lines - num_visible_lines;
3396       }
3397       break;
3398
3399     default:
3400       done = true;
3401       break;
3402     }
3403   }
3404   if (done)
3405     window.GetParent()->RemoveSubWindow(&window);
3406   return eKeyHandled;
3407 }
3408
3409 class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
3410 public:
3411   enum {
3412     eMenuID_LLDB = 1,
3413     eMenuID_LLDBAbout,
3414     eMenuID_LLDBExit,
3415
3416     eMenuID_Target,
3417     eMenuID_TargetCreate,
3418     eMenuID_TargetDelete,
3419
3420     eMenuID_Process,
3421     eMenuID_ProcessAttach,
3422     eMenuID_ProcessDetach,
3423     eMenuID_ProcessLaunch,
3424     eMenuID_ProcessContinue,
3425     eMenuID_ProcessHalt,
3426     eMenuID_ProcessKill,
3427
3428     eMenuID_Thread,
3429     eMenuID_ThreadStepIn,
3430     eMenuID_ThreadStepOver,
3431     eMenuID_ThreadStepOut,
3432
3433     eMenuID_View,
3434     eMenuID_ViewBacktrace,
3435     eMenuID_ViewRegisters,
3436     eMenuID_ViewSource,
3437     eMenuID_ViewVariables,
3438
3439     eMenuID_Help,
3440     eMenuID_HelpGUIHelp
3441   };
3442
3443   ApplicationDelegate(Application &app, Debugger &debugger)
3444       : WindowDelegate(), MenuDelegate(), m_app(app), m_debugger(debugger) {}
3445
3446   ~ApplicationDelegate() override = default;
3447
3448   bool WindowDelegateDraw(Window &window, bool force) override {
3449     return false; // Drawing not handled, let standard window drawing happen
3450   }
3451
3452   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
3453     switch (key) {
3454     case '\t':
3455       window.SelectNextWindowAsActive();
3456       return eKeyHandled;
3457
3458     case 'h':
3459       window.CreateHelpSubwindow();
3460       return eKeyHandled;
3461
3462     case KEY_ESCAPE:
3463       return eQuitApplication;
3464
3465     default:
3466       break;
3467     }
3468     return eKeyNotHandled;
3469   }
3470
3471   const char *WindowDelegateGetHelpText() override {
3472     return "Welcome to the LLDB curses GUI.\n\n"
3473            "Press the TAB key to change the selected view.\n"
3474            "Each view has its own keyboard shortcuts, press 'h' to open a "
3475            "dialog to display them.\n\n"
3476            "Common key bindings for all views:";
3477   }
3478
3479   KeyHelp *WindowDelegateGetKeyHelp() override {
3480     static curses::KeyHelp g_source_view_key_help[] = {
3481         {'\t', "Select next view"},
3482         {'h', "Show help dialog with view specific key bindings"},
3483         {',', "Page up"},
3484         {'.', "Page down"},
3485         {KEY_UP, "Select previous"},
3486         {KEY_DOWN, "Select next"},
3487         {KEY_LEFT, "Unexpand or select parent"},
3488         {KEY_RIGHT, "Expand"},
3489         {KEY_PPAGE, "Page up"},
3490         {KEY_NPAGE, "Page down"},
3491         {'\0', nullptr}};
3492     return g_source_view_key_help;
3493   }
3494
3495   MenuActionResult MenuDelegateAction(Menu &menu) override {
3496     switch (menu.GetIdentifier()) {
3497     case eMenuID_ThreadStepIn: {
3498       ExecutionContext exe_ctx =
3499           m_debugger.GetCommandInterpreter().GetExecutionContext();
3500       if (exe_ctx.HasThreadScope()) {
3501         Process *process = exe_ctx.GetProcessPtr();
3502         if (process && process->IsAlive() &&
3503             StateIsStoppedState(process->GetState(), true))
3504           exe_ctx.GetThreadRef().StepIn(true);
3505       }
3506     }
3507       return MenuActionResult::Handled;
3508
3509     case eMenuID_ThreadStepOut: {
3510       ExecutionContext exe_ctx =
3511           m_debugger.GetCommandInterpreter().GetExecutionContext();
3512       if (exe_ctx.HasThreadScope()) {
3513         Process *process = exe_ctx.GetProcessPtr();
3514         if (process && process->IsAlive() &&
3515             StateIsStoppedState(process->GetState(), true))
3516           exe_ctx.GetThreadRef().StepOut();
3517       }
3518     }
3519       return MenuActionResult::Handled;
3520
3521     case eMenuID_ThreadStepOver: {
3522       ExecutionContext exe_ctx =
3523           m_debugger.GetCommandInterpreter().GetExecutionContext();
3524       if (exe_ctx.HasThreadScope()) {
3525         Process *process = exe_ctx.GetProcessPtr();
3526         if (process && process->IsAlive() &&
3527             StateIsStoppedState(process->GetState(), true))
3528           exe_ctx.GetThreadRef().StepOver(true);
3529       }
3530     }
3531       return MenuActionResult::Handled;
3532
3533     case eMenuID_ProcessContinue: {
3534       ExecutionContext exe_ctx =
3535           m_debugger.GetCommandInterpreter().GetExecutionContext();
3536       if (exe_ctx.HasProcessScope()) {
3537         Process *process = exe_ctx.GetProcessPtr();
3538         if (process && process->IsAlive() &&
3539             StateIsStoppedState(process->GetState(), true))
3540           process->Resume();
3541       }
3542     }
3543       return MenuActionResult::Handled;
3544
3545     case eMenuID_ProcessKill: {
3546       ExecutionContext exe_ctx =
3547           m_debugger.GetCommandInterpreter().GetExecutionContext();
3548       if (exe_ctx.HasProcessScope()) {
3549         Process *process = exe_ctx.GetProcessPtr();
3550         if (process && process->IsAlive())
3551           process->Destroy(false);
3552       }
3553     }
3554       return MenuActionResult::Handled;
3555
3556     case eMenuID_ProcessHalt: {
3557       ExecutionContext exe_ctx =
3558           m_debugger.GetCommandInterpreter().GetExecutionContext();
3559       if (exe_ctx.HasProcessScope()) {
3560         Process *process = exe_ctx.GetProcessPtr();
3561         if (process && process->IsAlive())
3562           process->Halt();
3563       }
3564     }
3565       return MenuActionResult::Handled;
3566
3567     case eMenuID_ProcessDetach: {
3568       ExecutionContext exe_ctx =
3569           m_debugger.GetCommandInterpreter().GetExecutionContext();
3570       if (exe_ctx.HasProcessScope()) {
3571         Process *process = exe_ctx.GetProcessPtr();
3572         if (process && process->IsAlive())
3573           process->Detach(false);
3574       }
3575     }
3576       return MenuActionResult::Handled;
3577
3578     case eMenuID_Process: {
3579       // Populate the menu with all of the threads if the process is stopped
3580       // when the Process menu gets selected and is about to display its
3581       // submenu.
3582       Menus &submenus = menu.GetSubmenus();
3583       ExecutionContext exe_ctx =
3584           m_debugger.GetCommandInterpreter().GetExecutionContext();
3585       Process *process = exe_ctx.GetProcessPtr();
3586       if (process && process->IsAlive() &&
3587           StateIsStoppedState(process->GetState(), true)) {
3588         if (submenus.size() == 7)
3589           menu.AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
3590         else if (submenus.size() > 8)
3591           submenus.erase(submenus.begin() + 8, submenus.end());
3592
3593         ThreadList &threads = process->GetThreadList();
3594         std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
3595         size_t num_threads = threads.GetSize();
3596         for (size_t i = 0; i < num_threads; ++i) {
3597           ThreadSP thread_sp = threads.GetThreadAtIndex(i);
3598           char menu_char = '\0';
3599           if (i < 9)
3600             menu_char = '1' + i;
3601           StreamString thread_menu_title;
3602           thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID());
3603           const char *thread_name = thread_sp->GetName();
3604           if (thread_name && thread_name[0])
3605             thread_menu_title.Printf(" %s", thread_name);
3606           else {
3607             const char *queue_name = thread_sp->GetQueueName();
3608             if (queue_name && queue_name[0])
3609               thread_menu_title.Printf(" %s", queue_name);
3610           }
3611           menu.AddSubmenu(
3612               MenuSP(new Menu(thread_menu_title.GetString().str().c_str(),
3613                               nullptr, menu_char, thread_sp->GetID())));
3614         }
3615       } else if (submenus.size() > 7) {
3616         // Remove the separator and any other thread submenu items that were
3617         // previously added
3618         submenus.erase(submenus.begin() + 7, submenus.end());
3619       }
3620       // Since we are adding and removing items we need to recalculate the name
3621       // lengths
3622       menu.RecalculateNameLengths();
3623     }
3624       return MenuActionResult::Handled;
3625
3626     case eMenuID_ViewVariables: {
3627       WindowSP main_window_sp = m_app.GetMainWindow();
3628       WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
3629       WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
3630       WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
3631       const Rect source_bounds = source_window_sp->GetBounds();
3632
3633       if (variables_window_sp) {
3634         const Rect variables_bounds = variables_window_sp->GetBounds();
3635
3636         main_window_sp->RemoveSubWindow(variables_window_sp.get());
3637
3638         if (registers_window_sp) {
3639           // We have a registers window, so give all the area back to the
3640           // registers window
3641           Rect registers_bounds = variables_bounds;
3642           registers_bounds.size.width = source_bounds.size.width;
3643           registers_window_sp->SetBounds(registers_bounds);
3644         } else {
3645           // We have no registers window showing so give the bottom area back
3646           // to the source view
3647           source_window_sp->Resize(source_bounds.size.width,
3648                                    source_bounds.size.height +
3649                                        variables_bounds.size.height);
3650         }
3651       } else {
3652         Rect new_variables_rect;
3653         if (registers_window_sp) {
3654           // We have a registers window so split the area of the registers
3655           // window into two columns where the left hand side will be the
3656           // variables and the right hand side will be the registers
3657           const Rect variables_bounds = registers_window_sp->GetBounds();
3658           Rect new_registers_rect;
3659           variables_bounds.VerticalSplitPercentage(0.50, new_variables_rect,
3660                                                    new_registers_rect);
3661           registers_window_sp->SetBounds(new_registers_rect);
3662         } else {
3663           // No variables window, grab the bottom part of the source window
3664           Rect new_source_rect;
3665           source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
3666                                                   new_variables_rect);
3667           source_window_sp->SetBounds(new_source_rect);
3668         }
3669         WindowSP new_window_sp = main_window_sp->CreateSubWindow(
3670             "Variables", new_variables_rect, false);
3671         new_window_sp->SetDelegate(
3672             WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
3673       }
3674       touchwin(stdscr);
3675     }
3676       return MenuActionResult::Handled;
3677
3678     case eMenuID_ViewRegisters: {
3679       WindowSP main_window_sp = m_app.GetMainWindow();
3680       WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
3681       WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
3682       WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
3683       const Rect source_bounds = source_window_sp->GetBounds();
3684
3685       if (registers_window_sp) {
3686         if (variables_window_sp) {
3687           const Rect variables_bounds = variables_window_sp->GetBounds();
3688
3689           // We have a variables window, so give all the area back to the
3690           // variables window
3691           variables_window_sp->Resize(variables_bounds.size.width +
3692                                           registers_window_sp->GetWidth(),
3693                                       variables_bounds.size.height);
3694         } else {
3695           // We have no variables window showing so give the bottom area back
3696           // to the source view
3697           source_window_sp->Resize(source_bounds.size.width,
3698                                    source_bounds.size.height +
3699                                        registers_window_sp->GetHeight());
3700         }
3701         main_window_sp->RemoveSubWindow(registers_window_sp.get());
3702       } else {
3703         Rect new_regs_rect;
3704         if (variables_window_sp) {
3705           // We have a variables window, split it into two columns where the
3706           // left hand side will be the variables and the right hand side will
3707           // be the registers
3708           const Rect variables_bounds = variables_window_sp->GetBounds();
3709           Rect new_vars_rect;
3710           variables_bounds.VerticalSplitPercentage(0.50, new_vars_rect,
3711                                                    new_regs_rect);
3712           variables_window_sp->SetBounds(new_vars_rect);
3713         } else {
3714           // No registers window, grab the bottom part of the source window
3715           Rect new_source_rect;
3716           source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
3717                                                   new_regs_rect);
3718           source_window_sp->SetBounds(new_source_rect);
3719         }
3720         WindowSP new_window_sp =
3721             main_window_sp->CreateSubWindow("Registers", new_regs_rect, false);
3722         new_window_sp->SetDelegate(
3723             WindowDelegateSP(new RegistersWindowDelegate(m_debugger)));
3724       }
3725       touchwin(stdscr);
3726     }
3727       return MenuActionResult::Handled;
3728
3729     case eMenuID_HelpGUIHelp:
3730       m_app.GetMainWindow()->CreateHelpSubwindow();
3731       return MenuActionResult::Handled;
3732
3733     default:
3734       break;
3735     }
3736
3737     return MenuActionResult::NotHandled;
3738   }
3739
3740 protected:
3741   Application &m_app;
3742   Debugger &m_debugger;
3743 };
3744
3745 class StatusBarWindowDelegate : public WindowDelegate {
3746 public:
3747   StatusBarWindowDelegate(Debugger &debugger) : m_debugger(debugger) {
3748     FormatEntity::Parse("Thread: ${thread.id%tid}", m_format);
3749   }
3750
3751   ~StatusBarWindowDelegate() override = default;
3752
3753   bool WindowDelegateDraw(Window &window, bool force) override {
3754     ExecutionContext exe_ctx =
3755         m_debugger.GetCommandInterpreter().GetExecutionContext();
3756     Process *process = exe_ctx.GetProcessPtr();
3757     Thread *thread = exe_ctx.GetThreadPtr();
3758     StackFrame *frame = exe_ctx.GetFramePtr();
3759     window.Erase();
3760     window.SetBackground(2);
3761     window.MoveCursor(0, 0);
3762     if (process) {
3763       const StateType state = process->GetState();
3764       window.Printf("Process: %5" PRIu64 " %10s", process->GetID(),
3765                     StateAsCString(state));
3766
3767       if (StateIsStoppedState(state, true)) {
3768         StreamString strm;
3769         if (thread && FormatEntity::Format(m_format, strm, nullptr, &exe_ctx,
3770                                            nullptr, nullptr, false, false)) {
3771           window.MoveCursor(40, 0);
3772           window.PutCStringTruncated(strm.GetString().str().c_str(), 1);
3773         }
3774
3775         window.MoveCursor(60, 0);
3776         if (frame)
3777           window.Printf("Frame: %3u  PC = 0x%16.16" PRIx64,
3778                         frame->GetFrameIndex(),
3779                         frame->GetFrameCodeAddress().GetOpcodeLoadAddress(
3780                             exe_ctx.GetTargetPtr()));
3781       } else if (state == eStateExited) {
3782         const char *exit_desc = process->GetExitDescription();
3783         const int exit_status = process->GetExitStatus();
3784         if (exit_desc && exit_desc[0])
3785           window.Printf(" with status = %i (%s)", exit_status, exit_desc);
3786         else
3787           window.Printf(" with status = %i", exit_status);
3788       }
3789     }
3790     window.DeferredRefresh();
3791     return true;
3792   }
3793
3794 protected:
3795   Debugger &m_debugger;
3796   FormatEntity::Entry m_format;
3797 };
3798
3799 class SourceFileWindowDelegate : public WindowDelegate {
3800 public:
3801   SourceFileWindowDelegate(Debugger &debugger)
3802       : WindowDelegate(), m_debugger(debugger), m_sc(), m_file_sp(),
3803         m_disassembly_scope(nullptr), m_disassembly_sp(), m_disassembly_range(),
3804         m_title(), m_line_width(4), m_selected_line(0), m_pc_line(0),
3805         m_stop_id(0), m_frame_idx(UINT32_MAX), m_first_visible_line(0),
3806         m_min_x(0), m_min_y(0), m_max_x(0), m_max_y(0) {}
3807
3808   ~SourceFileWindowDelegate() override = default;
3809
3810   void Update(const SymbolContext &sc) { m_sc = sc; }
3811
3812   uint32_t NumVisibleLines() const { return m_max_y - m_min_y; }
3813
3814   const char *WindowDelegateGetHelpText() override {
3815     return "Source/Disassembly window keyboard shortcuts:";
3816   }
3817
3818   KeyHelp *WindowDelegateGetKeyHelp() override {
3819     static curses::KeyHelp g_source_view_key_help[] = {
3820         {KEY_RETURN, "Run to selected line with one shot breakpoint"},
3821         {KEY_UP, "Select previous source line"},
3822         {KEY_DOWN, "Select next source line"},
3823         {KEY_PPAGE, "Page up"},
3824         {KEY_NPAGE, "Page down"},
3825         {'b', "Set breakpoint on selected source/disassembly line"},
3826         {'c', "Continue process"},
3827         {'d', "Detach and resume process"},
3828         {'D', "Detach with process suspended"},
3829         {'h', "Show help dialog"},
3830         {'k', "Kill process"},
3831         {'n', "Step over (source line)"},
3832         {'N', "Step over (single instruction)"},
3833         {'o', "Step out"},
3834         {'s', "Step in (source line)"},
3835         {'S', "Step in (single instruction)"},
3836         {',', "Page up"},
3837         {'.', "Page down"},
3838         {'\0', nullptr}};
3839     return g_source_view_key_help;
3840   }
3841
3842   bool WindowDelegateDraw(Window &window, bool force) override {
3843     ExecutionContext exe_ctx =
3844         m_debugger.GetCommandInterpreter().GetExecutionContext();
3845     Process *process = exe_ctx.GetProcessPtr();
3846     Thread *thread = nullptr;
3847
3848     bool update_location = false;
3849     if (process) {
3850       StateType state = process->GetState();
3851       if (StateIsStoppedState(state, true)) {
3852         // We are stopped, so it is ok to
3853         update_location = true;
3854       }
3855     }
3856
3857     m_min_x = 1;
3858     m_min_y = 2;
3859     m_max_x = window.GetMaxX() - 1;
3860     m_max_y = window.GetMaxY() - 1;
3861
3862     const uint32_t num_visible_lines = NumVisibleLines();
3863     StackFrameSP frame_sp;
3864     bool set_selected_line_to_pc = false;
3865
3866     if (update_location) {
3867       const bool process_alive = process ? process->IsAlive() : false;
3868       bool thread_changed = false;
3869       if (process_alive) {
3870         thread = exe_ctx.GetThreadPtr();
3871         if (thread) {
3872           frame_sp = thread->GetSelectedFrame();
3873           auto tid = thread->GetID();
3874           thread_changed = tid != m_tid;
3875           m_tid = tid;
3876         } else {
3877           if (m_tid != LLDB_INVALID_THREAD_ID) {
3878             thread_changed = true;
3879             m_tid = LLDB_INVALID_THREAD_ID;
3880           }
3881         }
3882       }
3883       const uint32_t stop_id = process ? process->GetStopID() : 0;
3884       const bool stop_id_changed = stop_id != m_stop_id;
3885       bool frame_changed = false;
3886       m_stop_id = stop_id;
3887       m_title.Clear();
3888       if (frame_sp) {
3889         m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
3890         if (m_sc.module_sp) {
3891           m_title.Printf(
3892               "%s", m_sc.module_sp->GetFileSpec().GetFilename().GetCString());
3893           ConstString func_name = m_sc.GetFunctionName();
3894           if (func_name)
3895             m_title.Printf("`%s", func_name.GetCString());
3896         }
3897         const uint32_t frame_idx = frame_sp->GetFrameIndex();
3898         frame_changed = frame_idx != m_frame_idx;
3899         m_frame_idx = frame_idx;
3900       } else {
3901         m_sc.Clear(true);
3902         frame_changed = m_frame_idx != UINT32_MAX;
3903         m_frame_idx = UINT32_MAX;
3904       }
3905
3906       const bool context_changed =
3907           thread_changed || frame_changed || stop_id_changed;
3908
3909       if (process_alive) {
3910         if (m_sc.line_entry.IsValid()) {
3911           m_pc_line = m_sc.line_entry.line;
3912           if (m_pc_line != UINT32_MAX)
3913             --m_pc_line; // Convert to zero based line number...
3914           // Update the selected line if the stop ID changed...
3915           if (context_changed)
3916             m_selected_line = m_pc_line;
3917
3918           if (m_file_sp && m_file_sp->FileSpecMatches(m_sc.line_entry.file)) {
3919             // Same file, nothing to do, we should either have the lines or not
3920             // (source file missing)
3921             if (m_selected_line >= static_cast<size_t>(m_first_visible_line)) {
3922               if (m_selected_line >= m_first_visible_line + num_visible_lines)
3923                 m_first_visible_line = m_selected_line - 10;
3924             } else {
3925               if (m_selected_line > 10)
3926                 m_first_visible_line = m_selected_line - 10;
3927               else
3928                 m_first_visible_line = 0;
3929             }
3930           } else {
3931             // File changed, set selected line to the line with the PC
3932             m_selected_line = m_pc_line;
3933             m_file_sp =
3934                 m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file);
3935             if (m_file_sp) {
3936               const size_t num_lines = m_file_sp->GetNumLines();
3937               int m_line_width = 1;
3938               for (size_t n = num_lines; n >= 10; n = n / 10)
3939                 ++m_line_width;
3940
3941               snprintf(m_line_format, sizeof(m_line_format), " %%%iu ",
3942                        m_line_width);
3943               if (num_lines < num_visible_lines ||
3944                   m_selected_line < num_visible_lines)
3945                 m_first_visible_line = 0;
3946               else
3947                 m_first_visible_line = m_selected_line - 10;
3948             }
3949           }
3950         } else {
3951           m_file_sp.reset();
3952         }
3953
3954         if (!m_file_sp || m_file_sp->GetNumLines() == 0) {
3955           // Show disassembly
3956           bool prefer_file_cache = false;
3957           if (m_sc.function) {
3958             if (m_disassembly_scope != m_sc.function) {
3959               m_disassembly_scope = m_sc.function;
3960               m_disassembly_sp = m_sc.function->GetInstructions(
3961                   exe_ctx, nullptr, prefer_file_cache);
3962               if (m_disassembly_sp) {
3963                 set_selected_line_to_pc = true;
3964                 m_disassembly_range = m_sc.function->GetAddressRange();
3965               } else {
3966                 m_disassembly_range.Clear();
3967               }
3968             } else {
3969               set_selected_line_to_pc = context_changed;
3970             }
3971           } else if (m_sc.symbol) {
3972             if (m_disassembly_scope != m_sc.symbol) {
3973               m_disassembly_scope = m_sc.symbol;
3974               m_disassembly_sp = m_sc.symbol->GetInstructions(
3975                   exe_ctx, nullptr, prefer_file_cache);
3976               if (m_disassembly_sp) {
3977                 set_selected_line_to_pc = true;
3978                 m_disassembly_range.GetBaseAddress() =
3979                     m_sc.symbol->GetAddress();
3980                 m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize());
3981               } else {
3982                 m_disassembly_range.Clear();
3983               }
3984             } else {
3985               set_selected_line_to_pc = context_changed;
3986             }
3987           }
3988         }
3989       } else {
3990         m_pc_line = UINT32_MAX;
3991       }
3992     }
3993
3994     const int window_width = window.GetWidth();
3995     window.Erase();
3996     window.DrawTitleBox("Sources");
3997     if (!m_title.GetString().empty()) {
3998       window.AttributeOn(A_REVERSE);
3999       window.MoveCursor(1, 1);
4000       window.PutChar(' ');
4001       window.PutCStringTruncated(m_title.GetString().str().c_str(), 1);
4002       int x = window.GetCursorX();
4003       if (x < window_width - 1) {
4004         window.Printf("%*s", window_width - x - 1, "");
4005       }
4006       window.AttributeOff(A_REVERSE);
4007     }
4008
4009     Target *target = exe_ctx.GetTargetPtr();
4010     const size_t num_source_lines = GetNumSourceLines();
4011     if (num_source_lines > 0) {
4012       // Display source
4013       BreakpointLines bp_lines;
4014       if (target) {
4015         BreakpointList &bp_list = target->GetBreakpointList();
4016         const size_t num_bps = bp_list.GetSize();
4017         for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
4018           BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
4019           const size_t num_bps_locs = bp_sp->GetNumLocations();
4020           for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
4021             BreakpointLocationSP bp_loc_sp =
4022                 bp_sp->GetLocationAtIndex(bp_loc_idx);
4023             LineEntry bp_loc_line_entry;
4024             if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry(
4025                     bp_loc_line_entry)) {
4026               if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file) {
4027                 bp_lines.insert(bp_loc_line_entry.line);
4028               }
4029             }
4030           }
4031         }
4032       }
4033
4034       const attr_t selected_highlight_attr = A_REVERSE;
4035       const attr_t pc_highlight_attr = COLOR_PAIR(1);
4036
4037       for (size_t i = 0; i < num_visible_lines; ++i) {
4038         const uint32_t curr_line = m_first_visible_line + i;
4039         if (curr_line < num_source_lines) {
4040           const int line_y = m_min_y + i;
4041           window.MoveCursor(1, line_y);
4042           const bool is_pc_line = curr_line == m_pc_line;
4043           const bool line_is_selected = m_selected_line == curr_line;
4044           // Highlight the line as the PC line first, then if the selected line
4045           // isn't the same as the PC line, highlight it differently
4046           attr_t highlight_attr = 0;
4047           attr_t bp_attr = 0;
4048           if (is_pc_line)
4049             highlight_attr = pc_highlight_attr;
4050           else if (line_is_selected)
4051             highlight_attr = selected_highlight_attr;
4052
4053           if (bp_lines.find(curr_line + 1) != bp_lines.end())
4054             bp_attr = COLOR_PAIR(2);
4055
4056           if (bp_attr)
4057             window.AttributeOn(bp_attr);
4058
4059           window.Printf(m_line_format, curr_line + 1);
4060
4061           if (bp_attr)
4062             window.AttributeOff(bp_attr);
4063
4064           window.PutChar(ACS_VLINE);
4065           // Mark the line with the PC with a diamond
4066           if (is_pc_line)
4067             window.PutChar(ACS_DIAMOND);
4068           else
4069             window.PutChar(' ');
4070
4071           if (highlight_attr)
4072             window.AttributeOn(highlight_attr);
4073           const uint32_t line_len =
4074               m_file_sp->GetLineLength(curr_line + 1, false);
4075           if (line_len > 0)
4076             window.PutCString(m_file_sp->PeekLineData(curr_line + 1), line_len);
4077
4078           if (is_pc_line && frame_sp &&
4079               frame_sp->GetConcreteFrameIndex() == 0) {
4080             StopInfoSP stop_info_sp;
4081             if (thread)
4082               stop_info_sp = thread->GetStopInfo();
4083             if (stop_info_sp) {
4084               const char *stop_description = stop_info_sp->GetDescription();
4085               if (stop_description && stop_description[0]) {
4086                 size_t stop_description_len = strlen(stop_description);
4087                 int desc_x = window_width - stop_description_len - 16;
4088                 window.Printf("%*s", desc_x - window.GetCursorX(), "");
4089                 // window.MoveCursor(window_width - stop_description_len - 15,
4090                 // line_y);
4091                 window.Printf("<<< Thread %u: %s ", thread->GetIndexID(),
4092                               stop_description);
4093               }
4094             } else {
4095               window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
4096             }
4097           }
4098           if (highlight_attr)
4099             window.AttributeOff(highlight_attr);
4100         } else {
4101           break;
4102         }
4103       }
4104     } else {
4105       size_t num_disassembly_lines = GetNumDisassemblyLines();
4106       if (num_disassembly_lines > 0) {
4107         // Display disassembly
4108         BreakpointAddrs bp_file_addrs;
4109         Target *target = exe_ctx.GetTargetPtr();
4110         if (target) {
4111           BreakpointList &bp_list = target->GetBreakpointList();
4112           const size_t num_bps = bp_list.GetSize();
4113           for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
4114             BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
4115             const size_t num_bps_locs = bp_sp->GetNumLocations();
4116             for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs;
4117                  ++bp_loc_idx) {
4118               BreakpointLocationSP bp_loc_sp =
4119                   bp_sp->GetLocationAtIndex(bp_loc_idx);
4120               LineEntry bp_loc_line_entry;
4121               const lldb::addr_t file_addr =
4122                   bp_loc_sp->GetAddress().GetFileAddress();
4123               if (file_addr != LLDB_INVALID_ADDRESS) {
4124                 if (m_disassembly_range.ContainsFileAddress(file_addr))
4125                   bp_file_addrs.insert(file_addr);
4126               }
4127             }
4128           }
4129         }
4130
4131         const attr_t selected_highlight_attr = A_REVERSE;
4132         const attr_t pc_highlight_attr = COLOR_PAIR(1);
4133
4134         StreamString strm;
4135
4136         InstructionList &insts = m_disassembly_sp->GetInstructionList();
4137         Address pc_address;
4138
4139         if (frame_sp)
4140           pc_address = frame_sp->GetFrameCodeAddress();
4141         const uint32_t pc_idx =
4142             pc_address.IsValid()
4143                 ? insts.GetIndexOfInstructionAtAddress(pc_address)
4144                 : UINT32_MAX;
4145         if (set_selected_line_to_pc) {
4146           m_selected_line = pc_idx;
4147         }
4148
4149         const uint32_t non_visible_pc_offset = (num_visible_lines / 5);
4150         if (static_cast<size_t>(m_first_visible_line) >= num_disassembly_lines)
4151           m_first_visible_line = 0;
4152
4153         if (pc_idx < num_disassembly_lines) {
4154           if (pc_idx < static_cast<uint32_t>(m_first_visible_line) ||
4155               pc_idx >= m_first_visible_line + num_visible_lines)
4156             m_first_visible_line = pc_idx - non_visible_pc_offset;
4157         }
4158
4159         for (size_t i = 0; i < num_visible_lines; ++i) {
4160           const uint32_t inst_idx = m_first_visible_line + i;
4161           Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get();
4162           if (!inst)
4163             break;
4164
4165           const int line_y = m_min_y + i;
4166           window.MoveCursor(1, line_y);
4167           const bool is_pc_line = frame_sp && inst_idx == pc_idx;
4168           const bool line_is_selected = m_selected_line == inst_idx;
4169           // Highlight the line as the PC line first, then if the selected line
4170           // isn't the same as the PC line, highlight it differently
4171           attr_t highlight_attr = 0;
4172           attr_t bp_attr = 0;
4173           if (is_pc_line)
4174             highlight_attr = pc_highlight_attr;
4175           else if (line_is_selected)
4176             highlight_attr = selected_highlight_attr;
4177
4178           if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) !=
4179               bp_file_addrs.end())
4180             bp_attr = COLOR_PAIR(2);
4181
4182           if (bp_attr)
4183             window.AttributeOn(bp_attr);
4184
4185           window.Printf(" 0x%16.16llx ",
4186                         static_cast<unsigned long long>(
4187                             inst->GetAddress().GetLoadAddress(target)));
4188
4189           if (bp_attr)
4190             window.AttributeOff(bp_attr);
4191
4192           window.PutChar(ACS_VLINE);
4193           // Mark the line with the PC with a diamond
4194           if (is_pc_line)
4195             window.PutChar(ACS_DIAMOND);
4196           else
4197             window.PutChar(' ');
4198
4199           if (highlight_attr)
4200             window.AttributeOn(highlight_attr);
4201
4202           const char *mnemonic = inst->GetMnemonic(&exe_ctx);
4203           const char *operands = inst->GetOperands(&exe_ctx);
4204           const char *comment = inst->GetComment(&exe_ctx);
4205
4206           if (mnemonic != nullptr && mnemonic[0] == '\0')
4207             mnemonic = nullptr;
4208           if (operands != nullptr && operands[0] == '\0')
4209             operands = nullptr;
4210           if (comment != nullptr && comment[0] == '\0')
4211             comment = nullptr;
4212
4213           strm.Clear();
4214
4215           if (mnemonic != nullptr && operands != nullptr && comment != nullptr)
4216             strm.Printf("%-8s %-25s ; %s", mnemonic, operands, comment);
4217           else if (mnemonic != nullptr && operands != nullptr)
4218             strm.Printf("%-8s %s", mnemonic, operands);
4219           else if (mnemonic != nullptr)
4220             strm.Printf("%s", mnemonic);
4221
4222           int right_pad = 1;
4223           window.PutCStringTruncated(strm.GetData(), right_pad);
4224
4225           if (is_pc_line && frame_sp &&
4226               frame_sp->GetConcreteFrameIndex() == 0) {
4227             StopInfoSP stop_info_sp;
4228             if (thread)
4229               stop_info_sp = thread->GetStopInfo();
4230             if (stop_info_sp) {
4231               const char *stop_description = stop_info_sp->GetDescription();
4232               if (stop_description && stop_description[0]) {
4233                 size_t stop_description_len = strlen(stop_description);
4234                 int desc_x = window_width - stop_description_len - 16;
4235                 window.Printf("%*s", desc_x - window.GetCursorX(), "");
4236                 // window.MoveCursor(window_width - stop_description_len - 15,
4237                 // line_y);
4238                 window.Printf("<<< Thread %u: %s ", thread->GetIndexID(),
4239                               stop_description);
4240               }
4241             } else {
4242               window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
4243             }
4244           }
4245           if (highlight_attr)
4246             window.AttributeOff(highlight_attr);
4247         }
4248       }
4249     }
4250     window.DeferredRefresh();
4251     return true; // Drawing handled
4252   }
4253
4254   size_t GetNumLines() {
4255     size_t num_lines = GetNumSourceLines();
4256     if (num_lines == 0)
4257       num_lines = GetNumDisassemblyLines();
4258     return num_lines;
4259   }
4260
4261   size_t GetNumSourceLines() const {
4262     if (m_file_sp)
4263       return m_file_sp->GetNumLines();
4264     return 0;
4265   }
4266
4267   size_t GetNumDisassemblyLines() const {
4268     if (m_disassembly_sp)
4269       return m_disassembly_sp->GetInstructionList().GetSize();
4270     return 0;
4271   }
4272
4273   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
4274     const uint32_t num_visible_lines = NumVisibleLines();
4275     const size_t num_lines = GetNumLines();
4276
4277     switch (c) {
4278     case ',':
4279     case KEY_PPAGE:
4280       // Page up key
4281       if (static_cast<uint32_t>(m_first_visible_line) > num_visible_lines)
4282         m_first_visible_line -= num_visible_lines;
4283       else
4284         m_first_visible_line = 0;
4285       m_selected_line = m_first_visible_line;
4286       return eKeyHandled;
4287
4288     case '.':
4289     case KEY_NPAGE:
4290       // Page down key
4291       {
4292         if (m_first_visible_line + num_visible_lines < num_lines)
4293           m_first_visible_line += num_visible_lines;
4294         else if (num_lines < num_visible_lines)
4295           m_first_visible_line = 0;
4296         else
4297           m_first_visible_line = num_lines - num_visible_lines;
4298         m_selected_line = m_first_visible_line;
4299       }
4300       return eKeyHandled;
4301
4302     case KEY_UP:
4303       if (m_selected_line > 0) {
4304         m_selected_line--;
4305         if (static_cast<size_t>(m_first_visible_line) > m_selected_line)
4306           m_first_visible_line = m_selected_line;
4307       }
4308       return eKeyHandled;
4309
4310     case KEY_DOWN:
4311       if (m_selected_line + 1 < num_lines) {
4312         m_selected_line++;
4313         if (m_first_visible_line + num_visible_lines < m_selected_line)
4314           m_first_visible_line++;
4315       }
4316       return eKeyHandled;
4317
4318     case '\r':
4319     case '\n':
4320     case KEY_ENTER:
4321       // Set a breakpoint and run to the line using a one shot breakpoint
4322       if (GetNumSourceLines() > 0) {
4323         ExecutionContext exe_ctx =
4324             m_debugger.GetCommandInterpreter().GetExecutionContext();
4325         if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive()) {
4326           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
4327               nullptr, // Don't limit the breakpoint to certain modules
4328               m_file_sp->GetFileSpec(), // Source file
4329               m_selected_line +
4330                   1, // Source line number (m_selected_line is zero based)
4331               0,     // Unspecified column.
4332               0,     // No offset
4333               eLazyBoolCalculate,  // Check inlines using global setting
4334               eLazyBoolCalculate,  // Skip prologue using global setting,
4335               false,               // internal
4336               false,               // request_hardware
4337               eLazyBoolCalculate); // move_to_nearest_code
4338           // Make breakpoint one shot
4339           bp_sp->GetOptions()->SetOneShot(true);
4340           exe_ctx.GetProcessRef().Resume();
4341         }
4342       } else if (m_selected_line < GetNumDisassemblyLines()) {
4343         const Instruction *inst = m_disassembly_sp->GetInstructionList()
4344                                       .GetInstructionAtIndex(m_selected_line)
4345                                       .get();
4346         ExecutionContext exe_ctx =
4347             m_debugger.GetCommandInterpreter().GetExecutionContext();
4348         if (exe_ctx.HasTargetScope()) {
4349           Address addr = inst->GetAddress();
4350           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
4351               addr,   // lldb_private::Address
4352               false,  // internal
4353               false); // request_hardware
4354           // Make breakpoint one shot
4355           bp_sp->GetOptions()->SetOneShot(true);
4356           exe_ctx.GetProcessRef().Resume();
4357         }
4358       }
4359       return eKeyHandled;
4360
4361     case 'b': // 'b' == toggle breakpoint on currently selected line
4362       if (m_selected_line < GetNumSourceLines()) {
4363         ExecutionContext exe_ctx =
4364             m_debugger.GetCommandInterpreter().GetExecutionContext();
4365         if (exe_ctx.HasTargetScope()) {
4366           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
4367               nullptr, // Don't limit the breakpoint to certain modules
4368               m_file_sp->GetFileSpec(), // Source file
4369               m_selected_line +
4370                   1, // Source line number (m_selected_line is zero based)
4371               0,     // No column specified.
4372               0,     // No offset
4373               eLazyBoolCalculate,  // Check inlines using global setting
4374               eLazyBoolCalculate,  // Skip prologue using global setting,
4375               false,               // internal
4376               false,               // request_hardware
4377               eLazyBoolCalculate); // move_to_nearest_code
4378         }
4379       } else if (m_selected_line < GetNumDisassemblyLines()) {
4380         const Instruction *inst = m_disassembly_sp->GetInstructionList()
4381                                       .GetInstructionAtIndex(m_selected_line)
4382                                       .get();
4383         ExecutionContext exe_ctx =
4384             m_debugger.GetCommandInterpreter().GetExecutionContext();
4385         if (exe_ctx.HasTargetScope()) {
4386           Address addr = inst->GetAddress();
4387           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
4388               addr,   // lldb_private::Address
4389               false,  // internal
4390               false); // request_hardware
4391         }
4392       }
4393       return eKeyHandled;
4394
4395     case 'd': // 'd' == detach and let run
4396     case 'D': // 'D' == detach and keep stopped
4397     {
4398       ExecutionContext exe_ctx =
4399           m_debugger.GetCommandInterpreter().GetExecutionContext();
4400       if (exe_ctx.HasProcessScope())
4401         exe_ctx.GetProcessRef().Detach(c == 'D');
4402     }
4403       return eKeyHandled;
4404
4405     case 'k':
4406       // 'k' == kill
4407       {
4408         ExecutionContext exe_ctx =
4409             m_debugger.GetCommandInterpreter().GetExecutionContext();
4410         if (exe_ctx.HasProcessScope())
4411           exe_ctx.GetProcessRef().Destroy(false);
4412       }
4413       return eKeyHandled;
4414
4415     case 'c':
4416       // 'c' == continue
4417       {
4418         ExecutionContext exe_ctx =
4419             m_debugger.GetCommandInterpreter().GetExecutionContext();
4420         if (exe_ctx.HasProcessScope())
4421           exe_ctx.GetProcessRef().Resume();
4422       }
4423       return eKeyHandled;
4424
4425     case 'o':
4426       // 'o' == step out
4427       {
4428         ExecutionContext exe_ctx =
4429             m_debugger.GetCommandInterpreter().GetExecutionContext();
4430         if (exe_ctx.HasThreadScope() &&
4431             StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
4432           exe_ctx.GetThreadRef().StepOut();
4433         }
4434       }
4435       return eKeyHandled;
4436
4437     case 'n': // 'n' == step over
4438     case 'N': // 'N' == step over instruction
4439     {
4440       ExecutionContext exe_ctx =
4441           m_debugger.GetCommandInterpreter().GetExecutionContext();
4442       if (exe_ctx.HasThreadScope() &&
4443           StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
4444         bool source_step = (c == 'n');
4445         exe_ctx.GetThreadRef().StepOver(source_step);
4446       }
4447     }
4448       return eKeyHandled;
4449
4450     case 's': // 's' == step into
4451     case 'S': // 'S' == step into instruction
4452     {
4453       ExecutionContext exe_ctx =
4454           m_debugger.GetCommandInterpreter().GetExecutionContext();
4455       if (exe_ctx.HasThreadScope() &&
4456           StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
4457         bool source_step = (c == 's');
4458         exe_ctx.GetThreadRef().StepIn(source_step);
4459       }
4460     }
4461       return eKeyHandled;
4462
4463     case 'h':
4464       window.CreateHelpSubwindow();
4465       return eKeyHandled;
4466
4467     default:
4468       break;
4469     }
4470     return eKeyNotHandled;
4471   }
4472
4473 protected:
4474   typedef std::set<uint32_t> BreakpointLines;
4475   typedef std::set<lldb::addr_t> BreakpointAddrs;
4476
4477   Debugger &m_debugger;
4478   SymbolContext m_sc;
4479   SourceManager::FileSP m_file_sp;
4480   SymbolContextScope *m_disassembly_scope;
4481   lldb::DisassemblerSP m_disassembly_sp;
4482   AddressRange m_disassembly_range;
4483   StreamString m_title;
4484   lldb::user_id_t m_tid;
4485   char m_line_format[8];
4486   int m_line_width;
4487   uint32_t m_selected_line; // The selected line
4488   uint32_t m_pc_line;       // The line with the PC
4489   uint32_t m_stop_id;
4490   uint32_t m_frame_idx;
4491   int m_first_visible_line;
4492   int m_min_x;
4493   int m_min_y;
4494   int m_max_x;
4495   int m_max_y;
4496 };
4497
4498 DisplayOptions ValueObjectListDelegate::g_options = {true};
4499
4500 IOHandlerCursesGUI::IOHandlerCursesGUI(Debugger &debugger)
4501     : IOHandler(debugger, IOHandler::Type::Curses) {}
4502
4503 void IOHandlerCursesGUI::Activate() {
4504   IOHandler::Activate();
4505   if (!m_app_ap) {
4506     m_app_ap.reset(new Application(GetInputFILE(), GetOutputFILE()));
4507
4508     // This is both a window and a menu delegate
4509     std::shared_ptr<ApplicationDelegate> app_delegate_sp(
4510         new ApplicationDelegate(*m_app_ap, m_debugger));
4511
4512     MenuDelegateSP app_menu_delegate_sp =
4513         std::static_pointer_cast<MenuDelegate>(app_delegate_sp);
4514     MenuSP lldb_menu_sp(
4515         new Menu("LLDB", "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB));
4516     MenuSP exit_menuitem_sp(
4517         new Menu("Exit", nullptr, 'x', ApplicationDelegate::eMenuID_LLDBExit));
4518     exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit);
4519     lldb_menu_sp->AddSubmenu(MenuSP(new Menu(
4520         "About LLDB", nullptr, 'a', ApplicationDelegate::eMenuID_LLDBAbout)));
4521     lldb_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
4522     lldb_menu_sp->AddSubmenu(exit_menuitem_sp);
4523
4524     MenuSP target_menu_sp(new Menu("Target", "F2", KEY_F(2),
4525                                    ApplicationDelegate::eMenuID_Target));
4526     target_menu_sp->AddSubmenu(MenuSP(new Menu(
4527         "Create", nullptr, 'c', ApplicationDelegate::eMenuID_TargetCreate)));
4528     target_menu_sp->AddSubmenu(MenuSP(new Menu(
4529         "Delete", nullptr, 'd', ApplicationDelegate::eMenuID_TargetDelete)));
4530
4531     MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3),
4532                                     ApplicationDelegate::eMenuID_Process));
4533     process_menu_sp->AddSubmenu(MenuSP(new Menu(
4534         "Attach", nullptr, 'a', ApplicationDelegate::eMenuID_ProcessAttach)));
4535     process_menu_sp->AddSubmenu(MenuSP(new Menu(
4536         "Detach", nullptr, 'd', ApplicationDelegate::eMenuID_ProcessDetach)));
4537     process_menu_sp->AddSubmenu(MenuSP(new Menu(
4538         "Launch", nullptr, 'l', ApplicationDelegate::eMenuID_ProcessLaunch)));
4539     process_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
4540     process_menu_sp->AddSubmenu(
4541         MenuSP(new Menu("Continue", nullptr, 'c',
4542                         ApplicationDelegate::eMenuID_ProcessContinue)));
4543     process_menu_sp->AddSubmenu(MenuSP(new Menu(
4544         "Halt", nullptr, 'h', ApplicationDelegate::eMenuID_ProcessHalt)));
4545     process_menu_sp->AddSubmenu(MenuSP(new Menu(
4546         "Kill", nullptr, 'k', ApplicationDelegate::eMenuID_ProcessKill)));
4547
4548     MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4),
4549                                    ApplicationDelegate::eMenuID_Thread));
4550     thread_menu_sp->AddSubmenu(MenuSP(new Menu(
4551         "Step In", nullptr, 'i', ApplicationDelegate::eMenuID_ThreadStepIn)));
4552     thread_menu_sp->AddSubmenu(
4553         MenuSP(new Menu("Step Over", nullptr, 'v',
4554                         ApplicationDelegate::eMenuID_ThreadStepOver)));
4555     thread_menu_sp->AddSubmenu(MenuSP(new Menu(
4556         "Step Out", nullptr, 'o', ApplicationDelegate::eMenuID_ThreadStepOut)));
4557
4558     MenuSP view_menu_sp(
4559         new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View));
4560     view_menu_sp->AddSubmenu(
4561         MenuSP(new Menu("Backtrace", nullptr, 'b',
4562                         ApplicationDelegate::eMenuID_ViewBacktrace)));
4563     view_menu_sp->AddSubmenu(
4564         MenuSP(new Menu("Registers", nullptr, 'r',
4565                         ApplicationDelegate::eMenuID_ViewRegisters)));
4566     view_menu_sp->AddSubmenu(MenuSP(new Menu(
4567         "Source", nullptr, 's', ApplicationDelegate::eMenuID_ViewSource)));
4568     view_menu_sp->AddSubmenu(
4569         MenuSP(new Menu("Variables", nullptr, 'v',
4570                         ApplicationDelegate::eMenuID_ViewVariables)));
4571
4572     MenuSP help_menu_sp(
4573         new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help));
4574     help_menu_sp->AddSubmenu(MenuSP(new Menu(
4575         "GUI Help", nullptr, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp)));
4576
4577     m_app_ap->Initialize();
4578     WindowSP &main_window_sp = m_app_ap->GetMainWindow();
4579
4580     MenuSP menubar_sp(new Menu(Menu::Type::Bar));
4581     menubar_sp->AddSubmenu(lldb_menu_sp);
4582     menubar_sp->AddSubmenu(target_menu_sp);
4583     menubar_sp->AddSubmenu(process_menu_sp);
4584     menubar_sp->AddSubmenu(thread_menu_sp);
4585     menubar_sp->AddSubmenu(view_menu_sp);
4586     menubar_sp->AddSubmenu(help_menu_sp);
4587     menubar_sp->SetDelegate(app_menu_delegate_sp);
4588
4589     Rect content_bounds = main_window_sp->GetFrame();
4590     Rect menubar_bounds = content_bounds.MakeMenuBar();
4591     Rect status_bounds = content_bounds.MakeStatusBar();
4592     Rect source_bounds;
4593     Rect variables_bounds;
4594     Rect threads_bounds;
4595     Rect source_variables_bounds;
4596     content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds,
4597                                            threads_bounds);
4598     source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds,
4599                                                       variables_bounds);
4600
4601     WindowSP menubar_window_sp =
4602         main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false);
4603     // Let the menubar get keys if the active window doesn't handle the keys
4604     // that are typed so it can respond to menubar key presses.
4605     menubar_window_sp->SetCanBeActive(
4606         false); // Don't let the menubar become the active window
4607     menubar_window_sp->SetDelegate(menubar_sp);
4608
4609     WindowSP source_window_sp(
4610         main_window_sp->CreateSubWindow("Source", source_bounds, true));
4611     WindowSP variables_window_sp(
4612         main_window_sp->CreateSubWindow("Variables", variables_bounds, false));
4613     WindowSP threads_window_sp(
4614         main_window_sp->CreateSubWindow("Threads", threads_bounds, false));
4615     WindowSP status_window_sp(
4616         main_window_sp->CreateSubWindow("Status", status_bounds, false));
4617     status_window_sp->SetCanBeActive(
4618         false); // Don't let the status bar become the active window
4619     main_window_sp->SetDelegate(
4620         std::static_pointer_cast<WindowDelegate>(app_delegate_sp));
4621     source_window_sp->SetDelegate(
4622         WindowDelegateSP(new SourceFileWindowDelegate(m_debugger)));
4623     variables_window_sp->SetDelegate(
4624         WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
4625     TreeDelegateSP thread_delegate_sp(new ThreadsTreeDelegate(m_debugger));
4626     threads_window_sp->SetDelegate(WindowDelegateSP(
4627         new TreeWindowDelegate(m_debugger, thread_delegate_sp)));
4628     status_window_sp->SetDelegate(
4629         WindowDelegateSP(new StatusBarWindowDelegate(m_debugger)));
4630
4631     // Show the main help window once the first time the curses GUI is launched
4632     static bool g_showed_help = false;
4633     if (!g_showed_help) {
4634       g_showed_help = true;
4635       main_window_sp->CreateHelpSubwindow();
4636     }
4637
4638     init_pair(1, COLOR_WHITE, COLOR_BLUE);
4639     init_pair(2, COLOR_BLACK, COLOR_WHITE);
4640     init_pair(3, COLOR_MAGENTA, COLOR_WHITE);
4641     init_pair(4, COLOR_MAGENTA, COLOR_BLACK);
4642     init_pair(5, COLOR_RED, COLOR_BLACK);
4643   }
4644 }
4645
4646 void IOHandlerCursesGUI::Deactivate() { m_app_ap->Terminate(); }
4647
4648 void IOHandlerCursesGUI::Run() {
4649   m_app_ap->Run(m_debugger);
4650   SetIsDone(true);
4651 }
4652
4653 IOHandlerCursesGUI::~IOHandlerCursesGUI() = default;
4654
4655 void IOHandlerCursesGUI::Cancel() {}
4656
4657 bool IOHandlerCursesGUI::Interrupt() { return false; }
4658
4659 void IOHandlerCursesGUI::GotEOF() {}
4660
4661 #endif // LLDB_DISABLE_CURSES