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