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