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