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