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