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