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