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       // It we can get UTF8 characters to work we should try to use the
4542       // "symbol" UTF8 string below
4543       //            const char *symbol = "";
4544       //            if (row.expanded)
4545       //                symbol = "\xe2\x96\xbd ";
4546       //            else
4547       //                symbol = "\xe2\x96\xb7 ";
4548       //            window.PutCString (symbol);
4549 
4550       // The ACS_DARROW and ACS_RARROW don't look very nice they are just a 'v'
4551       // or '>' character...
4552       //            if (expanded)
4553       //                window.PutChar (ACS_DARROW);
4554       //            else
4555       //                window.PutChar (ACS_RARROW);
4556       // Since we can't find any good looking right arrow/down arrow symbols,
4557       // just use a diamond...
4558       window.PutChar(ACS_DIAMOND);
4559       window.PutChar(ACS_HLINE);
4560     }
4561   }
4562 
4563   void DrawTreeForChild(Window &window, Row *child, uint32_t reverse_depth) {
4564     if (parent)
4565       parent->DrawTreeForChild(window, this, reverse_depth + 1);
4566 
4567     if (&GetChildren().back() == child) {
4568       // Last child
4569       if (reverse_depth == 0) {
4570         window.PutChar(ACS_LLCORNER);
4571         window.PutChar(ACS_HLINE);
4572       } else {
4573         window.PutChar(' ');
4574         window.PutChar(' ');
4575       }
4576     } else {
4577       if (reverse_depth == 0) {
4578         window.PutChar(ACS_LTEE);
4579         window.PutChar(ACS_HLINE);
4580       } else {
4581         window.PutChar(ACS_VLINE);
4582         window.PutChar(' ');
4583       }
4584     }
4585   }
4586 };
4587 
4588 struct DisplayOptions {
4589   bool show_types;
4590 };
4591 
4592 class TreeItem;
4593 
4594 class TreeDelegate {
4595 public:
4596   TreeDelegate() = default;
4597   virtual ~TreeDelegate() = default;
4598 
4599   virtual void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) = 0;
4600   virtual void TreeDelegateGenerateChildren(TreeItem &item) = 0;
4601   virtual void TreeDelegateUpdateSelection(TreeItem &root, int &selection_index,
4602                                            TreeItem *&selected_item) {}
4603   // This is invoked when a tree item is selected. If true is returned, the
4604   // views are updated.
4605   virtual bool TreeDelegateItemSelected(TreeItem &item) = 0;
4606   virtual bool TreeDelegateExpandRootByDefault() { return false; }
4607   // This is mostly useful for root tree delegates. If false is returned,
4608   // drawing will be skipped completely. This is needed, for instance, in
4609   // skipping drawing of the threads tree if there is no running process.
4610   virtual bool TreeDelegateShouldDraw() { return true; }
4611 };
4612 
4613 typedef std::shared_ptr<TreeDelegate> TreeDelegateSP;
4614 
4615 class TreeItem {
4616 public:
4617   TreeItem(TreeItem *parent, TreeDelegate &delegate, bool might_have_children)
4618       : m_parent(parent), m_delegate(delegate), m_children(),
4619         m_might_have_children(might_have_children) {
4620     if (m_parent == nullptr)
4621       m_is_expanded = m_delegate.TreeDelegateExpandRootByDefault();
4622   }
4623 
4624   TreeItem &operator=(const TreeItem &rhs) {
4625     if (this != &rhs) {
4626       m_parent = rhs.m_parent;
4627       m_delegate = rhs.m_delegate;
4628       m_user_data = rhs.m_user_data;
4629       m_identifier = rhs.m_identifier;
4630       m_row_idx = rhs.m_row_idx;
4631       m_children = rhs.m_children;
4632       m_might_have_children = rhs.m_might_have_children;
4633       m_is_expanded = rhs.m_is_expanded;
4634     }
4635     return *this;
4636   }
4637 
4638   TreeItem(const TreeItem &) = default;
4639 
4640   size_t GetDepth() const {
4641     if (m_parent)
4642       return 1 + m_parent->GetDepth();
4643     return 0;
4644   }
4645 
4646   int GetRowIndex() const { return m_row_idx; }
4647 
4648   void ClearChildren() { m_children.clear(); }
4649 
4650   void Resize(size_t n, const TreeItem &t) { m_children.resize(n, t); }
4651 
4652   TreeItem &operator[](size_t i) { return m_children[i]; }
4653 
4654   void SetRowIndex(int row_idx) { m_row_idx = row_idx; }
4655 
4656   size_t GetNumChildren() {
4657     m_delegate.TreeDelegateGenerateChildren(*this);
4658     return m_children.size();
4659   }
4660 
4661   void ItemWasSelected() { m_delegate.TreeDelegateItemSelected(*this); }
4662 
4663   void CalculateRowIndexes(int &row_idx) {
4664     SetRowIndex(row_idx);
4665     ++row_idx;
4666 
4667     const bool expanded = IsExpanded();
4668 
4669     // The root item must calculate its children, or we must calculate the
4670     // number of children if the item is expanded
4671     if (m_parent == nullptr || expanded)
4672       GetNumChildren();
4673 
4674     for (auto &item : m_children) {
4675       if (expanded)
4676         item.CalculateRowIndexes(row_idx);
4677       else
4678         item.SetRowIndex(-1);
4679     }
4680   }
4681 
4682   TreeItem *GetParent() { return m_parent; }
4683 
4684   bool IsExpanded() const { return m_is_expanded; }
4685 
4686   void Expand() { m_is_expanded = true; }
4687 
4688   void Unexpand() { m_is_expanded = false; }
4689 
4690   bool Draw(Window &window, const int first_visible_row,
4691             const uint32_t selected_row_idx, int &row_idx, int &num_rows_left) {
4692     if (num_rows_left <= 0)
4693       return false;
4694 
4695     if (m_row_idx >= first_visible_row) {
4696       window.MoveCursor(2, row_idx + 1);
4697 
4698       if (m_parent)
4699         m_parent->DrawTreeForChild(window, this, 0);
4700 
4701       if (m_might_have_children) {
4702         // It we can get UTF8 characters to work we should try to use the
4703         // "symbol" UTF8 string below
4704         //            const char *symbol = "";
4705         //            if (row.expanded)
4706         //                symbol = "\xe2\x96\xbd ";
4707         //            else
4708         //                symbol = "\xe2\x96\xb7 ";
4709         //            window.PutCString (symbol);
4710 
4711         // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
4712         // 'v' or '>' character...
4713         //            if (expanded)
4714         //                window.PutChar (ACS_DARROW);
4715         //            else
4716         //                window.PutChar (ACS_RARROW);
4717         // Since we can't find any good looking right arrow/down arrow symbols,
4718         // just use a diamond...
4719         window.PutChar(ACS_DIAMOND);
4720         window.PutChar(ACS_HLINE);
4721       }
4722       bool highlight = (selected_row_idx == static_cast<size_t>(m_row_idx)) &&
4723                        window.IsActive();
4724 
4725       if (highlight)
4726         window.AttributeOn(A_REVERSE);
4727 
4728       m_delegate.TreeDelegateDrawTreeItem(*this, window);
4729 
4730       if (highlight)
4731         window.AttributeOff(A_REVERSE);
4732       ++row_idx;
4733       --num_rows_left;
4734     }
4735 
4736     if (num_rows_left <= 0)
4737       return false; // We are done drawing...
4738 
4739     if (IsExpanded()) {
4740       for (auto &item : m_children) {
4741         // If we displayed all the rows and item.Draw() returns false we are
4742         // done drawing and can exit this for loop
4743         if (!item.Draw(window, first_visible_row, selected_row_idx, row_idx,
4744                        num_rows_left))
4745           break;
4746       }
4747     }
4748     return num_rows_left >= 0; // Return true if not done drawing yet
4749   }
4750 
4751   void DrawTreeForChild(Window &window, TreeItem *child,
4752                         uint32_t reverse_depth) {
4753     if (m_parent)
4754       m_parent->DrawTreeForChild(window, this, reverse_depth + 1);
4755 
4756     if (&m_children.back() == child) {
4757       // Last child
4758       if (reverse_depth == 0) {
4759         window.PutChar(ACS_LLCORNER);
4760         window.PutChar(ACS_HLINE);
4761       } else {
4762         window.PutChar(' ');
4763         window.PutChar(' ');
4764       }
4765     } else {
4766       if (reverse_depth == 0) {
4767         window.PutChar(ACS_LTEE);
4768         window.PutChar(ACS_HLINE);
4769       } else {
4770         window.PutChar(ACS_VLINE);
4771         window.PutChar(' ');
4772       }
4773     }
4774   }
4775 
4776   TreeItem *GetItemForRowIndex(uint32_t row_idx) {
4777     if (static_cast<uint32_t>(m_row_idx) == row_idx)
4778       return this;
4779     if (m_children.empty())
4780       return nullptr;
4781     if (IsExpanded()) {
4782       for (auto &item : m_children) {
4783         TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx);
4784         if (selected_item_ptr)
4785           return selected_item_ptr;
4786       }
4787     }
4788     return nullptr;
4789   }
4790 
4791   void *GetUserData() const { return m_user_data; }
4792 
4793   void SetUserData(void *user_data) { m_user_data = user_data; }
4794 
4795   uint64_t GetIdentifier() const { return m_identifier; }
4796 
4797   void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
4798 
4799   const std::string &GetText() const { return m_text; }
4800 
4801   void SetText(const char *text) {
4802     if (text == nullptr) {
4803       m_text.clear();
4804       return;
4805     }
4806     m_text = text;
4807   }
4808 
4809   void SetMightHaveChildren(bool b) { m_might_have_children = b; }
4810 
4811 protected:
4812   TreeItem *m_parent;
4813   TreeDelegate &m_delegate;
4814   void *m_user_data = nullptr;
4815   uint64_t m_identifier = 0;
4816   std::string m_text;
4817   int m_row_idx = -1; // Zero based visible row index, -1 if not visible or for
4818                       // the root item
4819   std::vector<TreeItem> m_children;
4820   bool m_might_have_children;
4821   bool m_is_expanded = false;
4822 };
4823 
4824 class TreeWindowDelegate : public WindowDelegate {
4825 public:
4826   TreeWindowDelegate(Debugger &debugger, const TreeDelegateSP &delegate_sp)
4827       : m_debugger(debugger), m_delegate_sp(delegate_sp),
4828         m_root(nullptr, *delegate_sp, true) {}
4829 
4830   int NumVisibleRows() const { return m_max_y - m_min_y; }
4831 
4832   bool WindowDelegateDraw(Window &window, bool force) override {
4833     m_min_x = 2;
4834     m_min_y = 1;
4835     m_max_x = window.GetWidth() - 1;
4836     m_max_y = window.GetHeight() - 1;
4837 
4838     window.Erase();
4839     window.DrawTitleBox(window.GetName());
4840 
4841     if (!m_delegate_sp->TreeDelegateShouldDraw()) {
4842       m_selected_item = nullptr;
4843       return true;
4844     }
4845 
4846     const int num_visible_rows = NumVisibleRows();
4847     m_num_rows = 0;
4848     m_root.CalculateRowIndexes(m_num_rows);
4849     m_delegate_sp->TreeDelegateUpdateSelection(m_root, m_selected_row_idx,
4850                                                m_selected_item);
4851 
4852     // If we unexpanded while having something selected our total number of
4853     // rows is less than the num visible rows, then make sure we show all the
4854     // rows by setting the first visible row accordingly.
4855     if (m_first_visible_row > 0 && m_num_rows < num_visible_rows)
4856       m_first_visible_row = 0;
4857 
4858     // Make sure the selected row is always visible
4859     if (m_selected_row_idx < m_first_visible_row)
4860       m_first_visible_row = m_selected_row_idx;
4861     else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
4862       m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
4863 
4864     int row_idx = 0;
4865     int num_rows_left = num_visible_rows;
4866     m_root.Draw(window, m_first_visible_row, m_selected_row_idx, row_idx,
4867                 num_rows_left);
4868     // Get the selected row
4869     m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4870 
4871     return true; // Drawing handled
4872   }
4873 
4874   const char *WindowDelegateGetHelpText() override {
4875     return "Thread window keyboard shortcuts:";
4876   }
4877 
4878   KeyHelp *WindowDelegateGetKeyHelp() override {
4879     static curses::KeyHelp g_source_view_key_help[] = {
4880         {KEY_UP, "Select previous item"},
4881         {KEY_DOWN, "Select next item"},
4882         {KEY_RIGHT, "Expand the selected item"},
4883         {KEY_LEFT,
4884          "Unexpand the selected item or select parent if not expanded"},
4885         {KEY_PPAGE, "Page up"},
4886         {KEY_NPAGE, "Page down"},
4887         {'h', "Show help dialog"},
4888         {' ', "Toggle item expansion"},
4889         {',', "Page up"},
4890         {'.', "Page down"},
4891         {'\0', nullptr}};
4892     return g_source_view_key_help;
4893   }
4894 
4895   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
4896     switch (c) {
4897     case ',':
4898     case KEY_PPAGE:
4899       // Page up key
4900       if (m_first_visible_row > 0) {
4901         if (m_first_visible_row > m_max_y)
4902           m_first_visible_row -= m_max_y;
4903         else
4904           m_first_visible_row = 0;
4905         m_selected_row_idx = m_first_visible_row;
4906         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4907         if (m_selected_item)
4908           m_selected_item->ItemWasSelected();
4909       }
4910       return eKeyHandled;
4911 
4912     case '.':
4913     case KEY_NPAGE:
4914       // Page down key
4915       if (m_num_rows > m_max_y) {
4916         if (m_first_visible_row + m_max_y < m_num_rows) {
4917           m_first_visible_row += m_max_y;
4918           m_selected_row_idx = m_first_visible_row;
4919           m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4920           if (m_selected_item)
4921             m_selected_item->ItemWasSelected();
4922         }
4923       }
4924       return eKeyHandled;
4925 
4926     case KEY_UP:
4927       if (m_selected_row_idx > 0) {
4928         --m_selected_row_idx;
4929         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4930         if (m_selected_item)
4931           m_selected_item->ItemWasSelected();
4932       }
4933       return eKeyHandled;
4934 
4935     case KEY_DOWN:
4936       if (m_selected_row_idx + 1 < m_num_rows) {
4937         ++m_selected_row_idx;
4938         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4939         if (m_selected_item)
4940           m_selected_item->ItemWasSelected();
4941       }
4942       return eKeyHandled;
4943 
4944     case KEY_RIGHT:
4945       if (m_selected_item) {
4946         if (!m_selected_item->IsExpanded())
4947           m_selected_item->Expand();
4948       }
4949       return eKeyHandled;
4950 
4951     case KEY_LEFT:
4952       if (m_selected_item) {
4953         if (m_selected_item->IsExpanded())
4954           m_selected_item->Unexpand();
4955         else if (m_selected_item->GetParent()) {
4956           m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex();
4957           m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4958           if (m_selected_item)
4959             m_selected_item->ItemWasSelected();
4960         }
4961       }
4962       return eKeyHandled;
4963 
4964     case ' ':
4965       // Toggle expansion state when SPACE is pressed
4966       if (m_selected_item) {
4967         if (m_selected_item->IsExpanded())
4968           m_selected_item->Unexpand();
4969         else
4970           m_selected_item->Expand();
4971       }
4972       return eKeyHandled;
4973 
4974     case 'h':
4975       window.CreateHelpSubwindow();
4976       return eKeyHandled;
4977 
4978     default:
4979       break;
4980     }
4981     return eKeyNotHandled;
4982   }
4983 
4984 protected:
4985   Debugger &m_debugger;
4986   TreeDelegateSP m_delegate_sp;
4987   TreeItem m_root;
4988   TreeItem *m_selected_item = nullptr;
4989   int m_num_rows = 0;
4990   int m_selected_row_idx = 0;
4991   int m_first_visible_row = 0;
4992   int m_min_x = 0;
4993   int m_min_y = 0;
4994   int m_max_x = 0;
4995   int m_max_y = 0;
4996 };
4997 
4998 // A tree delegate that just draws the text member of the tree item, it doesn't
4999 // have any children or actions.
5000 class TextTreeDelegate : public TreeDelegate {
5001 public:
5002   TextTreeDelegate() : TreeDelegate() {}
5003 
5004   ~TextTreeDelegate() override = default;
5005 
5006   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5007     window.PutCStringTruncated(1, item.GetText().c_str());
5008   }
5009 
5010   void TreeDelegateGenerateChildren(TreeItem &item) override {}
5011 
5012   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5013 };
5014 
5015 class FrameTreeDelegate : public TreeDelegate {
5016 public:
5017   FrameTreeDelegate() : TreeDelegate() {
5018     FormatEntity::Parse(
5019         "#${frame.index}: {${function.name}${function.pc-offset}}}", m_format);
5020   }
5021 
5022   ~FrameTreeDelegate() override = default;
5023 
5024   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5025     Thread *thread = (Thread *)item.GetUserData();
5026     if (thread) {
5027       const uint64_t frame_idx = item.GetIdentifier();
5028       StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_idx);
5029       if (frame_sp) {
5030         StreamString strm;
5031         const SymbolContext &sc =
5032             frame_sp->GetSymbolContext(eSymbolContextEverything);
5033         ExecutionContext exe_ctx(frame_sp);
5034         if (FormatEntity::Format(m_format, strm, &sc, &exe_ctx, nullptr,
5035                                  nullptr, false, false)) {
5036           int right_pad = 1;
5037           window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
5038         }
5039       }
5040     }
5041   }
5042 
5043   void TreeDelegateGenerateChildren(TreeItem &item) override {
5044     // No children for frames yet...
5045   }
5046 
5047   bool TreeDelegateItemSelected(TreeItem &item) override {
5048     Thread *thread = (Thread *)item.GetUserData();
5049     if (thread) {
5050       thread->GetProcess()->GetThreadList().SetSelectedThreadByID(
5051           thread->GetID());
5052       const uint64_t frame_idx = item.GetIdentifier();
5053       thread->SetSelectedFrameByIndex(frame_idx);
5054       return true;
5055     }
5056     return false;
5057   }
5058 
5059 protected:
5060   FormatEntity::Entry m_format;
5061 };
5062 
5063 class ThreadTreeDelegate : public TreeDelegate {
5064 public:
5065   ThreadTreeDelegate(Debugger &debugger)
5066       : TreeDelegate(), m_debugger(debugger) {
5067     FormatEntity::Parse("thread #${thread.index}: tid = ${thread.id}{, stop "
5068                         "reason = ${thread.stop-reason}}",
5069                         m_format);
5070   }
5071 
5072   ~ThreadTreeDelegate() override = default;
5073 
5074   ProcessSP GetProcess() {
5075     return m_debugger.GetCommandInterpreter()
5076         .GetExecutionContext()
5077         .GetProcessSP();
5078   }
5079 
5080   ThreadSP GetThread(const TreeItem &item) {
5081     ProcessSP process_sp = GetProcess();
5082     if (process_sp)
5083       return process_sp->GetThreadList().FindThreadByID(item.GetIdentifier());
5084     return ThreadSP();
5085   }
5086 
5087   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5088     ThreadSP thread_sp = GetThread(item);
5089     if (thread_sp) {
5090       StreamString strm;
5091       ExecutionContext exe_ctx(thread_sp);
5092       if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
5093                                nullptr, false, false)) {
5094         int right_pad = 1;
5095         window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
5096       }
5097     }
5098   }
5099 
5100   void TreeDelegateGenerateChildren(TreeItem &item) override {
5101     ProcessSP process_sp = GetProcess();
5102     if (process_sp && process_sp->IsAlive()) {
5103       StateType state = process_sp->GetState();
5104       if (StateIsStoppedState(state, true)) {
5105         ThreadSP thread_sp = GetThread(item);
5106         if (thread_sp) {
5107           if (m_stop_id == process_sp->GetStopID() &&
5108               thread_sp->GetID() == m_tid)
5109             return; // Children are already up to date
5110           if (!m_frame_delegate_sp) {
5111             // Always expand the thread item the first time we show it
5112             m_frame_delegate_sp = std::make_shared<FrameTreeDelegate>();
5113           }
5114 
5115           m_stop_id = process_sp->GetStopID();
5116           m_tid = thread_sp->GetID();
5117 
5118           TreeItem t(&item, *m_frame_delegate_sp, false);
5119           size_t num_frames = thread_sp->GetStackFrameCount();
5120           item.Resize(num_frames, t);
5121           for (size_t i = 0; i < num_frames; ++i) {
5122             item[i].SetUserData(thread_sp.get());
5123             item[i].SetIdentifier(i);
5124           }
5125         }
5126         return;
5127       }
5128     }
5129     item.ClearChildren();
5130   }
5131 
5132   bool TreeDelegateItemSelected(TreeItem &item) override {
5133     ProcessSP process_sp = GetProcess();
5134     if (process_sp && process_sp->IsAlive()) {
5135       StateType state = process_sp->GetState();
5136       if (StateIsStoppedState(state, true)) {
5137         ThreadSP thread_sp = GetThread(item);
5138         if (thread_sp) {
5139           ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList();
5140           std::lock_guard<std::recursive_mutex> guard(thread_list.GetMutex());
5141           ThreadSP selected_thread_sp = thread_list.GetSelectedThread();
5142           if (selected_thread_sp->GetID() != thread_sp->GetID()) {
5143             thread_list.SetSelectedThreadByID(thread_sp->GetID());
5144             return true;
5145           }
5146         }
5147       }
5148     }
5149     return false;
5150   }
5151 
5152 protected:
5153   Debugger &m_debugger;
5154   std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp;
5155   lldb::user_id_t m_tid = LLDB_INVALID_THREAD_ID;
5156   uint32_t m_stop_id = UINT32_MAX;
5157   FormatEntity::Entry m_format;
5158 };
5159 
5160 class ThreadsTreeDelegate : public TreeDelegate {
5161 public:
5162   ThreadsTreeDelegate(Debugger &debugger)
5163       : TreeDelegate(), m_thread_delegate_sp(), m_debugger(debugger) {
5164     FormatEntity::Parse("process ${process.id}{, name = ${process.name}}",
5165                         m_format);
5166   }
5167 
5168   ~ThreadsTreeDelegate() override = default;
5169 
5170   ProcessSP GetProcess() {
5171     return m_debugger.GetCommandInterpreter()
5172         .GetExecutionContext()
5173         .GetProcessSP();
5174   }
5175 
5176   bool TreeDelegateShouldDraw() override {
5177     ProcessSP process = GetProcess();
5178     if (!process)
5179       return false;
5180 
5181     if (StateIsRunningState(process->GetState()))
5182       return false;
5183 
5184     return true;
5185   }
5186 
5187   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5188     ProcessSP process_sp = GetProcess();
5189     if (process_sp && process_sp->IsAlive()) {
5190       StreamString strm;
5191       ExecutionContext exe_ctx(process_sp);
5192       if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
5193                                nullptr, false, false)) {
5194         int right_pad = 1;
5195         window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
5196       }
5197     }
5198   }
5199 
5200   void TreeDelegateGenerateChildren(TreeItem &item) override {
5201     ProcessSP process_sp = GetProcess();
5202     m_update_selection = false;
5203     if (process_sp && process_sp->IsAlive()) {
5204       StateType state = process_sp->GetState();
5205       if (StateIsStoppedState(state, true)) {
5206         const uint32_t stop_id = process_sp->GetStopID();
5207         if (m_stop_id == stop_id)
5208           return; // Children are already up to date
5209 
5210         m_stop_id = stop_id;
5211         m_update_selection = true;
5212 
5213         if (!m_thread_delegate_sp) {
5214           // Always expand the thread item the first time we show it
5215           // item.Expand();
5216           m_thread_delegate_sp =
5217               std::make_shared<ThreadTreeDelegate>(m_debugger);
5218         }
5219 
5220         TreeItem t(&item, *m_thread_delegate_sp, false);
5221         ThreadList &threads = process_sp->GetThreadList();
5222         std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
5223         ThreadSP selected_thread = threads.GetSelectedThread();
5224         size_t num_threads = threads.GetSize();
5225         item.Resize(num_threads, t);
5226         for (size_t i = 0; i < num_threads; ++i) {
5227           ThreadSP thread = threads.GetThreadAtIndex(i);
5228           item[i].SetIdentifier(thread->GetID());
5229           item[i].SetMightHaveChildren(true);
5230           if (selected_thread->GetID() == thread->GetID())
5231             item[i].Expand();
5232         }
5233         return;
5234       }
5235     }
5236     item.ClearChildren();
5237   }
5238 
5239   void TreeDelegateUpdateSelection(TreeItem &root, int &selection_index,
5240                                    TreeItem *&selected_item) override {
5241     if (!m_update_selection)
5242       return;
5243 
5244     ProcessSP process_sp = GetProcess();
5245     if (!(process_sp && process_sp->IsAlive()))
5246       return;
5247 
5248     StateType state = process_sp->GetState();
5249     if (!StateIsStoppedState(state, true))
5250       return;
5251 
5252     ThreadList &threads = process_sp->GetThreadList();
5253     std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
5254     ThreadSP selected_thread = threads.GetSelectedThread();
5255     size_t num_threads = threads.GetSize();
5256     for (size_t i = 0; i < num_threads; ++i) {
5257       ThreadSP thread = threads.GetThreadAtIndex(i);
5258       if (selected_thread->GetID() == thread->GetID()) {
5259         selected_item = &root[i][thread->GetSelectedFrameIndex()];
5260         selection_index = selected_item->GetRowIndex();
5261         return;
5262       }
5263     }
5264   }
5265 
5266   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5267 
5268   bool TreeDelegateExpandRootByDefault() override { return true; }
5269 
5270 protected:
5271   std::shared_ptr<ThreadTreeDelegate> m_thread_delegate_sp;
5272   Debugger &m_debugger;
5273   uint32_t m_stop_id = UINT32_MAX;
5274   bool m_update_selection = false;
5275   FormatEntity::Entry m_format;
5276 };
5277 
5278 class BreakpointLocationTreeDelegate : public TreeDelegate {
5279 public:
5280   BreakpointLocationTreeDelegate(Debugger &debugger)
5281       : TreeDelegate(), m_debugger(debugger) {}
5282 
5283   ~BreakpointLocationTreeDelegate() override = default;
5284 
5285   Process *GetProcess() {
5286     ExecutionContext exe_ctx(
5287         m_debugger.GetCommandInterpreter().GetExecutionContext());
5288     return exe_ctx.GetProcessPtr();
5289   }
5290 
5291   BreakpointLocationSP GetBreakpointLocation(const TreeItem &item) {
5292     Breakpoint *breakpoint = (Breakpoint *)item.GetUserData();
5293     return breakpoint->GetLocationAtIndex(item.GetIdentifier());
5294   }
5295 
5296   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5297     BreakpointLocationSP breakpoint_location = GetBreakpointLocation(item);
5298     Process *process = GetProcess();
5299     StreamString stream;
5300     stream.Printf("%i.%i: ", breakpoint_location->GetBreakpoint().GetID(),
5301                   breakpoint_location->GetID());
5302     Address address = breakpoint_location->GetAddress();
5303     address.Dump(&stream, process, Address::DumpStyleResolvedDescription,
5304                  Address::DumpStyleInvalid);
5305     window.PutCStringTruncated(1, stream.GetString().str().c_str());
5306   }
5307 
5308   StringList ComputeDetailsList(BreakpointLocationSP breakpoint_location) {
5309     StringList details;
5310 
5311     Address address = breakpoint_location->GetAddress();
5312     SymbolContext symbol_context;
5313     address.CalculateSymbolContext(&symbol_context);
5314 
5315     if (symbol_context.module_sp) {
5316       StreamString module_stream;
5317       module_stream.PutCString("module = ");
5318       symbol_context.module_sp->GetFileSpec().Dump(
5319           module_stream.AsRawOstream());
5320       details.AppendString(module_stream.GetString());
5321     }
5322 
5323     if (symbol_context.comp_unit != nullptr) {
5324       StreamString compile_unit_stream;
5325       compile_unit_stream.PutCString("compile unit = ");
5326       symbol_context.comp_unit->GetPrimaryFile().GetFilename().Dump(
5327           &compile_unit_stream);
5328       details.AppendString(compile_unit_stream.GetString());
5329 
5330       if (symbol_context.function != nullptr) {
5331         StreamString function_stream;
5332         function_stream.PutCString("function = ");
5333         function_stream.PutCString(
5334             symbol_context.function->GetName().AsCString("<unknown>"));
5335         details.AppendString(function_stream.GetString());
5336       }
5337 
5338       if (symbol_context.line_entry.line > 0) {
5339         StreamString location_stream;
5340         location_stream.PutCString("location = ");
5341         symbol_context.line_entry.DumpStopContext(&location_stream, true);
5342         details.AppendString(location_stream.GetString());
5343       }
5344 
5345     } else {
5346       if (symbol_context.symbol) {
5347         StreamString symbol_stream;
5348         if (breakpoint_location->IsReExported())
5349           symbol_stream.PutCString("re-exported target = ");
5350         else
5351           symbol_stream.PutCString("symbol = ");
5352         symbol_stream.PutCString(
5353             symbol_context.symbol->GetName().AsCString("<unknown>"));
5354         details.AppendString(symbol_stream.GetString());
5355       }
5356     }
5357 
5358     Process *process = GetProcess();
5359 
5360     StreamString address_stream;
5361     address.Dump(&address_stream, process, Address::DumpStyleLoadAddress,
5362                  Address::DumpStyleModuleWithFileAddress);
5363     details.AppendString(address_stream.GetString());
5364 
5365     BreakpointSiteSP breakpoint_site = breakpoint_location->GetBreakpointSite();
5366     if (breakpoint_location->IsIndirect() && breakpoint_site) {
5367       Address resolved_address;
5368       resolved_address.SetLoadAddress(breakpoint_site->GetLoadAddress(),
5369                                       &breakpoint_location->GetTarget());
5370       Symbol *resolved_symbol = resolved_address.CalculateSymbolContextSymbol();
5371       if (resolved_symbol) {
5372         StreamString indirect_target_stream;
5373         indirect_target_stream.PutCString("indirect target = ");
5374         indirect_target_stream.PutCString(
5375             resolved_symbol->GetName().GetCString());
5376         details.AppendString(indirect_target_stream.GetString());
5377       }
5378     }
5379 
5380     bool is_resolved = breakpoint_location->IsResolved();
5381     StreamString resolved_stream;
5382     resolved_stream.Printf("resolved = %s", is_resolved ? "true" : "false");
5383     details.AppendString(resolved_stream.GetString());
5384 
5385     bool is_hardware = is_resolved && breakpoint_site->IsHardware();
5386     StreamString hardware_stream;
5387     hardware_stream.Printf("hardware = %s", is_hardware ? "true" : "false");
5388     details.AppendString(hardware_stream.GetString());
5389 
5390     StreamString hit_count_stream;
5391     hit_count_stream.Printf("hit count = %-4u",
5392                             breakpoint_location->GetHitCount());
5393     details.AppendString(hit_count_stream.GetString());
5394 
5395     return details;
5396   }
5397 
5398   void TreeDelegateGenerateChildren(TreeItem &item) override {
5399     BreakpointLocationSP breakpoint_location = GetBreakpointLocation(item);
5400     StringList details = ComputeDetailsList(breakpoint_location);
5401 
5402     if (!m_string_delegate_sp)
5403       m_string_delegate_sp = std::make_shared<TextTreeDelegate>();
5404     TreeItem details_tree_item(&item, *m_string_delegate_sp, false);
5405 
5406     item.Resize(details.GetSize(), details_tree_item);
5407     for (size_t i = 0; i < details.GetSize(); i++) {
5408       item[i].SetText(details.GetStringAtIndex(i));
5409     }
5410   }
5411 
5412   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5413 
5414 protected:
5415   Debugger &m_debugger;
5416   std::shared_ptr<TextTreeDelegate> m_string_delegate_sp;
5417 };
5418 
5419 class BreakpointTreeDelegate : public TreeDelegate {
5420 public:
5421   BreakpointTreeDelegate(Debugger &debugger)
5422       : TreeDelegate(), m_debugger(debugger),
5423         m_breakpoint_location_delegate_sp() {}
5424 
5425   ~BreakpointTreeDelegate() override = default;
5426 
5427   BreakpointSP GetBreakpoint(const TreeItem &item) {
5428     TargetSP target = m_debugger.GetSelectedTarget();
5429     BreakpointList &breakpoints = target->GetBreakpointList(false);
5430     return breakpoints.GetBreakpointAtIndex(item.GetIdentifier());
5431   }
5432 
5433   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5434     BreakpointSP breakpoint = GetBreakpoint(item);
5435     StreamString stream;
5436     stream.Format("{0}: ", breakpoint->GetID());
5437     breakpoint->GetResolverDescription(&stream);
5438     breakpoint->GetFilterDescription(&stream);
5439     window.PutCStringTruncated(1, stream.GetString().str().c_str());
5440   }
5441 
5442   void TreeDelegateGenerateChildren(TreeItem &item) override {
5443     BreakpointSP breakpoint = GetBreakpoint(item);
5444 
5445     if (!m_breakpoint_location_delegate_sp)
5446       m_breakpoint_location_delegate_sp =
5447           std::make_shared<BreakpointLocationTreeDelegate>(m_debugger);
5448     TreeItem breakpoint_location_tree_item(
5449         &item, *m_breakpoint_location_delegate_sp, true);
5450 
5451     item.Resize(breakpoint->GetNumLocations(), breakpoint_location_tree_item);
5452     for (size_t i = 0; i < breakpoint->GetNumLocations(); i++) {
5453       item[i].SetIdentifier(i);
5454       item[i].SetUserData(breakpoint.get());
5455     }
5456   }
5457 
5458   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5459 
5460 protected:
5461   Debugger &m_debugger;
5462   std::shared_ptr<BreakpointLocationTreeDelegate>
5463       m_breakpoint_location_delegate_sp;
5464 };
5465 
5466 class BreakpointsTreeDelegate : public TreeDelegate {
5467 public:
5468   BreakpointsTreeDelegate(Debugger &debugger)
5469       : TreeDelegate(), m_debugger(debugger), m_breakpoint_delegate_sp() {}
5470 
5471   ~BreakpointsTreeDelegate() override = default;
5472 
5473   bool TreeDelegateShouldDraw() override {
5474     TargetSP target = m_debugger.GetSelectedTarget();
5475     if (!target)
5476       return false;
5477 
5478     return true;
5479   }
5480 
5481   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5482     window.PutCString("Breakpoints");
5483   }
5484 
5485   void TreeDelegateGenerateChildren(TreeItem &item) override {
5486     TargetSP target = m_debugger.GetSelectedTarget();
5487 
5488     BreakpointList &breakpoints = target->GetBreakpointList(false);
5489     std::unique_lock<std::recursive_mutex> lock;
5490     breakpoints.GetListMutex(lock);
5491 
5492     if (!m_breakpoint_delegate_sp)
5493       m_breakpoint_delegate_sp =
5494           std::make_shared<BreakpointTreeDelegate>(m_debugger);
5495     TreeItem breakpoint_tree_item(&item, *m_breakpoint_delegate_sp, true);
5496 
5497     item.Resize(breakpoints.GetSize(), breakpoint_tree_item);
5498     for (size_t i = 0; i < breakpoints.GetSize(); i++) {
5499       item[i].SetIdentifier(i);
5500     }
5501   }
5502 
5503   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5504 
5505   bool TreeDelegateExpandRootByDefault() override { return true; }
5506 
5507 protected:
5508   Debugger &m_debugger;
5509   std::shared_ptr<BreakpointTreeDelegate> m_breakpoint_delegate_sp;
5510 };
5511 
5512 class ValueObjectListDelegate : public WindowDelegate {
5513 public:
5514   ValueObjectListDelegate() : m_rows() {}
5515 
5516   ValueObjectListDelegate(ValueObjectList &valobj_list) : m_rows() {
5517     SetValues(valobj_list);
5518   }
5519 
5520   ~ValueObjectListDelegate() override = default;
5521 
5522   void SetValues(ValueObjectList &valobj_list) {
5523     m_selected_row = nullptr;
5524     m_selected_row_idx = 0;
5525     m_first_visible_row = 0;
5526     m_num_rows = 0;
5527     m_rows.clear();
5528     for (auto &valobj_sp : valobj_list.GetObjects())
5529       m_rows.push_back(Row(valobj_sp, nullptr));
5530   }
5531 
5532   bool WindowDelegateDraw(Window &window, bool force) override {
5533     m_num_rows = 0;
5534     m_min_x = 2;
5535     m_min_y = 1;
5536     m_max_x = window.GetWidth() - 1;
5537     m_max_y = window.GetHeight() - 1;
5538 
5539     window.Erase();
5540     window.DrawTitleBox(window.GetName());
5541 
5542     const int num_visible_rows = NumVisibleRows();
5543     const int num_rows = CalculateTotalNumberRows(m_rows);
5544 
5545     // If we unexpanded while having something selected our total number of
5546     // rows is less than the num visible rows, then make sure we show all the
5547     // rows by setting the first visible row accordingly.
5548     if (m_first_visible_row > 0 && num_rows < num_visible_rows)
5549       m_first_visible_row = 0;
5550 
5551     // Make sure the selected row is always visible
5552     if (m_selected_row_idx < m_first_visible_row)
5553       m_first_visible_row = m_selected_row_idx;
5554     else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
5555       m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
5556 
5557     DisplayRows(window, m_rows, g_options);
5558 
5559     // Get the selected row
5560     m_selected_row = GetRowForRowIndex(m_selected_row_idx);
5561     // Keep the cursor on the selected row so the highlight and the cursor are
5562     // always on the same line
5563     if (m_selected_row)
5564       window.MoveCursor(m_selected_row->x, m_selected_row->y);
5565 
5566     return true; // Drawing handled
5567   }
5568 
5569   KeyHelp *WindowDelegateGetKeyHelp() override {
5570     static curses::KeyHelp g_source_view_key_help[] = {
5571         {KEY_UP, "Select previous item"},
5572         {KEY_DOWN, "Select next item"},
5573         {KEY_RIGHT, "Expand selected item"},
5574         {KEY_LEFT, "Unexpand selected item or select parent if not expanded"},
5575         {KEY_PPAGE, "Page up"},
5576         {KEY_NPAGE, "Page down"},
5577         {'A', "Format as annotated address"},
5578         {'b', "Format as binary"},
5579         {'B', "Format as hex bytes with ASCII"},
5580         {'c', "Format as character"},
5581         {'d', "Format as a signed integer"},
5582         {'D', "Format selected value using the default format for the type"},
5583         {'f', "Format as float"},
5584         {'h', "Show help dialog"},
5585         {'i', "Format as instructions"},
5586         {'o', "Format as octal"},
5587         {'p', "Format as pointer"},
5588         {'s', "Format as C string"},
5589         {'t', "Toggle showing/hiding type names"},
5590         {'u', "Format as an unsigned integer"},
5591         {'x', "Format as hex"},
5592         {'X', "Format as uppercase hex"},
5593         {' ', "Toggle item expansion"},
5594         {',', "Page up"},
5595         {'.', "Page down"},
5596         {'\0', nullptr}};
5597     return g_source_view_key_help;
5598   }
5599 
5600   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
5601     switch (c) {
5602     case 'x':
5603     case 'X':
5604     case 'o':
5605     case 's':
5606     case 'u':
5607     case 'd':
5608     case 'D':
5609     case 'i':
5610     case 'A':
5611     case 'p':
5612     case 'c':
5613     case 'b':
5614     case 'B':
5615     case 'f':
5616       // Change the format for the currently selected item
5617       if (m_selected_row) {
5618         auto valobj_sp = m_selected_row->value.GetSP();
5619         if (valobj_sp)
5620           valobj_sp->SetFormat(FormatForChar(c));
5621       }
5622       return eKeyHandled;
5623 
5624     case 't':
5625       // Toggle showing type names
5626       g_options.show_types = !g_options.show_types;
5627       return eKeyHandled;
5628 
5629     case ',':
5630     case KEY_PPAGE:
5631       // Page up key
5632       if (m_first_visible_row > 0) {
5633         if (static_cast<int>(m_first_visible_row) > m_max_y)
5634           m_first_visible_row -= m_max_y;
5635         else
5636           m_first_visible_row = 0;
5637         m_selected_row_idx = m_first_visible_row;
5638       }
5639       return eKeyHandled;
5640 
5641     case '.':
5642     case KEY_NPAGE:
5643       // Page down key
5644       if (m_num_rows > static_cast<size_t>(m_max_y)) {
5645         if (m_first_visible_row + m_max_y < m_num_rows) {
5646           m_first_visible_row += m_max_y;
5647           m_selected_row_idx = m_first_visible_row;
5648         }
5649       }
5650       return eKeyHandled;
5651 
5652     case KEY_UP:
5653       if (m_selected_row_idx > 0)
5654         --m_selected_row_idx;
5655       return eKeyHandled;
5656 
5657     case KEY_DOWN:
5658       if (m_selected_row_idx + 1 < m_num_rows)
5659         ++m_selected_row_idx;
5660       return eKeyHandled;
5661 
5662     case KEY_RIGHT:
5663       if (m_selected_row) {
5664         if (!m_selected_row->expanded)
5665           m_selected_row->Expand();
5666       }
5667       return eKeyHandled;
5668 
5669     case KEY_LEFT:
5670       if (m_selected_row) {
5671         if (m_selected_row->expanded)
5672           m_selected_row->Unexpand();
5673         else if (m_selected_row->parent)
5674           m_selected_row_idx = m_selected_row->parent->row_idx;
5675       }
5676       return eKeyHandled;
5677 
5678     case ' ':
5679       // Toggle expansion state when SPACE is pressed
5680       if (m_selected_row) {
5681         if (m_selected_row->expanded)
5682           m_selected_row->Unexpand();
5683         else
5684           m_selected_row->Expand();
5685       }
5686       return eKeyHandled;
5687 
5688     case 'h':
5689       window.CreateHelpSubwindow();
5690       return eKeyHandled;
5691 
5692     default:
5693       break;
5694     }
5695     return eKeyNotHandled;
5696   }
5697 
5698 protected:
5699   std::vector<Row> m_rows;
5700   Row *m_selected_row = nullptr;
5701   uint32_t m_selected_row_idx = 0;
5702   uint32_t m_first_visible_row = 0;
5703   uint32_t m_num_rows = 0;
5704   int m_min_x;
5705   int m_min_y;
5706   int m_max_x = 0;
5707   int m_max_y = 0;
5708 
5709   static Format FormatForChar(int c) {
5710     switch (c) {
5711     case 'x':
5712       return eFormatHex;
5713     case 'X':
5714       return eFormatHexUppercase;
5715     case 'o':
5716       return eFormatOctal;
5717     case 's':
5718       return eFormatCString;
5719     case 'u':
5720       return eFormatUnsigned;
5721     case 'd':
5722       return eFormatDecimal;
5723     case 'D':
5724       return eFormatDefault;
5725     case 'i':
5726       return eFormatInstruction;
5727     case 'A':
5728       return eFormatAddressInfo;
5729     case 'p':
5730       return eFormatPointer;
5731     case 'c':
5732       return eFormatChar;
5733     case 'b':
5734       return eFormatBinary;
5735     case 'B':
5736       return eFormatBytesWithASCII;
5737     case 'f':
5738       return eFormatFloat;
5739     }
5740     return eFormatDefault;
5741   }
5742 
5743   bool DisplayRowObject(Window &window, Row &row, DisplayOptions &options,
5744                         bool highlight, bool last_child) {
5745     ValueObject *valobj = row.value.GetSP().get();
5746 
5747     if (valobj == nullptr)
5748       return false;
5749 
5750     const char *type_name =
5751         options.show_types ? valobj->GetTypeName().GetCString() : nullptr;
5752     const char *name = valobj->GetName().GetCString();
5753     const char *value = valobj->GetValueAsCString();
5754     const char *summary = valobj->GetSummaryAsCString();
5755 
5756     window.MoveCursor(row.x, row.y);
5757 
5758     row.DrawTree(window);
5759 
5760     if (highlight)
5761       window.AttributeOn(A_REVERSE);
5762 
5763     if (type_name && type_name[0])
5764       window.PrintfTruncated(1, "(%s) ", type_name);
5765 
5766     if (name && name[0])
5767       window.PutCStringTruncated(1, name);
5768 
5769     attr_t changd_attr = 0;
5770     if (valobj->GetValueDidChange())
5771       changd_attr = COLOR_PAIR(RedOnBlack) | A_BOLD;
5772 
5773     if (value && value[0]) {
5774       window.PutCStringTruncated(1, " = ");
5775       if (changd_attr)
5776         window.AttributeOn(changd_attr);
5777       window.PutCStringTruncated(1, value);
5778       if (changd_attr)
5779         window.AttributeOff(changd_attr);
5780     }
5781 
5782     if (summary && summary[0]) {
5783       window.PutCStringTruncated(1, " ");
5784       if (changd_attr)
5785         window.AttributeOn(changd_attr);
5786       window.PutCStringTruncated(1, summary);
5787       if (changd_attr)
5788         window.AttributeOff(changd_attr);
5789     }
5790 
5791     if (highlight)
5792       window.AttributeOff(A_REVERSE);
5793 
5794     return true;
5795   }
5796 
5797   void DisplayRows(Window &window, std::vector<Row> &rows,
5798                    DisplayOptions &options) {
5799     // >   0x25B7
5800     // \/  0x25BD
5801 
5802     bool window_is_active = window.IsActive();
5803     for (auto &row : rows) {
5804       const bool last_child = row.parent && &rows[rows.size() - 1] == &row;
5805       // Save the row index in each Row structure
5806       row.row_idx = m_num_rows;
5807       if ((m_num_rows >= m_first_visible_row) &&
5808           ((m_num_rows - m_first_visible_row) <
5809            static_cast<size_t>(NumVisibleRows()))) {
5810         row.x = m_min_x;
5811         row.y = m_num_rows - m_first_visible_row + 1;
5812         if (DisplayRowObject(window, row, options,
5813                              window_is_active &&
5814                                  m_num_rows == m_selected_row_idx,
5815                              last_child)) {
5816           ++m_num_rows;
5817         } else {
5818           row.x = 0;
5819           row.y = 0;
5820         }
5821       } else {
5822         row.x = 0;
5823         row.y = 0;
5824         ++m_num_rows;
5825       }
5826 
5827       auto &children = row.GetChildren();
5828       if (row.expanded && !children.empty()) {
5829         DisplayRows(window, children, options);
5830       }
5831     }
5832   }
5833 
5834   int CalculateTotalNumberRows(std::vector<Row> &rows) {
5835     int row_count = 0;
5836     for (auto &row : rows) {
5837       ++row_count;
5838       if (row.expanded)
5839         row_count += CalculateTotalNumberRows(row.GetChildren());
5840     }
5841     return row_count;
5842   }
5843 
5844   static Row *GetRowForRowIndexImpl(std::vector<Row> &rows, size_t &row_index) {
5845     for (auto &row : rows) {
5846       if (row_index == 0)
5847         return &row;
5848       else {
5849         --row_index;
5850         auto &children = row.GetChildren();
5851         if (row.expanded && !children.empty()) {
5852           Row *result = GetRowForRowIndexImpl(children, row_index);
5853           if (result)
5854             return result;
5855         }
5856       }
5857     }
5858     return nullptr;
5859   }
5860 
5861   Row *GetRowForRowIndex(size_t row_index) {
5862     return GetRowForRowIndexImpl(m_rows, row_index);
5863   }
5864 
5865   int NumVisibleRows() const { return m_max_y - m_min_y; }
5866 
5867   static DisplayOptions g_options;
5868 };
5869 
5870 class FrameVariablesWindowDelegate : public ValueObjectListDelegate {
5871 public:
5872   FrameVariablesWindowDelegate(Debugger &debugger)
5873       : ValueObjectListDelegate(), m_debugger(debugger) {}
5874 
5875   ~FrameVariablesWindowDelegate() override = default;
5876 
5877   const char *WindowDelegateGetHelpText() override {
5878     return "Frame variable window keyboard shortcuts:";
5879   }
5880 
5881   bool WindowDelegateDraw(Window &window, bool force) override {
5882     ExecutionContext exe_ctx(
5883         m_debugger.GetCommandInterpreter().GetExecutionContext());
5884     Process *process = exe_ctx.GetProcessPtr();
5885     Block *frame_block = nullptr;
5886     StackFrame *frame = nullptr;
5887 
5888     if (process) {
5889       StateType state = process->GetState();
5890       if (StateIsStoppedState(state, true)) {
5891         frame = exe_ctx.GetFramePtr();
5892         if (frame)
5893           frame_block = frame->GetFrameBlock();
5894       } else if (StateIsRunningState(state)) {
5895         return true; // Don't do any updating when we are running
5896       }
5897     }
5898 
5899     ValueObjectList local_values;
5900     if (frame_block) {
5901       // Only update the variables if they have changed
5902       if (m_frame_block != frame_block) {
5903         m_frame_block = frame_block;
5904 
5905         VariableList *locals = frame->GetVariableList(true);
5906         if (locals) {
5907           const DynamicValueType use_dynamic = eDynamicDontRunTarget;
5908           for (const VariableSP &local_sp : *locals) {
5909             ValueObjectSP value_sp =
5910                 frame->GetValueObjectForFrameVariable(local_sp, use_dynamic);
5911             if (value_sp) {
5912               ValueObjectSP synthetic_value_sp = value_sp->GetSyntheticValue();
5913               if (synthetic_value_sp)
5914                 local_values.Append(synthetic_value_sp);
5915               else
5916                 local_values.Append(value_sp);
5917             }
5918           }
5919           // Update the values
5920           SetValues(local_values);
5921         }
5922       }
5923     } else {
5924       m_frame_block = nullptr;
5925       // Update the values with an empty list if there is no frame
5926       SetValues(local_values);
5927     }
5928 
5929     return ValueObjectListDelegate::WindowDelegateDraw(window, force);
5930   }
5931 
5932 protected:
5933   Debugger &m_debugger;
5934   Block *m_frame_block = nullptr;
5935 };
5936 
5937 class RegistersWindowDelegate : public ValueObjectListDelegate {
5938 public:
5939   RegistersWindowDelegate(Debugger &debugger)
5940       : ValueObjectListDelegate(), m_debugger(debugger) {}
5941 
5942   ~RegistersWindowDelegate() override = default;
5943 
5944   const char *WindowDelegateGetHelpText() override {
5945     return "Register window keyboard shortcuts:";
5946   }
5947 
5948   bool WindowDelegateDraw(Window &window, bool force) override {
5949     ExecutionContext exe_ctx(
5950         m_debugger.GetCommandInterpreter().GetExecutionContext());
5951     StackFrame *frame = exe_ctx.GetFramePtr();
5952 
5953     ValueObjectList value_list;
5954     if (frame) {
5955       if (frame->GetStackID() != m_stack_id) {
5956         m_stack_id = frame->GetStackID();
5957         RegisterContextSP reg_ctx(frame->GetRegisterContext());
5958         if (reg_ctx) {
5959           const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
5960           for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) {
5961             value_list.Append(
5962                 ValueObjectRegisterSet::Create(frame, reg_ctx, set_idx));
5963           }
5964         }
5965         SetValues(value_list);
5966       }
5967     } else {
5968       Process *process = exe_ctx.GetProcessPtr();
5969       if (process && process->IsAlive())
5970         return true; // Don't do any updating if we are running
5971       else {
5972         // Update the values with an empty list if there is no process or the
5973         // process isn't alive anymore
5974         SetValues(value_list);
5975       }
5976     }
5977     return ValueObjectListDelegate::WindowDelegateDraw(window, force);
5978   }
5979 
5980 protected:
5981   Debugger &m_debugger;
5982   StackID m_stack_id;
5983 };
5984 
5985 static const char *CursesKeyToCString(int ch) {
5986   static char g_desc[32];
5987   if (ch >= KEY_F0 && ch < KEY_F0 + 64) {
5988     snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0);
5989     return g_desc;
5990   }
5991   switch (ch) {
5992   case KEY_DOWN:
5993     return "down";
5994   case KEY_UP:
5995     return "up";
5996   case KEY_LEFT:
5997     return "left";
5998   case KEY_RIGHT:
5999     return "right";
6000   case KEY_HOME:
6001     return "home";
6002   case KEY_BACKSPACE:
6003     return "backspace";
6004   case KEY_DL:
6005     return "delete-line";
6006   case KEY_IL:
6007     return "insert-line";
6008   case KEY_DC:
6009     return "delete-char";
6010   case KEY_IC:
6011     return "insert-char";
6012   case KEY_CLEAR:
6013     return "clear";
6014   case KEY_EOS:
6015     return "clear-to-eos";
6016   case KEY_EOL:
6017     return "clear-to-eol";
6018   case KEY_SF:
6019     return "scroll-forward";
6020   case KEY_SR:
6021     return "scroll-backward";
6022   case KEY_NPAGE:
6023     return "page-down";
6024   case KEY_PPAGE:
6025     return "page-up";
6026   case KEY_STAB:
6027     return "set-tab";
6028   case KEY_CTAB:
6029     return "clear-tab";
6030   case KEY_CATAB:
6031     return "clear-all-tabs";
6032   case KEY_ENTER:
6033     return "enter";
6034   case KEY_PRINT:
6035     return "print";
6036   case KEY_LL:
6037     return "lower-left key";
6038   case KEY_A1:
6039     return "upper left of keypad";
6040   case KEY_A3:
6041     return "upper right of keypad";
6042   case KEY_B2:
6043     return "center of keypad";
6044   case KEY_C1:
6045     return "lower left of keypad";
6046   case KEY_C3:
6047     return "lower right of keypad";
6048   case KEY_BTAB:
6049     return "back-tab key";
6050   case KEY_BEG:
6051     return "begin key";
6052   case KEY_CANCEL:
6053     return "cancel key";
6054   case KEY_CLOSE:
6055     return "close key";
6056   case KEY_COMMAND:
6057     return "command key";
6058   case KEY_COPY:
6059     return "copy key";
6060   case KEY_CREATE:
6061     return "create key";
6062   case KEY_END:
6063     return "end key";
6064   case KEY_EXIT:
6065     return "exit key";
6066   case KEY_FIND:
6067     return "find key";
6068   case KEY_HELP:
6069     return "help key";
6070   case KEY_MARK:
6071     return "mark key";
6072   case KEY_MESSAGE:
6073     return "message key";
6074   case KEY_MOVE:
6075     return "move key";
6076   case KEY_NEXT:
6077     return "next key";
6078   case KEY_OPEN:
6079     return "open key";
6080   case KEY_OPTIONS:
6081     return "options key";
6082   case KEY_PREVIOUS:
6083     return "previous key";
6084   case KEY_REDO:
6085     return "redo key";
6086   case KEY_REFERENCE:
6087     return "reference key";
6088   case KEY_REFRESH:
6089     return "refresh key";
6090   case KEY_REPLACE:
6091     return "replace key";
6092   case KEY_RESTART:
6093     return "restart key";
6094   case KEY_RESUME:
6095     return "resume key";
6096   case KEY_SAVE:
6097     return "save key";
6098   case KEY_SBEG:
6099     return "shifted begin key";
6100   case KEY_SCANCEL:
6101     return "shifted cancel key";
6102   case KEY_SCOMMAND:
6103     return "shifted command key";
6104   case KEY_SCOPY:
6105     return "shifted copy key";
6106   case KEY_SCREATE:
6107     return "shifted create key";
6108   case KEY_SDC:
6109     return "shifted delete-character key";
6110   case KEY_SDL:
6111     return "shifted delete-line key";
6112   case KEY_SELECT:
6113     return "select key";
6114   case KEY_SEND:
6115     return "shifted end key";
6116   case KEY_SEOL:
6117     return "shifted clear-to-end-of-line key";
6118   case KEY_SEXIT:
6119     return "shifted exit key";
6120   case KEY_SFIND:
6121     return "shifted find key";
6122   case KEY_SHELP:
6123     return "shifted help key";
6124   case KEY_SHOME:
6125     return "shifted home key";
6126   case KEY_SIC:
6127     return "shifted insert-character key";
6128   case KEY_SLEFT:
6129     return "shifted left-arrow key";
6130   case KEY_SMESSAGE:
6131     return "shifted message key";
6132   case KEY_SMOVE:
6133     return "shifted move key";
6134   case KEY_SNEXT:
6135     return "shifted next key";
6136   case KEY_SOPTIONS:
6137     return "shifted options key";
6138   case KEY_SPREVIOUS:
6139     return "shifted previous key";
6140   case KEY_SPRINT:
6141     return "shifted print key";
6142   case KEY_SREDO:
6143     return "shifted redo key";
6144   case KEY_SREPLACE:
6145     return "shifted replace key";
6146   case KEY_SRIGHT:
6147     return "shifted right-arrow key";
6148   case KEY_SRSUME:
6149     return "shifted resume key";
6150   case KEY_SSAVE:
6151     return "shifted save key";
6152   case KEY_SSUSPEND:
6153     return "shifted suspend key";
6154   case KEY_SUNDO:
6155     return "shifted undo key";
6156   case KEY_SUSPEND:
6157     return "suspend key";
6158   case KEY_UNDO:
6159     return "undo key";
6160   case KEY_MOUSE:
6161     return "Mouse event has occurred";
6162   case KEY_RESIZE:
6163     return "Terminal resize event";
6164 #ifdef KEY_EVENT
6165   case KEY_EVENT:
6166     return "We were interrupted by an event";
6167 #endif
6168   case KEY_RETURN:
6169     return "return";
6170   case ' ':
6171     return "space";
6172   case '\t':
6173     return "tab";
6174   case KEY_ESCAPE:
6175     return "escape";
6176   default:
6177     if (llvm::isPrint(ch))
6178       snprintf(g_desc, sizeof(g_desc), "%c", ch);
6179     else
6180       snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch);
6181     return g_desc;
6182   }
6183   return nullptr;
6184 }
6185 
6186 HelpDialogDelegate::HelpDialogDelegate(const char *text,
6187                                        KeyHelp *key_help_array)
6188     : m_text() {
6189   if (text && text[0]) {
6190     m_text.SplitIntoLines(text);
6191     m_text.AppendString("");
6192   }
6193   if (key_help_array) {
6194     for (KeyHelp *key = key_help_array; key->ch; ++key) {
6195       StreamString key_description;
6196       key_description.Printf("%10s - %s", CursesKeyToCString(key->ch),
6197                              key->description);
6198       m_text.AppendString(key_description.GetString());
6199     }
6200   }
6201 }
6202 
6203 HelpDialogDelegate::~HelpDialogDelegate() = default;
6204 
6205 bool HelpDialogDelegate::WindowDelegateDraw(Window &window, bool force) {
6206   window.Erase();
6207   const int window_height = window.GetHeight();
6208   int x = 2;
6209   int y = 1;
6210   const int min_y = y;
6211   const int max_y = window_height - 1 - y;
6212   const size_t num_visible_lines = max_y - min_y + 1;
6213   const size_t num_lines = m_text.GetSize();
6214   const char *bottom_message;
6215   if (num_lines <= num_visible_lines)
6216     bottom_message = "Press any key to exit";
6217   else
6218     bottom_message = "Use arrows to scroll, any other key to exit";
6219   window.DrawTitleBox(window.GetName(), bottom_message);
6220   while (y <= max_y) {
6221     window.MoveCursor(x, y);
6222     window.PutCStringTruncated(
6223         1, m_text.GetStringAtIndex(m_first_visible_line + y - min_y));
6224     ++y;
6225   }
6226   return true;
6227 }
6228 
6229 HandleCharResult HelpDialogDelegate::WindowDelegateHandleChar(Window &window,
6230                                                               int key) {
6231   bool done = false;
6232   const size_t num_lines = m_text.GetSize();
6233   const size_t num_visible_lines = window.GetHeight() - 2;
6234 
6235   if (num_lines <= num_visible_lines) {
6236     done = true;
6237     // If we have all lines visible and don't need scrolling, then any key
6238     // press will cause us to exit
6239   } else {
6240     switch (key) {
6241     case KEY_UP:
6242       if (m_first_visible_line > 0)
6243         --m_first_visible_line;
6244       break;
6245 
6246     case KEY_DOWN:
6247       if (m_first_visible_line + num_visible_lines < num_lines)
6248         ++m_first_visible_line;
6249       break;
6250 
6251     case KEY_PPAGE:
6252     case ',':
6253       if (m_first_visible_line > 0) {
6254         if (static_cast<size_t>(m_first_visible_line) >= num_visible_lines)
6255           m_first_visible_line -= num_visible_lines;
6256         else
6257           m_first_visible_line = 0;
6258       }
6259       break;
6260 
6261     case KEY_NPAGE:
6262     case '.':
6263       if (m_first_visible_line + num_visible_lines < num_lines) {
6264         m_first_visible_line += num_visible_lines;
6265         if (static_cast<size_t>(m_first_visible_line) > num_lines)
6266           m_first_visible_line = num_lines - num_visible_lines;
6267       }
6268       break;
6269 
6270     default:
6271       done = true;
6272       break;
6273     }
6274   }
6275   if (done)
6276     window.GetParent()->RemoveSubWindow(&window);
6277   return eKeyHandled;
6278 }
6279 
6280 class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
6281 public:
6282   enum {
6283     eMenuID_LLDB = 1,
6284     eMenuID_LLDBAbout,
6285     eMenuID_LLDBExit,
6286 
6287     eMenuID_Target,
6288     eMenuID_TargetCreate,
6289     eMenuID_TargetDelete,
6290 
6291     eMenuID_Process,
6292     eMenuID_ProcessAttach,
6293     eMenuID_ProcessDetachResume,
6294     eMenuID_ProcessDetachSuspended,
6295     eMenuID_ProcessLaunch,
6296     eMenuID_ProcessContinue,
6297     eMenuID_ProcessHalt,
6298     eMenuID_ProcessKill,
6299 
6300     eMenuID_Thread,
6301     eMenuID_ThreadStepIn,
6302     eMenuID_ThreadStepOver,
6303     eMenuID_ThreadStepOut,
6304 
6305     eMenuID_View,
6306     eMenuID_ViewBacktrace,
6307     eMenuID_ViewRegisters,
6308     eMenuID_ViewSource,
6309     eMenuID_ViewVariables,
6310     eMenuID_ViewBreakpoints,
6311 
6312     eMenuID_Help,
6313     eMenuID_HelpGUIHelp
6314   };
6315 
6316   ApplicationDelegate(Application &app, Debugger &debugger)
6317       : WindowDelegate(), MenuDelegate(), m_app(app), m_debugger(debugger) {}
6318 
6319   ~ApplicationDelegate() override = default;
6320 
6321   bool WindowDelegateDraw(Window &window, bool force) override {
6322     return false; // Drawing not handled, let standard window drawing happen
6323   }
6324 
6325   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
6326     switch (key) {
6327     case '\t':
6328       window.SelectNextWindowAsActive();
6329       return eKeyHandled;
6330 
6331     case KEY_SHIFT_TAB:
6332       window.SelectPreviousWindowAsActive();
6333       return eKeyHandled;
6334 
6335     case 'h':
6336       window.CreateHelpSubwindow();
6337       return eKeyHandled;
6338 
6339     case KEY_ESCAPE:
6340       return eQuitApplication;
6341 
6342     default:
6343       break;
6344     }
6345     return eKeyNotHandled;
6346   }
6347 
6348   const char *WindowDelegateGetHelpText() override {
6349     return "Welcome to the LLDB curses GUI.\n\n"
6350            "Press the TAB key to change the selected view.\n"
6351            "Each view has its own keyboard shortcuts, press 'h' to open a "
6352            "dialog to display them.\n\n"
6353            "Common key bindings for all views:";
6354   }
6355 
6356   KeyHelp *WindowDelegateGetKeyHelp() override {
6357     static curses::KeyHelp g_source_view_key_help[] = {
6358         {'\t', "Select next view"},
6359         {KEY_BTAB, "Select previous view"},
6360         {'h', "Show help dialog with view specific key bindings"},
6361         {',', "Page up"},
6362         {'.', "Page down"},
6363         {KEY_UP, "Select previous"},
6364         {KEY_DOWN, "Select next"},
6365         {KEY_LEFT, "Unexpand or select parent"},
6366         {KEY_RIGHT, "Expand"},
6367         {KEY_PPAGE, "Page up"},
6368         {KEY_NPAGE, "Page down"},
6369         {'\0', nullptr}};
6370     return g_source_view_key_help;
6371   }
6372 
6373   MenuActionResult MenuDelegateAction(Menu &menu) override {
6374     switch (menu.GetIdentifier()) {
6375     case eMenuID_TargetCreate: {
6376       WindowSP main_window_sp = m_app.GetMainWindow();
6377       FormDelegateSP form_delegate_sp =
6378           FormDelegateSP(new TargetCreateFormDelegate(m_debugger));
6379       Rect bounds = main_window_sp->GetCenteredRect(80, 19);
6380       WindowSP form_window_sp = main_window_sp->CreateSubWindow(
6381           form_delegate_sp->GetName().c_str(), bounds, true);
6382       WindowDelegateSP window_delegate_sp =
6383           WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
6384       form_window_sp->SetDelegate(window_delegate_sp);
6385       return MenuActionResult::Handled;
6386     }
6387     case eMenuID_ThreadStepIn: {
6388       ExecutionContext exe_ctx =
6389           m_debugger.GetCommandInterpreter().GetExecutionContext();
6390       if (exe_ctx.HasThreadScope()) {
6391         Process *process = exe_ctx.GetProcessPtr();
6392         if (process && process->IsAlive() &&
6393             StateIsStoppedState(process->GetState(), true))
6394           exe_ctx.GetThreadRef().StepIn(true);
6395       }
6396     }
6397       return MenuActionResult::Handled;
6398 
6399     case eMenuID_ThreadStepOut: {
6400       ExecutionContext exe_ctx =
6401           m_debugger.GetCommandInterpreter().GetExecutionContext();
6402       if (exe_ctx.HasThreadScope()) {
6403         Process *process = exe_ctx.GetProcessPtr();
6404         if (process && process->IsAlive() &&
6405             StateIsStoppedState(process->GetState(), true)) {
6406           Thread *thread = exe_ctx.GetThreadPtr();
6407           uint32_t frame_idx = thread->GetSelectedFrameIndex();
6408           exe_ctx.GetThreadRef().StepOut(frame_idx);
6409         }
6410       }
6411     }
6412       return MenuActionResult::Handled;
6413 
6414     case eMenuID_ThreadStepOver: {
6415       ExecutionContext exe_ctx =
6416           m_debugger.GetCommandInterpreter().GetExecutionContext();
6417       if (exe_ctx.HasThreadScope()) {
6418         Process *process = exe_ctx.GetProcessPtr();
6419         if (process && process->IsAlive() &&
6420             StateIsStoppedState(process->GetState(), true))
6421           exe_ctx.GetThreadRef().StepOver(true);
6422       }
6423     }
6424       return MenuActionResult::Handled;
6425 
6426     case eMenuID_ProcessAttach: {
6427       WindowSP main_window_sp = m_app.GetMainWindow();
6428       FormDelegateSP form_delegate_sp = FormDelegateSP(
6429           new ProcessAttachFormDelegate(m_debugger, main_window_sp));
6430       Rect bounds = main_window_sp->GetCenteredRect(80, 22);
6431       WindowSP form_window_sp = main_window_sp->CreateSubWindow(
6432           form_delegate_sp->GetName().c_str(), bounds, true);
6433       WindowDelegateSP window_delegate_sp =
6434           WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
6435       form_window_sp->SetDelegate(window_delegate_sp);
6436       return MenuActionResult::Handled;
6437     }
6438     case eMenuID_ProcessLaunch: {
6439       WindowSP main_window_sp = m_app.GetMainWindow();
6440       FormDelegateSP form_delegate_sp = FormDelegateSP(
6441           new ProcessLaunchFormDelegate(m_debugger, main_window_sp));
6442       Rect bounds = main_window_sp->GetCenteredRect(80, 22);
6443       WindowSP form_window_sp = main_window_sp->CreateSubWindow(
6444           form_delegate_sp->GetName().c_str(), bounds, true);
6445       WindowDelegateSP window_delegate_sp =
6446           WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
6447       form_window_sp->SetDelegate(window_delegate_sp);
6448       return MenuActionResult::Handled;
6449     }
6450 
6451     case eMenuID_ProcessContinue: {
6452       ExecutionContext exe_ctx =
6453           m_debugger.GetCommandInterpreter().GetExecutionContext();
6454       if (exe_ctx.HasProcessScope()) {
6455         Process *process = exe_ctx.GetProcessPtr();
6456         if (process && process->IsAlive() &&
6457             StateIsStoppedState(process->GetState(), true))
6458           process->Resume();
6459       }
6460     }
6461       return MenuActionResult::Handled;
6462 
6463     case eMenuID_ProcessKill: {
6464       ExecutionContext exe_ctx =
6465           m_debugger.GetCommandInterpreter().GetExecutionContext();
6466       if (exe_ctx.HasProcessScope()) {
6467         Process *process = exe_ctx.GetProcessPtr();
6468         if (process && process->IsAlive())
6469           process->Destroy(false);
6470       }
6471     }
6472       return MenuActionResult::Handled;
6473 
6474     case eMenuID_ProcessHalt: {
6475       ExecutionContext exe_ctx =
6476           m_debugger.GetCommandInterpreter().GetExecutionContext();
6477       if (exe_ctx.HasProcessScope()) {
6478         Process *process = exe_ctx.GetProcessPtr();
6479         if (process && process->IsAlive())
6480           process->Halt();
6481       }
6482     }
6483       return MenuActionResult::Handled;
6484 
6485     case eMenuID_ProcessDetachResume:
6486     case eMenuID_ProcessDetachSuspended: {
6487       ExecutionContext exe_ctx =
6488           m_debugger.GetCommandInterpreter().GetExecutionContext();
6489       if (exe_ctx.HasProcessScope()) {
6490         Process *process = exe_ctx.GetProcessPtr();
6491         if (process && process->IsAlive())
6492           process->Detach(menu.GetIdentifier() ==
6493                           eMenuID_ProcessDetachSuspended);
6494       }
6495     }
6496       return MenuActionResult::Handled;
6497 
6498     case eMenuID_Process: {
6499       // Populate the menu with all of the threads if the process is stopped
6500       // when the Process menu gets selected and is about to display its
6501       // submenu.
6502       Menus &submenus = menu.GetSubmenus();
6503       ExecutionContext exe_ctx =
6504           m_debugger.GetCommandInterpreter().GetExecutionContext();
6505       Process *process = exe_ctx.GetProcessPtr();
6506       if (process && process->IsAlive() &&
6507           StateIsStoppedState(process->GetState(), true)) {
6508         if (submenus.size() == 7)
6509           menu.AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
6510         else if (submenus.size() > 8)
6511           submenus.erase(submenus.begin() + 8, submenus.end());
6512 
6513         ThreadList &threads = process->GetThreadList();
6514         std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
6515         size_t num_threads = threads.GetSize();
6516         for (size_t i = 0; i < num_threads; ++i) {
6517           ThreadSP thread_sp = threads.GetThreadAtIndex(i);
6518           char menu_char = '\0';
6519           if (i < 9)
6520             menu_char = '1' + i;
6521           StreamString thread_menu_title;
6522           thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID());
6523           const char *thread_name = thread_sp->GetName();
6524           if (thread_name && thread_name[0])
6525             thread_menu_title.Printf(" %s", thread_name);
6526           else {
6527             const char *queue_name = thread_sp->GetQueueName();
6528             if (queue_name && queue_name[0])
6529               thread_menu_title.Printf(" %s", queue_name);
6530           }
6531           menu.AddSubmenu(
6532               MenuSP(new Menu(thread_menu_title.GetString().str().c_str(),
6533                               nullptr, menu_char, thread_sp->GetID())));
6534         }
6535       } else if (submenus.size() > 7) {
6536         // Remove the separator and any other thread submenu items that were
6537         // previously added
6538         submenus.erase(submenus.begin() + 7, submenus.end());
6539       }
6540       // Since we are adding and removing items we need to recalculate the
6541       // name lengths
6542       menu.RecalculateNameLengths();
6543     }
6544       return MenuActionResult::Handled;
6545 
6546     case eMenuID_ViewVariables: {
6547       WindowSP main_window_sp = m_app.GetMainWindow();
6548       WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
6549       WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
6550       WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
6551       const Rect source_bounds = source_window_sp->GetBounds();
6552 
6553       if (variables_window_sp) {
6554         const Rect variables_bounds = variables_window_sp->GetBounds();
6555 
6556         main_window_sp->RemoveSubWindow(variables_window_sp.get());
6557 
6558         if (registers_window_sp) {
6559           // We have a registers window, so give all the area back to the
6560           // registers window
6561           Rect registers_bounds = variables_bounds;
6562           registers_bounds.size.width = source_bounds.size.width;
6563           registers_window_sp->SetBounds(registers_bounds);
6564         } else {
6565           // We have no registers window showing so give the bottom area back
6566           // to the source view
6567           source_window_sp->Resize(source_bounds.size.width,
6568                                    source_bounds.size.height +
6569                                        variables_bounds.size.height);
6570         }
6571       } else {
6572         Rect new_variables_rect;
6573         if (registers_window_sp) {
6574           // We have a registers window so split the area of the registers
6575           // window into two columns where the left hand side will be the
6576           // variables and the right hand side will be the registers
6577           const Rect variables_bounds = registers_window_sp->GetBounds();
6578           Rect new_registers_rect;
6579           variables_bounds.VerticalSplitPercentage(0.50, new_variables_rect,
6580                                                    new_registers_rect);
6581           registers_window_sp->SetBounds(new_registers_rect);
6582         } else {
6583           // No registers window, grab the bottom part of the source window
6584           Rect new_source_rect;
6585           source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
6586                                                   new_variables_rect);
6587           source_window_sp->SetBounds(new_source_rect);
6588         }
6589         WindowSP new_window_sp = main_window_sp->CreateSubWindow(
6590             "Variables", new_variables_rect, false);
6591         new_window_sp->SetDelegate(
6592             WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
6593       }
6594       touchwin(stdscr);
6595     }
6596       return MenuActionResult::Handled;
6597 
6598     case eMenuID_ViewRegisters: {
6599       WindowSP main_window_sp = m_app.GetMainWindow();
6600       WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
6601       WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
6602       WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
6603       const Rect source_bounds = source_window_sp->GetBounds();
6604 
6605       if (registers_window_sp) {
6606         if (variables_window_sp) {
6607           const Rect variables_bounds = variables_window_sp->GetBounds();
6608 
6609           // We have a variables window, so give all the area back to the
6610           // variables window
6611           variables_window_sp->Resize(variables_bounds.size.width +
6612                                           registers_window_sp->GetWidth(),
6613                                       variables_bounds.size.height);
6614         } else {
6615           // We have no variables window showing so give the bottom area back
6616           // to the source view
6617           source_window_sp->Resize(source_bounds.size.width,
6618                                    source_bounds.size.height +
6619                                        registers_window_sp->GetHeight());
6620         }
6621         main_window_sp->RemoveSubWindow(registers_window_sp.get());
6622       } else {
6623         Rect new_regs_rect;
6624         if (variables_window_sp) {
6625           // We have a variables window, split it into two columns where the
6626           // left hand side will be the variables and the right hand side will
6627           // be the registers
6628           const Rect variables_bounds = variables_window_sp->GetBounds();
6629           Rect new_vars_rect;
6630           variables_bounds.VerticalSplitPercentage(0.50, new_vars_rect,
6631                                                    new_regs_rect);
6632           variables_window_sp->SetBounds(new_vars_rect);
6633         } else {
6634           // No variables window, grab the bottom part of the source window
6635           Rect new_source_rect;
6636           source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
6637                                                   new_regs_rect);
6638           source_window_sp->SetBounds(new_source_rect);
6639         }
6640         WindowSP new_window_sp =
6641             main_window_sp->CreateSubWindow("Registers", new_regs_rect, false);
6642         new_window_sp->SetDelegate(
6643             WindowDelegateSP(new RegistersWindowDelegate(m_debugger)));
6644       }
6645       touchwin(stdscr);
6646     }
6647       return MenuActionResult::Handled;
6648 
6649     case eMenuID_ViewBreakpoints: {
6650       WindowSP main_window_sp = m_app.GetMainWindow();
6651       WindowSP threads_window_sp = main_window_sp->FindSubWindow("Threads");
6652       WindowSP breakpoints_window_sp =
6653           main_window_sp->FindSubWindow("Breakpoints");
6654       const Rect threads_bounds = threads_window_sp->GetBounds();
6655 
6656       // If a breakpoints window already exists, remove it and give the area
6657       // it used to occupy to the threads window. If it doesn't exist, split
6658       // the threads window horizontally into two windows where the top window
6659       // is the threads window and the bottom window is a newly added
6660       // breakpoints window.
6661       if (breakpoints_window_sp) {
6662         threads_window_sp->Resize(threads_bounds.size.width,
6663                                   threads_bounds.size.height +
6664                                       breakpoints_window_sp->GetHeight());
6665         main_window_sp->RemoveSubWindow(breakpoints_window_sp.get());
6666       } else {
6667         Rect new_threads_bounds, breakpoints_bounds;
6668         threads_bounds.HorizontalSplitPercentage(0.70, new_threads_bounds,
6669                                                  breakpoints_bounds);
6670         threads_window_sp->SetBounds(new_threads_bounds);
6671         breakpoints_window_sp = main_window_sp->CreateSubWindow(
6672             "Breakpoints", breakpoints_bounds, false);
6673         TreeDelegateSP breakpoints_delegate_sp(
6674             new BreakpointsTreeDelegate(m_debugger));
6675         breakpoints_window_sp->SetDelegate(WindowDelegateSP(
6676             new TreeWindowDelegate(m_debugger, breakpoints_delegate_sp)));
6677       }
6678       touchwin(stdscr);
6679       return MenuActionResult::Handled;
6680     }
6681 
6682     case eMenuID_HelpGUIHelp:
6683       m_app.GetMainWindow()->CreateHelpSubwindow();
6684       return MenuActionResult::Handled;
6685 
6686     default:
6687       break;
6688     }
6689 
6690     return MenuActionResult::NotHandled;
6691   }
6692 
6693 protected:
6694   Application &m_app;
6695   Debugger &m_debugger;
6696 };
6697 
6698 class StatusBarWindowDelegate : public WindowDelegate {
6699 public:
6700   StatusBarWindowDelegate(Debugger &debugger) : m_debugger(debugger) {
6701     FormatEntity::Parse("Thread: ${thread.id%tid}", m_format);
6702   }
6703 
6704   ~StatusBarWindowDelegate() override = default;
6705 
6706   bool WindowDelegateDraw(Window &window, bool force) override {
6707     ExecutionContext exe_ctx =
6708         m_debugger.GetCommandInterpreter().GetExecutionContext();
6709     Process *process = exe_ctx.GetProcessPtr();
6710     Thread *thread = exe_ctx.GetThreadPtr();
6711     StackFrame *frame = exe_ctx.GetFramePtr();
6712     window.Erase();
6713     window.SetBackground(BlackOnWhite);
6714     window.MoveCursor(0, 0);
6715     if (process) {
6716       const StateType state = process->GetState();
6717       window.Printf("Process: %5" PRIu64 " %10s", process->GetID(),
6718                     StateAsCString(state));
6719 
6720       if (StateIsStoppedState(state, true)) {
6721         StreamString strm;
6722         if (thread && FormatEntity::Format(m_format, strm, nullptr, &exe_ctx,
6723                                            nullptr, nullptr, false, false)) {
6724           window.MoveCursor(40, 0);
6725           window.PutCStringTruncated(1, strm.GetString().str().c_str());
6726         }
6727 
6728         window.MoveCursor(60, 0);
6729         if (frame)
6730           window.Printf("Frame: %3u  PC = 0x%16.16" PRIx64,
6731                         frame->GetFrameIndex(),
6732                         frame->GetFrameCodeAddress().GetOpcodeLoadAddress(
6733                             exe_ctx.GetTargetPtr()));
6734       } else if (state == eStateExited) {
6735         const char *exit_desc = process->GetExitDescription();
6736         const int exit_status = process->GetExitStatus();
6737         if (exit_desc && exit_desc[0])
6738           window.Printf(" with status = %i (%s)", exit_status, exit_desc);
6739         else
6740           window.Printf(" with status = %i", exit_status);
6741       }
6742     }
6743     return true;
6744   }
6745 
6746 protected:
6747   Debugger &m_debugger;
6748   FormatEntity::Entry m_format;
6749 };
6750 
6751 class SourceFileWindowDelegate : public WindowDelegate {
6752 public:
6753   SourceFileWindowDelegate(Debugger &debugger)
6754       : WindowDelegate(), m_debugger(debugger), m_sc(), m_file_sp(),
6755         m_disassembly_sp(), m_disassembly_range(), m_title() {}
6756 
6757   ~SourceFileWindowDelegate() override = default;
6758 
6759   void Update(const SymbolContext &sc) { m_sc = sc; }
6760 
6761   uint32_t NumVisibleLines() const { return m_max_y - m_min_y; }
6762 
6763   const char *WindowDelegateGetHelpText() override {
6764     return "Source/Disassembly window keyboard shortcuts:";
6765   }
6766 
6767   KeyHelp *WindowDelegateGetKeyHelp() override {
6768     static curses::KeyHelp g_source_view_key_help[] = {
6769         {KEY_RETURN, "Run to selected line with one shot breakpoint"},
6770         {KEY_UP, "Select previous source line"},
6771         {KEY_DOWN, "Select next source line"},
6772         {KEY_LEFT, "Scroll to the left"},
6773         {KEY_RIGHT, "Scroll to the right"},
6774         {KEY_PPAGE, "Page up"},
6775         {KEY_NPAGE, "Page down"},
6776         {'b', "Set breakpoint on selected source/disassembly line"},
6777         {'c', "Continue process"},
6778         {'D', "Detach with process suspended"},
6779         {'h', "Show help dialog"},
6780         {'n', "Step over (source line)"},
6781         {'N', "Step over (single instruction)"},
6782         {'f', "Step out (finish)"},
6783         {'s', "Step in (source line)"},
6784         {'S', "Step in (single instruction)"},
6785         {'u', "Frame up"},
6786         {'d', "Frame down"},
6787         {',', "Page up"},
6788         {'.', "Page down"},
6789         {'\0', nullptr}};
6790     return g_source_view_key_help;
6791   }
6792 
6793   bool WindowDelegateDraw(Window &window, bool force) override {
6794     ExecutionContext exe_ctx =
6795         m_debugger.GetCommandInterpreter().GetExecutionContext();
6796     Process *process = exe_ctx.GetProcessPtr();
6797     Thread *thread = nullptr;
6798 
6799     bool update_location = false;
6800     if (process) {
6801       StateType state = process->GetState();
6802       if (StateIsStoppedState(state, true)) {
6803         // We are stopped, so it is ok to
6804         update_location = true;
6805       }
6806     }
6807 
6808     m_min_x = 1;
6809     m_min_y = 2;
6810     m_max_x = window.GetMaxX() - 1;
6811     m_max_y = window.GetMaxY() - 1;
6812 
6813     const uint32_t num_visible_lines = NumVisibleLines();
6814     StackFrameSP frame_sp;
6815     bool set_selected_line_to_pc = false;
6816 
6817     if (update_location) {
6818       const bool process_alive = process ? process->IsAlive() : false;
6819       bool thread_changed = false;
6820       if (process_alive) {
6821         thread = exe_ctx.GetThreadPtr();
6822         if (thread) {
6823           frame_sp = thread->GetSelectedFrame();
6824           auto tid = thread->GetID();
6825           thread_changed = tid != m_tid;
6826           m_tid = tid;
6827         } else {
6828           if (m_tid != LLDB_INVALID_THREAD_ID) {
6829             thread_changed = true;
6830             m_tid = LLDB_INVALID_THREAD_ID;
6831           }
6832         }
6833       }
6834       const uint32_t stop_id = process ? process->GetStopID() : 0;
6835       const bool stop_id_changed = stop_id != m_stop_id;
6836       bool frame_changed = false;
6837       m_stop_id = stop_id;
6838       m_title.Clear();
6839       if (frame_sp) {
6840         m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
6841         if (m_sc.module_sp) {
6842           m_title.Printf(
6843               "%s", m_sc.module_sp->GetFileSpec().GetFilename().GetCString());
6844           ConstString func_name = m_sc.GetFunctionName();
6845           if (func_name)
6846             m_title.Printf("`%s", func_name.GetCString());
6847         }
6848         const uint32_t frame_idx = frame_sp->GetFrameIndex();
6849         frame_changed = frame_idx != m_frame_idx;
6850         m_frame_idx = frame_idx;
6851       } else {
6852         m_sc.Clear(true);
6853         frame_changed = m_frame_idx != UINT32_MAX;
6854         m_frame_idx = UINT32_MAX;
6855       }
6856 
6857       const bool context_changed =
6858           thread_changed || frame_changed || stop_id_changed;
6859 
6860       if (process_alive) {
6861         if (m_sc.line_entry.IsValid()) {
6862           m_pc_line = m_sc.line_entry.line;
6863           if (m_pc_line != UINT32_MAX)
6864             --m_pc_line; // Convert to zero based line number...
6865           // Update the selected line if the stop ID changed...
6866           if (context_changed)
6867             m_selected_line = m_pc_line;
6868 
6869           if (m_file_sp && m_file_sp->GetFileSpec() == m_sc.line_entry.file) {
6870             // Same file, nothing to do, we should either have the lines or
6871             // not (source file missing)
6872             if (m_selected_line >= static_cast<size_t>(m_first_visible_line)) {
6873               if (m_selected_line >= m_first_visible_line + num_visible_lines)
6874                 m_first_visible_line = m_selected_line - 10;
6875             } else {
6876               if (m_selected_line > 10)
6877                 m_first_visible_line = m_selected_line - 10;
6878               else
6879                 m_first_visible_line = 0;
6880             }
6881           } else {
6882             // File changed, set selected line to the line with the PC
6883             m_selected_line = m_pc_line;
6884             m_file_sp =
6885                 m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file);
6886             if (m_file_sp) {
6887               const size_t num_lines = m_file_sp->GetNumLines();
6888               m_line_width = 1;
6889               for (size_t n = num_lines; n >= 10; n = n / 10)
6890                 ++m_line_width;
6891 
6892               if (num_lines < num_visible_lines ||
6893                   m_selected_line < num_visible_lines)
6894                 m_first_visible_line = 0;
6895               else
6896                 m_first_visible_line = m_selected_line - 10;
6897             }
6898           }
6899         } else {
6900           m_file_sp.reset();
6901         }
6902 
6903         if (!m_file_sp || m_file_sp->GetNumLines() == 0) {
6904           // Show disassembly
6905           bool prefer_file_cache = false;
6906           if (m_sc.function) {
6907             if (m_disassembly_scope != m_sc.function) {
6908               m_disassembly_scope = m_sc.function;
6909               m_disassembly_sp = m_sc.function->GetInstructions(
6910                   exe_ctx, nullptr, !prefer_file_cache);
6911               if (m_disassembly_sp) {
6912                 set_selected_line_to_pc = true;
6913                 m_disassembly_range = m_sc.function->GetAddressRange();
6914               } else {
6915                 m_disassembly_range.Clear();
6916               }
6917             } else {
6918               set_selected_line_to_pc = context_changed;
6919             }
6920           } else if (m_sc.symbol) {
6921             if (m_disassembly_scope != m_sc.symbol) {
6922               m_disassembly_scope = m_sc.symbol;
6923               m_disassembly_sp = m_sc.symbol->GetInstructions(
6924                   exe_ctx, nullptr, prefer_file_cache);
6925               if (m_disassembly_sp) {
6926                 set_selected_line_to_pc = true;
6927                 m_disassembly_range.GetBaseAddress() =
6928                     m_sc.symbol->GetAddress();
6929                 m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize());
6930               } else {
6931                 m_disassembly_range.Clear();
6932               }
6933             } else {
6934               set_selected_line_to_pc = context_changed;
6935             }
6936           }
6937         }
6938       } else {
6939         m_pc_line = UINT32_MAX;
6940       }
6941     }
6942 
6943     const int window_width = window.GetWidth();
6944     window.Erase();
6945     window.DrawTitleBox("Sources");
6946     if (!m_title.GetString().empty()) {
6947       window.AttributeOn(A_REVERSE);
6948       window.MoveCursor(1, 1);
6949       window.PutChar(' ');
6950       window.PutCStringTruncated(1, m_title.GetString().str().c_str());
6951       int x = window.GetCursorX();
6952       if (x < window_width - 1) {
6953         window.Printf("%*s", window_width - x - 1, "");
6954       }
6955       window.AttributeOff(A_REVERSE);
6956     }
6957 
6958     Target *target = exe_ctx.GetTargetPtr();
6959     const size_t num_source_lines = GetNumSourceLines();
6960     if (num_source_lines > 0) {
6961       // Display source
6962       BreakpointLines bp_lines;
6963       if (target) {
6964         BreakpointList &bp_list = target->GetBreakpointList();
6965         const size_t num_bps = bp_list.GetSize();
6966         for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
6967           BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
6968           const size_t num_bps_locs = bp_sp->GetNumLocations();
6969           for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
6970             BreakpointLocationSP bp_loc_sp =
6971                 bp_sp->GetLocationAtIndex(bp_loc_idx);
6972             LineEntry bp_loc_line_entry;
6973             if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry(
6974                     bp_loc_line_entry)) {
6975               if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file) {
6976                 bp_lines.insert(bp_loc_line_entry.line);
6977               }
6978             }
6979           }
6980         }
6981       }
6982 
6983       for (size_t i = 0; i < num_visible_lines; ++i) {
6984         const uint32_t curr_line = m_first_visible_line + i;
6985         if (curr_line < num_source_lines) {
6986           const int line_y = m_min_y + i;
6987           window.MoveCursor(1, line_y);
6988           const bool is_pc_line = curr_line == m_pc_line;
6989           const bool line_is_selected = m_selected_line == curr_line;
6990           // Highlight the line as the PC line first (done by passing
6991           // argument to OutputColoredStringTruncated()), then if the selected
6992           // line isn't the same as the PC line, highlight it differently.
6993           attr_t highlight_attr = 0;
6994           attr_t bp_attr = 0;
6995           if (line_is_selected && !is_pc_line)
6996             highlight_attr = A_REVERSE;
6997 
6998           if (bp_lines.find(curr_line + 1) != bp_lines.end())
6999             bp_attr = COLOR_PAIR(BlackOnWhite);
7000 
7001           if (bp_attr)
7002             window.AttributeOn(bp_attr);
7003 
7004           window.Printf(" %*u ", m_line_width, curr_line + 1);
7005 
7006           if (bp_attr)
7007             window.AttributeOff(bp_attr);
7008 
7009           window.PutChar(ACS_VLINE);
7010           // Mark the line with the PC with a diamond
7011           if (is_pc_line)
7012             window.PutChar(ACS_DIAMOND);
7013           else
7014             window.PutChar(' ');
7015 
7016           if (highlight_attr)
7017             window.AttributeOn(highlight_attr);
7018 
7019           StreamString lineStream;
7020 
7021           llvm::Optional<size_t> column;
7022           if (is_pc_line && m_sc.line_entry.IsValid() && m_sc.line_entry.column)
7023             column = m_sc.line_entry.column - 1;
7024           m_file_sp->DisplaySourceLines(curr_line + 1, column, 0, 0,
7025                                         &lineStream);
7026           StringRef line = lineStream.GetString();
7027           if (line.endswith("\n"))
7028             line = line.drop_back();
7029           bool wasWritten = window.OutputColoredStringTruncated(
7030               1, line, m_first_visible_column, is_pc_line);
7031           if (!wasWritten && (line_is_selected || is_pc_line)) {
7032             // Draw an empty space to show the selected/PC line if empty,
7033             // or draw '<' if nothing is visible because of scrolling too much
7034             // to the right.
7035             window.PutCStringTruncated(
7036                 1, line.empty() && m_first_visible_column == 0 ? " " : "<");
7037           }
7038 
7039           if (is_pc_line && frame_sp &&
7040               frame_sp->GetConcreteFrameIndex() == 0) {
7041             StopInfoSP stop_info_sp;
7042             if (thread)
7043               stop_info_sp = thread->GetStopInfo();
7044             if (stop_info_sp) {
7045               const char *stop_description = stop_info_sp->GetDescription();
7046               if (stop_description && stop_description[0]) {
7047                 size_t stop_description_len = strlen(stop_description);
7048                 int desc_x = window_width - stop_description_len - 16;
7049                 if (desc_x - window.GetCursorX() > 0)
7050                   window.Printf("%*s", desc_x - window.GetCursorX(), "");
7051                 window.MoveCursor(window_width - stop_description_len - 16,
7052                                   line_y);
7053                 const attr_t stop_reason_attr = COLOR_PAIR(WhiteOnBlue);
7054                 window.AttributeOn(stop_reason_attr);
7055                 window.PrintfTruncated(1, " <<< Thread %u: %s ",
7056                                        thread->GetIndexID(), stop_description);
7057                 window.AttributeOff(stop_reason_attr);
7058               }
7059             } else {
7060               window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
7061             }
7062           }
7063           if (highlight_attr)
7064             window.AttributeOff(highlight_attr);
7065         } else {
7066           break;
7067         }
7068       }
7069     } else {
7070       size_t num_disassembly_lines = GetNumDisassemblyLines();
7071       if (num_disassembly_lines > 0) {
7072         // Display disassembly
7073         BreakpointAddrs bp_file_addrs;
7074         Target *target = exe_ctx.GetTargetPtr();
7075         if (target) {
7076           BreakpointList &bp_list = target->GetBreakpointList();
7077           const size_t num_bps = bp_list.GetSize();
7078           for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
7079             BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
7080             const size_t num_bps_locs = bp_sp->GetNumLocations();
7081             for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs;
7082                  ++bp_loc_idx) {
7083               BreakpointLocationSP bp_loc_sp =
7084                   bp_sp->GetLocationAtIndex(bp_loc_idx);
7085               LineEntry bp_loc_line_entry;
7086               const lldb::addr_t file_addr =
7087                   bp_loc_sp->GetAddress().GetFileAddress();
7088               if (file_addr != LLDB_INVALID_ADDRESS) {
7089                 if (m_disassembly_range.ContainsFileAddress(file_addr))
7090                   bp_file_addrs.insert(file_addr);
7091               }
7092             }
7093           }
7094         }
7095 
7096         const attr_t selected_highlight_attr = A_REVERSE;
7097         const attr_t pc_highlight_attr = COLOR_PAIR(WhiteOnBlue);
7098 
7099         StreamString strm;
7100 
7101         InstructionList &insts = m_disassembly_sp->GetInstructionList();
7102         Address pc_address;
7103 
7104         if (frame_sp)
7105           pc_address = frame_sp->GetFrameCodeAddress();
7106         const uint32_t pc_idx =
7107             pc_address.IsValid()
7108                 ? insts.GetIndexOfInstructionAtAddress(pc_address)
7109                 : UINT32_MAX;
7110         if (set_selected_line_to_pc) {
7111           m_selected_line = pc_idx;
7112         }
7113 
7114         const uint32_t non_visible_pc_offset = (num_visible_lines / 5);
7115         if (static_cast<size_t>(m_first_visible_line) >= num_disassembly_lines)
7116           m_first_visible_line = 0;
7117 
7118         if (pc_idx < num_disassembly_lines) {
7119           if (pc_idx < static_cast<uint32_t>(m_first_visible_line) ||
7120               pc_idx >= m_first_visible_line + num_visible_lines)
7121             m_first_visible_line = pc_idx - non_visible_pc_offset;
7122         }
7123 
7124         for (size_t i = 0; i < num_visible_lines; ++i) {
7125           const uint32_t inst_idx = m_first_visible_line + i;
7126           Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get();
7127           if (!inst)
7128             break;
7129 
7130           const int line_y = m_min_y + i;
7131           window.MoveCursor(1, line_y);
7132           const bool is_pc_line = frame_sp && inst_idx == pc_idx;
7133           const bool line_is_selected = m_selected_line == inst_idx;
7134           // Highlight the line as the PC line first, then if the selected
7135           // line isn't the same as the PC line, highlight it differently
7136           attr_t highlight_attr = 0;
7137           attr_t bp_attr = 0;
7138           if (is_pc_line)
7139             highlight_attr = pc_highlight_attr;
7140           else if (line_is_selected)
7141             highlight_attr = selected_highlight_attr;
7142 
7143           if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) !=
7144               bp_file_addrs.end())
7145             bp_attr = COLOR_PAIR(BlackOnWhite);
7146 
7147           if (bp_attr)
7148             window.AttributeOn(bp_attr);
7149 
7150           window.Printf(" 0x%16.16llx ",
7151                         static_cast<unsigned long long>(
7152                             inst->GetAddress().GetLoadAddress(target)));
7153 
7154           if (bp_attr)
7155             window.AttributeOff(bp_attr);
7156 
7157           window.PutChar(ACS_VLINE);
7158           // Mark the line with the PC with a diamond
7159           if (is_pc_line)
7160             window.PutChar(ACS_DIAMOND);
7161           else
7162             window.PutChar(' ');
7163 
7164           if (highlight_attr)
7165             window.AttributeOn(highlight_attr);
7166 
7167           const char *mnemonic = inst->GetMnemonic(&exe_ctx);
7168           const char *operands = inst->GetOperands(&exe_ctx);
7169           const char *comment = inst->GetComment(&exe_ctx);
7170 
7171           if (mnemonic != nullptr && mnemonic[0] == '\0')
7172             mnemonic = nullptr;
7173           if (operands != nullptr && operands[0] == '\0')
7174             operands = nullptr;
7175           if (comment != nullptr && comment[0] == '\0')
7176             comment = nullptr;
7177 
7178           strm.Clear();
7179 
7180           if (mnemonic != nullptr && operands != nullptr && comment != nullptr)
7181             strm.Printf("%-8s %-25s ; %s", mnemonic, operands, comment);
7182           else if (mnemonic != nullptr && operands != nullptr)
7183             strm.Printf("%-8s %s", mnemonic, operands);
7184           else if (mnemonic != nullptr)
7185             strm.Printf("%s", mnemonic);
7186 
7187           int right_pad = 1;
7188           window.PutCStringTruncated(
7189               right_pad,
7190               strm.GetString().substr(m_first_visible_column).data());
7191 
7192           if (is_pc_line && frame_sp &&
7193               frame_sp->GetConcreteFrameIndex() == 0) {
7194             StopInfoSP stop_info_sp;
7195             if (thread)
7196               stop_info_sp = thread->GetStopInfo();
7197             if (stop_info_sp) {
7198               const char *stop_description = stop_info_sp->GetDescription();
7199               if (stop_description && stop_description[0]) {
7200                 size_t stop_description_len = strlen(stop_description);
7201                 int desc_x = window_width - stop_description_len - 16;
7202                 if (desc_x - window.GetCursorX() > 0)
7203                   window.Printf("%*s", desc_x - window.GetCursorX(), "");
7204                 window.MoveCursor(window_width - stop_description_len - 15,
7205                                   line_y);
7206                 window.PrintfTruncated(1, "<<< Thread %u: %s ",
7207                                        thread->GetIndexID(), stop_description);
7208               }
7209             } else {
7210               window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
7211             }
7212           }
7213           if (highlight_attr)
7214             window.AttributeOff(highlight_attr);
7215         }
7216       }
7217     }
7218     return true; // Drawing handled
7219   }
7220 
7221   size_t GetNumLines() {
7222     size_t num_lines = GetNumSourceLines();
7223     if (num_lines == 0)
7224       num_lines = GetNumDisassemblyLines();
7225     return num_lines;
7226   }
7227 
7228   size_t GetNumSourceLines() const {
7229     if (m_file_sp)
7230       return m_file_sp->GetNumLines();
7231     return 0;
7232   }
7233 
7234   size_t GetNumDisassemblyLines() const {
7235     if (m_disassembly_sp)
7236       return m_disassembly_sp->GetInstructionList().GetSize();
7237     return 0;
7238   }
7239 
7240   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
7241     const uint32_t num_visible_lines = NumVisibleLines();
7242     const size_t num_lines = GetNumLines();
7243 
7244     switch (c) {
7245     case ',':
7246     case KEY_PPAGE:
7247       // Page up key
7248       if (static_cast<uint32_t>(m_first_visible_line) > num_visible_lines)
7249         m_first_visible_line -= num_visible_lines;
7250       else
7251         m_first_visible_line = 0;
7252       m_selected_line = m_first_visible_line;
7253       return eKeyHandled;
7254 
7255     case '.':
7256     case KEY_NPAGE:
7257       // Page down key
7258       {
7259         if (m_first_visible_line + num_visible_lines < num_lines)
7260           m_first_visible_line += num_visible_lines;
7261         else if (num_lines < num_visible_lines)
7262           m_first_visible_line = 0;
7263         else
7264           m_first_visible_line = num_lines - num_visible_lines;
7265         m_selected_line = m_first_visible_line;
7266       }
7267       return eKeyHandled;
7268 
7269     case KEY_UP:
7270       if (m_selected_line > 0) {
7271         m_selected_line--;
7272         if (static_cast<size_t>(m_first_visible_line) > m_selected_line)
7273           m_first_visible_line = m_selected_line;
7274       }
7275       return eKeyHandled;
7276 
7277     case KEY_DOWN:
7278       if (m_selected_line + 1 < num_lines) {
7279         m_selected_line++;
7280         if (m_first_visible_line + num_visible_lines < m_selected_line)
7281           m_first_visible_line++;
7282       }
7283       return eKeyHandled;
7284 
7285     case KEY_LEFT:
7286       if (m_first_visible_column > 0)
7287         --m_first_visible_column;
7288       return eKeyHandled;
7289 
7290     case KEY_RIGHT:
7291       ++m_first_visible_column;
7292       return eKeyHandled;
7293 
7294     case '\r':
7295     case '\n':
7296     case KEY_ENTER:
7297       // Set a breakpoint and run to the line using a one shot breakpoint
7298       if (GetNumSourceLines() > 0) {
7299         ExecutionContext exe_ctx =
7300             m_debugger.GetCommandInterpreter().GetExecutionContext();
7301         if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive()) {
7302           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
7303               nullptr, // Don't limit the breakpoint to certain modules
7304               m_file_sp->GetFileSpec(), // Source file
7305               m_selected_line +
7306                   1, // Source line number (m_selected_line is zero based)
7307               0,     // Unspecified column.
7308               0,     // No offset
7309               eLazyBoolCalculate,  // Check inlines using global setting
7310               eLazyBoolCalculate,  // Skip prologue using global setting,
7311               false,               // internal
7312               false,               // request_hardware
7313               eLazyBoolCalculate); // move_to_nearest_code
7314           // Make breakpoint one shot
7315           bp_sp->GetOptions().SetOneShot(true);
7316           exe_ctx.GetProcessRef().Resume();
7317         }
7318       } else if (m_selected_line < GetNumDisassemblyLines()) {
7319         const Instruction *inst = m_disassembly_sp->GetInstructionList()
7320                                       .GetInstructionAtIndex(m_selected_line)
7321                                       .get();
7322         ExecutionContext exe_ctx =
7323             m_debugger.GetCommandInterpreter().GetExecutionContext();
7324         if (exe_ctx.HasTargetScope()) {
7325           Address addr = inst->GetAddress();
7326           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
7327               addr,   // lldb_private::Address
7328               false,  // internal
7329               false); // request_hardware
7330           // Make breakpoint one shot
7331           bp_sp->GetOptions().SetOneShot(true);
7332           exe_ctx.GetProcessRef().Resume();
7333         }
7334       }
7335       return eKeyHandled;
7336 
7337     case 'b': // 'b' == toggle breakpoint on currently selected line
7338       ToggleBreakpointOnSelectedLine();
7339       return eKeyHandled;
7340 
7341     case 'D': // 'D' == detach and keep stopped
7342     {
7343       ExecutionContext exe_ctx =
7344           m_debugger.GetCommandInterpreter().GetExecutionContext();
7345       if (exe_ctx.HasProcessScope())
7346         exe_ctx.GetProcessRef().Detach(true);
7347     }
7348       return eKeyHandled;
7349 
7350     case 'c':
7351       // 'c' == continue
7352       {
7353         ExecutionContext exe_ctx =
7354             m_debugger.GetCommandInterpreter().GetExecutionContext();
7355         if (exe_ctx.HasProcessScope())
7356           exe_ctx.GetProcessRef().Resume();
7357       }
7358       return eKeyHandled;
7359 
7360     case 'f':
7361       // 'f' == step out (finish)
7362       {
7363         ExecutionContext exe_ctx =
7364             m_debugger.GetCommandInterpreter().GetExecutionContext();
7365         if (exe_ctx.HasThreadScope() &&
7366             StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
7367           Thread *thread = exe_ctx.GetThreadPtr();
7368           uint32_t frame_idx = thread->GetSelectedFrameIndex();
7369           exe_ctx.GetThreadRef().StepOut(frame_idx);
7370         }
7371       }
7372       return eKeyHandled;
7373 
7374     case 'n': // 'n' == step over
7375     case 'N': // 'N' == step over instruction
7376     {
7377       ExecutionContext exe_ctx =
7378           m_debugger.GetCommandInterpreter().GetExecutionContext();
7379       if (exe_ctx.HasThreadScope() &&
7380           StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
7381         bool source_step = (c == 'n');
7382         exe_ctx.GetThreadRef().StepOver(source_step);
7383       }
7384     }
7385       return eKeyHandled;
7386 
7387     case 's': // 's' == step into
7388     case 'S': // 'S' == step into instruction
7389     {
7390       ExecutionContext exe_ctx =
7391           m_debugger.GetCommandInterpreter().GetExecutionContext();
7392       if (exe_ctx.HasThreadScope() &&
7393           StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
7394         bool source_step = (c == 's');
7395         exe_ctx.GetThreadRef().StepIn(source_step);
7396       }
7397     }
7398       return eKeyHandled;
7399 
7400     case 'u': // 'u' == frame up
7401     case 'd': // 'd' == frame down
7402     {
7403       ExecutionContext exe_ctx =
7404           m_debugger.GetCommandInterpreter().GetExecutionContext();
7405       if (exe_ctx.HasThreadScope()) {
7406         Thread *thread = exe_ctx.GetThreadPtr();
7407         uint32_t frame_idx = thread->GetSelectedFrameIndex();
7408         if (frame_idx == UINT32_MAX)
7409           frame_idx = 0;
7410         if (c == 'u' && frame_idx + 1 < thread->GetStackFrameCount())
7411           ++frame_idx;
7412         else if (c == 'd' && frame_idx > 0)
7413           --frame_idx;
7414         if (thread->SetSelectedFrameByIndex(frame_idx, true))
7415           exe_ctx.SetFrameSP(thread->GetSelectedFrame());
7416       }
7417     }
7418       return eKeyHandled;
7419 
7420     case 'h':
7421       window.CreateHelpSubwindow();
7422       return eKeyHandled;
7423 
7424     default:
7425       break;
7426     }
7427     return eKeyNotHandled;
7428   }
7429 
7430   void ToggleBreakpointOnSelectedLine() {
7431     ExecutionContext exe_ctx =
7432         m_debugger.GetCommandInterpreter().GetExecutionContext();
7433     if (!exe_ctx.HasTargetScope())
7434       return;
7435     if (GetNumSourceLines() > 0) {
7436       // Source file breakpoint.
7437       BreakpointList &bp_list = exe_ctx.GetTargetRef().GetBreakpointList();
7438       const size_t num_bps = bp_list.GetSize();
7439       for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
7440         BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
7441         const size_t num_bps_locs = bp_sp->GetNumLocations();
7442         for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
7443           BreakpointLocationSP bp_loc_sp =
7444               bp_sp->GetLocationAtIndex(bp_loc_idx);
7445           LineEntry bp_loc_line_entry;
7446           if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry(
7447                   bp_loc_line_entry)) {
7448             if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file &&
7449                 m_selected_line + 1 == bp_loc_line_entry.line) {
7450               bool removed =
7451                   exe_ctx.GetTargetRef().RemoveBreakpointByID(bp_sp->GetID());
7452               assert(removed);
7453               UNUSED_IF_ASSERT_DISABLED(removed);
7454               return; // Existing breakpoint removed.
7455             }
7456           }
7457         }
7458       }
7459       // No breakpoint found on the location, add it.
7460       BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
7461           nullptr, // Don't limit the breakpoint to certain modules
7462           m_file_sp->GetFileSpec(), // Source file
7463           m_selected_line +
7464               1, // Source line number (m_selected_line is zero based)
7465           0,     // No column specified.
7466           0,     // No offset
7467           eLazyBoolCalculate,  // Check inlines using global setting
7468           eLazyBoolCalculate,  // Skip prologue using global setting,
7469           false,               // internal
7470           false,               // request_hardware
7471           eLazyBoolCalculate); // move_to_nearest_code
7472     } else {
7473       // Disassembly breakpoint.
7474       assert(GetNumDisassemblyLines() > 0);
7475       assert(m_selected_line < GetNumDisassemblyLines());
7476       const Instruction *inst = m_disassembly_sp->GetInstructionList()
7477                                     .GetInstructionAtIndex(m_selected_line)
7478                                     .get();
7479       Address addr = inst->GetAddress();
7480       // Try to find it.
7481       BreakpointList &bp_list = exe_ctx.GetTargetRef().GetBreakpointList();
7482       const size_t num_bps = bp_list.GetSize();
7483       for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
7484         BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
7485         const size_t num_bps_locs = bp_sp->GetNumLocations();
7486         for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
7487           BreakpointLocationSP bp_loc_sp =
7488               bp_sp->GetLocationAtIndex(bp_loc_idx);
7489           LineEntry bp_loc_line_entry;
7490           const lldb::addr_t file_addr =
7491               bp_loc_sp->GetAddress().GetFileAddress();
7492           if (file_addr == addr.GetFileAddress()) {
7493             bool removed =
7494                 exe_ctx.GetTargetRef().RemoveBreakpointByID(bp_sp->GetID());
7495             assert(removed);
7496             UNUSED_IF_ASSERT_DISABLED(removed);
7497             return; // Existing breakpoint removed.
7498           }
7499         }
7500       }
7501       // No breakpoint found on the address, add it.
7502       BreakpointSP bp_sp =
7503           exe_ctx.GetTargetRef().CreateBreakpoint(addr, // lldb_private::Address
7504                                                   false,  // internal
7505                                                   false); // request_hardware
7506     }
7507   }
7508 
7509 protected:
7510   typedef std::set<uint32_t> BreakpointLines;
7511   typedef std::set<lldb::addr_t> BreakpointAddrs;
7512 
7513   Debugger &m_debugger;
7514   SymbolContext m_sc;
7515   SourceManager::FileSP m_file_sp;
7516   SymbolContextScope *m_disassembly_scope = nullptr;
7517   lldb::DisassemblerSP m_disassembly_sp;
7518   AddressRange m_disassembly_range;
7519   StreamString m_title;
7520   lldb::user_id_t m_tid = LLDB_INVALID_THREAD_ID;
7521   int m_line_width = 4;
7522   uint32_t m_selected_line = 0; // The selected line
7523   uint32_t m_pc_line = 0;       // The line with the PC
7524   uint32_t m_stop_id = 0;
7525   uint32_t m_frame_idx = UINT32_MAX;
7526   int m_first_visible_line = 0;
7527   int m_first_visible_column = 0;
7528   int m_min_x = 0;
7529   int m_min_y = 0;
7530   int m_max_x = 0;
7531   int m_max_y = 0;
7532 };
7533 
7534 DisplayOptions ValueObjectListDelegate::g_options = {true};
7535 
7536 IOHandlerCursesGUI::IOHandlerCursesGUI(Debugger &debugger)
7537     : IOHandler(debugger, IOHandler::Type::Curses) {}
7538 
7539 void IOHandlerCursesGUI::Activate() {
7540   IOHandler::Activate();
7541   if (!m_app_ap) {
7542     m_app_ap = std::make_unique<Application>(GetInputFILE(), GetOutputFILE());
7543 
7544     // This is both a window and a menu delegate
7545     std::shared_ptr<ApplicationDelegate> app_delegate_sp(
7546         new ApplicationDelegate(*m_app_ap, m_debugger));
7547 
7548     MenuDelegateSP app_menu_delegate_sp =
7549         std::static_pointer_cast<MenuDelegate>(app_delegate_sp);
7550     MenuSP lldb_menu_sp(
7551         new Menu("LLDB", "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB));
7552     MenuSP exit_menuitem_sp(
7553         new Menu("Exit", nullptr, 'x', ApplicationDelegate::eMenuID_LLDBExit));
7554     exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit);
7555     lldb_menu_sp->AddSubmenu(MenuSP(new Menu(
7556         "About LLDB", nullptr, 'a', ApplicationDelegate::eMenuID_LLDBAbout)));
7557     lldb_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
7558     lldb_menu_sp->AddSubmenu(exit_menuitem_sp);
7559 
7560     MenuSP target_menu_sp(new Menu("Target", "F2", KEY_F(2),
7561                                    ApplicationDelegate::eMenuID_Target));
7562     target_menu_sp->AddSubmenu(MenuSP(new Menu(
7563         "Create", nullptr, 'c', ApplicationDelegate::eMenuID_TargetCreate)));
7564     target_menu_sp->AddSubmenu(MenuSP(new Menu(
7565         "Delete", nullptr, 'd', ApplicationDelegate::eMenuID_TargetDelete)));
7566 
7567     MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3),
7568                                     ApplicationDelegate::eMenuID_Process));
7569     process_menu_sp->AddSubmenu(MenuSP(new Menu(
7570         "Attach", nullptr, 'a', ApplicationDelegate::eMenuID_ProcessAttach)));
7571     process_menu_sp->AddSubmenu(
7572         MenuSP(new Menu("Detach and resume", nullptr, 'd',
7573                         ApplicationDelegate::eMenuID_ProcessDetachResume)));
7574     process_menu_sp->AddSubmenu(
7575         MenuSP(new Menu("Detach suspended", nullptr, 's',
7576                         ApplicationDelegate::eMenuID_ProcessDetachSuspended)));
7577     process_menu_sp->AddSubmenu(MenuSP(new Menu(
7578         "Launch", nullptr, 'l', ApplicationDelegate::eMenuID_ProcessLaunch)));
7579     process_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
7580     process_menu_sp->AddSubmenu(
7581         MenuSP(new Menu("Continue", nullptr, 'c',
7582                         ApplicationDelegate::eMenuID_ProcessContinue)));
7583     process_menu_sp->AddSubmenu(MenuSP(new Menu(
7584         "Halt", nullptr, 'h', ApplicationDelegate::eMenuID_ProcessHalt)));
7585     process_menu_sp->AddSubmenu(MenuSP(new Menu(
7586         "Kill", nullptr, 'k', ApplicationDelegate::eMenuID_ProcessKill)));
7587 
7588     MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4),
7589                                    ApplicationDelegate::eMenuID_Thread));
7590     thread_menu_sp->AddSubmenu(MenuSP(new Menu(
7591         "Step In", nullptr, 'i', ApplicationDelegate::eMenuID_ThreadStepIn)));
7592     thread_menu_sp->AddSubmenu(
7593         MenuSP(new Menu("Step Over", nullptr, 'v',
7594                         ApplicationDelegate::eMenuID_ThreadStepOver)));
7595     thread_menu_sp->AddSubmenu(MenuSP(new Menu(
7596         "Step Out", nullptr, 'o', ApplicationDelegate::eMenuID_ThreadStepOut)));
7597 
7598     MenuSP view_menu_sp(
7599         new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View));
7600     view_menu_sp->AddSubmenu(
7601         MenuSP(new Menu("Backtrace", nullptr, 't',
7602                         ApplicationDelegate::eMenuID_ViewBacktrace)));
7603     view_menu_sp->AddSubmenu(
7604         MenuSP(new Menu("Registers", nullptr, 'r',
7605                         ApplicationDelegate::eMenuID_ViewRegisters)));
7606     view_menu_sp->AddSubmenu(MenuSP(new Menu(
7607         "Source", nullptr, 's', ApplicationDelegate::eMenuID_ViewSource)));
7608     view_menu_sp->AddSubmenu(
7609         MenuSP(new Menu("Variables", nullptr, 'v',
7610                         ApplicationDelegate::eMenuID_ViewVariables)));
7611     view_menu_sp->AddSubmenu(
7612         MenuSP(new Menu("Breakpoints", nullptr, 'b',
7613                         ApplicationDelegate::eMenuID_ViewBreakpoints)));
7614 
7615     MenuSP help_menu_sp(
7616         new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help));
7617     help_menu_sp->AddSubmenu(MenuSP(new Menu(
7618         "GUI Help", nullptr, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp)));
7619 
7620     m_app_ap->Initialize();
7621     WindowSP &main_window_sp = m_app_ap->GetMainWindow();
7622 
7623     MenuSP menubar_sp(new Menu(Menu::Type::Bar));
7624     menubar_sp->AddSubmenu(lldb_menu_sp);
7625     menubar_sp->AddSubmenu(target_menu_sp);
7626     menubar_sp->AddSubmenu(process_menu_sp);
7627     menubar_sp->AddSubmenu(thread_menu_sp);
7628     menubar_sp->AddSubmenu(view_menu_sp);
7629     menubar_sp->AddSubmenu(help_menu_sp);
7630     menubar_sp->SetDelegate(app_menu_delegate_sp);
7631 
7632     Rect content_bounds = main_window_sp->GetFrame();
7633     Rect menubar_bounds = content_bounds.MakeMenuBar();
7634     Rect status_bounds = content_bounds.MakeStatusBar();
7635     Rect source_bounds;
7636     Rect variables_bounds;
7637     Rect threads_bounds;
7638     Rect source_variables_bounds;
7639     content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds,
7640                                            threads_bounds);
7641     source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds,
7642                                                       variables_bounds);
7643 
7644     WindowSP menubar_window_sp =
7645         main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false);
7646     // Let the menubar get keys if the active window doesn't handle the keys
7647     // that are typed so it can respond to menubar key presses.
7648     menubar_window_sp->SetCanBeActive(
7649         false); // Don't let the menubar become the active window
7650     menubar_window_sp->SetDelegate(menubar_sp);
7651 
7652     WindowSP source_window_sp(
7653         main_window_sp->CreateSubWindow("Source", source_bounds, true));
7654     WindowSP variables_window_sp(
7655         main_window_sp->CreateSubWindow("Variables", variables_bounds, false));
7656     WindowSP threads_window_sp(
7657         main_window_sp->CreateSubWindow("Threads", threads_bounds, false));
7658     WindowSP status_window_sp(
7659         main_window_sp->CreateSubWindow("Status", status_bounds, false));
7660     status_window_sp->SetCanBeActive(
7661         false); // Don't let the status bar become the active window
7662     main_window_sp->SetDelegate(
7663         std::static_pointer_cast<WindowDelegate>(app_delegate_sp));
7664     source_window_sp->SetDelegate(
7665         WindowDelegateSP(new SourceFileWindowDelegate(m_debugger)));
7666     variables_window_sp->SetDelegate(
7667         WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
7668     TreeDelegateSP thread_delegate_sp(new ThreadsTreeDelegate(m_debugger));
7669     threads_window_sp->SetDelegate(WindowDelegateSP(
7670         new TreeWindowDelegate(m_debugger, thread_delegate_sp)));
7671     status_window_sp->SetDelegate(
7672         WindowDelegateSP(new StatusBarWindowDelegate(m_debugger)));
7673 
7674     // All colors with black background.
7675     init_pair(1, COLOR_BLACK, COLOR_BLACK);
7676     init_pair(2, COLOR_RED, COLOR_BLACK);
7677     init_pair(3, COLOR_GREEN, COLOR_BLACK);
7678     init_pair(4, COLOR_YELLOW, COLOR_BLACK);
7679     init_pair(5, COLOR_BLUE, COLOR_BLACK);
7680     init_pair(6, COLOR_MAGENTA, COLOR_BLACK);
7681     init_pair(7, COLOR_CYAN, COLOR_BLACK);
7682     init_pair(8, COLOR_WHITE, COLOR_BLACK);
7683     // All colors with blue background.
7684     init_pair(9, COLOR_BLACK, COLOR_BLUE);
7685     init_pair(10, COLOR_RED, COLOR_BLUE);
7686     init_pair(11, COLOR_GREEN, COLOR_BLUE);
7687     init_pair(12, COLOR_YELLOW, COLOR_BLUE);
7688     init_pair(13, COLOR_BLUE, COLOR_BLUE);
7689     init_pair(14, COLOR_MAGENTA, COLOR_BLUE);
7690     init_pair(15, COLOR_CYAN, COLOR_BLUE);
7691     init_pair(16, COLOR_WHITE, COLOR_BLUE);
7692     // These must match the order in the color indexes enum.
7693     init_pair(17, COLOR_BLACK, COLOR_WHITE);
7694     init_pair(18, COLOR_MAGENTA, COLOR_WHITE);
7695     static_assert(LastColorPairIndex == 18, "Color indexes do not match.");
7696 
7697     define_key("\033[Z", KEY_SHIFT_TAB);
7698     define_key("\033\015", KEY_ALT_ENTER);
7699   }
7700 }
7701 
7702 void IOHandlerCursesGUI::Deactivate() { m_app_ap->Terminate(); }
7703 
7704 void IOHandlerCursesGUI::Run() {
7705   m_app_ap->Run(m_debugger);
7706   SetIsDone(true);
7707 }
7708 
7709 IOHandlerCursesGUI::~IOHandlerCursesGUI() = default;
7710 
7711 void IOHandlerCursesGUI::Cancel() {}
7712 
7713 bool IOHandlerCursesGUI::Interrupt() { return false; }
7714 
7715 void IOHandlerCursesGUI::GotEOF() {}
7716 
7717 void IOHandlerCursesGUI::TerminalSizeChanged() {
7718   m_app_ap->TerminalSizeChanged();
7719 }
7720 
7721 #endif // LLDB_ENABLE_CURSES
7722