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