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