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