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