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