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