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/Core/ValueObjectUpdater.h"
30 #include "lldb/Host/File.h"
31 #include "lldb/Utility/Predicate.h"
32 #include "lldb/Utility/Status.h"
33 #include "lldb/Utility/StreamString.h"
34 #include "lldb/Utility/StringList.h"
35 #include "lldb/lldb-forward.h"
36 
37 #include "lldb/Interpreter/CommandCompletions.h"
38 #include "lldb/Interpreter/CommandInterpreter.h"
39 
40 #if LLDB_ENABLE_CURSES
41 #include "lldb/Breakpoint/BreakpointLocation.h"
42 #include "lldb/Core/Module.h"
43 #include "lldb/Core/PluginManager.h"
44 #include "lldb/Core/ValueObject.h"
45 #include "lldb/Core/ValueObjectRegister.h"
46 #include "lldb/Symbol/Block.h"
47 #include "lldb/Symbol/Function.h"
48 #include "lldb/Symbol/Symbol.h"
49 #include "lldb/Symbol/VariableList.h"
50 #include "lldb/Target/Process.h"
51 #include "lldb/Target/RegisterContext.h"
52 #include "lldb/Target/StackFrame.h"
53 #include "lldb/Target/StopInfo.h"
54 #include "lldb/Target/Target.h"
55 #include "lldb/Target/Thread.h"
56 #include "lldb/Utility/State.h"
57 #endif
58 
59 #include "llvm/ADT/StringRef.h"
60 
61 #ifdef _WIN32
62 #include "lldb/Host/windows/windows.h"
63 #endif
64 
65 #include <memory>
66 #include <mutex>
67 
68 #include <cassert>
69 #include <cctype>
70 #include <cerrno>
71 #include <cstdint>
72 #include <cstdio>
73 #include <cstring>
74 #include <functional>
75 #include <type_traits>
76 
77 using namespace lldb;
78 using namespace lldb_private;
79 using llvm::None;
80 using llvm::Optional;
81 using llvm::StringRef;
82 
83 // we may want curses to be disabled for some builds for instance, windows
84 #if LLDB_ENABLE_CURSES
85 
86 #define KEY_RETURN 10
87 #define KEY_ESCAPE 27
88 
89 #define KEY_SHIFT_TAB (KEY_MAX + 1)
90 
91 namespace curses {
92 class Menu;
93 class MenuDelegate;
94 class Window;
95 class WindowDelegate;
96 typedef std::shared_ptr<Menu> MenuSP;
97 typedef std::shared_ptr<MenuDelegate> MenuDelegateSP;
98 typedef std::shared_ptr<Window> WindowSP;
99 typedef std::shared_ptr<WindowDelegate> WindowDelegateSP;
100 typedef std::vector<MenuSP> Menus;
101 typedef std::vector<WindowSP> Windows;
102 typedef std::vector<WindowDelegateSP> WindowDelegates;
103 
104 #if 0
105 type summary add -s "x=${var.x}, y=${var.y}" curses::Point
106 type summary add -s "w=${var.width}, h=${var.height}" curses::Size
107 type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect
108 #endif
109 
110 struct Point {
111   int x;
112   int y;
113 
114   Point(int _x = 0, int _y = 0) : x(_x), y(_y) {}
115 
116   void Clear() {
117     x = 0;
118     y = 0;
119   }
120 
121   Point &operator+=(const Point &rhs) {
122     x += rhs.x;
123     y += rhs.y;
124     return *this;
125   }
126 
127   void Dump() { printf("(x=%i, y=%i)\n", x, 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 bool operator!=(const Point &lhs, const Point &rhs) {
135   return lhs.x != rhs.x || lhs.y != rhs.y;
136 }
137 
138 struct Size {
139   int width;
140   int height;
141   Size(int w = 0, int h = 0) : width(w), height(h) {}
142 
143   void Clear() {
144     width = 0;
145     height = 0;
146   }
147 
148   void Dump() { printf("(w=%i, h=%i)\n", width, 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 bool operator!=(const Size &lhs, const Size &rhs) {
156   return lhs.width != rhs.width || lhs.height != rhs.height;
157 }
158 
159 struct Rect {
160   Point origin;
161   Size size;
162 
163   Rect() : origin(), size() {}
164 
165   Rect(const Point &p, const Size &s) : origin(p), size(s) {}
166 
167   void Clear() {
168     origin.Clear();
169     size.Clear();
170   }
171 
172   void Dump() {
173     printf("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width,
174            size.height);
175   }
176 
177   void Inset(int w, int h) {
178     if (size.width > w * 2)
179       size.width -= w * 2;
180     origin.x += w;
181 
182     if (size.height > h * 2)
183       size.height -= h * 2;
184     origin.y += h;
185   }
186 
187   // Return a status bar rectangle which is the last line of this rectangle.
188   // This rectangle will be modified to not include the status bar area.
189   Rect MakeStatusBar() {
190     Rect status_bar;
191     if (size.height > 1) {
192       status_bar.origin.x = origin.x;
193       status_bar.origin.y = size.height;
194       status_bar.size.width = size.width;
195       status_bar.size.height = 1;
196       --size.height;
197     }
198     return status_bar;
199   }
200 
201   // Return a menubar rectangle which is the first line of this rectangle. This
202   // rectangle will be modified to not include the menubar area.
203   Rect MakeMenuBar() {
204     Rect menubar;
205     if (size.height > 1) {
206       menubar.origin.x = origin.x;
207       menubar.origin.y = origin.y;
208       menubar.size.width = size.width;
209       menubar.size.height = 1;
210       ++origin.y;
211       --size.height;
212     }
213     return menubar;
214   }
215 
216   void HorizontalSplitPercentage(float top_percentage, Rect &top,
217                                  Rect &bottom) const {
218     float top_height = top_percentage * size.height;
219     HorizontalSplit(top_height, top, bottom);
220   }
221 
222   void HorizontalSplit(int top_height, Rect &top, Rect &bottom) const {
223     top = *this;
224     if (top_height < size.height) {
225       top.size.height = top_height;
226       bottom.origin.x = origin.x;
227       bottom.origin.y = origin.y + top.size.height;
228       bottom.size.width = size.width;
229       bottom.size.height = size.height - top.size.height;
230     } else {
231       bottom.Clear();
232     }
233   }
234 
235   void VerticalSplitPercentage(float left_percentage, Rect &left,
236                                Rect &right) const {
237     float left_width = left_percentage * size.width;
238     VerticalSplit(left_width, left, right);
239   }
240 
241   void VerticalSplit(int left_width, Rect &left, Rect &right) const {
242     left = *this;
243     if (left_width < size.width) {
244       left.size.width = left_width;
245       right.origin.x = origin.x + left.size.width;
246       right.origin.y = origin.y;
247       right.size.width = size.width - left.size.width;
248       right.size.height = size.height;
249     } else {
250       right.Clear();
251     }
252   }
253 };
254 
255 bool operator==(const Rect &lhs, const Rect &rhs) {
256   return lhs.origin == rhs.origin && lhs.size == rhs.size;
257 }
258 
259 bool operator!=(const Rect &lhs, const Rect &rhs) {
260   return lhs.origin != rhs.origin || lhs.size != rhs.size;
261 }
262 
263 enum HandleCharResult {
264   eKeyNotHandled = 0,
265   eKeyHandled = 1,
266   eQuitApplication = 2
267 };
268 
269 enum class MenuActionResult {
270   Handled,
271   NotHandled,
272   Quit // Exit all menus and quit
273 };
274 
275 struct KeyHelp {
276   int ch;
277   const char *description;
278 };
279 
280 // COLOR_PAIR index names
281 enum {
282   // First 16 colors are 8 black background and 8 blue background colors,
283   // needed by OutputColoredStringTruncated().
284   BlackOnBlack = 1,
285   RedOnBlack,
286   GreenOnBlack,
287   YellowOnBlack,
288   BlueOnBlack,
289   MagentaOnBlack,
290   CyanOnBlack,
291   WhiteOnBlack,
292   BlackOnBlue,
293   RedOnBlue,
294   GreenOnBlue,
295   YellowOnBlue,
296   BlueOnBlue,
297   MagentaOnBlue,
298   CyanOnBlue,
299   WhiteOnBlue,
300   // Other colors, as needed.
301   BlackOnWhite,
302   MagentaOnWhite,
303   LastColorPairIndex = MagentaOnWhite
304 };
305 
306 class WindowDelegate {
307 public:
308   virtual ~WindowDelegate() = default;
309 
310   virtual bool WindowDelegateDraw(Window &window, bool force) {
311     return false; // Drawing not handled
312   }
313 
314   virtual HandleCharResult WindowDelegateHandleChar(Window &window, int key) {
315     return eKeyNotHandled;
316   }
317 
318   virtual const char *WindowDelegateGetHelpText() { return nullptr; }
319 
320   virtual KeyHelp *WindowDelegateGetKeyHelp() { return nullptr; }
321 };
322 
323 class HelpDialogDelegate : public WindowDelegate {
324 public:
325   HelpDialogDelegate(const char *text, KeyHelp *key_help_array);
326 
327   ~HelpDialogDelegate() override;
328 
329   bool WindowDelegateDraw(Window &window, bool force) override;
330 
331   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override;
332 
333   size_t GetNumLines() const { return m_text.GetSize(); }
334 
335   size_t GetMaxLineLength() const { return m_text.GetMaxStringLength(); }
336 
337 protected:
338   StringList m_text;
339   int m_first_visible_line;
340 };
341 
342 // A surface is an abstraction for something than can be drawn on. The surface
343 // have a width, a height, a cursor position, and a multitude of drawing
344 // operations. This type should be sub-classed to get an actually useful ncurses
345 // object, such as a Window, SubWindow, Pad, or a SubPad.
346 class Surface {
347 public:
348   Surface() : m_window(nullptr) {}
349 
350   WINDOW *get() { return m_window; }
351 
352   operator WINDOW *() { return m_window; }
353 
354   // Copy a region of the surface to another surface.
355   void CopyToSurface(Surface &target, Point source_origin, Point target_origin,
356                      Size size) {
357     ::copywin(m_window, target.get(), source_origin.y, source_origin.x,
358               target_origin.y, target_origin.x,
359               target_origin.y + size.height - 1,
360               target_origin.x + size.width - 1, false);
361   }
362 
363   int GetCursorX() const { return getcurx(m_window); }
364   int GetCursorY() const { return getcury(m_window); }
365   void MoveCursor(int x, int y) { ::wmove(m_window, y, x); }
366 
367   void AttributeOn(attr_t attr) { ::wattron(m_window, attr); }
368   void AttributeOff(attr_t attr) { ::wattroff(m_window, attr); }
369 
370   int GetMaxX() const { return getmaxx(m_window); }
371   int GetMaxY() const { return getmaxy(m_window); }
372   int GetWidth() const { return GetMaxX(); }
373   int GetHeight() const { return GetMaxY(); }
374   Size GetSize() const { return Size(GetWidth(), GetHeight()); }
375   // Get a zero origin rectangle width the surface size.
376   Rect GetFrame() const { return Rect(Point(), GetSize()); }
377 
378   void Clear() { ::wclear(m_window); }
379   void Erase() { ::werase(m_window); }
380 
381   void SetBackground(int color_pair_idx) {
382     ::wbkgd(m_window, COLOR_PAIR(color_pair_idx));
383   }
384 
385   void PutChar(int ch) { ::waddch(m_window, ch); }
386   void PutCString(const char *s, int len = -1) { ::waddnstr(m_window, s, len); }
387 
388   void PutCStringTruncated(int right_pad, const char *s, int len = -1) {
389     int bytes_left = GetWidth() - GetCursorX();
390     if (bytes_left > right_pad) {
391       bytes_left -= right_pad;
392       ::waddnstr(m_window, s, len < 0 ? bytes_left : std::min(bytes_left, len));
393     }
394   }
395 
396   void Printf(const char *format, ...) __attribute__((format(printf, 2, 3))) {
397     va_list args;
398     va_start(args, format);
399     vw_printw(m_window, format, args);
400     va_end(args);
401   }
402 
403   void PrintfTruncated(int right_pad, const char *format, ...)
404       __attribute__((format(printf, 3, 4))) {
405     va_list args;
406     va_start(args, format);
407     StreamString strm;
408     strm.PrintfVarArg(format, args);
409     va_end(args);
410     PutCStringTruncated(right_pad, strm.GetData());
411   }
412 
413   void VerticalLine(int n, chtype v_char = ACS_VLINE) {
414     ::wvline(m_window, v_char, n);
415   }
416   void HorizontalLine(int n, chtype h_char = ACS_HLINE) {
417     ::whline(m_window, h_char, n);
418   }
419   void Box(chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) {
420     ::box(m_window, v_char, h_char);
421   }
422 
423   void TitledBox(const char *title, chtype v_char = ACS_VLINE,
424                  chtype h_char = ACS_HLINE) {
425     Box(v_char, h_char);
426     int title_offset = 2;
427     MoveCursor(title_offset, 0);
428     PutChar('[');
429     PutCString(title, GetWidth() - title_offset);
430     PutChar(']');
431   }
432 
433   void Box(const Rect &bounds, chtype v_char = ACS_VLINE,
434            chtype h_char = ACS_HLINE) {
435     MoveCursor(bounds.origin.x, bounds.origin.y);
436     VerticalLine(bounds.size.height);
437     HorizontalLine(bounds.size.width);
438     PutChar(ACS_ULCORNER);
439 
440     MoveCursor(bounds.origin.x + bounds.size.width - 1, bounds.origin.y);
441     VerticalLine(bounds.size.height);
442     PutChar(ACS_URCORNER);
443 
444     MoveCursor(bounds.origin.x, bounds.origin.y + bounds.size.height - 1);
445     HorizontalLine(bounds.size.width);
446     PutChar(ACS_LLCORNER);
447 
448     MoveCursor(bounds.origin.x + bounds.size.width - 1,
449                bounds.origin.y + bounds.size.height - 1);
450     PutChar(ACS_LRCORNER);
451   }
452 
453   void TitledBox(const Rect &bounds, const char *title,
454                  chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) {
455     Box(bounds, v_char, h_char);
456     int title_offset = 2;
457     MoveCursor(bounds.origin.x + title_offset, bounds.origin.y);
458     PutChar('[');
459     PutCString(title, bounds.size.width - title_offset);
460     PutChar(']');
461   }
462 
463   // Curses doesn't allow direct output of color escape sequences, but that's
464   // how we get source lines from the Highligher class. Read the line and
465   // convert color escape sequences to curses color attributes. Use
466   // first_skip_count to skip leading visible characters. Returns false if all
467   // visible characters were skipped due to first_skip_count.
468   bool OutputColoredStringTruncated(int right_pad, StringRef string,
469                                     size_t skip_first_count,
470                                     bool use_blue_background) {
471     attr_t saved_attr;
472     short saved_pair;
473     bool result = false;
474     wattr_get(m_window, &saved_attr, &saved_pair, nullptr);
475     if (use_blue_background)
476       ::wattron(m_window, COLOR_PAIR(WhiteOnBlue));
477     while (!string.empty()) {
478       size_t esc_pos = string.find('\x1b');
479       if (esc_pos == StringRef::npos) {
480         string = string.substr(skip_first_count);
481         if (!string.empty()) {
482           PutCStringTruncated(right_pad, string.data(), string.size());
483           result = true;
484         }
485         break;
486       }
487       if (esc_pos > 0) {
488         if (skip_first_count > 0) {
489           int skip = std::min(esc_pos, skip_first_count);
490           string = string.substr(skip);
491           skip_first_count -= skip;
492           esc_pos -= skip;
493         }
494         if (esc_pos > 0) {
495           PutCStringTruncated(right_pad, string.data(), esc_pos);
496           result = true;
497           string = string.drop_front(esc_pos);
498         }
499       }
500       bool consumed = string.consume_front("\x1b");
501       assert(consumed);
502       UNUSED_IF_ASSERT_DISABLED(consumed);
503       // This is written to match our Highlighter classes, which seem to
504       // generate only foreground color escape sequences. If necessary, this
505       // will need to be extended.
506       if (!string.consume_front("[")) {
507         llvm::errs() << "Missing '[' in color escape sequence.\n";
508         continue;
509       }
510       // Only 8 basic foreground colors and reset, our Highlighter doesn't use
511       // anything else.
512       int value;
513       if (!!string.consumeInteger(10, value) || // Returns false on success.
514           !(value == 0 || (value >= 30 && value <= 37))) {
515         llvm::errs() << "No valid color code in color escape sequence.\n";
516         continue;
517       }
518       if (!string.consume_front("m")) {
519         llvm::errs() << "Missing 'm' in color escape sequence.\n";
520         continue;
521       }
522       if (value == 0) { // Reset.
523         wattr_set(m_window, saved_attr, saved_pair, nullptr);
524         if (use_blue_background)
525           ::wattron(m_window, COLOR_PAIR(WhiteOnBlue));
526       } else {
527         // Mapped directly to first 16 color pairs (black/blue background).
528         ::wattron(m_window,
529                   COLOR_PAIR(value - 30 + 1 + (use_blue_background ? 8 : 0)));
530       }
531     }
532     wattr_set(m_window, saved_attr, saved_pair, nullptr);
533     return result;
534   }
535 
536 protected:
537   WINDOW *m_window;
538 };
539 
540 class Pad : public Surface {
541 public:
542   Pad(Size size) { m_window = ::newpad(size.height, size.width); }
543 
544   ~Pad() { ::delwin(m_window); }
545 };
546 
547 class SubPad : public Surface {
548 public:
549   SubPad(Pad &pad, Rect bounds) {
550     m_window = ::subpad(pad.get(), bounds.size.height, bounds.size.width,
551                         bounds.origin.y, bounds.origin.x);
552   }
553   SubPad(SubPad &subpad, Rect bounds) {
554     m_window = ::subpad(subpad.get(), bounds.size.height, bounds.size.width,
555                         bounds.origin.y, bounds.origin.x);
556   }
557 
558   ~SubPad() { ::delwin(m_window); }
559 };
560 
561 class Window : public Surface {
562 public:
563   Window(const char *name)
564       : m_name(name), m_panel(nullptr), m_parent(nullptr), m_subwindows(),
565         m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX),
566         m_prev_active_window_idx(UINT32_MAX), m_delete(false),
567         m_needs_update(true), m_can_activate(true), m_is_subwin(false) {}
568 
569   Window(const char *name, WINDOW *w, bool del = true)
570       : m_name(name), m_panel(nullptr), m_parent(nullptr), m_subwindows(),
571         m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX),
572         m_prev_active_window_idx(UINT32_MAX), m_delete(del),
573         m_needs_update(true), m_can_activate(true), m_is_subwin(false) {
574     if (w)
575       Reset(w);
576   }
577 
578   Window(const char *name, const Rect &bounds)
579       : m_name(name), m_parent(nullptr), m_subwindows(), m_delegate_sp(),
580         m_curr_active_window_idx(UINT32_MAX),
581         m_prev_active_window_idx(UINT32_MAX), m_delete(true),
582         m_needs_update(true), m_can_activate(true), m_is_subwin(false) {
583     Reset(::newwin(bounds.size.height, bounds.size.width, bounds.origin.y,
584                    bounds.origin.y));
585   }
586 
587   virtual ~Window() {
588     RemoveSubWindows();
589     Reset();
590   }
591 
592   void Reset(WINDOW *w = nullptr, bool del = true) {
593     if (m_window == w)
594       return;
595 
596     if (m_panel) {
597       ::del_panel(m_panel);
598       m_panel = nullptr;
599     }
600     if (m_window && m_delete) {
601       ::delwin(m_window);
602       m_window = nullptr;
603       m_delete = false;
604     }
605     if (w) {
606       m_window = w;
607       m_panel = ::new_panel(m_window);
608       m_delete = del;
609     }
610   }
611 
612   // Get the rectangle in our parent window
613   Rect GetBounds() const { return Rect(GetParentOrigin(), GetSize()); }
614 
615   Rect GetCenteredRect(int width, int height) {
616     Size size = GetSize();
617     width = std::min(size.width, width);
618     height = std::min(size.height, height);
619     int x = (size.width - width) / 2;
620     int y = (size.height - height) / 2;
621     return Rect(Point(x, y), Size(width, height));
622   }
623 
624   int GetChar() { return ::wgetch(m_window); }
625   Point GetParentOrigin() const { return Point(GetParentX(), GetParentY()); }
626   int GetParentX() const { return getparx(m_window); }
627   int GetParentY() const { return getpary(m_window); }
628   void MoveWindow(int x, int y) { MoveWindow(Point(x, y)); }
629   void Resize(int w, int h) { ::wresize(m_window, h, w); }
630   void Resize(const Size &size) {
631     ::wresize(m_window, size.height, size.width);
632   }
633   void MoveWindow(const Point &origin) {
634     const bool moving_window = origin != GetParentOrigin();
635     if (m_is_subwin && moving_window) {
636       // Can't move subwindows, must delete and re-create
637       Size size = GetSize();
638       Reset(::subwin(m_parent->m_window, size.height, size.width, origin.y,
639                      origin.x),
640             true);
641     } else {
642       ::mvwin(m_window, origin.y, origin.x);
643     }
644   }
645 
646   void SetBounds(const Rect &bounds) {
647     const bool moving_window = bounds.origin != GetParentOrigin();
648     if (m_is_subwin && moving_window) {
649       // Can't move subwindows, must delete and re-create
650       Reset(::subwin(m_parent->m_window, bounds.size.height, bounds.size.width,
651                      bounds.origin.y, bounds.origin.x),
652             true);
653     } else {
654       if (moving_window)
655         MoveWindow(bounds.origin);
656       Resize(bounds.size);
657     }
658   }
659 
660   void Touch() {
661     ::touchwin(m_window);
662     if (m_parent)
663       m_parent->Touch();
664   }
665 
666   WindowSP CreateSubWindow(const char *name, const Rect &bounds,
667                            bool make_active) {
668     auto get_window = [this, &bounds]() {
669       return m_window
670                  ? ::subwin(m_window, bounds.size.height, bounds.size.width,
671                             bounds.origin.y, bounds.origin.x)
672                  : ::newwin(bounds.size.height, bounds.size.width,
673                             bounds.origin.y, bounds.origin.x);
674     };
675     WindowSP subwindow_sp = std::make_shared<Window>(name, get_window(), true);
676     subwindow_sp->m_is_subwin = subwindow_sp.operator bool();
677     subwindow_sp->m_parent = this;
678     if (make_active) {
679       m_prev_active_window_idx = m_curr_active_window_idx;
680       m_curr_active_window_idx = m_subwindows.size();
681     }
682     m_subwindows.push_back(subwindow_sp);
683     ::top_panel(subwindow_sp->m_panel);
684     m_needs_update = true;
685     return subwindow_sp;
686   }
687 
688   bool RemoveSubWindow(Window *window) {
689     Windows::iterator pos, end = m_subwindows.end();
690     size_t i = 0;
691     for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) {
692       if ((*pos).get() == window) {
693         if (m_prev_active_window_idx == i)
694           m_prev_active_window_idx = UINT32_MAX;
695         else if (m_prev_active_window_idx != UINT32_MAX &&
696                  m_prev_active_window_idx > i)
697           --m_prev_active_window_idx;
698 
699         if (m_curr_active_window_idx == i)
700           m_curr_active_window_idx = UINT32_MAX;
701         else if (m_curr_active_window_idx != UINT32_MAX &&
702                  m_curr_active_window_idx > i)
703           --m_curr_active_window_idx;
704         window->Erase();
705         m_subwindows.erase(pos);
706         m_needs_update = true;
707         if (m_parent)
708           m_parent->Touch();
709         else
710           ::touchwin(stdscr);
711         return true;
712       }
713     }
714     return false;
715   }
716 
717   WindowSP FindSubWindow(const char *name) {
718     Windows::iterator pos, end = m_subwindows.end();
719     size_t i = 0;
720     for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) {
721       if ((*pos)->m_name == name)
722         return *pos;
723     }
724     return WindowSP();
725   }
726 
727   void RemoveSubWindows() {
728     m_curr_active_window_idx = UINT32_MAX;
729     m_prev_active_window_idx = UINT32_MAX;
730     for (Windows::iterator pos = m_subwindows.begin();
731          pos != m_subwindows.end(); pos = m_subwindows.erase(pos)) {
732       (*pos)->Erase();
733     }
734     if (m_parent)
735       m_parent->Touch();
736     else
737       ::touchwin(stdscr);
738   }
739 
740   // Window drawing utilities
741   void DrawTitleBox(const char *title, const char *bottom_message = nullptr) {
742     attr_t attr = 0;
743     if (IsActive())
744       attr = A_BOLD | COLOR_PAIR(BlackOnWhite);
745     else
746       attr = 0;
747     if (attr)
748       AttributeOn(attr);
749 
750     Box();
751     MoveCursor(3, 0);
752 
753     if (title && title[0]) {
754       PutChar('<');
755       PutCString(title);
756       PutChar('>');
757     }
758 
759     if (bottom_message && bottom_message[0]) {
760       int bottom_message_length = strlen(bottom_message);
761       int x = GetWidth() - 3 - (bottom_message_length + 2);
762 
763       if (x > 0) {
764         MoveCursor(x, GetHeight() - 1);
765         PutChar('[');
766         PutCString(bottom_message);
767         PutChar(']');
768       } else {
769         MoveCursor(1, GetHeight() - 1);
770         PutChar('[');
771         PutCStringTruncated(1, bottom_message);
772       }
773     }
774     if (attr)
775       AttributeOff(attr);
776   }
777 
778   virtual void Draw(bool force) {
779     if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw(*this, force))
780       return;
781 
782     for (auto &subwindow_sp : m_subwindows)
783       subwindow_sp->Draw(force);
784   }
785 
786   bool CreateHelpSubwindow() {
787     if (m_delegate_sp) {
788       const char *text = m_delegate_sp->WindowDelegateGetHelpText();
789       KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp();
790       if ((text && text[0]) || key_help) {
791         std::unique_ptr<HelpDialogDelegate> help_delegate_up(
792             new HelpDialogDelegate(text, key_help));
793         const size_t num_lines = help_delegate_up->GetNumLines();
794         const size_t max_length = help_delegate_up->GetMaxLineLength();
795         Rect bounds = GetBounds();
796         bounds.Inset(1, 1);
797         if (max_length + 4 < static_cast<size_t>(bounds.size.width)) {
798           bounds.origin.x += (bounds.size.width - max_length + 4) / 2;
799           bounds.size.width = max_length + 4;
800         } else {
801           if (bounds.size.width > 100) {
802             const int inset_w = bounds.size.width / 4;
803             bounds.origin.x += inset_w;
804             bounds.size.width -= 2 * inset_w;
805           }
806         }
807 
808         if (num_lines + 2 < static_cast<size_t>(bounds.size.height)) {
809           bounds.origin.y += (bounds.size.height - num_lines + 2) / 2;
810           bounds.size.height = num_lines + 2;
811         } else {
812           if (bounds.size.height > 100) {
813             const int inset_h = bounds.size.height / 4;
814             bounds.origin.y += inset_h;
815             bounds.size.height -= 2 * inset_h;
816           }
817         }
818         WindowSP help_window_sp;
819         Window *parent_window = GetParent();
820         if (parent_window)
821           help_window_sp = parent_window->CreateSubWindow("Help", bounds, true);
822         else
823           help_window_sp = CreateSubWindow("Help", bounds, true);
824         help_window_sp->SetDelegate(
825             WindowDelegateSP(help_delegate_up.release()));
826         return true;
827       }
828     }
829     return false;
830   }
831 
832   virtual HandleCharResult HandleChar(int key) {
833     // Always check the active window first
834     HandleCharResult result = eKeyNotHandled;
835     WindowSP active_window_sp = GetActiveWindow();
836     if (active_window_sp) {
837       result = active_window_sp->HandleChar(key);
838       if (result != eKeyNotHandled)
839         return result;
840     }
841 
842     if (m_delegate_sp) {
843       result = m_delegate_sp->WindowDelegateHandleChar(*this, key);
844       if (result != eKeyNotHandled)
845         return result;
846     }
847 
848     // Then check for any windows that want any keys that weren't handled. This
849     // is typically only for a menubar. Make a copy of the subwindows in case
850     // any HandleChar() functions muck with the subwindows. If we don't do
851     // this, we can crash when iterating over the subwindows.
852     Windows subwindows(m_subwindows);
853     for (auto subwindow_sp : subwindows) {
854       if (!subwindow_sp->m_can_activate) {
855         HandleCharResult result = subwindow_sp->HandleChar(key);
856         if (result != eKeyNotHandled)
857           return result;
858       }
859     }
860 
861     return eKeyNotHandled;
862   }
863 
864   WindowSP GetActiveWindow() {
865     if (!m_subwindows.empty()) {
866       if (m_curr_active_window_idx >= m_subwindows.size()) {
867         if (m_prev_active_window_idx < m_subwindows.size()) {
868           m_curr_active_window_idx = m_prev_active_window_idx;
869           m_prev_active_window_idx = UINT32_MAX;
870         } else if (IsActive()) {
871           m_prev_active_window_idx = UINT32_MAX;
872           m_curr_active_window_idx = UINT32_MAX;
873 
874           // Find first window that wants to be active if this window is active
875           const size_t num_subwindows = m_subwindows.size();
876           for (size_t i = 0; i < num_subwindows; ++i) {
877             if (m_subwindows[i]->GetCanBeActive()) {
878               m_curr_active_window_idx = i;
879               break;
880             }
881           }
882         }
883       }
884 
885       if (m_curr_active_window_idx < m_subwindows.size())
886         return m_subwindows[m_curr_active_window_idx];
887     }
888     return WindowSP();
889   }
890 
891   bool GetCanBeActive() const { return m_can_activate; }
892 
893   void SetCanBeActive(bool b) { m_can_activate = b; }
894 
895   void SetDelegate(const WindowDelegateSP &delegate_sp) {
896     m_delegate_sp = delegate_sp;
897   }
898 
899   Window *GetParent() const { return m_parent; }
900 
901   bool IsActive() const {
902     if (m_parent)
903       return m_parent->GetActiveWindow().get() == this;
904     else
905       return true; // Top level window is always active
906   }
907 
908   void SelectNextWindowAsActive() {
909     // Move active focus to next window
910     const int num_subwindows = m_subwindows.size();
911     int start_idx = 0;
912     if (m_curr_active_window_idx != UINT32_MAX) {
913       m_prev_active_window_idx = m_curr_active_window_idx;
914       start_idx = m_curr_active_window_idx + 1;
915     }
916     for (int idx = start_idx; idx < num_subwindows; ++idx) {
917       if (m_subwindows[idx]->GetCanBeActive()) {
918         m_curr_active_window_idx = idx;
919         return;
920       }
921     }
922     for (int idx = 0; idx < start_idx; ++idx) {
923       if (m_subwindows[idx]->GetCanBeActive()) {
924         m_curr_active_window_idx = idx;
925         break;
926       }
927     }
928   }
929 
930   void SelectPreviousWindowAsActive() {
931     // Move active focus to previous window
932     const int num_subwindows = m_subwindows.size();
933     int start_idx = num_subwindows - 1;
934     if (m_curr_active_window_idx != UINT32_MAX) {
935       m_prev_active_window_idx = m_curr_active_window_idx;
936       start_idx = m_curr_active_window_idx - 1;
937     }
938     for (int idx = start_idx; idx >= 0; --idx) {
939       if (m_subwindows[idx]->GetCanBeActive()) {
940         m_curr_active_window_idx = idx;
941         return;
942       }
943     }
944     for (int idx = num_subwindows - 1; idx > start_idx; --idx) {
945       if (m_subwindows[idx]->GetCanBeActive()) {
946         m_curr_active_window_idx = idx;
947         break;
948       }
949     }
950   }
951 
952   const char *GetName() const { return m_name.c_str(); }
953 
954 protected:
955   std::string m_name;
956   PANEL *m_panel;
957   Window *m_parent;
958   Windows m_subwindows;
959   WindowDelegateSP m_delegate_sp;
960   uint32_t m_curr_active_window_idx;
961   uint32_t m_prev_active_window_idx;
962   bool m_delete;
963   bool m_needs_update;
964   bool m_can_activate;
965   bool m_is_subwin;
966 
967 private:
968   Window(const Window &) = delete;
969   const Window &operator=(const Window &) = delete;
970 };
971 
972 class DerivedWindow : public Surface {
973 public:
974   DerivedWindow(Window &window, Rect bounds) {
975     m_window = ::derwin(window.get(), bounds.size.height, bounds.size.width,
976                         bounds.origin.y, bounds.origin.x);
977   }
978   DerivedWindow(DerivedWindow &derived_window, Rect bounds) {
979     m_window = ::derwin(derived_window.get(), bounds.size.height,
980                         bounds.size.width, bounds.origin.y, bounds.origin.x);
981   }
982 
983   ~DerivedWindow() { ::delwin(m_window); }
984 };
985 
986 /////////
987 // Forms
988 /////////
989 
990 // A scroll context defines a vertical region that needs to be visible in a
991 // scrolling area. The region is defined by the index of the start and end lines
992 // of the region. The start and end lines may be equal, in which case, the
993 // region is a single line.
994 struct ScrollContext {
995   int start;
996   int end;
997 
998   ScrollContext(int line) : start(line), end(line) {}
999   ScrollContext(int _start, int _end) : start(_start), end(_end) {}
1000 
1001   void Offset(int offset) {
1002     start += offset;
1003     end += offset;
1004   }
1005 };
1006 
1007 class FieldDelegate {
1008 public:
1009   virtual ~FieldDelegate() = default;
1010 
1011   // Returns the number of lines needed to draw the field. The draw method will
1012   // be given a surface that have exactly this number of lines.
1013   virtual int FieldDelegateGetHeight() = 0;
1014 
1015   // Returns the scroll context in the local coordinates of the field. By
1016   // default, the scroll context spans the whole field. Bigger fields with
1017   // internal navigation should override this method to provide a finer context.
1018   // Typical override methods would first get the scroll context of the internal
1019   // element then add the offset of the element in the field.
1020   virtual ScrollContext FieldDelegateGetScrollContext() {
1021     return ScrollContext(0, FieldDelegateGetHeight() - 1);
1022   }
1023 
1024   // Draw the field in the given subpad surface. The surface have a height that
1025   // is equal to the height returned by FieldDelegateGetHeight(). If the field
1026   // is selected in the form window, then is_selected will be true.
1027   virtual void FieldDelegateDraw(SubPad &surface, bool is_selected) = 0;
1028 
1029   // Handle the key that wasn't handled by the form window or a container field.
1030   virtual HandleCharResult FieldDelegateHandleChar(int key) {
1031     return eKeyNotHandled;
1032   }
1033 
1034   // This is executed once the user exists the field, that is, once the user
1035   // navigates to the next or the previous field. This is particularly useful to
1036   // do in-field validation and error setting. Fields with internal navigation
1037   // should call this method on their fields.
1038   virtual void FieldDelegateExitCallback() { return; }
1039 
1040   // Fields may have internal navigation, for instance, a List Field have
1041   // multiple internal elements, which needs to be navigated. To allow for this
1042   // mechanism, the window shouldn't handle the navigation keys all the time,
1043   // and instead call the key handing method of the selected field. It should
1044   // only handle the navigation keys when the field contains a single element or
1045   // have the last or first element selected depending on if the user is
1046   // navigating forward or backward. Additionally, once a field is selected in
1047   // the forward or backward direction, its first or last internal element
1048   // should be selected. The following methods implements those mechanisms.
1049 
1050   // Returns true if the first element in the field is selected or if the field
1051   // contains a single element.
1052   virtual bool FieldDelegateOnFirstOrOnlyElement() { return true; }
1053 
1054   // Returns true if the last element in the field is selected or if the field
1055   // contains a single element.
1056   virtual bool FieldDelegateOnLastOrOnlyElement() { return true; }
1057 
1058   // Select the first element in the field if multiple elements exists.
1059   virtual void FieldDelegateSelectFirstElement() { return; }
1060 
1061   // Select the last element in the field if multiple elements exists.
1062   virtual void FieldDelegateSelectLastElement() { return; }
1063 
1064   // Returns true if the field has an error, false otherwise.
1065   virtual bool FieldDelegateHasError() { return false; }
1066 
1067   bool FieldDelegateIsVisible() { return m_is_visible; }
1068 
1069   void FieldDelegateHide() { m_is_visible = false; }
1070 
1071   void FieldDelegateShow() { m_is_visible = true; }
1072 
1073 protected:
1074   bool m_is_visible = true;
1075 };
1076 
1077 typedef std::unique_ptr<FieldDelegate> FieldDelegateUP;
1078 
1079 class TextFieldDelegate : public FieldDelegate {
1080 public:
1081   TextFieldDelegate(const char *label, const char *content, bool required)
1082       : m_label(label), m_required(required), m_cursor_position(0),
1083         m_first_visibile_char(0) {
1084     if (content)
1085       m_content = content;
1086   }
1087 
1088   // Text fields are drawn as titled boxes of a single line, with a possible
1089   // error messages at the end.
1090   //
1091   // __[Label]___________
1092   // |                  |
1093   // |__________________|
1094   // - Error message if it exists.
1095 
1096   // The text field has a height of 3 lines. 2 lines for borders and 1 line for
1097   // the content.
1098   int GetFieldHeight() { return 3; }
1099 
1100   // The text field has a full height of 3 or 4 lines. 3 lines for the actual
1101   // field and an optional line for an error if it exists.
1102   int FieldDelegateGetHeight() override {
1103     int height = GetFieldHeight();
1104     if (FieldDelegateHasError())
1105       height++;
1106     return height;
1107   }
1108 
1109   // Get the cursor X position in the surface coordinate.
1110   int GetCursorXPosition() { return m_cursor_position - m_first_visibile_char; }
1111 
1112   int GetContentLength() { return m_content.length(); }
1113 
1114   void DrawContent(SubPad &surface, bool is_selected) {
1115     surface.MoveCursor(0, 0);
1116     const char *text = m_content.c_str() + m_first_visibile_char;
1117     surface.PutCString(text, surface.GetWidth());
1118     m_last_drawn_content_width = surface.GetWidth();
1119 
1120     // Highlight the cursor.
1121     surface.MoveCursor(GetCursorXPosition(), 0);
1122     if (is_selected)
1123       surface.AttributeOn(A_REVERSE);
1124     if (m_cursor_position == GetContentLength())
1125       // Cursor is past the last character. Highlight an empty space.
1126       surface.PutChar(' ');
1127     else
1128       surface.PutChar(m_content[m_cursor_position]);
1129     if (is_selected)
1130       surface.AttributeOff(A_REVERSE);
1131   }
1132 
1133   void DrawField(SubPad &surface, bool is_selected) {
1134     surface.TitledBox(m_label.c_str());
1135 
1136     Rect content_bounds = surface.GetFrame();
1137     content_bounds.Inset(1, 1);
1138     SubPad content_surface = SubPad(surface, content_bounds);
1139 
1140     DrawContent(content_surface, is_selected);
1141   }
1142 
1143   void DrawError(SubPad &surface) {
1144     if (!FieldDelegateHasError())
1145       return;
1146     surface.MoveCursor(0, 0);
1147     surface.AttributeOn(COLOR_PAIR(RedOnBlack));
1148     surface.PutChar(ACS_DIAMOND);
1149     surface.PutChar(' ');
1150     surface.PutCStringTruncated(1, GetError().c_str());
1151     surface.AttributeOff(COLOR_PAIR(RedOnBlack));
1152   }
1153 
1154   void FieldDelegateDraw(SubPad &surface, bool is_selected) override {
1155     Rect frame = surface.GetFrame();
1156     Rect field_bounds, error_bounds;
1157     frame.HorizontalSplit(GetFieldHeight(), field_bounds, error_bounds);
1158     SubPad field_surface = SubPad(surface, field_bounds);
1159     SubPad error_surface = SubPad(surface, error_bounds);
1160 
1161     DrawField(field_surface, is_selected);
1162     DrawError(error_surface);
1163   }
1164 
1165   // The cursor is allowed to move one character past the string.
1166   // m_cursor_position is in range [0, GetContentLength()].
1167   void MoveCursorRight() {
1168     if (m_cursor_position < GetContentLength())
1169       m_cursor_position++;
1170   }
1171 
1172   void MoveCursorLeft() {
1173     if (m_cursor_position > 0)
1174       m_cursor_position--;
1175   }
1176 
1177   // If the cursor moved past the last visible character, scroll right by one
1178   // character.
1179   void ScrollRightIfNeeded() {
1180     if (m_cursor_position - m_first_visibile_char == m_last_drawn_content_width)
1181       m_first_visibile_char++;
1182   }
1183 
1184   void ScrollLeft() {
1185     if (m_first_visibile_char > 0)
1186       m_first_visibile_char--;
1187   }
1188 
1189   // If the cursor moved past the first visible character, scroll left by one
1190   // character.
1191   void ScrollLeftIfNeeded() {
1192     if (m_cursor_position < m_first_visibile_char)
1193       m_first_visibile_char--;
1194   }
1195 
1196   // Insert a character at the current cursor position, advance the cursor
1197   // position, and make sure to scroll right if needed.
1198   void InsertChar(char character) {
1199     m_content.insert(m_cursor_position, 1, character);
1200     m_cursor_position++;
1201     ScrollRightIfNeeded();
1202   }
1203 
1204   // Remove the character before the cursor position, retreat the cursor
1205   // position, and make sure to scroll left if needed.
1206   void RemoveChar() {
1207     if (m_cursor_position == 0)
1208       return;
1209 
1210     m_content.erase(m_cursor_position - 1, 1);
1211     m_cursor_position--;
1212     ScrollLeft();
1213   }
1214 
1215   // True if the key represents a char that can be inserted in the field
1216   // content, false otherwise.
1217   virtual bool IsAcceptableChar(int key) { return isprint(key); }
1218 
1219   HandleCharResult FieldDelegateHandleChar(int key) override {
1220     if (IsAcceptableChar(key)) {
1221       ClearError();
1222       InsertChar((char)key);
1223       return eKeyHandled;
1224     }
1225 
1226     switch (key) {
1227     case KEY_RIGHT:
1228       MoveCursorRight();
1229       ScrollRightIfNeeded();
1230       return eKeyHandled;
1231     case KEY_LEFT:
1232       MoveCursorLeft();
1233       ScrollLeftIfNeeded();
1234       return eKeyHandled;
1235     case KEY_BACKSPACE:
1236       ClearError();
1237       RemoveChar();
1238       return eKeyHandled;
1239     default:
1240       break;
1241     }
1242     return eKeyNotHandled;
1243   }
1244 
1245   bool FieldDelegateHasError() override { return !m_error.empty(); }
1246 
1247   void FieldDelegateExitCallback() override {
1248     if (!IsSpecified() && m_required)
1249       SetError("This field is required!");
1250   }
1251 
1252   bool IsSpecified() { return !m_content.empty(); }
1253 
1254   void ClearError() { m_error.clear(); }
1255 
1256   const std::string &GetError() { return m_error; }
1257 
1258   void SetError(const char *error) { m_error = error; }
1259 
1260   const std::string &GetText() { return m_content; }
1261 
1262 protected:
1263   std::string m_label;
1264   bool m_required;
1265   // The position of the top left corner character of the border.
1266   std::string m_content;
1267   // The cursor position in the content string itself. Can be in the range
1268   // [0, GetContentLength()].
1269   int m_cursor_position;
1270   // The index of the first visible character in the content.
1271   int m_first_visibile_char;
1272   // The width of the fields content that was last drawn. Width can change, so
1273   // this is used to determine if scrolling is needed dynamically.
1274   int m_last_drawn_content_width;
1275   // Optional error message. If empty, field is considered to have no error.
1276   std::string m_error;
1277 };
1278 
1279 class IntegerFieldDelegate : public TextFieldDelegate {
1280 public:
1281   IntegerFieldDelegate(const char *label, int content, bool required)
1282       : TextFieldDelegate(label, std::to_string(content).c_str(), required) {}
1283 
1284   // Only accept digits.
1285   bool IsAcceptableChar(int key) override { return isdigit(key); }
1286 
1287   // Returns the integer content of the field.
1288   int GetInteger() { return std::stoi(m_content); }
1289 };
1290 
1291 class FileFieldDelegate : public TextFieldDelegate {
1292 public:
1293   FileFieldDelegate(const char *label, const char *content, bool need_to_exist,
1294                     bool required)
1295       : TextFieldDelegate(label, content, required),
1296         m_need_to_exist(need_to_exist) {}
1297 
1298   void FieldDelegateExitCallback() override {
1299     TextFieldDelegate::FieldDelegateExitCallback();
1300     if (!IsSpecified())
1301       return;
1302 
1303     if (!m_need_to_exist)
1304       return;
1305 
1306     FileSpec file = GetResolvedFileSpec();
1307     if (!FileSystem::Instance().Exists(file)) {
1308       SetError("File doesn't exist!");
1309       return;
1310     }
1311     if (FileSystem::Instance().IsDirectory(file)) {
1312       SetError("Not a file!");
1313       return;
1314     }
1315   }
1316 
1317   FileSpec GetFileSpec() {
1318     FileSpec file_spec(GetPath());
1319     return file_spec;
1320   }
1321 
1322   FileSpec GetResolvedFileSpec() {
1323     FileSpec file_spec(GetPath());
1324     FileSystem::Instance().Resolve(file_spec);
1325     return file_spec;
1326   }
1327 
1328   const std::string &GetPath() { return m_content; }
1329 
1330 protected:
1331   bool m_need_to_exist;
1332 };
1333 
1334 class DirectoryFieldDelegate : public TextFieldDelegate {
1335 public:
1336   DirectoryFieldDelegate(const char *label, const char *content,
1337                          bool need_to_exist, bool required)
1338       : TextFieldDelegate(label, content, required),
1339         m_need_to_exist(need_to_exist) {}
1340 
1341   void FieldDelegateExitCallback() override {
1342     TextFieldDelegate::FieldDelegateExitCallback();
1343     if (!IsSpecified())
1344       return;
1345 
1346     if (!m_need_to_exist)
1347       return;
1348 
1349     FileSpec file = GetResolvedFileSpec();
1350     if (!FileSystem::Instance().Exists(file)) {
1351       SetError("Directory doesn't exist!");
1352       return;
1353     }
1354     if (!FileSystem::Instance().IsDirectory(file)) {
1355       SetError("Not a directory!");
1356       return;
1357     }
1358   }
1359 
1360   FileSpec GetFileSpec() {
1361     FileSpec file_spec(GetPath());
1362     return file_spec;
1363   }
1364 
1365   FileSpec GetResolvedFileSpec() {
1366     FileSpec file_spec(GetPath());
1367     FileSystem::Instance().Resolve(file_spec);
1368     return file_spec;
1369   }
1370 
1371   const std::string &GetPath() { return m_content; }
1372 
1373 protected:
1374   bool m_need_to_exist;
1375 };
1376 
1377 class BooleanFieldDelegate : public FieldDelegate {
1378 public:
1379   BooleanFieldDelegate(const char *label, bool content)
1380       : m_label(label), m_content(content) {}
1381 
1382   // Boolean fields are drawn as checkboxes.
1383   //
1384   // [X] Label  or [ ] Label
1385 
1386   // Boolean fields are have a single line.
1387   int FieldDelegateGetHeight() override { return 1; }
1388 
1389   void FieldDelegateDraw(SubPad &surface, bool is_selected) override {
1390     surface.MoveCursor(0, 0);
1391     surface.PutChar('[');
1392     if (is_selected)
1393       surface.AttributeOn(A_REVERSE);
1394     surface.PutChar(m_content ? ACS_DIAMOND : ' ');
1395     if (is_selected)
1396       surface.AttributeOff(A_REVERSE);
1397     surface.PutChar(']');
1398     surface.PutChar(' ');
1399     surface.PutCString(m_label.c_str());
1400   }
1401 
1402   void ToggleContent() { m_content = !m_content; }
1403 
1404   void SetContentToTrue() { m_content = true; }
1405 
1406   void SetContentToFalse() { m_content = false; }
1407 
1408   HandleCharResult FieldDelegateHandleChar(int key) override {
1409     switch (key) {
1410     case 't':
1411     case '1':
1412       SetContentToTrue();
1413       return eKeyHandled;
1414     case 'f':
1415     case '0':
1416       SetContentToFalse();
1417       return eKeyHandled;
1418     case ' ':
1419     case '\r':
1420     case '\n':
1421     case KEY_ENTER:
1422       ToggleContent();
1423       return eKeyHandled;
1424     default:
1425       break;
1426     }
1427     return eKeyNotHandled;
1428   }
1429 
1430   // Returns the boolean content of the field.
1431   bool GetBoolean() { return m_content; }
1432 
1433 protected:
1434   std::string m_label;
1435   bool m_content;
1436 };
1437 
1438 class ChoicesFieldDelegate : public FieldDelegate {
1439 public:
1440   ChoicesFieldDelegate(const char *label, int number_of_visible_choices,
1441                        std::vector<std::string> choices)
1442       : m_label(label), m_number_of_visible_choices(number_of_visible_choices),
1443         m_choices(choices), m_choice(0), m_first_visibile_choice(0) {}
1444 
1445   // Choices fields are drawn as titles boxses of a number of visible choices.
1446   // The rest of the choices become visible as the user scroll. The selected
1447   // choice is denoted by a diamond as the first character.
1448   //
1449   // __[Label]___________
1450   // |-Choice 1         |
1451   // | Choice 2         |
1452   // | Choice 3         |
1453   // |__________________|
1454 
1455   // Choices field have two border characters plus the number of visible
1456   // choices.
1457   int FieldDelegateGetHeight() override {
1458     return m_number_of_visible_choices + 2;
1459   }
1460 
1461   int GetNumberOfChoices() { return m_choices.size(); }
1462 
1463   // Get the index of the last visible choice.
1464   int GetLastVisibleChoice() {
1465     int index = m_first_visibile_choice + m_number_of_visible_choices;
1466     return std::min(index, GetNumberOfChoices()) - 1;
1467   }
1468 
1469   void DrawContent(SubPad &surface, bool is_selected) {
1470     int choices_to_draw = GetLastVisibleChoice() - m_first_visibile_choice + 1;
1471     for (int i = 0; i < choices_to_draw; i++) {
1472       surface.MoveCursor(0, i);
1473       int current_choice = m_first_visibile_choice + i;
1474       const char *text = m_choices[current_choice].c_str();
1475       bool highlight = is_selected && current_choice == m_choice;
1476       if (highlight)
1477         surface.AttributeOn(A_REVERSE);
1478       surface.PutChar(current_choice == m_choice ? ACS_DIAMOND : ' ');
1479       surface.PutCString(text);
1480       if (highlight)
1481         surface.AttributeOff(A_REVERSE);
1482     }
1483   }
1484 
1485   void FieldDelegateDraw(SubPad &surface, bool is_selected) override {
1486     UpdateScrolling();
1487 
1488     surface.TitledBox(m_label.c_str());
1489 
1490     Rect content_bounds = surface.GetFrame();
1491     content_bounds.Inset(1, 1);
1492     SubPad content_surface = SubPad(surface, content_bounds);
1493 
1494     DrawContent(content_surface, is_selected);
1495   }
1496 
1497   void SelectPrevious() {
1498     if (m_choice > 0)
1499       m_choice--;
1500   }
1501 
1502   void SelectNext() {
1503     if (m_choice < GetNumberOfChoices() - 1)
1504       m_choice++;
1505   }
1506 
1507   void UpdateScrolling() {
1508     if (m_choice > GetLastVisibleChoice()) {
1509       m_first_visibile_choice = m_choice - (m_number_of_visible_choices - 1);
1510       return;
1511     }
1512 
1513     if (m_choice < m_first_visibile_choice)
1514       m_first_visibile_choice = m_choice;
1515   }
1516 
1517   HandleCharResult FieldDelegateHandleChar(int key) override {
1518     switch (key) {
1519     case KEY_UP:
1520       SelectPrevious();
1521       return eKeyHandled;
1522     case KEY_DOWN:
1523       SelectNext();
1524       return eKeyHandled;
1525     default:
1526       break;
1527     }
1528     return eKeyNotHandled;
1529   }
1530 
1531   // Returns the content of the choice as a string.
1532   std::string GetChoiceContent() { return m_choices[m_choice]; }
1533 
1534   // Returns the index of the choice.
1535   int GetChoice() { return m_choice; }
1536 
1537   void SetChoice(const std::string &choice) {
1538     for (int i = 0; i < GetNumberOfChoices(); i++) {
1539       if (choice == m_choices[i]) {
1540         m_choice = i;
1541         return;
1542       }
1543     }
1544   }
1545 
1546 protected:
1547   std::string m_label;
1548   int m_number_of_visible_choices;
1549   std::vector<std::string> m_choices;
1550   // The index of the selected choice.
1551   int m_choice;
1552   // The index of the first visible choice in the field.
1553   int m_first_visibile_choice;
1554 };
1555 
1556 class PlatformPluginFieldDelegate : public ChoicesFieldDelegate {
1557 public:
1558   PlatformPluginFieldDelegate(Debugger &debugger)
1559       : ChoicesFieldDelegate("Platform Plugin", 3, GetPossiblePluginNames()) {
1560     PlatformSP platform_sp = debugger.GetPlatformList().GetSelectedPlatform();
1561     if (platform_sp)
1562       SetChoice(platform_sp->GetName().AsCString());
1563   }
1564 
1565   std::vector<std::string> GetPossiblePluginNames() {
1566     std::vector<std::string> names;
1567     size_t i = 0;
1568     while (auto name = PluginManager::GetPlatformPluginNameAtIndex(i++))
1569       names.push_back(name);
1570     return names;
1571   }
1572 
1573   std::string GetPluginName() {
1574     std::string plugin_name = GetChoiceContent();
1575     return plugin_name;
1576   }
1577 };
1578 
1579 class ProcessPluginFieldDelegate : public ChoicesFieldDelegate {
1580 public:
1581   ProcessPluginFieldDelegate()
1582       : ChoicesFieldDelegate("Process Plugin", 3, GetPossiblePluginNames()) {}
1583 
1584   std::vector<std::string> GetPossiblePluginNames() {
1585     std::vector<std::string> names;
1586     names.push_back("<default>");
1587 
1588     size_t i = 0;
1589     while (auto name = PluginManager::GetProcessPluginNameAtIndex(i++))
1590       names.push_back(name);
1591     return names;
1592   }
1593 
1594   std::string GetPluginName() {
1595     std::string plugin_name = GetChoiceContent();
1596     if (plugin_name == "<default>")
1597       return "";
1598     return plugin_name;
1599   }
1600 };
1601 
1602 template <class T> class ListFieldDelegate : public FieldDelegate {
1603 public:
1604   ListFieldDelegate(const char *label, T default_field)
1605       : m_label(label), m_default_field(default_field), m_selection_index(0),
1606         m_selection_type(SelectionType::NewButton) {}
1607 
1608   // Signify which element is selected. If a field or a remove button is
1609   // selected, then m_selection_index signifies the particular field that
1610   // is selected or the field that the remove button belongs to.
1611   enum class SelectionType { Field, RemoveButton, NewButton };
1612 
1613   // A List field is drawn as a titled box of a number of other fields of the
1614   // same type. Each field has a Remove button next to it that removes the
1615   // corresponding field. Finally, the last line contains a New button to add a
1616   // new field.
1617   //
1618   // __[Label]___________
1619   // | Field 0 [Remove] |
1620   // | Field 1 [Remove] |
1621   // | Field 2 [Remove] |
1622   // |      [New]       |
1623   // |__________________|
1624 
1625   // List fields have two lines for border characters, 1 line for the New
1626   // button, and the total height of the available fields.
1627   int FieldDelegateGetHeight() override {
1628     // 2 border characters.
1629     int height = 2;
1630     // Total height of the fields.
1631     for (int i = 0; i < GetNumberOfFields(); i++) {
1632       height += m_fields[i].FieldDelegateGetHeight();
1633     }
1634     // A line for the New button.
1635     height++;
1636     return height;
1637   }
1638 
1639   ScrollContext FieldDelegateGetScrollContext() override {
1640     int height = FieldDelegateGetHeight();
1641     if (m_selection_type == SelectionType::NewButton)
1642       return ScrollContext(height - 2, height - 1);
1643 
1644     FieldDelegate &field = m_fields[m_selection_index];
1645     ScrollContext context = field.FieldDelegateGetScrollContext();
1646 
1647     // Start at 1 because of the top border.
1648     int offset = 1;
1649     for (int i = 0; i < m_selection_index; i++) {
1650       offset += m_fields[i].FieldDelegateGetHeight();
1651     }
1652     context.Offset(offset);
1653 
1654     // If the scroll context is touching the top border, include it in the
1655     // context to show the label.
1656     if (context.start == 1)
1657       context.start--;
1658 
1659     // If the scroll context is touching the new button, include it as well as
1660     // the bottom border in the context.
1661     if (context.end == height - 3)
1662       context.end += 2;
1663 
1664     return context;
1665   }
1666 
1667   void DrawRemoveButton(SubPad &surface, int highlight) {
1668     surface.MoveCursor(1, surface.GetHeight() / 2);
1669     if (highlight)
1670       surface.AttributeOn(A_REVERSE);
1671     surface.PutCString("[Remove]");
1672     if (highlight)
1673       surface.AttributeOff(A_REVERSE);
1674   }
1675 
1676   void DrawFields(SubPad &surface, bool is_selected) {
1677     int line = 0;
1678     int width = surface.GetWidth();
1679     for (int i = 0; i < GetNumberOfFields(); i++) {
1680       int height = m_fields[i].FieldDelegateGetHeight();
1681       Rect bounds = Rect(Point(0, line), Size(width, height));
1682       Rect field_bounds, remove_button_bounds;
1683       bounds.VerticalSplit(bounds.size.width - sizeof(" [Remove]"),
1684                            field_bounds, remove_button_bounds);
1685       SubPad field_surface = SubPad(surface, field_bounds);
1686       SubPad remove_button_surface = SubPad(surface, remove_button_bounds);
1687 
1688       bool is_element_selected = m_selection_index == i && is_selected;
1689       bool is_field_selected =
1690           is_element_selected && m_selection_type == SelectionType::Field;
1691       bool is_remove_button_selected =
1692           is_element_selected &&
1693           m_selection_type == SelectionType::RemoveButton;
1694       m_fields[i].FieldDelegateDraw(field_surface, is_field_selected);
1695       DrawRemoveButton(remove_button_surface, is_remove_button_selected);
1696 
1697       line += height;
1698     }
1699   }
1700 
1701   void DrawNewButton(SubPad &surface, bool is_selected) {
1702     const char *button_text = "[New]";
1703     int x = (surface.GetWidth() - sizeof(button_text) - 1) / 2;
1704     surface.MoveCursor(x, 0);
1705     bool highlight =
1706         is_selected && m_selection_type == SelectionType::NewButton;
1707     if (highlight)
1708       surface.AttributeOn(A_REVERSE);
1709     surface.PutCString(button_text);
1710     if (highlight)
1711       surface.AttributeOff(A_REVERSE);
1712   }
1713 
1714   void FieldDelegateDraw(SubPad &surface, bool is_selected) override {
1715     surface.TitledBox(m_label.c_str());
1716 
1717     Rect content_bounds = surface.GetFrame();
1718     content_bounds.Inset(1, 1);
1719     Rect fields_bounds, new_button_bounds;
1720     content_bounds.HorizontalSplit(content_bounds.size.height - 1,
1721                                    fields_bounds, new_button_bounds);
1722     SubPad fields_surface = SubPad(surface, fields_bounds);
1723     SubPad new_button_surface = SubPad(surface, new_button_bounds);
1724 
1725     DrawFields(fields_surface, is_selected);
1726     DrawNewButton(new_button_surface, is_selected);
1727   }
1728 
1729   void AddNewField() {
1730     m_fields.push_back(m_default_field);
1731     m_selection_index = GetNumberOfFields() - 1;
1732     m_selection_type = SelectionType::Field;
1733     FieldDelegate &field = m_fields[m_selection_index];
1734     field.FieldDelegateSelectFirstElement();
1735   }
1736 
1737   void RemoveField() {
1738     m_fields.erase(m_fields.begin() + m_selection_index);
1739     if (m_selection_index != 0)
1740       m_selection_index--;
1741 
1742     if (GetNumberOfFields() > 0) {
1743       m_selection_type = SelectionType::Field;
1744       FieldDelegate &field = m_fields[m_selection_index];
1745       field.FieldDelegateSelectFirstElement();
1746     } else
1747       m_selection_type = SelectionType::NewButton;
1748   }
1749 
1750   HandleCharResult SelectNext(int key) {
1751     if (m_selection_type == SelectionType::NewButton)
1752       return eKeyNotHandled;
1753 
1754     if (m_selection_type == SelectionType::RemoveButton) {
1755       if (m_selection_index == GetNumberOfFields() - 1) {
1756         m_selection_type = SelectionType::NewButton;
1757         return eKeyHandled;
1758       }
1759       m_selection_index++;
1760       m_selection_type = SelectionType::Field;
1761       FieldDelegate &next_field = m_fields[m_selection_index];
1762       next_field.FieldDelegateSelectFirstElement();
1763       return eKeyHandled;
1764     }
1765 
1766     FieldDelegate &field = m_fields[m_selection_index];
1767     if (!field.FieldDelegateOnLastOrOnlyElement()) {
1768       return field.FieldDelegateHandleChar(key);
1769     }
1770 
1771     field.FieldDelegateExitCallback();
1772 
1773     m_selection_type = SelectionType::RemoveButton;
1774     return eKeyHandled;
1775   }
1776 
1777   HandleCharResult SelectPrevious(int key) {
1778     if (FieldDelegateOnFirstOrOnlyElement())
1779       return eKeyNotHandled;
1780 
1781     if (m_selection_type == SelectionType::RemoveButton) {
1782       m_selection_type = SelectionType::Field;
1783       FieldDelegate &field = m_fields[m_selection_index];
1784       field.FieldDelegateSelectLastElement();
1785       return eKeyHandled;
1786     }
1787 
1788     if (m_selection_type == SelectionType::NewButton) {
1789       m_selection_type = SelectionType::RemoveButton;
1790       m_selection_index = GetNumberOfFields() - 1;
1791       return eKeyHandled;
1792     }
1793 
1794     FieldDelegate &field = m_fields[m_selection_index];
1795     if (!field.FieldDelegateOnFirstOrOnlyElement()) {
1796       return field.FieldDelegateHandleChar(key);
1797     }
1798 
1799     field.FieldDelegateExitCallback();
1800 
1801     m_selection_type = SelectionType::RemoveButton;
1802     m_selection_index--;
1803     return eKeyHandled;
1804   }
1805 
1806   HandleCharResult FieldDelegateHandleChar(int key) override {
1807     switch (key) {
1808     case '\r':
1809     case '\n':
1810     case KEY_ENTER:
1811       switch (m_selection_type) {
1812       case SelectionType::NewButton:
1813         AddNewField();
1814         return eKeyHandled;
1815       case SelectionType::RemoveButton:
1816         RemoveField();
1817         return eKeyHandled;
1818       default:
1819         break;
1820       }
1821       break;
1822     case '\t':
1823       SelectNext(key);
1824       return eKeyHandled;
1825     case KEY_SHIFT_TAB:
1826       SelectPrevious(key);
1827       return eKeyHandled;
1828     default:
1829       break;
1830     }
1831 
1832     // If the key wasn't handled and one of the fields is selected, pass the key
1833     // to that field.
1834     if (m_selection_type == SelectionType::Field) {
1835       return m_fields[m_selection_index].FieldDelegateHandleChar(key);
1836     }
1837 
1838     return eKeyNotHandled;
1839   }
1840 
1841   bool FieldDelegateOnLastOrOnlyElement() override {
1842     if (m_selection_type == SelectionType::NewButton) {
1843       return true;
1844     }
1845     return false;
1846   }
1847 
1848   bool FieldDelegateOnFirstOrOnlyElement() override {
1849     if (m_selection_type == SelectionType::NewButton &&
1850         GetNumberOfFields() == 0)
1851       return true;
1852 
1853     if (m_selection_type == SelectionType::Field && m_selection_index == 0) {
1854       FieldDelegate &field = m_fields[m_selection_index];
1855       return field.FieldDelegateOnFirstOrOnlyElement();
1856     }
1857 
1858     return false;
1859   }
1860 
1861   void FieldDelegateSelectFirstElement() override {
1862     if (GetNumberOfFields() == 0) {
1863       m_selection_type = SelectionType::NewButton;
1864       return;
1865     }
1866 
1867     m_selection_type = SelectionType::Field;
1868     m_selection_index = 0;
1869   }
1870 
1871   void FieldDelegateSelectLastElement() override {
1872     m_selection_type = SelectionType::NewButton;
1873     return;
1874   }
1875 
1876   int GetNumberOfFields() { return m_fields.size(); }
1877 
1878   // Returns the form delegate at the current index.
1879   T &GetField(int index) { return m_fields[index]; }
1880 
1881 protected:
1882   std::string m_label;
1883   // The default field delegate instance from which new field delegates will be
1884   // created though a copy.
1885   T m_default_field;
1886   std::vector<T> m_fields;
1887   int m_selection_index;
1888   // See SelectionType class enum.
1889   SelectionType m_selection_type;
1890 };
1891 
1892 class FormAction {
1893 public:
1894   FormAction(const char *label, std::function<void(Window &)> action)
1895       : m_action(action) {
1896     if (label)
1897       m_label = label;
1898   }
1899 
1900   // Draw a centered [Label].
1901   void Draw(SubPad &surface, bool is_selected) {
1902     int x = (surface.GetWidth() - m_label.length()) / 2;
1903     surface.MoveCursor(x, 0);
1904     if (is_selected)
1905       surface.AttributeOn(A_REVERSE);
1906     surface.PutChar('[');
1907     surface.PutCString(m_label.c_str());
1908     surface.PutChar(']');
1909     if (is_selected)
1910       surface.AttributeOff(A_REVERSE);
1911   }
1912 
1913   void Execute(Window &window) { m_action(window); }
1914 
1915   const std::string &GetLabel() { return m_label; }
1916 
1917 protected:
1918   std::string m_label;
1919   std::function<void(Window &)> m_action;
1920 };
1921 
1922 class FormDelegate {
1923 public:
1924   FormDelegate() {}
1925 
1926   virtual ~FormDelegate() = default;
1927 
1928   virtual std::string GetName() = 0;
1929 
1930   virtual void UpdateFieldsVisibility() { return; }
1931 
1932   FieldDelegate *GetField(uint32_t field_index) {
1933     if (field_index < m_fields.size())
1934       return m_fields[field_index].get();
1935     return nullptr;
1936   }
1937 
1938   FormAction &GetAction(int action_index) { return m_actions[action_index]; }
1939 
1940   int GetNumberOfFields() { return m_fields.size(); }
1941 
1942   int GetNumberOfActions() { return m_actions.size(); }
1943 
1944   bool HasError() { return !m_error.empty(); }
1945 
1946   void ClearError() { m_error.clear(); }
1947 
1948   const std::string &GetError() { return m_error; }
1949 
1950   void SetError(const char *error) { m_error = error; }
1951 
1952   // If all fields are valid, true is returned. Otherwise, an error message is
1953   // set and false is returned. This method is usually called at the start of an
1954   // action that requires valid fields.
1955   bool CheckFieldsValidity() {
1956     for (int i = 0; i < GetNumberOfFields(); i++) {
1957       if (GetField(i)->FieldDelegateHasError()) {
1958         SetError("Some fields are invalid!");
1959         return false;
1960       }
1961     }
1962     return true;
1963   }
1964 
1965   // Factory methods to create and add fields of specific types.
1966 
1967   TextFieldDelegate *AddTextField(const char *label, const char *content,
1968                                   bool required) {
1969     TextFieldDelegate *delegate =
1970         new TextFieldDelegate(label, content, required);
1971     m_fields.push_back(FieldDelegateUP(delegate));
1972     return delegate;
1973   }
1974 
1975   FileFieldDelegate *AddFileField(const char *label, const char *content,
1976                                   bool need_to_exist, bool required) {
1977     FileFieldDelegate *delegate =
1978         new FileFieldDelegate(label, content, need_to_exist, required);
1979     m_fields.push_back(FieldDelegateUP(delegate));
1980     return delegate;
1981   }
1982 
1983   DirectoryFieldDelegate *AddDirectoryField(const char *label,
1984                                             const char *content,
1985                                             bool need_to_exist, bool required) {
1986     DirectoryFieldDelegate *delegate =
1987         new DirectoryFieldDelegate(label, content, need_to_exist, required);
1988     m_fields.push_back(FieldDelegateUP(delegate));
1989     return delegate;
1990   }
1991 
1992   IntegerFieldDelegate *AddIntegerField(const char *label, int content,
1993                                         bool required) {
1994     IntegerFieldDelegate *delegate =
1995         new IntegerFieldDelegate(label, content, required);
1996     m_fields.push_back(FieldDelegateUP(delegate));
1997     return delegate;
1998   }
1999 
2000   BooleanFieldDelegate *AddBooleanField(const char *label, bool content) {
2001     BooleanFieldDelegate *delegate = new BooleanFieldDelegate(label, content);
2002     m_fields.push_back(FieldDelegateUP(delegate));
2003     return delegate;
2004   }
2005 
2006   ChoicesFieldDelegate *AddChoicesField(const char *label, int height,
2007                                         std::vector<std::string> choices) {
2008     ChoicesFieldDelegate *delegate =
2009         new ChoicesFieldDelegate(label, height, choices);
2010     m_fields.push_back(FieldDelegateUP(delegate));
2011     return delegate;
2012   }
2013 
2014   PlatformPluginFieldDelegate *AddPlatformPluginField(Debugger &debugger) {
2015     PlatformPluginFieldDelegate *delegate =
2016         new PlatformPluginFieldDelegate(debugger);
2017     m_fields.push_back(FieldDelegateUP(delegate));
2018     return delegate;
2019   }
2020 
2021   ProcessPluginFieldDelegate *AddProcessPluginField() {
2022     ProcessPluginFieldDelegate *delegate = new ProcessPluginFieldDelegate();
2023     m_fields.push_back(FieldDelegateUP(delegate));
2024     return delegate;
2025   }
2026 
2027   template <class T>
2028   ListFieldDelegate<T> *AddListField(const char *label, T default_field) {
2029     ListFieldDelegate<T> *delegate =
2030         new ListFieldDelegate<T>(label, default_field);
2031     m_fields.push_back(FieldDelegateUP(delegate));
2032     return delegate;
2033   }
2034 
2035   // Factory methods for adding actions.
2036 
2037   void AddAction(const char *label, std::function<void(Window &)> action) {
2038     m_actions.push_back(FormAction(label, action));
2039   }
2040 
2041 protected:
2042   std::vector<FieldDelegateUP> m_fields;
2043   std::vector<FormAction> m_actions;
2044   // Optional error message. If empty, form is considered to have no error.
2045   std::string m_error;
2046 };
2047 
2048 typedef std::shared_ptr<FormDelegate> FormDelegateSP;
2049 
2050 class FormWindowDelegate : public WindowDelegate {
2051 public:
2052   FormWindowDelegate(FormDelegateSP &delegate_sp)
2053       : m_delegate_sp(delegate_sp), m_selection_index(0),
2054         m_first_visible_line(0) {
2055     assert(m_delegate_sp->GetNumberOfActions() > 0);
2056     if (m_delegate_sp->GetNumberOfFields() > 0)
2057       m_selection_type = SelectionType::Field;
2058     else
2059       m_selection_type = SelectionType::Action;
2060   }
2061 
2062   // Signify which element is selected. If a field or an action is selected,
2063   // then m_selection_index signifies the particular field or action that is
2064   // selected.
2065   enum class SelectionType { Field, Action };
2066 
2067   // A form window is padded by one character from all sides. First, if an error
2068   // message exists, it is drawn followed by a separator. Then one or more
2069   // fields are drawn. Finally, all available actions are drawn on a single
2070   // line.
2071   //
2072   // ___<Form Name>_________________________________________________
2073   // |                                                             |
2074   // | - Error message if it exists.                               |
2075   // |-------------------------------------------------------------|
2076   // | Form elements here.                                         |
2077   // |                       Form actions here.                    |
2078   // |                                                             |
2079   // |______________________________________[Press Esc to cancel]__|
2080   //
2081 
2082   // One line for the error and another for the horizontal line.
2083   int GetErrorHeight() {
2084     if (m_delegate_sp->HasError())
2085       return 2;
2086     return 0;
2087   }
2088 
2089   // Actions span a single line.
2090   int GetActionsHeight() {
2091     if (m_delegate_sp->GetNumberOfActions() > 0)
2092       return 1;
2093     return 0;
2094   }
2095 
2096   // Get the total number of needed lines to draw the contents.
2097   int GetContentHeight() {
2098     int height = 0;
2099     height += GetErrorHeight();
2100     for (int i = 0; i < m_delegate_sp->GetNumberOfFields(); i++) {
2101       if (!m_delegate_sp->GetField(i)->FieldDelegateIsVisible())
2102         continue;
2103       height += m_delegate_sp->GetField(i)->FieldDelegateGetHeight();
2104     }
2105     height += GetActionsHeight();
2106     return height;
2107   }
2108 
2109   ScrollContext GetScrollContext() {
2110     if (m_selection_type == SelectionType::Action)
2111       return ScrollContext(GetContentHeight() - 1);
2112 
2113     FieldDelegate *field = m_delegate_sp->GetField(m_selection_index);
2114     ScrollContext context = field->FieldDelegateGetScrollContext();
2115 
2116     int offset = GetErrorHeight();
2117     for (int i = 0; i < m_selection_index; i++) {
2118       if (!m_delegate_sp->GetField(i)->FieldDelegateIsVisible())
2119         continue;
2120       offset += m_delegate_sp->GetField(i)->FieldDelegateGetHeight();
2121     }
2122     context.Offset(offset);
2123 
2124     // If the context is touching the error, include the error in the context as
2125     // well.
2126     if (context.start == GetErrorHeight())
2127       context.start = 0;
2128 
2129     return context;
2130   }
2131 
2132   void UpdateScrolling(DerivedWindow &surface) {
2133     ScrollContext context = GetScrollContext();
2134     int content_height = GetContentHeight();
2135     int surface_height = surface.GetHeight();
2136     int visible_height = std::min(content_height, surface_height);
2137     int last_visible_line = m_first_visible_line + visible_height - 1;
2138 
2139     // If the last visible line is bigger than the content, then it is invalid
2140     // and needs to be set to the last line in the content. This can happen when
2141     // a field has shrunk in height.
2142     if (last_visible_line > content_height - 1) {
2143       m_first_visible_line = content_height - visible_height;
2144     }
2145 
2146     if (context.start < m_first_visible_line) {
2147       m_first_visible_line = context.start;
2148       return;
2149     }
2150 
2151     if (context.end > last_visible_line) {
2152       m_first_visible_line = context.end - visible_height + 1;
2153     }
2154   }
2155 
2156   void DrawError(SubPad &surface) {
2157     if (!m_delegate_sp->HasError())
2158       return;
2159     surface.MoveCursor(0, 0);
2160     surface.AttributeOn(COLOR_PAIR(RedOnBlack));
2161     surface.PutChar(ACS_DIAMOND);
2162     surface.PutChar(' ');
2163     surface.PutCStringTruncated(1, m_delegate_sp->GetError().c_str());
2164     surface.AttributeOff(COLOR_PAIR(RedOnBlack));
2165 
2166     surface.MoveCursor(0, 1);
2167     surface.HorizontalLine(surface.GetWidth());
2168   }
2169 
2170   void DrawFields(SubPad &surface) {
2171     int line = 0;
2172     int width = surface.GetWidth();
2173     bool a_field_is_selected = m_selection_type == SelectionType::Field;
2174     for (int i = 0; i < m_delegate_sp->GetNumberOfFields(); i++) {
2175       FieldDelegate *field = m_delegate_sp->GetField(i);
2176       if (!field->FieldDelegateIsVisible())
2177         continue;
2178       bool is_field_selected = a_field_is_selected && m_selection_index == i;
2179       int height = field->FieldDelegateGetHeight();
2180       Rect bounds = Rect(Point(0, line), Size(width, height));
2181       SubPad field_surface = SubPad(surface, bounds);
2182       field->FieldDelegateDraw(field_surface, is_field_selected);
2183       line += height;
2184     }
2185   }
2186 
2187   void DrawActions(SubPad &surface) {
2188     int number_of_actions = m_delegate_sp->GetNumberOfActions();
2189     int width = surface.GetWidth() / number_of_actions;
2190     bool an_action_is_selected = m_selection_type == SelectionType::Action;
2191     int x = 0;
2192     for (int i = 0; i < number_of_actions; i++) {
2193       bool is_action_selected = an_action_is_selected && m_selection_index == i;
2194       FormAction &action = m_delegate_sp->GetAction(i);
2195       Rect bounds = Rect(Point(x, 0), Size(width, 1));
2196       SubPad action_surface = SubPad(surface, bounds);
2197       action.Draw(action_surface, is_action_selected);
2198       x += width;
2199     }
2200   }
2201 
2202   void DrawElements(SubPad &surface) {
2203     Rect frame = surface.GetFrame();
2204     Rect fields_bounds, actions_bounds;
2205     frame.HorizontalSplit(surface.GetHeight() - GetActionsHeight(),
2206                           fields_bounds, actions_bounds);
2207     SubPad fields_surface = SubPad(surface, fields_bounds);
2208     SubPad actions_surface = SubPad(surface, actions_bounds);
2209 
2210     DrawFields(fields_surface);
2211     DrawActions(actions_surface);
2212   }
2213 
2214   // Contents are first drawn on a pad. Then a subset of that pad is copied to
2215   // the derived window starting at the first visible line. This essentially
2216   // provides scrolling functionality.
2217   void DrawContent(DerivedWindow &surface) {
2218     UpdateScrolling(surface);
2219 
2220     int width = surface.GetWidth();
2221     int height = GetContentHeight();
2222     Pad pad = Pad(Size(width, height));
2223 
2224     Rect frame = pad.GetFrame();
2225     Rect error_bounds, elements_bounds;
2226     frame.HorizontalSplit(GetErrorHeight(), error_bounds, elements_bounds);
2227     SubPad error_surface = SubPad(pad, error_bounds);
2228     SubPad elements_surface = SubPad(pad, elements_bounds);
2229 
2230     DrawError(error_surface);
2231     DrawElements(elements_surface);
2232 
2233     int copy_height = std::min(surface.GetHeight(), pad.GetHeight());
2234     pad.CopyToSurface(surface, Point(0, m_first_visible_line), Point(),
2235                       Size(width, copy_height));
2236   }
2237 
2238   bool WindowDelegateDraw(Window &window, bool force) override {
2239     m_delegate_sp->UpdateFieldsVisibility();
2240 
2241     window.Erase();
2242 
2243     window.DrawTitleBox(m_delegate_sp->GetName().c_str(),
2244                         "Press Esc to cancel");
2245 
2246     Rect content_bounds = window.GetFrame();
2247     content_bounds.Inset(2, 2);
2248     DerivedWindow content_surface = DerivedWindow(window, content_bounds);
2249 
2250     DrawContent(content_surface);
2251     return true;
2252   }
2253 
2254   void SkipNextHiddenFields() {
2255     while (true) {
2256       if (m_delegate_sp->GetField(m_selection_index)->FieldDelegateIsVisible())
2257         return;
2258 
2259       if (m_selection_index == m_delegate_sp->GetNumberOfFields() - 1) {
2260         m_selection_type = SelectionType::Action;
2261         m_selection_index = 0;
2262         return;
2263       }
2264 
2265       m_selection_index++;
2266     }
2267   }
2268 
2269   HandleCharResult SelectNext(int key) {
2270     if (m_selection_type == SelectionType::Action) {
2271       if (m_selection_index < m_delegate_sp->GetNumberOfActions() - 1) {
2272         m_selection_index++;
2273         return eKeyHandled;
2274       }
2275 
2276       m_selection_index = 0;
2277       m_selection_type = SelectionType::Field;
2278       SkipNextHiddenFields();
2279       if (m_selection_type == SelectionType::Field) {
2280         FieldDelegate *next_field = m_delegate_sp->GetField(m_selection_index);
2281         next_field->FieldDelegateSelectFirstElement();
2282       }
2283       return eKeyHandled;
2284     }
2285 
2286     FieldDelegate *field = m_delegate_sp->GetField(m_selection_index);
2287     if (!field->FieldDelegateOnLastOrOnlyElement()) {
2288       return field->FieldDelegateHandleChar(key);
2289     }
2290 
2291     field->FieldDelegateExitCallback();
2292 
2293     if (m_selection_index == m_delegate_sp->GetNumberOfFields() - 1) {
2294       m_selection_type = SelectionType::Action;
2295       m_selection_index = 0;
2296       return eKeyHandled;
2297     }
2298 
2299     m_selection_index++;
2300     SkipNextHiddenFields();
2301 
2302     if (m_selection_type == SelectionType::Field) {
2303       FieldDelegate *next_field = m_delegate_sp->GetField(m_selection_index);
2304       next_field->FieldDelegateSelectFirstElement();
2305     }
2306 
2307     return eKeyHandled;
2308   }
2309 
2310   void SkipPreviousHiddenFields() {
2311     while (true) {
2312       if (m_delegate_sp->GetField(m_selection_index)->FieldDelegateIsVisible())
2313         return;
2314 
2315       if (m_selection_index == 0) {
2316         m_selection_type = SelectionType::Action;
2317         m_selection_index = 0;
2318         return;
2319       }
2320 
2321       m_selection_index--;
2322     }
2323   }
2324 
2325   HandleCharResult SelectPrevious(int key) {
2326     if (m_selection_type == SelectionType::Action) {
2327       if (m_selection_index > 0) {
2328         m_selection_index--;
2329         return eKeyHandled;
2330       }
2331       m_selection_index = m_delegate_sp->GetNumberOfFields() - 1;
2332       m_selection_type = SelectionType::Field;
2333       SkipPreviousHiddenFields();
2334       if (m_selection_type == SelectionType::Field) {
2335         FieldDelegate *previous_field =
2336             m_delegate_sp->GetField(m_selection_index);
2337         previous_field->FieldDelegateSelectLastElement();
2338       }
2339       return eKeyHandled;
2340     }
2341 
2342     FieldDelegate *field = m_delegate_sp->GetField(m_selection_index);
2343     if (!field->FieldDelegateOnFirstOrOnlyElement()) {
2344       return field->FieldDelegateHandleChar(key);
2345     }
2346 
2347     field->FieldDelegateExitCallback();
2348 
2349     if (m_selection_index == 0) {
2350       m_selection_type = SelectionType::Action;
2351       m_selection_index = m_delegate_sp->GetNumberOfActions() - 1;
2352       return eKeyHandled;
2353     }
2354 
2355     m_selection_index--;
2356     SkipPreviousHiddenFields();
2357 
2358     if (m_selection_type == SelectionType::Field) {
2359       FieldDelegate *previous_field =
2360           m_delegate_sp->GetField(m_selection_index);
2361       previous_field->FieldDelegateSelectLastElement();
2362     }
2363 
2364     return eKeyHandled;
2365   }
2366 
2367   void ExecuteAction(Window &window) {
2368     FormAction &action = m_delegate_sp->GetAction(m_selection_index);
2369     action.Execute(window);
2370     if (m_delegate_sp->HasError()) {
2371       m_first_visible_line = 0;
2372       m_selection_index = 0;
2373       m_selection_type = SelectionType::Field;
2374     }
2375   }
2376 
2377   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
2378     switch (key) {
2379     case '\r':
2380     case '\n':
2381     case KEY_ENTER:
2382       if (m_selection_type == SelectionType::Action) {
2383         ExecuteAction(window);
2384         return eKeyHandled;
2385       }
2386       break;
2387     case '\t':
2388       return SelectNext(key);
2389     case KEY_SHIFT_TAB:
2390       return SelectPrevious(key);
2391     case KEY_ESCAPE:
2392       window.GetParent()->RemoveSubWindow(&window);
2393       return eKeyHandled;
2394     default:
2395       break;
2396     }
2397 
2398     // If the key wasn't handled and one of the fields is selected, pass the key
2399     // to that field.
2400     if (m_selection_type == SelectionType::Field) {
2401       FieldDelegate *field = m_delegate_sp->GetField(m_selection_index);
2402       return field->FieldDelegateHandleChar(key);
2403     }
2404 
2405     return eKeyNotHandled;
2406   }
2407 
2408 protected:
2409   FormDelegateSP m_delegate_sp;
2410   // The index of the currently selected SelectionType.
2411   int m_selection_index;
2412   // See SelectionType class enum.
2413   SelectionType m_selection_type;
2414   // The first visible line from the pad.
2415   int m_first_visible_line;
2416 };
2417 
2418 ///////////////////////////
2419 // Form Delegate Instances
2420 ///////////////////////////
2421 
2422 class DetachOrKillProcessFormDelegate : public FormDelegate {
2423 public:
2424   DetachOrKillProcessFormDelegate(Process *process) : m_process(process) {
2425     SetError("There is a running process, either detach or kill it.");
2426 
2427     m_keep_stopped_field =
2428         AddBooleanField("Keep process stopped when detaching.", false);
2429 
2430     AddAction("Detach", [this](Window &window) { Detach(window); });
2431     AddAction("Kill", [this](Window &window) { Kill(window); });
2432   }
2433 
2434   std::string GetName() override { return "Detach/Kill Process"; }
2435 
2436   void Kill(Window &window) {
2437     Status destroy_status(m_process->Destroy(false));
2438     if (destroy_status.Fail()) {
2439       SetError("Failed to kill process.");
2440       return;
2441     }
2442     window.GetParent()->RemoveSubWindow(&window);
2443   }
2444 
2445   void Detach(Window &window) {
2446     Status detach_status(m_process->Detach(m_keep_stopped_field->GetBoolean()));
2447     if (detach_status.Fail()) {
2448       SetError("Failed to detach from process.");
2449       return;
2450     }
2451     window.GetParent()->RemoveSubWindow(&window);
2452   }
2453 
2454 protected:
2455   Process *m_process;
2456   BooleanFieldDelegate *m_keep_stopped_field;
2457 };
2458 
2459 class ProcessAttachFormDelegate : public FormDelegate {
2460 public:
2461   ProcessAttachFormDelegate(Debugger &debugger, WindowSP main_window_sp)
2462       : m_debugger(debugger), m_main_window_sp(main_window_sp) {
2463     std::vector<std::string> types;
2464     types.push_back(std::string("Name"));
2465     types.push_back(std::string("PID"));
2466     m_type_field = AddChoicesField("Attach By", 2, types);
2467     m_pid_field = AddIntegerField("PID", 0, true);
2468     m_name_field =
2469         AddTextField("Process Name", GetDefaultProcessName().c_str(), true);
2470     m_continue_field = AddBooleanField("Continue once attached.", false);
2471     m_wait_for_field = AddBooleanField("Wait for process to launch.", false);
2472     m_include_existing_field =
2473         AddBooleanField("Include existing processes.", false);
2474     m_show_advanced_field = AddBooleanField("Show advanced settings.", false);
2475     m_plugin_field = AddProcessPluginField();
2476 
2477     AddAction("Attach", [this](Window &window) { Attach(window); });
2478   }
2479 
2480   std::string GetName() override { return "Attach Process"; }
2481 
2482   void UpdateFieldsVisibility() override {
2483     if (m_type_field->GetChoiceContent() == "Name") {
2484       m_pid_field->FieldDelegateHide();
2485       m_name_field->FieldDelegateShow();
2486       m_wait_for_field->FieldDelegateShow();
2487       if (m_wait_for_field->GetBoolean())
2488         m_include_existing_field->FieldDelegateShow();
2489       else
2490         m_include_existing_field->FieldDelegateHide();
2491     } else {
2492       m_pid_field->FieldDelegateShow();
2493       m_name_field->FieldDelegateHide();
2494       m_wait_for_field->FieldDelegateHide();
2495       m_include_existing_field->FieldDelegateHide();
2496     }
2497     if (m_show_advanced_field->GetBoolean())
2498       m_plugin_field->FieldDelegateShow();
2499     else
2500       m_plugin_field->FieldDelegateHide();
2501   }
2502 
2503   // Get the basename of the target's main executable if available, empty string
2504   // otherwise.
2505   std::string GetDefaultProcessName() {
2506     Target *target = m_debugger.GetSelectedTarget().get();
2507     if (target == nullptr)
2508       return "";
2509 
2510     ModuleSP module_sp = target->GetExecutableModule();
2511     if (!module_sp->IsExecutable())
2512       return "";
2513 
2514     return module_sp->GetFileSpec().GetFilename().AsCString();
2515   }
2516 
2517   bool StopRunningProcess() {
2518     ExecutionContext exe_ctx =
2519         m_debugger.GetCommandInterpreter().GetExecutionContext();
2520 
2521     if (!exe_ctx.HasProcessScope())
2522       return false;
2523 
2524     Process *process = exe_ctx.GetProcessPtr();
2525     if (!(process && process->IsAlive()))
2526       return false;
2527 
2528     FormDelegateSP form_delegate_sp =
2529         FormDelegateSP(new DetachOrKillProcessFormDelegate(process));
2530     Rect bounds = m_main_window_sp->GetCenteredRect(85, 8);
2531     WindowSP form_window_sp = m_main_window_sp->CreateSubWindow(
2532         form_delegate_sp->GetName().c_str(), bounds, true);
2533     WindowDelegateSP window_delegate_sp =
2534         WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
2535     form_window_sp->SetDelegate(window_delegate_sp);
2536 
2537     return true;
2538   }
2539 
2540   Target *GetTarget() {
2541     Target *target = m_debugger.GetSelectedTarget().get();
2542 
2543     if (target != nullptr)
2544       return target;
2545 
2546     TargetSP new_target_sp;
2547     m_debugger.GetTargetList().CreateTarget(
2548         m_debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp);
2549 
2550     target = new_target_sp.get();
2551 
2552     if (target == nullptr)
2553       SetError("Failed to create target.");
2554 
2555     m_debugger.GetTargetList().SetSelectedTarget(new_target_sp);
2556 
2557     return target;
2558   }
2559 
2560   ProcessAttachInfo GetAttachInfo() {
2561     ProcessAttachInfo attach_info;
2562     attach_info.SetContinueOnceAttached(m_continue_field->GetBoolean());
2563     if (m_type_field->GetChoiceContent() == "Name") {
2564       attach_info.GetExecutableFile().SetFile(m_name_field->GetText(),
2565                                               FileSpec::Style::native);
2566       attach_info.SetWaitForLaunch(m_wait_for_field->GetBoolean());
2567       if (m_wait_for_field->GetBoolean())
2568         attach_info.SetIgnoreExisting(!m_include_existing_field->GetBoolean());
2569     } else {
2570       attach_info.SetProcessID(m_pid_field->GetInteger());
2571     }
2572     attach_info.SetProcessPluginName(m_plugin_field->GetPluginName());
2573 
2574     return attach_info;
2575   }
2576 
2577   void Attach(Window &window) {
2578     ClearError();
2579 
2580     bool all_fields_are_valid = CheckFieldsValidity();
2581     if (!all_fields_are_valid)
2582       return;
2583 
2584     bool process_is_running = StopRunningProcess();
2585     if (process_is_running)
2586       return;
2587 
2588     Target *target = GetTarget();
2589     if (HasError())
2590       return;
2591 
2592     StreamString stream;
2593     ProcessAttachInfo attach_info = GetAttachInfo();
2594     Status status = target->Attach(attach_info, &stream);
2595 
2596     if (status.Fail()) {
2597       SetError(status.AsCString());
2598       return;
2599     }
2600 
2601     ProcessSP process_sp(target->GetProcessSP());
2602     if (!process_sp) {
2603       SetError("Attached sucessfully but target has no process.");
2604       return;
2605     }
2606 
2607     if (attach_info.GetContinueOnceAttached())
2608       process_sp->Resume();
2609 
2610     window.GetParent()->RemoveSubWindow(&window);
2611   }
2612 
2613 protected:
2614   Debugger &m_debugger;
2615   WindowSP m_main_window_sp;
2616 
2617   ChoicesFieldDelegate *m_type_field;
2618   IntegerFieldDelegate *m_pid_field;
2619   TextFieldDelegate *m_name_field;
2620   BooleanFieldDelegate *m_continue_field;
2621   BooleanFieldDelegate *m_wait_for_field;
2622   BooleanFieldDelegate *m_include_existing_field;
2623   BooleanFieldDelegate *m_show_advanced_field;
2624   ProcessPluginFieldDelegate *m_plugin_field;
2625 };
2626 
2627 class MenuDelegate {
2628 public:
2629   virtual ~MenuDelegate() = default;
2630 
2631   virtual MenuActionResult MenuDelegateAction(Menu &menu) = 0;
2632 };
2633 
2634 class Menu : public WindowDelegate {
2635 public:
2636   enum class Type { Invalid, Bar, Item, Separator };
2637 
2638   // Menubar or separator constructor
2639   Menu(Type type);
2640 
2641   // Menuitem constructor
2642   Menu(const char *name, const char *key_name, int key_value,
2643        uint64_t identifier);
2644 
2645   ~Menu() override = default;
2646 
2647   const MenuDelegateSP &GetDelegate() const { return m_delegate_sp; }
2648 
2649   void SetDelegate(const MenuDelegateSP &delegate_sp) {
2650     m_delegate_sp = delegate_sp;
2651   }
2652 
2653   void RecalculateNameLengths();
2654 
2655   void AddSubmenu(const MenuSP &menu_sp);
2656 
2657   int DrawAndRunMenu(Window &window);
2658 
2659   void DrawMenuTitle(Window &window, bool highlight);
2660 
2661   bool WindowDelegateDraw(Window &window, bool force) override;
2662 
2663   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override;
2664 
2665   MenuActionResult ActionPrivate(Menu &menu) {
2666     MenuActionResult result = MenuActionResult::NotHandled;
2667     if (m_delegate_sp) {
2668       result = m_delegate_sp->MenuDelegateAction(menu);
2669       if (result != MenuActionResult::NotHandled)
2670         return result;
2671     } else if (m_parent) {
2672       result = m_parent->ActionPrivate(menu);
2673       if (result != MenuActionResult::NotHandled)
2674         return result;
2675     }
2676     return m_canned_result;
2677   }
2678 
2679   MenuActionResult Action() {
2680     // Call the recursive action so it can try to handle it with the menu
2681     // delegate, and if not, try our parent menu
2682     return ActionPrivate(*this);
2683   }
2684 
2685   void SetCannedResult(MenuActionResult result) { m_canned_result = result; }
2686 
2687   Menus &GetSubmenus() { return m_submenus; }
2688 
2689   const Menus &GetSubmenus() const { return m_submenus; }
2690 
2691   int GetSelectedSubmenuIndex() const { return m_selected; }
2692 
2693   void SetSelectedSubmenuIndex(int idx) { m_selected = idx; }
2694 
2695   Type GetType() const { return m_type; }
2696 
2697   int GetStartingColumn() const { return m_start_col; }
2698 
2699   void SetStartingColumn(int col) { m_start_col = col; }
2700 
2701   int GetKeyValue() const { return m_key_value; }
2702 
2703   std::string &GetName() { return m_name; }
2704 
2705   int GetDrawWidth() const {
2706     return m_max_submenu_name_length + m_max_submenu_key_name_length + 8;
2707   }
2708 
2709   uint64_t GetIdentifier() const { return m_identifier; }
2710 
2711   void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
2712 
2713 protected:
2714   std::string m_name;
2715   std::string m_key_name;
2716   uint64_t m_identifier;
2717   Type m_type;
2718   int m_key_value;
2719   int m_start_col;
2720   int m_max_submenu_name_length;
2721   int m_max_submenu_key_name_length;
2722   int m_selected;
2723   Menu *m_parent;
2724   Menus m_submenus;
2725   WindowSP m_menu_window_sp;
2726   MenuActionResult m_canned_result;
2727   MenuDelegateSP m_delegate_sp;
2728 };
2729 
2730 // Menubar or separator constructor
2731 Menu::Menu(Type type)
2732     : m_name(), m_key_name(), m_identifier(0), m_type(type), m_key_value(0),
2733       m_start_col(0), m_max_submenu_name_length(0),
2734       m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr),
2735       m_submenus(), m_canned_result(MenuActionResult::NotHandled),
2736       m_delegate_sp() {}
2737 
2738 // Menuitem constructor
2739 Menu::Menu(const char *name, const char *key_name, int key_value,
2740            uint64_t identifier)
2741     : m_name(), m_key_name(), m_identifier(identifier), m_type(Type::Invalid),
2742       m_key_value(key_value), m_start_col(0), m_max_submenu_name_length(0),
2743       m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr),
2744       m_submenus(), m_canned_result(MenuActionResult::NotHandled),
2745       m_delegate_sp() {
2746   if (name && name[0]) {
2747     m_name = name;
2748     m_type = Type::Item;
2749     if (key_name && key_name[0])
2750       m_key_name = key_name;
2751   } else {
2752     m_type = Type::Separator;
2753   }
2754 }
2755 
2756 void Menu::RecalculateNameLengths() {
2757   m_max_submenu_name_length = 0;
2758   m_max_submenu_key_name_length = 0;
2759   Menus &submenus = GetSubmenus();
2760   const size_t num_submenus = submenus.size();
2761   for (size_t i = 0; i < num_submenus; ++i) {
2762     Menu *submenu = submenus[i].get();
2763     if (static_cast<size_t>(m_max_submenu_name_length) < submenu->m_name.size())
2764       m_max_submenu_name_length = submenu->m_name.size();
2765     if (static_cast<size_t>(m_max_submenu_key_name_length) <
2766         submenu->m_key_name.size())
2767       m_max_submenu_key_name_length = submenu->m_key_name.size();
2768   }
2769 }
2770 
2771 void Menu::AddSubmenu(const MenuSP &menu_sp) {
2772   menu_sp->m_parent = this;
2773   if (static_cast<size_t>(m_max_submenu_name_length) < menu_sp->m_name.size())
2774     m_max_submenu_name_length = menu_sp->m_name.size();
2775   if (static_cast<size_t>(m_max_submenu_key_name_length) <
2776       menu_sp->m_key_name.size())
2777     m_max_submenu_key_name_length = menu_sp->m_key_name.size();
2778   m_submenus.push_back(menu_sp);
2779 }
2780 
2781 void Menu::DrawMenuTitle(Window &window, bool highlight) {
2782   if (m_type == Type::Separator) {
2783     window.MoveCursor(0, window.GetCursorY());
2784     window.PutChar(ACS_LTEE);
2785     int width = window.GetWidth();
2786     if (width > 2) {
2787       width -= 2;
2788       for (int i = 0; i < width; ++i)
2789         window.PutChar(ACS_HLINE);
2790     }
2791     window.PutChar(ACS_RTEE);
2792   } else {
2793     const int shortcut_key = m_key_value;
2794     bool underlined_shortcut = false;
2795     const attr_t highlight_attr = A_REVERSE;
2796     if (highlight)
2797       window.AttributeOn(highlight_attr);
2798     if (llvm::isPrint(shortcut_key)) {
2799       size_t lower_pos = m_name.find(tolower(shortcut_key));
2800       size_t upper_pos = m_name.find(toupper(shortcut_key));
2801       const char *name = m_name.c_str();
2802       size_t pos = std::min<size_t>(lower_pos, upper_pos);
2803       if (pos != std::string::npos) {
2804         underlined_shortcut = true;
2805         if (pos > 0) {
2806           window.PutCString(name, pos);
2807           name += pos;
2808         }
2809         const attr_t shortcut_attr = A_UNDERLINE | A_BOLD;
2810         window.AttributeOn(shortcut_attr);
2811         window.PutChar(name[0]);
2812         window.AttributeOff(shortcut_attr);
2813         name++;
2814         if (name[0])
2815           window.PutCString(name);
2816       }
2817     }
2818 
2819     if (!underlined_shortcut) {
2820       window.PutCString(m_name.c_str());
2821     }
2822 
2823     if (highlight)
2824       window.AttributeOff(highlight_attr);
2825 
2826     if (m_key_name.empty()) {
2827       if (!underlined_shortcut && llvm::isPrint(m_key_value)) {
2828         window.AttributeOn(COLOR_PAIR(MagentaOnWhite));
2829         window.Printf(" (%c)", m_key_value);
2830         window.AttributeOff(COLOR_PAIR(MagentaOnWhite));
2831       }
2832     } else {
2833       window.AttributeOn(COLOR_PAIR(MagentaOnWhite));
2834       window.Printf(" (%s)", m_key_name.c_str());
2835       window.AttributeOff(COLOR_PAIR(MagentaOnWhite));
2836     }
2837   }
2838 }
2839 
2840 bool Menu::WindowDelegateDraw(Window &window, bool force) {
2841   Menus &submenus = GetSubmenus();
2842   const size_t num_submenus = submenus.size();
2843   const int selected_idx = GetSelectedSubmenuIndex();
2844   Menu::Type menu_type = GetType();
2845   switch (menu_type) {
2846   case Menu::Type::Bar: {
2847     window.SetBackground(BlackOnWhite);
2848     window.MoveCursor(0, 0);
2849     for (size_t i = 0; i < num_submenus; ++i) {
2850       Menu *menu = submenus[i].get();
2851       if (i > 0)
2852         window.PutChar(' ');
2853       menu->SetStartingColumn(window.GetCursorX());
2854       window.PutCString("| ");
2855       menu->DrawMenuTitle(window, false);
2856     }
2857     window.PutCString(" |");
2858   } break;
2859 
2860   case Menu::Type::Item: {
2861     int y = 1;
2862     int x = 3;
2863     // Draw the menu
2864     int cursor_x = 0;
2865     int cursor_y = 0;
2866     window.Erase();
2867     window.SetBackground(BlackOnWhite);
2868     window.Box();
2869     for (size_t i = 0; i < num_submenus; ++i) {
2870       const bool is_selected = (i == static_cast<size_t>(selected_idx));
2871       window.MoveCursor(x, y + i);
2872       if (is_selected) {
2873         // Remember where we want the cursor to be
2874         cursor_x = x - 1;
2875         cursor_y = y + i;
2876       }
2877       submenus[i]->DrawMenuTitle(window, is_selected);
2878     }
2879     window.MoveCursor(cursor_x, cursor_y);
2880   } break;
2881 
2882   default:
2883   case Menu::Type::Separator:
2884     break;
2885   }
2886   return true; // Drawing handled...
2887 }
2888 
2889 HandleCharResult Menu::WindowDelegateHandleChar(Window &window, int key) {
2890   HandleCharResult result = eKeyNotHandled;
2891 
2892   Menus &submenus = GetSubmenus();
2893   const size_t num_submenus = submenus.size();
2894   const int selected_idx = GetSelectedSubmenuIndex();
2895   Menu::Type menu_type = GetType();
2896   if (menu_type == Menu::Type::Bar) {
2897     MenuSP run_menu_sp;
2898     switch (key) {
2899     case KEY_DOWN:
2900     case KEY_UP:
2901       // Show last menu or first menu
2902       if (selected_idx < static_cast<int>(num_submenus))
2903         run_menu_sp = submenus[selected_idx];
2904       else if (!submenus.empty())
2905         run_menu_sp = submenus.front();
2906       result = eKeyHandled;
2907       break;
2908 
2909     case KEY_RIGHT:
2910       ++m_selected;
2911       if (m_selected >= static_cast<int>(num_submenus))
2912         m_selected = 0;
2913       if (m_selected < static_cast<int>(num_submenus))
2914         run_menu_sp = submenus[m_selected];
2915       else if (!submenus.empty())
2916         run_menu_sp = submenus.front();
2917       result = eKeyHandled;
2918       break;
2919 
2920     case KEY_LEFT:
2921       --m_selected;
2922       if (m_selected < 0)
2923         m_selected = num_submenus - 1;
2924       if (m_selected < static_cast<int>(num_submenus))
2925         run_menu_sp = submenus[m_selected];
2926       else if (!submenus.empty())
2927         run_menu_sp = submenus.front();
2928       result = eKeyHandled;
2929       break;
2930 
2931     default:
2932       for (size_t i = 0; i < num_submenus; ++i) {
2933         if (submenus[i]->GetKeyValue() == key) {
2934           SetSelectedSubmenuIndex(i);
2935           run_menu_sp = submenus[i];
2936           result = eKeyHandled;
2937           break;
2938         }
2939       }
2940       break;
2941     }
2942 
2943     if (run_menu_sp) {
2944       // Run the action on this menu in case we need to populate the menu with
2945       // dynamic content and also in case check marks, and any other menu
2946       // decorations need to be calculated
2947       if (run_menu_sp->Action() == MenuActionResult::Quit)
2948         return eQuitApplication;
2949 
2950       Rect menu_bounds;
2951       menu_bounds.origin.x = run_menu_sp->GetStartingColumn();
2952       menu_bounds.origin.y = 1;
2953       menu_bounds.size.width = run_menu_sp->GetDrawWidth();
2954       menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2;
2955       if (m_menu_window_sp)
2956         window.GetParent()->RemoveSubWindow(m_menu_window_sp.get());
2957 
2958       m_menu_window_sp = window.GetParent()->CreateSubWindow(
2959           run_menu_sp->GetName().c_str(), menu_bounds, true);
2960       m_menu_window_sp->SetDelegate(run_menu_sp);
2961     }
2962   } else if (menu_type == Menu::Type::Item) {
2963     switch (key) {
2964     case KEY_DOWN:
2965       if (m_submenus.size() > 1) {
2966         const int start_select = m_selected;
2967         while (++m_selected != start_select) {
2968           if (static_cast<size_t>(m_selected) >= num_submenus)
2969             m_selected = 0;
2970           if (m_submenus[m_selected]->GetType() == Type::Separator)
2971             continue;
2972           else
2973             break;
2974         }
2975         return eKeyHandled;
2976       }
2977       break;
2978 
2979     case KEY_UP:
2980       if (m_submenus.size() > 1) {
2981         const int start_select = m_selected;
2982         while (--m_selected != start_select) {
2983           if (m_selected < static_cast<int>(0))
2984             m_selected = num_submenus - 1;
2985           if (m_submenus[m_selected]->GetType() == Type::Separator)
2986             continue;
2987           else
2988             break;
2989         }
2990         return eKeyHandled;
2991       }
2992       break;
2993 
2994     case KEY_RETURN:
2995       if (static_cast<size_t>(selected_idx) < num_submenus) {
2996         if (submenus[selected_idx]->Action() == MenuActionResult::Quit)
2997           return eQuitApplication;
2998         window.GetParent()->RemoveSubWindow(&window);
2999         return eKeyHandled;
3000       }
3001       break;
3002 
3003     case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in
3004                      // case other chars are entered for escaped sequences
3005       window.GetParent()->RemoveSubWindow(&window);
3006       return eKeyHandled;
3007 
3008     default:
3009       for (size_t i = 0; i < num_submenus; ++i) {
3010         Menu *menu = submenus[i].get();
3011         if (menu->GetKeyValue() == key) {
3012           SetSelectedSubmenuIndex(i);
3013           window.GetParent()->RemoveSubWindow(&window);
3014           if (menu->Action() == MenuActionResult::Quit)
3015             return eQuitApplication;
3016           return eKeyHandled;
3017         }
3018       }
3019       break;
3020     }
3021   } else if (menu_type == Menu::Type::Separator) {
3022   }
3023   return result;
3024 }
3025 
3026 class Application {
3027 public:
3028   Application(FILE *in, FILE *out)
3029       : m_window_sp(), m_screen(nullptr), m_in(in), m_out(out) {}
3030 
3031   ~Application() {
3032     m_window_delegates.clear();
3033     m_window_sp.reset();
3034     if (m_screen) {
3035       ::delscreen(m_screen);
3036       m_screen = nullptr;
3037     }
3038   }
3039 
3040   void Initialize() {
3041     m_screen = ::newterm(nullptr, m_out, m_in);
3042     ::start_color();
3043     ::curs_set(0);
3044     ::noecho();
3045     ::keypad(stdscr, TRUE);
3046   }
3047 
3048   void Terminate() { ::endwin(); }
3049 
3050   void Run(Debugger &debugger) {
3051     bool done = false;
3052     int delay_in_tenths_of_a_second = 1;
3053 
3054     // Alas the threading model in curses is a bit lame so we need to resort to
3055     // polling every 0.5 seconds. We could poll for stdin ourselves and then
3056     // pass the keys down but then we need to translate all of the escape
3057     // sequences ourselves. So we resort to polling for input because we need
3058     // to receive async process events while in this loop.
3059 
3060     halfdelay(delay_in_tenths_of_a_second); // Poll using some number of tenths
3061                                             // of seconds seconds when calling
3062                                             // Window::GetChar()
3063 
3064     ListenerSP listener_sp(
3065         Listener::MakeListener("lldb.IOHandler.curses.Application"));
3066     ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass());
3067     debugger.EnableForwardEvents(listener_sp);
3068 
3069     m_update_screen = true;
3070 #if defined(__APPLE__)
3071     std::deque<int> escape_chars;
3072 #endif
3073 
3074     while (!done) {
3075       if (m_update_screen) {
3076         m_window_sp->Draw(false);
3077         // All windows should be calling Window::DeferredRefresh() instead of
3078         // Window::Refresh() so we can do a single update and avoid any screen
3079         // blinking
3080         update_panels();
3081 
3082         // Cursor hiding isn't working on MacOSX, so hide it in the top left
3083         // corner
3084         m_window_sp->MoveCursor(0, 0);
3085 
3086         doupdate();
3087         m_update_screen = false;
3088       }
3089 
3090 #if defined(__APPLE__)
3091       // Terminal.app doesn't map its function keys correctly, F1-F4 default
3092       // to: \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if
3093       // possible
3094       int ch;
3095       if (escape_chars.empty())
3096         ch = m_window_sp->GetChar();
3097       else {
3098         ch = escape_chars.front();
3099         escape_chars.pop_front();
3100       }
3101       if (ch == KEY_ESCAPE) {
3102         int ch2 = m_window_sp->GetChar();
3103         if (ch2 == 'O') {
3104           int ch3 = m_window_sp->GetChar();
3105           switch (ch3) {
3106           case 'P':
3107             ch = KEY_F(1);
3108             break;
3109           case 'Q':
3110             ch = KEY_F(2);
3111             break;
3112           case 'R':
3113             ch = KEY_F(3);
3114             break;
3115           case 'S':
3116             ch = KEY_F(4);
3117             break;
3118           default:
3119             escape_chars.push_back(ch2);
3120             if (ch3 != -1)
3121               escape_chars.push_back(ch3);
3122             break;
3123           }
3124         } else if (ch2 != -1)
3125           escape_chars.push_back(ch2);
3126       }
3127 #else
3128       int ch = m_window_sp->GetChar();
3129 
3130 #endif
3131       if (ch == -1) {
3132         if (feof(m_in) || ferror(m_in)) {
3133           done = true;
3134         } else {
3135           // Just a timeout from using halfdelay(), check for events
3136           EventSP event_sp;
3137           while (listener_sp->PeekAtNextEvent()) {
3138             listener_sp->GetEvent(event_sp, std::chrono::seconds(0));
3139 
3140             if (event_sp) {
3141               Broadcaster *broadcaster = event_sp->GetBroadcaster();
3142               if (broadcaster) {
3143                 // uint32_t event_type = event_sp->GetType();
3144                 ConstString broadcaster_class(
3145                     broadcaster->GetBroadcasterClass());
3146                 if (broadcaster_class == broadcaster_class_process) {
3147                   m_update_screen = true;
3148                   continue; // Don't get any key, just update our view
3149                 }
3150               }
3151             }
3152           }
3153         }
3154       } else {
3155         HandleCharResult key_result = m_window_sp->HandleChar(ch);
3156         switch (key_result) {
3157         case eKeyHandled:
3158           m_update_screen = true;
3159           break;
3160         case eKeyNotHandled:
3161           if (ch == 12) { // Ctrl+L, force full redraw
3162             redrawwin(m_window_sp->get());
3163             m_update_screen = true;
3164           }
3165           break;
3166         case eQuitApplication:
3167           done = true;
3168           break;
3169         }
3170       }
3171     }
3172 
3173     debugger.CancelForwardEvents(listener_sp);
3174   }
3175 
3176   WindowSP &GetMainWindow() {
3177     if (!m_window_sp)
3178       m_window_sp = std::make_shared<Window>("main", stdscr, false);
3179     return m_window_sp;
3180   }
3181 
3182   void TerminalSizeChanged() {
3183     ::endwin();
3184     ::refresh();
3185     Rect content_bounds = m_window_sp->GetFrame();
3186     m_window_sp->SetBounds(content_bounds);
3187     if (WindowSP menubar_window_sp = m_window_sp->FindSubWindow("Menubar"))
3188       menubar_window_sp->SetBounds(content_bounds.MakeMenuBar());
3189     if (WindowSP status_window_sp = m_window_sp->FindSubWindow("Status"))
3190       status_window_sp->SetBounds(content_bounds.MakeStatusBar());
3191 
3192     WindowSP source_window_sp = m_window_sp->FindSubWindow("Source");
3193     WindowSP variables_window_sp = m_window_sp->FindSubWindow("Variables");
3194     WindowSP registers_window_sp = m_window_sp->FindSubWindow("Registers");
3195     WindowSP threads_window_sp = m_window_sp->FindSubWindow("Threads");
3196 
3197     Rect threads_bounds;
3198     Rect source_variables_bounds;
3199     content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds,
3200                                            threads_bounds);
3201     if (threads_window_sp)
3202       threads_window_sp->SetBounds(threads_bounds);
3203     else
3204       source_variables_bounds = content_bounds;
3205 
3206     Rect source_bounds;
3207     Rect variables_registers_bounds;
3208     source_variables_bounds.HorizontalSplitPercentage(
3209         0.70, source_bounds, variables_registers_bounds);
3210     if (variables_window_sp || registers_window_sp) {
3211       if (variables_window_sp && registers_window_sp) {
3212         Rect variables_bounds;
3213         Rect registers_bounds;
3214         variables_registers_bounds.VerticalSplitPercentage(
3215             0.50, variables_bounds, registers_bounds);
3216         variables_window_sp->SetBounds(variables_bounds);
3217         registers_window_sp->SetBounds(registers_bounds);
3218       } else if (variables_window_sp) {
3219         variables_window_sp->SetBounds(variables_registers_bounds);
3220       } else {
3221         registers_window_sp->SetBounds(variables_registers_bounds);
3222       }
3223     } else {
3224       source_bounds = source_variables_bounds;
3225     }
3226 
3227     source_window_sp->SetBounds(source_bounds);
3228 
3229     touchwin(stdscr);
3230     redrawwin(m_window_sp->get());
3231     m_update_screen = true;
3232   }
3233 
3234 protected:
3235   WindowSP m_window_sp;
3236   WindowDelegates m_window_delegates;
3237   SCREEN *m_screen;
3238   FILE *m_in;
3239   FILE *m_out;
3240   bool m_update_screen = false;
3241 };
3242 
3243 } // namespace curses
3244 
3245 using namespace curses;
3246 
3247 struct Row {
3248   ValueObjectUpdater value;
3249   Row *parent;
3250   // The process stop ID when the children were calculated.
3251   uint32_t children_stop_id = 0;
3252   int row_idx = 0;
3253   int x = 1;
3254   int y = 1;
3255   bool might_have_children;
3256   bool expanded = false;
3257   bool calculated_children = false;
3258   std::vector<Row> children;
3259 
3260   Row(const ValueObjectSP &v, Row *p)
3261       : value(v), parent(p),
3262         might_have_children(v ? v->MightHaveChildren() : false) {}
3263 
3264   size_t GetDepth() const {
3265     if (parent)
3266       return 1 + parent->GetDepth();
3267     return 0;
3268   }
3269 
3270   void Expand() { expanded = true; }
3271 
3272   std::vector<Row> &GetChildren() {
3273     ProcessSP process_sp = value.GetProcessSP();
3274     auto stop_id = process_sp->GetStopID();
3275     if (process_sp && stop_id != children_stop_id) {
3276       children_stop_id = stop_id;
3277       calculated_children = false;
3278     }
3279     if (!calculated_children) {
3280       children.clear();
3281       calculated_children = true;
3282       ValueObjectSP valobj = value.GetSP();
3283       if (valobj) {
3284         const size_t num_children = valobj->GetNumChildren();
3285         for (size_t i = 0; i < num_children; ++i) {
3286           children.push_back(Row(valobj->GetChildAtIndex(i, true), this));
3287         }
3288       }
3289     }
3290     return children;
3291   }
3292 
3293   void Unexpand() {
3294     expanded = false;
3295     calculated_children = false;
3296     children.clear();
3297   }
3298 
3299   void DrawTree(Window &window) {
3300     if (parent)
3301       parent->DrawTreeForChild(window, this, 0);
3302 
3303     if (might_have_children) {
3304       // It we can get UTF8 characters to work we should try to use the
3305       // "symbol" UTF8 string below
3306       //            const char *symbol = "";
3307       //            if (row.expanded)
3308       //                symbol = "\xe2\x96\xbd ";
3309       //            else
3310       //                symbol = "\xe2\x96\xb7 ";
3311       //            window.PutCString (symbol);
3312 
3313       // The ACS_DARROW and ACS_RARROW don't look very nice they are just a 'v'
3314       // or '>' character...
3315       //            if (expanded)
3316       //                window.PutChar (ACS_DARROW);
3317       //            else
3318       //                window.PutChar (ACS_RARROW);
3319       // Since we can't find any good looking right arrow/down arrow symbols,
3320       // just use a diamond...
3321       window.PutChar(ACS_DIAMOND);
3322       window.PutChar(ACS_HLINE);
3323     }
3324   }
3325 
3326   void DrawTreeForChild(Window &window, Row *child, uint32_t reverse_depth) {
3327     if (parent)
3328       parent->DrawTreeForChild(window, this, reverse_depth + 1);
3329 
3330     if (&GetChildren().back() == child) {
3331       // Last child
3332       if (reverse_depth == 0) {
3333         window.PutChar(ACS_LLCORNER);
3334         window.PutChar(ACS_HLINE);
3335       } else {
3336         window.PutChar(' ');
3337         window.PutChar(' ');
3338       }
3339     } else {
3340       if (reverse_depth == 0) {
3341         window.PutChar(ACS_LTEE);
3342         window.PutChar(ACS_HLINE);
3343       } else {
3344         window.PutChar(ACS_VLINE);
3345         window.PutChar(' ');
3346       }
3347     }
3348   }
3349 };
3350 
3351 struct DisplayOptions {
3352   bool show_types;
3353 };
3354 
3355 class TreeItem;
3356 
3357 class TreeDelegate {
3358 public:
3359   TreeDelegate() = default;
3360   virtual ~TreeDelegate() = default;
3361 
3362   virtual void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) = 0;
3363   virtual void TreeDelegateGenerateChildren(TreeItem &item) = 0;
3364   virtual void TreeDelegateUpdateSelection(TreeItem &root, int &selection_index,
3365                                            TreeItem *&selected_item) {
3366     return;
3367   }
3368   virtual bool TreeDelegateItemSelected(
3369       TreeItem &item) = 0; // Return true if we need to update views
3370   virtual bool TreeDelegateExpandRootByDefault() { return false; }
3371 };
3372 
3373 typedef std::shared_ptr<TreeDelegate> TreeDelegateSP;
3374 
3375 class TreeItem {
3376 public:
3377   TreeItem(TreeItem *parent, TreeDelegate &delegate, bool might_have_children)
3378       : m_parent(parent), m_delegate(delegate), m_user_data(nullptr),
3379         m_identifier(0), m_row_idx(-1), m_children(),
3380         m_might_have_children(might_have_children), m_is_expanded(false) {
3381     if (m_parent == nullptr)
3382       m_is_expanded = m_delegate.TreeDelegateExpandRootByDefault();
3383   }
3384 
3385   TreeItem &operator=(const TreeItem &rhs) {
3386     if (this != &rhs) {
3387       m_parent = rhs.m_parent;
3388       m_delegate = rhs.m_delegate;
3389       m_user_data = rhs.m_user_data;
3390       m_identifier = rhs.m_identifier;
3391       m_row_idx = rhs.m_row_idx;
3392       m_children = rhs.m_children;
3393       m_might_have_children = rhs.m_might_have_children;
3394       m_is_expanded = rhs.m_is_expanded;
3395     }
3396     return *this;
3397   }
3398 
3399   TreeItem(const TreeItem &) = default;
3400 
3401   size_t GetDepth() const {
3402     if (m_parent)
3403       return 1 + m_parent->GetDepth();
3404     return 0;
3405   }
3406 
3407   int GetRowIndex() const { return m_row_idx; }
3408 
3409   void ClearChildren() { m_children.clear(); }
3410 
3411   void Resize(size_t n, const TreeItem &t) { m_children.resize(n, t); }
3412 
3413   TreeItem &operator[](size_t i) { return m_children[i]; }
3414 
3415   void SetRowIndex(int row_idx) { m_row_idx = row_idx; }
3416 
3417   size_t GetNumChildren() {
3418     m_delegate.TreeDelegateGenerateChildren(*this);
3419     return m_children.size();
3420   }
3421 
3422   void ItemWasSelected() { m_delegate.TreeDelegateItemSelected(*this); }
3423 
3424   void CalculateRowIndexes(int &row_idx) {
3425     SetRowIndex(row_idx);
3426     ++row_idx;
3427 
3428     const bool expanded = IsExpanded();
3429 
3430     // The root item must calculate its children, or we must calculate the
3431     // number of children if the item is expanded
3432     if (m_parent == nullptr || expanded)
3433       GetNumChildren();
3434 
3435     for (auto &item : m_children) {
3436       if (expanded)
3437         item.CalculateRowIndexes(row_idx);
3438       else
3439         item.SetRowIndex(-1);
3440     }
3441   }
3442 
3443   TreeItem *GetParent() { return m_parent; }
3444 
3445   bool IsExpanded() const { return m_is_expanded; }
3446 
3447   void Expand() { m_is_expanded = true; }
3448 
3449   void Unexpand() { m_is_expanded = false; }
3450 
3451   bool Draw(Window &window, const int first_visible_row,
3452             const uint32_t selected_row_idx, int &row_idx, int &num_rows_left) {
3453     if (num_rows_left <= 0)
3454       return false;
3455 
3456     if (m_row_idx >= first_visible_row) {
3457       window.MoveCursor(2, row_idx + 1);
3458 
3459       if (m_parent)
3460         m_parent->DrawTreeForChild(window, this, 0);
3461 
3462       if (m_might_have_children) {
3463         // It we can get UTF8 characters to work we should try to use the
3464         // "symbol" UTF8 string below
3465         //            const char *symbol = "";
3466         //            if (row.expanded)
3467         //                symbol = "\xe2\x96\xbd ";
3468         //            else
3469         //                symbol = "\xe2\x96\xb7 ";
3470         //            window.PutCString (symbol);
3471 
3472         // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
3473         // 'v' or '>' character...
3474         //            if (expanded)
3475         //                window.PutChar (ACS_DARROW);
3476         //            else
3477         //                window.PutChar (ACS_RARROW);
3478         // Since we can't find any good looking right arrow/down arrow symbols,
3479         // just use a diamond...
3480         window.PutChar(ACS_DIAMOND);
3481         window.PutChar(ACS_HLINE);
3482       }
3483       bool highlight = (selected_row_idx == static_cast<size_t>(m_row_idx)) &&
3484                        window.IsActive();
3485 
3486       if (highlight)
3487         window.AttributeOn(A_REVERSE);
3488 
3489       m_delegate.TreeDelegateDrawTreeItem(*this, window);
3490 
3491       if (highlight)
3492         window.AttributeOff(A_REVERSE);
3493       ++row_idx;
3494       --num_rows_left;
3495     }
3496 
3497     if (num_rows_left <= 0)
3498       return false; // We are done drawing...
3499 
3500     if (IsExpanded()) {
3501       for (auto &item : m_children) {
3502         // If we displayed all the rows and item.Draw() returns false we are
3503         // done drawing and can exit this for loop
3504         if (!item.Draw(window, first_visible_row, selected_row_idx, row_idx,
3505                        num_rows_left))
3506           break;
3507       }
3508     }
3509     return num_rows_left >= 0; // Return true if not done drawing yet
3510   }
3511 
3512   void DrawTreeForChild(Window &window, TreeItem *child,
3513                         uint32_t reverse_depth) {
3514     if (m_parent)
3515       m_parent->DrawTreeForChild(window, this, reverse_depth + 1);
3516 
3517     if (&m_children.back() == child) {
3518       // Last child
3519       if (reverse_depth == 0) {
3520         window.PutChar(ACS_LLCORNER);
3521         window.PutChar(ACS_HLINE);
3522       } else {
3523         window.PutChar(' ');
3524         window.PutChar(' ');
3525       }
3526     } else {
3527       if (reverse_depth == 0) {
3528         window.PutChar(ACS_LTEE);
3529         window.PutChar(ACS_HLINE);
3530       } else {
3531         window.PutChar(ACS_VLINE);
3532         window.PutChar(' ');
3533       }
3534     }
3535   }
3536 
3537   TreeItem *GetItemForRowIndex(uint32_t row_idx) {
3538     if (static_cast<uint32_t>(m_row_idx) == row_idx)
3539       return this;
3540     if (m_children.empty())
3541       return nullptr;
3542     if (IsExpanded()) {
3543       for (auto &item : m_children) {
3544         TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx);
3545         if (selected_item_ptr)
3546           return selected_item_ptr;
3547       }
3548     }
3549     return nullptr;
3550   }
3551 
3552   void *GetUserData() const { return m_user_data; }
3553 
3554   void SetUserData(void *user_data) { m_user_data = user_data; }
3555 
3556   uint64_t GetIdentifier() const { return m_identifier; }
3557 
3558   void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
3559 
3560   void SetMightHaveChildren(bool b) { m_might_have_children = b; }
3561 
3562 protected:
3563   TreeItem *m_parent;
3564   TreeDelegate &m_delegate;
3565   void *m_user_data;
3566   uint64_t m_identifier;
3567   int m_row_idx; // Zero based visible row index, -1 if not visible or for the
3568                  // root item
3569   std::vector<TreeItem> m_children;
3570   bool m_might_have_children;
3571   bool m_is_expanded;
3572 };
3573 
3574 class TreeWindowDelegate : public WindowDelegate {
3575 public:
3576   TreeWindowDelegate(Debugger &debugger, const TreeDelegateSP &delegate_sp)
3577       : m_debugger(debugger), m_delegate_sp(delegate_sp),
3578         m_root(nullptr, *delegate_sp, true), m_selected_item(nullptr),
3579         m_num_rows(0), m_selected_row_idx(0), m_first_visible_row(0),
3580         m_min_x(0), m_min_y(0), m_max_x(0), m_max_y(0) {}
3581 
3582   int NumVisibleRows() const { return m_max_y - m_min_y; }
3583 
3584   bool WindowDelegateDraw(Window &window, bool force) override {
3585     ExecutionContext exe_ctx(
3586         m_debugger.GetCommandInterpreter().GetExecutionContext());
3587     Process *process = exe_ctx.GetProcessPtr();
3588 
3589     bool display_content = false;
3590     if (process) {
3591       StateType state = process->GetState();
3592       if (StateIsStoppedState(state, true)) {
3593         // We are stopped, so it is ok to
3594         display_content = true;
3595       } else if (StateIsRunningState(state)) {
3596         return true; // Don't do any updating when we are running
3597       }
3598     }
3599 
3600     m_min_x = 2;
3601     m_min_y = 1;
3602     m_max_x = window.GetWidth() - 1;
3603     m_max_y = window.GetHeight() - 1;
3604 
3605     window.Erase();
3606     window.DrawTitleBox(window.GetName());
3607 
3608     if (display_content) {
3609       const int num_visible_rows = NumVisibleRows();
3610       m_num_rows = 0;
3611       m_root.CalculateRowIndexes(m_num_rows);
3612       m_delegate_sp->TreeDelegateUpdateSelection(m_root, m_selected_row_idx,
3613                                                  m_selected_item);
3614 
3615       // If we unexpanded while having something selected our total number of
3616       // rows is less than the num visible rows, then make sure we show all the
3617       // rows by setting the first visible row accordingly.
3618       if (m_first_visible_row > 0 && m_num_rows < num_visible_rows)
3619         m_first_visible_row = 0;
3620 
3621       // Make sure the selected row is always visible
3622       if (m_selected_row_idx < m_first_visible_row)
3623         m_first_visible_row = m_selected_row_idx;
3624       else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
3625         m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
3626 
3627       int row_idx = 0;
3628       int num_rows_left = num_visible_rows;
3629       m_root.Draw(window, m_first_visible_row, m_selected_row_idx, row_idx,
3630                   num_rows_left);
3631       // Get the selected row
3632       m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3633     } else {
3634       m_selected_item = nullptr;
3635     }
3636 
3637     return true; // Drawing handled
3638   }
3639 
3640   const char *WindowDelegateGetHelpText() override {
3641     return "Thread window keyboard shortcuts:";
3642   }
3643 
3644   KeyHelp *WindowDelegateGetKeyHelp() override {
3645     static curses::KeyHelp g_source_view_key_help[] = {
3646         {KEY_UP, "Select previous item"},
3647         {KEY_DOWN, "Select next item"},
3648         {KEY_RIGHT, "Expand the selected item"},
3649         {KEY_LEFT,
3650          "Unexpand the selected item or select parent if not expanded"},
3651         {KEY_PPAGE, "Page up"},
3652         {KEY_NPAGE, "Page down"},
3653         {'h', "Show help dialog"},
3654         {' ', "Toggle item expansion"},
3655         {',', "Page up"},
3656         {'.', "Page down"},
3657         {'\0', nullptr}};
3658     return g_source_view_key_help;
3659   }
3660 
3661   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
3662     switch (c) {
3663     case ',':
3664     case KEY_PPAGE:
3665       // Page up key
3666       if (m_first_visible_row > 0) {
3667         if (m_first_visible_row > m_max_y)
3668           m_first_visible_row -= m_max_y;
3669         else
3670           m_first_visible_row = 0;
3671         m_selected_row_idx = m_first_visible_row;
3672         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3673         if (m_selected_item)
3674           m_selected_item->ItemWasSelected();
3675       }
3676       return eKeyHandled;
3677 
3678     case '.':
3679     case KEY_NPAGE:
3680       // Page down key
3681       if (m_num_rows > m_max_y) {
3682         if (m_first_visible_row + m_max_y < m_num_rows) {
3683           m_first_visible_row += m_max_y;
3684           m_selected_row_idx = m_first_visible_row;
3685           m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3686           if (m_selected_item)
3687             m_selected_item->ItemWasSelected();
3688         }
3689       }
3690       return eKeyHandled;
3691 
3692     case KEY_UP:
3693       if (m_selected_row_idx > 0) {
3694         --m_selected_row_idx;
3695         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3696         if (m_selected_item)
3697           m_selected_item->ItemWasSelected();
3698       }
3699       return eKeyHandled;
3700 
3701     case KEY_DOWN:
3702       if (m_selected_row_idx + 1 < m_num_rows) {
3703         ++m_selected_row_idx;
3704         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3705         if (m_selected_item)
3706           m_selected_item->ItemWasSelected();
3707       }
3708       return eKeyHandled;
3709 
3710     case KEY_RIGHT:
3711       if (m_selected_item) {
3712         if (!m_selected_item->IsExpanded())
3713           m_selected_item->Expand();
3714       }
3715       return eKeyHandled;
3716 
3717     case KEY_LEFT:
3718       if (m_selected_item) {
3719         if (m_selected_item->IsExpanded())
3720           m_selected_item->Unexpand();
3721         else if (m_selected_item->GetParent()) {
3722           m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex();
3723           m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3724           if (m_selected_item)
3725             m_selected_item->ItemWasSelected();
3726         }
3727       }
3728       return eKeyHandled;
3729 
3730     case ' ':
3731       // Toggle expansion state when SPACE is pressed
3732       if (m_selected_item) {
3733         if (m_selected_item->IsExpanded())
3734           m_selected_item->Unexpand();
3735         else
3736           m_selected_item->Expand();
3737       }
3738       return eKeyHandled;
3739 
3740     case 'h':
3741       window.CreateHelpSubwindow();
3742       return eKeyHandled;
3743 
3744     default:
3745       break;
3746     }
3747     return eKeyNotHandled;
3748   }
3749 
3750 protected:
3751   Debugger &m_debugger;
3752   TreeDelegateSP m_delegate_sp;
3753   TreeItem m_root;
3754   TreeItem *m_selected_item;
3755   int m_num_rows;
3756   int m_selected_row_idx;
3757   int m_first_visible_row;
3758   int m_min_x;
3759   int m_min_y;
3760   int m_max_x;
3761   int m_max_y;
3762 };
3763 
3764 class FrameTreeDelegate : public TreeDelegate {
3765 public:
3766   FrameTreeDelegate() : TreeDelegate() {
3767     FormatEntity::Parse(
3768         "frame #${frame.index}: {${function.name}${function.pc-offset}}}",
3769         m_format);
3770   }
3771 
3772   ~FrameTreeDelegate() override = default;
3773 
3774   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
3775     Thread *thread = (Thread *)item.GetUserData();
3776     if (thread) {
3777       const uint64_t frame_idx = item.GetIdentifier();
3778       StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_idx);
3779       if (frame_sp) {
3780         StreamString strm;
3781         const SymbolContext &sc =
3782             frame_sp->GetSymbolContext(eSymbolContextEverything);
3783         ExecutionContext exe_ctx(frame_sp);
3784         if (FormatEntity::Format(m_format, strm, &sc, &exe_ctx, nullptr,
3785                                  nullptr, false, false)) {
3786           int right_pad = 1;
3787           window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
3788         }
3789       }
3790     }
3791   }
3792 
3793   void TreeDelegateGenerateChildren(TreeItem &item) override {
3794     // No children for frames yet...
3795   }
3796 
3797   bool TreeDelegateItemSelected(TreeItem &item) override {
3798     Thread *thread = (Thread *)item.GetUserData();
3799     if (thread) {
3800       thread->GetProcess()->GetThreadList().SetSelectedThreadByID(
3801           thread->GetID());
3802       const uint64_t frame_idx = item.GetIdentifier();
3803       thread->SetSelectedFrameByIndex(frame_idx);
3804       return true;
3805     }
3806     return false;
3807   }
3808 
3809 protected:
3810   FormatEntity::Entry m_format;
3811 };
3812 
3813 class ThreadTreeDelegate : public TreeDelegate {
3814 public:
3815   ThreadTreeDelegate(Debugger &debugger)
3816       : TreeDelegate(), m_debugger(debugger), m_tid(LLDB_INVALID_THREAD_ID),
3817         m_stop_id(UINT32_MAX) {
3818     FormatEntity::Parse("thread #${thread.index}: tid = ${thread.id}{, stop "
3819                         "reason = ${thread.stop-reason}}",
3820                         m_format);
3821   }
3822 
3823   ~ThreadTreeDelegate() override = default;
3824 
3825   ProcessSP GetProcess() {
3826     return m_debugger.GetCommandInterpreter()
3827         .GetExecutionContext()
3828         .GetProcessSP();
3829   }
3830 
3831   ThreadSP GetThread(const TreeItem &item) {
3832     ProcessSP process_sp = GetProcess();
3833     if (process_sp)
3834       return process_sp->GetThreadList().FindThreadByID(item.GetIdentifier());
3835     return ThreadSP();
3836   }
3837 
3838   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
3839     ThreadSP thread_sp = GetThread(item);
3840     if (thread_sp) {
3841       StreamString strm;
3842       ExecutionContext exe_ctx(thread_sp);
3843       if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
3844                                nullptr, false, false)) {
3845         int right_pad = 1;
3846         window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
3847       }
3848     }
3849   }
3850 
3851   void TreeDelegateGenerateChildren(TreeItem &item) override {
3852     ProcessSP process_sp = GetProcess();
3853     if (process_sp && process_sp->IsAlive()) {
3854       StateType state = process_sp->GetState();
3855       if (StateIsStoppedState(state, true)) {
3856         ThreadSP thread_sp = GetThread(item);
3857         if (thread_sp) {
3858           if (m_stop_id == process_sp->GetStopID() &&
3859               thread_sp->GetID() == m_tid)
3860             return; // Children are already up to date
3861           if (!m_frame_delegate_sp) {
3862             // Always expand the thread item the first time we show it
3863             m_frame_delegate_sp = std::make_shared<FrameTreeDelegate>();
3864           }
3865 
3866           m_stop_id = process_sp->GetStopID();
3867           m_tid = thread_sp->GetID();
3868 
3869           TreeItem t(&item, *m_frame_delegate_sp, false);
3870           size_t num_frames = thread_sp->GetStackFrameCount();
3871           item.Resize(num_frames, t);
3872           for (size_t i = 0; i < num_frames; ++i) {
3873             item[i].SetUserData(thread_sp.get());
3874             item[i].SetIdentifier(i);
3875           }
3876         }
3877         return;
3878       }
3879     }
3880     item.ClearChildren();
3881   }
3882 
3883   bool TreeDelegateItemSelected(TreeItem &item) override {
3884     ProcessSP process_sp = GetProcess();
3885     if (process_sp && process_sp->IsAlive()) {
3886       StateType state = process_sp->GetState();
3887       if (StateIsStoppedState(state, true)) {
3888         ThreadSP thread_sp = GetThread(item);
3889         if (thread_sp) {
3890           ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList();
3891           std::lock_guard<std::recursive_mutex> guard(thread_list.GetMutex());
3892           ThreadSP selected_thread_sp = thread_list.GetSelectedThread();
3893           if (selected_thread_sp->GetID() != thread_sp->GetID()) {
3894             thread_list.SetSelectedThreadByID(thread_sp->GetID());
3895             return true;
3896           }
3897         }
3898       }
3899     }
3900     return false;
3901   }
3902 
3903 protected:
3904   Debugger &m_debugger;
3905   std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp;
3906   lldb::user_id_t m_tid;
3907   uint32_t m_stop_id;
3908   FormatEntity::Entry m_format;
3909 };
3910 
3911 class ThreadsTreeDelegate : public TreeDelegate {
3912 public:
3913   ThreadsTreeDelegate(Debugger &debugger)
3914       : TreeDelegate(), m_thread_delegate_sp(), m_debugger(debugger),
3915         m_stop_id(UINT32_MAX), m_update_selection(false) {
3916     FormatEntity::Parse("process ${process.id}{, name = ${process.name}}",
3917                         m_format);
3918   }
3919 
3920   ~ThreadsTreeDelegate() override = default;
3921 
3922   ProcessSP GetProcess() {
3923     return m_debugger.GetCommandInterpreter()
3924         .GetExecutionContext()
3925         .GetProcessSP();
3926   }
3927 
3928   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
3929     ProcessSP process_sp = GetProcess();
3930     if (process_sp && process_sp->IsAlive()) {
3931       StreamString strm;
3932       ExecutionContext exe_ctx(process_sp);
3933       if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
3934                                nullptr, false, false)) {
3935         int right_pad = 1;
3936         window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
3937       }
3938     }
3939   }
3940 
3941   void TreeDelegateGenerateChildren(TreeItem &item) override {
3942     ProcessSP process_sp = GetProcess();
3943     m_update_selection = false;
3944     if (process_sp && process_sp->IsAlive()) {
3945       StateType state = process_sp->GetState();
3946       if (StateIsStoppedState(state, true)) {
3947         const uint32_t stop_id = process_sp->GetStopID();
3948         if (m_stop_id == stop_id)
3949           return; // Children are already up to date
3950 
3951         m_stop_id = stop_id;
3952         m_update_selection = true;
3953 
3954         if (!m_thread_delegate_sp) {
3955           // Always expand the thread item the first time we show it
3956           // item.Expand();
3957           m_thread_delegate_sp =
3958               std::make_shared<ThreadTreeDelegate>(m_debugger);
3959         }
3960 
3961         TreeItem t(&item, *m_thread_delegate_sp, false);
3962         ThreadList &threads = process_sp->GetThreadList();
3963         std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
3964         ThreadSP selected_thread = threads.GetSelectedThread();
3965         size_t num_threads = threads.GetSize();
3966         item.Resize(num_threads, t);
3967         for (size_t i = 0; i < num_threads; ++i) {
3968           ThreadSP thread = threads.GetThreadAtIndex(i);
3969           item[i].SetIdentifier(thread->GetID());
3970           item[i].SetMightHaveChildren(true);
3971           if (selected_thread->GetID() == thread->GetID())
3972             item[i].Expand();
3973         }
3974         return;
3975       }
3976     }
3977     item.ClearChildren();
3978   }
3979 
3980   void TreeDelegateUpdateSelection(TreeItem &root, int &selection_index,
3981                                    TreeItem *&selected_item) override {
3982     if (!m_update_selection)
3983       return;
3984 
3985     ProcessSP process_sp = GetProcess();
3986     if (!(process_sp && process_sp->IsAlive()))
3987       return;
3988 
3989     StateType state = process_sp->GetState();
3990     if (!StateIsStoppedState(state, true))
3991       return;
3992 
3993     ThreadList &threads = process_sp->GetThreadList();
3994     std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
3995     ThreadSP selected_thread = threads.GetSelectedThread();
3996     size_t num_threads = threads.GetSize();
3997     for (size_t i = 0; i < num_threads; ++i) {
3998       ThreadSP thread = threads.GetThreadAtIndex(i);
3999       if (selected_thread->GetID() == thread->GetID()) {
4000         selected_item = &root[i][thread->GetSelectedFrameIndex()];
4001         selection_index = selected_item->GetRowIndex();
4002         return;
4003       }
4004     }
4005   }
4006 
4007   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
4008 
4009   bool TreeDelegateExpandRootByDefault() override { return true; }
4010 
4011 protected:
4012   std::shared_ptr<ThreadTreeDelegate> m_thread_delegate_sp;
4013   Debugger &m_debugger;
4014   uint32_t m_stop_id;
4015   bool m_update_selection;
4016   FormatEntity::Entry m_format;
4017 };
4018 
4019 class ValueObjectListDelegate : public WindowDelegate {
4020 public:
4021   ValueObjectListDelegate() : m_rows() {}
4022 
4023   ValueObjectListDelegate(ValueObjectList &valobj_list)
4024       : m_rows(), m_selected_row(nullptr), m_selected_row_idx(0),
4025         m_first_visible_row(0), m_num_rows(0), m_max_x(0), m_max_y(0) {
4026     SetValues(valobj_list);
4027   }
4028 
4029   ~ValueObjectListDelegate() override = default;
4030 
4031   void SetValues(ValueObjectList &valobj_list) {
4032     m_selected_row = nullptr;
4033     m_selected_row_idx = 0;
4034     m_first_visible_row = 0;
4035     m_num_rows = 0;
4036     m_rows.clear();
4037     for (auto &valobj_sp : valobj_list.GetObjects())
4038       m_rows.push_back(Row(valobj_sp, nullptr));
4039   }
4040 
4041   bool WindowDelegateDraw(Window &window, bool force) override {
4042     m_num_rows = 0;
4043     m_min_x = 2;
4044     m_min_y = 1;
4045     m_max_x = window.GetWidth() - 1;
4046     m_max_y = window.GetHeight() - 1;
4047 
4048     window.Erase();
4049     window.DrawTitleBox(window.GetName());
4050 
4051     const int num_visible_rows = NumVisibleRows();
4052     const int num_rows = CalculateTotalNumberRows(m_rows);
4053 
4054     // If we unexpanded while having something selected our total number of
4055     // rows is less than the num visible rows, then make sure we show all the
4056     // rows by setting the first visible row accordingly.
4057     if (m_first_visible_row > 0 && num_rows < num_visible_rows)
4058       m_first_visible_row = 0;
4059 
4060     // Make sure the selected row is always visible
4061     if (m_selected_row_idx < m_first_visible_row)
4062       m_first_visible_row = m_selected_row_idx;
4063     else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
4064       m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
4065 
4066     DisplayRows(window, m_rows, g_options);
4067 
4068     // Get the selected row
4069     m_selected_row = GetRowForRowIndex(m_selected_row_idx);
4070     // Keep the cursor on the selected row so the highlight and the cursor are
4071     // always on the same line
4072     if (m_selected_row)
4073       window.MoveCursor(m_selected_row->x, m_selected_row->y);
4074 
4075     return true; // Drawing handled
4076   }
4077 
4078   KeyHelp *WindowDelegateGetKeyHelp() override {
4079     static curses::KeyHelp g_source_view_key_help[] = {
4080         {KEY_UP, "Select previous item"},
4081         {KEY_DOWN, "Select next item"},
4082         {KEY_RIGHT, "Expand selected item"},
4083         {KEY_LEFT, "Unexpand selected item or select parent if not expanded"},
4084         {KEY_PPAGE, "Page up"},
4085         {KEY_NPAGE, "Page down"},
4086         {'A', "Format as annotated address"},
4087         {'b', "Format as binary"},
4088         {'B', "Format as hex bytes with ASCII"},
4089         {'c', "Format as character"},
4090         {'d', "Format as a signed integer"},
4091         {'D', "Format selected value using the default format for the type"},
4092         {'f', "Format as float"},
4093         {'h', "Show help dialog"},
4094         {'i', "Format as instructions"},
4095         {'o', "Format as octal"},
4096         {'p', "Format as pointer"},
4097         {'s', "Format as C string"},
4098         {'t', "Toggle showing/hiding type names"},
4099         {'u', "Format as an unsigned integer"},
4100         {'x', "Format as hex"},
4101         {'X', "Format as uppercase hex"},
4102         {' ', "Toggle item expansion"},
4103         {',', "Page up"},
4104         {'.', "Page down"},
4105         {'\0', nullptr}};
4106     return g_source_view_key_help;
4107   }
4108 
4109   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
4110     switch (c) {
4111     case 'x':
4112     case 'X':
4113     case 'o':
4114     case 's':
4115     case 'u':
4116     case 'd':
4117     case 'D':
4118     case 'i':
4119     case 'A':
4120     case 'p':
4121     case 'c':
4122     case 'b':
4123     case 'B':
4124     case 'f':
4125       // Change the format for the currently selected item
4126       if (m_selected_row) {
4127         auto valobj_sp = m_selected_row->value.GetSP();
4128         if (valobj_sp)
4129           valobj_sp->SetFormat(FormatForChar(c));
4130       }
4131       return eKeyHandled;
4132 
4133     case 't':
4134       // Toggle showing type names
4135       g_options.show_types = !g_options.show_types;
4136       return eKeyHandled;
4137 
4138     case ',':
4139     case KEY_PPAGE:
4140       // Page up key
4141       if (m_first_visible_row > 0) {
4142         if (static_cast<int>(m_first_visible_row) > m_max_y)
4143           m_first_visible_row -= m_max_y;
4144         else
4145           m_first_visible_row = 0;
4146         m_selected_row_idx = m_first_visible_row;
4147       }
4148       return eKeyHandled;
4149 
4150     case '.':
4151     case KEY_NPAGE:
4152       // Page down key
4153       if (m_num_rows > static_cast<size_t>(m_max_y)) {
4154         if (m_first_visible_row + m_max_y < m_num_rows) {
4155           m_first_visible_row += m_max_y;
4156           m_selected_row_idx = m_first_visible_row;
4157         }
4158       }
4159       return eKeyHandled;
4160 
4161     case KEY_UP:
4162       if (m_selected_row_idx > 0)
4163         --m_selected_row_idx;
4164       return eKeyHandled;
4165 
4166     case KEY_DOWN:
4167       if (m_selected_row_idx + 1 < m_num_rows)
4168         ++m_selected_row_idx;
4169       return eKeyHandled;
4170 
4171     case KEY_RIGHT:
4172       if (m_selected_row) {
4173         if (!m_selected_row->expanded)
4174           m_selected_row->Expand();
4175       }
4176       return eKeyHandled;
4177 
4178     case KEY_LEFT:
4179       if (m_selected_row) {
4180         if (m_selected_row->expanded)
4181           m_selected_row->Unexpand();
4182         else if (m_selected_row->parent)
4183           m_selected_row_idx = m_selected_row->parent->row_idx;
4184       }
4185       return eKeyHandled;
4186 
4187     case ' ':
4188       // Toggle expansion state when SPACE is pressed
4189       if (m_selected_row) {
4190         if (m_selected_row->expanded)
4191           m_selected_row->Unexpand();
4192         else
4193           m_selected_row->Expand();
4194       }
4195       return eKeyHandled;
4196 
4197     case 'h':
4198       window.CreateHelpSubwindow();
4199       return eKeyHandled;
4200 
4201     default:
4202       break;
4203     }
4204     return eKeyNotHandled;
4205   }
4206 
4207 protected:
4208   std::vector<Row> m_rows;
4209   Row *m_selected_row = nullptr;
4210   uint32_t m_selected_row_idx = 0;
4211   uint32_t m_first_visible_row = 0;
4212   uint32_t m_num_rows = 0;
4213   int m_min_x;
4214   int m_min_y;
4215   int m_max_x = 0;
4216   int m_max_y = 0;
4217 
4218   static Format FormatForChar(int c) {
4219     switch (c) {
4220     case 'x':
4221       return eFormatHex;
4222     case 'X':
4223       return eFormatHexUppercase;
4224     case 'o':
4225       return eFormatOctal;
4226     case 's':
4227       return eFormatCString;
4228     case 'u':
4229       return eFormatUnsigned;
4230     case 'd':
4231       return eFormatDecimal;
4232     case 'D':
4233       return eFormatDefault;
4234     case 'i':
4235       return eFormatInstruction;
4236     case 'A':
4237       return eFormatAddressInfo;
4238     case 'p':
4239       return eFormatPointer;
4240     case 'c':
4241       return eFormatChar;
4242     case 'b':
4243       return eFormatBinary;
4244     case 'B':
4245       return eFormatBytesWithASCII;
4246     case 'f':
4247       return eFormatFloat;
4248     }
4249     return eFormatDefault;
4250   }
4251 
4252   bool DisplayRowObject(Window &window, Row &row, DisplayOptions &options,
4253                         bool highlight, bool last_child) {
4254     ValueObject *valobj = row.value.GetSP().get();
4255 
4256     if (valobj == nullptr)
4257       return false;
4258 
4259     const char *type_name =
4260         options.show_types ? valobj->GetTypeName().GetCString() : nullptr;
4261     const char *name = valobj->GetName().GetCString();
4262     const char *value = valobj->GetValueAsCString();
4263     const char *summary = valobj->GetSummaryAsCString();
4264 
4265     window.MoveCursor(row.x, row.y);
4266 
4267     row.DrawTree(window);
4268 
4269     if (highlight)
4270       window.AttributeOn(A_REVERSE);
4271 
4272     if (type_name && type_name[0])
4273       window.PrintfTruncated(1, "(%s) ", type_name);
4274 
4275     if (name && name[0])
4276       window.PutCStringTruncated(1, name);
4277 
4278     attr_t changd_attr = 0;
4279     if (valobj->GetValueDidChange())
4280       changd_attr = COLOR_PAIR(RedOnBlack) | A_BOLD;
4281 
4282     if (value && value[0]) {
4283       window.PutCStringTruncated(1, " = ");
4284       if (changd_attr)
4285         window.AttributeOn(changd_attr);
4286       window.PutCStringTruncated(1, value);
4287       if (changd_attr)
4288         window.AttributeOff(changd_attr);
4289     }
4290 
4291     if (summary && summary[0]) {
4292       window.PutCStringTruncated(1, " ");
4293       if (changd_attr)
4294         window.AttributeOn(changd_attr);
4295       window.PutCStringTruncated(1, summary);
4296       if (changd_attr)
4297         window.AttributeOff(changd_attr);
4298     }
4299 
4300     if (highlight)
4301       window.AttributeOff(A_REVERSE);
4302 
4303     return true;
4304   }
4305 
4306   void DisplayRows(Window &window, std::vector<Row> &rows,
4307                    DisplayOptions &options) {
4308     // >   0x25B7
4309     // \/  0x25BD
4310 
4311     bool window_is_active = window.IsActive();
4312     for (auto &row : rows) {
4313       const bool last_child = row.parent && &rows[rows.size() - 1] == &row;
4314       // Save the row index in each Row structure
4315       row.row_idx = m_num_rows;
4316       if ((m_num_rows >= m_first_visible_row) &&
4317           ((m_num_rows - m_first_visible_row) <
4318            static_cast<size_t>(NumVisibleRows()))) {
4319         row.x = m_min_x;
4320         row.y = m_num_rows - m_first_visible_row + 1;
4321         if (DisplayRowObject(window, row, options,
4322                              window_is_active &&
4323                                  m_num_rows == m_selected_row_idx,
4324                              last_child)) {
4325           ++m_num_rows;
4326         } else {
4327           row.x = 0;
4328           row.y = 0;
4329         }
4330       } else {
4331         row.x = 0;
4332         row.y = 0;
4333         ++m_num_rows;
4334       }
4335 
4336       auto &children = row.GetChildren();
4337       if (row.expanded && !children.empty()) {
4338         DisplayRows(window, children, options);
4339       }
4340     }
4341   }
4342 
4343   int CalculateTotalNumberRows(std::vector<Row> &rows) {
4344     int row_count = 0;
4345     for (auto &row : rows) {
4346       ++row_count;
4347       if (row.expanded)
4348         row_count += CalculateTotalNumberRows(row.GetChildren());
4349     }
4350     return row_count;
4351   }
4352 
4353   static Row *GetRowForRowIndexImpl(std::vector<Row> &rows, size_t &row_index) {
4354     for (auto &row : rows) {
4355       if (row_index == 0)
4356         return &row;
4357       else {
4358         --row_index;
4359         auto &children = row.GetChildren();
4360         if (row.expanded && !children.empty()) {
4361           Row *result = GetRowForRowIndexImpl(children, row_index);
4362           if (result)
4363             return result;
4364         }
4365       }
4366     }
4367     return nullptr;
4368   }
4369 
4370   Row *GetRowForRowIndex(size_t row_index) {
4371     return GetRowForRowIndexImpl(m_rows, row_index);
4372   }
4373 
4374   int NumVisibleRows() const { return m_max_y - m_min_y; }
4375 
4376   static DisplayOptions g_options;
4377 };
4378 
4379 class FrameVariablesWindowDelegate : public ValueObjectListDelegate {
4380 public:
4381   FrameVariablesWindowDelegate(Debugger &debugger)
4382       : ValueObjectListDelegate(), m_debugger(debugger),
4383         m_frame_block(nullptr) {}
4384 
4385   ~FrameVariablesWindowDelegate() override = default;
4386 
4387   const char *WindowDelegateGetHelpText() override {
4388     return "Frame variable window keyboard shortcuts:";
4389   }
4390 
4391   bool WindowDelegateDraw(Window &window, bool force) override {
4392     ExecutionContext exe_ctx(
4393         m_debugger.GetCommandInterpreter().GetExecutionContext());
4394     Process *process = exe_ctx.GetProcessPtr();
4395     Block *frame_block = nullptr;
4396     StackFrame *frame = nullptr;
4397 
4398     if (process) {
4399       StateType state = process->GetState();
4400       if (StateIsStoppedState(state, true)) {
4401         frame = exe_ctx.GetFramePtr();
4402         if (frame)
4403           frame_block = frame->GetFrameBlock();
4404       } else if (StateIsRunningState(state)) {
4405         return true; // Don't do any updating when we are running
4406       }
4407     }
4408 
4409     ValueObjectList local_values;
4410     if (frame_block) {
4411       // Only update the variables if they have changed
4412       if (m_frame_block != frame_block) {
4413         m_frame_block = frame_block;
4414 
4415         VariableList *locals = frame->GetVariableList(true);
4416         if (locals) {
4417           const DynamicValueType use_dynamic = eDynamicDontRunTarget;
4418           for (const VariableSP &local_sp : *locals) {
4419             ValueObjectSP value_sp =
4420                 frame->GetValueObjectForFrameVariable(local_sp, use_dynamic);
4421             if (value_sp) {
4422               ValueObjectSP synthetic_value_sp = value_sp->GetSyntheticValue();
4423               if (synthetic_value_sp)
4424                 local_values.Append(synthetic_value_sp);
4425               else
4426                 local_values.Append(value_sp);
4427             }
4428           }
4429           // Update the values
4430           SetValues(local_values);
4431         }
4432       }
4433     } else {
4434       m_frame_block = nullptr;
4435       // Update the values with an empty list if there is no frame
4436       SetValues(local_values);
4437     }
4438 
4439     return ValueObjectListDelegate::WindowDelegateDraw(window, force);
4440   }
4441 
4442 protected:
4443   Debugger &m_debugger;
4444   Block *m_frame_block;
4445 };
4446 
4447 class RegistersWindowDelegate : public ValueObjectListDelegate {
4448 public:
4449   RegistersWindowDelegate(Debugger &debugger)
4450       : ValueObjectListDelegate(), m_debugger(debugger) {}
4451 
4452   ~RegistersWindowDelegate() override = default;
4453 
4454   const char *WindowDelegateGetHelpText() override {
4455     return "Register window keyboard shortcuts:";
4456   }
4457 
4458   bool WindowDelegateDraw(Window &window, bool force) override {
4459     ExecutionContext exe_ctx(
4460         m_debugger.GetCommandInterpreter().GetExecutionContext());
4461     StackFrame *frame = exe_ctx.GetFramePtr();
4462 
4463     ValueObjectList value_list;
4464     if (frame) {
4465       if (frame->GetStackID() != m_stack_id) {
4466         m_stack_id = frame->GetStackID();
4467         RegisterContextSP reg_ctx(frame->GetRegisterContext());
4468         if (reg_ctx) {
4469           const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
4470           for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) {
4471             value_list.Append(
4472                 ValueObjectRegisterSet::Create(frame, reg_ctx, set_idx));
4473           }
4474         }
4475         SetValues(value_list);
4476       }
4477     } else {
4478       Process *process = exe_ctx.GetProcessPtr();
4479       if (process && process->IsAlive())
4480         return true; // Don't do any updating if we are running
4481       else {
4482         // Update the values with an empty list if there is no process or the
4483         // process isn't alive anymore
4484         SetValues(value_list);
4485       }
4486     }
4487     return ValueObjectListDelegate::WindowDelegateDraw(window, force);
4488   }
4489 
4490 protected:
4491   Debugger &m_debugger;
4492   StackID m_stack_id;
4493 };
4494 
4495 static const char *CursesKeyToCString(int ch) {
4496   static char g_desc[32];
4497   if (ch >= KEY_F0 && ch < KEY_F0 + 64) {
4498     snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0);
4499     return g_desc;
4500   }
4501   switch (ch) {
4502   case KEY_DOWN:
4503     return "down";
4504   case KEY_UP:
4505     return "up";
4506   case KEY_LEFT:
4507     return "left";
4508   case KEY_RIGHT:
4509     return "right";
4510   case KEY_HOME:
4511     return "home";
4512   case KEY_BACKSPACE:
4513     return "backspace";
4514   case KEY_DL:
4515     return "delete-line";
4516   case KEY_IL:
4517     return "insert-line";
4518   case KEY_DC:
4519     return "delete-char";
4520   case KEY_IC:
4521     return "insert-char";
4522   case KEY_CLEAR:
4523     return "clear";
4524   case KEY_EOS:
4525     return "clear-to-eos";
4526   case KEY_EOL:
4527     return "clear-to-eol";
4528   case KEY_SF:
4529     return "scroll-forward";
4530   case KEY_SR:
4531     return "scroll-backward";
4532   case KEY_NPAGE:
4533     return "page-down";
4534   case KEY_PPAGE:
4535     return "page-up";
4536   case KEY_STAB:
4537     return "set-tab";
4538   case KEY_CTAB:
4539     return "clear-tab";
4540   case KEY_CATAB:
4541     return "clear-all-tabs";
4542   case KEY_ENTER:
4543     return "enter";
4544   case KEY_PRINT:
4545     return "print";
4546   case KEY_LL:
4547     return "lower-left key";
4548   case KEY_A1:
4549     return "upper left of keypad";
4550   case KEY_A3:
4551     return "upper right of keypad";
4552   case KEY_B2:
4553     return "center of keypad";
4554   case KEY_C1:
4555     return "lower left of keypad";
4556   case KEY_C3:
4557     return "lower right of keypad";
4558   case KEY_BTAB:
4559     return "back-tab key";
4560   case KEY_BEG:
4561     return "begin key";
4562   case KEY_CANCEL:
4563     return "cancel key";
4564   case KEY_CLOSE:
4565     return "close key";
4566   case KEY_COMMAND:
4567     return "command key";
4568   case KEY_COPY:
4569     return "copy key";
4570   case KEY_CREATE:
4571     return "create key";
4572   case KEY_END:
4573     return "end key";
4574   case KEY_EXIT:
4575     return "exit key";
4576   case KEY_FIND:
4577     return "find key";
4578   case KEY_HELP:
4579     return "help key";
4580   case KEY_MARK:
4581     return "mark key";
4582   case KEY_MESSAGE:
4583     return "message key";
4584   case KEY_MOVE:
4585     return "move key";
4586   case KEY_NEXT:
4587     return "next key";
4588   case KEY_OPEN:
4589     return "open key";
4590   case KEY_OPTIONS:
4591     return "options key";
4592   case KEY_PREVIOUS:
4593     return "previous key";
4594   case KEY_REDO:
4595     return "redo key";
4596   case KEY_REFERENCE:
4597     return "reference key";
4598   case KEY_REFRESH:
4599     return "refresh key";
4600   case KEY_REPLACE:
4601     return "replace key";
4602   case KEY_RESTART:
4603     return "restart key";
4604   case KEY_RESUME:
4605     return "resume key";
4606   case KEY_SAVE:
4607     return "save key";
4608   case KEY_SBEG:
4609     return "shifted begin key";
4610   case KEY_SCANCEL:
4611     return "shifted cancel key";
4612   case KEY_SCOMMAND:
4613     return "shifted command key";
4614   case KEY_SCOPY:
4615     return "shifted copy key";
4616   case KEY_SCREATE:
4617     return "shifted create key";
4618   case KEY_SDC:
4619     return "shifted delete-character key";
4620   case KEY_SDL:
4621     return "shifted delete-line key";
4622   case KEY_SELECT:
4623     return "select key";
4624   case KEY_SEND:
4625     return "shifted end key";
4626   case KEY_SEOL:
4627     return "shifted clear-to-end-of-line key";
4628   case KEY_SEXIT:
4629     return "shifted exit key";
4630   case KEY_SFIND:
4631     return "shifted find key";
4632   case KEY_SHELP:
4633     return "shifted help key";
4634   case KEY_SHOME:
4635     return "shifted home key";
4636   case KEY_SIC:
4637     return "shifted insert-character key";
4638   case KEY_SLEFT:
4639     return "shifted left-arrow key";
4640   case KEY_SMESSAGE:
4641     return "shifted message key";
4642   case KEY_SMOVE:
4643     return "shifted move key";
4644   case KEY_SNEXT:
4645     return "shifted next key";
4646   case KEY_SOPTIONS:
4647     return "shifted options key";
4648   case KEY_SPREVIOUS:
4649     return "shifted previous key";
4650   case KEY_SPRINT:
4651     return "shifted print key";
4652   case KEY_SREDO:
4653     return "shifted redo key";
4654   case KEY_SREPLACE:
4655     return "shifted replace key";
4656   case KEY_SRIGHT:
4657     return "shifted right-arrow key";
4658   case KEY_SRSUME:
4659     return "shifted resume key";
4660   case KEY_SSAVE:
4661     return "shifted save key";
4662   case KEY_SSUSPEND:
4663     return "shifted suspend key";
4664   case KEY_SUNDO:
4665     return "shifted undo key";
4666   case KEY_SUSPEND:
4667     return "suspend key";
4668   case KEY_UNDO:
4669     return "undo key";
4670   case KEY_MOUSE:
4671     return "Mouse event has occurred";
4672   case KEY_RESIZE:
4673     return "Terminal resize event";
4674 #ifdef KEY_EVENT
4675   case KEY_EVENT:
4676     return "We were interrupted by an event";
4677 #endif
4678   case KEY_RETURN:
4679     return "return";
4680   case ' ':
4681     return "space";
4682   case '\t':
4683     return "tab";
4684   case KEY_ESCAPE:
4685     return "escape";
4686   default:
4687     if (llvm::isPrint(ch))
4688       snprintf(g_desc, sizeof(g_desc), "%c", ch);
4689     else
4690       snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch);
4691     return g_desc;
4692   }
4693   return nullptr;
4694 }
4695 
4696 HelpDialogDelegate::HelpDialogDelegate(const char *text,
4697                                        KeyHelp *key_help_array)
4698     : m_text(), m_first_visible_line(0) {
4699   if (text && text[0]) {
4700     m_text.SplitIntoLines(text);
4701     m_text.AppendString("");
4702   }
4703   if (key_help_array) {
4704     for (KeyHelp *key = key_help_array; key->ch; ++key) {
4705       StreamString key_description;
4706       key_description.Printf("%10s - %s", CursesKeyToCString(key->ch),
4707                              key->description);
4708       m_text.AppendString(key_description.GetString());
4709     }
4710   }
4711 }
4712 
4713 HelpDialogDelegate::~HelpDialogDelegate() = default;
4714 
4715 bool HelpDialogDelegate::WindowDelegateDraw(Window &window, bool force) {
4716   window.Erase();
4717   const int window_height = window.GetHeight();
4718   int x = 2;
4719   int y = 1;
4720   const int min_y = y;
4721   const int max_y = window_height - 1 - y;
4722   const size_t num_visible_lines = max_y - min_y + 1;
4723   const size_t num_lines = m_text.GetSize();
4724   const char *bottom_message;
4725   if (num_lines <= num_visible_lines)
4726     bottom_message = "Press any key to exit";
4727   else
4728     bottom_message = "Use arrows to scroll, any other key to exit";
4729   window.DrawTitleBox(window.GetName(), bottom_message);
4730   while (y <= max_y) {
4731     window.MoveCursor(x, y);
4732     window.PutCStringTruncated(
4733         1, m_text.GetStringAtIndex(m_first_visible_line + y - min_y));
4734     ++y;
4735   }
4736   return true;
4737 }
4738 
4739 HandleCharResult HelpDialogDelegate::WindowDelegateHandleChar(Window &window,
4740                                                               int key) {
4741   bool done = false;
4742   const size_t num_lines = m_text.GetSize();
4743   const size_t num_visible_lines = window.GetHeight() - 2;
4744 
4745   if (num_lines <= num_visible_lines) {
4746     done = true;
4747     // If we have all lines visible and don't need scrolling, then any key
4748     // press will cause us to exit
4749   } else {
4750     switch (key) {
4751     case KEY_UP:
4752       if (m_first_visible_line > 0)
4753         --m_first_visible_line;
4754       break;
4755 
4756     case KEY_DOWN:
4757       if (m_first_visible_line + num_visible_lines < num_lines)
4758         ++m_first_visible_line;
4759       break;
4760 
4761     case KEY_PPAGE:
4762     case ',':
4763       if (m_first_visible_line > 0) {
4764         if (static_cast<size_t>(m_first_visible_line) >= num_visible_lines)
4765           m_first_visible_line -= num_visible_lines;
4766         else
4767           m_first_visible_line = 0;
4768       }
4769       break;
4770 
4771     case KEY_NPAGE:
4772     case '.':
4773       if (m_first_visible_line + num_visible_lines < num_lines) {
4774         m_first_visible_line += num_visible_lines;
4775         if (static_cast<size_t>(m_first_visible_line) > num_lines)
4776           m_first_visible_line = num_lines - num_visible_lines;
4777       }
4778       break;
4779 
4780     default:
4781       done = true;
4782       break;
4783     }
4784   }
4785   if (done)
4786     window.GetParent()->RemoveSubWindow(&window);
4787   return eKeyHandled;
4788 }
4789 
4790 class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
4791 public:
4792   enum {
4793     eMenuID_LLDB = 1,
4794     eMenuID_LLDBAbout,
4795     eMenuID_LLDBExit,
4796 
4797     eMenuID_Target,
4798     eMenuID_TargetCreate,
4799     eMenuID_TargetDelete,
4800 
4801     eMenuID_Process,
4802     eMenuID_ProcessAttach,
4803     eMenuID_ProcessDetachResume,
4804     eMenuID_ProcessDetachSuspended,
4805     eMenuID_ProcessLaunch,
4806     eMenuID_ProcessContinue,
4807     eMenuID_ProcessHalt,
4808     eMenuID_ProcessKill,
4809 
4810     eMenuID_Thread,
4811     eMenuID_ThreadStepIn,
4812     eMenuID_ThreadStepOver,
4813     eMenuID_ThreadStepOut,
4814 
4815     eMenuID_View,
4816     eMenuID_ViewBacktrace,
4817     eMenuID_ViewRegisters,
4818     eMenuID_ViewSource,
4819     eMenuID_ViewVariables,
4820 
4821     eMenuID_Help,
4822     eMenuID_HelpGUIHelp
4823   };
4824 
4825   ApplicationDelegate(Application &app, Debugger &debugger)
4826       : WindowDelegate(), MenuDelegate(), m_app(app), m_debugger(debugger) {}
4827 
4828   ~ApplicationDelegate() override = default;
4829 
4830   bool WindowDelegateDraw(Window &window, bool force) override {
4831     return false; // Drawing not handled, let standard window drawing happen
4832   }
4833 
4834   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
4835     switch (key) {
4836     case '\t':
4837       window.SelectNextWindowAsActive();
4838       return eKeyHandled;
4839 
4840     case KEY_SHIFT_TAB:
4841       window.SelectPreviousWindowAsActive();
4842       return eKeyHandled;
4843 
4844     case 'h':
4845       window.CreateHelpSubwindow();
4846       return eKeyHandled;
4847 
4848     case KEY_ESCAPE:
4849       return eQuitApplication;
4850 
4851     default:
4852       break;
4853     }
4854     return eKeyNotHandled;
4855   }
4856 
4857   const char *WindowDelegateGetHelpText() override {
4858     return "Welcome to the LLDB curses GUI.\n\n"
4859            "Press the TAB key to change the selected view.\n"
4860            "Each view has its own keyboard shortcuts, press 'h' to open a "
4861            "dialog to display them.\n\n"
4862            "Common key bindings for all views:";
4863   }
4864 
4865   KeyHelp *WindowDelegateGetKeyHelp() override {
4866     static curses::KeyHelp g_source_view_key_help[] = {
4867         {'\t', "Select next view"},
4868         {KEY_BTAB, "Select previous view"},
4869         {'h', "Show help dialog with view specific key bindings"},
4870         {',', "Page up"},
4871         {'.', "Page down"},
4872         {KEY_UP, "Select previous"},
4873         {KEY_DOWN, "Select next"},
4874         {KEY_LEFT, "Unexpand or select parent"},
4875         {KEY_RIGHT, "Expand"},
4876         {KEY_PPAGE, "Page up"},
4877         {KEY_NPAGE, "Page down"},
4878         {'\0', nullptr}};
4879     return g_source_view_key_help;
4880   }
4881 
4882   MenuActionResult MenuDelegateAction(Menu &menu) override {
4883     switch (menu.GetIdentifier()) {
4884     case eMenuID_ThreadStepIn: {
4885       ExecutionContext exe_ctx =
4886           m_debugger.GetCommandInterpreter().GetExecutionContext();
4887       if (exe_ctx.HasThreadScope()) {
4888         Process *process = exe_ctx.GetProcessPtr();
4889         if (process && process->IsAlive() &&
4890             StateIsStoppedState(process->GetState(), true))
4891           exe_ctx.GetThreadRef().StepIn(true);
4892       }
4893     }
4894       return MenuActionResult::Handled;
4895 
4896     case eMenuID_ThreadStepOut: {
4897       ExecutionContext exe_ctx =
4898           m_debugger.GetCommandInterpreter().GetExecutionContext();
4899       if (exe_ctx.HasThreadScope()) {
4900         Process *process = exe_ctx.GetProcessPtr();
4901         if (process && process->IsAlive() &&
4902             StateIsStoppedState(process->GetState(), true))
4903           exe_ctx.GetThreadRef().StepOut();
4904       }
4905     }
4906       return MenuActionResult::Handled;
4907 
4908     case eMenuID_ThreadStepOver: {
4909       ExecutionContext exe_ctx =
4910           m_debugger.GetCommandInterpreter().GetExecutionContext();
4911       if (exe_ctx.HasThreadScope()) {
4912         Process *process = exe_ctx.GetProcessPtr();
4913         if (process && process->IsAlive() &&
4914             StateIsStoppedState(process->GetState(), true))
4915           exe_ctx.GetThreadRef().StepOver(true);
4916       }
4917     }
4918       return MenuActionResult::Handled;
4919 
4920     case eMenuID_ProcessAttach: {
4921       WindowSP main_window_sp = m_app.GetMainWindow();
4922       FormDelegateSP form_delegate_sp = FormDelegateSP(
4923           new ProcessAttachFormDelegate(m_debugger, main_window_sp));
4924       Rect bounds = main_window_sp->GetCenteredRect(80, 22);
4925       WindowSP form_window_sp = main_window_sp->CreateSubWindow(
4926           form_delegate_sp->GetName().c_str(), bounds, true);
4927       WindowDelegateSP window_delegate_sp =
4928           WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
4929       form_window_sp->SetDelegate(window_delegate_sp);
4930       return MenuActionResult::Handled;
4931     }
4932 
4933     case eMenuID_ProcessContinue: {
4934       ExecutionContext exe_ctx =
4935           m_debugger.GetCommandInterpreter().GetExecutionContext();
4936       if (exe_ctx.HasProcessScope()) {
4937         Process *process = exe_ctx.GetProcessPtr();
4938         if (process && process->IsAlive() &&
4939             StateIsStoppedState(process->GetState(), true))
4940           process->Resume();
4941       }
4942     }
4943       return MenuActionResult::Handled;
4944 
4945     case eMenuID_ProcessKill: {
4946       ExecutionContext exe_ctx =
4947           m_debugger.GetCommandInterpreter().GetExecutionContext();
4948       if (exe_ctx.HasProcessScope()) {
4949         Process *process = exe_ctx.GetProcessPtr();
4950         if (process && process->IsAlive())
4951           process->Destroy(false);
4952       }
4953     }
4954       return MenuActionResult::Handled;
4955 
4956     case eMenuID_ProcessHalt: {
4957       ExecutionContext exe_ctx =
4958           m_debugger.GetCommandInterpreter().GetExecutionContext();
4959       if (exe_ctx.HasProcessScope()) {
4960         Process *process = exe_ctx.GetProcessPtr();
4961         if (process && process->IsAlive())
4962           process->Halt();
4963       }
4964     }
4965       return MenuActionResult::Handled;
4966 
4967     case eMenuID_ProcessDetachResume:
4968     case eMenuID_ProcessDetachSuspended: {
4969       ExecutionContext exe_ctx =
4970           m_debugger.GetCommandInterpreter().GetExecutionContext();
4971       if (exe_ctx.HasProcessScope()) {
4972         Process *process = exe_ctx.GetProcessPtr();
4973         if (process && process->IsAlive())
4974           process->Detach(menu.GetIdentifier() ==
4975                           eMenuID_ProcessDetachSuspended);
4976       }
4977     }
4978       return MenuActionResult::Handled;
4979 
4980     case eMenuID_Process: {
4981       // Populate the menu with all of the threads if the process is stopped
4982       // when the Process menu gets selected and is about to display its
4983       // submenu.
4984       Menus &submenus = menu.GetSubmenus();
4985       ExecutionContext exe_ctx =
4986           m_debugger.GetCommandInterpreter().GetExecutionContext();
4987       Process *process = exe_ctx.GetProcessPtr();
4988       if (process && process->IsAlive() &&
4989           StateIsStoppedState(process->GetState(), true)) {
4990         if (submenus.size() == 7)
4991           menu.AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
4992         else if (submenus.size() > 8)
4993           submenus.erase(submenus.begin() + 8, submenus.end());
4994 
4995         ThreadList &threads = process->GetThreadList();
4996         std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
4997         size_t num_threads = threads.GetSize();
4998         for (size_t i = 0; i < num_threads; ++i) {
4999           ThreadSP thread_sp = threads.GetThreadAtIndex(i);
5000           char menu_char = '\0';
5001           if (i < 9)
5002             menu_char = '1' + i;
5003           StreamString thread_menu_title;
5004           thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID());
5005           const char *thread_name = thread_sp->GetName();
5006           if (thread_name && thread_name[0])
5007             thread_menu_title.Printf(" %s", thread_name);
5008           else {
5009             const char *queue_name = thread_sp->GetQueueName();
5010             if (queue_name && queue_name[0])
5011               thread_menu_title.Printf(" %s", queue_name);
5012           }
5013           menu.AddSubmenu(
5014               MenuSP(new Menu(thread_menu_title.GetString().str().c_str(),
5015                               nullptr, menu_char, thread_sp->GetID())));
5016         }
5017       } else if (submenus.size() > 7) {
5018         // Remove the separator and any other thread submenu items that were
5019         // previously added
5020         submenus.erase(submenus.begin() + 7, submenus.end());
5021       }
5022       // Since we are adding and removing items we need to recalculate the name
5023       // lengths
5024       menu.RecalculateNameLengths();
5025     }
5026       return MenuActionResult::Handled;
5027 
5028     case eMenuID_ViewVariables: {
5029       WindowSP main_window_sp = m_app.GetMainWindow();
5030       WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
5031       WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
5032       WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
5033       const Rect source_bounds = source_window_sp->GetBounds();
5034 
5035       if (variables_window_sp) {
5036         const Rect variables_bounds = variables_window_sp->GetBounds();
5037 
5038         main_window_sp->RemoveSubWindow(variables_window_sp.get());
5039 
5040         if (registers_window_sp) {
5041           // We have a registers window, so give all the area back to the
5042           // registers window
5043           Rect registers_bounds = variables_bounds;
5044           registers_bounds.size.width = source_bounds.size.width;
5045           registers_window_sp->SetBounds(registers_bounds);
5046         } else {
5047           // We have no registers window showing so give the bottom area back
5048           // to the source view
5049           source_window_sp->Resize(source_bounds.size.width,
5050                                    source_bounds.size.height +
5051                                        variables_bounds.size.height);
5052         }
5053       } else {
5054         Rect new_variables_rect;
5055         if (registers_window_sp) {
5056           // We have a registers window so split the area of the registers
5057           // window into two columns where the left hand side will be the
5058           // variables and the right hand side will be the registers
5059           const Rect variables_bounds = registers_window_sp->GetBounds();
5060           Rect new_registers_rect;
5061           variables_bounds.VerticalSplitPercentage(0.50, new_variables_rect,
5062                                                    new_registers_rect);
5063           registers_window_sp->SetBounds(new_registers_rect);
5064         } else {
5065           // No registers window, grab the bottom part of the source window
5066           Rect new_source_rect;
5067           source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
5068                                                   new_variables_rect);
5069           source_window_sp->SetBounds(new_source_rect);
5070         }
5071         WindowSP new_window_sp = main_window_sp->CreateSubWindow(
5072             "Variables", new_variables_rect, false);
5073         new_window_sp->SetDelegate(
5074             WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
5075       }
5076       touchwin(stdscr);
5077     }
5078       return MenuActionResult::Handled;
5079 
5080     case eMenuID_ViewRegisters: {
5081       WindowSP main_window_sp = m_app.GetMainWindow();
5082       WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
5083       WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
5084       WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
5085       const Rect source_bounds = source_window_sp->GetBounds();
5086 
5087       if (registers_window_sp) {
5088         if (variables_window_sp) {
5089           const Rect variables_bounds = variables_window_sp->GetBounds();
5090 
5091           // We have a variables window, so give all the area back to the
5092           // variables window
5093           variables_window_sp->Resize(variables_bounds.size.width +
5094                                           registers_window_sp->GetWidth(),
5095                                       variables_bounds.size.height);
5096         } else {
5097           // We have no variables window showing so give the bottom area back
5098           // to the source view
5099           source_window_sp->Resize(source_bounds.size.width,
5100                                    source_bounds.size.height +
5101                                        registers_window_sp->GetHeight());
5102         }
5103         main_window_sp->RemoveSubWindow(registers_window_sp.get());
5104       } else {
5105         Rect new_regs_rect;
5106         if (variables_window_sp) {
5107           // We have a variables window, split it into two columns where the
5108           // left hand side will be the variables and the right hand side will
5109           // be the registers
5110           const Rect variables_bounds = variables_window_sp->GetBounds();
5111           Rect new_vars_rect;
5112           variables_bounds.VerticalSplitPercentage(0.50, new_vars_rect,
5113                                                    new_regs_rect);
5114           variables_window_sp->SetBounds(new_vars_rect);
5115         } else {
5116           // No variables window, grab the bottom part of the source window
5117           Rect new_source_rect;
5118           source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
5119                                                   new_regs_rect);
5120           source_window_sp->SetBounds(new_source_rect);
5121         }
5122         WindowSP new_window_sp =
5123             main_window_sp->CreateSubWindow("Registers", new_regs_rect, false);
5124         new_window_sp->SetDelegate(
5125             WindowDelegateSP(new RegistersWindowDelegate(m_debugger)));
5126       }
5127       touchwin(stdscr);
5128     }
5129       return MenuActionResult::Handled;
5130 
5131     case eMenuID_HelpGUIHelp:
5132       m_app.GetMainWindow()->CreateHelpSubwindow();
5133       return MenuActionResult::Handled;
5134 
5135     default:
5136       break;
5137     }
5138 
5139     return MenuActionResult::NotHandled;
5140   }
5141 
5142 protected:
5143   Application &m_app;
5144   Debugger &m_debugger;
5145 };
5146 
5147 class StatusBarWindowDelegate : public WindowDelegate {
5148 public:
5149   StatusBarWindowDelegate(Debugger &debugger) : m_debugger(debugger) {
5150     FormatEntity::Parse("Thread: ${thread.id%tid}", m_format);
5151   }
5152 
5153   ~StatusBarWindowDelegate() override = default;
5154 
5155   bool WindowDelegateDraw(Window &window, bool force) override {
5156     ExecutionContext exe_ctx =
5157         m_debugger.GetCommandInterpreter().GetExecutionContext();
5158     Process *process = exe_ctx.GetProcessPtr();
5159     Thread *thread = exe_ctx.GetThreadPtr();
5160     StackFrame *frame = exe_ctx.GetFramePtr();
5161     window.Erase();
5162     window.SetBackground(BlackOnWhite);
5163     window.MoveCursor(0, 0);
5164     if (process) {
5165       const StateType state = process->GetState();
5166       window.Printf("Process: %5" PRIu64 " %10s", process->GetID(),
5167                     StateAsCString(state));
5168 
5169       if (StateIsStoppedState(state, true)) {
5170         StreamString strm;
5171         if (thread && FormatEntity::Format(m_format, strm, nullptr, &exe_ctx,
5172                                            nullptr, nullptr, false, false)) {
5173           window.MoveCursor(40, 0);
5174           window.PutCStringTruncated(1, strm.GetString().str().c_str());
5175         }
5176 
5177         window.MoveCursor(60, 0);
5178         if (frame)
5179           window.Printf("Frame: %3u  PC = 0x%16.16" PRIx64,
5180                         frame->GetFrameIndex(),
5181                         frame->GetFrameCodeAddress().GetOpcodeLoadAddress(
5182                             exe_ctx.GetTargetPtr()));
5183       } else if (state == eStateExited) {
5184         const char *exit_desc = process->GetExitDescription();
5185         const int exit_status = process->GetExitStatus();
5186         if (exit_desc && exit_desc[0])
5187           window.Printf(" with status = %i (%s)", exit_status, exit_desc);
5188         else
5189           window.Printf(" with status = %i", exit_status);
5190       }
5191     }
5192     return true;
5193   }
5194 
5195 protected:
5196   Debugger &m_debugger;
5197   FormatEntity::Entry m_format;
5198 };
5199 
5200 class SourceFileWindowDelegate : public WindowDelegate {
5201 public:
5202   SourceFileWindowDelegate(Debugger &debugger)
5203       : WindowDelegate(), m_debugger(debugger), m_sc(), m_file_sp(),
5204         m_disassembly_scope(nullptr), m_disassembly_sp(), m_disassembly_range(),
5205         m_title(), m_tid(LLDB_INVALID_THREAD_ID), m_line_width(4),
5206         m_selected_line(0), m_pc_line(0), m_stop_id(0), m_frame_idx(UINT32_MAX),
5207         m_first_visible_line(0), m_first_visible_column(0), m_min_x(0),
5208         m_min_y(0), m_max_x(0), m_max_y(0) {}
5209 
5210   ~SourceFileWindowDelegate() override = default;
5211 
5212   void Update(const SymbolContext &sc) { m_sc = sc; }
5213 
5214   uint32_t NumVisibleLines() const { return m_max_y - m_min_y; }
5215 
5216   const char *WindowDelegateGetHelpText() override {
5217     return "Source/Disassembly window keyboard shortcuts:";
5218   }
5219 
5220   KeyHelp *WindowDelegateGetKeyHelp() override {
5221     static curses::KeyHelp g_source_view_key_help[] = {
5222         {KEY_RETURN, "Run to selected line with one shot breakpoint"},
5223         {KEY_UP, "Select previous source line"},
5224         {KEY_DOWN, "Select next source line"},
5225         {KEY_LEFT, "Scroll to the left"},
5226         {KEY_RIGHT, "Scroll to the right"},
5227         {KEY_PPAGE, "Page up"},
5228         {KEY_NPAGE, "Page down"},
5229         {'b', "Set breakpoint on selected source/disassembly line"},
5230         {'c', "Continue process"},
5231         {'D', "Detach with process suspended"},
5232         {'h', "Show help dialog"},
5233         {'n', "Step over (source line)"},
5234         {'N', "Step over (single instruction)"},
5235         {'f', "Step out (finish)"},
5236         {'s', "Step in (source line)"},
5237         {'S', "Step in (single instruction)"},
5238         {'u', "Frame up"},
5239         {'d', "Frame down"},
5240         {',', "Page up"},
5241         {'.', "Page down"},
5242         {'\0', nullptr}};
5243     return g_source_view_key_help;
5244   }
5245 
5246   bool WindowDelegateDraw(Window &window, bool force) override {
5247     ExecutionContext exe_ctx =
5248         m_debugger.GetCommandInterpreter().GetExecutionContext();
5249     Process *process = exe_ctx.GetProcessPtr();
5250     Thread *thread = nullptr;
5251 
5252     bool update_location = false;
5253     if (process) {
5254       StateType state = process->GetState();
5255       if (StateIsStoppedState(state, true)) {
5256         // We are stopped, so it is ok to
5257         update_location = true;
5258       }
5259     }
5260 
5261     m_min_x = 1;
5262     m_min_y = 2;
5263     m_max_x = window.GetMaxX() - 1;
5264     m_max_y = window.GetMaxY() - 1;
5265 
5266     const uint32_t num_visible_lines = NumVisibleLines();
5267     StackFrameSP frame_sp;
5268     bool set_selected_line_to_pc = false;
5269 
5270     if (update_location) {
5271       const bool process_alive = process ? process->IsAlive() : false;
5272       bool thread_changed = false;
5273       if (process_alive) {
5274         thread = exe_ctx.GetThreadPtr();
5275         if (thread) {
5276           frame_sp = thread->GetSelectedFrame();
5277           auto tid = thread->GetID();
5278           thread_changed = tid != m_tid;
5279           m_tid = tid;
5280         } else {
5281           if (m_tid != LLDB_INVALID_THREAD_ID) {
5282             thread_changed = true;
5283             m_tid = LLDB_INVALID_THREAD_ID;
5284           }
5285         }
5286       }
5287       const uint32_t stop_id = process ? process->GetStopID() : 0;
5288       const bool stop_id_changed = stop_id != m_stop_id;
5289       bool frame_changed = false;
5290       m_stop_id = stop_id;
5291       m_title.Clear();
5292       if (frame_sp) {
5293         m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
5294         if (m_sc.module_sp) {
5295           m_title.Printf(
5296               "%s", m_sc.module_sp->GetFileSpec().GetFilename().GetCString());
5297           ConstString func_name = m_sc.GetFunctionName();
5298           if (func_name)
5299             m_title.Printf("`%s", func_name.GetCString());
5300         }
5301         const uint32_t frame_idx = frame_sp->GetFrameIndex();
5302         frame_changed = frame_idx != m_frame_idx;
5303         m_frame_idx = frame_idx;
5304       } else {
5305         m_sc.Clear(true);
5306         frame_changed = m_frame_idx != UINT32_MAX;
5307         m_frame_idx = UINT32_MAX;
5308       }
5309 
5310       const bool context_changed =
5311           thread_changed || frame_changed || stop_id_changed;
5312 
5313       if (process_alive) {
5314         if (m_sc.line_entry.IsValid()) {
5315           m_pc_line = m_sc.line_entry.line;
5316           if (m_pc_line != UINT32_MAX)
5317             --m_pc_line; // Convert to zero based line number...
5318           // Update the selected line if the stop ID changed...
5319           if (context_changed)
5320             m_selected_line = m_pc_line;
5321 
5322           if (m_file_sp && m_file_sp->GetFileSpec() == m_sc.line_entry.file) {
5323             // Same file, nothing to do, we should either have the lines or not
5324             // (source file missing)
5325             if (m_selected_line >= static_cast<size_t>(m_first_visible_line)) {
5326               if (m_selected_line >= m_first_visible_line + num_visible_lines)
5327                 m_first_visible_line = m_selected_line - 10;
5328             } else {
5329               if (m_selected_line > 10)
5330                 m_first_visible_line = m_selected_line - 10;
5331               else
5332                 m_first_visible_line = 0;
5333             }
5334           } else {
5335             // File changed, set selected line to the line with the PC
5336             m_selected_line = m_pc_line;
5337             m_file_sp =
5338                 m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file);
5339             if (m_file_sp) {
5340               const size_t num_lines = m_file_sp->GetNumLines();
5341               m_line_width = 1;
5342               for (size_t n = num_lines; n >= 10; n = n / 10)
5343                 ++m_line_width;
5344 
5345               if (num_lines < num_visible_lines ||
5346                   m_selected_line < num_visible_lines)
5347                 m_first_visible_line = 0;
5348               else
5349                 m_first_visible_line = m_selected_line - 10;
5350             }
5351           }
5352         } else {
5353           m_file_sp.reset();
5354         }
5355 
5356         if (!m_file_sp || m_file_sp->GetNumLines() == 0) {
5357           // Show disassembly
5358           bool prefer_file_cache = false;
5359           if (m_sc.function) {
5360             if (m_disassembly_scope != m_sc.function) {
5361               m_disassembly_scope = m_sc.function;
5362               m_disassembly_sp = m_sc.function->GetInstructions(
5363                   exe_ctx, nullptr, !prefer_file_cache);
5364               if (m_disassembly_sp) {
5365                 set_selected_line_to_pc = true;
5366                 m_disassembly_range = m_sc.function->GetAddressRange();
5367               } else {
5368                 m_disassembly_range.Clear();
5369               }
5370             } else {
5371               set_selected_line_to_pc = context_changed;
5372             }
5373           } else if (m_sc.symbol) {
5374             if (m_disassembly_scope != m_sc.symbol) {
5375               m_disassembly_scope = m_sc.symbol;
5376               m_disassembly_sp = m_sc.symbol->GetInstructions(
5377                   exe_ctx, nullptr, prefer_file_cache);
5378               if (m_disassembly_sp) {
5379                 set_selected_line_to_pc = true;
5380                 m_disassembly_range.GetBaseAddress() =
5381                     m_sc.symbol->GetAddress();
5382                 m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize());
5383               } else {
5384                 m_disassembly_range.Clear();
5385               }
5386             } else {
5387               set_selected_line_to_pc = context_changed;
5388             }
5389           }
5390         }
5391       } else {
5392         m_pc_line = UINT32_MAX;
5393       }
5394     }
5395 
5396     const int window_width = window.GetWidth();
5397     window.Erase();
5398     window.DrawTitleBox("Sources");
5399     if (!m_title.GetString().empty()) {
5400       window.AttributeOn(A_REVERSE);
5401       window.MoveCursor(1, 1);
5402       window.PutChar(' ');
5403       window.PutCStringTruncated(1, m_title.GetString().str().c_str());
5404       int x = window.GetCursorX();
5405       if (x < window_width - 1) {
5406         window.Printf("%*s", window_width - x - 1, "");
5407       }
5408       window.AttributeOff(A_REVERSE);
5409     }
5410 
5411     Target *target = exe_ctx.GetTargetPtr();
5412     const size_t num_source_lines = GetNumSourceLines();
5413     if (num_source_lines > 0) {
5414       // Display source
5415       BreakpointLines bp_lines;
5416       if (target) {
5417         BreakpointList &bp_list = target->GetBreakpointList();
5418         const size_t num_bps = bp_list.GetSize();
5419         for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
5420           BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
5421           const size_t num_bps_locs = bp_sp->GetNumLocations();
5422           for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
5423             BreakpointLocationSP bp_loc_sp =
5424                 bp_sp->GetLocationAtIndex(bp_loc_idx);
5425             LineEntry bp_loc_line_entry;
5426             if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry(
5427                     bp_loc_line_entry)) {
5428               if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file) {
5429                 bp_lines.insert(bp_loc_line_entry.line);
5430               }
5431             }
5432           }
5433         }
5434       }
5435 
5436       const attr_t selected_highlight_attr = A_REVERSE;
5437       const attr_t pc_highlight_attr = COLOR_PAIR(BlackOnBlue);
5438 
5439       for (size_t i = 0; i < num_visible_lines; ++i) {
5440         const uint32_t curr_line = m_first_visible_line + i;
5441         if (curr_line < num_source_lines) {
5442           const int line_y = m_min_y + i;
5443           window.MoveCursor(1, line_y);
5444           const bool is_pc_line = curr_line == m_pc_line;
5445           const bool line_is_selected = m_selected_line == curr_line;
5446           // Highlight the line as the PC line first, then if the selected line
5447           // isn't the same as the PC line, highlight it differently
5448           attr_t highlight_attr = 0;
5449           attr_t bp_attr = 0;
5450           if (is_pc_line)
5451             highlight_attr = pc_highlight_attr;
5452           else if (line_is_selected)
5453             highlight_attr = selected_highlight_attr;
5454 
5455           if (bp_lines.find(curr_line + 1) != bp_lines.end())
5456             bp_attr = COLOR_PAIR(BlackOnWhite);
5457 
5458           if (bp_attr)
5459             window.AttributeOn(bp_attr);
5460 
5461           window.Printf(" %*u ", m_line_width, curr_line + 1);
5462 
5463           if (bp_attr)
5464             window.AttributeOff(bp_attr);
5465 
5466           window.PutChar(ACS_VLINE);
5467           // Mark the line with the PC with a diamond
5468           if (is_pc_line)
5469             window.PutChar(ACS_DIAMOND);
5470           else
5471             window.PutChar(' ');
5472 
5473           if (highlight_attr)
5474             window.AttributeOn(highlight_attr);
5475 
5476           StreamString lineStream;
5477           m_file_sp->DisplaySourceLines(curr_line + 1, {}, 0, 0, &lineStream);
5478           StringRef line = lineStream.GetString();
5479           if (line.endswith("\n"))
5480             line = line.drop_back();
5481           bool wasWritten = window.OutputColoredStringTruncated(
5482               1, line, m_first_visible_column, line_is_selected);
5483           if (line_is_selected && !wasWritten) {
5484             // Draw an empty space to show the selected line if empty,
5485             // or draw '<' if nothing is visible because of scrolling too much
5486             // to the right.
5487             window.PutCStringTruncated(
5488                 1, line.empty() && m_first_visible_column == 0 ? " " : "<");
5489           }
5490 
5491           if (is_pc_line && frame_sp &&
5492               frame_sp->GetConcreteFrameIndex() == 0) {
5493             StopInfoSP stop_info_sp;
5494             if (thread)
5495               stop_info_sp = thread->GetStopInfo();
5496             if (stop_info_sp) {
5497               const char *stop_description = stop_info_sp->GetDescription();
5498               if (stop_description && stop_description[0]) {
5499                 size_t stop_description_len = strlen(stop_description);
5500                 int desc_x = window_width - stop_description_len - 16;
5501                 if (desc_x - window.GetCursorX() > 0)
5502                   window.Printf("%*s", desc_x - window.GetCursorX(), "");
5503                 window.MoveCursor(window_width - stop_description_len - 16,
5504                                   line_y);
5505                 const attr_t stop_reason_attr = COLOR_PAIR(WhiteOnBlue);
5506                 window.AttributeOn(stop_reason_attr);
5507                 window.PrintfTruncated(1, " <<< Thread %u: %s ",
5508                                        thread->GetIndexID(), stop_description);
5509                 window.AttributeOff(stop_reason_attr);
5510               }
5511             } else {
5512               window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
5513             }
5514           }
5515           if (highlight_attr)
5516             window.AttributeOff(highlight_attr);
5517         } else {
5518           break;
5519         }
5520       }
5521     } else {
5522       size_t num_disassembly_lines = GetNumDisassemblyLines();
5523       if (num_disassembly_lines > 0) {
5524         // Display disassembly
5525         BreakpointAddrs bp_file_addrs;
5526         Target *target = exe_ctx.GetTargetPtr();
5527         if (target) {
5528           BreakpointList &bp_list = target->GetBreakpointList();
5529           const size_t num_bps = bp_list.GetSize();
5530           for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
5531             BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
5532             const size_t num_bps_locs = bp_sp->GetNumLocations();
5533             for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs;
5534                  ++bp_loc_idx) {
5535               BreakpointLocationSP bp_loc_sp =
5536                   bp_sp->GetLocationAtIndex(bp_loc_idx);
5537               LineEntry bp_loc_line_entry;
5538               const lldb::addr_t file_addr =
5539                   bp_loc_sp->GetAddress().GetFileAddress();
5540               if (file_addr != LLDB_INVALID_ADDRESS) {
5541                 if (m_disassembly_range.ContainsFileAddress(file_addr))
5542                   bp_file_addrs.insert(file_addr);
5543               }
5544             }
5545           }
5546         }
5547 
5548         const attr_t selected_highlight_attr = A_REVERSE;
5549         const attr_t pc_highlight_attr = COLOR_PAIR(WhiteOnBlue);
5550 
5551         StreamString strm;
5552 
5553         InstructionList &insts = m_disassembly_sp->GetInstructionList();
5554         Address pc_address;
5555 
5556         if (frame_sp)
5557           pc_address = frame_sp->GetFrameCodeAddress();
5558         const uint32_t pc_idx =
5559             pc_address.IsValid()
5560                 ? insts.GetIndexOfInstructionAtAddress(pc_address)
5561                 : UINT32_MAX;
5562         if (set_selected_line_to_pc) {
5563           m_selected_line = pc_idx;
5564         }
5565 
5566         const uint32_t non_visible_pc_offset = (num_visible_lines / 5);
5567         if (static_cast<size_t>(m_first_visible_line) >= num_disassembly_lines)
5568           m_first_visible_line = 0;
5569 
5570         if (pc_idx < num_disassembly_lines) {
5571           if (pc_idx < static_cast<uint32_t>(m_first_visible_line) ||
5572               pc_idx >= m_first_visible_line + num_visible_lines)
5573             m_first_visible_line = pc_idx - non_visible_pc_offset;
5574         }
5575 
5576         for (size_t i = 0; i < num_visible_lines; ++i) {
5577           const uint32_t inst_idx = m_first_visible_line + i;
5578           Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get();
5579           if (!inst)
5580             break;
5581 
5582           const int line_y = m_min_y + i;
5583           window.MoveCursor(1, line_y);
5584           const bool is_pc_line = frame_sp && inst_idx == pc_idx;
5585           const bool line_is_selected = m_selected_line == inst_idx;
5586           // Highlight the line as the PC line first, then if the selected line
5587           // isn't the same as the PC line, highlight it differently
5588           attr_t highlight_attr = 0;
5589           attr_t bp_attr = 0;
5590           if (is_pc_line)
5591             highlight_attr = pc_highlight_attr;
5592           else if (line_is_selected)
5593             highlight_attr = selected_highlight_attr;
5594 
5595           if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) !=
5596               bp_file_addrs.end())
5597             bp_attr = COLOR_PAIR(BlackOnWhite);
5598 
5599           if (bp_attr)
5600             window.AttributeOn(bp_attr);
5601 
5602           window.Printf(" 0x%16.16llx ",
5603                         static_cast<unsigned long long>(
5604                             inst->GetAddress().GetLoadAddress(target)));
5605 
5606           if (bp_attr)
5607             window.AttributeOff(bp_attr);
5608 
5609           window.PutChar(ACS_VLINE);
5610           // Mark the line with the PC with a diamond
5611           if (is_pc_line)
5612             window.PutChar(ACS_DIAMOND);
5613           else
5614             window.PutChar(' ');
5615 
5616           if (highlight_attr)
5617             window.AttributeOn(highlight_attr);
5618 
5619           const char *mnemonic = inst->GetMnemonic(&exe_ctx);
5620           const char *operands = inst->GetOperands(&exe_ctx);
5621           const char *comment = inst->GetComment(&exe_ctx);
5622 
5623           if (mnemonic != nullptr && mnemonic[0] == '\0')
5624             mnemonic = nullptr;
5625           if (operands != nullptr && operands[0] == '\0')
5626             operands = nullptr;
5627           if (comment != nullptr && comment[0] == '\0')
5628             comment = nullptr;
5629 
5630           strm.Clear();
5631 
5632           if (mnemonic != nullptr && operands != nullptr && comment != nullptr)
5633             strm.Printf("%-8s %-25s ; %s", mnemonic, operands, comment);
5634           else if (mnemonic != nullptr && operands != nullptr)
5635             strm.Printf("%-8s %s", mnemonic, operands);
5636           else if (mnemonic != nullptr)
5637             strm.Printf("%s", mnemonic);
5638 
5639           int right_pad = 1;
5640           window.PutCStringTruncated(
5641               right_pad,
5642               strm.GetString().substr(m_first_visible_column).data());
5643 
5644           if (is_pc_line && frame_sp &&
5645               frame_sp->GetConcreteFrameIndex() == 0) {
5646             StopInfoSP stop_info_sp;
5647             if (thread)
5648               stop_info_sp = thread->GetStopInfo();
5649             if (stop_info_sp) {
5650               const char *stop_description = stop_info_sp->GetDescription();
5651               if (stop_description && stop_description[0]) {
5652                 size_t stop_description_len = strlen(stop_description);
5653                 int desc_x = window_width - stop_description_len - 16;
5654                 if (desc_x - window.GetCursorX() > 0)
5655                   window.Printf("%*s", desc_x - window.GetCursorX(), "");
5656                 window.MoveCursor(window_width - stop_description_len - 15,
5657                                   line_y);
5658                 window.PrintfTruncated(1, "<<< Thread %u: %s ",
5659                                        thread->GetIndexID(), stop_description);
5660               }
5661             } else {
5662               window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
5663             }
5664           }
5665           if (highlight_attr)
5666             window.AttributeOff(highlight_attr);
5667         }
5668       }
5669     }
5670     return true; // Drawing handled
5671   }
5672 
5673   size_t GetNumLines() {
5674     size_t num_lines = GetNumSourceLines();
5675     if (num_lines == 0)
5676       num_lines = GetNumDisassemblyLines();
5677     return num_lines;
5678   }
5679 
5680   size_t GetNumSourceLines() const {
5681     if (m_file_sp)
5682       return m_file_sp->GetNumLines();
5683     return 0;
5684   }
5685 
5686   size_t GetNumDisassemblyLines() const {
5687     if (m_disassembly_sp)
5688       return m_disassembly_sp->GetInstructionList().GetSize();
5689     return 0;
5690   }
5691 
5692   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
5693     const uint32_t num_visible_lines = NumVisibleLines();
5694     const size_t num_lines = GetNumLines();
5695 
5696     switch (c) {
5697     case ',':
5698     case KEY_PPAGE:
5699       // Page up key
5700       if (static_cast<uint32_t>(m_first_visible_line) > num_visible_lines)
5701         m_first_visible_line -= num_visible_lines;
5702       else
5703         m_first_visible_line = 0;
5704       m_selected_line = m_first_visible_line;
5705       return eKeyHandled;
5706 
5707     case '.':
5708     case KEY_NPAGE:
5709       // Page down key
5710       {
5711         if (m_first_visible_line + num_visible_lines < num_lines)
5712           m_first_visible_line += num_visible_lines;
5713         else if (num_lines < num_visible_lines)
5714           m_first_visible_line = 0;
5715         else
5716           m_first_visible_line = num_lines - num_visible_lines;
5717         m_selected_line = m_first_visible_line;
5718       }
5719       return eKeyHandled;
5720 
5721     case KEY_UP:
5722       if (m_selected_line > 0) {
5723         m_selected_line--;
5724         if (static_cast<size_t>(m_first_visible_line) > m_selected_line)
5725           m_first_visible_line = m_selected_line;
5726       }
5727       return eKeyHandled;
5728 
5729     case KEY_DOWN:
5730       if (m_selected_line + 1 < num_lines) {
5731         m_selected_line++;
5732         if (m_first_visible_line + num_visible_lines < m_selected_line)
5733           m_first_visible_line++;
5734       }
5735       return eKeyHandled;
5736 
5737     case KEY_LEFT:
5738       if (m_first_visible_column > 0)
5739         --m_first_visible_column;
5740       return eKeyHandled;
5741 
5742     case KEY_RIGHT:
5743       ++m_first_visible_column;
5744       return eKeyHandled;
5745 
5746     case '\r':
5747     case '\n':
5748     case KEY_ENTER:
5749       // Set a breakpoint and run to the line using a one shot breakpoint
5750       if (GetNumSourceLines() > 0) {
5751         ExecutionContext exe_ctx =
5752             m_debugger.GetCommandInterpreter().GetExecutionContext();
5753         if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive()) {
5754           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
5755               nullptr, // Don't limit the breakpoint to certain modules
5756               m_file_sp->GetFileSpec(), // Source file
5757               m_selected_line +
5758                   1, // Source line number (m_selected_line is zero based)
5759               0,     // Unspecified column.
5760               0,     // No offset
5761               eLazyBoolCalculate,  // Check inlines using global setting
5762               eLazyBoolCalculate,  // Skip prologue using global setting,
5763               false,               // internal
5764               false,               // request_hardware
5765               eLazyBoolCalculate); // move_to_nearest_code
5766           // Make breakpoint one shot
5767           bp_sp->GetOptions().SetOneShot(true);
5768           exe_ctx.GetProcessRef().Resume();
5769         }
5770       } else if (m_selected_line < GetNumDisassemblyLines()) {
5771         const Instruction *inst = m_disassembly_sp->GetInstructionList()
5772                                       .GetInstructionAtIndex(m_selected_line)
5773                                       .get();
5774         ExecutionContext exe_ctx =
5775             m_debugger.GetCommandInterpreter().GetExecutionContext();
5776         if (exe_ctx.HasTargetScope()) {
5777           Address addr = inst->GetAddress();
5778           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
5779               addr,   // lldb_private::Address
5780               false,  // internal
5781               false); // request_hardware
5782           // Make breakpoint one shot
5783           bp_sp->GetOptions().SetOneShot(true);
5784           exe_ctx.GetProcessRef().Resume();
5785         }
5786       }
5787       return eKeyHandled;
5788 
5789     case 'b': // 'b' == toggle breakpoint on currently selected line
5790       ToggleBreakpointOnSelectedLine();
5791       return eKeyHandled;
5792 
5793     case 'D': // 'D' == detach and keep stopped
5794     {
5795       ExecutionContext exe_ctx =
5796           m_debugger.GetCommandInterpreter().GetExecutionContext();
5797       if (exe_ctx.HasProcessScope())
5798         exe_ctx.GetProcessRef().Detach(true);
5799     }
5800       return eKeyHandled;
5801 
5802     case 'c':
5803       // 'c' == continue
5804       {
5805         ExecutionContext exe_ctx =
5806             m_debugger.GetCommandInterpreter().GetExecutionContext();
5807         if (exe_ctx.HasProcessScope())
5808           exe_ctx.GetProcessRef().Resume();
5809       }
5810       return eKeyHandled;
5811 
5812     case 'f':
5813       // 'f' == step out (finish)
5814       {
5815         ExecutionContext exe_ctx =
5816             m_debugger.GetCommandInterpreter().GetExecutionContext();
5817         if (exe_ctx.HasThreadScope() &&
5818             StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
5819           exe_ctx.GetThreadRef().StepOut();
5820         }
5821       }
5822       return eKeyHandled;
5823 
5824     case 'n': // 'n' == step over
5825     case 'N': // 'N' == step over instruction
5826     {
5827       ExecutionContext exe_ctx =
5828           m_debugger.GetCommandInterpreter().GetExecutionContext();
5829       if (exe_ctx.HasThreadScope() &&
5830           StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
5831         bool source_step = (c == 'n');
5832         exe_ctx.GetThreadRef().StepOver(source_step);
5833       }
5834     }
5835       return eKeyHandled;
5836 
5837     case 's': // 's' == step into
5838     case 'S': // 'S' == step into instruction
5839     {
5840       ExecutionContext exe_ctx =
5841           m_debugger.GetCommandInterpreter().GetExecutionContext();
5842       if (exe_ctx.HasThreadScope() &&
5843           StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
5844         bool source_step = (c == 's');
5845         exe_ctx.GetThreadRef().StepIn(source_step);
5846       }
5847     }
5848       return eKeyHandled;
5849 
5850     case 'u': // 'u' == frame up
5851     case 'd': // 'd' == frame down
5852     {
5853       ExecutionContext exe_ctx =
5854           m_debugger.GetCommandInterpreter().GetExecutionContext();
5855       if (exe_ctx.HasThreadScope()) {
5856         Thread *thread = exe_ctx.GetThreadPtr();
5857         uint32_t frame_idx = thread->GetSelectedFrameIndex();
5858         if (frame_idx == UINT32_MAX)
5859           frame_idx = 0;
5860         if (c == 'u' && frame_idx + 1 < thread->GetStackFrameCount())
5861           ++frame_idx;
5862         else if (c == 'd' && frame_idx > 0)
5863           --frame_idx;
5864         if (thread->SetSelectedFrameByIndex(frame_idx, true))
5865           exe_ctx.SetFrameSP(thread->GetSelectedFrame());
5866       }
5867     }
5868       return eKeyHandled;
5869 
5870     case 'h':
5871       window.CreateHelpSubwindow();
5872       return eKeyHandled;
5873 
5874     default:
5875       break;
5876     }
5877     return eKeyNotHandled;
5878   }
5879 
5880   void ToggleBreakpointOnSelectedLine() {
5881     ExecutionContext exe_ctx =
5882         m_debugger.GetCommandInterpreter().GetExecutionContext();
5883     if (!exe_ctx.HasTargetScope())
5884       return;
5885     if (GetNumSourceLines() > 0) {
5886       // Source file breakpoint.
5887       BreakpointList &bp_list = exe_ctx.GetTargetRef().GetBreakpointList();
5888       const size_t num_bps = bp_list.GetSize();
5889       for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
5890         BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
5891         const size_t num_bps_locs = bp_sp->GetNumLocations();
5892         for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
5893           BreakpointLocationSP bp_loc_sp =
5894               bp_sp->GetLocationAtIndex(bp_loc_idx);
5895           LineEntry bp_loc_line_entry;
5896           if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry(
5897                   bp_loc_line_entry)) {
5898             if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file &&
5899                 m_selected_line + 1 == bp_loc_line_entry.line) {
5900               bool removed =
5901                   exe_ctx.GetTargetRef().RemoveBreakpointByID(bp_sp->GetID());
5902               assert(removed);
5903               UNUSED_IF_ASSERT_DISABLED(removed);
5904               return; // Existing breakpoint removed.
5905             }
5906           }
5907         }
5908       }
5909       // No breakpoint found on the location, add it.
5910       BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
5911           nullptr, // Don't limit the breakpoint to certain modules
5912           m_file_sp->GetFileSpec(), // Source file
5913           m_selected_line +
5914               1, // Source line number (m_selected_line is zero based)
5915           0,     // No column specified.
5916           0,     // No offset
5917           eLazyBoolCalculate,  // Check inlines using global setting
5918           eLazyBoolCalculate,  // Skip prologue using global setting,
5919           false,               // internal
5920           false,               // request_hardware
5921           eLazyBoolCalculate); // move_to_nearest_code
5922     } else {
5923       // Disassembly breakpoint.
5924       assert(GetNumDisassemblyLines() > 0);
5925       assert(m_selected_line < GetNumDisassemblyLines());
5926       const Instruction *inst = m_disassembly_sp->GetInstructionList()
5927                                     .GetInstructionAtIndex(m_selected_line)
5928                                     .get();
5929       Address addr = inst->GetAddress();
5930       // Try to find it.
5931       BreakpointList &bp_list = exe_ctx.GetTargetRef().GetBreakpointList();
5932       const size_t num_bps = bp_list.GetSize();
5933       for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
5934         BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
5935         const size_t num_bps_locs = bp_sp->GetNumLocations();
5936         for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
5937           BreakpointLocationSP bp_loc_sp =
5938               bp_sp->GetLocationAtIndex(bp_loc_idx);
5939           LineEntry bp_loc_line_entry;
5940           const lldb::addr_t file_addr =
5941               bp_loc_sp->GetAddress().GetFileAddress();
5942           if (file_addr == addr.GetFileAddress()) {
5943             bool removed =
5944                 exe_ctx.GetTargetRef().RemoveBreakpointByID(bp_sp->GetID());
5945             assert(removed);
5946             UNUSED_IF_ASSERT_DISABLED(removed);
5947             return; // Existing breakpoint removed.
5948           }
5949         }
5950       }
5951       // No breakpoint found on the address, add it.
5952       BreakpointSP bp_sp =
5953           exe_ctx.GetTargetRef().CreateBreakpoint(addr, // lldb_private::Address
5954                                                   false,  // internal
5955                                                   false); // request_hardware
5956     }
5957   }
5958 
5959 protected:
5960   typedef std::set<uint32_t> BreakpointLines;
5961   typedef std::set<lldb::addr_t> BreakpointAddrs;
5962 
5963   Debugger &m_debugger;
5964   SymbolContext m_sc;
5965   SourceManager::FileSP m_file_sp;
5966   SymbolContextScope *m_disassembly_scope;
5967   lldb::DisassemblerSP m_disassembly_sp;
5968   AddressRange m_disassembly_range;
5969   StreamString m_title;
5970   lldb::user_id_t m_tid;
5971   int m_line_width;
5972   uint32_t m_selected_line; // The selected line
5973   uint32_t m_pc_line;       // The line with the PC
5974   uint32_t m_stop_id;
5975   uint32_t m_frame_idx;
5976   int m_first_visible_line;
5977   int m_first_visible_column;
5978   int m_min_x;
5979   int m_min_y;
5980   int m_max_x;
5981   int m_max_y;
5982 };
5983 
5984 DisplayOptions ValueObjectListDelegate::g_options = {true};
5985 
5986 IOHandlerCursesGUI::IOHandlerCursesGUI(Debugger &debugger)
5987     : IOHandler(debugger, IOHandler::Type::Curses) {}
5988 
5989 void IOHandlerCursesGUI::Activate() {
5990   IOHandler::Activate();
5991   if (!m_app_ap) {
5992     m_app_ap = std::make_unique<Application>(GetInputFILE(), GetOutputFILE());
5993 
5994     // This is both a window and a menu delegate
5995     std::shared_ptr<ApplicationDelegate> app_delegate_sp(
5996         new ApplicationDelegate(*m_app_ap, m_debugger));
5997 
5998     MenuDelegateSP app_menu_delegate_sp =
5999         std::static_pointer_cast<MenuDelegate>(app_delegate_sp);
6000     MenuSP lldb_menu_sp(
6001         new Menu("LLDB", "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB));
6002     MenuSP exit_menuitem_sp(
6003         new Menu("Exit", nullptr, 'x', ApplicationDelegate::eMenuID_LLDBExit));
6004     exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit);
6005     lldb_menu_sp->AddSubmenu(MenuSP(new Menu(
6006         "About LLDB", nullptr, 'a', ApplicationDelegate::eMenuID_LLDBAbout)));
6007     lldb_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
6008     lldb_menu_sp->AddSubmenu(exit_menuitem_sp);
6009 
6010     MenuSP target_menu_sp(new Menu("Target", "F2", KEY_F(2),
6011                                    ApplicationDelegate::eMenuID_Target));
6012     target_menu_sp->AddSubmenu(MenuSP(new Menu(
6013         "Create", nullptr, 'c', ApplicationDelegate::eMenuID_TargetCreate)));
6014     target_menu_sp->AddSubmenu(MenuSP(new Menu(
6015         "Delete", nullptr, 'd', ApplicationDelegate::eMenuID_TargetDelete)));
6016 
6017     MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3),
6018                                     ApplicationDelegate::eMenuID_Process));
6019     process_menu_sp->AddSubmenu(MenuSP(new Menu(
6020         "Attach", nullptr, 'a', ApplicationDelegate::eMenuID_ProcessAttach)));
6021     process_menu_sp->AddSubmenu(
6022         MenuSP(new Menu("Detach and resume", nullptr, 'd',
6023                         ApplicationDelegate::eMenuID_ProcessDetachResume)));
6024     process_menu_sp->AddSubmenu(
6025         MenuSP(new Menu("Detach suspended", nullptr, 's',
6026                         ApplicationDelegate::eMenuID_ProcessDetachSuspended)));
6027     process_menu_sp->AddSubmenu(MenuSP(new Menu(
6028         "Launch", nullptr, 'l', ApplicationDelegate::eMenuID_ProcessLaunch)));
6029     process_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
6030     process_menu_sp->AddSubmenu(
6031         MenuSP(new Menu("Continue", nullptr, 'c',
6032                         ApplicationDelegate::eMenuID_ProcessContinue)));
6033     process_menu_sp->AddSubmenu(MenuSP(new Menu(
6034         "Halt", nullptr, 'h', ApplicationDelegate::eMenuID_ProcessHalt)));
6035     process_menu_sp->AddSubmenu(MenuSP(new Menu(
6036         "Kill", nullptr, 'k', ApplicationDelegate::eMenuID_ProcessKill)));
6037 
6038     MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4),
6039                                    ApplicationDelegate::eMenuID_Thread));
6040     thread_menu_sp->AddSubmenu(MenuSP(new Menu(
6041         "Step In", nullptr, 'i', ApplicationDelegate::eMenuID_ThreadStepIn)));
6042     thread_menu_sp->AddSubmenu(
6043         MenuSP(new Menu("Step Over", nullptr, 'v',
6044                         ApplicationDelegate::eMenuID_ThreadStepOver)));
6045     thread_menu_sp->AddSubmenu(MenuSP(new Menu(
6046         "Step Out", nullptr, 'o', ApplicationDelegate::eMenuID_ThreadStepOut)));
6047 
6048     MenuSP view_menu_sp(
6049         new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View));
6050     view_menu_sp->AddSubmenu(
6051         MenuSP(new Menu("Backtrace", nullptr, 'b',
6052                         ApplicationDelegate::eMenuID_ViewBacktrace)));
6053     view_menu_sp->AddSubmenu(
6054         MenuSP(new Menu("Registers", nullptr, 'r',
6055                         ApplicationDelegate::eMenuID_ViewRegisters)));
6056     view_menu_sp->AddSubmenu(MenuSP(new Menu(
6057         "Source", nullptr, 's', ApplicationDelegate::eMenuID_ViewSource)));
6058     view_menu_sp->AddSubmenu(
6059         MenuSP(new Menu("Variables", nullptr, 'v',
6060                         ApplicationDelegate::eMenuID_ViewVariables)));
6061 
6062     MenuSP help_menu_sp(
6063         new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help));
6064     help_menu_sp->AddSubmenu(MenuSP(new Menu(
6065         "GUI Help", nullptr, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp)));
6066 
6067     m_app_ap->Initialize();
6068     WindowSP &main_window_sp = m_app_ap->GetMainWindow();
6069 
6070     MenuSP menubar_sp(new Menu(Menu::Type::Bar));
6071     menubar_sp->AddSubmenu(lldb_menu_sp);
6072     menubar_sp->AddSubmenu(target_menu_sp);
6073     menubar_sp->AddSubmenu(process_menu_sp);
6074     menubar_sp->AddSubmenu(thread_menu_sp);
6075     menubar_sp->AddSubmenu(view_menu_sp);
6076     menubar_sp->AddSubmenu(help_menu_sp);
6077     menubar_sp->SetDelegate(app_menu_delegate_sp);
6078 
6079     Rect content_bounds = main_window_sp->GetFrame();
6080     Rect menubar_bounds = content_bounds.MakeMenuBar();
6081     Rect status_bounds = content_bounds.MakeStatusBar();
6082     Rect source_bounds;
6083     Rect variables_bounds;
6084     Rect threads_bounds;
6085     Rect source_variables_bounds;
6086     content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds,
6087                                            threads_bounds);
6088     source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds,
6089                                                       variables_bounds);
6090 
6091     WindowSP menubar_window_sp =
6092         main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false);
6093     // Let the menubar get keys if the active window doesn't handle the keys
6094     // that are typed so it can respond to menubar key presses.
6095     menubar_window_sp->SetCanBeActive(
6096         false); // Don't let the menubar become the active window
6097     menubar_window_sp->SetDelegate(menubar_sp);
6098 
6099     WindowSP source_window_sp(
6100         main_window_sp->CreateSubWindow("Source", source_bounds, true));
6101     WindowSP variables_window_sp(
6102         main_window_sp->CreateSubWindow("Variables", variables_bounds, false));
6103     WindowSP threads_window_sp(
6104         main_window_sp->CreateSubWindow("Threads", threads_bounds, false));
6105     WindowSP status_window_sp(
6106         main_window_sp->CreateSubWindow("Status", status_bounds, false));
6107     status_window_sp->SetCanBeActive(
6108         false); // Don't let the status bar become the active window
6109     main_window_sp->SetDelegate(
6110         std::static_pointer_cast<WindowDelegate>(app_delegate_sp));
6111     source_window_sp->SetDelegate(
6112         WindowDelegateSP(new SourceFileWindowDelegate(m_debugger)));
6113     variables_window_sp->SetDelegate(
6114         WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
6115     TreeDelegateSP thread_delegate_sp(new ThreadsTreeDelegate(m_debugger));
6116     threads_window_sp->SetDelegate(WindowDelegateSP(
6117         new TreeWindowDelegate(m_debugger, thread_delegate_sp)));
6118     status_window_sp->SetDelegate(
6119         WindowDelegateSP(new StatusBarWindowDelegate(m_debugger)));
6120 
6121     // Show the main help window once the first time the curses GUI is launched
6122     static bool g_showed_help = false;
6123     if (!g_showed_help) {
6124       g_showed_help = true;
6125       main_window_sp->CreateHelpSubwindow();
6126     }
6127 
6128     // All colors with black background.
6129     init_pair(1, COLOR_BLACK, COLOR_BLACK);
6130     init_pair(2, COLOR_RED, COLOR_BLACK);
6131     init_pair(3, COLOR_GREEN, COLOR_BLACK);
6132     init_pair(4, COLOR_YELLOW, COLOR_BLACK);
6133     init_pair(5, COLOR_BLUE, COLOR_BLACK);
6134     init_pair(6, COLOR_MAGENTA, COLOR_BLACK);
6135     init_pair(7, COLOR_CYAN, COLOR_BLACK);
6136     init_pair(8, COLOR_WHITE, COLOR_BLACK);
6137     // All colors with blue background.
6138     init_pair(9, COLOR_BLACK, COLOR_BLUE);
6139     init_pair(10, COLOR_RED, COLOR_BLUE);
6140     init_pair(11, COLOR_GREEN, COLOR_BLUE);
6141     init_pair(12, COLOR_YELLOW, COLOR_BLUE);
6142     init_pair(13, COLOR_BLUE, COLOR_BLUE);
6143     init_pair(14, COLOR_MAGENTA, COLOR_BLUE);
6144     init_pair(15, COLOR_CYAN, COLOR_BLUE);
6145     init_pair(16, COLOR_WHITE, COLOR_BLUE);
6146     // These must match the order in the color indexes enum.
6147     init_pair(17, COLOR_BLACK, COLOR_WHITE);
6148     init_pair(18, COLOR_MAGENTA, COLOR_WHITE);
6149     static_assert(LastColorPairIndex == 18, "Color indexes do not match.");
6150 
6151     define_key("\033[Z", KEY_SHIFT_TAB);
6152   }
6153 }
6154 
6155 void IOHandlerCursesGUI::Deactivate() { m_app_ap->Terminate(); }
6156 
6157 void IOHandlerCursesGUI::Run() {
6158   m_app_ap->Run(m_debugger);
6159   SetIsDone(true);
6160 }
6161 
6162 IOHandlerCursesGUI::~IOHandlerCursesGUI() = default;
6163 
6164 void IOHandlerCursesGUI::Cancel() {}
6165 
6166 bool IOHandlerCursesGUI::Interrupt() { return false; }
6167 
6168 void IOHandlerCursesGUI::GotEOF() {}
6169 
6170 void IOHandlerCursesGUI::TerminalSizeChanged() {
6171   m_app_ap->TerminalSizeChanged();
6172 }
6173 
6174 #endif // LLDB_ENABLE_CURSES
6175