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