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   // Always return eKeyHandled to absorb all events since forms are always
2805   // added as pop-ups that should take full control until canceled or submitted.
2806   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
2807     switch (key) {
2808     case '\r':
2809     case '\n':
2810     case KEY_ENTER:
2811       if (m_selection_type == SelectionType::Action) {
2812         ExecuteAction(window, m_selection_index);
2813         return eKeyHandled;
2814       }
2815       break;
2816     case KEY_ALT_ENTER:
2817       ExecuteAction(window, 0);
2818       return eKeyHandled;
2819     case '\t':
2820       SelectNext(key);
2821       return eKeyHandled;
2822     case KEY_SHIFT_TAB:
2823       SelectPrevious(key);
2824       return eKeyHandled;
2825     case KEY_ESCAPE:
2826       window.GetParent()->RemoveSubWindow(&window);
2827       return eKeyHandled;
2828     default:
2829       break;
2830     }
2831 
2832     // If the key wasn't handled and one of the fields is selected, pass the key
2833     // to that field.
2834     if (m_selection_type == SelectionType::Field) {
2835       FieldDelegate *field = m_delegate_sp->GetField(m_selection_index);
2836       if (field->FieldDelegateHandleChar(key) == eKeyHandled)
2837         return eKeyHandled;
2838     }
2839 
2840     // If the key wasn't handled by the possibly selected field, handle some
2841     // extra keys for navigation.
2842     switch (key) {
2843     case KEY_DOWN:
2844       SelectNext(key);
2845       return eKeyHandled;
2846     case KEY_UP:
2847       SelectPrevious(key);
2848       return eKeyHandled;
2849     default:
2850       break;
2851     }
2852 
2853     return eKeyHandled;
2854   }
2855 
2856 protected:
2857   FormDelegateSP m_delegate_sp;
2858   // The index of the currently selected SelectionType.
2859   int m_selection_index;
2860   // See SelectionType class enum.
2861   SelectionType m_selection_type;
2862   // The first visible line from the pad.
2863   int m_first_visible_line;
2864 };
2865 
2866 ///////////////////////////
2867 // Form Delegate Instances
2868 ///////////////////////////
2869 
2870 class DetachOrKillProcessFormDelegate : public FormDelegate {
2871 public:
2872   DetachOrKillProcessFormDelegate(Process *process) : m_process(process) {
2873     SetError("There is a running process, either detach or kill it.");
2874 
2875     m_keep_stopped_field =
2876         AddBooleanField("Keep process stopped when detaching.", false);
2877 
2878     AddAction("Detach", [this](Window &window) { Detach(window); });
2879     AddAction("Kill", [this](Window &window) { Kill(window); });
2880   }
2881 
2882   std::string GetName() override { return "Detach/Kill Process"; }
2883 
2884   void Kill(Window &window) {
2885     Status destroy_status(m_process->Destroy(false));
2886     if (destroy_status.Fail()) {
2887       SetError("Failed to kill process.");
2888       return;
2889     }
2890     window.GetParent()->RemoveSubWindow(&window);
2891   }
2892 
2893   void Detach(Window &window) {
2894     Status detach_status(m_process->Detach(m_keep_stopped_field->GetBoolean()));
2895     if (detach_status.Fail()) {
2896       SetError("Failed to detach from process.");
2897       return;
2898     }
2899     window.GetParent()->RemoveSubWindow(&window);
2900   }
2901 
2902 protected:
2903   Process *m_process;
2904   BooleanFieldDelegate *m_keep_stopped_field;
2905 };
2906 
2907 class ProcessAttachFormDelegate : public FormDelegate {
2908 public:
2909   ProcessAttachFormDelegate(Debugger &debugger, WindowSP main_window_sp)
2910       : m_debugger(debugger), m_main_window_sp(main_window_sp) {
2911     std::vector<std::string> types;
2912     types.push_back(std::string("Name"));
2913     types.push_back(std::string("PID"));
2914     m_type_field = AddChoicesField("Attach By", 2, types);
2915     m_pid_field = AddIntegerField("PID", 0, true);
2916     m_name_field =
2917         AddTextField("Process Name", GetDefaultProcessName().c_str(), true);
2918     m_continue_field = AddBooleanField("Continue once attached.", false);
2919     m_wait_for_field = AddBooleanField("Wait for process to launch.", false);
2920     m_include_existing_field =
2921         AddBooleanField("Include existing processes.", false);
2922     m_show_advanced_field = AddBooleanField("Show advanced settings.", false);
2923     m_plugin_field = AddProcessPluginField();
2924 
2925     AddAction("Attach", [this](Window &window) { Attach(window); });
2926   }
2927 
2928   std::string GetName() override { return "Attach Process"; }
2929 
2930   void UpdateFieldsVisibility() override {
2931     if (m_type_field->GetChoiceContent() == "Name") {
2932       m_pid_field->FieldDelegateHide();
2933       m_name_field->FieldDelegateShow();
2934       m_wait_for_field->FieldDelegateShow();
2935       if (m_wait_for_field->GetBoolean())
2936         m_include_existing_field->FieldDelegateShow();
2937       else
2938         m_include_existing_field->FieldDelegateHide();
2939     } else {
2940       m_pid_field->FieldDelegateShow();
2941       m_name_field->FieldDelegateHide();
2942       m_wait_for_field->FieldDelegateHide();
2943       m_include_existing_field->FieldDelegateHide();
2944     }
2945     if (m_show_advanced_field->GetBoolean())
2946       m_plugin_field->FieldDelegateShow();
2947     else
2948       m_plugin_field->FieldDelegateHide();
2949   }
2950 
2951   // Get the basename of the target's main executable if available, empty string
2952   // otherwise.
2953   std::string GetDefaultProcessName() {
2954     Target *target = m_debugger.GetSelectedTarget().get();
2955     if (target == nullptr)
2956       return "";
2957 
2958     ModuleSP module_sp = target->GetExecutableModule();
2959     if (!module_sp->IsExecutable())
2960       return "";
2961 
2962     return module_sp->GetFileSpec().GetFilename().AsCString();
2963   }
2964 
2965   bool StopRunningProcess() {
2966     ExecutionContext exe_ctx =
2967         m_debugger.GetCommandInterpreter().GetExecutionContext();
2968 
2969     if (!exe_ctx.HasProcessScope())
2970       return false;
2971 
2972     Process *process = exe_ctx.GetProcessPtr();
2973     if (!(process && process->IsAlive()))
2974       return false;
2975 
2976     FormDelegateSP form_delegate_sp =
2977         FormDelegateSP(new DetachOrKillProcessFormDelegate(process));
2978     Rect bounds = m_main_window_sp->GetCenteredRect(85, 8);
2979     WindowSP form_window_sp = m_main_window_sp->CreateSubWindow(
2980         form_delegate_sp->GetName().c_str(), bounds, true);
2981     WindowDelegateSP window_delegate_sp =
2982         WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
2983     form_window_sp->SetDelegate(window_delegate_sp);
2984 
2985     return true;
2986   }
2987 
2988   Target *GetTarget() {
2989     Target *target = m_debugger.GetSelectedTarget().get();
2990 
2991     if (target != nullptr)
2992       return target;
2993 
2994     TargetSP new_target_sp;
2995     m_debugger.GetTargetList().CreateTarget(
2996         m_debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp);
2997 
2998     target = new_target_sp.get();
2999 
3000     if (target == nullptr)
3001       SetError("Failed to create target.");
3002 
3003     m_debugger.GetTargetList().SetSelectedTarget(new_target_sp);
3004 
3005     return target;
3006   }
3007 
3008   ProcessAttachInfo GetAttachInfo() {
3009     ProcessAttachInfo attach_info;
3010     attach_info.SetContinueOnceAttached(m_continue_field->GetBoolean());
3011     if (m_type_field->GetChoiceContent() == "Name") {
3012       attach_info.GetExecutableFile().SetFile(m_name_field->GetText(),
3013                                               FileSpec::Style::native);
3014       attach_info.SetWaitForLaunch(m_wait_for_field->GetBoolean());
3015       if (m_wait_for_field->GetBoolean())
3016         attach_info.SetIgnoreExisting(!m_include_existing_field->GetBoolean());
3017     } else {
3018       attach_info.SetProcessID(m_pid_field->GetInteger());
3019     }
3020     attach_info.SetProcessPluginName(m_plugin_field->GetPluginName());
3021 
3022     return attach_info;
3023   }
3024 
3025   void Attach(Window &window) {
3026     ClearError();
3027 
3028     bool all_fields_are_valid = CheckFieldsValidity();
3029     if (!all_fields_are_valid)
3030       return;
3031 
3032     bool process_is_running = StopRunningProcess();
3033     if (process_is_running)
3034       return;
3035 
3036     Target *target = GetTarget();
3037     if (HasError())
3038       return;
3039 
3040     StreamString stream;
3041     ProcessAttachInfo attach_info = GetAttachInfo();
3042     Status status = target->Attach(attach_info, &stream);
3043 
3044     if (status.Fail()) {
3045       SetError(status.AsCString());
3046       return;
3047     }
3048 
3049     ProcessSP process_sp(target->GetProcessSP());
3050     if (!process_sp) {
3051       SetError("Attached sucessfully but target has no process.");
3052       return;
3053     }
3054 
3055     if (attach_info.GetContinueOnceAttached())
3056       process_sp->Resume();
3057 
3058     window.GetParent()->RemoveSubWindow(&window);
3059   }
3060 
3061 protected:
3062   Debugger &m_debugger;
3063   WindowSP m_main_window_sp;
3064 
3065   ChoicesFieldDelegate *m_type_field;
3066   IntegerFieldDelegate *m_pid_field;
3067   TextFieldDelegate *m_name_field;
3068   BooleanFieldDelegate *m_continue_field;
3069   BooleanFieldDelegate *m_wait_for_field;
3070   BooleanFieldDelegate *m_include_existing_field;
3071   BooleanFieldDelegate *m_show_advanced_field;
3072   ProcessPluginFieldDelegate *m_plugin_field;
3073 };
3074 
3075 class TargetCreateFormDelegate : public FormDelegate {
3076 public:
3077   TargetCreateFormDelegate(Debugger &debugger) : m_debugger(debugger) {
3078     m_executable_field = AddFileField("Executable", "", /*need_to_exist=*/true,
3079                                       /*required=*/true);
3080     m_core_file_field = AddFileField("Core File", "", /*need_to_exist=*/true,
3081                                      /*required=*/false);
3082     m_symbol_file_field = AddFileField(
3083         "Symbol File", "", /*need_to_exist=*/true, /*required=*/false);
3084     m_show_advanced_field = AddBooleanField("Show advanced settings.", false);
3085     m_remote_file_field = AddFileField(
3086         "Remote File", "", /*need_to_exist=*/false, /*required=*/false);
3087     m_arch_field = AddArchField("Architecture", "", /*required=*/false);
3088     m_platform_field = AddPlatformPluginField(debugger);
3089     m_load_dependent_files_field =
3090         AddChoicesField("Load Dependents", 3, GetLoadDependentFilesChoices());
3091 
3092     AddAction("Create", [this](Window &window) { CreateTarget(window); });
3093   }
3094 
3095   std::string GetName() override { return "Create Target"; }
3096 
3097   void UpdateFieldsVisibility() override {
3098     if (m_show_advanced_field->GetBoolean()) {
3099       m_remote_file_field->FieldDelegateShow();
3100       m_arch_field->FieldDelegateShow();
3101       m_platform_field->FieldDelegateShow();
3102       m_load_dependent_files_field->FieldDelegateShow();
3103     } else {
3104       m_remote_file_field->FieldDelegateHide();
3105       m_arch_field->FieldDelegateHide();
3106       m_platform_field->FieldDelegateHide();
3107       m_load_dependent_files_field->FieldDelegateHide();
3108     }
3109   }
3110 
3111   static constexpr const char *kLoadDependentFilesNo = "No";
3112   static constexpr const char *kLoadDependentFilesYes = "Yes";
3113   static constexpr const char *kLoadDependentFilesExecOnly = "Executable only";
3114 
3115   std::vector<std::string> GetLoadDependentFilesChoices() {
3116     std::vector<std::string> load_depentents_options;
3117     load_depentents_options.push_back(kLoadDependentFilesExecOnly);
3118     load_depentents_options.push_back(kLoadDependentFilesYes);
3119     load_depentents_options.push_back(kLoadDependentFilesNo);
3120     return load_depentents_options;
3121   }
3122 
3123   LoadDependentFiles GetLoadDependentFiles() {
3124     std::string choice = m_load_dependent_files_field->GetChoiceContent();
3125     if (choice == kLoadDependentFilesNo)
3126       return eLoadDependentsNo;
3127     if (choice == kLoadDependentFilesYes)
3128       return eLoadDependentsYes;
3129     return eLoadDependentsDefault;
3130   }
3131 
3132   OptionGroupPlatform GetPlatformOptions() {
3133     OptionGroupPlatform platform_options(false);
3134     platform_options.SetPlatformName(m_platform_field->GetPluginName().c_str());
3135     return platform_options;
3136   }
3137 
3138   TargetSP GetTarget() {
3139     OptionGroupPlatform platform_options = GetPlatformOptions();
3140     TargetSP target_sp;
3141     Status status = m_debugger.GetTargetList().CreateTarget(
3142         m_debugger, m_executable_field->GetPath(),
3143         m_arch_field->GetArchString(), GetLoadDependentFiles(),
3144         &platform_options, target_sp);
3145 
3146     if (status.Fail()) {
3147       SetError(status.AsCString());
3148       return nullptr;
3149     }
3150 
3151     m_debugger.GetTargetList().SetSelectedTarget(target_sp);
3152 
3153     return target_sp;
3154   }
3155 
3156   void SetSymbolFile(TargetSP target_sp) {
3157     if (!m_symbol_file_field->IsSpecified())
3158       return;
3159 
3160     ModuleSP module_sp(target_sp->GetExecutableModule());
3161     if (!module_sp)
3162       return;
3163 
3164     module_sp->SetSymbolFileFileSpec(
3165         m_symbol_file_field->GetResolvedFileSpec());
3166   }
3167 
3168   void SetCoreFile(TargetSP target_sp) {
3169     if (!m_core_file_field->IsSpecified())
3170       return;
3171 
3172     FileSpec core_file_spec = m_core_file_field->GetResolvedFileSpec();
3173 
3174     FileSpec core_file_directory_spec;
3175     core_file_directory_spec.GetDirectory() = core_file_spec.GetDirectory();
3176     target_sp->AppendExecutableSearchPaths(core_file_directory_spec);
3177 
3178     ProcessSP process_sp(target_sp->CreateProcess(
3179         m_debugger.GetListener(), llvm::StringRef(), &core_file_spec, false));
3180 
3181     if (!process_sp) {
3182       SetError("Unable to find process plug-in for core file!");
3183       return;
3184     }
3185 
3186     Status status = process_sp->LoadCore();
3187     if (status.Fail()) {
3188       SetError("Can't find plug-in for core file!");
3189       return;
3190     }
3191   }
3192 
3193   void SetRemoteFile(TargetSP target_sp) {
3194     if (!m_remote_file_field->IsSpecified())
3195       return;
3196 
3197     ModuleSP module_sp(target_sp->GetExecutableModule());
3198     if (!module_sp)
3199       return;
3200 
3201     FileSpec remote_file_spec = m_remote_file_field->GetFileSpec();
3202     module_sp->SetPlatformFileSpec(remote_file_spec);
3203   }
3204 
3205   void RemoveTarget(TargetSP target_sp) {
3206     m_debugger.GetTargetList().DeleteTarget(target_sp);
3207   }
3208 
3209   void CreateTarget(Window &window) {
3210     ClearError();
3211 
3212     bool all_fields_are_valid = CheckFieldsValidity();
3213     if (!all_fields_are_valid)
3214       return;
3215 
3216     TargetSP target_sp = GetTarget();
3217     if (HasError())
3218       return;
3219 
3220     SetSymbolFile(target_sp);
3221     if (HasError()) {
3222       RemoveTarget(target_sp);
3223       return;
3224     }
3225 
3226     SetCoreFile(target_sp);
3227     if (HasError()) {
3228       RemoveTarget(target_sp);
3229       return;
3230     }
3231 
3232     SetRemoteFile(target_sp);
3233     if (HasError()) {
3234       RemoveTarget(target_sp);
3235       return;
3236     }
3237 
3238     window.GetParent()->RemoveSubWindow(&window);
3239   }
3240 
3241 protected:
3242   Debugger &m_debugger;
3243 
3244   FileFieldDelegate *m_executable_field;
3245   FileFieldDelegate *m_core_file_field;
3246   FileFieldDelegate *m_symbol_file_field;
3247   BooleanFieldDelegate *m_show_advanced_field;
3248   FileFieldDelegate *m_remote_file_field;
3249   ArchFieldDelegate *m_arch_field;
3250   PlatformPluginFieldDelegate *m_platform_field;
3251   ChoicesFieldDelegate *m_load_dependent_files_field;
3252 };
3253 
3254 class ProcessLaunchFormDelegate : public FormDelegate {
3255 public:
3256   ProcessLaunchFormDelegate(Debugger &debugger, WindowSP main_window_sp)
3257       : m_debugger(debugger), m_main_window_sp(main_window_sp) {
3258 
3259     m_arguments_field = AddArgumentsField();
3260     SetArgumentsFieldDefaultValue();
3261     m_target_environment_field =
3262         AddEnvironmentVariableListField("Target Environment Variables");
3263     SetTargetEnvironmentFieldDefaultValue();
3264     m_working_directory_field = AddDirectoryField(
3265         "Working Directory", GetDefaultWorkingDirectory().c_str(), true, false);
3266 
3267     m_show_advanced_field = AddBooleanField("Show advanced settings.", false);
3268 
3269     m_stop_at_entry_field = AddBooleanField("Stop at entry point.", false);
3270     m_detach_on_error_field =
3271         AddBooleanField("Detach on error.", GetDefaultDetachOnError());
3272     m_disable_aslr_field =
3273         AddBooleanField("Disable ASLR", GetDefaultDisableASLR());
3274     m_plugin_field = AddProcessPluginField();
3275     m_arch_field = AddArchField("Architecture", "", false);
3276     m_shell_field = AddFileField("Shell", "", true, false);
3277     m_expand_shell_arguments_field =
3278         AddBooleanField("Expand shell arguments.", false);
3279 
3280     m_disable_standard_io_field =
3281         AddBooleanField("Disable Standard IO", GetDefaultDisableStandardIO());
3282     m_standard_output_field =
3283         AddFileField("Standard Output File", "", /*need_to_exist=*/false,
3284                      /*required=*/false);
3285     m_standard_error_field =
3286         AddFileField("Standard Error File", "", /*need_to_exist=*/false,
3287                      /*required=*/false);
3288     m_standard_input_field =
3289         AddFileField("Standard Input File", "", /*need_to_exist=*/false,
3290                      /*required=*/false);
3291 
3292     m_show_inherited_environment_field =
3293         AddBooleanField("Show inherited environment variables.", false);
3294     m_inherited_environment_field =
3295         AddEnvironmentVariableListField("Inherited Environment Variables");
3296     SetInheritedEnvironmentFieldDefaultValue();
3297 
3298     AddAction("Launch", [this](Window &window) { Launch(window); });
3299   }
3300 
3301   std::string GetName() override { return "Launch Process"; }
3302 
3303   void UpdateFieldsVisibility() override {
3304     if (m_show_advanced_field->GetBoolean()) {
3305       m_stop_at_entry_field->FieldDelegateShow();
3306       m_detach_on_error_field->FieldDelegateShow();
3307       m_disable_aslr_field->FieldDelegateShow();
3308       m_plugin_field->FieldDelegateShow();
3309       m_arch_field->FieldDelegateShow();
3310       m_shell_field->FieldDelegateShow();
3311       m_expand_shell_arguments_field->FieldDelegateShow();
3312       m_disable_standard_io_field->FieldDelegateShow();
3313       if (m_disable_standard_io_field->GetBoolean()) {
3314         m_standard_input_field->FieldDelegateHide();
3315         m_standard_output_field->FieldDelegateHide();
3316         m_standard_error_field->FieldDelegateHide();
3317       } else {
3318         m_standard_input_field->FieldDelegateShow();
3319         m_standard_output_field->FieldDelegateShow();
3320         m_standard_error_field->FieldDelegateShow();
3321       }
3322       m_show_inherited_environment_field->FieldDelegateShow();
3323       if (m_show_inherited_environment_field->GetBoolean())
3324         m_inherited_environment_field->FieldDelegateShow();
3325       else
3326         m_inherited_environment_field->FieldDelegateHide();
3327     } else {
3328       m_stop_at_entry_field->FieldDelegateHide();
3329       m_detach_on_error_field->FieldDelegateHide();
3330       m_disable_aslr_field->FieldDelegateHide();
3331       m_plugin_field->FieldDelegateHide();
3332       m_arch_field->FieldDelegateHide();
3333       m_shell_field->FieldDelegateHide();
3334       m_expand_shell_arguments_field->FieldDelegateHide();
3335       m_disable_standard_io_field->FieldDelegateHide();
3336       m_standard_input_field->FieldDelegateHide();
3337       m_standard_output_field->FieldDelegateHide();
3338       m_standard_error_field->FieldDelegateHide();
3339       m_show_inherited_environment_field->FieldDelegateHide();
3340       m_inherited_environment_field->FieldDelegateHide();
3341     }
3342   }
3343 
3344   // Methods for setting the default value of the fields.
3345 
3346   void SetArgumentsFieldDefaultValue() {
3347     TargetSP target = m_debugger.GetSelectedTarget();
3348     if (target == nullptr)
3349       return;
3350 
3351     const Args &target_arguments =
3352         target->GetProcessLaunchInfo().GetArguments();
3353     m_arguments_field->AddArguments(target_arguments);
3354   }
3355 
3356   void SetTargetEnvironmentFieldDefaultValue() {
3357     TargetSP target = m_debugger.GetSelectedTarget();
3358     if (target == nullptr)
3359       return;
3360 
3361     const Environment &target_environment = target->GetTargetEnvironment();
3362     m_target_environment_field->AddEnvironmentVariables(target_environment);
3363   }
3364 
3365   void SetInheritedEnvironmentFieldDefaultValue() {
3366     TargetSP target = m_debugger.GetSelectedTarget();
3367     if (target == nullptr)
3368       return;
3369 
3370     const Environment &inherited_environment =
3371         target->GetInheritedEnvironment();
3372     m_inherited_environment_field->AddEnvironmentVariables(
3373         inherited_environment);
3374   }
3375 
3376   std::string GetDefaultWorkingDirectory() {
3377     TargetSP target = m_debugger.GetSelectedTarget();
3378     if (target == nullptr)
3379       return "";
3380 
3381     PlatformSP platform = target->GetPlatform();
3382     return platform->GetWorkingDirectory().GetPath();
3383   }
3384 
3385   bool GetDefaultDisableASLR() {
3386     TargetSP target = m_debugger.GetSelectedTarget();
3387     if (target == nullptr)
3388       return false;
3389 
3390     return target->GetDisableASLR();
3391   }
3392 
3393   bool GetDefaultDisableStandardIO() {
3394     TargetSP target = m_debugger.GetSelectedTarget();
3395     if (target == nullptr)
3396       return true;
3397 
3398     return target->GetDisableSTDIO();
3399   }
3400 
3401   bool GetDefaultDetachOnError() {
3402     TargetSP target = m_debugger.GetSelectedTarget();
3403     if (target == nullptr)
3404       return true;
3405 
3406     return target->GetDetachOnError();
3407   }
3408 
3409   // Methods for getting the necessary information and setting them to the
3410   // ProcessLaunchInfo.
3411 
3412   void GetExecutableSettings(ProcessLaunchInfo &launch_info) {
3413     TargetSP target = m_debugger.GetSelectedTarget();
3414     ModuleSP executable_module = target->GetExecutableModule();
3415     llvm::StringRef target_settings_argv0 = target->GetArg0();
3416 
3417     if (!target_settings_argv0.empty()) {
3418       launch_info.GetArguments().AppendArgument(target_settings_argv0);
3419       launch_info.SetExecutableFile(executable_module->GetPlatformFileSpec(),
3420                                     false);
3421       return;
3422     }
3423 
3424     launch_info.SetExecutableFile(executable_module->GetPlatformFileSpec(),
3425                                   true);
3426   }
3427 
3428   void GetArguments(ProcessLaunchInfo &launch_info) {
3429     TargetSP target = m_debugger.GetSelectedTarget();
3430     Args arguments = m_arguments_field->GetArguments();
3431     launch_info.GetArguments().AppendArguments(arguments);
3432   }
3433 
3434   void GetEnvironment(ProcessLaunchInfo &launch_info) {
3435     Environment target_environment =
3436         m_target_environment_field->GetEnvironment();
3437     Environment inherited_environment =
3438         m_inherited_environment_field->GetEnvironment();
3439     launch_info.GetEnvironment().insert(target_environment.begin(),
3440                                         target_environment.end());
3441     launch_info.GetEnvironment().insert(inherited_environment.begin(),
3442                                         inherited_environment.end());
3443   }
3444 
3445   void GetWorkingDirectory(ProcessLaunchInfo &launch_info) {
3446     if (m_working_directory_field->IsSpecified())
3447       launch_info.SetWorkingDirectory(
3448           m_working_directory_field->GetResolvedFileSpec());
3449   }
3450 
3451   void GetStopAtEntry(ProcessLaunchInfo &launch_info) {
3452     if (m_stop_at_entry_field->GetBoolean())
3453       launch_info.GetFlags().Set(eLaunchFlagStopAtEntry);
3454     else
3455       launch_info.GetFlags().Clear(eLaunchFlagStopAtEntry);
3456   }
3457 
3458   void GetDetachOnError(ProcessLaunchInfo &launch_info) {
3459     if (m_detach_on_error_field->GetBoolean())
3460       launch_info.GetFlags().Set(eLaunchFlagDetachOnError);
3461     else
3462       launch_info.GetFlags().Clear(eLaunchFlagDetachOnError);
3463   }
3464 
3465   void GetDisableASLR(ProcessLaunchInfo &launch_info) {
3466     if (m_disable_aslr_field->GetBoolean())
3467       launch_info.GetFlags().Set(eLaunchFlagDisableASLR);
3468     else
3469       launch_info.GetFlags().Clear(eLaunchFlagDisableASLR);
3470   }
3471 
3472   void GetPlugin(ProcessLaunchInfo &launch_info) {
3473     launch_info.SetProcessPluginName(m_plugin_field->GetPluginName());
3474   }
3475 
3476   void GetArch(ProcessLaunchInfo &launch_info) {
3477     if (!m_arch_field->IsSpecified())
3478       return;
3479 
3480     TargetSP target_sp = m_debugger.GetSelectedTarget();
3481     PlatformSP platform_sp =
3482         target_sp ? target_sp->GetPlatform() : PlatformSP();
3483     launch_info.GetArchitecture() = Platform::GetAugmentedArchSpec(
3484         platform_sp.get(), m_arch_field->GetArchString());
3485   }
3486 
3487   void GetShell(ProcessLaunchInfo &launch_info) {
3488     if (!m_shell_field->IsSpecified())
3489       return;
3490 
3491     launch_info.SetShell(m_shell_field->GetResolvedFileSpec());
3492     launch_info.SetShellExpandArguments(
3493         m_expand_shell_arguments_field->GetBoolean());
3494   }
3495 
3496   void GetStandardIO(ProcessLaunchInfo &launch_info) {
3497     if (m_disable_standard_io_field->GetBoolean()) {
3498       launch_info.GetFlags().Set(eLaunchFlagDisableSTDIO);
3499       return;
3500     }
3501 
3502     FileAction action;
3503     if (m_standard_input_field->IsSpecified()) {
3504       action.Open(STDIN_FILENO, m_standard_input_field->GetFileSpec(), true,
3505                   false);
3506       launch_info.AppendFileAction(action);
3507     }
3508     if (m_standard_output_field->IsSpecified()) {
3509       action.Open(STDOUT_FILENO, m_standard_output_field->GetFileSpec(), false,
3510                   true);
3511       launch_info.AppendFileAction(action);
3512     }
3513     if (m_standard_error_field->IsSpecified()) {
3514       action.Open(STDERR_FILENO, m_standard_error_field->GetFileSpec(), false,
3515                   true);
3516       launch_info.AppendFileAction(action);
3517     }
3518   }
3519 
3520   void GetInheritTCC(ProcessLaunchInfo &launch_info) {
3521     if (m_debugger.GetSelectedTarget()->GetInheritTCC())
3522       launch_info.GetFlags().Set(eLaunchFlagInheritTCCFromParent);
3523   }
3524 
3525   ProcessLaunchInfo GetLaunchInfo() {
3526     ProcessLaunchInfo launch_info;
3527 
3528     GetExecutableSettings(launch_info);
3529     GetArguments(launch_info);
3530     GetEnvironment(launch_info);
3531     GetWorkingDirectory(launch_info);
3532     GetStopAtEntry(launch_info);
3533     GetDetachOnError(launch_info);
3534     GetDisableASLR(launch_info);
3535     GetPlugin(launch_info);
3536     GetArch(launch_info);
3537     GetShell(launch_info);
3538     GetStandardIO(launch_info);
3539     GetInheritTCC(launch_info);
3540 
3541     return launch_info;
3542   }
3543 
3544   bool StopRunningProcess() {
3545     ExecutionContext exe_ctx =
3546         m_debugger.GetCommandInterpreter().GetExecutionContext();
3547 
3548     if (!exe_ctx.HasProcessScope())
3549       return false;
3550 
3551     Process *process = exe_ctx.GetProcessPtr();
3552     if (!(process && process->IsAlive()))
3553       return false;
3554 
3555     FormDelegateSP form_delegate_sp =
3556         FormDelegateSP(new DetachOrKillProcessFormDelegate(process));
3557     Rect bounds = m_main_window_sp->GetCenteredRect(85, 8);
3558     WindowSP form_window_sp = m_main_window_sp->CreateSubWindow(
3559         form_delegate_sp->GetName().c_str(), bounds, true);
3560     WindowDelegateSP window_delegate_sp =
3561         WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
3562     form_window_sp->SetDelegate(window_delegate_sp);
3563 
3564     return true;
3565   }
3566 
3567   Target *GetTarget() {
3568     Target *target = m_debugger.GetSelectedTarget().get();
3569 
3570     if (target == nullptr) {
3571       SetError("No target exists!");
3572       return nullptr;
3573     }
3574 
3575     ModuleSP exe_module_sp = target->GetExecutableModule();
3576 
3577     if (exe_module_sp == nullptr) {
3578       SetError("No executable in target!");
3579       return nullptr;
3580     }
3581 
3582     return target;
3583   }
3584 
3585   void Launch(Window &window) {
3586     ClearError();
3587 
3588     bool all_fields_are_valid = CheckFieldsValidity();
3589     if (!all_fields_are_valid)
3590       return;
3591 
3592     bool process_is_running = StopRunningProcess();
3593     if (process_is_running)
3594       return;
3595 
3596     Target *target = GetTarget();
3597     if (HasError())
3598       return;
3599 
3600     StreamString stream;
3601     ProcessLaunchInfo launch_info = GetLaunchInfo();
3602     Status status = target->Launch(launch_info, &stream);
3603 
3604     if (status.Fail()) {
3605       SetError(status.AsCString());
3606       return;
3607     }
3608 
3609     ProcessSP process_sp(target->GetProcessSP());
3610     if (!process_sp) {
3611       SetError("Launched successfully but target has no process!");
3612       return;
3613     }
3614 
3615     window.GetParent()->RemoveSubWindow(&window);
3616   }
3617 
3618 protected:
3619   Debugger &m_debugger;
3620   WindowSP m_main_window_sp;
3621 
3622   ArgumentsFieldDelegate *m_arguments_field;
3623   EnvironmentVariableListFieldDelegate *m_target_environment_field;
3624   DirectoryFieldDelegate *m_working_directory_field;
3625 
3626   BooleanFieldDelegate *m_show_advanced_field;
3627 
3628   BooleanFieldDelegate *m_stop_at_entry_field;
3629   BooleanFieldDelegate *m_detach_on_error_field;
3630   BooleanFieldDelegate *m_disable_aslr_field;
3631   ProcessPluginFieldDelegate *m_plugin_field;
3632   ArchFieldDelegate *m_arch_field;
3633   FileFieldDelegate *m_shell_field;
3634   BooleanFieldDelegate *m_expand_shell_arguments_field;
3635   BooleanFieldDelegate *m_disable_standard_io_field;
3636   FileFieldDelegate *m_standard_input_field;
3637   FileFieldDelegate *m_standard_output_field;
3638   FileFieldDelegate *m_standard_error_field;
3639 
3640   BooleanFieldDelegate *m_show_inherited_environment_field;
3641   EnvironmentVariableListFieldDelegate *m_inherited_environment_field;
3642 };
3643 
3644 class MenuDelegate {
3645 public:
3646   virtual ~MenuDelegate() = default;
3647 
3648   virtual MenuActionResult MenuDelegateAction(Menu &menu) = 0;
3649 };
3650 
3651 class Menu : public WindowDelegate {
3652 public:
3653   enum class Type { Invalid, Bar, Item, Separator };
3654 
3655   // Menubar or separator constructor
3656   Menu(Type type);
3657 
3658   // Menuitem constructor
3659   Menu(const char *name, const char *key_name, int key_value,
3660        uint64_t identifier);
3661 
3662   ~Menu() override = default;
3663 
3664   const MenuDelegateSP &GetDelegate() const { return m_delegate_sp; }
3665 
3666   void SetDelegate(const MenuDelegateSP &delegate_sp) {
3667     m_delegate_sp = delegate_sp;
3668   }
3669 
3670   void RecalculateNameLengths();
3671 
3672   void AddSubmenu(const MenuSP &menu_sp);
3673 
3674   int DrawAndRunMenu(Window &window);
3675 
3676   void DrawMenuTitle(Window &window, bool highlight);
3677 
3678   bool WindowDelegateDraw(Window &window, bool force) override;
3679 
3680   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override;
3681 
3682   MenuActionResult ActionPrivate(Menu &menu) {
3683     MenuActionResult result = MenuActionResult::NotHandled;
3684     if (m_delegate_sp) {
3685       result = m_delegate_sp->MenuDelegateAction(menu);
3686       if (result != MenuActionResult::NotHandled)
3687         return result;
3688     } else if (m_parent) {
3689       result = m_parent->ActionPrivate(menu);
3690       if (result != MenuActionResult::NotHandled)
3691         return result;
3692     }
3693     return m_canned_result;
3694   }
3695 
3696   MenuActionResult Action() {
3697     // Call the recursive action so it can try to handle it with the menu
3698     // delegate, and if not, try our parent menu
3699     return ActionPrivate(*this);
3700   }
3701 
3702   void SetCannedResult(MenuActionResult result) { m_canned_result = result; }
3703 
3704   Menus &GetSubmenus() { return m_submenus; }
3705 
3706   const Menus &GetSubmenus() const { return m_submenus; }
3707 
3708   int GetSelectedSubmenuIndex() const { return m_selected; }
3709 
3710   void SetSelectedSubmenuIndex(int idx) { m_selected = idx; }
3711 
3712   Type GetType() const { return m_type; }
3713 
3714   int GetStartingColumn() const { return m_start_col; }
3715 
3716   void SetStartingColumn(int col) { m_start_col = col; }
3717 
3718   int GetKeyValue() const { return m_key_value; }
3719 
3720   std::string &GetName() { return m_name; }
3721 
3722   int GetDrawWidth() const {
3723     return m_max_submenu_name_length + m_max_submenu_key_name_length + 8;
3724   }
3725 
3726   uint64_t GetIdentifier() const { return m_identifier; }
3727 
3728   void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
3729 
3730 protected:
3731   std::string m_name;
3732   std::string m_key_name;
3733   uint64_t m_identifier;
3734   Type m_type;
3735   int m_key_value;
3736   int m_start_col;
3737   int m_max_submenu_name_length;
3738   int m_max_submenu_key_name_length;
3739   int m_selected;
3740   Menu *m_parent;
3741   Menus m_submenus;
3742   WindowSP m_menu_window_sp;
3743   MenuActionResult m_canned_result;
3744   MenuDelegateSP m_delegate_sp;
3745 };
3746 
3747 // Menubar or separator constructor
3748 Menu::Menu(Type type)
3749     : m_name(), m_key_name(), m_identifier(0), m_type(type), m_key_value(0),
3750       m_start_col(0), m_max_submenu_name_length(0),
3751       m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr),
3752       m_submenus(), m_canned_result(MenuActionResult::NotHandled),
3753       m_delegate_sp() {}
3754 
3755 // Menuitem constructor
3756 Menu::Menu(const char *name, const char *key_name, int key_value,
3757            uint64_t identifier)
3758     : m_name(), m_key_name(), m_identifier(identifier), m_type(Type::Invalid),
3759       m_key_value(key_value), m_start_col(0), m_max_submenu_name_length(0),
3760       m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr),
3761       m_submenus(), m_canned_result(MenuActionResult::NotHandled),
3762       m_delegate_sp() {
3763   if (name && name[0]) {
3764     m_name = name;
3765     m_type = Type::Item;
3766     if (key_name && key_name[0])
3767       m_key_name = key_name;
3768   } else {
3769     m_type = Type::Separator;
3770   }
3771 }
3772 
3773 void Menu::RecalculateNameLengths() {
3774   m_max_submenu_name_length = 0;
3775   m_max_submenu_key_name_length = 0;
3776   Menus &submenus = GetSubmenus();
3777   const size_t num_submenus = submenus.size();
3778   for (size_t i = 0; i < num_submenus; ++i) {
3779     Menu *submenu = submenus[i].get();
3780     if (static_cast<size_t>(m_max_submenu_name_length) < submenu->m_name.size())
3781       m_max_submenu_name_length = submenu->m_name.size();
3782     if (static_cast<size_t>(m_max_submenu_key_name_length) <
3783         submenu->m_key_name.size())
3784       m_max_submenu_key_name_length = submenu->m_key_name.size();
3785   }
3786 }
3787 
3788 void Menu::AddSubmenu(const MenuSP &menu_sp) {
3789   menu_sp->m_parent = this;
3790   if (static_cast<size_t>(m_max_submenu_name_length) < menu_sp->m_name.size())
3791     m_max_submenu_name_length = menu_sp->m_name.size();
3792   if (static_cast<size_t>(m_max_submenu_key_name_length) <
3793       menu_sp->m_key_name.size())
3794     m_max_submenu_key_name_length = menu_sp->m_key_name.size();
3795   m_submenus.push_back(menu_sp);
3796 }
3797 
3798 void Menu::DrawMenuTitle(Window &window, bool highlight) {
3799   if (m_type == Type::Separator) {
3800     window.MoveCursor(0, window.GetCursorY());
3801     window.PutChar(ACS_LTEE);
3802     int width = window.GetWidth();
3803     if (width > 2) {
3804       width -= 2;
3805       for (int i = 0; i < width; ++i)
3806         window.PutChar(ACS_HLINE);
3807     }
3808     window.PutChar(ACS_RTEE);
3809   } else {
3810     const int shortcut_key = m_key_value;
3811     bool underlined_shortcut = false;
3812     const attr_t highlight_attr = A_REVERSE;
3813     if (highlight)
3814       window.AttributeOn(highlight_attr);
3815     if (llvm::isPrint(shortcut_key)) {
3816       size_t lower_pos = m_name.find(tolower(shortcut_key));
3817       size_t upper_pos = m_name.find(toupper(shortcut_key));
3818       const char *name = m_name.c_str();
3819       size_t pos = std::min<size_t>(lower_pos, upper_pos);
3820       if (pos != std::string::npos) {
3821         underlined_shortcut = true;
3822         if (pos > 0) {
3823           window.PutCString(name, pos);
3824           name += pos;
3825         }
3826         const attr_t shortcut_attr = A_UNDERLINE | A_BOLD;
3827         window.AttributeOn(shortcut_attr);
3828         window.PutChar(name[0]);
3829         window.AttributeOff(shortcut_attr);
3830         name++;
3831         if (name[0])
3832           window.PutCString(name);
3833       }
3834     }
3835 
3836     if (!underlined_shortcut) {
3837       window.PutCString(m_name.c_str());
3838     }
3839 
3840     if (highlight)
3841       window.AttributeOff(highlight_attr);
3842 
3843     if (m_key_name.empty()) {
3844       if (!underlined_shortcut && llvm::isPrint(m_key_value)) {
3845         window.AttributeOn(COLOR_PAIR(MagentaOnWhite));
3846         window.Printf(" (%c)", m_key_value);
3847         window.AttributeOff(COLOR_PAIR(MagentaOnWhite));
3848       }
3849     } else {
3850       window.AttributeOn(COLOR_PAIR(MagentaOnWhite));
3851       window.Printf(" (%s)", m_key_name.c_str());
3852       window.AttributeOff(COLOR_PAIR(MagentaOnWhite));
3853     }
3854   }
3855 }
3856 
3857 bool Menu::WindowDelegateDraw(Window &window, bool force) {
3858   Menus &submenus = GetSubmenus();
3859   const size_t num_submenus = submenus.size();
3860   const int selected_idx = GetSelectedSubmenuIndex();
3861   Menu::Type menu_type = GetType();
3862   switch (menu_type) {
3863   case Menu::Type::Bar: {
3864     window.SetBackground(BlackOnWhite);
3865     window.MoveCursor(0, 0);
3866     for (size_t i = 0; i < num_submenus; ++i) {
3867       Menu *menu = submenus[i].get();
3868       if (i > 0)
3869         window.PutChar(' ');
3870       menu->SetStartingColumn(window.GetCursorX());
3871       window.PutCString("| ");
3872       menu->DrawMenuTitle(window, false);
3873     }
3874     window.PutCString(" |");
3875   } break;
3876 
3877   case Menu::Type::Item: {
3878     int y = 1;
3879     int x = 3;
3880     // Draw the menu
3881     int cursor_x = 0;
3882     int cursor_y = 0;
3883     window.Erase();
3884     window.SetBackground(BlackOnWhite);
3885     window.Box();
3886     for (size_t i = 0; i < num_submenus; ++i) {
3887       const bool is_selected = (i == static_cast<size_t>(selected_idx));
3888       window.MoveCursor(x, y + i);
3889       if (is_selected) {
3890         // Remember where we want the cursor to be
3891         cursor_x = x - 1;
3892         cursor_y = y + i;
3893       }
3894       submenus[i]->DrawMenuTitle(window, is_selected);
3895     }
3896     window.MoveCursor(cursor_x, cursor_y);
3897   } break;
3898 
3899   default:
3900   case Menu::Type::Separator:
3901     break;
3902   }
3903   return true; // Drawing handled...
3904 }
3905 
3906 HandleCharResult Menu::WindowDelegateHandleChar(Window &window, int key) {
3907   HandleCharResult result = eKeyNotHandled;
3908 
3909   Menus &submenus = GetSubmenus();
3910   const size_t num_submenus = submenus.size();
3911   const int selected_idx = GetSelectedSubmenuIndex();
3912   Menu::Type menu_type = GetType();
3913   if (menu_type == Menu::Type::Bar) {
3914     MenuSP run_menu_sp;
3915     switch (key) {
3916     case KEY_DOWN:
3917     case KEY_UP:
3918       // Show last menu or first menu
3919       if (selected_idx < static_cast<int>(num_submenus))
3920         run_menu_sp = submenus[selected_idx];
3921       else if (!submenus.empty())
3922         run_menu_sp = submenus.front();
3923       result = eKeyHandled;
3924       break;
3925 
3926     case KEY_RIGHT:
3927       ++m_selected;
3928       if (m_selected >= static_cast<int>(num_submenus))
3929         m_selected = 0;
3930       if (m_selected < static_cast<int>(num_submenus))
3931         run_menu_sp = submenus[m_selected];
3932       else if (!submenus.empty())
3933         run_menu_sp = submenus.front();
3934       result = eKeyHandled;
3935       break;
3936 
3937     case KEY_LEFT:
3938       --m_selected;
3939       if (m_selected < 0)
3940         m_selected = num_submenus - 1;
3941       if (m_selected < static_cast<int>(num_submenus))
3942         run_menu_sp = submenus[m_selected];
3943       else if (!submenus.empty())
3944         run_menu_sp = submenus.front();
3945       result = eKeyHandled;
3946       break;
3947 
3948     default:
3949       for (size_t i = 0; i < num_submenus; ++i) {
3950         if (submenus[i]->GetKeyValue() == key) {
3951           SetSelectedSubmenuIndex(i);
3952           run_menu_sp = submenus[i];
3953           result = eKeyHandled;
3954           break;
3955         }
3956       }
3957       break;
3958     }
3959 
3960     if (run_menu_sp) {
3961       // Run the action on this menu in case we need to populate the menu with
3962       // dynamic content and also in case check marks, and any other menu
3963       // decorations need to be calculated
3964       if (run_menu_sp->Action() == MenuActionResult::Quit)
3965         return eQuitApplication;
3966 
3967       Rect menu_bounds;
3968       menu_bounds.origin.x = run_menu_sp->GetStartingColumn();
3969       menu_bounds.origin.y = 1;
3970       menu_bounds.size.width = run_menu_sp->GetDrawWidth();
3971       menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2;
3972       if (m_menu_window_sp)
3973         window.GetParent()->RemoveSubWindow(m_menu_window_sp.get());
3974 
3975       m_menu_window_sp = window.GetParent()->CreateSubWindow(
3976           run_menu_sp->GetName().c_str(), menu_bounds, true);
3977       m_menu_window_sp->SetDelegate(run_menu_sp);
3978     }
3979   } else if (menu_type == Menu::Type::Item) {
3980     switch (key) {
3981     case KEY_DOWN:
3982       if (m_submenus.size() > 1) {
3983         const int start_select = m_selected;
3984         while (++m_selected != start_select) {
3985           if (static_cast<size_t>(m_selected) >= num_submenus)
3986             m_selected = 0;
3987           if (m_submenus[m_selected]->GetType() == Type::Separator)
3988             continue;
3989           else
3990             break;
3991         }
3992         return eKeyHandled;
3993       }
3994       break;
3995 
3996     case KEY_UP:
3997       if (m_submenus.size() > 1) {
3998         const int start_select = m_selected;
3999         while (--m_selected != start_select) {
4000           if (m_selected < static_cast<int>(0))
4001             m_selected = num_submenus - 1;
4002           if (m_submenus[m_selected]->GetType() == Type::Separator)
4003             continue;
4004           else
4005             break;
4006         }
4007         return eKeyHandled;
4008       }
4009       break;
4010 
4011     case KEY_RETURN:
4012       if (static_cast<size_t>(selected_idx) < num_submenus) {
4013         if (submenus[selected_idx]->Action() == MenuActionResult::Quit)
4014           return eQuitApplication;
4015         window.GetParent()->RemoveSubWindow(&window);
4016         return eKeyHandled;
4017       }
4018       break;
4019 
4020     case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in
4021                      // case other chars are entered for escaped sequences
4022       window.GetParent()->RemoveSubWindow(&window);
4023       return eKeyHandled;
4024 
4025     default:
4026       for (size_t i = 0; i < num_submenus; ++i) {
4027         Menu *menu = submenus[i].get();
4028         if (menu->GetKeyValue() == key) {
4029           SetSelectedSubmenuIndex(i);
4030           window.GetParent()->RemoveSubWindow(&window);
4031           if (menu->Action() == MenuActionResult::Quit)
4032             return eQuitApplication;
4033           return eKeyHandled;
4034         }
4035       }
4036       break;
4037     }
4038   } else if (menu_type == Menu::Type::Separator) {
4039   }
4040   return result;
4041 }
4042 
4043 class Application {
4044 public:
4045   Application(FILE *in, FILE *out)
4046       : m_window_sp(), m_screen(nullptr), m_in(in), m_out(out) {}
4047 
4048   ~Application() {
4049     m_window_delegates.clear();
4050     m_window_sp.reset();
4051     if (m_screen) {
4052       ::delscreen(m_screen);
4053       m_screen = nullptr;
4054     }
4055   }
4056 
4057   void Initialize() {
4058     m_screen = ::newterm(nullptr, m_out, m_in);
4059     ::start_color();
4060     ::curs_set(0);
4061     ::noecho();
4062     ::keypad(stdscr, TRUE);
4063   }
4064 
4065   void Terminate() { ::endwin(); }
4066 
4067   void Run(Debugger &debugger) {
4068     bool done = false;
4069     int delay_in_tenths_of_a_second = 1;
4070 
4071     // Alas the threading model in curses is a bit lame so we need to resort
4072     // to polling every 0.5 seconds. We could poll for stdin ourselves and
4073     // then pass the keys down but then we need to translate all of the escape
4074     // sequences ourselves. So we resort to polling for input because we need
4075     // to receive async process events while in this loop.
4076 
4077     halfdelay(delay_in_tenths_of_a_second); // Poll using some number of
4078                                             // tenths of seconds seconds when
4079                                             // calling Window::GetChar()
4080 
4081     ListenerSP listener_sp(
4082         Listener::MakeListener("lldb.IOHandler.curses.Application"));
4083     ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass());
4084     debugger.EnableForwardEvents(listener_sp);
4085 
4086     m_update_screen = true;
4087 #if defined(__APPLE__)
4088     std::deque<int> escape_chars;
4089 #endif
4090 
4091     while (!done) {
4092       if (m_update_screen) {
4093         m_window_sp->Draw(false);
4094         // All windows should be calling Window::DeferredRefresh() instead of
4095         // Window::Refresh() so we can do a single update and avoid any screen
4096         // blinking
4097         update_panels();
4098 
4099         // Cursor hiding isn't working on MacOSX, so hide it in the top left
4100         // corner
4101         m_window_sp->MoveCursor(0, 0);
4102 
4103         doupdate();
4104         m_update_screen = false;
4105       }
4106 
4107 #if defined(__APPLE__)
4108       // Terminal.app doesn't map its function keys correctly, F1-F4 default
4109       // to: \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if
4110       // possible
4111       int ch;
4112       if (escape_chars.empty())
4113         ch = m_window_sp->GetChar();
4114       else {
4115         ch = escape_chars.front();
4116         escape_chars.pop_front();
4117       }
4118       if (ch == KEY_ESCAPE) {
4119         int ch2 = m_window_sp->GetChar();
4120         if (ch2 == 'O') {
4121           int ch3 = m_window_sp->GetChar();
4122           switch (ch3) {
4123           case 'P':
4124             ch = KEY_F(1);
4125             break;
4126           case 'Q':
4127             ch = KEY_F(2);
4128             break;
4129           case 'R':
4130             ch = KEY_F(3);
4131             break;
4132           case 'S':
4133             ch = KEY_F(4);
4134             break;
4135           default:
4136             escape_chars.push_back(ch2);
4137             if (ch3 != -1)
4138               escape_chars.push_back(ch3);
4139             break;
4140           }
4141         } else if (ch2 != -1)
4142           escape_chars.push_back(ch2);
4143       }
4144 #else
4145       int ch = m_window_sp->GetChar();
4146 
4147 #endif
4148       if (ch == -1) {
4149         if (feof(m_in) || ferror(m_in)) {
4150           done = true;
4151         } else {
4152           // Just a timeout from using halfdelay(), check for events
4153           EventSP event_sp;
4154           while (listener_sp->PeekAtNextEvent()) {
4155             listener_sp->GetEvent(event_sp, std::chrono::seconds(0));
4156 
4157             if (event_sp) {
4158               Broadcaster *broadcaster = event_sp->GetBroadcaster();
4159               if (broadcaster) {
4160                 // uint32_t event_type = event_sp->GetType();
4161                 ConstString broadcaster_class(
4162                     broadcaster->GetBroadcasterClass());
4163                 if (broadcaster_class == broadcaster_class_process) {
4164                   m_update_screen = true;
4165                   continue; // Don't get any key, just update our view
4166                 }
4167               }
4168             }
4169           }
4170         }
4171       } else {
4172         HandleCharResult key_result = m_window_sp->HandleChar(ch);
4173         switch (key_result) {
4174         case eKeyHandled:
4175           m_update_screen = true;
4176           break;
4177         case eKeyNotHandled:
4178           if (ch == 12) { // Ctrl+L, force full redraw
4179             redrawwin(m_window_sp->get());
4180             m_update_screen = true;
4181           }
4182           break;
4183         case eQuitApplication:
4184           done = true;
4185           break;
4186         }
4187       }
4188     }
4189 
4190     debugger.CancelForwardEvents(listener_sp);
4191   }
4192 
4193   WindowSP &GetMainWindow() {
4194     if (!m_window_sp)
4195       m_window_sp = std::make_shared<Window>("main", stdscr, false);
4196     return m_window_sp;
4197   }
4198 
4199   void TerminalSizeChanged() {
4200     ::endwin();
4201     ::refresh();
4202     Rect content_bounds = m_window_sp->GetFrame();
4203     m_window_sp->SetBounds(content_bounds);
4204     if (WindowSP menubar_window_sp = m_window_sp->FindSubWindow("Menubar"))
4205       menubar_window_sp->SetBounds(content_bounds.MakeMenuBar());
4206     if (WindowSP status_window_sp = m_window_sp->FindSubWindow("Status"))
4207       status_window_sp->SetBounds(content_bounds.MakeStatusBar());
4208 
4209     WindowSP source_window_sp = m_window_sp->FindSubWindow("Source");
4210     WindowSP variables_window_sp = m_window_sp->FindSubWindow("Variables");
4211     WindowSP registers_window_sp = m_window_sp->FindSubWindow("Registers");
4212     WindowSP threads_window_sp = m_window_sp->FindSubWindow("Threads");
4213 
4214     Rect threads_bounds;
4215     Rect source_variables_bounds;
4216     content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds,
4217                                            threads_bounds);
4218     if (threads_window_sp)
4219       threads_window_sp->SetBounds(threads_bounds);
4220     else
4221       source_variables_bounds = content_bounds;
4222 
4223     Rect source_bounds;
4224     Rect variables_registers_bounds;
4225     source_variables_bounds.HorizontalSplitPercentage(
4226         0.70, source_bounds, variables_registers_bounds);
4227     if (variables_window_sp || registers_window_sp) {
4228       if (variables_window_sp && registers_window_sp) {
4229         Rect variables_bounds;
4230         Rect registers_bounds;
4231         variables_registers_bounds.VerticalSplitPercentage(
4232             0.50, variables_bounds, registers_bounds);
4233         variables_window_sp->SetBounds(variables_bounds);
4234         registers_window_sp->SetBounds(registers_bounds);
4235       } else if (variables_window_sp) {
4236         variables_window_sp->SetBounds(variables_registers_bounds);
4237       } else {
4238         registers_window_sp->SetBounds(variables_registers_bounds);
4239       }
4240     } else {
4241       source_bounds = source_variables_bounds;
4242     }
4243 
4244     source_window_sp->SetBounds(source_bounds);
4245 
4246     touchwin(stdscr);
4247     redrawwin(m_window_sp->get());
4248     m_update_screen = true;
4249   }
4250 
4251 protected:
4252   WindowSP m_window_sp;
4253   WindowDelegates m_window_delegates;
4254   SCREEN *m_screen;
4255   FILE *m_in;
4256   FILE *m_out;
4257   bool m_update_screen = false;
4258 };
4259 
4260 } // namespace curses
4261 
4262 using namespace curses;
4263 
4264 struct Row {
4265   ValueObjectUpdater value;
4266   Row *parent;
4267   // The process stop ID when the children were calculated.
4268   uint32_t children_stop_id = 0;
4269   int row_idx = 0;
4270   int x = 1;
4271   int y = 1;
4272   bool might_have_children;
4273   bool expanded = false;
4274   bool calculated_children = false;
4275   std::vector<Row> children;
4276 
4277   Row(const ValueObjectSP &v, Row *p)
4278       : value(v), parent(p),
4279         might_have_children(v ? v->MightHaveChildren() : false) {}
4280 
4281   size_t GetDepth() const {
4282     if (parent)
4283       return 1 + parent->GetDepth();
4284     return 0;
4285   }
4286 
4287   void Expand() { expanded = true; }
4288 
4289   std::vector<Row> &GetChildren() {
4290     ProcessSP process_sp = value.GetProcessSP();
4291     auto stop_id = process_sp->GetStopID();
4292     if (process_sp && stop_id != children_stop_id) {
4293       children_stop_id = stop_id;
4294       calculated_children = false;
4295     }
4296     if (!calculated_children) {
4297       children.clear();
4298       calculated_children = true;
4299       ValueObjectSP valobj = value.GetSP();
4300       if (valobj) {
4301         const size_t num_children = valobj->GetNumChildren();
4302         for (size_t i = 0; i < num_children; ++i) {
4303           children.push_back(Row(valobj->GetChildAtIndex(i, true), this));
4304         }
4305       }
4306     }
4307     return children;
4308   }
4309 
4310   void Unexpand() {
4311     expanded = false;
4312     calculated_children = false;
4313     children.clear();
4314   }
4315 
4316   void DrawTree(Window &window) {
4317     if (parent)
4318       parent->DrawTreeForChild(window, this, 0);
4319 
4320     if (might_have_children) {
4321       // It we can get UTF8 characters to work we should try to use the
4322       // "symbol" UTF8 string below
4323       //            const char *symbol = "";
4324       //            if (row.expanded)
4325       //                symbol = "\xe2\x96\xbd ";
4326       //            else
4327       //                symbol = "\xe2\x96\xb7 ";
4328       //            window.PutCString (symbol);
4329 
4330       // The ACS_DARROW and ACS_RARROW don't look very nice they are just a 'v'
4331       // or '>' character...
4332       //            if (expanded)
4333       //                window.PutChar (ACS_DARROW);
4334       //            else
4335       //                window.PutChar (ACS_RARROW);
4336       // Since we can't find any good looking right arrow/down arrow symbols,
4337       // just use a diamond...
4338       window.PutChar(ACS_DIAMOND);
4339       window.PutChar(ACS_HLINE);
4340     }
4341   }
4342 
4343   void DrawTreeForChild(Window &window, Row *child, uint32_t reverse_depth) {
4344     if (parent)
4345       parent->DrawTreeForChild(window, this, reverse_depth + 1);
4346 
4347     if (&GetChildren().back() == child) {
4348       // Last child
4349       if (reverse_depth == 0) {
4350         window.PutChar(ACS_LLCORNER);
4351         window.PutChar(ACS_HLINE);
4352       } else {
4353         window.PutChar(' ');
4354         window.PutChar(' ');
4355       }
4356     } else {
4357       if (reverse_depth == 0) {
4358         window.PutChar(ACS_LTEE);
4359         window.PutChar(ACS_HLINE);
4360       } else {
4361         window.PutChar(ACS_VLINE);
4362         window.PutChar(' ');
4363       }
4364     }
4365   }
4366 };
4367 
4368 struct DisplayOptions {
4369   bool show_types;
4370 };
4371 
4372 class TreeItem;
4373 
4374 class TreeDelegate {
4375 public:
4376   TreeDelegate() = default;
4377   virtual ~TreeDelegate() = default;
4378 
4379   virtual void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) = 0;
4380   virtual void TreeDelegateGenerateChildren(TreeItem &item) = 0;
4381   virtual void TreeDelegateUpdateSelection(TreeItem &root, int &selection_index,
4382                                            TreeItem *&selected_item) {
4383     return;
4384   }
4385   // This is invoked when a tree item is selected. If true is returned, the
4386   // views are updated.
4387   virtual bool TreeDelegateItemSelected(TreeItem &item) = 0;
4388   virtual bool TreeDelegateExpandRootByDefault() { return false; }
4389   // This is mostly useful for root tree delegates. If false is returned,
4390   // drawing will be skipped completely. This is needed, for instance, in
4391   // skipping drawing of the threads tree if there is no running process.
4392   virtual bool TreeDelegateShouldDraw() { return true; }
4393 };
4394 
4395 typedef std::shared_ptr<TreeDelegate> TreeDelegateSP;
4396 
4397 class TreeItem {
4398 public:
4399   TreeItem(TreeItem *parent, TreeDelegate &delegate, bool might_have_children)
4400       : m_parent(parent), m_delegate(delegate), m_user_data(nullptr),
4401         m_identifier(0), m_row_idx(-1), m_children(),
4402         m_might_have_children(might_have_children), m_is_expanded(false) {
4403     if (m_parent == nullptr)
4404       m_is_expanded = m_delegate.TreeDelegateExpandRootByDefault();
4405   }
4406 
4407   TreeItem &operator=(const TreeItem &rhs) {
4408     if (this != &rhs) {
4409       m_parent = rhs.m_parent;
4410       m_delegate = rhs.m_delegate;
4411       m_user_data = rhs.m_user_data;
4412       m_identifier = rhs.m_identifier;
4413       m_row_idx = rhs.m_row_idx;
4414       m_children = rhs.m_children;
4415       m_might_have_children = rhs.m_might_have_children;
4416       m_is_expanded = rhs.m_is_expanded;
4417     }
4418     return *this;
4419   }
4420 
4421   TreeItem(const TreeItem &) = default;
4422 
4423   size_t GetDepth() const {
4424     if (m_parent)
4425       return 1 + m_parent->GetDepth();
4426     return 0;
4427   }
4428 
4429   int GetRowIndex() const { return m_row_idx; }
4430 
4431   void ClearChildren() { m_children.clear(); }
4432 
4433   void Resize(size_t n, const TreeItem &t) { m_children.resize(n, t); }
4434 
4435   TreeItem &operator[](size_t i) { return m_children[i]; }
4436 
4437   void SetRowIndex(int row_idx) { m_row_idx = row_idx; }
4438 
4439   size_t GetNumChildren() {
4440     m_delegate.TreeDelegateGenerateChildren(*this);
4441     return m_children.size();
4442   }
4443 
4444   void ItemWasSelected() { m_delegate.TreeDelegateItemSelected(*this); }
4445 
4446   void CalculateRowIndexes(int &row_idx) {
4447     SetRowIndex(row_idx);
4448     ++row_idx;
4449 
4450     const bool expanded = IsExpanded();
4451 
4452     // The root item must calculate its children, or we must calculate the
4453     // number of children if the item is expanded
4454     if (m_parent == nullptr || expanded)
4455       GetNumChildren();
4456 
4457     for (auto &item : m_children) {
4458       if (expanded)
4459         item.CalculateRowIndexes(row_idx);
4460       else
4461         item.SetRowIndex(-1);
4462     }
4463   }
4464 
4465   TreeItem *GetParent() { return m_parent; }
4466 
4467   bool IsExpanded() const { return m_is_expanded; }
4468 
4469   void Expand() { m_is_expanded = true; }
4470 
4471   void Unexpand() { m_is_expanded = false; }
4472 
4473   bool Draw(Window &window, const int first_visible_row,
4474             const uint32_t selected_row_idx, int &row_idx, int &num_rows_left) {
4475     if (num_rows_left <= 0)
4476       return false;
4477 
4478     if (m_row_idx >= first_visible_row) {
4479       window.MoveCursor(2, row_idx + 1);
4480 
4481       if (m_parent)
4482         m_parent->DrawTreeForChild(window, this, 0);
4483 
4484       if (m_might_have_children) {
4485         // It we can get UTF8 characters to work we should try to use the
4486         // "symbol" UTF8 string below
4487         //            const char *symbol = "";
4488         //            if (row.expanded)
4489         //                symbol = "\xe2\x96\xbd ";
4490         //            else
4491         //                symbol = "\xe2\x96\xb7 ";
4492         //            window.PutCString (symbol);
4493 
4494         // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
4495         // 'v' or '>' character...
4496         //            if (expanded)
4497         //                window.PutChar (ACS_DARROW);
4498         //            else
4499         //                window.PutChar (ACS_RARROW);
4500         // Since we can't find any good looking right arrow/down arrow symbols,
4501         // just use a diamond...
4502         window.PutChar(ACS_DIAMOND);
4503         window.PutChar(ACS_HLINE);
4504       }
4505       bool highlight = (selected_row_idx == static_cast<size_t>(m_row_idx)) &&
4506                        window.IsActive();
4507 
4508       if (highlight)
4509         window.AttributeOn(A_REVERSE);
4510 
4511       m_delegate.TreeDelegateDrawTreeItem(*this, window);
4512 
4513       if (highlight)
4514         window.AttributeOff(A_REVERSE);
4515       ++row_idx;
4516       --num_rows_left;
4517     }
4518 
4519     if (num_rows_left <= 0)
4520       return false; // We are done drawing...
4521 
4522     if (IsExpanded()) {
4523       for (auto &item : m_children) {
4524         // If we displayed all the rows and item.Draw() returns false we are
4525         // done drawing and can exit this for loop
4526         if (!item.Draw(window, first_visible_row, selected_row_idx, row_idx,
4527                        num_rows_left))
4528           break;
4529       }
4530     }
4531     return num_rows_left >= 0; // Return true if not done drawing yet
4532   }
4533 
4534   void DrawTreeForChild(Window &window, TreeItem *child,
4535                         uint32_t reverse_depth) {
4536     if (m_parent)
4537       m_parent->DrawTreeForChild(window, this, reverse_depth + 1);
4538 
4539     if (&m_children.back() == child) {
4540       // Last child
4541       if (reverse_depth == 0) {
4542         window.PutChar(ACS_LLCORNER);
4543         window.PutChar(ACS_HLINE);
4544       } else {
4545         window.PutChar(' ');
4546         window.PutChar(' ');
4547       }
4548     } else {
4549       if (reverse_depth == 0) {
4550         window.PutChar(ACS_LTEE);
4551         window.PutChar(ACS_HLINE);
4552       } else {
4553         window.PutChar(ACS_VLINE);
4554         window.PutChar(' ');
4555       }
4556     }
4557   }
4558 
4559   TreeItem *GetItemForRowIndex(uint32_t row_idx) {
4560     if (static_cast<uint32_t>(m_row_idx) == row_idx)
4561       return this;
4562     if (m_children.empty())
4563       return nullptr;
4564     if (IsExpanded()) {
4565       for (auto &item : m_children) {
4566         TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx);
4567         if (selected_item_ptr)
4568           return selected_item_ptr;
4569       }
4570     }
4571     return nullptr;
4572   }
4573 
4574   void *GetUserData() const { return m_user_data; }
4575 
4576   void SetUserData(void *user_data) { m_user_data = user_data; }
4577 
4578   uint64_t GetIdentifier() const { return m_identifier; }
4579 
4580   void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
4581 
4582   const std::string &GetText() const { return m_text; }
4583 
4584   void SetText(const char *text) {
4585     if (text == nullptr) {
4586       m_text.clear();
4587       return;
4588     }
4589     m_text = text;
4590   }
4591 
4592   void SetMightHaveChildren(bool b) { m_might_have_children = b; }
4593 
4594 protected:
4595   TreeItem *m_parent;
4596   TreeDelegate &m_delegate;
4597   void *m_user_data;
4598   uint64_t m_identifier;
4599   std::string m_text;
4600   int m_row_idx; // Zero based visible row index, -1 if not visible or for the
4601                  // root item
4602   std::vector<TreeItem> m_children;
4603   bool m_might_have_children;
4604   bool m_is_expanded;
4605 };
4606 
4607 class TreeWindowDelegate : public WindowDelegate {
4608 public:
4609   TreeWindowDelegate(Debugger &debugger, const TreeDelegateSP &delegate_sp)
4610       : m_debugger(debugger), m_delegate_sp(delegate_sp),
4611         m_root(nullptr, *delegate_sp, true), m_selected_item(nullptr),
4612         m_num_rows(0), m_selected_row_idx(0), m_first_visible_row(0),
4613         m_min_x(0), m_min_y(0), m_max_x(0), m_max_y(0) {}
4614 
4615   int NumVisibleRows() const { return m_max_y - m_min_y; }
4616 
4617   bool WindowDelegateDraw(Window &window, bool force) override {
4618     m_min_x = 2;
4619     m_min_y = 1;
4620     m_max_x = window.GetWidth() - 1;
4621     m_max_y = window.GetHeight() - 1;
4622 
4623     window.Erase();
4624     window.DrawTitleBox(window.GetName());
4625 
4626     if (!m_delegate_sp->TreeDelegateShouldDraw()) {
4627       m_selected_item = nullptr;
4628       return true;
4629     }
4630 
4631     const int num_visible_rows = NumVisibleRows();
4632     m_num_rows = 0;
4633     m_root.CalculateRowIndexes(m_num_rows);
4634     m_delegate_sp->TreeDelegateUpdateSelection(m_root, m_selected_row_idx,
4635                                                m_selected_item);
4636 
4637     // If we unexpanded while having something selected our total number of
4638     // rows is less than the num visible rows, then make sure we show all the
4639     // rows by setting the first visible row accordingly.
4640     if (m_first_visible_row > 0 && m_num_rows < num_visible_rows)
4641       m_first_visible_row = 0;
4642 
4643     // Make sure the selected row is always visible
4644     if (m_selected_row_idx < m_first_visible_row)
4645       m_first_visible_row = m_selected_row_idx;
4646     else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
4647       m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
4648 
4649     int row_idx = 0;
4650     int num_rows_left = num_visible_rows;
4651     m_root.Draw(window, m_first_visible_row, m_selected_row_idx, row_idx,
4652                 num_rows_left);
4653     // Get the selected row
4654     m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4655 
4656     return true; // Drawing handled
4657   }
4658 
4659   const char *WindowDelegateGetHelpText() override {
4660     return "Thread window keyboard shortcuts:";
4661   }
4662 
4663   KeyHelp *WindowDelegateGetKeyHelp() override {
4664     static curses::KeyHelp g_source_view_key_help[] = {
4665         {KEY_UP, "Select previous item"},
4666         {KEY_DOWN, "Select next item"},
4667         {KEY_RIGHT, "Expand the selected item"},
4668         {KEY_LEFT,
4669          "Unexpand the selected item or select parent if not expanded"},
4670         {KEY_PPAGE, "Page up"},
4671         {KEY_NPAGE, "Page down"},
4672         {'h', "Show help dialog"},
4673         {' ', "Toggle item expansion"},
4674         {',', "Page up"},
4675         {'.', "Page down"},
4676         {'\0', nullptr}};
4677     return g_source_view_key_help;
4678   }
4679 
4680   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
4681     switch (c) {
4682     case ',':
4683     case KEY_PPAGE:
4684       // Page up key
4685       if (m_first_visible_row > 0) {
4686         if (m_first_visible_row > m_max_y)
4687           m_first_visible_row -= m_max_y;
4688         else
4689           m_first_visible_row = 0;
4690         m_selected_row_idx = m_first_visible_row;
4691         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4692         if (m_selected_item)
4693           m_selected_item->ItemWasSelected();
4694       }
4695       return eKeyHandled;
4696 
4697     case '.':
4698     case KEY_NPAGE:
4699       // Page down key
4700       if (m_num_rows > m_max_y) {
4701         if (m_first_visible_row + m_max_y < m_num_rows) {
4702           m_first_visible_row += m_max_y;
4703           m_selected_row_idx = m_first_visible_row;
4704           m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4705           if (m_selected_item)
4706             m_selected_item->ItemWasSelected();
4707         }
4708       }
4709       return eKeyHandled;
4710 
4711     case KEY_UP:
4712       if (m_selected_row_idx > 0) {
4713         --m_selected_row_idx;
4714         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4715         if (m_selected_item)
4716           m_selected_item->ItemWasSelected();
4717       }
4718       return eKeyHandled;
4719 
4720     case KEY_DOWN:
4721       if (m_selected_row_idx + 1 < m_num_rows) {
4722         ++m_selected_row_idx;
4723         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4724         if (m_selected_item)
4725           m_selected_item->ItemWasSelected();
4726       }
4727       return eKeyHandled;
4728 
4729     case KEY_RIGHT:
4730       if (m_selected_item) {
4731         if (!m_selected_item->IsExpanded())
4732           m_selected_item->Expand();
4733       }
4734       return eKeyHandled;
4735 
4736     case KEY_LEFT:
4737       if (m_selected_item) {
4738         if (m_selected_item->IsExpanded())
4739           m_selected_item->Unexpand();
4740         else if (m_selected_item->GetParent()) {
4741           m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex();
4742           m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4743           if (m_selected_item)
4744             m_selected_item->ItemWasSelected();
4745         }
4746       }
4747       return eKeyHandled;
4748 
4749     case ' ':
4750       // Toggle expansion state when SPACE is pressed
4751       if (m_selected_item) {
4752         if (m_selected_item->IsExpanded())
4753           m_selected_item->Unexpand();
4754         else
4755           m_selected_item->Expand();
4756       }
4757       return eKeyHandled;
4758 
4759     case 'h':
4760       window.CreateHelpSubwindow();
4761       return eKeyHandled;
4762 
4763     default:
4764       break;
4765     }
4766     return eKeyNotHandled;
4767   }
4768 
4769 protected:
4770   Debugger &m_debugger;
4771   TreeDelegateSP m_delegate_sp;
4772   TreeItem m_root;
4773   TreeItem *m_selected_item;
4774   int m_num_rows;
4775   int m_selected_row_idx;
4776   int m_first_visible_row;
4777   int m_min_x;
4778   int m_min_y;
4779   int m_max_x;
4780   int m_max_y;
4781 };
4782 
4783 // A tree delegate that just draws the text member of the tree item, it doesn't
4784 // have any children or actions.
4785 class TextTreeDelegate : public TreeDelegate {
4786 public:
4787   TextTreeDelegate() : TreeDelegate() {}
4788 
4789   ~TextTreeDelegate() override = default;
4790 
4791   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
4792     window.PutCStringTruncated(1, item.GetText().c_str());
4793   }
4794 
4795   void TreeDelegateGenerateChildren(TreeItem &item) override {}
4796 
4797   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
4798 };
4799 
4800 class FrameTreeDelegate : public TreeDelegate {
4801 public:
4802   FrameTreeDelegate() : TreeDelegate() {
4803     FormatEntity::Parse(
4804         "frame #${frame.index}: {${function.name}${function.pc-offset}}}",
4805         m_format);
4806   }
4807 
4808   ~FrameTreeDelegate() override = default;
4809 
4810   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
4811     Thread *thread = (Thread *)item.GetUserData();
4812     if (thread) {
4813       const uint64_t frame_idx = item.GetIdentifier();
4814       StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_idx);
4815       if (frame_sp) {
4816         StreamString strm;
4817         const SymbolContext &sc =
4818             frame_sp->GetSymbolContext(eSymbolContextEverything);
4819         ExecutionContext exe_ctx(frame_sp);
4820         if (FormatEntity::Format(m_format, strm, &sc, &exe_ctx, nullptr,
4821                                  nullptr, false, false)) {
4822           int right_pad = 1;
4823           window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
4824         }
4825       }
4826     }
4827   }
4828 
4829   void TreeDelegateGenerateChildren(TreeItem &item) override {
4830     // No children for frames yet...
4831   }
4832 
4833   bool TreeDelegateItemSelected(TreeItem &item) override {
4834     Thread *thread = (Thread *)item.GetUserData();
4835     if (thread) {
4836       thread->GetProcess()->GetThreadList().SetSelectedThreadByID(
4837           thread->GetID());
4838       const uint64_t frame_idx = item.GetIdentifier();
4839       thread->SetSelectedFrameByIndex(frame_idx);
4840       return true;
4841     }
4842     return false;
4843   }
4844 
4845 protected:
4846   FormatEntity::Entry m_format;
4847 };
4848 
4849 class ThreadTreeDelegate : public TreeDelegate {
4850 public:
4851   ThreadTreeDelegate(Debugger &debugger)
4852       : TreeDelegate(), m_debugger(debugger), m_tid(LLDB_INVALID_THREAD_ID),
4853         m_stop_id(UINT32_MAX) {
4854     FormatEntity::Parse("thread #${thread.index}: tid = ${thread.id}{, stop "
4855                         "reason = ${thread.stop-reason}}",
4856                         m_format);
4857   }
4858 
4859   ~ThreadTreeDelegate() override = default;
4860 
4861   ProcessSP GetProcess() {
4862     return m_debugger.GetCommandInterpreter()
4863         .GetExecutionContext()
4864         .GetProcessSP();
4865   }
4866 
4867   ThreadSP GetThread(const TreeItem &item) {
4868     ProcessSP process_sp = GetProcess();
4869     if (process_sp)
4870       return process_sp->GetThreadList().FindThreadByID(item.GetIdentifier());
4871     return ThreadSP();
4872   }
4873 
4874   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
4875     ThreadSP thread_sp = GetThread(item);
4876     if (thread_sp) {
4877       StreamString strm;
4878       ExecutionContext exe_ctx(thread_sp);
4879       if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
4880                                nullptr, false, false)) {
4881         int right_pad = 1;
4882         window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
4883       }
4884     }
4885   }
4886 
4887   void TreeDelegateGenerateChildren(TreeItem &item) override {
4888     ProcessSP process_sp = GetProcess();
4889     if (process_sp && process_sp->IsAlive()) {
4890       StateType state = process_sp->GetState();
4891       if (StateIsStoppedState(state, true)) {
4892         ThreadSP thread_sp = GetThread(item);
4893         if (thread_sp) {
4894           if (m_stop_id == process_sp->GetStopID() &&
4895               thread_sp->GetID() == m_tid)
4896             return; // Children are already up to date
4897           if (!m_frame_delegate_sp) {
4898             // Always expand the thread item the first time we show it
4899             m_frame_delegate_sp = std::make_shared<FrameTreeDelegate>();
4900           }
4901 
4902           m_stop_id = process_sp->GetStopID();
4903           m_tid = thread_sp->GetID();
4904 
4905           TreeItem t(&item, *m_frame_delegate_sp, false);
4906           size_t num_frames = thread_sp->GetStackFrameCount();
4907           item.Resize(num_frames, t);
4908           for (size_t i = 0; i < num_frames; ++i) {
4909             item[i].SetUserData(thread_sp.get());
4910             item[i].SetIdentifier(i);
4911           }
4912         }
4913         return;
4914       }
4915     }
4916     item.ClearChildren();
4917   }
4918 
4919   bool TreeDelegateItemSelected(TreeItem &item) override {
4920     ProcessSP process_sp = GetProcess();
4921     if (process_sp && process_sp->IsAlive()) {
4922       StateType state = process_sp->GetState();
4923       if (StateIsStoppedState(state, true)) {
4924         ThreadSP thread_sp = GetThread(item);
4925         if (thread_sp) {
4926           ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList();
4927           std::lock_guard<std::recursive_mutex> guard(thread_list.GetMutex());
4928           ThreadSP selected_thread_sp = thread_list.GetSelectedThread();
4929           if (selected_thread_sp->GetID() != thread_sp->GetID()) {
4930             thread_list.SetSelectedThreadByID(thread_sp->GetID());
4931             return true;
4932           }
4933         }
4934       }
4935     }
4936     return false;
4937   }
4938 
4939 protected:
4940   Debugger &m_debugger;
4941   std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp;
4942   lldb::user_id_t m_tid;
4943   uint32_t m_stop_id;
4944   FormatEntity::Entry m_format;
4945 };
4946 
4947 class ThreadsTreeDelegate : public TreeDelegate {
4948 public:
4949   ThreadsTreeDelegate(Debugger &debugger)
4950       : TreeDelegate(), m_thread_delegate_sp(), m_debugger(debugger),
4951         m_stop_id(UINT32_MAX), m_update_selection(false) {
4952     FormatEntity::Parse("process ${process.id}{, name = ${process.name}}",
4953                         m_format);
4954   }
4955 
4956   ~ThreadsTreeDelegate() override = default;
4957 
4958   ProcessSP GetProcess() {
4959     return m_debugger.GetCommandInterpreter()
4960         .GetExecutionContext()
4961         .GetProcessSP();
4962   }
4963 
4964   bool TreeDelegateShouldDraw() override {
4965     ProcessSP process = GetProcess();
4966     if (!process)
4967       return false;
4968 
4969     if (StateIsRunningState(process->GetState()))
4970       return false;
4971 
4972     return true;
4973   }
4974 
4975   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
4976     ProcessSP process_sp = GetProcess();
4977     if (process_sp && process_sp->IsAlive()) {
4978       StreamString strm;
4979       ExecutionContext exe_ctx(process_sp);
4980       if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
4981                                nullptr, false, false)) {
4982         int right_pad = 1;
4983         window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
4984       }
4985     }
4986   }
4987 
4988   void TreeDelegateGenerateChildren(TreeItem &item) override {
4989     ProcessSP process_sp = GetProcess();
4990     m_update_selection = false;
4991     if (process_sp && process_sp->IsAlive()) {
4992       StateType state = process_sp->GetState();
4993       if (StateIsStoppedState(state, true)) {
4994         const uint32_t stop_id = process_sp->GetStopID();
4995         if (m_stop_id == stop_id)
4996           return; // Children are already up to date
4997 
4998         m_stop_id = stop_id;
4999         m_update_selection = true;
5000 
5001         if (!m_thread_delegate_sp) {
5002           // Always expand the thread item the first time we show it
5003           // item.Expand();
5004           m_thread_delegate_sp =
5005               std::make_shared<ThreadTreeDelegate>(m_debugger);
5006         }
5007 
5008         TreeItem t(&item, *m_thread_delegate_sp, false);
5009         ThreadList &threads = process_sp->GetThreadList();
5010         std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
5011         ThreadSP selected_thread = threads.GetSelectedThread();
5012         size_t num_threads = threads.GetSize();
5013         item.Resize(num_threads, t);
5014         for (size_t i = 0; i < num_threads; ++i) {
5015           ThreadSP thread = threads.GetThreadAtIndex(i);
5016           item[i].SetIdentifier(thread->GetID());
5017           item[i].SetMightHaveChildren(true);
5018           if (selected_thread->GetID() == thread->GetID())
5019             item[i].Expand();
5020         }
5021         return;
5022       }
5023     }
5024     item.ClearChildren();
5025   }
5026 
5027   void TreeDelegateUpdateSelection(TreeItem &root, int &selection_index,
5028                                    TreeItem *&selected_item) override {
5029     if (!m_update_selection)
5030       return;
5031 
5032     ProcessSP process_sp = GetProcess();
5033     if (!(process_sp && process_sp->IsAlive()))
5034       return;
5035 
5036     StateType state = process_sp->GetState();
5037     if (!StateIsStoppedState(state, true))
5038       return;
5039 
5040     ThreadList &threads = process_sp->GetThreadList();
5041     std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
5042     ThreadSP selected_thread = threads.GetSelectedThread();
5043     size_t num_threads = threads.GetSize();
5044     for (size_t i = 0; i < num_threads; ++i) {
5045       ThreadSP thread = threads.GetThreadAtIndex(i);
5046       if (selected_thread->GetID() == thread->GetID()) {
5047         selected_item = &root[i][thread->GetSelectedFrameIndex()];
5048         selection_index = selected_item->GetRowIndex();
5049         return;
5050       }
5051     }
5052   }
5053 
5054   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5055 
5056   bool TreeDelegateExpandRootByDefault() override { return true; }
5057 
5058 protected:
5059   std::shared_ptr<ThreadTreeDelegate> m_thread_delegate_sp;
5060   Debugger &m_debugger;
5061   uint32_t m_stop_id;
5062   bool m_update_selection;
5063   FormatEntity::Entry m_format;
5064 };
5065 
5066 class BreakpointLocationTreeDelegate : public TreeDelegate {
5067 public:
5068   BreakpointLocationTreeDelegate(Debugger &debugger)
5069       : TreeDelegate(), m_debugger(debugger) {}
5070 
5071   ~BreakpointLocationTreeDelegate() override = default;
5072 
5073   Process *GetProcess() {
5074     ExecutionContext exe_ctx(
5075         m_debugger.GetCommandInterpreter().GetExecutionContext());
5076     return exe_ctx.GetProcessPtr();
5077   }
5078 
5079   BreakpointLocationSP GetBreakpointLocation(const TreeItem &item) {
5080     Breakpoint *breakpoint = (Breakpoint *)item.GetUserData();
5081     return breakpoint->GetLocationAtIndex(item.GetIdentifier());
5082   }
5083 
5084   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5085     BreakpointLocationSP breakpoint_location = GetBreakpointLocation(item);
5086     Process *process = GetProcess();
5087     StreamString stream;
5088     stream.Printf("%i.%i: ", breakpoint_location->GetBreakpoint().GetID(),
5089                   breakpoint_location->GetID());
5090     Address address = breakpoint_location->GetAddress();
5091     address.Dump(&stream, process, Address::DumpStyleResolvedDescription,
5092                  Address::DumpStyleInvalid);
5093     window.PutCStringTruncated(1, stream.GetString().str().c_str());
5094   }
5095 
5096   StringList ComputeDetailsList(BreakpointLocationSP breakpoint_location) {
5097     StringList details;
5098 
5099     Address address = breakpoint_location->GetAddress();
5100     SymbolContext symbol_context;
5101     address.CalculateSymbolContext(&symbol_context);
5102 
5103     if (symbol_context.module_sp) {
5104       StreamString module_stream;
5105       module_stream.PutCString("module = ");
5106       symbol_context.module_sp->GetFileSpec().Dump(
5107           module_stream.AsRawOstream());
5108       details.AppendString(module_stream.GetString());
5109     }
5110 
5111     if (symbol_context.comp_unit != nullptr) {
5112       StreamString compile_unit_stream;
5113       compile_unit_stream.PutCString("compile unit = ");
5114       symbol_context.comp_unit->GetPrimaryFile().GetFilename().Dump(
5115           &compile_unit_stream);
5116       details.AppendString(compile_unit_stream.GetString());
5117 
5118       if (symbol_context.function != nullptr) {
5119         StreamString function_stream;
5120         function_stream.PutCString("function = ");
5121         function_stream.PutCString(
5122             symbol_context.function->GetName().AsCString("<unknown>"));
5123         details.AppendString(function_stream.GetString());
5124       }
5125 
5126       if (symbol_context.line_entry.line > 0) {
5127         StreamString location_stream;
5128         location_stream.PutCString("location = ");
5129         symbol_context.line_entry.DumpStopContext(&location_stream, true);
5130         details.AppendString(location_stream.GetString());
5131       }
5132 
5133     } else {
5134       if (symbol_context.symbol) {
5135         StreamString symbol_stream;
5136         if (breakpoint_location->IsReExported())
5137           symbol_stream.PutCString("re-exported target = ");
5138         else
5139           symbol_stream.PutCString("symbol = ");
5140         symbol_stream.PutCString(
5141             symbol_context.symbol->GetName().AsCString("<unknown>"));
5142         details.AppendString(symbol_stream.GetString());
5143       }
5144     }
5145 
5146     Process *process = GetProcess();
5147 
5148     StreamString address_stream;
5149     address.Dump(&address_stream, process, Address::DumpStyleLoadAddress,
5150                  Address::DumpStyleModuleWithFileAddress);
5151     details.AppendString(address_stream.GetString());
5152 
5153     BreakpointSiteSP breakpoint_site = breakpoint_location->GetBreakpointSite();
5154     if (breakpoint_location->IsIndirect() && breakpoint_site) {
5155       Address resolved_address;
5156       resolved_address.SetLoadAddress(breakpoint_site->GetLoadAddress(),
5157                                       &breakpoint_location->GetTarget());
5158       Symbol *resolved_symbol = resolved_address.CalculateSymbolContextSymbol();
5159       if (resolved_symbol) {
5160         StreamString indirect_target_stream;
5161         indirect_target_stream.PutCString("indirect target = ");
5162         indirect_target_stream.PutCString(
5163             resolved_symbol->GetName().GetCString());
5164         details.AppendString(indirect_target_stream.GetString());
5165       }
5166     }
5167 
5168     bool is_resolved = breakpoint_location->IsResolved();
5169     StreamString resolved_stream;
5170     resolved_stream.Printf("resolved = %s", is_resolved ? "true" : "false");
5171     details.AppendString(resolved_stream.GetString());
5172 
5173     bool is_hardware = is_resolved && breakpoint_site->IsHardware();
5174     StreamString hardware_stream;
5175     hardware_stream.Printf("hardware = %s", is_hardware ? "true" : "false");
5176     details.AppendString(hardware_stream.GetString());
5177 
5178     StreamString hit_count_stream;
5179     hit_count_stream.Printf("hit count = %-4u",
5180                             breakpoint_location->GetHitCount());
5181     details.AppendString(hit_count_stream.GetString());
5182 
5183     return details;
5184   }
5185 
5186   void TreeDelegateGenerateChildren(TreeItem &item) override {
5187     BreakpointLocationSP breakpoint_location = GetBreakpointLocation(item);
5188     StringList details = ComputeDetailsList(breakpoint_location);
5189 
5190     if (!m_string_delegate_sp)
5191       m_string_delegate_sp = std::make_shared<TextTreeDelegate>();
5192     TreeItem details_tree_item(&item, *m_string_delegate_sp, false);
5193 
5194     item.Resize(details.GetSize(), details_tree_item);
5195     for (size_t i = 0; i < details.GetSize(); i++) {
5196       item[i].SetText(details.GetStringAtIndex(i));
5197     }
5198   }
5199 
5200   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5201 
5202 protected:
5203   Debugger &m_debugger;
5204   std::shared_ptr<TextTreeDelegate> m_string_delegate_sp;
5205 };
5206 
5207 class BreakpointTreeDelegate : public TreeDelegate {
5208 public:
5209   BreakpointTreeDelegate(Debugger &debugger)
5210       : TreeDelegate(), m_debugger(debugger),
5211         m_breakpoint_location_delegate_sp() {}
5212 
5213   ~BreakpointTreeDelegate() override = default;
5214 
5215   BreakpointSP GetBreakpoint(const TreeItem &item) {
5216     TargetSP target = m_debugger.GetSelectedTarget();
5217     BreakpointList &breakpoints = target->GetBreakpointList(false);
5218     return breakpoints.GetBreakpointAtIndex(item.GetIdentifier());
5219   }
5220 
5221   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5222     BreakpointSP breakpoint = GetBreakpoint(item);
5223     StreamString stream;
5224     stream.Format("{0}: ", breakpoint->GetID());
5225     breakpoint->GetResolverDescription(&stream);
5226     breakpoint->GetFilterDescription(&stream);
5227     window.PutCStringTruncated(1, stream.GetString().str().c_str());
5228   }
5229 
5230   void TreeDelegateGenerateChildren(TreeItem &item) override {
5231     BreakpointSP breakpoint = GetBreakpoint(item);
5232 
5233     if (!m_breakpoint_location_delegate_sp)
5234       m_breakpoint_location_delegate_sp =
5235           std::make_shared<BreakpointLocationTreeDelegate>(m_debugger);
5236     TreeItem breakpoint_location_tree_item(
5237         &item, *m_breakpoint_location_delegate_sp, true);
5238 
5239     item.Resize(breakpoint->GetNumLocations(), breakpoint_location_tree_item);
5240     for (size_t i = 0; i < breakpoint->GetNumLocations(); i++) {
5241       item[i].SetIdentifier(i);
5242       item[i].SetUserData(breakpoint.get());
5243     }
5244   }
5245 
5246   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5247 
5248 protected:
5249   Debugger &m_debugger;
5250   std::shared_ptr<BreakpointLocationTreeDelegate>
5251       m_breakpoint_location_delegate_sp;
5252 };
5253 
5254 class BreakpointsTreeDelegate : public TreeDelegate {
5255 public:
5256   BreakpointsTreeDelegate(Debugger &debugger)
5257       : TreeDelegate(), m_debugger(debugger), m_breakpoint_delegate_sp() {}
5258 
5259   ~BreakpointsTreeDelegate() override = default;
5260 
5261   bool TreeDelegateShouldDraw() override {
5262     TargetSP target = m_debugger.GetSelectedTarget();
5263     if (!target)
5264       return false;
5265 
5266     return true;
5267   }
5268 
5269   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5270     window.PutCString("Breakpoints");
5271   }
5272 
5273   void TreeDelegateGenerateChildren(TreeItem &item) override {
5274     TargetSP target = m_debugger.GetSelectedTarget();
5275 
5276     BreakpointList &breakpoints = target->GetBreakpointList(false);
5277     std::unique_lock<std::recursive_mutex> lock;
5278     breakpoints.GetListMutex(lock);
5279 
5280     if (!m_breakpoint_delegate_sp)
5281       m_breakpoint_delegate_sp =
5282           std::make_shared<BreakpointTreeDelegate>(m_debugger);
5283     TreeItem breakpoint_tree_item(&item, *m_breakpoint_delegate_sp, true);
5284 
5285     item.Resize(breakpoints.GetSize(), breakpoint_tree_item);
5286     for (size_t i = 0; i < breakpoints.GetSize(); i++) {
5287       item[i].SetIdentifier(i);
5288     }
5289   }
5290 
5291   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5292 
5293   bool TreeDelegateExpandRootByDefault() override { return true; }
5294 
5295 protected:
5296   Debugger &m_debugger;
5297   std::shared_ptr<BreakpointTreeDelegate> m_breakpoint_delegate_sp;
5298 };
5299 
5300 class ValueObjectListDelegate : public WindowDelegate {
5301 public:
5302   ValueObjectListDelegate() : m_rows() {}
5303 
5304   ValueObjectListDelegate(ValueObjectList &valobj_list)
5305       : m_rows(), m_selected_row(nullptr), m_selected_row_idx(0),
5306         m_first_visible_row(0), m_num_rows(0), m_max_x(0), m_max_y(0) {
5307     SetValues(valobj_list);
5308   }
5309 
5310   ~ValueObjectListDelegate() override = default;
5311 
5312   void SetValues(ValueObjectList &valobj_list) {
5313     m_selected_row = nullptr;
5314     m_selected_row_idx = 0;
5315     m_first_visible_row = 0;
5316     m_num_rows = 0;
5317     m_rows.clear();
5318     for (auto &valobj_sp : valobj_list.GetObjects())
5319       m_rows.push_back(Row(valobj_sp, nullptr));
5320   }
5321 
5322   bool WindowDelegateDraw(Window &window, bool force) override {
5323     m_num_rows = 0;
5324     m_min_x = 2;
5325     m_min_y = 1;
5326     m_max_x = window.GetWidth() - 1;
5327     m_max_y = window.GetHeight() - 1;
5328 
5329     window.Erase();
5330     window.DrawTitleBox(window.GetName());
5331 
5332     const int num_visible_rows = NumVisibleRows();
5333     const int num_rows = CalculateTotalNumberRows(m_rows);
5334 
5335     // If we unexpanded while having something selected our total number of
5336     // rows is less than the num visible rows, then make sure we show all the
5337     // rows by setting the first visible row accordingly.
5338     if (m_first_visible_row > 0 && num_rows < num_visible_rows)
5339       m_first_visible_row = 0;
5340 
5341     // Make sure the selected row is always visible
5342     if (m_selected_row_idx < m_first_visible_row)
5343       m_first_visible_row = m_selected_row_idx;
5344     else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
5345       m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
5346 
5347     DisplayRows(window, m_rows, g_options);
5348 
5349     // Get the selected row
5350     m_selected_row = GetRowForRowIndex(m_selected_row_idx);
5351     // Keep the cursor on the selected row so the highlight and the cursor are
5352     // always on the same line
5353     if (m_selected_row)
5354       window.MoveCursor(m_selected_row->x, m_selected_row->y);
5355 
5356     return true; // Drawing handled
5357   }
5358 
5359   KeyHelp *WindowDelegateGetKeyHelp() override {
5360     static curses::KeyHelp g_source_view_key_help[] = {
5361         {KEY_UP, "Select previous item"},
5362         {KEY_DOWN, "Select next item"},
5363         {KEY_RIGHT, "Expand selected item"},
5364         {KEY_LEFT, "Unexpand selected item or select parent if not expanded"},
5365         {KEY_PPAGE, "Page up"},
5366         {KEY_NPAGE, "Page down"},
5367         {'A', "Format as annotated address"},
5368         {'b', "Format as binary"},
5369         {'B', "Format as hex bytes with ASCII"},
5370         {'c', "Format as character"},
5371         {'d', "Format as a signed integer"},
5372         {'D', "Format selected value using the default format for the type"},
5373         {'f', "Format as float"},
5374         {'h', "Show help dialog"},
5375         {'i', "Format as instructions"},
5376         {'o', "Format as octal"},
5377         {'p', "Format as pointer"},
5378         {'s', "Format as C string"},
5379         {'t', "Toggle showing/hiding type names"},
5380         {'u', "Format as an unsigned integer"},
5381         {'x', "Format as hex"},
5382         {'X', "Format as uppercase hex"},
5383         {' ', "Toggle item expansion"},
5384         {',', "Page up"},
5385         {'.', "Page down"},
5386         {'\0', nullptr}};
5387     return g_source_view_key_help;
5388   }
5389 
5390   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
5391     switch (c) {
5392     case 'x':
5393     case 'X':
5394     case 'o':
5395     case 's':
5396     case 'u':
5397     case 'd':
5398     case 'D':
5399     case 'i':
5400     case 'A':
5401     case 'p':
5402     case 'c':
5403     case 'b':
5404     case 'B':
5405     case 'f':
5406       // Change the format for the currently selected item
5407       if (m_selected_row) {
5408         auto valobj_sp = m_selected_row->value.GetSP();
5409         if (valobj_sp)
5410           valobj_sp->SetFormat(FormatForChar(c));
5411       }
5412       return eKeyHandled;
5413 
5414     case 't':
5415       // Toggle showing type names
5416       g_options.show_types = !g_options.show_types;
5417       return eKeyHandled;
5418 
5419     case ',':
5420     case KEY_PPAGE:
5421       // Page up key
5422       if (m_first_visible_row > 0) {
5423         if (static_cast<int>(m_first_visible_row) > m_max_y)
5424           m_first_visible_row -= m_max_y;
5425         else
5426           m_first_visible_row = 0;
5427         m_selected_row_idx = m_first_visible_row;
5428       }
5429       return eKeyHandled;
5430 
5431     case '.':
5432     case KEY_NPAGE:
5433       // Page down key
5434       if (m_num_rows > static_cast<size_t>(m_max_y)) {
5435         if (m_first_visible_row + m_max_y < m_num_rows) {
5436           m_first_visible_row += m_max_y;
5437           m_selected_row_idx = m_first_visible_row;
5438         }
5439       }
5440       return eKeyHandled;
5441 
5442     case KEY_UP:
5443       if (m_selected_row_idx > 0)
5444         --m_selected_row_idx;
5445       return eKeyHandled;
5446 
5447     case KEY_DOWN:
5448       if (m_selected_row_idx + 1 < m_num_rows)
5449         ++m_selected_row_idx;
5450       return eKeyHandled;
5451 
5452     case KEY_RIGHT:
5453       if (m_selected_row) {
5454         if (!m_selected_row->expanded)
5455           m_selected_row->Expand();
5456       }
5457       return eKeyHandled;
5458 
5459     case KEY_LEFT:
5460       if (m_selected_row) {
5461         if (m_selected_row->expanded)
5462           m_selected_row->Unexpand();
5463         else if (m_selected_row->parent)
5464           m_selected_row_idx = m_selected_row->parent->row_idx;
5465       }
5466       return eKeyHandled;
5467 
5468     case ' ':
5469       // Toggle expansion state when SPACE is pressed
5470       if (m_selected_row) {
5471         if (m_selected_row->expanded)
5472           m_selected_row->Unexpand();
5473         else
5474           m_selected_row->Expand();
5475       }
5476       return eKeyHandled;
5477 
5478     case 'h':
5479       window.CreateHelpSubwindow();
5480       return eKeyHandled;
5481 
5482     default:
5483       break;
5484     }
5485     return eKeyNotHandled;
5486   }
5487 
5488 protected:
5489   std::vector<Row> m_rows;
5490   Row *m_selected_row = nullptr;
5491   uint32_t m_selected_row_idx = 0;
5492   uint32_t m_first_visible_row = 0;
5493   uint32_t m_num_rows = 0;
5494   int m_min_x;
5495   int m_min_y;
5496   int m_max_x = 0;
5497   int m_max_y = 0;
5498 
5499   static Format FormatForChar(int c) {
5500     switch (c) {
5501     case 'x':
5502       return eFormatHex;
5503     case 'X':
5504       return eFormatHexUppercase;
5505     case 'o':
5506       return eFormatOctal;
5507     case 's':
5508       return eFormatCString;
5509     case 'u':
5510       return eFormatUnsigned;
5511     case 'd':
5512       return eFormatDecimal;
5513     case 'D':
5514       return eFormatDefault;
5515     case 'i':
5516       return eFormatInstruction;
5517     case 'A':
5518       return eFormatAddressInfo;
5519     case 'p':
5520       return eFormatPointer;
5521     case 'c':
5522       return eFormatChar;
5523     case 'b':
5524       return eFormatBinary;
5525     case 'B':
5526       return eFormatBytesWithASCII;
5527     case 'f':
5528       return eFormatFloat;
5529     }
5530     return eFormatDefault;
5531   }
5532 
5533   bool DisplayRowObject(Window &window, Row &row, DisplayOptions &options,
5534                         bool highlight, bool last_child) {
5535     ValueObject *valobj = row.value.GetSP().get();
5536 
5537     if (valobj == nullptr)
5538       return false;
5539 
5540     const char *type_name =
5541         options.show_types ? valobj->GetTypeName().GetCString() : nullptr;
5542     const char *name = valobj->GetName().GetCString();
5543     const char *value = valobj->GetValueAsCString();
5544     const char *summary = valobj->GetSummaryAsCString();
5545 
5546     window.MoveCursor(row.x, row.y);
5547 
5548     row.DrawTree(window);
5549 
5550     if (highlight)
5551       window.AttributeOn(A_REVERSE);
5552 
5553     if (type_name && type_name[0])
5554       window.PrintfTruncated(1, "(%s) ", type_name);
5555 
5556     if (name && name[0])
5557       window.PutCStringTruncated(1, name);
5558 
5559     attr_t changd_attr = 0;
5560     if (valobj->GetValueDidChange())
5561       changd_attr = COLOR_PAIR(RedOnBlack) | A_BOLD;
5562 
5563     if (value && value[0]) {
5564       window.PutCStringTruncated(1, " = ");
5565       if (changd_attr)
5566         window.AttributeOn(changd_attr);
5567       window.PutCStringTruncated(1, value);
5568       if (changd_attr)
5569         window.AttributeOff(changd_attr);
5570     }
5571 
5572     if (summary && summary[0]) {
5573       window.PutCStringTruncated(1, " ");
5574       if (changd_attr)
5575         window.AttributeOn(changd_attr);
5576       window.PutCStringTruncated(1, summary);
5577       if (changd_attr)
5578         window.AttributeOff(changd_attr);
5579     }
5580 
5581     if (highlight)
5582       window.AttributeOff(A_REVERSE);
5583 
5584     return true;
5585   }
5586 
5587   void DisplayRows(Window &window, std::vector<Row> &rows,
5588                    DisplayOptions &options) {
5589     // >   0x25B7
5590     // \/  0x25BD
5591 
5592     bool window_is_active = window.IsActive();
5593     for (auto &row : rows) {
5594       const bool last_child = row.parent && &rows[rows.size() - 1] == &row;
5595       // Save the row index in each Row structure
5596       row.row_idx = m_num_rows;
5597       if ((m_num_rows >= m_first_visible_row) &&
5598           ((m_num_rows - m_first_visible_row) <
5599            static_cast<size_t>(NumVisibleRows()))) {
5600         row.x = m_min_x;
5601         row.y = m_num_rows - m_first_visible_row + 1;
5602         if (DisplayRowObject(window, row, options,
5603                              window_is_active &&
5604                                  m_num_rows == m_selected_row_idx,
5605                              last_child)) {
5606           ++m_num_rows;
5607         } else {
5608           row.x = 0;
5609           row.y = 0;
5610         }
5611       } else {
5612         row.x = 0;
5613         row.y = 0;
5614         ++m_num_rows;
5615       }
5616 
5617       auto &children = row.GetChildren();
5618       if (row.expanded && !children.empty()) {
5619         DisplayRows(window, children, options);
5620       }
5621     }
5622   }
5623 
5624   int CalculateTotalNumberRows(std::vector<Row> &rows) {
5625     int row_count = 0;
5626     for (auto &row : rows) {
5627       ++row_count;
5628       if (row.expanded)
5629         row_count += CalculateTotalNumberRows(row.GetChildren());
5630     }
5631     return row_count;
5632   }
5633 
5634   static Row *GetRowForRowIndexImpl(std::vector<Row> &rows, size_t &row_index) {
5635     for (auto &row : rows) {
5636       if (row_index == 0)
5637         return &row;
5638       else {
5639         --row_index;
5640         auto &children = row.GetChildren();
5641         if (row.expanded && !children.empty()) {
5642           Row *result = GetRowForRowIndexImpl(children, row_index);
5643           if (result)
5644             return result;
5645         }
5646       }
5647     }
5648     return nullptr;
5649   }
5650 
5651   Row *GetRowForRowIndex(size_t row_index) {
5652     return GetRowForRowIndexImpl(m_rows, row_index);
5653   }
5654 
5655   int NumVisibleRows() const { return m_max_y - m_min_y; }
5656 
5657   static DisplayOptions g_options;
5658 };
5659 
5660 class FrameVariablesWindowDelegate : public ValueObjectListDelegate {
5661 public:
5662   FrameVariablesWindowDelegate(Debugger &debugger)
5663       : ValueObjectListDelegate(), m_debugger(debugger),
5664         m_frame_block(nullptr) {}
5665 
5666   ~FrameVariablesWindowDelegate() override = default;
5667 
5668   const char *WindowDelegateGetHelpText() override {
5669     return "Frame variable window keyboard shortcuts:";
5670   }
5671 
5672   bool WindowDelegateDraw(Window &window, bool force) override {
5673     ExecutionContext exe_ctx(
5674         m_debugger.GetCommandInterpreter().GetExecutionContext());
5675     Process *process = exe_ctx.GetProcessPtr();
5676     Block *frame_block = nullptr;
5677     StackFrame *frame = nullptr;
5678 
5679     if (process) {
5680       StateType state = process->GetState();
5681       if (StateIsStoppedState(state, true)) {
5682         frame = exe_ctx.GetFramePtr();
5683         if (frame)
5684           frame_block = frame->GetFrameBlock();
5685       } else if (StateIsRunningState(state)) {
5686         return true; // Don't do any updating when we are running
5687       }
5688     }
5689 
5690     ValueObjectList local_values;
5691     if (frame_block) {
5692       // Only update the variables if they have changed
5693       if (m_frame_block != frame_block) {
5694         m_frame_block = frame_block;
5695 
5696         VariableList *locals = frame->GetVariableList(true);
5697         if (locals) {
5698           const DynamicValueType use_dynamic = eDynamicDontRunTarget;
5699           for (const VariableSP &local_sp : *locals) {
5700             ValueObjectSP value_sp =
5701                 frame->GetValueObjectForFrameVariable(local_sp, use_dynamic);
5702             if (value_sp) {
5703               ValueObjectSP synthetic_value_sp = value_sp->GetSyntheticValue();
5704               if (synthetic_value_sp)
5705                 local_values.Append(synthetic_value_sp);
5706               else
5707                 local_values.Append(value_sp);
5708             }
5709           }
5710           // Update the values
5711           SetValues(local_values);
5712         }
5713       }
5714     } else {
5715       m_frame_block = nullptr;
5716       // Update the values with an empty list if there is no frame
5717       SetValues(local_values);
5718     }
5719 
5720     return ValueObjectListDelegate::WindowDelegateDraw(window, force);
5721   }
5722 
5723 protected:
5724   Debugger &m_debugger;
5725   Block *m_frame_block;
5726 };
5727 
5728 class RegistersWindowDelegate : public ValueObjectListDelegate {
5729 public:
5730   RegistersWindowDelegate(Debugger &debugger)
5731       : ValueObjectListDelegate(), m_debugger(debugger) {}
5732 
5733   ~RegistersWindowDelegate() override = default;
5734 
5735   const char *WindowDelegateGetHelpText() override {
5736     return "Register window keyboard shortcuts:";
5737   }
5738 
5739   bool WindowDelegateDraw(Window &window, bool force) override {
5740     ExecutionContext exe_ctx(
5741         m_debugger.GetCommandInterpreter().GetExecutionContext());
5742     StackFrame *frame = exe_ctx.GetFramePtr();
5743 
5744     ValueObjectList value_list;
5745     if (frame) {
5746       if (frame->GetStackID() != m_stack_id) {
5747         m_stack_id = frame->GetStackID();
5748         RegisterContextSP reg_ctx(frame->GetRegisterContext());
5749         if (reg_ctx) {
5750           const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
5751           for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) {
5752             value_list.Append(
5753                 ValueObjectRegisterSet::Create(frame, reg_ctx, set_idx));
5754           }
5755         }
5756         SetValues(value_list);
5757       }
5758     } else {
5759       Process *process = exe_ctx.GetProcessPtr();
5760       if (process && process->IsAlive())
5761         return true; // Don't do any updating if we are running
5762       else {
5763         // Update the values with an empty list if there is no process or the
5764         // process isn't alive anymore
5765         SetValues(value_list);
5766       }
5767     }
5768     return ValueObjectListDelegate::WindowDelegateDraw(window, force);
5769   }
5770 
5771 protected:
5772   Debugger &m_debugger;
5773   StackID m_stack_id;
5774 };
5775 
5776 static const char *CursesKeyToCString(int ch) {
5777   static char g_desc[32];
5778   if (ch >= KEY_F0 && ch < KEY_F0 + 64) {
5779     snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0);
5780     return g_desc;
5781   }
5782   switch (ch) {
5783   case KEY_DOWN:
5784     return "down";
5785   case KEY_UP:
5786     return "up";
5787   case KEY_LEFT:
5788     return "left";
5789   case KEY_RIGHT:
5790     return "right";
5791   case KEY_HOME:
5792     return "home";
5793   case KEY_BACKSPACE:
5794     return "backspace";
5795   case KEY_DL:
5796     return "delete-line";
5797   case KEY_IL:
5798     return "insert-line";
5799   case KEY_DC:
5800     return "delete-char";
5801   case KEY_IC:
5802     return "insert-char";
5803   case KEY_CLEAR:
5804     return "clear";
5805   case KEY_EOS:
5806     return "clear-to-eos";
5807   case KEY_EOL:
5808     return "clear-to-eol";
5809   case KEY_SF:
5810     return "scroll-forward";
5811   case KEY_SR:
5812     return "scroll-backward";
5813   case KEY_NPAGE:
5814     return "page-down";
5815   case KEY_PPAGE:
5816     return "page-up";
5817   case KEY_STAB:
5818     return "set-tab";
5819   case KEY_CTAB:
5820     return "clear-tab";
5821   case KEY_CATAB:
5822     return "clear-all-tabs";
5823   case KEY_ENTER:
5824     return "enter";
5825   case KEY_PRINT:
5826     return "print";
5827   case KEY_LL:
5828     return "lower-left key";
5829   case KEY_A1:
5830     return "upper left of keypad";
5831   case KEY_A3:
5832     return "upper right of keypad";
5833   case KEY_B2:
5834     return "center of keypad";
5835   case KEY_C1:
5836     return "lower left of keypad";
5837   case KEY_C3:
5838     return "lower right of keypad";
5839   case KEY_BTAB:
5840     return "back-tab key";
5841   case KEY_BEG:
5842     return "begin key";
5843   case KEY_CANCEL:
5844     return "cancel key";
5845   case KEY_CLOSE:
5846     return "close key";
5847   case KEY_COMMAND:
5848     return "command key";
5849   case KEY_COPY:
5850     return "copy key";
5851   case KEY_CREATE:
5852     return "create key";
5853   case KEY_END:
5854     return "end key";
5855   case KEY_EXIT:
5856     return "exit key";
5857   case KEY_FIND:
5858     return "find key";
5859   case KEY_HELP:
5860     return "help key";
5861   case KEY_MARK:
5862     return "mark key";
5863   case KEY_MESSAGE:
5864     return "message key";
5865   case KEY_MOVE:
5866     return "move key";
5867   case KEY_NEXT:
5868     return "next key";
5869   case KEY_OPEN:
5870     return "open key";
5871   case KEY_OPTIONS:
5872     return "options key";
5873   case KEY_PREVIOUS:
5874     return "previous key";
5875   case KEY_REDO:
5876     return "redo key";
5877   case KEY_REFERENCE:
5878     return "reference key";
5879   case KEY_REFRESH:
5880     return "refresh key";
5881   case KEY_REPLACE:
5882     return "replace key";
5883   case KEY_RESTART:
5884     return "restart key";
5885   case KEY_RESUME:
5886     return "resume key";
5887   case KEY_SAVE:
5888     return "save key";
5889   case KEY_SBEG:
5890     return "shifted begin key";
5891   case KEY_SCANCEL:
5892     return "shifted cancel key";
5893   case KEY_SCOMMAND:
5894     return "shifted command key";
5895   case KEY_SCOPY:
5896     return "shifted copy key";
5897   case KEY_SCREATE:
5898     return "shifted create key";
5899   case KEY_SDC:
5900     return "shifted delete-character key";
5901   case KEY_SDL:
5902     return "shifted delete-line key";
5903   case KEY_SELECT:
5904     return "select key";
5905   case KEY_SEND:
5906     return "shifted end key";
5907   case KEY_SEOL:
5908     return "shifted clear-to-end-of-line key";
5909   case KEY_SEXIT:
5910     return "shifted exit key";
5911   case KEY_SFIND:
5912     return "shifted find key";
5913   case KEY_SHELP:
5914     return "shifted help key";
5915   case KEY_SHOME:
5916     return "shifted home key";
5917   case KEY_SIC:
5918     return "shifted insert-character key";
5919   case KEY_SLEFT:
5920     return "shifted left-arrow key";
5921   case KEY_SMESSAGE:
5922     return "shifted message key";
5923   case KEY_SMOVE:
5924     return "shifted move key";
5925   case KEY_SNEXT:
5926     return "shifted next key";
5927   case KEY_SOPTIONS:
5928     return "shifted options key";
5929   case KEY_SPREVIOUS:
5930     return "shifted previous key";
5931   case KEY_SPRINT:
5932     return "shifted print key";
5933   case KEY_SREDO:
5934     return "shifted redo key";
5935   case KEY_SREPLACE:
5936     return "shifted replace key";
5937   case KEY_SRIGHT:
5938     return "shifted right-arrow key";
5939   case KEY_SRSUME:
5940     return "shifted resume key";
5941   case KEY_SSAVE:
5942     return "shifted save key";
5943   case KEY_SSUSPEND:
5944     return "shifted suspend key";
5945   case KEY_SUNDO:
5946     return "shifted undo key";
5947   case KEY_SUSPEND:
5948     return "suspend key";
5949   case KEY_UNDO:
5950     return "undo key";
5951   case KEY_MOUSE:
5952     return "Mouse event has occurred";
5953   case KEY_RESIZE:
5954     return "Terminal resize event";
5955 #ifdef KEY_EVENT
5956   case KEY_EVENT:
5957     return "We were interrupted by an event";
5958 #endif
5959   case KEY_RETURN:
5960     return "return";
5961   case ' ':
5962     return "space";
5963   case '\t':
5964     return "tab";
5965   case KEY_ESCAPE:
5966     return "escape";
5967   default:
5968     if (llvm::isPrint(ch))
5969       snprintf(g_desc, sizeof(g_desc), "%c", ch);
5970     else
5971       snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch);
5972     return g_desc;
5973   }
5974   return nullptr;
5975 }
5976 
5977 HelpDialogDelegate::HelpDialogDelegate(const char *text,
5978                                        KeyHelp *key_help_array)
5979     : m_text(), m_first_visible_line(0) {
5980   if (text && text[0]) {
5981     m_text.SplitIntoLines(text);
5982     m_text.AppendString("");
5983   }
5984   if (key_help_array) {
5985     for (KeyHelp *key = key_help_array; key->ch; ++key) {
5986       StreamString key_description;
5987       key_description.Printf("%10s - %s", CursesKeyToCString(key->ch),
5988                              key->description);
5989       m_text.AppendString(key_description.GetString());
5990     }
5991   }
5992 }
5993 
5994 HelpDialogDelegate::~HelpDialogDelegate() = default;
5995 
5996 bool HelpDialogDelegate::WindowDelegateDraw(Window &window, bool force) {
5997   window.Erase();
5998   const int window_height = window.GetHeight();
5999   int x = 2;
6000   int y = 1;
6001   const int min_y = y;
6002   const int max_y = window_height - 1 - y;
6003   const size_t num_visible_lines = max_y - min_y + 1;
6004   const size_t num_lines = m_text.GetSize();
6005   const char *bottom_message;
6006   if (num_lines <= num_visible_lines)
6007     bottom_message = "Press any key to exit";
6008   else
6009     bottom_message = "Use arrows to scroll, any other key to exit";
6010   window.DrawTitleBox(window.GetName(), bottom_message);
6011   while (y <= max_y) {
6012     window.MoveCursor(x, y);
6013     window.PutCStringTruncated(
6014         1, m_text.GetStringAtIndex(m_first_visible_line + y - min_y));
6015     ++y;
6016   }
6017   return true;
6018 }
6019 
6020 HandleCharResult HelpDialogDelegate::WindowDelegateHandleChar(Window &window,
6021                                                               int key) {
6022   bool done = false;
6023   const size_t num_lines = m_text.GetSize();
6024   const size_t num_visible_lines = window.GetHeight() - 2;
6025 
6026   if (num_lines <= num_visible_lines) {
6027     done = true;
6028     // If we have all lines visible and don't need scrolling, then any key
6029     // press will cause us to exit
6030   } else {
6031     switch (key) {
6032     case KEY_UP:
6033       if (m_first_visible_line > 0)
6034         --m_first_visible_line;
6035       break;
6036 
6037     case KEY_DOWN:
6038       if (m_first_visible_line + num_visible_lines < num_lines)
6039         ++m_first_visible_line;
6040       break;
6041 
6042     case KEY_PPAGE:
6043     case ',':
6044       if (m_first_visible_line > 0) {
6045         if (static_cast<size_t>(m_first_visible_line) >= num_visible_lines)
6046           m_first_visible_line -= num_visible_lines;
6047         else
6048           m_first_visible_line = 0;
6049       }
6050       break;
6051 
6052     case KEY_NPAGE:
6053     case '.':
6054       if (m_first_visible_line + num_visible_lines < num_lines) {
6055         m_first_visible_line += num_visible_lines;
6056         if (static_cast<size_t>(m_first_visible_line) > num_lines)
6057           m_first_visible_line = num_lines - num_visible_lines;
6058       }
6059       break;
6060 
6061     default:
6062       done = true;
6063       break;
6064     }
6065   }
6066   if (done)
6067     window.GetParent()->RemoveSubWindow(&window);
6068   return eKeyHandled;
6069 }
6070 
6071 class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
6072 public:
6073   enum {
6074     eMenuID_LLDB = 1,
6075     eMenuID_LLDBAbout,
6076     eMenuID_LLDBExit,
6077 
6078     eMenuID_Target,
6079     eMenuID_TargetCreate,
6080     eMenuID_TargetDelete,
6081 
6082     eMenuID_Process,
6083     eMenuID_ProcessAttach,
6084     eMenuID_ProcessDetachResume,
6085     eMenuID_ProcessDetachSuspended,
6086     eMenuID_ProcessLaunch,
6087     eMenuID_ProcessContinue,
6088     eMenuID_ProcessHalt,
6089     eMenuID_ProcessKill,
6090 
6091     eMenuID_Thread,
6092     eMenuID_ThreadStepIn,
6093     eMenuID_ThreadStepOver,
6094     eMenuID_ThreadStepOut,
6095 
6096     eMenuID_View,
6097     eMenuID_ViewBacktrace,
6098     eMenuID_ViewRegisters,
6099     eMenuID_ViewSource,
6100     eMenuID_ViewVariables,
6101     eMenuID_ViewBreakpoints,
6102 
6103     eMenuID_Help,
6104     eMenuID_HelpGUIHelp
6105   };
6106 
6107   ApplicationDelegate(Application &app, Debugger &debugger)
6108       : WindowDelegate(), MenuDelegate(), m_app(app), m_debugger(debugger) {}
6109 
6110   ~ApplicationDelegate() override = default;
6111 
6112   bool WindowDelegateDraw(Window &window, bool force) override {
6113     return false; // Drawing not handled, let standard window drawing happen
6114   }
6115 
6116   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
6117     switch (key) {
6118     case '\t':
6119       window.SelectNextWindowAsActive();
6120       return eKeyHandled;
6121 
6122     case KEY_SHIFT_TAB:
6123       window.SelectPreviousWindowAsActive();
6124       return eKeyHandled;
6125 
6126     case 'h':
6127       window.CreateHelpSubwindow();
6128       return eKeyHandled;
6129 
6130     case KEY_ESCAPE:
6131       return eQuitApplication;
6132 
6133     default:
6134       break;
6135     }
6136     return eKeyNotHandled;
6137   }
6138 
6139   const char *WindowDelegateGetHelpText() override {
6140     return "Welcome to the LLDB curses GUI.\n\n"
6141            "Press the TAB key to change the selected view.\n"
6142            "Each view has its own keyboard shortcuts, press 'h' to open a "
6143            "dialog to display them.\n\n"
6144            "Common key bindings for all views:";
6145   }
6146 
6147   KeyHelp *WindowDelegateGetKeyHelp() override {
6148     static curses::KeyHelp g_source_view_key_help[] = {
6149         {'\t', "Select next view"},
6150         {KEY_BTAB, "Select previous view"},
6151         {'h', "Show help dialog with view specific key bindings"},
6152         {',', "Page up"},
6153         {'.', "Page down"},
6154         {KEY_UP, "Select previous"},
6155         {KEY_DOWN, "Select next"},
6156         {KEY_LEFT, "Unexpand or select parent"},
6157         {KEY_RIGHT, "Expand"},
6158         {KEY_PPAGE, "Page up"},
6159         {KEY_NPAGE, "Page down"},
6160         {'\0', nullptr}};
6161     return g_source_view_key_help;
6162   }
6163 
6164   MenuActionResult MenuDelegateAction(Menu &menu) override {
6165     switch (menu.GetIdentifier()) {
6166     case eMenuID_TargetCreate: {
6167       WindowSP main_window_sp = m_app.GetMainWindow();
6168       FormDelegateSP form_delegate_sp =
6169           FormDelegateSP(new TargetCreateFormDelegate(m_debugger));
6170       Rect bounds = main_window_sp->GetCenteredRect(80, 19);
6171       WindowSP form_window_sp = main_window_sp->CreateSubWindow(
6172           form_delegate_sp->GetName().c_str(), bounds, true);
6173       WindowDelegateSP window_delegate_sp =
6174           WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
6175       form_window_sp->SetDelegate(window_delegate_sp);
6176       return MenuActionResult::Handled;
6177     }
6178     case eMenuID_ThreadStepIn: {
6179       ExecutionContext exe_ctx =
6180           m_debugger.GetCommandInterpreter().GetExecutionContext();
6181       if (exe_ctx.HasThreadScope()) {
6182         Process *process = exe_ctx.GetProcessPtr();
6183         if (process && process->IsAlive() &&
6184             StateIsStoppedState(process->GetState(), true))
6185           exe_ctx.GetThreadRef().StepIn(true);
6186       }
6187     }
6188       return MenuActionResult::Handled;
6189 
6190     case eMenuID_ThreadStepOut: {
6191       ExecutionContext exe_ctx =
6192           m_debugger.GetCommandInterpreter().GetExecutionContext();
6193       if (exe_ctx.HasThreadScope()) {
6194         Process *process = exe_ctx.GetProcessPtr();
6195         if (process && process->IsAlive() &&
6196             StateIsStoppedState(process->GetState(), true))
6197           exe_ctx.GetThreadRef().StepOut();
6198       }
6199     }
6200       return MenuActionResult::Handled;
6201 
6202     case eMenuID_ThreadStepOver: {
6203       ExecutionContext exe_ctx =
6204           m_debugger.GetCommandInterpreter().GetExecutionContext();
6205       if (exe_ctx.HasThreadScope()) {
6206         Process *process = exe_ctx.GetProcessPtr();
6207         if (process && process->IsAlive() &&
6208             StateIsStoppedState(process->GetState(), true))
6209           exe_ctx.GetThreadRef().StepOver(true);
6210       }
6211     }
6212       return MenuActionResult::Handled;
6213 
6214     case eMenuID_ProcessAttach: {
6215       WindowSP main_window_sp = m_app.GetMainWindow();
6216       FormDelegateSP form_delegate_sp = FormDelegateSP(
6217           new ProcessAttachFormDelegate(m_debugger, main_window_sp));
6218       Rect bounds = main_window_sp->GetCenteredRect(80, 22);
6219       WindowSP form_window_sp = main_window_sp->CreateSubWindow(
6220           form_delegate_sp->GetName().c_str(), bounds, true);
6221       WindowDelegateSP window_delegate_sp =
6222           WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
6223       form_window_sp->SetDelegate(window_delegate_sp);
6224       return MenuActionResult::Handled;
6225     }
6226     case eMenuID_ProcessLaunch: {
6227       WindowSP main_window_sp = m_app.GetMainWindow();
6228       FormDelegateSP form_delegate_sp = FormDelegateSP(
6229           new ProcessLaunchFormDelegate(m_debugger, main_window_sp));
6230       Rect bounds = main_window_sp->GetCenteredRect(80, 22);
6231       WindowSP form_window_sp = main_window_sp->CreateSubWindow(
6232           form_delegate_sp->GetName().c_str(), bounds, true);
6233       WindowDelegateSP window_delegate_sp =
6234           WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
6235       form_window_sp->SetDelegate(window_delegate_sp);
6236       return MenuActionResult::Handled;
6237     }
6238 
6239     case eMenuID_ProcessContinue: {
6240       ExecutionContext exe_ctx =
6241           m_debugger.GetCommandInterpreter().GetExecutionContext();
6242       if (exe_ctx.HasProcessScope()) {
6243         Process *process = exe_ctx.GetProcessPtr();
6244         if (process && process->IsAlive() &&
6245             StateIsStoppedState(process->GetState(), true))
6246           process->Resume();
6247       }
6248     }
6249       return MenuActionResult::Handled;
6250 
6251     case eMenuID_ProcessKill: {
6252       ExecutionContext exe_ctx =
6253           m_debugger.GetCommandInterpreter().GetExecutionContext();
6254       if (exe_ctx.HasProcessScope()) {
6255         Process *process = exe_ctx.GetProcessPtr();
6256         if (process && process->IsAlive())
6257           process->Destroy(false);
6258       }
6259     }
6260       return MenuActionResult::Handled;
6261 
6262     case eMenuID_ProcessHalt: {
6263       ExecutionContext exe_ctx =
6264           m_debugger.GetCommandInterpreter().GetExecutionContext();
6265       if (exe_ctx.HasProcessScope()) {
6266         Process *process = exe_ctx.GetProcessPtr();
6267         if (process && process->IsAlive())
6268           process->Halt();
6269       }
6270     }
6271       return MenuActionResult::Handled;
6272 
6273     case eMenuID_ProcessDetachResume:
6274     case eMenuID_ProcessDetachSuspended: {
6275       ExecutionContext exe_ctx =
6276           m_debugger.GetCommandInterpreter().GetExecutionContext();
6277       if (exe_ctx.HasProcessScope()) {
6278         Process *process = exe_ctx.GetProcessPtr();
6279         if (process && process->IsAlive())
6280           process->Detach(menu.GetIdentifier() ==
6281                           eMenuID_ProcessDetachSuspended);
6282       }
6283     }
6284       return MenuActionResult::Handled;
6285 
6286     case eMenuID_Process: {
6287       // Populate the menu with all of the threads if the process is stopped
6288       // when the Process menu gets selected and is about to display its
6289       // submenu.
6290       Menus &submenus = menu.GetSubmenus();
6291       ExecutionContext exe_ctx =
6292           m_debugger.GetCommandInterpreter().GetExecutionContext();
6293       Process *process = exe_ctx.GetProcessPtr();
6294       if (process && process->IsAlive() &&
6295           StateIsStoppedState(process->GetState(), true)) {
6296         if (submenus.size() == 7)
6297           menu.AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
6298         else if (submenus.size() > 8)
6299           submenus.erase(submenus.begin() + 8, submenus.end());
6300 
6301         ThreadList &threads = process->GetThreadList();
6302         std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
6303         size_t num_threads = threads.GetSize();
6304         for (size_t i = 0; i < num_threads; ++i) {
6305           ThreadSP thread_sp = threads.GetThreadAtIndex(i);
6306           char menu_char = '\0';
6307           if (i < 9)
6308             menu_char = '1' + i;
6309           StreamString thread_menu_title;
6310           thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID());
6311           const char *thread_name = thread_sp->GetName();
6312           if (thread_name && thread_name[0])
6313             thread_menu_title.Printf(" %s", thread_name);
6314           else {
6315             const char *queue_name = thread_sp->GetQueueName();
6316             if (queue_name && queue_name[0])
6317               thread_menu_title.Printf(" %s", queue_name);
6318           }
6319           menu.AddSubmenu(
6320               MenuSP(new Menu(thread_menu_title.GetString().str().c_str(),
6321                               nullptr, menu_char, thread_sp->GetID())));
6322         }
6323       } else if (submenus.size() > 7) {
6324         // Remove the separator and any other thread submenu items that were
6325         // previously added
6326         submenus.erase(submenus.begin() + 7, submenus.end());
6327       }
6328       // Since we are adding and removing items we need to recalculate the
6329       // name lengths
6330       menu.RecalculateNameLengths();
6331     }
6332       return MenuActionResult::Handled;
6333 
6334     case eMenuID_ViewVariables: {
6335       WindowSP main_window_sp = m_app.GetMainWindow();
6336       WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
6337       WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
6338       WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
6339       const Rect source_bounds = source_window_sp->GetBounds();
6340 
6341       if (variables_window_sp) {
6342         const Rect variables_bounds = variables_window_sp->GetBounds();
6343 
6344         main_window_sp->RemoveSubWindow(variables_window_sp.get());
6345 
6346         if (registers_window_sp) {
6347           // We have a registers window, so give all the area back to the
6348           // registers window
6349           Rect registers_bounds = variables_bounds;
6350           registers_bounds.size.width = source_bounds.size.width;
6351           registers_window_sp->SetBounds(registers_bounds);
6352         } else {
6353           // We have no registers window showing so give the bottom area back
6354           // to the source view
6355           source_window_sp->Resize(source_bounds.size.width,
6356                                    source_bounds.size.height +
6357                                        variables_bounds.size.height);
6358         }
6359       } else {
6360         Rect new_variables_rect;
6361         if (registers_window_sp) {
6362           // We have a registers window so split the area of the registers
6363           // window into two columns where the left hand side will be the
6364           // variables and the right hand side will be the registers
6365           const Rect variables_bounds = registers_window_sp->GetBounds();
6366           Rect new_registers_rect;
6367           variables_bounds.VerticalSplitPercentage(0.50, new_variables_rect,
6368                                                    new_registers_rect);
6369           registers_window_sp->SetBounds(new_registers_rect);
6370         } else {
6371           // No registers window, grab the bottom part of the source window
6372           Rect new_source_rect;
6373           source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
6374                                                   new_variables_rect);
6375           source_window_sp->SetBounds(new_source_rect);
6376         }
6377         WindowSP new_window_sp = main_window_sp->CreateSubWindow(
6378             "Variables", new_variables_rect, false);
6379         new_window_sp->SetDelegate(
6380             WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
6381       }
6382       touchwin(stdscr);
6383     }
6384       return MenuActionResult::Handled;
6385 
6386     case eMenuID_ViewRegisters: {
6387       WindowSP main_window_sp = m_app.GetMainWindow();
6388       WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
6389       WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
6390       WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
6391       const Rect source_bounds = source_window_sp->GetBounds();
6392 
6393       if (registers_window_sp) {
6394         if (variables_window_sp) {
6395           const Rect variables_bounds = variables_window_sp->GetBounds();
6396 
6397           // We have a variables window, so give all the area back to the
6398           // variables window
6399           variables_window_sp->Resize(variables_bounds.size.width +
6400                                           registers_window_sp->GetWidth(),
6401                                       variables_bounds.size.height);
6402         } else {
6403           // We have no variables window showing so give the bottom area back
6404           // to the source view
6405           source_window_sp->Resize(source_bounds.size.width,
6406                                    source_bounds.size.height +
6407                                        registers_window_sp->GetHeight());
6408         }
6409         main_window_sp->RemoveSubWindow(registers_window_sp.get());
6410       } else {
6411         Rect new_regs_rect;
6412         if (variables_window_sp) {
6413           // We have a variables window, split it into two columns where the
6414           // left hand side will be the variables and the right hand side will
6415           // be the registers
6416           const Rect variables_bounds = variables_window_sp->GetBounds();
6417           Rect new_vars_rect;
6418           variables_bounds.VerticalSplitPercentage(0.50, new_vars_rect,
6419                                                    new_regs_rect);
6420           variables_window_sp->SetBounds(new_vars_rect);
6421         } else {
6422           // No variables window, grab the bottom part of the source window
6423           Rect new_source_rect;
6424           source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
6425                                                   new_regs_rect);
6426           source_window_sp->SetBounds(new_source_rect);
6427         }
6428         WindowSP new_window_sp =
6429             main_window_sp->CreateSubWindow("Registers", new_regs_rect, false);
6430         new_window_sp->SetDelegate(
6431             WindowDelegateSP(new RegistersWindowDelegate(m_debugger)));
6432       }
6433       touchwin(stdscr);
6434     }
6435       return MenuActionResult::Handled;
6436 
6437     case eMenuID_ViewBreakpoints: {
6438       WindowSP main_window_sp = m_app.GetMainWindow();
6439       WindowSP threads_window_sp = main_window_sp->FindSubWindow("Threads");
6440       WindowSP breakpoints_window_sp =
6441           main_window_sp->FindSubWindow("Breakpoints");
6442       const Rect threads_bounds = threads_window_sp->GetBounds();
6443 
6444       // If a breakpoints window already exists, remove it and give the area
6445       // it used to occupy to the threads window. If it doesn't exist, split
6446       // the threads window horizontally into two windows where the top window
6447       // is the threads window and the bottom window is a newly added
6448       // breakpoints window.
6449       if (breakpoints_window_sp) {
6450         threads_window_sp->Resize(threads_bounds.size.width,
6451                                   threads_bounds.size.height +
6452                                       breakpoints_window_sp->GetHeight());
6453         main_window_sp->RemoveSubWindow(breakpoints_window_sp.get());
6454       } else {
6455         Rect new_threads_bounds, breakpoints_bounds;
6456         threads_bounds.HorizontalSplitPercentage(0.70, new_threads_bounds,
6457                                                  breakpoints_bounds);
6458         threads_window_sp->SetBounds(new_threads_bounds);
6459         breakpoints_window_sp = main_window_sp->CreateSubWindow(
6460             "Breakpoints", breakpoints_bounds, false);
6461         TreeDelegateSP breakpoints_delegate_sp(
6462             new BreakpointsTreeDelegate(m_debugger));
6463         breakpoints_window_sp->SetDelegate(WindowDelegateSP(
6464             new TreeWindowDelegate(m_debugger, breakpoints_delegate_sp)));
6465       }
6466       touchwin(stdscr);
6467       return MenuActionResult::Handled;
6468     }
6469 
6470     case eMenuID_HelpGUIHelp:
6471       m_app.GetMainWindow()->CreateHelpSubwindow();
6472       return MenuActionResult::Handled;
6473 
6474     default:
6475       break;
6476     }
6477 
6478     return MenuActionResult::NotHandled;
6479   }
6480 
6481 protected:
6482   Application &m_app;
6483   Debugger &m_debugger;
6484 };
6485 
6486 class StatusBarWindowDelegate : public WindowDelegate {
6487 public:
6488   StatusBarWindowDelegate(Debugger &debugger) : m_debugger(debugger) {
6489     FormatEntity::Parse("Thread: ${thread.id%tid}", m_format);
6490   }
6491 
6492   ~StatusBarWindowDelegate() override = default;
6493 
6494   bool WindowDelegateDraw(Window &window, bool force) override {
6495     ExecutionContext exe_ctx =
6496         m_debugger.GetCommandInterpreter().GetExecutionContext();
6497     Process *process = exe_ctx.GetProcessPtr();
6498     Thread *thread = exe_ctx.GetThreadPtr();
6499     StackFrame *frame = exe_ctx.GetFramePtr();
6500     window.Erase();
6501     window.SetBackground(BlackOnWhite);
6502     window.MoveCursor(0, 0);
6503     if (process) {
6504       const StateType state = process->GetState();
6505       window.Printf("Process: %5" PRIu64 " %10s", process->GetID(),
6506                     StateAsCString(state));
6507 
6508       if (StateIsStoppedState(state, true)) {
6509         StreamString strm;
6510         if (thread && FormatEntity::Format(m_format, strm, nullptr, &exe_ctx,
6511                                            nullptr, nullptr, false, false)) {
6512           window.MoveCursor(40, 0);
6513           window.PutCStringTruncated(1, strm.GetString().str().c_str());
6514         }
6515 
6516         window.MoveCursor(60, 0);
6517         if (frame)
6518           window.Printf("Frame: %3u  PC = 0x%16.16" PRIx64,
6519                         frame->GetFrameIndex(),
6520                         frame->GetFrameCodeAddress().GetOpcodeLoadAddress(
6521                             exe_ctx.GetTargetPtr()));
6522       } else if (state == eStateExited) {
6523         const char *exit_desc = process->GetExitDescription();
6524         const int exit_status = process->GetExitStatus();
6525         if (exit_desc && exit_desc[0])
6526           window.Printf(" with status = %i (%s)", exit_status, exit_desc);
6527         else
6528           window.Printf(" with status = %i", exit_status);
6529       }
6530     }
6531     return true;
6532   }
6533 
6534 protected:
6535   Debugger &m_debugger;
6536   FormatEntity::Entry m_format;
6537 };
6538 
6539 class SourceFileWindowDelegate : public WindowDelegate {
6540 public:
6541   SourceFileWindowDelegate(Debugger &debugger)
6542       : WindowDelegate(), m_debugger(debugger), m_sc(), m_file_sp(),
6543         m_disassembly_scope(nullptr), m_disassembly_sp(), m_disassembly_range(),
6544         m_title(), m_tid(LLDB_INVALID_THREAD_ID), m_line_width(4),
6545         m_selected_line(0), m_pc_line(0), m_stop_id(0), m_frame_idx(UINT32_MAX),
6546         m_first_visible_line(0), m_first_visible_column(0), m_min_x(0),
6547         m_min_y(0), m_max_x(0), m_max_y(0) {}
6548 
6549   ~SourceFileWindowDelegate() override = default;
6550 
6551   void Update(const SymbolContext &sc) { m_sc = sc; }
6552 
6553   uint32_t NumVisibleLines() const { return m_max_y - m_min_y; }
6554 
6555   const char *WindowDelegateGetHelpText() override {
6556     return "Source/Disassembly window keyboard shortcuts:";
6557   }
6558 
6559   KeyHelp *WindowDelegateGetKeyHelp() override {
6560     static curses::KeyHelp g_source_view_key_help[] = {
6561         {KEY_RETURN, "Run to selected line with one shot breakpoint"},
6562         {KEY_UP, "Select previous source line"},
6563         {KEY_DOWN, "Select next source line"},
6564         {KEY_LEFT, "Scroll to the left"},
6565         {KEY_RIGHT, "Scroll to the right"},
6566         {KEY_PPAGE, "Page up"},
6567         {KEY_NPAGE, "Page down"},
6568         {'b', "Set breakpoint on selected source/disassembly line"},
6569         {'c', "Continue process"},
6570         {'D', "Detach with process suspended"},
6571         {'h', "Show help dialog"},
6572         {'n', "Step over (source line)"},
6573         {'N', "Step over (single instruction)"},
6574         {'f', "Step out (finish)"},
6575         {'s', "Step in (source line)"},
6576         {'S', "Step in (single instruction)"},
6577         {'u', "Frame up"},
6578         {'d', "Frame down"},
6579         {',', "Page up"},
6580         {'.', "Page down"},
6581         {'\0', nullptr}};
6582     return g_source_view_key_help;
6583   }
6584 
6585   bool WindowDelegateDraw(Window &window, bool force) override {
6586     ExecutionContext exe_ctx =
6587         m_debugger.GetCommandInterpreter().GetExecutionContext();
6588     Process *process = exe_ctx.GetProcessPtr();
6589     Thread *thread = nullptr;
6590 
6591     bool update_location = false;
6592     if (process) {
6593       StateType state = process->GetState();
6594       if (StateIsStoppedState(state, true)) {
6595         // We are stopped, so it is ok to
6596         update_location = true;
6597       }
6598     }
6599 
6600     m_min_x = 1;
6601     m_min_y = 2;
6602     m_max_x = window.GetMaxX() - 1;
6603     m_max_y = window.GetMaxY() - 1;
6604 
6605     const uint32_t num_visible_lines = NumVisibleLines();
6606     StackFrameSP frame_sp;
6607     bool set_selected_line_to_pc = false;
6608 
6609     if (update_location) {
6610       const bool process_alive = process ? process->IsAlive() : false;
6611       bool thread_changed = false;
6612       if (process_alive) {
6613         thread = exe_ctx.GetThreadPtr();
6614         if (thread) {
6615           frame_sp = thread->GetSelectedFrame();
6616           auto tid = thread->GetID();
6617           thread_changed = tid != m_tid;
6618           m_tid = tid;
6619         } else {
6620           if (m_tid != LLDB_INVALID_THREAD_ID) {
6621             thread_changed = true;
6622             m_tid = LLDB_INVALID_THREAD_ID;
6623           }
6624         }
6625       }
6626       const uint32_t stop_id = process ? process->GetStopID() : 0;
6627       const bool stop_id_changed = stop_id != m_stop_id;
6628       bool frame_changed = false;
6629       m_stop_id = stop_id;
6630       m_title.Clear();
6631       if (frame_sp) {
6632         m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
6633         if (m_sc.module_sp) {
6634           m_title.Printf(
6635               "%s", m_sc.module_sp->GetFileSpec().GetFilename().GetCString());
6636           ConstString func_name = m_sc.GetFunctionName();
6637           if (func_name)
6638             m_title.Printf("`%s", func_name.GetCString());
6639         }
6640         const uint32_t frame_idx = frame_sp->GetFrameIndex();
6641         frame_changed = frame_idx != m_frame_idx;
6642         m_frame_idx = frame_idx;
6643       } else {
6644         m_sc.Clear(true);
6645         frame_changed = m_frame_idx != UINT32_MAX;
6646         m_frame_idx = UINT32_MAX;
6647       }
6648 
6649       const bool context_changed =
6650           thread_changed || frame_changed || stop_id_changed;
6651 
6652       if (process_alive) {
6653         if (m_sc.line_entry.IsValid()) {
6654           m_pc_line = m_sc.line_entry.line;
6655           if (m_pc_line != UINT32_MAX)
6656             --m_pc_line; // Convert to zero based line number...
6657           // Update the selected line if the stop ID changed...
6658           if (context_changed)
6659             m_selected_line = m_pc_line;
6660 
6661           if (m_file_sp && m_file_sp->GetFileSpec() == m_sc.line_entry.file) {
6662             // Same file, nothing to do, we should either have the lines or
6663             // not (source file missing)
6664             if (m_selected_line >= static_cast<size_t>(m_first_visible_line)) {
6665               if (m_selected_line >= m_first_visible_line + num_visible_lines)
6666                 m_first_visible_line = m_selected_line - 10;
6667             } else {
6668               if (m_selected_line > 10)
6669                 m_first_visible_line = m_selected_line - 10;
6670               else
6671                 m_first_visible_line = 0;
6672             }
6673           } else {
6674             // File changed, set selected line to the line with the PC
6675             m_selected_line = m_pc_line;
6676             m_file_sp =
6677                 m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file);
6678             if (m_file_sp) {
6679               const size_t num_lines = m_file_sp->GetNumLines();
6680               m_line_width = 1;
6681               for (size_t n = num_lines; n >= 10; n = n / 10)
6682                 ++m_line_width;
6683 
6684               if (num_lines < num_visible_lines ||
6685                   m_selected_line < num_visible_lines)
6686                 m_first_visible_line = 0;
6687               else
6688                 m_first_visible_line = m_selected_line - 10;
6689             }
6690           }
6691         } else {
6692           m_file_sp.reset();
6693         }
6694 
6695         if (!m_file_sp || m_file_sp->GetNumLines() == 0) {
6696           // Show disassembly
6697           bool prefer_file_cache = false;
6698           if (m_sc.function) {
6699             if (m_disassembly_scope != m_sc.function) {
6700               m_disassembly_scope = m_sc.function;
6701               m_disassembly_sp = m_sc.function->GetInstructions(
6702                   exe_ctx, nullptr, !prefer_file_cache);
6703               if (m_disassembly_sp) {
6704                 set_selected_line_to_pc = true;
6705                 m_disassembly_range = m_sc.function->GetAddressRange();
6706               } else {
6707                 m_disassembly_range.Clear();
6708               }
6709             } else {
6710               set_selected_line_to_pc = context_changed;
6711             }
6712           } else if (m_sc.symbol) {
6713             if (m_disassembly_scope != m_sc.symbol) {
6714               m_disassembly_scope = m_sc.symbol;
6715               m_disassembly_sp = m_sc.symbol->GetInstructions(
6716                   exe_ctx, nullptr, prefer_file_cache);
6717               if (m_disassembly_sp) {
6718                 set_selected_line_to_pc = true;
6719                 m_disassembly_range.GetBaseAddress() =
6720                     m_sc.symbol->GetAddress();
6721                 m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize());
6722               } else {
6723                 m_disassembly_range.Clear();
6724               }
6725             } else {
6726               set_selected_line_to_pc = context_changed;
6727             }
6728           }
6729         }
6730       } else {
6731         m_pc_line = UINT32_MAX;
6732       }
6733     }
6734 
6735     const int window_width = window.GetWidth();
6736     window.Erase();
6737     window.DrawTitleBox("Sources");
6738     if (!m_title.GetString().empty()) {
6739       window.AttributeOn(A_REVERSE);
6740       window.MoveCursor(1, 1);
6741       window.PutChar(' ');
6742       window.PutCStringTruncated(1, m_title.GetString().str().c_str());
6743       int x = window.GetCursorX();
6744       if (x < window_width - 1) {
6745         window.Printf("%*s", window_width - x - 1, "");
6746       }
6747       window.AttributeOff(A_REVERSE);
6748     }
6749 
6750     Target *target = exe_ctx.GetTargetPtr();
6751     const size_t num_source_lines = GetNumSourceLines();
6752     if (num_source_lines > 0) {
6753       // Display source
6754       BreakpointLines bp_lines;
6755       if (target) {
6756         BreakpointList &bp_list = target->GetBreakpointList();
6757         const size_t num_bps = bp_list.GetSize();
6758         for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
6759           BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
6760           const size_t num_bps_locs = bp_sp->GetNumLocations();
6761           for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
6762             BreakpointLocationSP bp_loc_sp =
6763                 bp_sp->GetLocationAtIndex(bp_loc_idx);
6764             LineEntry bp_loc_line_entry;
6765             if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry(
6766                     bp_loc_line_entry)) {
6767               if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file) {
6768                 bp_lines.insert(bp_loc_line_entry.line);
6769               }
6770             }
6771           }
6772         }
6773       }
6774 
6775       const attr_t selected_highlight_attr = A_REVERSE;
6776       const attr_t pc_highlight_attr = COLOR_PAIR(BlackOnBlue);
6777 
6778       for (size_t i = 0; i < num_visible_lines; ++i) {
6779         const uint32_t curr_line = m_first_visible_line + i;
6780         if (curr_line < num_source_lines) {
6781           const int line_y = m_min_y + i;
6782           window.MoveCursor(1, line_y);
6783           const bool is_pc_line = curr_line == m_pc_line;
6784           const bool line_is_selected = m_selected_line == curr_line;
6785           // Highlight the line as the PC line first, then if the selected
6786           // line isn't the same as the PC line, highlight it differently
6787           attr_t highlight_attr = 0;
6788           attr_t bp_attr = 0;
6789           if (is_pc_line)
6790             highlight_attr = pc_highlight_attr;
6791           else if (line_is_selected)
6792             highlight_attr = selected_highlight_attr;
6793 
6794           if (bp_lines.find(curr_line + 1) != bp_lines.end())
6795             bp_attr = COLOR_PAIR(BlackOnWhite);
6796 
6797           if (bp_attr)
6798             window.AttributeOn(bp_attr);
6799 
6800           window.Printf(" %*u ", m_line_width, curr_line + 1);
6801 
6802           if (bp_attr)
6803             window.AttributeOff(bp_attr);
6804 
6805           window.PutChar(ACS_VLINE);
6806           // Mark the line with the PC with a diamond
6807           if (is_pc_line)
6808             window.PutChar(ACS_DIAMOND);
6809           else
6810             window.PutChar(' ');
6811 
6812           if (highlight_attr)
6813             window.AttributeOn(highlight_attr);
6814 
6815           StreamString lineStream;
6816           m_file_sp->DisplaySourceLines(curr_line + 1, {}, 0, 0, &lineStream);
6817           StringRef line = lineStream.GetString();
6818           if (line.endswith("\n"))
6819             line = line.drop_back();
6820           bool wasWritten = window.OutputColoredStringTruncated(
6821               1, line, m_first_visible_column, line_is_selected);
6822           if (line_is_selected && !wasWritten) {
6823             // Draw an empty space to show the selected line if empty,
6824             // or draw '<' if nothing is visible because of scrolling too much
6825             // to the right.
6826             window.PutCStringTruncated(
6827                 1, line.empty() && m_first_visible_column == 0 ? " " : "<");
6828           }
6829 
6830           if (is_pc_line && frame_sp &&
6831               frame_sp->GetConcreteFrameIndex() == 0) {
6832             StopInfoSP stop_info_sp;
6833             if (thread)
6834               stop_info_sp = thread->GetStopInfo();
6835             if (stop_info_sp) {
6836               const char *stop_description = stop_info_sp->GetDescription();
6837               if (stop_description && stop_description[0]) {
6838                 size_t stop_description_len = strlen(stop_description);
6839                 int desc_x = window_width - stop_description_len - 16;
6840                 if (desc_x - window.GetCursorX() > 0)
6841                   window.Printf("%*s", desc_x - window.GetCursorX(), "");
6842                 window.MoveCursor(window_width - stop_description_len - 16,
6843                                   line_y);
6844                 const attr_t stop_reason_attr = COLOR_PAIR(WhiteOnBlue);
6845                 window.AttributeOn(stop_reason_attr);
6846                 window.PrintfTruncated(1, " <<< Thread %u: %s ",
6847                                        thread->GetIndexID(), stop_description);
6848                 window.AttributeOff(stop_reason_attr);
6849               }
6850             } else {
6851               window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
6852             }
6853           }
6854           if (highlight_attr)
6855             window.AttributeOff(highlight_attr);
6856         } else {
6857           break;
6858         }
6859       }
6860     } else {
6861       size_t num_disassembly_lines = GetNumDisassemblyLines();
6862       if (num_disassembly_lines > 0) {
6863         // Display disassembly
6864         BreakpointAddrs bp_file_addrs;
6865         Target *target = exe_ctx.GetTargetPtr();
6866         if (target) {
6867           BreakpointList &bp_list = target->GetBreakpointList();
6868           const size_t num_bps = bp_list.GetSize();
6869           for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
6870             BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
6871             const size_t num_bps_locs = bp_sp->GetNumLocations();
6872             for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs;
6873                  ++bp_loc_idx) {
6874               BreakpointLocationSP bp_loc_sp =
6875                   bp_sp->GetLocationAtIndex(bp_loc_idx);
6876               LineEntry bp_loc_line_entry;
6877               const lldb::addr_t file_addr =
6878                   bp_loc_sp->GetAddress().GetFileAddress();
6879               if (file_addr != LLDB_INVALID_ADDRESS) {
6880                 if (m_disassembly_range.ContainsFileAddress(file_addr))
6881                   bp_file_addrs.insert(file_addr);
6882               }
6883             }
6884           }
6885         }
6886 
6887         const attr_t selected_highlight_attr = A_REVERSE;
6888         const attr_t pc_highlight_attr = COLOR_PAIR(WhiteOnBlue);
6889 
6890         StreamString strm;
6891 
6892         InstructionList &insts = m_disassembly_sp->GetInstructionList();
6893         Address pc_address;
6894 
6895         if (frame_sp)
6896           pc_address = frame_sp->GetFrameCodeAddress();
6897         const uint32_t pc_idx =
6898             pc_address.IsValid()
6899                 ? insts.GetIndexOfInstructionAtAddress(pc_address)
6900                 : UINT32_MAX;
6901         if (set_selected_line_to_pc) {
6902           m_selected_line = pc_idx;
6903         }
6904 
6905         const uint32_t non_visible_pc_offset = (num_visible_lines / 5);
6906         if (static_cast<size_t>(m_first_visible_line) >= num_disassembly_lines)
6907           m_first_visible_line = 0;
6908 
6909         if (pc_idx < num_disassembly_lines) {
6910           if (pc_idx < static_cast<uint32_t>(m_first_visible_line) ||
6911               pc_idx >= m_first_visible_line + num_visible_lines)
6912             m_first_visible_line = pc_idx - non_visible_pc_offset;
6913         }
6914 
6915         for (size_t i = 0; i < num_visible_lines; ++i) {
6916           const uint32_t inst_idx = m_first_visible_line + i;
6917           Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get();
6918           if (!inst)
6919             break;
6920 
6921           const int line_y = m_min_y + i;
6922           window.MoveCursor(1, line_y);
6923           const bool is_pc_line = frame_sp && inst_idx == pc_idx;
6924           const bool line_is_selected = m_selected_line == inst_idx;
6925           // Highlight the line as the PC line first, then if the selected
6926           // line isn't the same as the PC line, highlight it differently
6927           attr_t highlight_attr = 0;
6928           attr_t bp_attr = 0;
6929           if (is_pc_line)
6930             highlight_attr = pc_highlight_attr;
6931           else if (line_is_selected)
6932             highlight_attr = selected_highlight_attr;
6933 
6934           if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) !=
6935               bp_file_addrs.end())
6936             bp_attr = COLOR_PAIR(BlackOnWhite);
6937 
6938           if (bp_attr)
6939             window.AttributeOn(bp_attr);
6940 
6941           window.Printf(" 0x%16.16llx ",
6942                         static_cast<unsigned long long>(
6943                             inst->GetAddress().GetLoadAddress(target)));
6944 
6945           if (bp_attr)
6946             window.AttributeOff(bp_attr);
6947 
6948           window.PutChar(ACS_VLINE);
6949           // Mark the line with the PC with a diamond
6950           if (is_pc_line)
6951             window.PutChar(ACS_DIAMOND);
6952           else
6953             window.PutChar(' ');
6954 
6955           if (highlight_attr)
6956             window.AttributeOn(highlight_attr);
6957 
6958           const char *mnemonic = inst->GetMnemonic(&exe_ctx);
6959           const char *operands = inst->GetOperands(&exe_ctx);
6960           const char *comment = inst->GetComment(&exe_ctx);
6961 
6962           if (mnemonic != nullptr && mnemonic[0] == '\0')
6963             mnemonic = nullptr;
6964           if (operands != nullptr && operands[0] == '\0')
6965             operands = nullptr;
6966           if (comment != nullptr && comment[0] == '\0')
6967             comment = nullptr;
6968 
6969           strm.Clear();
6970 
6971           if (mnemonic != nullptr && operands != nullptr && comment != nullptr)
6972             strm.Printf("%-8s %-25s ; %s", mnemonic, operands, comment);
6973           else if (mnemonic != nullptr && operands != nullptr)
6974             strm.Printf("%-8s %s", mnemonic, operands);
6975           else if (mnemonic != nullptr)
6976             strm.Printf("%s", mnemonic);
6977 
6978           int right_pad = 1;
6979           window.PutCStringTruncated(
6980               right_pad,
6981               strm.GetString().substr(m_first_visible_column).data());
6982 
6983           if (is_pc_line && frame_sp &&
6984               frame_sp->GetConcreteFrameIndex() == 0) {
6985             StopInfoSP stop_info_sp;
6986             if (thread)
6987               stop_info_sp = thread->GetStopInfo();
6988             if (stop_info_sp) {
6989               const char *stop_description = stop_info_sp->GetDescription();
6990               if (stop_description && stop_description[0]) {
6991                 size_t stop_description_len = strlen(stop_description);
6992                 int desc_x = window_width - stop_description_len - 16;
6993                 if (desc_x - window.GetCursorX() > 0)
6994                   window.Printf("%*s", desc_x - window.GetCursorX(), "");
6995                 window.MoveCursor(window_width - stop_description_len - 15,
6996                                   line_y);
6997                 window.PrintfTruncated(1, "<<< Thread %u: %s ",
6998                                        thread->GetIndexID(), stop_description);
6999               }
7000             } else {
7001               window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
7002             }
7003           }
7004           if (highlight_attr)
7005             window.AttributeOff(highlight_attr);
7006         }
7007       }
7008     }
7009     return true; // Drawing handled
7010   }
7011 
7012   size_t GetNumLines() {
7013     size_t num_lines = GetNumSourceLines();
7014     if (num_lines == 0)
7015       num_lines = GetNumDisassemblyLines();
7016     return num_lines;
7017   }
7018 
7019   size_t GetNumSourceLines() const {
7020     if (m_file_sp)
7021       return m_file_sp->GetNumLines();
7022     return 0;
7023   }
7024 
7025   size_t GetNumDisassemblyLines() const {
7026     if (m_disassembly_sp)
7027       return m_disassembly_sp->GetInstructionList().GetSize();
7028     return 0;
7029   }
7030 
7031   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
7032     const uint32_t num_visible_lines = NumVisibleLines();
7033     const size_t num_lines = GetNumLines();
7034 
7035     switch (c) {
7036     case ',':
7037     case KEY_PPAGE:
7038       // Page up key
7039       if (static_cast<uint32_t>(m_first_visible_line) > num_visible_lines)
7040         m_first_visible_line -= num_visible_lines;
7041       else
7042         m_first_visible_line = 0;
7043       m_selected_line = m_first_visible_line;
7044       return eKeyHandled;
7045 
7046     case '.':
7047     case KEY_NPAGE:
7048       // Page down key
7049       {
7050         if (m_first_visible_line + num_visible_lines < num_lines)
7051           m_first_visible_line += num_visible_lines;
7052         else if (num_lines < num_visible_lines)
7053           m_first_visible_line = 0;
7054         else
7055           m_first_visible_line = num_lines - num_visible_lines;
7056         m_selected_line = m_first_visible_line;
7057       }
7058       return eKeyHandled;
7059 
7060     case KEY_UP:
7061       if (m_selected_line > 0) {
7062         m_selected_line--;
7063         if (static_cast<size_t>(m_first_visible_line) > m_selected_line)
7064           m_first_visible_line = m_selected_line;
7065       }
7066       return eKeyHandled;
7067 
7068     case KEY_DOWN:
7069       if (m_selected_line + 1 < num_lines) {
7070         m_selected_line++;
7071         if (m_first_visible_line + num_visible_lines < m_selected_line)
7072           m_first_visible_line++;
7073       }
7074       return eKeyHandled;
7075 
7076     case KEY_LEFT:
7077       if (m_first_visible_column > 0)
7078         --m_first_visible_column;
7079       return eKeyHandled;
7080 
7081     case KEY_RIGHT:
7082       ++m_first_visible_column;
7083       return eKeyHandled;
7084 
7085     case '\r':
7086     case '\n':
7087     case KEY_ENTER:
7088       // Set a breakpoint and run to the line using a one shot breakpoint
7089       if (GetNumSourceLines() > 0) {
7090         ExecutionContext exe_ctx =
7091             m_debugger.GetCommandInterpreter().GetExecutionContext();
7092         if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive()) {
7093           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
7094               nullptr, // Don't limit the breakpoint to certain modules
7095               m_file_sp->GetFileSpec(), // Source file
7096               m_selected_line +
7097                   1, // Source line number (m_selected_line is zero based)
7098               0,     // Unspecified column.
7099               0,     // No offset
7100               eLazyBoolCalculate,  // Check inlines using global setting
7101               eLazyBoolCalculate,  // Skip prologue using global setting,
7102               false,               // internal
7103               false,               // request_hardware
7104               eLazyBoolCalculate); // move_to_nearest_code
7105           // Make breakpoint one shot
7106           bp_sp->GetOptions().SetOneShot(true);
7107           exe_ctx.GetProcessRef().Resume();
7108         }
7109       } else if (m_selected_line < GetNumDisassemblyLines()) {
7110         const Instruction *inst = m_disassembly_sp->GetInstructionList()
7111                                       .GetInstructionAtIndex(m_selected_line)
7112                                       .get();
7113         ExecutionContext exe_ctx =
7114             m_debugger.GetCommandInterpreter().GetExecutionContext();
7115         if (exe_ctx.HasTargetScope()) {
7116           Address addr = inst->GetAddress();
7117           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
7118               addr,   // lldb_private::Address
7119               false,  // internal
7120               false); // request_hardware
7121           // Make breakpoint one shot
7122           bp_sp->GetOptions().SetOneShot(true);
7123           exe_ctx.GetProcessRef().Resume();
7124         }
7125       }
7126       return eKeyHandled;
7127 
7128     case 'b': // 'b' == toggle breakpoint on currently selected line
7129       ToggleBreakpointOnSelectedLine();
7130       return eKeyHandled;
7131 
7132     case 'D': // 'D' == detach and keep stopped
7133     {
7134       ExecutionContext exe_ctx =
7135           m_debugger.GetCommandInterpreter().GetExecutionContext();
7136       if (exe_ctx.HasProcessScope())
7137         exe_ctx.GetProcessRef().Detach(true);
7138     }
7139       return eKeyHandled;
7140 
7141     case 'c':
7142       // 'c' == continue
7143       {
7144         ExecutionContext exe_ctx =
7145             m_debugger.GetCommandInterpreter().GetExecutionContext();
7146         if (exe_ctx.HasProcessScope())
7147           exe_ctx.GetProcessRef().Resume();
7148       }
7149       return eKeyHandled;
7150 
7151     case 'f':
7152       // 'f' == step out (finish)
7153       {
7154         ExecutionContext exe_ctx =
7155             m_debugger.GetCommandInterpreter().GetExecutionContext();
7156         if (exe_ctx.HasThreadScope() &&
7157             StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
7158           exe_ctx.GetThreadRef().StepOut();
7159         }
7160       }
7161       return eKeyHandled;
7162 
7163     case 'n': // 'n' == step over
7164     case 'N': // 'N' == step over instruction
7165     {
7166       ExecutionContext exe_ctx =
7167           m_debugger.GetCommandInterpreter().GetExecutionContext();
7168       if (exe_ctx.HasThreadScope() &&
7169           StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
7170         bool source_step = (c == 'n');
7171         exe_ctx.GetThreadRef().StepOver(source_step);
7172       }
7173     }
7174       return eKeyHandled;
7175 
7176     case 's': // 's' == step into
7177     case 'S': // 'S' == step into instruction
7178     {
7179       ExecutionContext exe_ctx =
7180           m_debugger.GetCommandInterpreter().GetExecutionContext();
7181       if (exe_ctx.HasThreadScope() &&
7182           StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
7183         bool source_step = (c == 's');
7184         exe_ctx.GetThreadRef().StepIn(source_step);
7185       }
7186     }
7187       return eKeyHandled;
7188 
7189     case 'u': // 'u' == frame up
7190     case 'd': // 'd' == frame down
7191     {
7192       ExecutionContext exe_ctx =
7193           m_debugger.GetCommandInterpreter().GetExecutionContext();
7194       if (exe_ctx.HasThreadScope()) {
7195         Thread *thread = exe_ctx.GetThreadPtr();
7196         uint32_t frame_idx = thread->GetSelectedFrameIndex();
7197         if (frame_idx == UINT32_MAX)
7198           frame_idx = 0;
7199         if (c == 'u' && frame_idx + 1 < thread->GetStackFrameCount())
7200           ++frame_idx;
7201         else if (c == 'd' && frame_idx > 0)
7202           --frame_idx;
7203         if (thread->SetSelectedFrameByIndex(frame_idx, true))
7204           exe_ctx.SetFrameSP(thread->GetSelectedFrame());
7205       }
7206     }
7207       return eKeyHandled;
7208 
7209     case 'h':
7210       window.CreateHelpSubwindow();
7211       return eKeyHandled;
7212 
7213     default:
7214       break;
7215     }
7216     return eKeyNotHandled;
7217   }
7218 
7219   void ToggleBreakpointOnSelectedLine() {
7220     ExecutionContext exe_ctx =
7221         m_debugger.GetCommandInterpreter().GetExecutionContext();
7222     if (!exe_ctx.HasTargetScope())
7223       return;
7224     if (GetNumSourceLines() > 0) {
7225       // Source file breakpoint.
7226       BreakpointList &bp_list = exe_ctx.GetTargetRef().GetBreakpointList();
7227       const size_t num_bps = bp_list.GetSize();
7228       for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
7229         BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
7230         const size_t num_bps_locs = bp_sp->GetNumLocations();
7231         for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
7232           BreakpointLocationSP bp_loc_sp =
7233               bp_sp->GetLocationAtIndex(bp_loc_idx);
7234           LineEntry bp_loc_line_entry;
7235           if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry(
7236                   bp_loc_line_entry)) {
7237             if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file &&
7238                 m_selected_line + 1 == bp_loc_line_entry.line) {
7239               bool removed =
7240                   exe_ctx.GetTargetRef().RemoveBreakpointByID(bp_sp->GetID());
7241               assert(removed);
7242               UNUSED_IF_ASSERT_DISABLED(removed);
7243               return; // Existing breakpoint removed.
7244             }
7245           }
7246         }
7247       }
7248       // No breakpoint found on the location, add it.
7249       BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
7250           nullptr, // Don't limit the breakpoint to certain modules
7251           m_file_sp->GetFileSpec(), // Source file
7252           m_selected_line +
7253               1, // Source line number (m_selected_line is zero based)
7254           0,     // No column specified.
7255           0,     // No offset
7256           eLazyBoolCalculate,  // Check inlines using global setting
7257           eLazyBoolCalculate,  // Skip prologue using global setting,
7258           false,               // internal
7259           false,               // request_hardware
7260           eLazyBoolCalculate); // move_to_nearest_code
7261     } else {
7262       // Disassembly breakpoint.
7263       assert(GetNumDisassemblyLines() > 0);
7264       assert(m_selected_line < GetNumDisassemblyLines());
7265       const Instruction *inst = m_disassembly_sp->GetInstructionList()
7266                                     .GetInstructionAtIndex(m_selected_line)
7267                                     .get();
7268       Address addr = inst->GetAddress();
7269       // Try to find it.
7270       BreakpointList &bp_list = exe_ctx.GetTargetRef().GetBreakpointList();
7271       const size_t num_bps = bp_list.GetSize();
7272       for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
7273         BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
7274         const size_t num_bps_locs = bp_sp->GetNumLocations();
7275         for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
7276           BreakpointLocationSP bp_loc_sp =
7277               bp_sp->GetLocationAtIndex(bp_loc_idx);
7278           LineEntry bp_loc_line_entry;
7279           const lldb::addr_t file_addr =
7280               bp_loc_sp->GetAddress().GetFileAddress();
7281           if (file_addr == addr.GetFileAddress()) {
7282             bool removed =
7283                 exe_ctx.GetTargetRef().RemoveBreakpointByID(bp_sp->GetID());
7284             assert(removed);
7285             UNUSED_IF_ASSERT_DISABLED(removed);
7286             return; // Existing breakpoint removed.
7287           }
7288         }
7289       }
7290       // No breakpoint found on the address, add it.
7291       BreakpointSP bp_sp =
7292           exe_ctx.GetTargetRef().CreateBreakpoint(addr, // lldb_private::Address
7293                                                   false,  // internal
7294                                                   false); // request_hardware
7295     }
7296   }
7297 
7298 protected:
7299   typedef std::set<uint32_t> BreakpointLines;
7300   typedef std::set<lldb::addr_t> BreakpointAddrs;
7301 
7302   Debugger &m_debugger;
7303   SymbolContext m_sc;
7304   SourceManager::FileSP m_file_sp;
7305   SymbolContextScope *m_disassembly_scope;
7306   lldb::DisassemblerSP m_disassembly_sp;
7307   AddressRange m_disassembly_range;
7308   StreamString m_title;
7309   lldb::user_id_t m_tid;
7310   int m_line_width;
7311   uint32_t m_selected_line; // The selected line
7312   uint32_t m_pc_line;       // The line with the PC
7313   uint32_t m_stop_id;
7314   uint32_t m_frame_idx;
7315   int m_first_visible_line;
7316   int m_first_visible_column;
7317   int m_min_x;
7318   int m_min_y;
7319   int m_max_x;
7320   int m_max_y;
7321 };
7322 
7323 DisplayOptions ValueObjectListDelegate::g_options = {true};
7324 
7325 IOHandlerCursesGUI::IOHandlerCursesGUI(Debugger &debugger)
7326     : IOHandler(debugger, IOHandler::Type::Curses) {}
7327 
7328 void IOHandlerCursesGUI::Activate() {
7329   IOHandler::Activate();
7330   if (!m_app_ap) {
7331     m_app_ap = std::make_unique<Application>(GetInputFILE(), GetOutputFILE());
7332 
7333     // This is both a window and a menu delegate
7334     std::shared_ptr<ApplicationDelegate> app_delegate_sp(
7335         new ApplicationDelegate(*m_app_ap, m_debugger));
7336 
7337     MenuDelegateSP app_menu_delegate_sp =
7338         std::static_pointer_cast<MenuDelegate>(app_delegate_sp);
7339     MenuSP lldb_menu_sp(
7340         new Menu("LLDB", "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB));
7341     MenuSP exit_menuitem_sp(
7342         new Menu("Exit", nullptr, 'x', ApplicationDelegate::eMenuID_LLDBExit));
7343     exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit);
7344     lldb_menu_sp->AddSubmenu(MenuSP(new Menu(
7345         "About LLDB", nullptr, 'a', ApplicationDelegate::eMenuID_LLDBAbout)));
7346     lldb_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
7347     lldb_menu_sp->AddSubmenu(exit_menuitem_sp);
7348 
7349     MenuSP target_menu_sp(new Menu("Target", "F2", KEY_F(2),
7350                                    ApplicationDelegate::eMenuID_Target));
7351     target_menu_sp->AddSubmenu(MenuSP(new Menu(
7352         "Create", nullptr, 'c', ApplicationDelegate::eMenuID_TargetCreate)));
7353     target_menu_sp->AddSubmenu(MenuSP(new Menu(
7354         "Delete", nullptr, 'd', ApplicationDelegate::eMenuID_TargetDelete)));
7355 
7356     MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3),
7357                                     ApplicationDelegate::eMenuID_Process));
7358     process_menu_sp->AddSubmenu(MenuSP(new Menu(
7359         "Attach", nullptr, 'a', ApplicationDelegate::eMenuID_ProcessAttach)));
7360     process_menu_sp->AddSubmenu(
7361         MenuSP(new Menu("Detach and resume", nullptr, 'd',
7362                         ApplicationDelegate::eMenuID_ProcessDetachResume)));
7363     process_menu_sp->AddSubmenu(
7364         MenuSP(new Menu("Detach suspended", nullptr, 's',
7365                         ApplicationDelegate::eMenuID_ProcessDetachSuspended)));
7366     process_menu_sp->AddSubmenu(MenuSP(new Menu(
7367         "Launch", nullptr, 'l', ApplicationDelegate::eMenuID_ProcessLaunch)));
7368     process_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
7369     process_menu_sp->AddSubmenu(
7370         MenuSP(new Menu("Continue", nullptr, 'c',
7371                         ApplicationDelegate::eMenuID_ProcessContinue)));
7372     process_menu_sp->AddSubmenu(MenuSP(new Menu(
7373         "Halt", nullptr, 'h', ApplicationDelegate::eMenuID_ProcessHalt)));
7374     process_menu_sp->AddSubmenu(MenuSP(new Menu(
7375         "Kill", nullptr, 'k', ApplicationDelegate::eMenuID_ProcessKill)));
7376 
7377     MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4),
7378                                    ApplicationDelegate::eMenuID_Thread));
7379     thread_menu_sp->AddSubmenu(MenuSP(new Menu(
7380         "Step In", nullptr, 'i', ApplicationDelegate::eMenuID_ThreadStepIn)));
7381     thread_menu_sp->AddSubmenu(
7382         MenuSP(new Menu("Step Over", nullptr, 'v',
7383                         ApplicationDelegate::eMenuID_ThreadStepOver)));
7384     thread_menu_sp->AddSubmenu(MenuSP(new Menu(
7385         "Step Out", nullptr, 'o', ApplicationDelegate::eMenuID_ThreadStepOut)));
7386 
7387     MenuSP view_menu_sp(
7388         new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View));
7389     view_menu_sp->AddSubmenu(
7390         MenuSP(new Menu("Backtrace", nullptr, 't',
7391                         ApplicationDelegate::eMenuID_ViewBacktrace)));
7392     view_menu_sp->AddSubmenu(
7393         MenuSP(new Menu("Registers", nullptr, 'r',
7394                         ApplicationDelegate::eMenuID_ViewRegisters)));
7395     view_menu_sp->AddSubmenu(MenuSP(new Menu(
7396         "Source", nullptr, 's', ApplicationDelegate::eMenuID_ViewSource)));
7397     view_menu_sp->AddSubmenu(
7398         MenuSP(new Menu("Variables", nullptr, 'v',
7399                         ApplicationDelegate::eMenuID_ViewVariables)));
7400     view_menu_sp->AddSubmenu(
7401         MenuSP(new Menu("Breakpoints", nullptr, 'b',
7402                         ApplicationDelegate::eMenuID_ViewBreakpoints)));
7403 
7404     MenuSP help_menu_sp(
7405         new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help));
7406     help_menu_sp->AddSubmenu(MenuSP(new Menu(
7407         "GUI Help", nullptr, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp)));
7408 
7409     m_app_ap->Initialize();
7410     WindowSP &main_window_sp = m_app_ap->GetMainWindow();
7411 
7412     MenuSP menubar_sp(new Menu(Menu::Type::Bar));
7413     menubar_sp->AddSubmenu(lldb_menu_sp);
7414     menubar_sp->AddSubmenu(target_menu_sp);
7415     menubar_sp->AddSubmenu(process_menu_sp);
7416     menubar_sp->AddSubmenu(thread_menu_sp);
7417     menubar_sp->AddSubmenu(view_menu_sp);
7418     menubar_sp->AddSubmenu(help_menu_sp);
7419     menubar_sp->SetDelegate(app_menu_delegate_sp);
7420 
7421     Rect content_bounds = main_window_sp->GetFrame();
7422     Rect menubar_bounds = content_bounds.MakeMenuBar();
7423     Rect status_bounds = content_bounds.MakeStatusBar();
7424     Rect source_bounds;
7425     Rect variables_bounds;
7426     Rect threads_bounds;
7427     Rect source_variables_bounds;
7428     content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds,
7429                                            threads_bounds);
7430     source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds,
7431                                                       variables_bounds);
7432 
7433     WindowSP menubar_window_sp =
7434         main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false);
7435     // Let the menubar get keys if the active window doesn't handle the keys
7436     // that are typed so it can respond to menubar key presses.
7437     menubar_window_sp->SetCanBeActive(
7438         false); // Don't let the menubar become the active window
7439     menubar_window_sp->SetDelegate(menubar_sp);
7440 
7441     WindowSP source_window_sp(
7442         main_window_sp->CreateSubWindow("Source", source_bounds, true));
7443     WindowSP variables_window_sp(
7444         main_window_sp->CreateSubWindow("Variables", variables_bounds, false));
7445     WindowSP threads_window_sp(
7446         main_window_sp->CreateSubWindow("Threads", threads_bounds, false));
7447     WindowSP status_window_sp(
7448         main_window_sp->CreateSubWindow("Status", status_bounds, false));
7449     status_window_sp->SetCanBeActive(
7450         false); // Don't let the status bar become the active window
7451     main_window_sp->SetDelegate(
7452         std::static_pointer_cast<WindowDelegate>(app_delegate_sp));
7453     source_window_sp->SetDelegate(
7454         WindowDelegateSP(new SourceFileWindowDelegate(m_debugger)));
7455     variables_window_sp->SetDelegate(
7456         WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
7457     TreeDelegateSP thread_delegate_sp(new ThreadsTreeDelegate(m_debugger));
7458     threads_window_sp->SetDelegate(WindowDelegateSP(
7459         new TreeWindowDelegate(m_debugger, thread_delegate_sp)));
7460     status_window_sp->SetDelegate(
7461         WindowDelegateSP(new StatusBarWindowDelegate(m_debugger)));
7462 
7463     // Show the main help window once the first time the curses GUI is
7464     // launched
7465     static bool g_showed_help = false;
7466     if (!g_showed_help) {
7467       g_showed_help = true;
7468       main_window_sp->CreateHelpSubwindow();
7469     }
7470 
7471     // All colors with black background.
7472     init_pair(1, COLOR_BLACK, COLOR_BLACK);
7473     init_pair(2, COLOR_RED, COLOR_BLACK);
7474     init_pair(3, COLOR_GREEN, COLOR_BLACK);
7475     init_pair(4, COLOR_YELLOW, COLOR_BLACK);
7476     init_pair(5, COLOR_BLUE, COLOR_BLACK);
7477     init_pair(6, COLOR_MAGENTA, COLOR_BLACK);
7478     init_pair(7, COLOR_CYAN, COLOR_BLACK);
7479     init_pair(8, COLOR_WHITE, COLOR_BLACK);
7480     // All colors with blue background.
7481     init_pair(9, COLOR_BLACK, COLOR_BLUE);
7482     init_pair(10, COLOR_RED, COLOR_BLUE);
7483     init_pair(11, COLOR_GREEN, COLOR_BLUE);
7484     init_pair(12, COLOR_YELLOW, COLOR_BLUE);
7485     init_pair(13, COLOR_BLUE, COLOR_BLUE);
7486     init_pair(14, COLOR_MAGENTA, COLOR_BLUE);
7487     init_pair(15, COLOR_CYAN, COLOR_BLUE);
7488     init_pair(16, COLOR_WHITE, COLOR_BLUE);
7489     // These must match the order in the color indexes enum.
7490     init_pair(17, COLOR_BLACK, COLOR_WHITE);
7491     init_pair(18, COLOR_MAGENTA, COLOR_WHITE);
7492     static_assert(LastColorPairIndex == 18, "Color indexes do not match.");
7493 
7494     define_key("\033[Z", KEY_SHIFT_TAB);
7495     define_key("\033\015", KEY_ALT_ENTER);
7496   }
7497 }
7498 
7499 void IOHandlerCursesGUI::Deactivate() { m_app_ap->Terminate(); }
7500 
7501 void IOHandlerCursesGUI::Run() {
7502   m_app_ap->Run(m_debugger);
7503   SetIsDone(true);
7504 }
7505 
7506 IOHandlerCursesGUI::~IOHandlerCursesGUI() = default;
7507 
7508 void IOHandlerCursesGUI::Cancel() {}
7509 
7510 bool IOHandlerCursesGUI::Interrupt() { return false; }
7511 
7512 void IOHandlerCursesGUI::GotEOF() {}
7513 
7514 void IOHandlerCursesGUI::TerminalSizeChanged() {
7515   m_app_ap->TerminalSizeChanged();
7516 }
7517 
7518 #endif // LLDB_ENABLE_CURSES
7519