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     m_update_screen = true;
1201 #if defined(__APPLE__)
1202     std::deque<int> escape_chars;
1203 #endif
1204 
1205     while (!done) {
1206       if (m_update_screen) {
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         m_update_screen = 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                   m_update_screen = 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           m_update_screen = true;
1293           break;
1294         case eKeyNotHandled:
1295           if (ch == 12) { // Ctrl+L, force full redraw
1296             redrawwin(m_window_sp->get());
1297             m_update_screen = 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   void TerminalSizeChanged() {
1317     ::endwin();
1318     ::refresh();
1319     Rect content_bounds = m_window_sp->GetFrame();
1320     m_window_sp->SetBounds(content_bounds);
1321     if (WindowSP menubar_window_sp = m_window_sp->FindSubWindow("Menubar"))
1322       menubar_window_sp->SetBounds(content_bounds.MakeMenuBar());
1323     if (WindowSP status_window_sp = m_window_sp->FindSubWindow("Status"))
1324       status_window_sp->SetBounds(content_bounds.MakeStatusBar());
1325 
1326     WindowSP source_window_sp = m_window_sp->FindSubWindow("Source");
1327     WindowSP variables_window_sp = m_window_sp->FindSubWindow("Variables");
1328     WindowSP registers_window_sp = m_window_sp->FindSubWindow("Registers");
1329     WindowSP threads_window_sp = m_window_sp->FindSubWindow("Threads");
1330 
1331     Rect threads_bounds;
1332     Rect source_variables_bounds;
1333     content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds,
1334                                            threads_bounds);
1335     if (threads_window_sp)
1336       threads_window_sp->SetBounds(threads_bounds);
1337     else
1338       source_variables_bounds = content_bounds;
1339 
1340     Rect source_bounds;
1341     Rect variables_registers_bounds;
1342     source_variables_bounds.HorizontalSplitPercentage(
1343         0.70, source_bounds, variables_registers_bounds);
1344     if (variables_window_sp || registers_window_sp) {
1345       if (variables_window_sp && registers_window_sp) {
1346         Rect variables_bounds;
1347         Rect registers_bounds;
1348         variables_registers_bounds.VerticalSplitPercentage(
1349             0.50, variables_bounds, registers_bounds);
1350         variables_window_sp->SetBounds(variables_bounds);
1351         registers_window_sp->SetBounds(registers_bounds);
1352       } else if (variables_window_sp) {
1353         variables_window_sp->SetBounds(variables_registers_bounds);
1354       } else {
1355         registers_window_sp->SetBounds(variables_registers_bounds);
1356       }
1357     } else {
1358       source_bounds = source_variables_bounds;
1359     }
1360 
1361     source_window_sp->SetBounds(source_bounds);
1362 
1363     touchwin(stdscr);
1364     redrawwin(m_window_sp->get());
1365     m_update_screen = true;
1366   }
1367 
1368 protected:
1369   WindowSP m_window_sp;
1370   WindowDelegates m_window_delegates;
1371   SCREEN *m_screen;
1372   FILE *m_in;
1373   FILE *m_out;
1374   bool m_update_screen = false;
1375 };
1376 
1377 } // namespace curses
1378 
1379 using namespace curses;
1380 
1381 struct Row {
1382   ValueObjectManager value;
1383   Row *parent;
1384   // The process stop ID when the children were calculated.
1385   uint32_t children_stop_id;
1386   int row_idx;
1387   int x;
1388   int y;
1389   bool might_have_children;
1390   bool expanded;
1391   bool calculated_children;
1392   std::vector<Row> children;
1393 
1394   Row(const ValueObjectSP &v, Row *p)
1395       : value(v, lldb::eDynamicDontRunTarget, true), parent(p), row_idx(0),
1396         x(1), y(1), might_have_children(v ? v->MightHaveChildren() : false),
1397         expanded(false), calculated_children(false), children() {}
1398 
1399   size_t GetDepth() const {
1400     if (parent)
1401       return 1 + parent->GetDepth();
1402     return 0;
1403   }
1404 
1405   void Expand() { expanded = true; }
1406 
1407   std::vector<Row> &GetChildren() {
1408     ProcessSP process_sp = value.GetProcessSP();
1409     auto stop_id = process_sp->GetStopID();
1410     if (process_sp && stop_id != children_stop_id) {
1411       children_stop_id = stop_id;
1412       calculated_children = false;
1413     }
1414     if (!calculated_children) {
1415       children.clear();
1416       calculated_children = true;
1417       ValueObjectSP valobj = value.GetSP();
1418       if (valobj) {
1419         const size_t num_children = valobj->GetNumChildren();
1420         for (size_t i = 0; i < num_children; ++i) {
1421           children.push_back(Row(valobj->GetChildAtIndex(i, true), this));
1422         }
1423       }
1424     }
1425     return children;
1426   }
1427 
1428   void Unexpand() {
1429     expanded = false;
1430     calculated_children = false;
1431     children.clear();
1432   }
1433 
1434   void DrawTree(Window &window) {
1435     if (parent)
1436       parent->DrawTreeForChild(window, this, 0);
1437 
1438     if (might_have_children) {
1439       // It we can get UTF8 characters to work we should try to use the
1440       // "symbol" UTF8 string below
1441       //            const char *symbol = "";
1442       //            if (row.expanded)
1443       //                symbol = "\xe2\x96\xbd ";
1444       //            else
1445       //                symbol = "\xe2\x96\xb7 ";
1446       //            window.PutCString (symbol);
1447 
1448       // The ACS_DARROW and ACS_RARROW don't look very nice they are just a 'v'
1449       // or '>' character...
1450       //            if (expanded)
1451       //                window.PutChar (ACS_DARROW);
1452       //            else
1453       //                window.PutChar (ACS_RARROW);
1454       // Since we can't find any good looking right arrow/down arrow symbols,
1455       // just use a diamond...
1456       window.PutChar(ACS_DIAMOND);
1457       window.PutChar(ACS_HLINE);
1458     }
1459   }
1460 
1461   void DrawTreeForChild(Window &window, Row *child, uint32_t reverse_depth) {
1462     if (parent)
1463       parent->DrawTreeForChild(window, this, reverse_depth + 1);
1464 
1465     if (&GetChildren().back() == child) {
1466       // Last child
1467       if (reverse_depth == 0) {
1468         window.PutChar(ACS_LLCORNER);
1469         window.PutChar(ACS_HLINE);
1470       } else {
1471         window.PutChar(' ');
1472         window.PutChar(' ');
1473       }
1474     } else {
1475       if (reverse_depth == 0) {
1476         window.PutChar(ACS_LTEE);
1477         window.PutChar(ACS_HLINE);
1478       } else {
1479         window.PutChar(ACS_VLINE);
1480         window.PutChar(' ');
1481       }
1482     }
1483   }
1484 };
1485 
1486 struct DisplayOptions {
1487   bool show_types;
1488 };
1489 
1490 class TreeItem;
1491 
1492 class TreeDelegate {
1493 public:
1494   TreeDelegate() = default;
1495   virtual ~TreeDelegate() = default;
1496 
1497   virtual void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) = 0;
1498   virtual void TreeDelegateGenerateChildren(TreeItem &item) = 0;
1499   virtual bool TreeDelegateItemSelected(
1500       TreeItem &item) = 0; // Return true if we need to update views
1501 };
1502 
1503 typedef std::shared_ptr<TreeDelegate> TreeDelegateSP;
1504 
1505 class TreeItem {
1506 public:
1507   TreeItem(TreeItem *parent, TreeDelegate &delegate, bool might_have_children)
1508       : m_parent(parent), m_delegate(delegate), m_user_data(nullptr),
1509         m_identifier(0), m_row_idx(-1), m_children(),
1510         m_might_have_children(might_have_children), m_is_expanded(false) {}
1511 
1512   TreeItem &operator=(const TreeItem &rhs) {
1513     if (this != &rhs) {
1514       m_parent = rhs.m_parent;
1515       m_delegate = rhs.m_delegate;
1516       m_user_data = rhs.m_user_data;
1517       m_identifier = rhs.m_identifier;
1518       m_row_idx = rhs.m_row_idx;
1519       m_children = rhs.m_children;
1520       m_might_have_children = rhs.m_might_have_children;
1521       m_is_expanded = rhs.m_is_expanded;
1522     }
1523     return *this;
1524   }
1525 
1526   TreeItem(const TreeItem &) = default;
1527 
1528   size_t GetDepth() const {
1529     if (m_parent)
1530       return 1 + m_parent->GetDepth();
1531     return 0;
1532   }
1533 
1534   int GetRowIndex() const { return m_row_idx; }
1535 
1536   void ClearChildren() { m_children.clear(); }
1537 
1538   void Resize(size_t n, const TreeItem &t) { m_children.resize(n, t); }
1539 
1540   TreeItem &operator[](size_t i) { return m_children[i]; }
1541 
1542   void SetRowIndex(int row_idx) { m_row_idx = row_idx; }
1543 
1544   size_t GetNumChildren() {
1545     m_delegate.TreeDelegateGenerateChildren(*this);
1546     return m_children.size();
1547   }
1548 
1549   void ItemWasSelected() { m_delegate.TreeDelegateItemSelected(*this); }
1550 
1551   void CalculateRowIndexes(int &row_idx) {
1552     SetRowIndex(row_idx);
1553     ++row_idx;
1554 
1555     const bool expanded = IsExpanded();
1556 
1557     // The root item must calculate its children, or we must calculate the
1558     // number of children if the item is expanded
1559     if (m_parent == nullptr || expanded)
1560       GetNumChildren();
1561 
1562     for (auto &item : m_children) {
1563       if (expanded)
1564         item.CalculateRowIndexes(row_idx);
1565       else
1566         item.SetRowIndex(-1);
1567     }
1568   }
1569 
1570   TreeItem *GetParent() { return m_parent; }
1571 
1572   bool IsExpanded() const { return m_is_expanded; }
1573 
1574   void Expand() { m_is_expanded = true; }
1575 
1576   void Unexpand() { m_is_expanded = false; }
1577 
1578   bool Draw(Window &window, const int first_visible_row,
1579             const uint32_t selected_row_idx, int &row_idx, int &num_rows_left) {
1580     if (num_rows_left <= 0)
1581       return false;
1582 
1583     if (m_row_idx >= first_visible_row) {
1584       window.MoveCursor(2, row_idx + 1);
1585 
1586       if (m_parent)
1587         m_parent->DrawTreeForChild(window, this, 0);
1588 
1589       if (m_might_have_children) {
1590         // It we can get UTF8 characters to work we should try to use the
1591         // "symbol" UTF8 string below
1592         //            const char *symbol = "";
1593         //            if (row.expanded)
1594         //                symbol = "\xe2\x96\xbd ";
1595         //            else
1596         //                symbol = "\xe2\x96\xb7 ";
1597         //            window.PutCString (symbol);
1598 
1599         // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
1600         // 'v' or '>' character...
1601         //            if (expanded)
1602         //                window.PutChar (ACS_DARROW);
1603         //            else
1604         //                window.PutChar (ACS_RARROW);
1605         // Since we can't find any good looking right arrow/down arrow symbols,
1606         // just use a diamond...
1607         window.PutChar(ACS_DIAMOND);
1608         window.PutChar(ACS_HLINE);
1609       }
1610       bool highlight = (selected_row_idx == static_cast<size_t>(m_row_idx)) &&
1611                        window.IsActive();
1612 
1613       if (highlight)
1614         window.AttributeOn(A_REVERSE);
1615 
1616       m_delegate.TreeDelegateDrawTreeItem(*this, window);
1617 
1618       if (highlight)
1619         window.AttributeOff(A_REVERSE);
1620       ++row_idx;
1621       --num_rows_left;
1622     }
1623 
1624     if (num_rows_left <= 0)
1625       return false; // We are done drawing...
1626 
1627     if (IsExpanded()) {
1628       for (auto &item : m_children) {
1629         // If we displayed all the rows and item.Draw() returns false we are
1630         // done drawing and can exit this for loop
1631         if (!item.Draw(window, first_visible_row, selected_row_idx, row_idx,
1632                        num_rows_left))
1633           break;
1634       }
1635     }
1636     return num_rows_left >= 0; // Return true if not done drawing yet
1637   }
1638 
1639   void DrawTreeForChild(Window &window, TreeItem *child,
1640                         uint32_t reverse_depth) {
1641     if (m_parent)
1642       m_parent->DrawTreeForChild(window, this, reverse_depth + 1);
1643 
1644     if (&m_children.back() == child) {
1645       // Last child
1646       if (reverse_depth == 0) {
1647         window.PutChar(ACS_LLCORNER);
1648         window.PutChar(ACS_HLINE);
1649       } else {
1650         window.PutChar(' ');
1651         window.PutChar(' ');
1652       }
1653     } else {
1654       if (reverse_depth == 0) {
1655         window.PutChar(ACS_LTEE);
1656         window.PutChar(ACS_HLINE);
1657       } else {
1658         window.PutChar(ACS_VLINE);
1659         window.PutChar(' ');
1660       }
1661     }
1662   }
1663 
1664   TreeItem *GetItemForRowIndex(uint32_t row_idx) {
1665     if (static_cast<uint32_t>(m_row_idx) == row_idx)
1666       return this;
1667     if (m_children.empty())
1668       return nullptr;
1669     if (IsExpanded()) {
1670       for (auto &item : m_children) {
1671         TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx);
1672         if (selected_item_ptr)
1673           return selected_item_ptr;
1674       }
1675     }
1676     return nullptr;
1677   }
1678 
1679   void *GetUserData() const { return m_user_data; }
1680 
1681   void SetUserData(void *user_data) { m_user_data = user_data; }
1682 
1683   uint64_t GetIdentifier() const { return m_identifier; }
1684 
1685   void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
1686 
1687   void SetMightHaveChildren(bool b) { m_might_have_children = b; }
1688 
1689 protected:
1690   TreeItem *m_parent;
1691   TreeDelegate &m_delegate;
1692   void *m_user_data;
1693   uint64_t m_identifier;
1694   int m_row_idx; // Zero based visible row index, -1 if not visible or for the
1695                  // root item
1696   std::vector<TreeItem> m_children;
1697   bool m_might_have_children;
1698   bool m_is_expanded;
1699 };
1700 
1701 class TreeWindowDelegate : public WindowDelegate {
1702 public:
1703   TreeWindowDelegate(Debugger &debugger, const TreeDelegateSP &delegate_sp)
1704       : m_debugger(debugger), m_delegate_sp(delegate_sp),
1705         m_root(nullptr, *delegate_sp, true), m_selected_item(nullptr),
1706         m_num_rows(0), m_selected_row_idx(0), m_first_visible_row(0),
1707         m_min_x(0), m_min_y(0), m_max_x(0), m_max_y(0) {}
1708 
1709   int NumVisibleRows() const { return m_max_y - m_min_y; }
1710 
1711   bool WindowDelegateDraw(Window &window, bool force) override {
1712     ExecutionContext exe_ctx(
1713         m_debugger.GetCommandInterpreter().GetExecutionContext());
1714     Process *process = exe_ctx.GetProcessPtr();
1715 
1716     bool display_content = false;
1717     if (process) {
1718       StateType state = process->GetState();
1719       if (StateIsStoppedState(state, true)) {
1720         // We are stopped, so it is ok to
1721         display_content = true;
1722       } else if (StateIsRunningState(state)) {
1723         return true; // Don't do any updating when we are running
1724       }
1725     }
1726 
1727     m_min_x = 2;
1728     m_min_y = 1;
1729     m_max_x = window.GetWidth() - 1;
1730     m_max_y = window.GetHeight() - 1;
1731 
1732     window.Erase();
1733     window.DrawTitleBox(window.GetName());
1734 
1735     if (display_content) {
1736       const int num_visible_rows = NumVisibleRows();
1737       m_num_rows = 0;
1738       m_root.CalculateRowIndexes(m_num_rows);
1739 
1740       // If we unexpanded while having something selected our total number of
1741       // rows is less than the num visible rows, then make sure we show all the
1742       // rows by setting the first visible row accordingly.
1743       if (m_first_visible_row > 0 && m_num_rows < num_visible_rows)
1744         m_first_visible_row = 0;
1745 
1746       // Make sure the selected row is always visible
1747       if (m_selected_row_idx < m_first_visible_row)
1748         m_first_visible_row = m_selected_row_idx;
1749       else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
1750         m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
1751 
1752       int row_idx = 0;
1753       int num_rows_left = num_visible_rows;
1754       m_root.Draw(window, m_first_visible_row, m_selected_row_idx, row_idx,
1755                   num_rows_left);
1756       // Get the selected row
1757       m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
1758     } else {
1759       m_selected_item = nullptr;
1760     }
1761 
1762     return true; // Drawing handled
1763   }
1764 
1765   const char *WindowDelegateGetHelpText() override {
1766     return "Thread window keyboard shortcuts:";
1767   }
1768 
1769   KeyHelp *WindowDelegateGetKeyHelp() override {
1770     static curses::KeyHelp g_source_view_key_help[] = {
1771         {KEY_UP, "Select previous item"},
1772         {KEY_DOWN, "Select next item"},
1773         {KEY_RIGHT, "Expand the selected item"},
1774         {KEY_LEFT,
1775          "Unexpand the selected item or select parent if not expanded"},
1776         {KEY_PPAGE, "Page up"},
1777         {KEY_NPAGE, "Page down"},
1778         {'h', "Show help dialog"},
1779         {' ', "Toggle item expansion"},
1780         {',', "Page up"},
1781         {'.', "Page down"},
1782         {'\0', nullptr}};
1783     return g_source_view_key_help;
1784   }
1785 
1786   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
1787     switch (c) {
1788     case ',':
1789     case KEY_PPAGE:
1790       // Page up key
1791       if (m_first_visible_row > 0) {
1792         if (m_first_visible_row > m_max_y)
1793           m_first_visible_row -= m_max_y;
1794         else
1795           m_first_visible_row = 0;
1796         m_selected_row_idx = m_first_visible_row;
1797         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
1798         if (m_selected_item)
1799           m_selected_item->ItemWasSelected();
1800       }
1801       return eKeyHandled;
1802 
1803     case '.':
1804     case KEY_NPAGE:
1805       // Page down key
1806       if (m_num_rows > m_max_y) {
1807         if (m_first_visible_row + m_max_y < m_num_rows) {
1808           m_first_visible_row += m_max_y;
1809           m_selected_row_idx = m_first_visible_row;
1810           m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
1811           if (m_selected_item)
1812             m_selected_item->ItemWasSelected();
1813         }
1814       }
1815       return eKeyHandled;
1816 
1817     case KEY_UP:
1818       if (m_selected_row_idx > 0) {
1819         --m_selected_row_idx;
1820         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
1821         if (m_selected_item)
1822           m_selected_item->ItemWasSelected();
1823       }
1824       return eKeyHandled;
1825 
1826     case KEY_DOWN:
1827       if (m_selected_row_idx + 1 < m_num_rows) {
1828         ++m_selected_row_idx;
1829         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
1830         if (m_selected_item)
1831           m_selected_item->ItemWasSelected();
1832       }
1833       return eKeyHandled;
1834 
1835     case KEY_RIGHT:
1836       if (m_selected_item) {
1837         if (!m_selected_item->IsExpanded())
1838           m_selected_item->Expand();
1839       }
1840       return eKeyHandled;
1841 
1842     case KEY_LEFT:
1843       if (m_selected_item) {
1844         if (m_selected_item->IsExpanded())
1845           m_selected_item->Unexpand();
1846         else if (m_selected_item->GetParent()) {
1847           m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex();
1848           m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
1849           if (m_selected_item)
1850             m_selected_item->ItemWasSelected();
1851         }
1852       }
1853       return eKeyHandled;
1854 
1855     case ' ':
1856       // Toggle expansion state when SPACE is pressed
1857       if (m_selected_item) {
1858         if (m_selected_item->IsExpanded())
1859           m_selected_item->Unexpand();
1860         else
1861           m_selected_item->Expand();
1862       }
1863       return eKeyHandled;
1864 
1865     case 'h':
1866       window.CreateHelpSubwindow();
1867       return eKeyHandled;
1868 
1869     default:
1870       break;
1871     }
1872     return eKeyNotHandled;
1873   }
1874 
1875 protected:
1876   Debugger &m_debugger;
1877   TreeDelegateSP m_delegate_sp;
1878   TreeItem m_root;
1879   TreeItem *m_selected_item;
1880   int m_num_rows;
1881   int m_selected_row_idx;
1882   int m_first_visible_row;
1883   int m_min_x;
1884   int m_min_y;
1885   int m_max_x;
1886   int m_max_y;
1887 };
1888 
1889 class FrameTreeDelegate : public TreeDelegate {
1890 public:
1891   FrameTreeDelegate() : TreeDelegate() {
1892     FormatEntity::Parse(
1893         "frame #${frame.index}: {${function.name}${function.pc-offset}}}",
1894         m_format);
1895   }
1896 
1897   ~FrameTreeDelegate() override = default;
1898 
1899   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
1900     Thread *thread = (Thread *)item.GetUserData();
1901     if (thread) {
1902       const uint64_t frame_idx = item.GetIdentifier();
1903       StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_idx);
1904       if (frame_sp) {
1905         StreamString strm;
1906         const SymbolContext &sc =
1907             frame_sp->GetSymbolContext(eSymbolContextEverything);
1908         ExecutionContext exe_ctx(frame_sp);
1909         if (FormatEntity::Format(m_format, strm, &sc, &exe_ctx, nullptr,
1910                                  nullptr, false, false)) {
1911           int right_pad = 1;
1912           window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad);
1913         }
1914       }
1915     }
1916   }
1917 
1918   void TreeDelegateGenerateChildren(TreeItem &item) override {
1919     // No children for frames yet...
1920   }
1921 
1922   bool TreeDelegateItemSelected(TreeItem &item) override {
1923     Thread *thread = (Thread *)item.GetUserData();
1924     if (thread) {
1925       thread->GetProcess()->GetThreadList().SetSelectedThreadByID(
1926           thread->GetID());
1927       const uint64_t frame_idx = item.GetIdentifier();
1928       thread->SetSelectedFrameByIndex(frame_idx);
1929       return true;
1930     }
1931     return false;
1932   }
1933 
1934 protected:
1935   FormatEntity::Entry m_format;
1936 };
1937 
1938 class ThreadTreeDelegate : public TreeDelegate {
1939 public:
1940   ThreadTreeDelegate(Debugger &debugger)
1941       : TreeDelegate(), m_debugger(debugger), m_tid(LLDB_INVALID_THREAD_ID),
1942         m_stop_id(UINT32_MAX) {
1943     FormatEntity::Parse("thread #${thread.index}: tid = ${thread.id}{, stop "
1944                         "reason = ${thread.stop-reason}}",
1945                         m_format);
1946   }
1947 
1948   ~ThreadTreeDelegate() override = default;
1949 
1950   ProcessSP GetProcess() {
1951     return m_debugger.GetCommandInterpreter()
1952         .GetExecutionContext()
1953         .GetProcessSP();
1954   }
1955 
1956   ThreadSP GetThread(const TreeItem &item) {
1957     ProcessSP process_sp = GetProcess();
1958     if (process_sp)
1959       return process_sp->GetThreadList().FindThreadByID(item.GetIdentifier());
1960     return ThreadSP();
1961   }
1962 
1963   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
1964     ThreadSP thread_sp = GetThread(item);
1965     if (thread_sp) {
1966       StreamString strm;
1967       ExecutionContext exe_ctx(thread_sp);
1968       if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
1969                                nullptr, false, false)) {
1970         int right_pad = 1;
1971         window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad);
1972       }
1973     }
1974   }
1975 
1976   void TreeDelegateGenerateChildren(TreeItem &item) override {
1977     ProcessSP process_sp = GetProcess();
1978     if (process_sp && process_sp->IsAlive()) {
1979       StateType state = process_sp->GetState();
1980       if (StateIsStoppedState(state, true)) {
1981         ThreadSP thread_sp = GetThread(item);
1982         if (thread_sp) {
1983           if (m_stop_id == process_sp->GetStopID() &&
1984               thread_sp->GetID() == m_tid)
1985             return; // Children are already up to date
1986           if (!m_frame_delegate_sp) {
1987             // Always expand the thread item the first time we show it
1988             m_frame_delegate_sp = std::make_shared<FrameTreeDelegate>();
1989           }
1990 
1991           m_stop_id = process_sp->GetStopID();
1992           m_tid = thread_sp->GetID();
1993 
1994           TreeItem t(&item, *m_frame_delegate_sp, false);
1995           size_t num_frames = thread_sp->GetStackFrameCount();
1996           item.Resize(num_frames, t);
1997           for (size_t i = 0; i < num_frames; ++i) {
1998             item[i].SetUserData(thread_sp.get());
1999             item[i].SetIdentifier(i);
2000           }
2001         }
2002         return;
2003       }
2004     }
2005     item.ClearChildren();
2006   }
2007 
2008   bool TreeDelegateItemSelected(TreeItem &item) override {
2009     ProcessSP process_sp = GetProcess();
2010     if (process_sp && process_sp->IsAlive()) {
2011       StateType state = process_sp->GetState();
2012       if (StateIsStoppedState(state, true)) {
2013         ThreadSP thread_sp = GetThread(item);
2014         if (thread_sp) {
2015           ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList();
2016           std::lock_guard<std::recursive_mutex> guard(thread_list.GetMutex());
2017           ThreadSP selected_thread_sp = thread_list.GetSelectedThread();
2018           if (selected_thread_sp->GetID() != thread_sp->GetID()) {
2019             thread_list.SetSelectedThreadByID(thread_sp->GetID());
2020             return true;
2021           }
2022         }
2023       }
2024     }
2025     return false;
2026   }
2027 
2028 protected:
2029   Debugger &m_debugger;
2030   std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp;
2031   lldb::user_id_t m_tid;
2032   uint32_t m_stop_id;
2033   FormatEntity::Entry m_format;
2034 };
2035 
2036 class ThreadsTreeDelegate : public TreeDelegate {
2037 public:
2038   ThreadsTreeDelegate(Debugger &debugger)
2039       : TreeDelegate(), m_thread_delegate_sp(), m_debugger(debugger),
2040         m_stop_id(UINT32_MAX) {
2041     FormatEntity::Parse("process ${process.id}{, name = ${process.name}}",
2042                         m_format);
2043   }
2044 
2045   ~ThreadsTreeDelegate() override = default;
2046 
2047   ProcessSP GetProcess() {
2048     return m_debugger.GetCommandInterpreter()
2049         .GetExecutionContext()
2050         .GetProcessSP();
2051   }
2052 
2053   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
2054     ProcessSP process_sp = GetProcess();
2055     if (process_sp && process_sp->IsAlive()) {
2056       StreamString strm;
2057       ExecutionContext exe_ctx(process_sp);
2058       if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
2059                                nullptr, false, false)) {
2060         int right_pad = 1;
2061         window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad);
2062       }
2063     }
2064   }
2065 
2066   void TreeDelegateGenerateChildren(TreeItem &item) override {
2067     ProcessSP process_sp = GetProcess();
2068     if (process_sp && process_sp->IsAlive()) {
2069       StateType state = process_sp->GetState();
2070       if (StateIsStoppedState(state, true)) {
2071         const uint32_t stop_id = process_sp->GetStopID();
2072         if (m_stop_id == stop_id)
2073           return; // Children are already up to date
2074 
2075         m_stop_id = stop_id;
2076 
2077         if (!m_thread_delegate_sp) {
2078           // Always expand the thread item the first time we show it
2079           // item.Expand();
2080           m_thread_delegate_sp =
2081               std::make_shared<ThreadTreeDelegate>(m_debugger);
2082         }
2083 
2084         TreeItem t(&item, *m_thread_delegate_sp, false);
2085         ThreadList &threads = process_sp->GetThreadList();
2086         std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
2087         size_t num_threads = threads.GetSize();
2088         item.Resize(num_threads, t);
2089         for (size_t i = 0; i < num_threads; ++i) {
2090           item[i].SetIdentifier(threads.GetThreadAtIndex(i)->GetID());
2091           item[i].SetMightHaveChildren(true);
2092         }
2093         return;
2094       }
2095     }
2096     item.ClearChildren();
2097   }
2098 
2099   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
2100 
2101 protected:
2102   std::shared_ptr<ThreadTreeDelegate> m_thread_delegate_sp;
2103   Debugger &m_debugger;
2104   uint32_t m_stop_id;
2105   FormatEntity::Entry m_format;
2106 };
2107 
2108 class ValueObjectListDelegate : public WindowDelegate {
2109 public:
2110   ValueObjectListDelegate()
2111       : m_rows(), m_selected_row(nullptr), m_selected_row_idx(0),
2112         m_first_visible_row(0), m_num_rows(0), m_max_x(0), m_max_y(0) {}
2113 
2114   ValueObjectListDelegate(ValueObjectList &valobj_list)
2115       : m_rows(), m_selected_row(nullptr), m_selected_row_idx(0),
2116         m_first_visible_row(0), m_num_rows(0), m_max_x(0), m_max_y(0) {
2117     SetValues(valobj_list);
2118   }
2119 
2120   ~ValueObjectListDelegate() override = default;
2121 
2122   void SetValues(ValueObjectList &valobj_list) {
2123     m_selected_row = nullptr;
2124     m_selected_row_idx = 0;
2125     m_first_visible_row = 0;
2126     m_num_rows = 0;
2127     m_rows.clear();
2128     for (auto &valobj_sp : valobj_list.GetObjects())
2129       m_rows.push_back(Row(valobj_sp, nullptr));
2130   }
2131 
2132   bool WindowDelegateDraw(Window &window, bool force) override {
2133     m_num_rows = 0;
2134     m_min_x = 2;
2135     m_min_y = 1;
2136     m_max_x = window.GetWidth() - 1;
2137     m_max_y = window.GetHeight() - 1;
2138 
2139     window.Erase();
2140     window.DrawTitleBox(window.GetName());
2141 
2142     const int num_visible_rows = NumVisibleRows();
2143     const int num_rows = CalculateTotalNumberRows(m_rows);
2144 
2145     // If we unexpanded while having something selected our total number of
2146     // rows is less than the num visible rows, then make sure we show all the
2147     // rows by setting the first visible row accordingly.
2148     if (m_first_visible_row > 0 && num_rows < num_visible_rows)
2149       m_first_visible_row = 0;
2150 
2151     // Make sure the selected row is always visible
2152     if (m_selected_row_idx < m_first_visible_row)
2153       m_first_visible_row = m_selected_row_idx;
2154     else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
2155       m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
2156 
2157     DisplayRows(window, m_rows, g_options);
2158 
2159     // Get the selected row
2160     m_selected_row = GetRowForRowIndex(m_selected_row_idx);
2161     // Keep the cursor on the selected row so the highlight and the cursor are
2162     // always on the same line
2163     if (m_selected_row)
2164       window.MoveCursor(m_selected_row->x, m_selected_row->y);
2165 
2166     return true; // Drawing handled
2167   }
2168 
2169   KeyHelp *WindowDelegateGetKeyHelp() override {
2170     static curses::KeyHelp g_source_view_key_help[] = {
2171         {KEY_UP, "Select previous item"},
2172         {KEY_DOWN, "Select next item"},
2173         {KEY_RIGHT, "Expand selected item"},
2174         {KEY_LEFT, "Unexpand selected item or select parent if not expanded"},
2175         {KEY_PPAGE, "Page up"},
2176         {KEY_NPAGE, "Page down"},
2177         {'A', "Format as annotated address"},
2178         {'b', "Format as binary"},
2179         {'B', "Format as hex bytes with ASCII"},
2180         {'c', "Format as character"},
2181         {'d', "Format as a signed integer"},
2182         {'D', "Format selected value using the default format for the type"},
2183         {'f', "Format as float"},
2184         {'h', "Show help dialog"},
2185         {'i', "Format as instructions"},
2186         {'o', "Format as octal"},
2187         {'p', "Format as pointer"},
2188         {'s', "Format as C string"},
2189         {'t', "Toggle showing/hiding type names"},
2190         {'u', "Format as an unsigned integer"},
2191         {'x', "Format as hex"},
2192         {'X', "Format as uppercase hex"},
2193         {' ', "Toggle item expansion"},
2194         {',', "Page up"},
2195         {'.', "Page down"},
2196         {'\0', nullptr}};
2197     return g_source_view_key_help;
2198   }
2199 
2200   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
2201     switch (c) {
2202     case 'x':
2203     case 'X':
2204     case 'o':
2205     case 's':
2206     case 'u':
2207     case 'd':
2208     case 'D':
2209     case 'i':
2210     case 'A':
2211     case 'p':
2212     case 'c':
2213     case 'b':
2214     case 'B':
2215     case 'f':
2216       // Change the format for the currently selected item
2217       if (m_selected_row) {
2218         auto valobj_sp = m_selected_row->value.GetSP();
2219         if (valobj_sp)
2220           valobj_sp->SetFormat(FormatForChar(c));
2221       }
2222       return eKeyHandled;
2223 
2224     case 't':
2225       // Toggle showing type names
2226       g_options.show_types = !g_options.show_types;
2227       return eKeyHandled;
2228 
2229     case ',':
2230     case KEY_PPAGE:
2231       // Page up key
2232       if (m_first_visible_row > 0) {
2233         if (static_cast<int>(m_first_visible_row) > m_max_y)
2234           m_first_visible_row -= m_max_y;
2235         else
2236           m_first_visible_row = 0;
2237         m_selected_row_idx = m_first_visible_row;
2238       }
2239       return eKeyHandled;
2240 
2241     case '.':
2242     case KEY_NPAGE:
2243       // Page down key
2244       if (m_num_rows > static_cast<size_t>(m_max_y)) {
2245         if (m_first_visible_row + m_max_y < m_num_rows) {
2246           m_first_visible_row += m_max_y;
2247           m_selected_row_idx = m_first_visible_row;
2248         }
2249       }
2250       return eKeyHandled;
2251 
2252     case KEY_UP:
2253       if (m_selected_row_idx > 0)
2254         --m_selected_row_idx;
2255       return eKeyHandled;
2256 
2257     case KEY_DOWN:
2258       if (m_selected_row_idx + 1 < m_num_rows)
2259         ++m_selected_row_idx;
2260       return eKeyHandled;
2261 
2262     case KEY_RIGHT:
2263       if (m_selected_row) {
2264         if (!m_selected_row->expanded)
2265           m_selected_row->Expand();
2266       }
2267       return eKeyHandled;
2268 
2269     case KEY_LEFT:
2270       if (m_selected_row) {
2271         if (m_selected_row->expanded)
2272           m_selected_row->Unexpand();
2273         else if (m_selected_row->parent)
2274           m_selected_row_idx = m_selected_row->parent->row_idx;
2275       }
2276       return eKeyHandled;
2277 
2278     case ' ':
2279       // Toggle expansion state when SPACE is pressed
2280       if (m_selected_row) {
2281         if (m_selected_row->expanded)
2282           m_selected_row->Unexpand();
2283         else
2284           m_selected_row->Expand();
2285       }
2286       return eKeyHandled;
2287 
2288     case 'h':
2289       window.CreateHelpSubwindow();
2290       return eKeyHandled;
2291 
2292     default:
2293       break;
2294     }
2295     return eKeyNotHandled;
2296   }
2297 
2298 protected:
2299   std::vector<Row> m_rows;
2300   Row *m_selected_row;
2301   uint32_t m_selected_row_idx;
2302   uint32_t m_first_visible_row;
2303   uint32_t m_num_rows;
2304   int m_min_x;
2305   int m_min_y;
2306   int m_max_x;
2307   int m_max_y;
2308 
2309   static Format FormatForChar(int c) {
2310     switch (c) {
2311     case 'x':
2312       return eFormatHex;
2313     case 'X':
2314       return eFormatHexUppercase;
2315     case 'o':
2316       return eFormatOctal;
2317     case 's':
2318       return eFormatCString;
2319     case 'u':
2320       return eFormatUnsigned;
2321     case 'd':
2322       return eFormatDecimal;
2323     case 'D':
2324       return eFormatDefault;
2325     case 'i':
2326       return eFormatInstruction;
2327     case 'A':
2328       return eFormatAddressInfo;
2329     case 'p':
2330       return eFormatPointer;
2331     case 'c':
2332       return eFormatChar;
2333     case 'b':
2334       return eFormatBinary;
2335     case 'B':
2336       return eFormatBytesWithASCII;
2337     case 'f':
2338       return eFormatFloat;
2339     }
2340     return eFormatDefault;
2341   }
2342 
2343   bool DisplayRowObject(Window &window, Row &row, DisplayOptions &options,
2344                         bool highlight, bool last_child) {
2345     ValueObject *valobj = row.value.GetSP().get();
2346 
2347     if (valobj == nullptr)
2348       return false;
2349 
2350     const char *type_name =
2351         options.show_types ? valobj->GetTypeName().GetCString() : nullptr;
2352     const char *name = valobj->GetName().GetCString();
2353     const char *value = valobj->GetValueAsCString();
2354     const char *summary = valobj->GetSummaryAsCString();
2355 
2356     window.MoveCursor(row.x, row.y);
2357 
2358     row.DrawTree(window);
2359 
2360     if (highlight)
2361       window.AttributeOn(A_REVERSE);
2362 
2363     if (type_name && type_name[0])
2364       window.Printf("(%s) ", type_name);
2365 
2366     if (name && name[0])
2367       window.PutCString(name);
2368 
2369     attr_t changd_attr = 0;
2370     if (valobj->GetValueDidChange())
2371       changd_attr = COLOR_PAIR(5) | A_BOLD;
2372 
2373     if (value && value[0]) {
2374       window.PutCString(" = ");
2375       if (changd_attr)
2376         window.AttributeOn(changd_attr);
2377       window.PutCString(value);
2378       if (changd_attr)
2379         window.AttributeOff(changd_attr);
2380     }
2381 
2382     if (summary && summary[0]) {
2383       window.PutChar(' ');
2384       if (changd_attr)
2385         window.AttributeOn(changd_attr);
2386       window.PutCString(summary);
2387       if (changd_attr)
2388         window.AttributeOff(changd_attr);
2389     }
2390 
2391     if (highlight)
2392       window.AttributeOff(A_REVERSE);
2393 
2394     return true;
2395   }
2396 
2397   void DisplayRows(Window &window, std::vector<Row> &rows,
2398                    DisplayOptions &options) {
2399     // >   0x25B7
2400     // \/  0x25BD
2401 
2402     bool window_is_active = window.IsActive();
2403     for (auto &row : rows) {
2404       const bool last_child = row.parent && &rows[rows.size() - 1] == &row;
2405       // Save the row index in each Row structure
2406       row.row_idx = m_num_rows;
2407       if ((m_num_rows >= m_first_visible_row) &&
2408           ((m_num_rows - m_first_visible_row) <
2409            static_cast<size_t>(NumVisibleRows()))) {
2410         row.x = m_min_x;
2411         row.y = m_num_rows - m_first_visible_row + 1;
2412         if (DisplayRowObject(window, row, options,
2413                              window_is_active &&
2414                                  m_num_rows == m_selected_row_idx,
2415                              last_child)) {
2416           ++m_num_rows;
2417         } else {
2418           row.x = 0;
2419           row.y = 0;
2420         }
2421       } else {
2422         row.x = 0;
2423         row.y = 0;
2424         ++m_num_rows;
2425       }
2426 
2427       auto &children = row.GetChildren();
2428       if (row.expanded && !children.empty()) {
2429         DisplayRows(window, children, options);
2430       }
2431     }
2432   }
2433 
2434   int CalculateTotalNumberRows(std::vector<Row> &rows) {
2435     int row_count = 0;
2436     for (auto &row : rows) {
2437       ++row_count;
2438       if (row.expanded)
2439         row_count += CalculateTotalNumberRows(row.GetChildren());
2440     }
2441     return row_count;
2442   }
2443 
2444   static Row *GetRowForRowIndexImpl(std::vector<Row> &rows, size_t &row_index) {
2445     for (auto &row : rows) {
2446       if (row_index == 0)
2447         return &row;
2448       else {
2449         --row_index;
2450         auto &children = row.GetChildren();
2451         if (row.expanded && !children.empty()) {
2452           Row *result = GetRowForRowIndexImpl(children, row_index);
2453           if (result)
2454             return result;
2455         }
2456       }
2457     }
2458     return nullptr;
2459   }
2460 
2461   Row *GetRowForRowIndex(size_t row_index) {
2462     return GetRowForRowIndexImpl(m_rows, row_index);
2463   }
2464 
2465   int NumVisibleRows() const { return m_max_y - m_min_y; }
2466 
2467   static DisplayOptions g_options;
2468 };
2469 
2470 class FrameVariablesWindowDelegate : public ValueObjectListDelegate {
2471 public:
2472   FrameVariablesWindowDelegate(Debugger &debugger)
2473       : ValueObjectListDelegate(), m_debugger(debugger),
2474         m_frame_block(nullptr) {}
2475 
2476   ~FrameVariablesWindowDelegate() override = default;
2477 
2478   const char *WindowDelegateGetHelpText() override {
2479     return "Frame variable window keyboard shortcuts:";
2480   }
2481 
2482   bool WindowDelegateDraw(Window &window, bool force) override {
2483     ExecutionContext exe_ctx(
2484         m_debugger.GetCommandInterpreter().GetExecutionContext());
2485     Process *process = exe_ctx.GetProcessPtr();
2486     Block *frame_block = nullptr;
2487     StackFrame *frame = nullptr;
2488 
2489     if (process) {
2490       StateType state = process->GetState();
2491       if (StateIsStoppedState(state, true)) {
2492         frame = exe_ctx.GetFramePtr();
2493         if (frame)
2494           frame_block = frame->GetFrameBlock();
2495       } else if (StateIsRunningState(state)) {
2496         return true; // Don't do any updating when we are running
2497       }
2498     }
2499 
2500     ValueObjectList local_values;
2501     if (frame_block) {
2502       // Only update the variables if they have changed
2503       if (m_frame_block != frame_block) {
2504         m_frame_block = frame_block;
2505 
2506         VariableList *locals = frame->GetVariableList(true);
2507         if (locals) {
2508           const DynamicValueType use_dynamic = eDynamicDontRunTarget;
2509           for (const VariableSP &local_sp : *locals) {
2510             ValueObjectSP value_sp =
2511                 frame->GetValueObjectForFrameVariable(local_sp, use_dynamic);
2512             if (value_sp) {
2513               ValueObjectSP synthetic_value_sp = value_sp->GetSyntheticValue();
2514               if (synthetic_value_sp)
2515                 local_values.Append(synthetic_value_sp);
2516               else
2517                 local_values.Append(value_sp);
2518             }
2519           }
2520           // Update the values
2521           SetValues(local_values);
2522         }
2523       }
2524     } else {
2525       m_frame_block = nullptr;
2526       // Update the values with an empty list if there is no frame
2527       SetValues(local_values);
2528     }
2529 
2530     return ValueObjectListDelegate::WindowDelegateDraw(window, force);
2531   }
2532 
2533 protected:
2534   Debugger &m_debugger;
2535   Block *m_frame_block;
2536 };
2537 
2538 class RegistersWindowDelegate : public ValueObjectListDelegate {
2539 public:
2540   RegistersWindowDelegate(Debugger &debugger)
2541       : ValueObjectListDelegate(), m_debugger(debugger) {}
2542 
2543   ~RegistersWindowDelegate() override = default;
2544 
2545   const char *WindowDelegateGetHelpText() override {
2546     return "Register window keyboard shortcuts:";
2547   }
2548 
2549   bool WindowDelegateDraw(Window &window, bool force) override {
2550     ExecutionContext exe_ctx(
2551         m_debugger.GetCommandInterpreter().GetExecutionContext());
2552     StackFrame *frame = exe_ctx.GetFramePtr();
2553 
2554     ValueObjectList value_list;
2555     if (frame) {
2556       if (frame->GetStackID() != m_stack_id) {
2557         m_stack_id = frame->GetStackID();
2558         RegisterContextSP reg_ctx(frame->GetRegisterContext());
2559         if (reg_ctx) {
2560           const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
2561           for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) {
2562             value_list.Append(
2563                 ValueObjectRegisterSet::Create(frame, reg_ctx, set_idx));
2564           }
2565         }
2566         SetValues(value_list);
2567       }
2568     } else {
2569       Process *process = exe_ctx.GetProcessPtr();
2570       if (process && process->IsAlive())
2571         return true; // Don't do any updating if we are running
2572       else {
2573         // Update the values with an empty list if there is no process or the
2574         // process isn't alive anymore
2575         SetValues(value_list);
2576       }
2577     }
2578     return ValueObjectListDelegate::WindowDelegateDraw(window, force);
2579   }
2580 
2581 protected:
2582   Debugger &m_debugger;
2583   StackID m_stack_id;
2584 };
2585 
2586 static const char *CursesKeyToCString(int ch) {
2587   static char g_desc[32];
2588   if (ch >= KEY_F0 && ch < KEY_F0 + 64) {
2589     snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0);
2590     return g_desc;
2591   }
2592   switch (ch) {
2593   case KEY_DOWN:
2594     return "down";
2595   case KEY_UP:
2596     return "up";
2597   case KEY_LEFT:
2598     return "left";
2599   case KEY_RIGHT:
2600     return "right";
2601   case KEY_HOME:
2602     return "home";
2603   case KEY_BACKSPACE:
2604     return "backspace";
2605   case KEY_DL:
2606     return "delete-line";
2607   case KEY_IL:
2608     return "insert-line";
2609   case KEY_DC:
2610     return "delete-char";
2611   case KEY_IC:
2612     return "insert-char";
2613   case KEY_CLEAR:
2614     return "clear";
2615   case KEY_EOS:
2616     return "clear-to-eos";
2617   case KEY_EOL:
2618     return "clear-to-eol";
2619   case KEY_SF:
2620     return "scroll-forward";
2621   case KEY_SR:
2622     return "scroll-backward";
2623   case KEY_NPAGE:
2624     return "page-down";
2625   case KEY_PPAGE:
2626     return "page-up";
2627   case KEY_STAB:
2628     return "set-tab";
2629   case KEY_CTAB:
2630     return "clear-tab";
2631   case KEY_CATAB:
2632     return "clear-all-tabs";
2633   case KEY_ENTER:
2634     return "enter";
2635   case KEY_PRINT:
2636     return "print";
2637   case KEY_LL:
2638     return "lower-left key";
2639   case KEY_A1:
2640     return "upper left of keypad";
2641   case KEY_A3:
2642     return "upper right of keypad";
2643   case KEY_B2:
2644     return "center of keypad";
2645   case KEY_C1:
2646     return "lower left of keypad";
2647   case KEY_C3:
2648     return "lower right of keypad";
2649   case KEY_BTAB:
2650     return "back-tab key";
2651   case KEY_BEG:
2652     return "begin key";
2653   case KEY_CANCEL:
2654     return "cancel key";
2655   case KEY_CLOSE:
2656     return "close key";
2657   case KEY_COMMAND:
2658     return "command key";
2659   case KEY_COPY:
2660     return "copy key";
2661   case KEY_CREATE:
2662     return "create key";
2663   case KEY_END:
2664     return "end key";
2665   case KEY_EXIT:
2666     return "exit key";
2667   case KEY_FIND:
2668     return "find key";
2669   case KEY_HELP:
2670     return "help key";
2671   case KEY_MARK:
2672     return "mark key";
2673   case KEY_MESSAGE:
2674     return "message key";
2675   case KEY_MOVE:
2676     return "move key";
2677   case KEY_NEXT:
2678     return "next key";
2679   case KEY_OPEN:
2680     return "open key";
2681   case KEY_OPTIONS:
2682     return "options key";
2683   case KEY_PREVIOUS:
2684     return "previous key";
2685   case KEY_REDO:
2686     return "redo key";
2687   case KEY_REFERENCE:
2688     return "reference key";
2689   case KEY_REFRESH:
2690     return "refresh key";
2691   case KEY_REPLACE:
2692     return "replace key";
2693   case KEY_RESTART:
2694     return "restart key";
2695   case KEY_RESUME:
2696     return "resume key";
2697   case KEY_SAVE:
2698     return "save key";
2699   case KEY_SBEG:
2700     return "shifted begin key";
2701   case KEY_SCANCEL:
2702     return "shifted cancel key";
2703   case KEY_SCOMMAND:
2704     return "shifted command key";
2705   case KEY_SCOPY:
2706     return "shifted copy key";
2707   case KEY_SCREATE:
2708     return "shifted create key";
2709   case KEY_SDC:
2710     return "shifted delete-character key";
2711   case KEY_SDL:
2712     return "shifted delete-line key";
2713   case KEY_SELECT:
2714     return "select key";
2715   case KEY_SEND:
2716     return "shifted end key";
2717   case KEY_SEOL:
2718     return "shifted clear-to-end-of-line key";
2719   case KEY_SEXIT:
2720     return "shifted exit key";
2721   case KEY_SFIND:
2722     return "shifted find key";
2723   case KEY_SHELP:
2724     return "shifted help key";
2725   case KEY_SHOME:
2726     return "shifted home key";
2727   case KEY_SIC:
2728     return "shifted insert-character key";
2729   case KEY_SLEFT:
2730     return "shifted left-arrow key";
2731   case KEY_SMESSAGE:
2732     return "shifted message key";
2733   case KEY_SMOVE:
2734     return "shifted move key";
2735   case KEY_SNEXT:
2736     return "shifted next key";
2737   case KEY_SOPTIONS:
2738     return "shifted options key";
2739   case KEY_SPREVIOUS:
2740     return "shifted previous key";
2741   case KEY_SPRINT:
2742     return "shifted print key";
2743   case KEY_SREDO:
2744     return "shifted redo key";
2745   case KEY_SREPLACE:
2746     return "shifted replace key";
2747   case KEY_SRIGHT:
2748     return "shifted right-arrow key";
2749   case KEY_SRSUME:
2750     return "shifted resume key";
2751   case KEY_SSAVE:
2752     return "shifted save key";
2753   case KEY_SSUSPEND:
2754     return "shifted suspend key";
2755   case KEY_SUNDO:
2756     return "shifted undo key";
2757   case KEY_SUSPEND:
2758     return "suspend key";
2759   case KEY_UNDO:
2760     return "undo key";
2761   case KEY_MOUSE:
2762     return "Mouse event has occurred";
2763   case KEY_RESIZE:
2764     return "Terminal resize event";
2765 #ifdef KEY_EVENT
2766   case KEY_EVENT:
2767     return "We were interrupted by an event";
2768 #endif
2769   case KEY_RETURN:
2770     return "return";
2771   case ' ':
2772     return "space";
2773   case '\t':
2774     return "tab";
2775   case KEY_ESCAPE:
2776     return "escape";
2777   default:
2778     if (llvm::isPrint(ch))
2779       snprintf(g_desc, sizeof(g_desc), "%c", ch);
2780     else
2781       snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch);
2782     return g_desc;
2783   }
2784   return nullptr;
2785 }
2786 
2787 HelpDialogDelegate::HelpDialogDelegate(const char *text,
2788                                        KeyHelp *key_help_array)
2789     : m_text(), m_first_visible_line(0) {
2790   if (text && text[0]) {
2791     m_text.SplitIntoLines(text);
2792     m_text.AppendString("");
2793   }
2794   if (key_help_array) {
2795     for (KeyHelp *key = key_help_array; key->ch; ++key) {
2796       StreamString key_description;
2797       key_description.Printf("%10s - %s", CursesKeyToCString(key->ch),
2798                              key->description);
2799       m_text.AppendString(key_description.GetString());
2800     }
2801   }
2802 }
2803 
2804 HelpDialogDelegate::~HelpDialogDelegate() = default;
2805 
2806 bool HelpDialogDelegate::WindowDelegateDraw(Window &window, bool force) {
2807   window.Erase();
2808   const int window_height = window.GetHeight();
2809   int x = 2;
2810   int y = 1;
2811   const int min_y = y;
2812   const int max_y = window_height - 1 - y;
2813   const size_t num_visible_lines = max_y - min_y + 1;
2814   const size_t num_lines = m_text.GetSize();
2815   const char *bottom_message;
2816   if (num_lines <= num_visible_lines)
2817     bottom_message = "Press any key to exit";
2818   else
2819     bottom_message = "Use arrows to scroll, any other key to exit";
2820   window.DrawTitleBox(window.GetName(), bottom_message);
2821   while (y <= max_y) {
2822     window.MoveCursor(x, y);
2823     window.PutCStringTruncated(
2824         m_text.GetStringAtIndex(m_first_visible_line + y - min_y), 1);
2825     ++y;
2826   }
2827   return true;
2828 }
2829 
2830 HandleCharResult HelpDialogDelegate::WindowDelegateHandleChar(Window &window,
2831                                                               int key) {
2832   bool done = false;
2833   const size_t num_lines = m_text.GetSize();
2834   const size_t num_visible_lines = window.GetHeight() - 2;
2835 
2836   if (num_lines <= num_visible_lines) {
2837     done = true;
2838     // If we have all lines visible and don't need scrolling, then any key
2839     // press will cause us to exit
2840   } else {
2841     switch (key) {
2842     case KEY_UP:
2843       if (m_first_visible_line > 0)
2844         --m_first_visible_line;
2845       break;
2846 
2847     case KEY_DOWN:
2848       if (m_first_visible_line + num_visible_lines < num_lines)
2849         ++m_first_visible_line;
2850       break;
2851 
2852     case KEY_PPAGE:
2853     case ',':
2854       if (m_first_visible_line > 0) {
2855         if (static_cast<size_t>(m_first_visible_line) >= num_visible_lines)
2856           m_first_visible_line -= num_visible_lines;
2857         else
2858           m_first_visible_line = 0;
2859       }
2860       break;
2861 
2862     case KEY_NPAGE:
2863     case '.':
2864       if (m_first_visible_line + num_visible_lines < num_lines) {
2865         m_first_visible_line += num_visible_lines;
2866         if (static_cast<size_t>(m_first_visible_line) > num_lines)
2867           m_first_visible_line = num_lines - num_visible_lines;
2868       }
2869       break;
2870 
2871     default:
2872       done = true;
2873       break;
2874     }
2875   }
2876   if (done)
2877     window.GetParent()->RemoveSubWindow(&window);
2878   return eKeyHandled;
2879 }
2880 
2881 class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
2882 public:
2883   enum {
2884     eMenuID_LLDB = 1,
2885     eMenuID_LLDBAbout,
2886     eMenuID_LLDBExit,
2887 
2888     eMenuID_Target,
2889     eMenuID_TargetCreate,
2890     eMenuID_TargetDelete,
2891 
2892     eMenuID_Process,
2893     eMenuID_ProcessAttach,
2894     eMenuID_ProcessDetachResume,
2895     eMenuID_ProcessDetachSuspended,
2896     eMenuID_ProcessLaunch,
2897     eMenuID_ProcessContinue,
2898     eMenuID_ProcessHalt,
2899     eMenuID_ProcessKill,
2900 
2901     eMenuID_Thread,
2902     eMenuID_ThreadStepIn,
2903     eMenuID_ThreadStepOver,
2904     eMenuID_ThreadStepOut,
2905 
2906     eMenuID_View,
2907     eMenuID_ViewBacktrace,
2908     eMenuID_ViewRegisters,
2909     eMenuID_ViewSource,
2910     eMenuID_ViewVariables,
2911 
2912     eMenuID_Help,
2913     eMenuID_HelpGUIHelp
2914   };
2915 
2916   ApplicationDelegate(Application &app, Debugger &debugger)
2917       : WindowDelegate(), MenuDelegate(), m_app(app), m_debugger(debugger) {}
2918 
2919   ~ApplicationDelegate() override = default;
2920 
2921   bool WindowDelegateDraw(Window &window, bool force) override {
2922     return false; // Drawing not handled, let standard window drawing happen
2923   }
2924 
2925   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
2926     switch (key) {
2927     case '\t':
2928       window.SelectNextWindowAsActive();
2929       return eKeyHandled;
2930 
2931     case 'h':
2932       window.CreateHelpSubwindow();
2933       return eKeyHandled;
2934 
2935     case KEY_ESCAPE:
2936       return eQuitApplication;
2937 
2938     default:
2939       break;
2940     }
2941     return eKeyNotHandled;
2942   }
2943 
2944   const char *WindowDelegateGetHelpText() override {
2945     return "Welcome to the LLDB curses GUI.\n\n"
2946            "Press the TAB key to change the selected view.\n"
2947            "Each view has its own keyboard shortcuts, press 'h' to open a "
2948            "dialog to display them.\n\n"
2949            "Common key bindings for all views:";
2950   }
2951 
2952   KeyHelp *WindowDelegateGetKeyHelp() override {
2953     static curses::KeyHelp g_source_view_key_help[] = {
2954         {'\t', "Select next view"},
2955         {'h', "Show help dialog with view specific key bindings"},
2956         {',', "Page up"},
2957         {'.', "Page down"},
2958         {KEY_UP, "Select previous"},
2959         {KEY_DOWN, "Select next"},
2960         {KEY_LEFT, "Unexpand or select parent"},
2961         {KEY_RIGHT, "Expand"},
2962         {KEY_PPAGE, "Page up"},
2963         {KEY_NPAGE, "Page down"},
2964         {'\0', nullptr}};
2965     return g_source_view_key_help;
2966   }
2967 
2968   MenuActionResult MenuDelegateAction(Menu &menu) override {
2969     switch (menu.GetIdentifier()) {
2970     case eMenuID_ThreadStepIn: {
2971       ExecutionContext exe_ctx =
2972           m_debugger.GetCommandInterpreter().GetExecutionContext();
2973       if (exe_ctx.HasThreadScope()) {
2974         Process *process = exe_ctx.GetProcessPtr();
2975         if (process && process->IsAlive() &&
2976             StateIsStoppedState(process->GetState(), true))
2977           exe_ctx.GetThreadRef().StepIn(true);
2978       }
2979     }
2980       return MenuActionResult::Handled;
2981 
2982     case eMenuID_ThreadStepOut: {
2983       ExecutionContext exe_ctx =
2984           m_debugger.GetCommandInterpreter().GetExecutionContext();
2985       if (exe_ctx.HasThreadScope()) {
2986         Process *process = exe_ctx.GetProcessPtr();
2987         if (process && process->IsAlive() &&
2988             StateIsStoppedState(process->GetState(), true))
2989           exe_ctx.GetThreadRef().StepOut();
2990       }
2991     }
2992       return MenuActionResult::Handled;
2993 
2994     case eMenuID_ThreadStepOver: {
2995       ExecutionContext exe_ctx =
2996           m_debugger.GetCommandInterpreter().GetExecutionContext();
2997       if (exe_ctx.HasThreadScope()) {
2998         Process *process = exe_ctx.GetProcessPtr();
2999         if (process && process->IsAlive() &&
3000             StateIsStoppedState(process->GetState(), true))
3001           exe_ctx.GetThreadRef().StepOver(true);
3002       }
3003     }
3004       return MenuActionResult::Handled;
3005 
3006     case eMenuID_ProcessContinue: {
3007       ExecutionContext exe_ctx =
3008           m_debugger.GetCommandInterpreter().GetExecutionContext();
3009       if (exe_ctx.HasProcessScope()) {
3010         Process *process = exe_ctx.GetProcessPtr();
3011         if (process && process->IsAlive() &&
3012             StateIsStoppedState(process->GetState(), true))
3013           process->Resume();
3014       }
3015     }
3016       return MenuActionResult::Handled;
3017 
3018     case eMenuID_ProcessKill: {
3019       ExecutionContext exe_ctx =
3020           m_debugger.GetCommandInterpreter().GetExecutionContext();
3021       if (exe_ctx.HasProcessScope()) {
3022         Process *process = exe_ctx.GetProcessPtr();
3023         if (process && process->IsAlive())
3024           process->Destroy(false);
3025       }
3026     }
3027       return MenuActionResult::Handled;
3028 
3029     case eMenuID_ProcessHalt: {
3030       ExecutionContext exe_ctx =
3031           m_debugger.GetCommandInterpreter().GetExecutionContext();
3032       if (exe_ctx.HasProcessScope()) {
3033         Process *process = exe_ctx.GetProcessPtr();
3034         if (process && process->IsAlive())
3035           process->Halt();
3036       }
3037     }
3038       return MenuActionResult::Handled;
3039 
3040     case eMenuID_ProcessDetachResume:
3041     case eMenuID_ProcessDetachSuspended: {
3042       ExecutionContext exe_ctx =
3043           m_debugger.GetCommandInterpreter().GetExecutionContext();
3044       if (exe_ctx.HasProcessScope()) {
3045         Process *process = exe_ctx.GetProcessPtr();
3046         if (process && process->IsAlive())
3047           process->Detach(menu.GetIdentifier() ==
3048                           eMenuID_ProcessDetachSuspended);
3049       }
3050     }
3051       return MenuActionResult::Handled;
3052 
3053     case eMenuID_Process: {
3054       // Populate the menu with all of the threads if the process is stopped
3055       // when the Process menu gets selected and is about to display its
3056       // submenu.
3057       Menus &submenus = menu.GetSubmenus();
3058       ExecutionContext exe_ctx =
3059           m_debugger.GetCommandInterpreter().GetExecutionContext();
3060       Process *process = exe_ctx.GetProcessPtr();
3061       if (process && process->IsAlive() &&
3062           StateIsStoppedState(process->GetState(), true)) {
3063         if (submenus.size() == 7)
3064           menu.AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
3065         else if (submenus.size() > 8)
3066           submenus.erase(submenus.begin() + 8, submenus.end());
3067 
3068         ThreadList &threads = process->GetThreadList();
3069         std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
3070         size_t num_threads = threads.GetSize();
3071         for (size_t i = 0; i < num_threads; ++i) {
3072           ThreadSP thread_sp = threads.GetThreadAtIndex(i);
3073           char menu_char = '\0';
3074           if (i < 9)
3075             menu_char = '1' + i;
3076           StreamString thread_menu_title;
3077           thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID());
3078           const char *thread_name = thread_sp->GetName();
3079           if (thread_name && thread_name[0])
3080             thread_menu_title.Printf(" %s", thread_name);
3081           else {
3082             const char *queue_name = thread_sp->GetQueueName();
3083             if (queue_name && queue_name[0])
3084               thread_menu_title.Printf(" %s", queue_name);
3085           }
3086           menu.AddSubmenu(
3087               MenuSP(new Menu(thread_menu_title.GetString().str().c_str(),
3088                               nullptr, menu_char, thread_sp->GetID())));
3089         }
3090       } else if (submenus.size() > 7) {
3091         // Remove the separator and any other thread submenu items that were
3092         // previously added
3093         submenus.erase(submenus.begin() + 7, submenus.end());
3094       }
3095       // Since we are adding and removing items we need to recalculate the name
3096       // lengths
3097       menu.RecalculateNameLengths();
3098     }
3099       return MenuActionResult::Handled;
3100 
3101     case eMenuID_ViewVariables: {
3102       WindowSP main_window_sp = m_app.GetMainWindow();
3103       WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
3104       WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
3105       WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
3106       const Rect source_bounds = source_window_sp->GetBounds();
3107 
3108       if (variables_window_sp) {
3109         const Rect variables_bounds = variables_window_sp->GetBounds();
3110 
3111         main_window_sp->RemoveSubWindow(variables_window_sp.get());
3112 
3113         if (registers_window_sp) {
3114           // We have a registers window, so give all the area back to the
3115           // registers window
3116           Rect registers_bounds = variables_bounds;
3117           registers_bounds.size.width = source_bounds.size.width;
3118           registers_window_sp->SetBounds(registers_bounds);
3119         } else {
3120           // We have no registers window showing so give the bottom area back
3121           // to the source view
3122           source_window_sp->Resize(source_bounds.size.width,
3123                                    source_bounds.size.height +
3124                                        variables_bounds.size.height);
3125         }
3126       } else {
3127         Rect new_variables_rect;
3128         if (registers_window_sp) {
3129           // We have a registers window so split the area of the registers
3130           // window into two columns where the left hand side will be the
3131           // variables and the right hand side will be the registers
3132           const Rect variables_bounds = registers_window_sp->GetBounds();
3133           Rect new_registers_rect;
3134           variables_bounds.VerticalSplitPercentage(0.50, new_variables_rect,
3135                                                    new_registers_rect);
3136           registers_window_sp->SetBounds(new_registers_rect);
3137         } else {
3138           // No registers window, grab the bottom part of the source window
3139           Rect new_source_rect;
3140           source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
3141                                                   new_variables_rect);
3142           source_window_sp->SetBounds(new_source_rect);
3143         }
3144         WindowSP new_window_sp = main_window_sp->CreateSubWindow(
3145             "Variables", new_variables_rect, false);
3146         new_window_sp->SetDelegate(
3147             WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
3148       }
3149       touchwin(stdscr);
3150     }
3151       return MenuActionResult::Handled;
3152 
3153     case eMenuID_ViewRegisters: {
3154       WindowSP main_window_sp = m_app.GetMainWindow();
3155       WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
3156       WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
3157       WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
3158       const Rect source_bounds = source_window_sp->GetBounds();
3159 
3160       if (registers_window_sp) {
3161         if (variables_window_sp) {
3162           const Rect variables_bounds = variables_window_sp->GetBounds();
3163 
3164           // We have a variables window, so give all the area back to the
3165           // variables window
3166           variables_window_sp->Resize(variables_bounds.size.width +
3167                                           registers_window_sp->GetWidth(),
3168                                       variables_bounds.size.height);
3169         } else {
3170           // We have no variables window showing so give the bottom area back
3171           // to the source view
3172           source_window_sp->Resize(source_bounds.size.width,
3173                                    source_bounds.size.height +
3174                                        registers_window_sp->GetHeight());
3175         }
3176         main_window_sp->RemoveSubWindow(registers_window_sp.get());
3177       } else {
3178         Rect new_regs_rect;
3179         if (variables_window_sp) {
3180           // We have a variables window, split it into two columns where the
3181           // left hand side will be the variables and the right hand side will
3182           // be the registers
3183           const Rect variables_bounds = variables_window_sp->GetBounds();
3184           Rect new_vars_rect;
3185           variables_bounds.VerticalSplitPercentage(0.50, new_vars_rect,
3186                                                    new_regs_rect);
3187           variables_window_sp->SetBounds(new_vars_rect);
3188         } else {
3189           // No variables window, grab the bottom part of the source window
3190           Rect new_source_rect;
3191           source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
3192                                                   new_regs_rect);
3193           source_window_sp->SetBounds(new_source_rect);
3194         }
3195         WindowSP new_window_sp =
3196             main_window_sp->CreateSubWindow("Registers", new_regs_rect, false);
3197         new_window_sp->SetDelegate(
3198             WindowDelegateSP(new RegistersWindowDelegate(m_debugger)));
3199       }
3200       touchwin(stdscr);
3201     }
3202       return MenuActionResult::Handled;
3203 
3204     case eMenuID_HelpGUIHelp:
3205       m_app.GetMainWindow()->CreateHelpSubwindow();
3206       return MenuActionResult::Handled;
3207 
3208     default:
3209       break;
3210     }
3211 
3212     return MenuActionResult::NotHandled;
3213   }
3214 
3215 protected:
3216   Application &m_app;
3217   Debugger &m_debugger;
3218 };
3219 
3220 class StatusBarWindowDelegate : public WindowDelegate {
3221 public:
3222   StatusBarWindowDelegate(Debugger &debugger) : m_debugger(debugger) {
3223     FormatEntity::Parse("Thread: ${thread.id%tid}", m_format);
3224   }
3225 
3226   ~StatusBarWindowDelegate() override = default;
3227 
3228   bool WindowDelegateDraw(Window &window, bool force) override {
3229     ExecutionContext exe_ctx =
3230         m_debugger.GetCommandInterpreter().GetExecutionContext();
3231     Process *process = exe_ctx.GetProcessPtr();
3232     Thread *thread = exe_ctx.GetThreadPtr();
3233     StackFrame *frame = exe_ctx.GetFramePtr();
3234     window.Erase();
3235     window.SetBackground(2);
3236     window.MoveCursor(0, 0);
3237     if (process) {
3238       const StateType state = process->GetState();
3239       window.Printf("Process: %5" PRIu64 " %10s", process->GetID(),
3240                     StateAsCString(state));
3241 
3242       if (StateIsStoppedState(state, true)) {
3243         StreamString strm;
3244         if (thread && FormatEntity::Format(m_format, strm, nullptr, &exe_ctx,
3245                                            nullptr, nullptr, false, false)) {
3246           window.MoveCursor(40, 0);
3247           window.PutCStringTruncated(strm.GetString().str().c_str(), 1);
3248         }
3249 
3250         window.MoveCursor(60, 0);
3251         if (frame)
3252           window.Printf("Frame: %3u  PC = 0x%16.16" PRIx64,
3253                         frame->GetFrameIndex(),
3254                         frame->GetFrameCodeAddress().GetOpcodeLoadAddress(
3255                             exe_ctx.GetTargetPtr()));
3256       } else if (state == eStateExited) {
3257         const char *exit_desc = process->GetExitDescription();
3258         const int exit_status = process->GetExitStatus();
3259         if (exit_desc && exit_desc[0])
3260           window.Printf(" with status = %i (%s)", exit_status, exit_desc);
3261         else
3262           window.Printf(" with status = %i", exit_status);
3263       }
3264     }
3265     return true;
3266   }
3267 
3268 protected:
3269   Debugger &m_debugger;
3270   FormatEntity::Entry m_format;
3271 };
3272 
3273 class SourceFileWindowDelegate : public WindowDelegate {
3274 public:
3275   SourceFileWindowDelegate(Debugger &debugger)
3276       : WindowDelegate(), m_debugger(debugger), m_sc(), m_file_sp(),
3277         m_disassembly_scope(nullptr), m_disassembly_sp(), m_disassembly_range(),
3278         m_title(), m_line_width(4), m_selected_line(0), m_pc_line(0),
3279         m_stop_id(0), m_frame_idx(UINT32_MAX), m_first_visible_line(0),
3280         m_min_x(0), m_min_y(0), m_max_x(0), m_max_y(0) {}
3281 
3282   ~SourceFileWindowDelegate() override = default;
3283 
3284   void Update(const SymbolContext &sc) { m_sc = sc; }
3285 
3286   uint32_t NumVisibleLines() const { return m_max_y - m_min_y; }
3287 
3288   const char *WindowDelegateGetHelpText() override {
3289     return "Source/Disassembly window keyboard shortcuts:";
3290   }
3291 
3292   KeyHelp *WindowDelegateGetKeyHelp() override {
3293     static curses::KeyHelp g_source_view_key_help[] = {
3294         {KEY_RETURN, "Run to selected line with one shot breakpoint"},
3295         {KEY_UP, "Select previous source line"},
3296         {KEY_DOWN, "Select next source line"},
3297         {KEY_PPAGE, "Page up"},
3298         {KEY_NPAGE, "Page down"},
3299         {'b', "Set breakpoint on selected source/disassembly line"},
3300         {'c', "Continue process"},
3301         {'D', "Detach with process suspended"},
3302         {'h', "Show help dialog"},
3303         {'n', "Step over (source line)"},
3304         {'N', "Step over (single instruction)"},
3305         {'f', "Step out (finish)"},
3306         {'s', "Step in (source line)"},
3307         {'S', "Step in (single instruction)"},
3308         {'u', "Frame up"},
3309         {'d', "Frame down"},
3310         {',', "Page up"},
3311         {'.', "Page down"},
3312         {'\0', nullptr}};
3313     return g_source_view_key_help;
3314   }
3315 
3316   bool WindowDelegateDraw(Window &window, bool force) override {
3317     ExecutionContext exe_ctx =
3318         m_debugger.GetCommandInterpreter().GetExecutionContext();
3319     Process *process = exe_ctx.GetProcessPtr();
3320     Thread *thread = nullptr;
3321 
3322     bool update_location = false;
3323     if (process) {
3324       StateType state = process->GetState();
3325       if (StateIsStoppedState(state, true)) {
3326         // We are stopped, so it is ok to
3327         update_location = true;
3328       }
3329     }
3330 
3331     m_min_x = 1;
3332     m_min_y = 2;
3333     m_max_x = window.GetMaxX() - 1;
3334     m_max_y = window.GetMaxY() - 1;
3335 
3336     const uint32_t num_visible_lines = NumVisibleLines();
3337     StackFrameSP frame_sp;
3338     bool set_selected_line_to_pc = false;
3339 
3340     if (update_location) {
3341       const bool process_alive = process ? process->IsAlive() : false;
3342       bool thread_changed = false;
3343       if (process_alive) {
3344         thread = exe_ctx.GetThreadPtr();
3345         if (thread) {
3346           frame_sp = thread->GetSelectedFrame();
3347           auto tid = thread->GetID();
3348           thread_changed = tid != m_tid;
3349           m_tid = tid;
3350         } else {
3351           if (m_tid != LLDB_INVALID_THREAD_ID) {
3352             thread_changed = true;
3353             m_tid = LLDB_INVALID_THREAD_ID;
3354           }
3355         }
3356       }
3357       const uint32_t stop_id = process ? process->GetStopID() : 0;
3358       const bool stop_id_changed = stop_id != m_stop_id;
3359       bool frame_changed = false;
3360       m_stop_id = stop_id;
3361       m_title.Clear();
3362       if (frame_sp) {
3363         m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
3364         if (m_sc.module_sp) {
3365           m_title.Printf(
3366               "%s", m_sc.module_sp->GetFileSpec().GetFilename().GetCString());
3367           ConstString func_name = m_sc.GetFunctionName();
3368           if (func_name)
3369             m_title.Printf("`%s", func_name.GetCString());
3370         }
3371         const uint32_t frame_idx = frame_sp->GetFrameIndex();
3372         frame_changed = frame_idx != m_frame_idx;
3373         m_frame_idx = frame_idx;
3374       } else {
3375         m_sc.Clear(true);
3376         frame_changed = m_frame_idx != UINT32_MAX;
3377         m_frame_idx = UINT32_MAX;
3378       }
3379 
3380       const bool context_changed =
3381           thread_changed || frame_changed || stop_id_changed;
3382 
3383       if (process_alive) {
3384         if (m_sc.line_entry.IsValid()) {
3385           m_pc_line = m_sc.line_entry.line;
3386           if (m_pc_line != UINT32_MAX)
3387             --m_pc_line; // Convert to zero based line number...
3388           // Update the selected line if the stop ID changed...
3389           if (context_changed)
3390             m_selected_line = m_pc_line;
3391 
3392           if (m_file_sp && m_file_sp->GetFileSpec() == m_sc.line_entry.file) {
3393             // Same file, nothing to do, we should either have the lines or not
3394             // (source file missing)
3395             if (m_selected_line >= static_cast<size_t>(m_first_visible_line)) {
3396               if (m_selected_line >= m_first_visible_line + num_visible_lines)
3397                 m_first_visible_line = m_selected_line - 10;
3398             } else {
3399               if (m_selected_line > 10)
3400                 m_first_visible_line = m_selected_line - 10;
3401               else
3402                 m_first_visible_line = 0;
3403             }
3404           } else {
3405             // File changed, set selected line to the line with the PC
3406             m_selected_line = m_pc_line;
3407             m_file_sp =
3408                 m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file);
3409             if (m_file_sp) {
3410               const size_t num_lines = m_file_sp->GetNumLines();
3411               m_line_width = 1;
3412               for (size_t n = num_lines; n >= 10; n = n / 10)
3413                 ++m_line_width;
3414 
3415               if (num_lines < num_visible_lines ||
3416                   m_selected_line < num_visible_lines)
3417                 m_first_visible_line = 0;
3418               else
3419                 m_first_visible_line = m_selected_line - 10;
3420             }
3421           }
3422         } else {
3423           m_file_sp.reset();
3424         }
3425 
3426         if (!m_file_sp || m_file_sp->GetNumLines() == 0) {
3427           // Show disassembly
3428           bool prefer_file_cache = false;
3429           if (m_sc.function) {
3430             if (m_disassembly_scope != m_sc.function) {
3431               m_disassembly_scope = m_sc.function;
3432               m_disassembly_sp = m_sc.function->GetInstructions(
3433                   exe_ctx, nullptr, prefer_file_cache);
3434               if (m_disassembly_sp) {
3435                 set_selected_line_to_pc = true;
3436                 m_disassembly_range = m_sc.function->GetAddressRange();
3437               } else {
3438                 m_disassembly_range.Clear();
3439               }
3440             } else {
3441               set_selected_line_to_pc = context_changed;
3442             }
3443           } else if (m_sc.symbol) {
3444             if (m_disassembly_scope != m_sc.symbol) {
3445               m_disassembly_scope = m_sc.symbol;
3446               m_disassembly_sp = m_sc.symbol->GetInstructions(
3447                   exe_ctx, nullptr, prefer_file_cache);
3448               if (m_disassembly_sp) {
3449                 set_selected_line_to_pc = true;
3450                 m_disassembly_range.GetBaseAddress() =
3451                     m_sc.symbol->GetAddress();
3452                 m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize());
3453               } else {
3454                 m_disassembly_range.Clear();
3455               }
3456             } else {
3457               set_selected_line_to_pc = context_changed;
3458             }
3459           }
3460         }
3461       } else {
3462         m_pc_line = UINT32_MAX;
3463       }
3464     }
3465 
3466     const int window_width = window.GetWidth();
3467     window.Erase();
3468     window.DrawTitleBox("Sources");
3469     if (!m_title.GetString().empty()) {
3470       window.AttributeOn(A_REVERSE);
3471       window.MoveCursor(1, 1);
3472       window.PutChar(' ');
3473       window.PutCStringTruncated(m_title.GetString().str().c_str(), 1);
3474       int x = window.GetCursorX();
3475       if (x < window_width - 1) {
3476         window.Printf("%*s", window_width - x - 1, "");
3477       }
3478       window.AttributeOff(A_REVERSE);
3479     }
3480 
3481     Target *target = exe_ctx.GetTargetPtr();
3482     const size_t num_source_lines = GetNumSourceLines();
3483     if (num_source_lines > 0) {
3484       // Display source
3485       BreakpointLines bp_lines;
3486       if (target) {
3487         BreakpointList &bp_list = target->GetBreakpointList();
3488         const size_t num_bps = bp_list.GetSize();
3489         for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
3490           BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
3491           const size_t num_bps_locs = bp_sp->GetNumLocations();
3492           for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
3493             BreakpointLocationSP bp_loc_sp =
3494                 bp_sp->GetLocationAtIndex(bp_loc_idx);
3495             LineEntry bp_loc_line_entry;
3496             if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry(
3497                     bp_loc_line_entry)) {
3498               if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file) {
3499                 bp_lines.insert(bp_loc_line_entry.line);
3500               }
3501             }
3502           }
3503         }
3504       }
3505 
3506       const attr_t selected_highlight_attr = A_REVERSE;
3507       const attr_t pc_highlight_attr = COLOR_PAIR(1);
3508 
3509       for (size_t i = 0; i < num_visible_lines; ++i) {
3510         const uint32_t curr_line = m_first_visible_line + i;
3511         if (curr_line < num_source_lines) {
3512           const int line_y = m_min_y + i;
3513           window.MoveCursor(1, line_y);
3514           const bool is_pc_line = curr_line == m_pc_line;
3515           const bool line_is_selected = m_selected_line == curr_line;
3516           // Highlight the line as the PC line first, then if the selected line
3517           // isn't the same as the PC line, highlight it differently
3518           attr_t highlight_attr = 0;
3519           attr_t bp_attr = 0;
3520           if (is_pc_line)
3521             highlight_attr = pc_highlight_attr;
3522           else if (line_is_selected)
3523             highlight_attr = selected_highlight_attr;
3524 
3525           if (bp_lines.find(curr_line + 1) != bp_lines.end())
3526             bp_attr = COLOR_PAIR(2);
3527 
3528           if (bp_attr)
3529             window.AttributeOn(bp_attr);
3530 
3531           window.Printf(" %*u ", m_line_width, curr_line + 1);
3532 
3533           if (bp_attr)
3534             window.AttributeOff(bp_attr);
3535 
3536           window.PutChar(ACS_VLINE);
3537           // Mark the line with the PC with a diamond
3538           if (is_pc_line)
3539             window.PutChar(ACS_DIAMOND);
3540           else
3541             window.PutChar(' ');
3542 
3543           if (highlight_attr)
3544             window.AttributeOn(highlight_attr);
3545           const uint32_t line_len =
3546               m_file_sp->GetLineLength(curr_line + 1, false);
3547           if (line_len > 0)
3548             window.PutCString(m_file_sp->PeekLineData(curr_line + 1), line_len);
3549 
3550           if (is_pc_line && frame_sp &&
3551               frame_sp->GetConcreteFrameIndex() == 0) {
3552             StopInfoSP stop_info_sp;
3553             if (thread)
3554               stop_info_sp = thread->GetStopInfo();
3555             if (stop_info_sp) {
3556               const char *stop_description = stop_info_sp->GetDescription();
3557               if (stop_description && stop_description[0]) {
3558                 size_t stop_description_len = strlen(stop_description);
3559                 int desc_x = window_width - stop_description_len - 16;
3560                 window.Printf("%*s", desc_x - window.GetCursorX(), "");
3561                 // window.MoveCursor(window_width - stop_description_len - 15,
3562                 // line_y);
3563                 window.Printf("<<< Thread %u: %s ", thread->GetIndexID(),
3564                               stop_description);
3565               }
3566             } else {
3567               window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
3568             }
3569           }
3570           if (highlight_attr)
3571             window.AttributeOff(highlight_attr);
3572         } else {
3573           break;
3574         }
3575       }
3576     } else {
3577       size_t num_disassembly_lines = GetNumDisassemblyLines();
3578       if (num_disassembly_lines > 0) {
3579         // Display disassembly
3580         BreakpointAddrs bp_file_addrs;
3581         Target *target = exe_ctx.GetTargetPtr();
3582         if (target) {
3583           BreakpointList &bp_list = target->GetBreakpointList();
3584           const size_t num_bps = bp_list.GetSize();
3585           for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
3586             BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
3587             const size_t num_bps_locs = bp_sp->GetNumLocations();
3588             for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs;
3589                  ++bp_loc_idx) {
3590               BreakpointLocationSP bp_loc_sp =
3591                   bp_sp->GetLocationAtIndex(bp_loc_idx);
3592               LineEntry bp_loc_line_entry;
3593               const lldb::addr_t file_addr =
3594                   bp_loc_sp->GetAddress().GetFileAddress();
3595               if (file_addr != LLDB_INVALID_ADDRESS) {
3596                 if (m_disassembly_range.ContainsFileAddress(file_addr))
3597                   bp_file_addrs.insert(file_addr);
3598               }
3599             }
3600           }
3601         }
3602 
3603         const attr_t selected_highlight_attr = A_REVERSE;
3604         const attr_t pc_highlight_attr = COLOR_PAIR(1);
3605 
3606         StreamString strm;
3607 
3608         InstructionList &insts = m_disassembly_sp->GetInstructionList();
3609         Address pc_address;
3610 
3611         if (frame_sp)
3612           pc_address = frame_sp->GetFrameCodeAddress();
3613         const uint32_t pc_idx =
3614             pc_address.IsValid()
3615                 ? insts.GetIndexOfInstructionAtAddress(pc_address)
3616                 : UINT32_MAX;
3617         if (set_selected_line_to_pc) {
3618           m_selected_line = pc_idx;
3619         }
3620 
3621         const uint32_t non_visible_pc_offset = (num_visible_lines / 5);
3622         if (static_cast<size_t>(m_first_visible_line) >= num_disassembly_lines)
3623           m_first_visible_line = 0;
3624 
3625         if (pc_idx < num_disassembly_lines) {
3626           if (pc_idx < static_cast<uint32_t>(m_first_visible_line) ||
3627               pc_idx >= m_first_visible_line + num_visible_lines)
3628             m_first_visible_line = pc_idx - non_visible_pc_offset;
3629         }
3630 
3631         for (size_t i = 0; i < num_visible_lines; ++i) {
3632           const uint32_t inst_idx = m_first_visible_line + i;
3633           Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get();
3634           if (!inst)
3635             break;
3636 
3637           const int line_y = m_min_y + i;
3638           window.MoveCursor(1, line_y);
3639           const bool is_pc_line = frame_sp && inst_idx == pc_idx;
3640           const bool line_is_selected = m_selected_line == inst_idx;
3641           // Highlight the line as the PC line first, then if the selected line
3642           // isn't the same as the PC line, highlight it differently
3643           attr_t highlight_attr = 0;
3644           attr_t bp_attr = 0;
3645           if (is_pc_line)
3646             highlight_attr = pc_highlight_attr;
3647           else if (line_is_selected)
3648             highlight_attr = selected_highlight_attr;
3649 
3650           if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) !=
3651               bp_file_addrs.end())
3652             bp_attr = COLOR_PAIR(2);
3653 
3654           if (bp_attr)
3655             window.AttributeOn(bp_attr);
3656 
3657           window.Printf(" 0x%16.16llx ",
3658                         static_cast<unsigned long long>(
3659                             inst->GetAddress().GetLoadAddress(target)));
3660 
3661           if (bp_attr)
3662             window.AttributeOff(bp_attr);
3663 
3664           window.PutChar(ACS_VLINE);
3665           // Mark the line with the PC with a diamond
3666           if (is_pc_line)
3667             window.PutChar(ACS_DIAMOND);
3668           else
3669             window.PutChar(' ');
3670 
3671           if (highlight_attr)
3672             window.AttributeOn(highlight_attr);
3673 
3674           const char *mnemonic = inst->GetMnemonic(&exe_ctx);
3675           const char *operands = inst->GetOperands(&exe_ctx);
3676           const char *comment = inst->GetComment(&exe_ctx);
3677 
3678           if (mnemonic != nullptr && mnemonic[0] == '\0')
3679             mnemonic = nullptr;
3680           if (operands != nullptr && operands[0] == '\0')
3681             operands = nullptr;
3682           if (comment != nullptr && comment[0] == '\0')
3683             comment = nullptr;
3684 
3685           strm.Clear();
3686 
3687           if (mnemonic != nullptr && operands != nullptr && comment != nullptr)
3688             strm.Printf("%-8s %-25s ; %s", mnemonic, operands, comment);
3689           else if (mnemonic != nullptr && operands != nullptr)
3690             strm.Printf("%-8s %s", mnemonic, operands);
3691           else if (mnemonic != nullptr)
3692             strm.Printf("%s", mnemonic);
3693 
3694           int right_pad = 1;
3695           window.PutCStringTruncated(strm.GetData(), right_pad);
3696 
3697           if (is_pc_line && frame_sp &&
3698               frame_sp->GetConcreteFrameIndex() == 0) {
3699             StopInfoSP stop_info_sp;
3700             if (thread)
3701               stop_info_sp = thread->GetStopInfo();
3702             if (stop_info_sp) {
3703               const char *stop_description = stop_info_sp->GetDescription();
3704               if (stop_description && stop_description[0]) {
3705                 size_t stop_description_len = strlen(stop_description);
3706                 int desc_x = window_width - stop_description_len - 16;
3707                 window.Printf("%*s", desc_x - window.GetCursorX(), "");
3708                 // window.MoveCursor(window_width - stop_description_len - 15,
3709                 // line_y);
3710                 window.Printf("<<< Thread %u: %s ", thread->GetIndexID(),
3711                               stop_description);
3712               }
3713             } else {
3714               window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
3715             }
3716           }
3717           if (highlight_attr)
3718             window.AttributeOff(highlight_attr);
3719         }
3720       }
3721     }
3722     return true; // Drawing handled
3723   }
3724 
3725   size_t GetNumLines() {
3726     size_t num_lines = GetNumSourceLines();
3727     if (num_lines == 0)
3728       num_lines = GetNumDisassemblyLines();
3729     return num_lines;
3730   }
3731 
3732   size_t GetNumSourceLines() const {
3733     if (m_file_sp)
3734       return m_file_sp->GetNumLines();
3735     return 0;
3736   }
3737 
3738   size_t GetNumDisassemblyLines() const {
3739     if (m_disassembly_sp)
3740       return m_disassembly_sp->GetInstructionList().GetSize();
3741     return 0;
3742   }
3743 
3744   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
3745     const uint32_t num_visible_lines = NumVisibleLines();
3746     const size_t num_lines = GetNumLines();
3747 
3748     switch (c) {
3749     case ',':
3750     case KEY_PPAGE:
3751       // Page up key
3752       if (static_cast<uint32_t>(m_first_visible_line) > num_visible_lines)
3753         m_first_visible_line -= num_visible_lines;
3754       else
3755         m_first_visible_line = 0;
3756       m_selected_line = m_first_visible_line;
3757       return eKeyHandled;
3758 
3759     case '.':
3760     case KEY_NPAGE:
3761       // Page down key
3762       {
3763         if (m_first_visible_line + num_visible_lines < num_lines)
3764           m_first_visible_line += num_visible_lines;
3765         else if (num_lines < num_visible_lines)
3766           m_first_visible_line = 0;
3767         else
3768           m_first_visible_line = num_lines - num_visible_lines;
3769         m_selected_line = m_first_visible_line;
3770       }
3771       return eKeyHandled;
3772 
3773     case KEY_UP:
3774       if (m_selected_line > 0) {
3775         m_selected_line--;
3776         if (static_cast<size_t>(m_first_visible_line) > m_selected_line)
3777           m_first_visible_line = m_selected_line;
3778       }
3779       return eKeyHandled;
3780 
3781     case KEY_DOWN:
3782       if (m_selected_line + 1 < num_lines) {
3783         m_selected_line++;
3784         if (m_first_visible_line + num_visible_lines < m_selected_line)
3785           m_first_visible_line++;
3786       }
3787       return eKeyHandled;
3788 
3789     case '\r':
3790     case '\n':
3791     case KEY_ENTER:
3792       // Set a breakpoint and run to the line using a one shot breakpoint
3793       if (GetNumSourceLines() > 0) {
3794         ExecutionContext exe_ctx =
3795             m_debugger.GetCommandInterpreter().GetExecutionContext();
3796         if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive()) {
3797           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
3798               nullptr, // Don't limit the breakpoint to certain modules
3799               m_file_sp->GetFileSpec(), // Source file
3800               m_selected_line +
3801                   1, // Source line number (m_selected_line is zero based)
3802               0,     // Unspecified column.
3803               0,     // No offset
3804               eLazyBoolCalculate,  // Check inlines using global setting
3805               eLazyBoolCalculate,  // Skip prologue using global setting,
3806               false,               // internal
3807               false,               // request_hardware
3808               eLazyBoolCalculate); // move_to_nearest_code
3809           // Make breakpoint one shot
3810           bp_sp->GetOptions()->SetOneShot(true);
3811           exe_ctx.GetProcessRef().Resume();
3812         }
3813       } else if (m_selected_line < GetNumDisassemblyLines()) {
3814         const Instruction *inst = m_disassembly_sp->GetInstructionList()
3815                                       .GetInstructionAtIndex(m_selected_line)
3816                                       .get();
3817         ExecutionContext exe_ctx =
3818             m_debugger.GetCommandInterpreter().GetExecutionContext();
3819         if (exe_ctx.HasTargetScope()) {
3820           Address addr = inst->GetAddress();
3821           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
3822               addr,   // lldb_private::Address
3823               false,  // internal
3824               false); // request_hardware
3825           // Make breakpoint one shot
3826           bp_sp->GetOptions()->SetOneShot(true);
3827           exe_ctx.GetProcessRef().Resume();
3828         }
3829       }
3830       return eKeyHandled;
3831 
3832     case 'b': // 'b' == toggle breakpoint on currently selected line
3833       if (m_selected_line < GetNumSourceLines()) {
3834         ExecutionContext exe_ctx =
3835             m_debugger.GetCommandInterpreter().GetExecutionContext();
3836         if (exe_ctx.HasTargetScope()) {
3837           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
3838               nullptr, // Don't limit the breakpoint to certain modules
3839               m_file_sp->GetFileSpec(), // Source file
3840               m_selected_line +
3841                   1, // Source line number (m_selected_line is zero based)
3842               0,     // No column specified.
3843               0,     // No offset
3844               eLazyBoolCalculate,  // Check inlines using global setting
3845               eLazyBoolCalculate,  // Skip prologue using global setting,
3846               false,               // internal
3847               false,               // request_hardware
3848               eLazyBoolCalculate); // move_to_nearest_code
3849         }
3850       } else if (m_selected_line < GetNumDisassemblyLines()) {
3851         const Instruction *inst = m_disassembly_sp->GetInstructionList()
3852                                       .GetInstructionAtIndex(m_selected_line)
3853                                       .get();
3854         ExecutionContext exe_ctx =
3855             m_debugger.GetCommandInterpreter().GetExecutionContext();
3856         if (exe_ctx.HasTargetScope()) {
3857           Address addr = inst->GetAddress();
3858           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
3859               addr,   // lldb_private::Address
3860               false,  // internal
3861               false); // request_hardware
3862         }
3863       }
3864       return eKeyHandled;
3865 
3866     case 'D': // 'D' == detach and keep stopped
3867     {
3868       ExecutionContext exe_ctx =
3869           m_debugger.GetCommandInterpreter().GetExecutionContext();
3870       if (exe_ctx.HasProcessScope())
3871         exe_ctx.GetProcessRef().Detach(true);
3872     }
3873       return eKeyHandled;
3874 
3875     case 'c':
3876       // 'c' == continue
3877       {
3878         ExecutionContext exe_ctx =
3879             m_debugger.GetCommandInterpreter().GetExecutionContext();
3880         if (exe_ctx.HasProcessScope())
3881           exe_ctx.GetProcessRef().Resume();
3882       }
3883       return eKeyHandled;
3884 
3885     case 'f':
3886       // 'f' == step out (finish)
3887       {
3888         ExecutionContext exe_ctx =
3889             m_debugger.GetCommandInterpreter().GetExecutionContext();
3890         if (exe_ctx.HasThreadScope() &&
3891             StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
3892           exe_ctx.GetThreadRef().StepOut();
3893         }
3894       }
3895       return eKeyHandled;
3896 
3897     case 'n': // 'n' == step over
3898     case 'N': // 'N' == step over instruction
3899     {
3900       ExecutionContext exe_ctx =
3901           m_debugger.GetCommandInterpreter().GetExecutionContext();
3902       if (exe_ctx.HasThreadScope() &&
3903           StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
3904         bool source_step = (c == 'n');
3905         exe_ctx.GetThreadRef().StepOver(source_step);
3906       }
3907     }
3908       return eKeyHandled;
3909 
3910     case 's': // 's' == step into
3911     case 'S': // 'S' == step into instruction
3912     {
3913       ExecutionContext exe_ctx =
3914           m_debugger.GetCommandInterpreter().GetExecutionContext();
3915       if (exe_ctx.HasThreadScope() &&
3916           StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
3917         bool source_step = (c == 's');
3918         exe_ctx.GetThreadRef().StepIn(source_step);
3919       }
3920     }
3921       return eKeyHandled;
3922 
3923     case 'u': // 'u' == frame up
3924     case 'd': // 'd' == frame down
3925     {
3926       ExecutionContext exe_ctx =
3927           m_debugger.GetCommandInterpreter().GetExecutionContext();
3928       if (exe_ctx.HasThreadScope()) {
3929         Thread *thread = exe_ctx.GetThreadPtr();
3930         uint32_t frame_idx = thread->GetSelectedFrameIndex();
3931         if (frame_idx == UINT32_MAX)
3932           frame_idx = 0;
3933         if (c == 'u' && frame_idx + 1 < thread->GetStackFrameCount())
3934           ++frame_idx;
3935         else if (c == 'd' && frame_idx > 0)
3936           --frame_idx;
3937         if (thread->SetSelectedFrameByIndex(frame_idx, true))
3938           exe_ctx.SetFrameSP(thread->GetSelectedFrame());
3939       }
3940     }
3941       return eKeyHandled;
3942 
3943     case 'h':
3944       window.CreateHelpSubwindow();
3945       return eKeyHandled;
3946 
3947     default:
3948       break;
3949     }
3950     return eKeyNotHandled;
3951   }
3952 
3953 protected:
3954   typedef std::set<uint32_t> BreakpointLines;
3955   typedef std::set<lldb::addr_t> BreakpointAddrs;
3956 
3957   Debugger &m_debugger;
3958   SymbolContext m_sc;
3959   SourceManager::FileSP m_file_sp;
3960   SymbolContextScope *m_disassembly_scope;
3961   lldb::DisassemblerSP m_disassembly_sp;
3962   AddressRange m_disassembly_range;
3963   StreamString m_title;
3964   lldb::user_id_t m_tid;
3965   int m_line_width;
3966   uint32_t m_selected_line; // The selected line
3967   uint32_t m_pc_line;       // The line with the PC
3968   uint32_t m_stop_id;
3969   uint32_t m_frame_idx;
3970   int m_first_visible_line;
3971   int m_min_x;
3972   int m_min_y;
3973   int m_max_x;
3974   int m_max_y;
3975 };
3976 
3977 DisplayOptions ValueObjectListDelegate::g_options = {true};
3978 
3979 IOHandlerCursesGUI::IOHandlerCursesGUI(Debugger &debugger)
3980     : IOHandler(debugger, IOHandler::Type::Curses) {}
3981 
3982 void IOHandlerCursesGUI::Activate() {
3983   IOHandler::Activate();
3984   if (!m_app_ap) {
3985     m_app_ap = std::make_unique<Application>(GetInputFILE(), GetOutputFILE());
3986 
3987     // This is both a window and a menu delegate
3988     std::shared_ptr<ApplicationDelegate> app_delegate_sp(
3989         new ApplicationDelegate(*m_app_ap, m_debugger));
3990 
3991     MenuDelegateSP app_menu_delegate_sp =
3992         std::static_pointer_cast<MenuDelegate>(app_delegate_sp);
3993     MenuSP lldb_menu_sp(
3994         new Menu("LLDB", "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB));
3995     MenuSP exit_menuitem_sp(
3996         new Menu("Exit", nullptr, 'x', ApplicationDelegate::eMenuID_LLDBExit));
3997     exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit);
3998     lldb_menu_sp->AddSubmenu(MenuSP(new Menu(
3999         "About LLDB", nullptr, 'a', ApplicationDelegate::eMenuID_LLDBAbout)));
4000     lldb_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
4001     lldb_menu_sp->AddSubmenu(exit_menuitem_sp);
4002 
4003     MenuSP target_menu_sp(new Menu("Target", "F2", KEY_F(2),
4004                                    ApplicationDelegate::eMenuID_Target));
4005     target_menu_sp->AddSubmenu(MenuSP(new Menu(
4006         "Create", nullptr, 'c', ApplicationDelegate::eMenuID_TargetCreate)));
4007     target_menu_sp->AddSubmenu(MenuSP(new Menu(
4008         "Delete", nullptr, 'd', ApplicationDelegate::eMenuID_TargetDelete)));
4009 
4010     MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3),
4011                                     ApplicationDelegate::eMenuID_Process));
4012     process_menu_sp->AddSubmenu(MenuSP(new Menu(
4013         "Attach", nullptr, 'a', ApplicationDelegate::eMenuID_ProcessAttach)));
4014     process_menu_sp->AddSubmenu(
4015         MenuSP(new Menu("Detach and resume", nullptr, 'd',
4016                         ApplicationDelegate::eMenuID_ProcessDetachResume)));
4017     process_menu_sp->AddSubmenu(
4018         MenuSP(new Menu("Detach suspended", nullptr, 's',
4019                         ApplicationDelegate::eMenuID_ProcessDetachSuspended)));
4020     process_menu_sp->AddSubmenu(MenuSP(new Menu(
4021         "Launch", nullptr, 'l', ApplicationDelegate::eMenuID_ProcessLaunch)));
4022     process_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
4023     process_menu_sp->AddSubmenu(
4024         MenuSP(new Menu("Continue", nullptr, 'c',
4025                         ApplicationDelegate::eMenuID_ProcessContinue)));
4026     process_menu_sp->AddSubmenu(MenuSP(new Menu(
4027         "Halt", nullptr, 'h', ApplicationDelegate::eMenuID_ProcessHalt)));
4028     process_menu_sp->AddSubmenu(MenuSP(new Menu(
4029         "Kill", nullptr, 'k', ApplicationDelegate::eMenuID_ProcessKill)));
4030 
4031     MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4),
4032                                    ApplicationDelegate::eMenuID_Thread));
4033     thread_menu_sp->AddSubmenu(MenuSP(new Menu(
4034         "Step In", nullptr, 'i', ApplicationDelegate::eMenuID_ThreadStepIn)));
4035     thread_menu_sp->AddSubmenu(
4036         MenuSP(new Menu("Step Over", nullptr, 'v',
4037                         ApplicationDelegate::eMenuID_ThreadStepOver)));
4038     thread_menu_sp->AddSubmenu(MenuSP(new Menu(
4039         "Step Out", nullptr, 'o', ApplicationDelegate::eMenuID_ThreadStepOut)));
4040 
4041     MenuSP view_menu_sp(
4042         new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View));
4043     view_menu_sp->AddSubmenu(
4044         MenuSP(new Menu("Backtrace", nullptr, 'b',
4045                         ApplicationDelegate::eMenuID_ViewBacktrace)));
4046     view_menu_sp->AddSubmenu(
4047         MenuSP(new Menu("Registers", nullptr, 'r',
4048                         ApplicationDelegate::eMenuID_ViewRegisters)));
4049     view_menu_sp->AddSubmenu(MenuSP(new Menu(
4050         "Source", nullptr, 's', ApplicationDelegate::eMenuID_ViewSource)));
4051     view_menu_sp->AddSubmenu(
4052         MenuSP(new Menu("Variables", nullptr, 'v',
4053                         ApplicationDelegate::eMenuID_ViewVariables)));
4054 
4055     MenuSP help_menu_sp(
4056         new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help));
4057     help_menu_sp->AddSubmenu(MenuSP(new Menu(
4058         "GUI Help", nullptr, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp)));
4059 
4060     m_app_ap->Initialize();
4061     WindowSP &main_window_sp = m_app_ap->GetMainWindow();
4062 
4063     MenuSP menubar_sp(new Menu(Menu::Type::Bar));
4064     menubar_sp->AddSubmenu(lldb_menu_sp);
4065     menubar_sp->AddSubmenu(target_menu_sp);
4066     menubar_sp->AddSubmenu(process_menu_sp);
4067     menubar_sp->AddSubmenu(thread_menu_sp);
4068     menubar_sp->AddSubmenu(view_menu_sp);
4069     menubar_sp->AddSubmenu(help_menu_sp);
4070     menubar_sp->SetDelegate(app_menu_delegate_sp);
4071 
4072     Rect content_bounds = main_window_sp->GetFrame();
4073     Rect menubar_bounds = content_bounds.MakeMenuBar();
4074     Rect status_bounds = content_bounds.MakeStatusBar();
4075     Rect source_bounds;
4076     Rect variables_bounds;
4077     Rect threads_bounds;
4078     Rect source_variables_bounds;
4079     content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds,
4080                                            threads_bounds);
4081     source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds,
4082                                                       variables_bounds);
4083 
4084     WindowSP menubar_window_sp =
4085         main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false);
4086     // Let the menubar get keys if the active window doesn't handle the keys
4087     // that are typed so it can respond to menubar key presses.
4088     menubar_window_sp->SetCanBeActive(
4089         false); // Don't let the menubar become the active window
4090     menubar_window_sp->SetDelegate(menubar_sp);
4091 
4092     WindowSP source_window_sp(
4093         main_window_sp->CreateSubWindow("Source", source_bounds, true));
4094     WindowSP variables_window_sp(
4095         main_window_sp->CreateSubWindow("Variables", variables_bounds, false));
4096     WindowSP threads_window_sp(
4097         main_window_sp->CreateSubWindow("Threads", threads_bounds, false));
4098     WindowSP status_window_sp(
4099         main_window_sp->CreateSubWindow("Status", status_bounds, false));
4100     status_window_sp->SetCanBeActive(
4101         false); // Don't let the status bar become the active window
4102     main_window_sp->SetDelegate(
4103         std::static_pointer_cast<WindowDelegate>(app_delegate_sp));
4104     source_window_sp->SetDelegate(
4105         WindowDelegateSP(new SourceFileWindowDelegate(m_debugger)));
4106     variables_window_sp->SetDelegate(
4107         WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
4108     TreeDelegateSP thread_delegate_sp(new ThreadsTreeDelegate(m_debugger));
4109     threads_window_sp->SetDelegate(WindowDelegateSP(
4110         new TreeWindowDelegate(m_debugger, thread_delegate_sp)));
4111     status_window_sp->SetDelegate(
4112         WindowDelegateSP(new StatusBarWindowDelegate(m_debugger)));
4113 
4114     // Show the main help window once the first time the curses GUI is launched
4115     static bool g_showed_help = false;
4116     if (!g_showed_help) {
4117       g_showed_help = true;
4118       main_window_sp->CreateHelpSubwindow();
4119     }
4120 
4121     init_pair(1, COLOR_WHITE, COLOR_BLUE);
4122     init_pair(2, COLOR_BLACK, COLOR_WHITE);
4123     init_pair(3, COLOR_MAGENTA, COLOR_WHITE);
4124     init_pair(4, COLOR_MAGENTA, COLOR_BLACK);
4125     init_pair(5, COLOR_RED, COLOR_BLACK);
4126   }
4127 }
4128 
4129 void IOHandlerCursesGUI::Deactivate() { m_app_ap->Terminate(); }
4130 
4131 void IOHandlerCursesGUI::Run() {
4132   m_app_ap->Run(m_debugger);
4133   SetIsDone(true);
4134 }
4135 
4136 IOHandlerCursesGUI::~IOHandlerCursesGUI() = default;
4137 
4138 void IOHandlerCursesGUI::Cancel() {}
4139 
4140 bool IOHandlerCursesGUI::Interrupt() { return false; }
4141 
4142 void IOHandlerCursesGUI::GotEOF() {}
4143 
4144 void IOHandlerCursesGUI::TerminalSizeChanged() {
4145   m_app_ap->TerminalSizeChanged();
4146 }
4147 
4148 #endif // LLDB_ENABLE_CURSES
4149