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