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