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