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