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