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 bool TreeDelegateItemSelected(
3772       TreeItem &item) = 0; // Return true if we need to update views
3773 };
3774 
3775 typedef std::shared_ptr<TreeDelegate> TreeDelegateSP;
3776 
3777 class TreeItem {
3778 public:
3779   TreeItem(TreeItem *parent, TreeDelegate &delegate, bool might_have_children)
3780       : m_parent(parent), m_delegate(delegate), m_user_data(nullptr),
3781         m_identifier(0), m_row_idx(-1), m_children(),
3782         m_might_have_children(might_have_children), m_is_expanded(false) {}
3783 
3784   TreeItem &operator=(const TreeItem &rhs) {
3785     if (this != &rhs) {
3786       m_parent = rhs.m_parent;
3787       m_delegate = rhs.m_delegate;
3788       m_user_data = rhs.m_user_data;
3789       m_identifier = rhs.m_identifier;
3790       m_row_idx = rhs.m_row_idx;
3791       m_children = rhs.m_children;
3792       m_might_have_children = rhs.m_might_have_children;
3793       m_is_expanded = rhs.m_is_expanded;
3794     }
3795     return *this;
3796   }
3797 
3798   TreeItem(const TreeItem &) = default;
3799 
3800   size_t GetDepth() const {
3801     if (m_parent)
3802       return 1 + m_parent->GetDepth();
3803     return 0;
3804   }
3805 
3806   int GetRowIndex() const { return m_row_idx; }
3807 
3808   void ClearChildren() { m_children.clear(); }
3809 
3810   void Resize(size_t n, const TreeItem &t) { m_children.resize(n, t); }
3811 
3812   TreeItem &operator[](size_t i) { return m_children[i]; }
3813 
3814   void SetRowIndex(int row_idx) { m_row_idx = row_idx; }
3815 
3816   size_t GetNumChildren() {
3817     m_delegate.TreeDelegateGenerateChildren(*this);
3818     return m_children.size();
3819   }
3820 
3821   void ItemWasSelected() { m_delegate.TreeDelegateItemSelected(*this); }
3822 
3823   void CalculateRowIndexes(int &row_idx) {
3824     SetRowIndex(row_idx);
3825     ++row_idx;
3826 
3827     const bool expanded = IsExpanded();
3828 
3829     // The root item must calculate its children, or we must calculate the
3830     // number of children if the item is expanded
3831     if (m_parent == nullptr || expanded)
3832       GetNumChildren();
3833 
3834     for (auto &item : m_children) {
3835       if (expanded)
3836         item.CalculateRowIndexes(row_idx);
3837       else
3838         item.SetRowIndex(-1);
3839     }
3840   }
3841 
3842   TreeItem *GetParent() { return m_parent; }
3843 
3844   bool IsExpanded() const { return m_is_expanded; }
3845 
3846   void Expand() { m_is_expanded = true; }
3847 
3848   void Unexpand() { m_is_expanded = false; }
3849 
3850   bool Draw(Window &window, const int first_visible_row,
3851             const uint32_t selected_row_idx, int &row_idx, int &num_rows_left) {
3852     if (num_rows_left <= 0)
3853       return false;
3854 
3855     if (m_row_idx >= first_visible_row) {
3856       window.MoveCursor(2, row_idx + 1);
3857 
3858       if (m_parent)
3859         m_parent->DrawTreeForChild(window, this, 0);
3860 
3861       if (m_might_have_children) {
3862         // It we can get UTF8 characters to work we should try to use the
3863         // "symbol" UTF8 string below
3864         //            const char *symbol = "";
3865         //            if (row.expanded)
3866         //                symbol = "\xe2\x96\xbd ";
3867         //            else
3868         //                symbol = "\xe2\x96\xb7 ";
3869         //            window.PutCString (symbol);
3870 
3871         // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
3872         // 'v' or '>' character...
3873         //            if (expanded)
3874         //                window.PutChar (ACS_DARROW);
3875         //            else
3876         //                window.PutChar (ACS_RARROW);
3877         // Since we can't find any good looking right arrow/down arrow symbols,
3878         // just use a diamond...
3879         window.PutChar(ACS_DIAMOND);
3880         window.PutChar(ACS_HLINE);
3881       }
3882       bool highlight = (selected_row_idx == static_cast<size_t>(m_row_idx)) &&
3883                        window.IsActive();
3884 
3885       if (highlight)
3886         window.AttributeOn(A_REVERSE);
3887 
3888       m_delegate.TreeDelegateDrawTreeItem(*this, window);
3889 
3890       if (highlight)
3891         window.AttributeOff(A_REVERSE);
3892       ++row_idx;
3893       --num_rows_left;
3894     }
3895 
3896     if (num_rows_left <= 0)
3897       return false; // We are done drawing...
3898 
3899     if (IsExpanded()) {
3900       for (auto &item : m_children) {
3901         // If we displayed all the rows and item.Draw() returns false we are
3902         // done drawing and can exit this for loop
3903         if (!item.Draw(window, first_visible_row, selected_row_idx, row_idx,
3904                        num_rows_left))
3905           break;
3906       }
3907     }
3908     return num_rows_left >= 0; // Return true if not done drawing yet
3909   }
3910 
3911   void DrawTreeForChild(Window &window, TreeItem *child,
3912                         uint32_t reverse_depth) {
3913     if (m_parent)
3914       m_parent->DrawTreeForChild(window, this, reverse_depth + 1);
3915 
3916     if (&m_children.back() == child) {
3917       // Last child
3918       if (reverse_depth == 0) {
3919         window.PutChar(ACS_LLCORNER);
3920         window.PutChar(ACS_HLINE);
3921       } else {
3922         window.PutChar(' ');
3923         window.PutChar(' ');
3924       }
3925     } else {
3926       if (reverse_depth == 0) {
3927         window.PutChar(ACS_LTEE);
3928         window.PutChar(ACS_HLINE);
3929       } else {
3930         window.PutChar(ACS_VLINE);
3931         window.PutChar(' ');
3932       }
3933     }
3934   }
3935 
3936   TreeItem *GetItemForRowIndex(uint32_t row_idx) {
3937     if (static_cast<uint32_t>(m_row_idx) == row_idx)
3938       return this;
3939     if (m_children.empty())
3940       return nullptr;
3941     if (IsExpanded()) {
3942       for (auto &item : m_children) {
3943         TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx);
3944         if (selected_item_ptr)
3945           return selected_item_ptr;
3946       }
3947     }
3948     return nullptr;
3949   }
3950 
3951   void *GetUserData() const { return m_user_data; }
3952 
3953   void SetUserData(void *user_data) { m_user_data = user_data; }
3954 
3955   uint64_t GetIdentifier() const { return m_identifier; }
3956 
3957   void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
3958 
3959   void SetMightHaveChildren(bool b) { m_might_have_children = b; }
3960 
3961 protected:
3962   TreeItem *m_parent;
3963   TreeDelegate &m_delegate;
3964   void *m_user_data;
3965   uint64_t m_identifier;
3966   int m_row_idx; // Zero based visible row index, -1 if not visible or for the
3967                  // root item
3968   std::vector<TreeItem> m_children;
3969   bool m_might_have_children;
3970   bool m_is_expanded;
3971 };
3972 
3973 class TreeWindowDelegate : public WindowDelegate {
3974 public:
3975   TreeWindowDelegate(Debugger &debugger, const TreeDelegateSP &delegate_sp)
3976       : m_debugger(debugger), m_delegate_sp(delegate_sp),
3977         m_root(nullptr, *delegate_sp, true), m_selected_item(nullptr),
3978         m_num_rows(0), m_selected_row_idx(0), m_first_visible_row(0),
3979         m_min_x(0), m_min_y(0), m_max_x(0), m_max_y(0) {}
3980 
3981   int NumVisibleRows() const { return m_max_y - m_min_y; }
3982 
3983   bool WindowDelegateDraw(Window &window, bool force) override {
3984     ExecutionContext exe_ctx(
3985         m_debugger.GetCommandInterpreter().GetExecutionContext());
3986     Process *process = exe_ctx.GetProcessPtr();
3987 
3988     bool display_content = false;
3989     if (process) {
3990       StateType state = process->GetState();
3991       if (StateIsStoppedState(state, true)) {
3992         // We are stopped, so it is ok to
3993         display_content = true;
3994       } else if (StateIsRunningState(state)) {
3995         return true; // Don't do any updating when we are running
3996       }
3997     }
3998 
3999     m_min_x = 2;
4000     m_min_y = 1;
4001     m_max_x = window.GetWidth() - 1;
4002     m_max_y = window.GetHeight() - 1;
4003 
4004     window.Erase();
4005     window.DrawTitleBox(window.GetName());
4006 
4007     if (display_content) {
4008       const int num_visible_rows = NumVisibleRows();
4009       m_num_rows = 0;
4010       m_root.CalculateRowIndexes(m_num_rows);
4011 
4012       // If we unexpanded while having something selected our total number of
4013       // rows is less than the num visible rows, then make sure we show all the
4014       // rows by setting the first visible row accordingly.
4015       if (m_first_visible_row > 0 && m_num_rows < num_visible_rows)
4016         m_first_visible_row = 0;
4017 
4018       // Make sure the selected row is always visible
4019       if (m_selected_row_idx < m_first_visible_row)
4020         m_first_visible_row = m_selected_row_idx;
4021       else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
4022         m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
4023 
4024       int row_idx = 0;
4025       int num_rows_left = num_visible_rows;
4026       m_root.Draw(window, m_first_visible_row, m_selected_row_idx, row_idx,
4027                   num_rows_left);
4028       // Get the selected row
4029       m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4030     } else {
4031       m_selected_item = nullptr;
4032     }
4033 
4034     return true; // Drawing handled
4035   }
4036 
4037   const char *WindowDelegateGetHelpText() override {
4038     return "Thread window keyboard shortcuts:";
4039   }
4040 
4041   KeyHelp *WindowDelegateGetKeyHelp() override {
4042     static curses::KeyHelp g_source_view_key_help[] = {
4043         {KEY_UP, "Select previous item"},
4044         {KEY_DOWN, "Select next item"},
4045         {KEY_RIGHT, "Expand the selected item"},
4046         {KEY_LEFT,
4047          "Unexpand the selected item or select parent if not expanded"},
4048         {KEY_PPAGE, "Page up"},
4049         {KEY_NPAGE, "Page down"},
4050         {'h', "Show help dialog"},
4051         {' ', "Toggle item expansion"},
4052         {',', "Page up"},
4053         {'.', "Page down"},
4054         {'\0', nullptr}};
4055     return g_source_view_key_help;
4056   }
4057 
4058   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
4059     switch (c) {
4060     case ',':
4061     case KEY_PPAGE:
4062       // Page up key
4063       if (m_first_visible_row > 0) {
4064         if (m_first_visible_row > m_max_y)
4065           m_first_visible_row -= m_max_y;
4066         else
4067           m_first_visible_row = 0;
4068         m_selected_row_idx = m_first_visible_row;
4069         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4070         if (m_selected_item)
4071           m_selected_item->ItemWasSelected();
4072       }
4073       return eKeyHandled;
4074 
4075     case '.':
4076     case KEY_NPAGE:
4077       // Page down key
4078       if (m_num_rows > m_max_y) {
4079         if (m_first_visible_row + m_max_y < m_num_rows) {
4080           m_first_visible_row += m_max_y;
4081           m_selected_row_idx = m_first_visible_row;
4082           m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4083           if (m_selected_item)
4084             m_selected_item->ItemWasSelected();
4085         }
4086       }
4087       return eKeyHandled;
4088 
4089     case KEY_UP:
4090       if (m_selected_row_idx > 0) {
4091         --m_selected_row_idx;
4092         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4093         if (m_selected_item)
4094           m_selected_item->ItemWasSelected();
4095       }
4096       return eKeyHandled;
4097 
4098     case KEY_DOWN:
4099       if (m_selected_row_idx + 1 < m_num_rows) {
4100         ++m_selected_row_idx;
4101         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4102         if (m_selected_item)
4103           m_selected_item->ItemWasSelected();
4104       }
4105       return eKeyHandled;
4106 
4107     case KEY_RIGHT:
4108       if (m_selected_item) {
4109         if (!m_selected_item->IsExpanded())
4110           m_selected_item->Expand();
4111       }
4112       return eKeyHandled;
4113 
4114     case KEY_LEFT:
4115       if (m_selected_item) {
4116         if (m_selected_item->IsExpanded())
4117           m_selected_item->Unexpand();
4118         else if (m_selected_item->GetParent()) {
4119           m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex();
4120           m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4121           if (m_selected_item)
4122             m_selected_item->ItemWasSelected();
4123         }
4124       }
4125       return eKeyHandled;
4126 
4127     case ' ':
4128       // Toggle expansion state when SPACE is pressed
4129       if (m_selected_item) {
4130         if (m_selected_item->IsExpanded())
4131           m_selected_item->Unexpand();
4132         else
4133           m_selected_item->Expand();
4134       }
4135       return eKeyHandled;
4136 
4137     case 'h':
4138       window.CreateHelpSubwindow();
4139       return eKeyHandled;
4140 
4141     default:
4142       break;
4143     }
4144     return eKeyNotHandled;
4145   }
4146 
4147 protected:
4148   Debugger &m_debugger;
4149   TreeDelegateSP m_delegate_sp;
4150   TreeItem m_root;
4151   TreeItem *m_selected_item;
4152   int m_num_rows;
4153   int m_selected_row_idx;
4154   int m_first_visible_row;
4155   int m_min_x;
4156   int m_min_y;
4157   int m_max_x;
4158   int m_max_y;
4159 };
4160 
4161 class FrameTreeDelegate : public TreeDelegate {
4162 public:
4163   FrameTreeDelegate() : TreeDelegate() {
4164     FormatEntity::Parse(
4165         "frame #${frame.index}: {${function.name}${function.pc-offset}}}",
4166         m_format);
4167   }
4168 
4169   ~FrameTreeDelegate() override = default;
4170 
4171   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
4172     Thread *thread = (Thread *)item.GetUserData();
4173     if (thread) {
4174       const uint64_t frame_idx = item.GetIdentifier();
4175       StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_idx);
4176       if (frame_sp) {
4177         StreamString strm;
4178         const SymbolContext &sc =
4179             frame_sp->GetSymbolContext(eSymbolContextEverything);
4180         ExecutionContext exe_ctx(frame_sp);
4181         if (FormatEntity::Format(m_format, strm, &sc, &exe_ctx, nullptr,
4182                                  nullptr, false, false)) {
4183           int right_pad = 1;
4184           window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
4185         }
4186       }
4187     }
4188   }
4189 
4190   void TreeDelegateGenerateChildren(TreeItem &item) override {
4191     // No children for frames yet...
4192   }
4193 
4194   bool TreeDelegateItemSelected(TreeItem &item) override {
4195     Thread *thread = (Thread *)item.GetUserData();
4196     if (thread) {
4197       thread->GetProcess()->GetThreadList().SetSelectedThreadByID(
4198           thread->GetID());
4199       const uint64_t frame_idx = item.GetIdentifier();
4200       thread->SetSelectedFrameByIndex(frame_idx);
4201       return true;
4202     }
4203     return false;
4204   }
4205 
4206 protected:
4207   FormatEntity::Entry m_format;
4208 };
4209 
4210 class ThreadTreeDelegate : public TreeDelegate {
4211 public:
4212   ThreadTreeDelegate(Debugger &debugger)
4213       : TreeDelegate(), m_debugger(debugger), m_tid(LLDB_INVALID_THREAD_ID),
4214         m_stop_id(UINT32_MAX) {
4215     FormatEntity::Parse("thread #${thread.index}: tid = ${thread.id}{, stop "
4216                         "reason = ${thread.stop-reason}}",
4217                         m_format);
4218   }
4219 
4220   ~ThreadTreeDelegate() override = default;
4221 
4222   ProcessSP GetProcess() {
4223     return m_debugger.GetCommandInterpreter()
4224         .GetExecutionContext()
4225         .GetProcessSP();
4226   }
4227 
4228   ThreadSP GetThread(const TreeItem &item) {
4229     ProcessSP process_sp = GetProcess();
4230     if (process_sp)
4231       return process_sp->GetThreadList().FindThreadByID(item.GetIdentifier());
4232     return ThreadSP();
4233   }
4234 
4235   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
4236     ThreadSP thread_sp = GetThread(item);
4237     if (thread_sp) {
4238       StreamString strm;
4239       ExecutionContext exe_ctx(thread_sp);
4240       if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
4241                                nullptr, false, false)) {
4242         int right_pad = 1;
4243         window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
4244       }
4245     }
4246   }
4247 
4248   void TreeDelegateGenerateChildren(TreeItem &item) override {
4249     ProcessSP process_sp = GetProcess();
4250     if (process_sp && process_sp->IsAlive()) {
4251       StateType state = process_sp->GetState();
4252       if (StateIsStoppedState(state, true)) {
4253         ThreadSP thread_sp = GetThread(item);
4254         if (thread_sp) {
4255           if (m_stop_id == process_sp->GetStopID() &&
4256               thread_sp->GetID() == m_tid)
4257             return; // Children are already up to date
4258           if (!m_frame_delegate_sp) {
4259             // Always expand the thread item the first time we show it
4260             m_frame_delegate_sp = std::make_shared<FrameTreeDelegate>();
4261           }
4262 
4263           m_stop_id = process_sp->GetStopID();
4264           m_tid = thread_sp->GetID();
4265 
4266           TreeItem t(&item, *m_frame_delegate_sp, false);
4267           size_t num_frames = thread_sp->GetStackFrameCount();
4268           item.Resize(num_frames, t);
4269           for (size_t i = 0; i < num_frames; ++i) {
4270             item[i].SetUserData(thread_sp.get());
4271             item[i].SetIdentifier(i);
4272           }
4273         }
4274         return;
4275       }
4276     }
4277     item.ClearChildren();
4278   }
4279 
4280   bool TreeDelegateItemSelected(TreeItem &item) override {
4281     ProcessSP process_sp = GetProcess();
4282     if (process_sp && process_sp->IsAlive()) {
4283       StateType state = process_sp->GetState();
4284       if (StateIsStoppedState(state, true)) {
4285         ThreadSP thread_sp = GetThread(item);
4286         if (thread_sp) {
4287           ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList();
4288           std::lock_guard<std::recursive_mutex> guard(thread_list.GetMutex());
4289           ThreadSP selected_thread_sp = thread_list.GetSelectedThread();
4290           if (selected_thread_sp->GetID() != thread_sp->GetID()) {
4291             thread_list.SetSelectedThreadByID(thread_sp->GetID());
4292             return true;
4293           }
4294         }
4295       }
4296     }
4297     return false;
4298   }
4299 
4300 protected:
4301   Debugger &m_debugger;
4302   std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp;
4303   lldb::user_id_t m_tid;
4304   uint32_t m_stop_id;
4305   FormatEntity::Entry m_format;
4306 };
4307 
4308 class ThreadsTreeDelegate : public TreeDelegate {
4309 public:
4310   ThreadsTreeDelegate(Debugger &debugger)
4311       : TreeDelegate(), m_thread_delegate_sp(), m_debugger(debugger),
4312         m_stop_id(UINT32_MAX) {
4313     FormatEntity::Parse("process ${process.id}{, name = ${process.name}}",
4314                         m_format);
4315   }
4316 
4317   ~ThreadsTreeDelegate() override = default;
4318 
4319   ProcessSP GetProcess() {
4320     return m_debugger.GetCommandInterpreter()
4321         .GetExecutionContext()
4322         .GetProcessSP();
4323   }
4324 
4325   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
4326     ProcessSP process_sp = GetProcess();
4327     if (process_sp && process_sp->IsAlive()) {
4328       StreamString strm;
4329       ExecutionContext exe_ctx(process_sp);
4330       if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
4331                                nullptr, false, false)) {
4332         int right_pad = 1;
4333         window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
4334       }
4335     }
4336   }
4337 
4338   void TreeDelegateGenerateChildren(TreeItem &item) override {
4339     ProcessSP process_sp = GetProcess();
4340     if (process_sp && process_sp->IsAlive()) {
4341       StateType state = process_sp->GetState();
4342       if (StateIsStoppedState(state, true)) {
4343         const uint32_t stop_id = process_sp->GetStopID();
4344         if (m_stop_id == stop_id)
4345           return; // Children are already up to date
4346 
4347         m_stop_id = stop_id;
4348 
4349         if (!m_thread_delegate_sp) {
4350           // Always expand the thread item the first time we show it
4351           // item.Expand();
4352           m_thread_delegate_sp =
4353               std::make_shared<ThreadTreeDelegate>(m_debugger);
4354         }
4355 
4356         TreeItem t(&item, *m_thread_delegate_sp, false);
4357         ThreadList &threads = process_sp->GetThreadList();
4358         std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
4359         size_t num_threads = threads.GetSize();
4360         item.Resize(num_threads, t);
4361         for (size_t i = 0; i < num_threads; ++i) {
4362           item[i].SetIdentifier(threads.GetThreadAtIndex(i)->GetID());
4363           item[i].SetMightHaveChildren(true);
4364         }
4365         return;
4366       }
4367     }
4368     item.ClearChildren();
4369   }
4370 
4371   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
4372 
4373 protected:
4374   std::shared_ptr<ThreadTreeDelegate> m_thread_delegate_sp;
4375   Debugger &m_debugger;
4376   uint32_t m_stop_id;
4377   FormatEntity::Entry m_format;
4378 };
4379 
4380 class ValueObjectListDelegate : public WindowDelegate {
4381 public:
4382   ValueObjectListDelegate() : m_rows() {}
4383 
4384   ValueObjectListDelegate(ValueObjectList &valobj_list)
4385       : m_rows(), m_selected_row(nullptr), m_selected_row_idx(0),
4386         m_first_visible_row(0), m_num_rows(0), m_max_x(0), m_max_y(0) {
4387     SetValues(valobj_list);
4388   }
4389 
4390   ~ValueObjectListDelegate() override = default;
4391 
4392   void SetValues(ValueObjectList &valobj_list) {
4393     m_selected_row = nullptr;
4394     m_selected_row_idx = 0;
4395     m_first_visible_row = 0;
4396     m_num_rows = 0;
4397     m_rows.clear();
4398     for (auto &valobj_sp : valobj_list.GetObjects())
4399       m_rows.push_back(Row(valobj_sp, nullptr));
4400   }
4401 
4402   bool WindowDelegateDraw(Window &window, bool force) override {
4403     m_num_rows = 0;
4404     m_min_x = 2;
4405     m_min_y = 1;
4406     m_max_x = window.GetWidth() - 1;
4407     m_max_y = window.GetHeight() - 1;
4408 
4409     window.Erase();
4410     window.DrawTitleBox(window.GetName());
4411 
4412     const int num_visible_rows = NumVisibleRows();
4413     const int num_rows = CalculateTotalNumberRows(m_rows);
4414 
4415     // If we unexpanded while having something selected our total number of
4416     // rows is less than the num visible rows, then make sure we show all the
4417     // rows by setting the first visible row accordingly.
4418     if (m_first_visible_row > 0 && num_rows < num_visible_rows)
4419       m_first_visible_row = 0;
4420 
4421     // Make sure the selected row is always visible
4422     if (m_selected_row_idx < m_first_visible_row)
4423       m_first_visible_row = m_selected_row_idx;
4424     else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
4425       m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
4426 
4427     DisplayRows(window, m_rows, g_options);
4428 
4429     // Get the selected row
4430     m_selected_row = GetRowForRowIndex(m_selected_row_idx);
4431     // Keep the cursor on the selected row so the highlight and the cursor are
4432     // always on the same line
4433     if (m_selected_row)
4434       window.MoveCursor(m_selected_row->x, m_selected_row->y);
4435 
4436     return true; // Drawing handled
4437   }
4438 
4439   KeyHelp *WindowDelegateGetKeyHelp() override {
4440     static curses::KeyHelp g_source_view_key_help[] = {
4441         {KEY_UP, "Select previous item"},
4442         {KEY_DOWN, "Select next item"},
4443         {KEY_RIGHT, "Expand selected item"},
4444         {KEY_LEFT, "Unexpand selected item or select parent if not expanded"},
4445         {KEY_PPAGE, "Page up"},
4446         {KEY_NPAGE, "Page down"},
4447         {'A', "Format as annotated address"},
4448         {'b', "Format as binary"},
4449         {'B', "Format as hex bytes with ASCII"},
4450         {'c', "Format as character"},
4451         {'d', "Format as a signed integer"},
4452         {'D', "Format selected value using the default format for the type"},
4453         {'f', "Format as float"},
4454         {'h', "Show help dialog"},
4455         {'i', "Format as instructions"},
4456         {'o', "Format as octal"},
4457         {'p', "Format as pointer"},
4458         {'s', "Format as C string"},
4459         {'t', "Toggle showing/hiding type names"},
4460         {'u', "Format as an unsigned integer"},
4461         {'x', "Format as hex"},
4462         {'X', "Format as uppercase hex"},
4463         {' ', "Toggle item expansion"},
4464         {',', "Page up"},
4465         {'.', "Page down"},
4466         {'\0', nullptr}};
4467     return g_source_view_key_help;
4468   }
4469 
4470   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
4471     switch (c) {
4472     case 'x':
4473     case 'X':
4474     case 'o':
4475     case 's':
4476     case 'u':
4477     case 'd':
4478     case 'D':
4479     case 'i':
4480     case 'A':
4481     case 'p':
4482     case 'c':
4483     case 'b':
4484     case 'B':
4485     case 'f':
4486       // Change the format for the currently selected item
4487       if (m_selected_row) {
4488         auto valobj_sp = m_selected_row->value.GetSP();
4489         if (valobj_sp)
4490           valobj_sp->SetFormat(FormatForChar(c));
4491       }
4492       return eKeyHandled;
4493 
4494     case 't':
4495       // Toggle showing type names
4496       g_options.show_types = !g_options.show_types;
4497       return eKeyHandled;
4498 
4499     case ',':
4500     case KEY_PPAGE:
4501       // Page up key
4502       if (m_first_visible_row > 0) {
4503         if (static_cast<int>(m_first_visible_row) > m_max_y)
4504           m_first_visible_row -= m_max_y;
4505         else
4506           m_first_visible_row = 0;
4507         m_selected_row_idx = m_first_visible_row;
4508       }
4509       return eKeyHandled;
4510 
4511     case '.':
4512     case KEY_NPAGE:
4513       // Page down key
4514       if (m_num_rows > static_cast<size_t>(m_max_y)) {
4515         if (m_first_visible_row + m_max_y < m_num_rows) {
4516           m_first_visible_row += m_max_y;
4517           m_selected_row_idx = m_first_visible_row;
4518         }
4519       }
4520       return eKeyHandled;
4521 
4522     case KEY_UP:
4523       if (m_selected_row_idx > 0)
4524         --m_selected_row_idx;
4525       return eKeyHandled;
4526 
4527     case KEY_DOWN:
4528       if (m_selected_row_idx + 1 < m_num_rows)
4529         ++m_selected_row_idx;
4530       return eKeyHandled;
4531 
4532     case KEY_RIGHT:
4533       if (m_selected_row) {
4534         if (!m_selected_row->expanded)
4535           m_selected_row->Expand();
4536       }
4537       return eKeyHandled;
4538 
4539     case KEY_LEFT:
4540       if (m_selected_row) {
4541         if (m_selected_row->expanded)
4542           m_selected_row->Unexpand();
4543         else if (m_selected_row->parent)
4544           m_selected_row_idx = m_selected_row->parent->row_idx;
4545       }
4546       return eKeyHandled;
4547 
4548     case ' ':
4549       // Toggle expansion state when SPACE is pressed
4550       if (m_selected_row) {
4551         if (m_selected_row->expanded)
4552           m_selected_row->Unexpand();
4553         else
4554           m_selected_row->Expand();
4555       }
4556       return eKeyHandled;
4557 
4558     case 'h':
4559       window.CreateHelpSubwindow();
4560       return eKeyHandled;
4561 
4562     default:
4563       break;
4564     }
4565     return eKeyNotHandled;
4566   }
4567 
4568 protected:
4569   std::vector<Row> m_rows;
4570   Row *m_selected_row = nullptr;
4571   uint32_t m_selected_row_idx = 0;
4572   uint32_t m_first_visible_row = 0;
4573   uint32_t m_num_rows = 0;
4574   int m_min_x;
4575   int m_min_y;
4576   int m_max_x = 0;
4577   int m_max_y = 0;
4578 
4579   static Format FormatForChar(int c) {
4580     switch (c) {
4581     case 'x':
4582       return eFormatHex;
4583     case 'X':
4584       return eFormatHexUppercase;
4585     case 'o':
4586       return eFormatOctal;
4587     case 's':
4588       return eFormatCString;
4589     case 'u':
4590       return eFormatUnsigned;
4591     case 'd':
4592       return eFormatDecimal;
4593     case 'D':
4594       return eFormatDefault;
4595     case 'i':
4596       return eFormatInstruction;
4597     case 'A':
4598       return eFormatAddressInfo;
4599     case 'p':
4600       return eFormatPointer;
4601     case 'c':
4602       return eFormatChar;
4603     case 'b':
4604       return eFormatBinary;
4605     case 'B':
4606       return eFormatBytesWithASCII;
4607     case 'f':
4608       return eFormatFloat;
4609     }
4610     return eFormatDefault;
4611   }
4612 
4613   bool DisplayRowObject(Window &window, Row &row, DisplayOptions &options,
4614                         bool highlight, bool last_child) {
4615     ValueObject *valobj = row.value.GetSP().get();
4616 
4617     if (valobj == nullptr)
4618       return false;
4619 
4620     const char *type_name =
4621         options.show_types ? valobj->GetTypeName().GetCString() : nullptr;
4622     const char *name = valobj->GetName().GetCString();
4623     const char *value = valobj->GetValueAsCString();
4624     const char *summary = valobj->GetSummaryAsCString();
4625 
4626     window.MoveCursor(row.x, row.y);
4627 
4628     row.DrawTree(window);
4629 
4630     if (highlight)
4631       window.AttributeOn(A_REVERSE);
4632 
4633     if (type_name && type_name[0])
4634       window.PrintfTruncated(1, "(%s) ", type_name);
4635 
4636     if (name && name[0])
4637       window.PutCStringTruncated(1, name);
4638 
4639     attr_t changd_attr = 0;
4640     if (valobj->GetValueDidChange())
4641       changd_attr = COLOR_PAIR(RedOnBlack) | A_BOLD;
4642 
4643     if (value && value[0]) {
4644       window.PutCStringTruncated(1, " = ");
4645       if (changd_attr)
4646         window.AttributeOn(changd_attr);
4647       window.PutCStringTruncated(1, value);
4648       if (changd_attr)
4649         window.AttributeOff(changd_attr);
4650     }
4651 
4652     if (summary && summary[0]) {
4653       window.PutCStringTruncated(1, " ");
4654       if (changd_attr)
4655         window.AttributeOn(changd_attr);
4656       window.PutCStringTruncated(1, summary);
4657       if (changd_attr)
4658         window.AttributeOff(changd_attr);
4659     }
4660 
4661     if (highlight)
4662       window.AttributeOff(A_REVERSE);
4663 
4664     return true;
4665   }
4666 
4667   void DisplayRows(Window &window, std::vector<Row> &rows,
4668                    DisplayOptions &options) {
4669     // >   0x25B7
4670     // \/  0x25BD
4671 
4672     bool window_is_active = window.IsActive();
4673     for (auto &row : rows) {
4674       const bool last_child = row.parent && &rows[rows.size() - 1] == &row;
4675       // Save the row index in each Row structure
4676       row.row_idx = m_num_rows;
4677       if ((m_num_rows >= m_first_visible_row) &&
4678           ((m_num_rows - m_first_visible_row) <
4679            static_cast<size_t>(NumVisibleRows()))) {
4680         row.x = m_min_x;
4681         row.y = m_num_rows - m_first_visible_row + 1;
4682         if (DisplayRowObject(window, row, options,
4683                              window_is_active &&
4684                                  m_num_rows == m_selected_row_idx,
4685                              last_child)) {
4686           ++m_num_rows;
4687         } else {
4688           row.x = 0;
4689           row.y = 0;
4690         }
4691       } else {
4692         row.x = 0;
4693         row.y = 0;
4694         ++m_num_rows;
4695       }
4696 
4697       auto &children = row.GetChildren();
4698       if (row.expanded && !children.empty()) {
4699         DisplayRows(window, children, options);
4700       }
4701     }
4702   }
4703 
4704   int CalculateTotalNumberRows(std::vector<Row> &rows) {
4705     int row_count = 0;
4706     for (auto &row : rows) {
4707       ++row_count;
4708       if (row.expanded)
4709         row_count += CalculateTotalNumberRows(row.GetChildren());
4710     }
4711     return row_count;
4712   }
4713 
4714   static Row *GetRowForRowIndexImpl(std::vector<Row> &rows, size_t &row_index) {
4715     for (auto &row : rows) {
4716       if (row_index == 0)
4717         return &row;
4718       else {
4719         --row_index;
4720         auto &children = row.GetChildren();
4721         if (row.expanded && !children.empty()) {
4722           Row *result = GetRowForRowIndexImpl(children, row_index);
4723           if (result)
4724             return result;
4725         }
4726       }
4727     }
4728     return nullptr;
4729   }
4730 
4731   Row *GetRowForRowIndex(size_t row_index) {
4732     return GetRowForRowIndexImpl(m_rows, row_index);
4733   }
4734 
4735   int NumVisibleRows() const { return m_max_y - m_min_y; }
4736 
4737   static DisplayOptions g_options;
4738 };
4739 
4740 class FrameVariablesWindowDelegate : public ValueObjectListDelegate {
4741 public:
4742   FrameVariablesWindowDelegate(Debugger &debugger)
4743       : ValueObjectListDelegate(), m_debugger(debugger),
4744         m_frame_block(nullptr) {}
4745 
4746   ~FrameVariablesWindowDelegate() override = default;
4747 
4748   const char *WindowDelegateGetHelpText() override {
4749     return "Frame variable window keyboard shortcuts:";
4750   }
4751 
4752   bool WindowDelegateDraw(Window &window, bool force) override {
4753     ExecutionContext exe_ctx(
4754         m_debugger.GetCommandInterpreter().GetExecutionContext());
4755     Process *process = exe_ctx.GetProcessPtr();
4756     Block *frame_block = nullptr;
4757     StackFrame *frame = nullptr;
4758 
4759     if (process) {
4760       StateType state = process->GetState();
4761       if (StateIsStoppedState(state, true)) {
4762         frame = exe_ctx.GetFramePtr();
4763         if (frame)
4764           frame_block = frame->GetFrameBlock();
4765       } else if (StateIsRunningState(state)) {
4766         return true; // Don't do any updating when we are running
4767       }
4768     }
4769 
4770     ValueObjectList local_values;
4771     if (frame_block) {
4772       // Only update the variables if they have changed
4773       if (m_frame_block != frame_block) {
4774         m_frame_block = frame_block;
4775 
4776         VariableList *locals = frame->GetVariableList(true);
4777         if (locals) {
4778           const DynamicValueType use_dynamic = eDynamicDontRunTarget;
4779           for (const VariableSP &local_sp : *locals) {
4780             ValueObjectSP value_sp =
4781                 frame->GetValueObjectForFrameVariable(local_sp, use_dynamic);
4782             if (value_sp) {
4783               ValueObjectSP synthetic_value_sp = value_sp->GetSyntheticValue();
4784               if (synthetic_value_sp)
4785                 local_values.Append(synthetic_value_sp);
4786               else
4787                 local_values.Append(value_sp);
4788             }
4789           }
4790           // Update the values
4791           SetValues(local_values);
4792         }
4793       }
4794     } else {
4795       m_frame_block = nullptr;
4796       // Update the values with an empty list if there is no frame
4797       SetValues(local_values);
4798     }
4799 
4800     return ValueObjectListDelegate::WindowDelegateDraw(window, force);
4801   }
4802 
4803 protected:
4804   Debugger &m_debugger;
4805   Block *m_frame_block;
4806 };
4807 
4808 class RegistersWindowDelegate : public ValueObjectListDelegate {
4809 public:
4810   RegistersWindowDelegate(Debugger &debugger)
4811       : ValueObjectListDelegate(), m_debugger(debugger) {}
4812 
4813   ~RegistersWindowDelegate() override = default;
4814 
4815   const char *WindowDelegateGetHelpText() override {
4816     return "Register window keyboard shortcuts:";
4817   }
4818 
4819   bool WindowDelegateDraw(Window &window, bool force) override {
4820     ExecutionContext exe_ctx(
4821         m_debugger.GetCommandInterpreter().GetExecutionContext());
4822     StackFrame *frame = exe_ctx.GetFramePtr();
4823 
4824     ValueObjectList value_list;
4825     if (frame) {
4826       if (frame->GetStackID() != m_stack_id) {
4827         m_stack_id = frame->GetStackID();
4828         RegisterContextSP reg_ctx(frame->GetRegisterContext());
4829         if (reg_ctx) {
4830           const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
4831           for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) {
4832             value_list.Append(
4833                 ValueObjectRegisterSet::Create(frame, reg_ctx, set_idx));
4834           }
4835         }
4836         SetValues(value_list);
4837       }
4838     } else {
4839       Process *process = exe_ctx.GetProcessPtr();
4840       if (process && process->IsAlive())
4841         return true; // Don't do any updating if we are running
4842       else {
4843         // Update the values with an empty list if there is no process or the
4844         // process isn't alive anymore
4845         SetValues(value_list);
4846       }
4847     }
4848     return ValueObjectListDelegate::WindowDelegateDraw(window, force);
4849   }
4850 
4851 protected:
4852   Debugger &m_debugger;
4853   StackID m_stack_id;
4854 };
4855 
4856 static const char *CursesKeyToCString(int ch) {
4857   static char g_desc[32];
4858   if (ch >= KEY_F0 && ch < KEY_F0 + 64) {
4859     snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0);
4860     return g_desc;
4861   }
4862   switch (ch) {
4863   case KEY_DOWN:
4864     return "down";
4865   case KEY_UP:
4866     return "up";
4867   case KEY_LEFT:
4868     return "left";
4869   case KEY_RIGHT:
4870     return "right";
4871   case KEY_HOME:
4872     return "home";
4873   case KEY_BACKSPACE:
4874     return "backspace";
4875   case KEY_DL:
4876     return "delete-line";
4877   case KEY_IL:
4878     return "insert-line";
4879   case KEY_DC:
4880     return "delete-char";
4881   case KEY_IC:
4882     return "insert-char";
4883   case KEY_CLEAR:
4884     return "clear";
4885   case KEY_EOS:
4886     return "clear-to-eos";
4887   case KEY_EOL:
4888     return "clear-to-eol";
4889   case KEY_SF:
4890     return "scroll-forward";
4891   case KEY_SR:
4892     return "scroll-backward";
4893   case KEY_NPAGE:
4894     return "page-down";
4895   case KEY_PPAGE:
4896     return "page-up";
4897   case KEY_STAB:
4898     return "set-tab";
4899   case KEY_CTAB:
4900     return "clear-tab";
4901   case KEY_CATAB:
4902     return "clear-all-tabs";
4903   case KEY_ENTER:
4904     return "enter";
4905   case KEY_PRINT:
4906     return "print";
4907   case KEY_LL:
4908     return "lower-left key";
4909   case KEY_A1:
4910     return "upper left of keypad";
4911   case KEY_A3:
4912     return "upper right of keypad";
4913   case KEY_B2:
4914     return "center of keypad";
4915   case KEY_C1:
4916     return "lower left of keypad";
4917   case KEY_C3:
4918     return "lower right of keypad";
4919   case KEY_BTAB:
4920     return "back-tab key";
4921   case KEY_BEG:
4922     return "begin key";
4923   case KEY_CANCEL:
4924     return "cancel key";
4925   case KEY_CLOSE:
4926     return "close key";
4927   case KEY_COMMAND:
4928     return "command key";
4929   case KEY_COPY:
4930     return "copy key";
4931   case KEY_CREATE:
4932     return "create key";
4933   case KEY_END:
4934     return "end key";
4935   case KEY_EXIT:
4936     return "exit key";
4937   case KEY_FIND:
4938     return "find key";
4939   case KEY_HELP:
4940     return "help key";
4941   case KEY_MARK:
4942     return "mark key";
4943   case KEY_MESSAGE:
4944     return "message key";
4945   case KEY_MOVE:
4946     return "move key";
4947   case KEY_NEXT:
4948     return "next key";
4949   case KEY_OPEN:
4950     return "open key";
4951   case KEY_OPTIONS:
4952     return "options key";
4953   case KEY_PREVIOUS:
4954     return "previous key";
4955   case KEY_REDO:
4956     return "redo key";
4957   case KEY_REFERENCE:
4958     return "reference key";
4959   case KEY_REFRESH:
4960     return "refresh key";
4961   case KEY_REPLACE:
4962     return "replace key";
4963   case KEY_RESTART:
4964     return "restart key";
4965   case KEY_RESUME:
4966     return "resume key";
4967   case KEY_SAVE:
4968     return "save key";
4969   case KEY_SBEG:
4970     return "shifted begin key";
4971   case KEY_SCANCEL:
4972     return "shifted cancel key";
4973   case KEY_SCOMMAND:
4974     return "shifted command key";
4975   case KEY_SCOPY:
4976     return "shifted copy key";
4977   case KEY_SCREATE:
4978     return "shifted create key";
4979   case KEY_SDC:
4980     return "shifted delete-character key";
4981   case KEY_SDL:
4982     return "shifted delete-line key";
4983   case KEY_SELECT:
4984     return "select key";
4985   case KEY_SEND:
4986     return "shifted end key";
4987   case KEY_SEOL:
4988     return "shifted clear-to-end-of-line key";
4989   case KEY_SEXIT:
4990     return "shifted exit key";
4991   case KEY_SFIND:
4992     return "shifted find key";
4993   case KEY_SHELP:
4994     return "shifted help key";
4995   case KEY_SHOME:
4996     return "shifted home key";
4997   case KEY_SIC:
4998     return "shifted insert-character key";
4999   case KEY_SLEFT:
5000     return "shifted left-arrow key";
5001   case KEY_SMESSAGE:
5002     return "shifted message key";
5003   case KEY_SMOVE:
5004     return "shifted move key";
5005   case KEY_SNEXT:
5006     return "shifted next key";
5007   case KEY_SOPTIONS:
5008     return "shifted options key";
5009   case KEY_SPREVIOUS:
5010     return "shifted previous key";
5011   case KEY_SPRINT:
5012     return "shifted print key";
5013   case KEY_SREDO:
5014     return "shifted redo key";
5015   case KEY_SREPLACE:
5016     return "shifted replace key";
5017   case KEY_SRIGHT:
5018     return "shifted right-arrow key";
5019   case KEY_SRSUME:
5020     return "shifted resume key";
5021   case KEY_SSAVE:
5022     return "shifted save key";
5023   case KEY_SSUSPEND:
5024     return "shifted suspend key";
5025   case KEY_SUNDO:
5026     return "shifted undo key";
5027   case KEY_SUSPEND:
5028     return "suspend key";
5029   case KEY_UNDO:
5030     return "undo key";
5031   case KEY_MOUSE:
5032     return "Mouse event has occurred";
5033   case KEY_RESIZE:
5034     return "Terminal resize event";
5035 #ifdef KEY_EVENT
5036   case KEY_EVENT:
5037     return "We were interrupted by an event";
5038 #endif
5039   case KEY_RETURN:
5040     return "return";
5041   case ' ':
5042     return "space";
5043   case '\t':
5044     return "tab";
5045   case KEY_ESCAPE:
5046     return "escape";
5047   default:
5048     if (llvm::isPrint(ch))
5049       snprintf(g_desc, sizeof(g_desc), "%c", ch);
5050     else
5051       snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch);
5052     return g_desc;
5053   }
5054   return nullptr;
5055 }
5056 
5057 HelpDialogDelegate::HelpDialogDelegate(const char *text,
5058                                        KeyHelp *key_help_array)
5059     : m_text(), m_first_visible_line(0) {
5060   if (text && text[0]) {
5061     m_text.SplitIntoLines(text);
5062     m_text.AppendString("");
5063   }
5064   if (key_help_array) {
5065     for (KeyHelp *key = key_help_array; key->ch; ++key) {
5066       StreamString key_description;
5067       key_description.Printf("%10s - %s", CursesKeyToCString(key->ch),
5068                              key->description);
5069       m_text.AppendString(key_description.GetString());
5070     }
5071   }
5072 }
5073 
5074 HelpDialogDelegate::~HelpDialogDelegate() = default;
5075 
5076 bool HelpDialogDelegate::WindowDelegateDraw(Window &window, bool force) {
5077   window.Erase();
5078   const int window_height = window.GetHeight();
5079   int x = 2;
5080   int y = 1;
5081   const int min_y = y;
5082   const int max_y = window_height - 1 - y;
5083   const size_t num_visible_lines = max_y - min_y + 1;
5084   const size_t num_lines = m_text.GetSize();
5085   const char *bottom_message;
5086   if (num_lines <= num_visible_lines)
5087     bottom_message = "Press any key to exit";
5088   else
5089     bottom_message = "Use arrows to scroll, any other key to exit";
5090   window.DrawTitleBox(window.GetName(), bottom_message);
5091   while (y <= max_y) {
5092     window.MoveCursor(x, y);
5093     window.PutCStringTruncated(
5094         1, m_text.GetStringAtIndex(m_first_visible_line + y - min_y));
5095     ++y;
5096   }
5097   return true;
5098 }
5099 
5100 HandleCharResult HelpDialogDelegate::WindowDelegateHandleChar(Window &window,
5101                                                               int key) {
5102   bool done = false;
5103   const size_t num_lines = m_text.GetSize();
5104   const size_t num_visible_lines = window.GetHeight() - 2;
5105 
5106   if (num_lines <= num_visible_lines) {
5107     done = true;
5108     // If we have all lines visible and don't need scrolling, then any key
5109     // press will cause us to exit
5110   } else {
5111     switch (key) {
5112     case KEY_UP:
5113       if (m_first_visible_line > 0)
5114         --m_first_visible_line;
5115       break;
5116 
5117     case KEY_DOWN:
5118       if (m_first_visible_line + num_visible_lines < num_lines)
5119         ++m_first_visible_line;
5120       break;
5121 
5122     case KEY_PPAGE:
5123     case ',':
5124       if (m_first_visible_line > 0) {
5125         if (static_cast<size_t>(m_first_visible_line) >= num_visible_lines)
5126           m_first_visible_line -= num_visible_lines;
5127         else
5128           m_first_visible_line = 0;
5129       }
5130       break;
5131 
5132     case KEY_NPAGE:
5133     case '.':
5134       if (m_first_visible_line + num_visible_lines < num_lines) {
5135         m_first_visible_line += num_visible_lines;
5136         if (static_cast<size_t>(m_first_visible_line) > num_lines)
5137           m_first_visible_line = num_lines - num_visible_lines;
5138       }
5139       break;
5140 
5141     default:
5142       done = true;
5143       break;
5144     }
5145   }
5146   if (done)
5147     window.GetParent()->RemoveSubWindow(&window);
5148   return eKeyHandled;
5149 }
5150 
5151 class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
5152 public:
5153   enum {
5154     eMenuID_LLDB = 1,
5155     eMenuID_LLDBAbout,
5156     eMenuID_LLDBExit,
5157 
5158     eMenuID_Target,
5159     eMenuID_TargetCreate,
5160     eMenuID_TargetDelete,
5161 
5162     eMenuID_Process,
5163     eMenuID_ProcessAttach,
5164     eMenuID_ProcessDetachResume,
5165     eMenuID_ProcessDetachSuspended,
5166     eMenuID_ProcessLaunch,
5167     eMenuID_ProcessContinue,
5168     eMenuID_ProcessHalt,
5169     eMenuID_ProcessKill,
5170 
5171     eMenuID_Thread,
5172     eMenuID_ThreadStepIn,
5173     eMenuID_ThreadStepOver,
5174     eMenuID_ThreadStepOut,
5175 
5176     eMenuID_View,
5177     eMenuID_ViewBacktrace,
5178     eMenuID_ViewRegisters,
5179     eMenuID_ViewSource,
5180     eMenuID_ViewVariables,
5181 
5182     eMenuID_Help,
5183     eMenuID_HelpGUIHelp
5184   };
5185 
5186   ApplicationDelegate(Application &app, Debugger &debugger)
5187       : WindowDelegate(), MenuDelegate(), m_app(app), m_debugger(debugger) {}
5188 
5189   ~ApplicationDelegate() override = default;
5190 
5191   bool WindowDelegateDraw(Window &window, bool force) override {
5192     return false; // Drawing not handled, let standard window drawing happen
5193   }
5194 
5195   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
5196     switch (key) {
5197     case '\t':
5198       window.SelectNextWindowAsActive();
5199       return eKeyHandled;
5200 
5201     case KEY_SHIFT_TAB:
5202       window.SelectPreviousWindowAsActive();
5203       return eKeyHandled;
5204 
5205     case 'h':
5206       window.CreateHelpSubwindow();
5207       return eKeyHandled;
5208 
5209     case KEY_ESCAPE:
5210       return eQuitApplication;
5211 
5212     default:
5213       break;
5214     }
5215     return eKeyNotHandled;
5216   }
5217 
5218   const char *WindowDelegateGetHelpText() override {
5219     return "Welcome to the LLDB curses GUI.\n\n"
5220            "Press the TAB key to change the selected view.\n"
5221            "Each view has its own keyboard shortcuts, press 'h' to open a "
5222            "dialog to display them.\n\n"
5223            "Common key bindings for all views:";
5224   }
5225 
5226   KeyHelp *WindowDelegateGetKeyHelp() override {
5227     static curses::KeyHelp g_source_view_key_help[] = {
5228         {'\t', "Select next view"},
5229         {KEY_BTAB, "Select previous view"},
5230         {'h', "Show help dialog with view specific key bindings"},
5231         {',', "Page up"},
5232         {'.', "Page down"},
5233         {KEY_UP, "Select previous"},
5234         {KEY_DOWN, "Select next"},
5235         {KEY_LEFT, "Unexpand or select parent"},
5236         {KEY_RIGHT, "Expand"},
5237         {KEY_PPAGE, "Page up"},
5238         {KEY_NPAGE, "Page down"},
5239         {'\0', nullptr}};
5240     return g_source_view_key_help;
5241   }
5242 
5243   MenuActionResult MenuDelegateAction(Menu &menu) override {
5244     switch (menu.GetIdentifier()) {
5245     case eMenuID_TargetCreate: {
5246       WindowSP main_window_sp = m_app.GetMainWindow();
5247       FormDelegateSP form_delegate_sp =
5248           FormDelegateSP(new TargetCreateFormDelegate(m_debugger));
5249       Rect bounds = main_window_sp->GetCenteredRect(80, 19);
5250       WindowSP form_window_sp = main_window_sp->CreateSubWindow(
5251           form_delegate_sp->GetName().c_str(), bounds, true);
5252       WindowDelegateSP window_delegate_sp =
5253           WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
5254       form_window_sp->SetDelegate(window_delegate_sp);
5255       return MenuActionResult::Handled;
5256     }
5257     case eMenuID_ThreadStepIn: {
5258       ExecutionContext exe_ctx =
5259           m_debugger.GetCommandInterpreter().GetExecutionContext();
5260       if (exe_ctx.HasThreadScope()) {
5261         Process *process = exe_ctx.GetProcessPtr();
5262         if (process && process->IsAlive() &&
5263             StateIsStoppedState(process->GetState(), true))
5264           exe_ctx.GetThreadRef().StepIn(true);
5265       }
5266     }
5267       return MenuActionResult::Handled;
5268 
5269     case eMenuID_ThreadStepOut: {
5270       ExecutionContext exe_ctx =
5271           m_debugger.GetCommandInterpreter().GetExecutionContext();
5272       if (exe_ctx.HasThreadScope()) {
5273         Process *process = exe_ctx.GetProcessPtr();
5274         if (process && process->IsAlive() &&
5275             StateIsStoppedState(process->GetState(), true))
5276           exe_ctx.GetThreadRef().StepOut();
5277       }
5278     }
5279       return MenuActionResult::Handled;
5280 
5281     case eMenuID_ThreadStepOver: {
5282       ExecutionContext exe_ctx =
5283           m_debugger.GetCommandInterpreter().GetExecutionContext();
5284       if (exe_ctx.HasThreadScope()) {
5285         Process *process = exe_ctx.GetProcessPtr();
5286         if (process && process->IsAlive() &&
5287             StateIsStoppedState(process->GetState(), true))
5288           exe_ctx.GetThreadRef().StepOver(true);
5289       }
5290     }
5291       return MenuActionResult::Handled;
5292 
5293     case eMenuID_ProcessAttach: {
5294       WindowSP main_window_sp = m_app.GetMainWindow();
5295       FormDelegateSP form_delegate_sp = FormDelegateSP(
5296           new ProcessAttachFormDelegate(m_debugger, main_window_sp));
5297       Rect bounds = main_window_sp->GetCenteredRect(80, 22);
5298       WindowSP form_window_sp = main_window_sp->CreateSubWindow(
5299           form_delegate_sp->GetName().c_str(), bounds, true);
5300       WindowDelegateSP window_delegate_sp =
5301           WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
5302       form_window_sp->SetDelegate(window_delegate_sp);
5303       return MenuActionResult::Handled;
5304     }
5305 
5306     case eMenuID_ProcessContinue: {
5307       ExecutionContext exe_ctx =
5308           m_debugger.GetCommandInterpreter().GetExecutionContext();
5309       if (exe_ctx.HasProcessScope()) {
5310         Process *process = exe_ctx.GetProcessPtr();
5311         if (process && process->IsAlive() &&
5312             StateIsStoppedState(process->GetState(), true))
5313           process->Resume();
5314       }
5315     }
5316       return MenuActionResult::Handled;
5317 
5318     case eMenuID_ProcessKill: {
5319       ExecutionContext exe_ctx =
5320           m_debugger.GetCommandInterpreter().GetExecutionContext();
5321       if (exe_ctx.HasProcessScope()) {
5322         Process *process = exe_ctx.GetProcessPtr();
5323         if (process && process->IsAlive())
5324           process->Destroy(false);
5325       }
5326     }
5327       return MenuActionResult::Handled;
5328 
5329     case eMenuID_ProcessHalt: {
5330       ExecutionContext exe_ctx =
5331           m_debugger.GetCommandInterpreter().GetExecutionContext();
5332       if (exe_ctx.HasProcessScope()) {
5333         Process *process = exe_ctx.GetProcessPtr();
5334         if (process && process->IsAlive())
5335           process->Halt();
5336       }
5337     }
5338       return MenuActionResult::Handled;
5339 
5340     case eMenuID_ProcessDetachResume:
5341     case eMenuID_ProcessDetachSuspended: {
5342       ExecutionContext exe_ctx =
5343           m_debugger.GetCommandInterpreter().GetExecutionContext();
5344       if (exe_ctx.HasProcessScope()) {
5345         Process *process = exe_ctx.GetProcessPtr();
5346         if (process && process->IsAlive())
5347           process->Detach(menu.GetIdentifier() ==
5348                           eMenuID_ProcessDetachSuspended);
5349       }
5350     }
5351       return MenuActionResult::Handled;
5352 
5353     case eMenuID_Process: {
5354       // Populate the menu with all of the threads if the process is stopped
5355       // when the Process menu gets selected and is about to display its
5356       // submenu.
5357       Menus &submenus = menu.GetSubmenus();
5358       ExecutionContext exe_ctx =
5359           m_debugger.GetCommandInterpreter().GetExecutionContext();
5360       Process *process = exe_ctx.GetProcessPtr();
5361       if (process && process->IsAlive() &&
5362           StateIsStoppedState(process->GetState(), true)) {
5363         if (submenus.size() == 7)
5364           menu.AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
5365         else if (submenus.size() > 8)
5366           submenus.erase(submenus.begin() + 8, submenus.end());
5367 
5368         ThreadList &threads = process->GetThreadList();
5369         std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
5370         size_t num_threads = threads.GetSize();
5371         for (size_t i = 0; i < num_threads; ++i) {
5372           ThreadSP thread_sp = threads.GetThreadAtIndex(i);
5373           char menu_char = '\0';
5374           if (i < 9)
5375             menu_char = '1' + i;
5376           StreamString thread_menu_title;
5377           thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID());
5378           const char *thread_name = thread_sp->GetName();
5379           if (thread_name && thread_name[0])
5380             thread_menu_title.Printf(" %s", thread_name);
5381           else {
5382             const char *queue_name = thread_sp->GetQueueName();
5383             if (queue_name && queue_name[0])
5384               thread_menu_title.Printf(" %s", queue_name);
5385           }
5386           menu.AddSubmenu(
5387               MenuSP(new Menu(thread_menu_title.GetString().str().c_str(),
5388                               nullptr, menu_char, thread_sp->GetID())));
5389         }
5390       } else if (submenus.size() > 7) {
5391         // Remove the separator and any other thread submenu items that were
5392         // previously added
5393         submenus.erase(submenus.begin() + 7, submenus.end());
5394       }
5395       // Since we are adding and removing items we need to recalculate the name
5396       // lengths
5397       menu.RecalculateNameLengths();
5398     }
5399       return MenuActionResult::Handled;
5400 
5401     case eMenuID_ViewVariables: {
5402       WindowSP main_window_sp = m_app.GetMainWindow();
5403       WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
5404       WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
5405       WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
5406       const Rect source_bounds = source_window_sp->GetBounds();
5407 
5408       if (variables_window_sp) {
5409         const Rect variables_bounds = variables_window_sp->GetBounds();
5410 
5411         main_window_sp->RemoveSubWindow(variables_window_sp.get());
5412 
5413         if (registers_window_sp) {
5414           // We have a registers window, so give all the area back to the
5415           // registers window
5416           Rect registers_bounds = variables_bounds;
5417           registers_bounds.size.width = source_bounds.size.width;
5418           registers_window_sp->SetBounds(registers_bounds);
5419         } else {
5420           // We have no registers window showing so give the bottom area back
5421           // to the source view
5422           source_window_sp->Resize(source_bounds.size.width,
5423                                    source_bounds.size.height +
5424                                        variables_bounds.size.height);
5425         }
5426       } else {
5427         Rect new_variables_rect;
5428         if (registers_window_sp) {
5429           // We have a registers window so split the area of the registers
5430           // window into two columns where the left hand side will be the
5431           // variables and the right hand side will be the registers
5432           const Rect variables_bounds = registers_window_sp->GetBounds();
5433           Rect new_registers_rect;
5434           variables_bounds.VerticalSplitPercentage(0.50, new_variables_rect,
5435                                                    new_registers_rect);
5436           registers_window_sp->SetBounds(new_registers_rect);
5437         } else {
5438           // No registers window, grab the bottom part of the source window
5439           Rect new_source_rect;
5440           source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
5441                                                   new_variables_rect);
5442           source_window_sp->SetBounds(new_source_rect);
5443         }
5444         WindowSP new_window_sp = main_window_sp->CreateSubWindow(
5445             "Variables", new_variables_rect, false);
5446         new_window_sp->SetDelegate(
5447             WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
5448       }
5449       touchwin(stdscr);
5450     }
5451       return MenuActionResult::Handled;
5452 
5453     case eMenuID_ViewRegisters: {
5454       WindowSP main_window_sp = m_app.GetMainWindow();
5455       WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
5456       WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
5457       WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
5458       const Rect source_bounds = source_window_sp->GetBounds();
5459 
5460       if (registers_window_sp) {
5461         if (variables_window_sp) {
5462           const Rect variables_bounds = variables_window_sp->GetBounds();
5463 
5464           // We have a variables window, so give all the area back to the
5465           // variables window
5466           variables_window_sp->Resize(variables_bounds.size.width +
5467                                           registers_window_sp->GetWidth(),
5468                                       variables_bounds.size.height);
5469         } else {
5470           // We have no variables window showing so give the bottom area back
5471           // to the source view
5472           source_window_sp->Resize(source_bounds.size.width,
5473                                    source_bounds.size.height +
5474                                        registers_window_sp->GetHeight());
5475         }
5476         main_window_sp->RemoveSubWindow(registers_window_sp.get());
5477       } else {
5478         Rect new_regs_rect;
5479         if (variables_window_sp) {
5480           // We have a variables window, split it into two columns where the
5481           // left hand side will be the variables and the right hand side will
5482           // be the registers
5483           const Rect variables_bounds = variables_window_sp->GetBounds();
5484           Rect new_vars_rect;
5485           variables_bounds.VerticalSplitPercentage(0.50, new_vars_rect,
5486                                                    new_regs_rect);
5487           variables_window_sp->SetBounds(new_vars_rect);
5488         } else {
5489           // No variables window, grab the bottom part of the source window
5490           Rect new_source_rect;
5491           source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
5492                                                   new_regs_rect);
5493           source_window_sp->SetBounds(new_source_rect);
5494         }
5495         WindowSP new_window_sp =
5496             main_window_sp->CreateSubWindow("Registers", new_regs_rect, false);
5497         new_window_sp->SetDelegate(
5498             WindowDelegateSP(new RegistersWindowDelegate(m_debugger)));
5499       }
5500       touchwin(stdscr);
5501     }
5502       return MenuActionResult::Handled;
5503 
5504     case eMenuID_HelpGUIHelp:
5505       m_app.GetMainWindow()->CreateHelpSubwindow();
5506       return MenuActionResult::Handled;
5507 
5508     default:
5509       break;
5510     }
5511 
5512     return MenuActionResult::NotHandled;
5513   }
5514 
5515 protected:
5516   Application &m_app;
5517   Debugger &m_debugger;
5518 };
5519 
5520 class StatusBarWindowDelegate : public WindowDelegate {
5521 public:
5522   StatusBarWindowDelegate(Debugger &debugger) : m_debugger(debugger) {
5523     FormatEntity::Parse("Thread: ${thread.id%tid}", m_format);
5524   }
5525 
5526   ~StatusBarWindowDelegate() override = default;
5527 
5528   bool WindowDelegateDraw(Window &window, bool force) override {
5529     ExecutionContext exe_ctx =
5530         m_debugger.GetCommandInterpreter().GetExecutionContext();
5531     Process *process = exe_ctx.GetProcessPtr();
5532     Thread *thread = exe_ctx.GetThreadPtr();
5533     StackFrame *frame = exe_ctx.GetFramePtr();
5534     window.Erase();
5535     window.SetBackground(BlackOnWhite);
5536     window.MoveCursor(0, 0);
5537     if (process) {
5538       const StateType state = process->GetState();
5539       window.Printf("Process: %5" PRIu64 " %10s", process->GetID(),
5540                     StateAsCString(state));
5541 
5542       if (StateIsStoppedState(state, true)) {
5543         StreamString strm;
5544         if (thread && FormatEntity::Format(m_format, strm, nullptr, &exe_ctx,
5545                                            nullptr, nullptr, false, false)) {
5546           window.MoveCursor(40, 0);
5547           window.PutCStringTruncated(1, strm.GetString().str().c_str());
5548         }
5549 
5550         window.MoveCursor(60, 0);
5551         if (frame)
5552           window.Printf("Frame: %3u  PC = 0x%16.16" PRIx64,
5553                         frame->GetFrameIndex(),
5554                         frame->GetFrameCodeAddress().GetOpcodeLoadAddress(
5555                             exe_ctx.GetTargetPtr()));
5556       } else if (state == eStateExited) {
5557         const char *exit_desc = process->GetExitDescription();
5558         const int exit_status = process->GetExitStatus();
5559         if (exit_desc && exit_desc[0])
5560           window.Printf(" with status = %i (%s)", exit_status, exit_desc);
5561         else
5562           window.Printf(" with status = %i", exit_status);
5563       }
5564     }
5565     return true;
5566   }
5567 
5568 protected:
5569   Debugger &m_debugger;
5570   FormatEntity::Entry m_format;
5571 };
5572 
5573 class SourceFileWindowDelegate : public WindowDelegate {
5574 public:
5575   SourceFileWindowDelegate(Debugger &debugger)
5576       : WindowDelegate(), m_debugger(debugger), m_sc(), m_file_sp(),
5577         m_disassembly_scope(nullptr), m_disassembly_sp(), m_disassembly_range(),
5578         m_title(), m_tid(LLDB_INVALID_THREAD_ID), m_line_width(4),
5579         m_selected_line(0), m_pc_line(0), m_stop_id(0), m_frame_idx(UINT32_MAX),
5580         m_first_visible_line(0), m_first_visible_column(0), m_min_x(0),
5581         m_min_y(0), m_max_x(0), m_max_y(0) {}
5582 
5583   ~SourceFileWindowDelegate() override = default;
5584 
5585   void Update(const SymbolContext &sc) { m_sc = sc; }
5586 
5587   uint32_t NumVisibleLines() const { return m_max_y - m_min_y; }
5588 
5589   const char *WindowDelegateGetHelpText() override {
5590     return "Source/Disassembly window keyboard shortcuts:";
5591   }
5592 
5593   KeyHelp *WindowDelegateGetKeyHelp() override {
5594     static curses::KeyHelp g_source_view_key_help[] = {
5595         {KEY_RETURN, "Run to selected line with one shot breakpoint"},
5596         {KEY_UP, "Select previous source line"},
5597         {KEY_DOWN, "Select next source line"},
5598         {KEY_LEFT, "Scroll to the left"},
5599         {KEY_RIGHT, "Scroll to the right"},
5600         {KEY_PPAGE, "Page up"},
5601         {KEY_NPAGE, "Page down"},
5602         {'b', "Set breakpoint on selected source/disassembly line"},
5603         {'c', "Continue process"},
5604         {'D', "Detach with process suspended"},
5605         {'h', "Show help dialog"},
5606         {'n', "Step over (source line)"},
5607         {'N', "Step over (single instruction)"},
5608         {'f', "Step out (finish)"},
5609         {'s', "Step in (source line)"},
5610         {'S', "Step in (single instruction)"},
5611         {'u', "Frame up"},
5612         {'d', "Frame down"},
5613         {',', "Page up"},
5614         {'.', "Page down"},
5615         {'\0', nullptr}};
5616     return g_source_view_key_help;
5617   }
5618 
5619   bool WindowDelegateDraw(Window &window, bool force) override {
5620     ExecutionContext exe_ctx =
5621         m_debugger.GetCommandInterpreter().GetExecutionContext();
5622     Process *process = exe_ctx.GetProcessPtr();
5623     Thread *thread = nullptr;
5624 
5625     bool update_location = false;
5626     if (process) {
5627       StateType state = process->GetState();
5628       if (StateIsStoppedState(state, true)) {
5629         // We are stopped, so it is ok to
5630         update_location = true;
5631       }
5632     }
5633 
5634     m_min_x = 1;
5635     m_min_y = 2;
5636     m_max_x = window.GetMaxX() - 1;
5637     m_max_y = window.GetMaxY() - 1;
5638 
5639     const uint32_t num_visible_lines = NumVisibleLines();
5640     StackFrameSP frame_sp;
5641     bool set_selected_line_to_pc = false;
5642 
5643     if (update_location) {
5644       const bool process_alive = process ? process->IsAlive() : false;
5645       bool thread_changed = false;
5646       if (process_alive) {
5647         thread = exe_ctx.GetThreadPtr();
5648         if (thread) {
5649           frame_sp = thread->GetSelectedFrame();
5650           auto tid = thread->GetID();
5651           thread_changed = tid != m_tid;
5652           m_tid = tid;
5653         } else {
5654           if (m_tid != LLDB_INVALID_THREAD_ID) {
5655             thread_changed = true;
5656             m_tid = LLDB_INVALID_THREAD_ID;
5657           }
5658         }
5659       }
5660       const uint32_t stop_id = process ? process->GetStopID() : 0;
5661       const bool stop_id_changed = stop_id != m_stop_id;
5662       bool frame_changed = false;
5663       m_stop_id = stop_id;
5664       m_title.Clear();
5665       if (frame_sp) {
5666         m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
5667         if (m_sc.module_sp) {
5668           m_title.Printf(
5669               "%s", m_sc.module_sp->GetFileSpec().GetFilename().GetCString());
5670           ConstString func_name = m_sc.GetFunctionName();
5671           if (func_name)
5672             m_title.Printf("`%s", func_name.GetCString());
5673         }
5674         const uint32_t frame_idx = frame_sp->GetFrameIndex();
5675         frame_changed = frame_idx != m_frame_idx;
5676         m_frame_idx = frame_idx;
5677       } else {
5678         m_sc.Clear(true);
5679         frame_changed = m_frame_idx != UINT32_MAX;
5680         m_frame_idx = UINT32_MAX;
5681       }
5682 
5683       const bool context_changed =
5684           thread_changed || frame_changed || stop_id_changed;
5685 
5686       if (process_alive) {
5687         if (m_sc.line_entry.IsValid()) {
5688           m_pc_line = m_sc.line_entry.line;
5689           if (m_pc_line != UINT32_MAX)
5690             --m_pc_line; // Convert to zero based line number...
5691           // Update the selected line if the stop ID changed...
5692           if (context_changed)
5693             m_selected_line = m_pc_line;
5694 
5695           if (m_file_sp && m_file_sp->GetFileSpec() == m_sc.line_entry.file) {
5696             // Same file, nothing to do, we should either have the lines or not
5697             // (source file missing)
5698             if (m_selected_line >= static_cast<size_t>(m_first_visible_line)) {
5699               if (m_selected_line >= m_first_visible_line + num_visible_lines)
5700                 m_first_visible_line = m_selected_line - 10;
5701             } else {
5702               if (m_selected_line > 10)
5703                 m_first_visible_line = m_selected_line - 10;
5704               else
5705                 m_first_visible_line = 0;
5706             }
5707           } else {
5708             // File changed, set selected line to the line with the PC
5709             m_selected_line = m_pc_line;
5710             m_file_sp =
5711                 m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file);
5712             if (m_file_sp) {
5713               const size_t num_lines = m_file_sp->GetNumLines();
5714               m_line_width = 1;
5715               for (size_t n = num_lines; n >= 10; n = n / 10)
5716                 ++m_line_width;
5717 
5718               if (num_lines < num_visible_lines ||
5719                   m_selected_line < num_visible_lines)
5720                 m_first_visible_line = 0;
5721               else
5722                 m_first_visible_line = m_selected_line - 10;
5723             }
5724           }
5725         } else {
5726           m_file_sp.reset();
5727         }
5728 
5729         if (!m_file_sp || m_file_sp->GetNumLines() == 0) {
5730           // Show disassembly
5731           bool prefer_file_cache = false;
5732           if (m_sc.function) {
5733             if (m_disassembly_scope != m_sc.function) {
5734               m_disassembly_scope = m_sc.function;
5735               m_disassembly_sp = m_sc.function->GetInstructions(
5736                   exe_ctx, nullptr, !prefer_file_cache);
5737               if (m_disassembly_sp) {
5738                 set_selected_line_to_pc = true;
5739                 m_disassembly_range = m_sc.function->GetAddressRange();
5740               } else {
5741                 m_disassembly_range.Clear();
5742               }
5743             } else {
5744               set_selected_line_to_pc = context_changed;
5745             }
5746           } else if (m_sc.symbol) {
5747             if (m_disassembly_scope != m_sc.symbol) {
5748               m_disassembly_scope = m_sc.symbol;
5749               m_disassembly_sp = m_sc.symbol->GetInstructions(
5750                   exe_ctx, nullptr, prefer_file_cache);
5751               if (m_disassembly_sp) {
5752                 set_selected_line_to_pc = true;
5753                 m_disassembly_range.GetBaseAddress() =
5754                     m_sc.symbol->GetAddress();
5755                 m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize());
5756               } else {
5757                 m_disassembly_range.Clear();
5758               }
5759             } else {
5760               set_selected_line_to_pc = context_changed;
5761             }
5762           }
5763         }
5764       } else {
5765         m_pc_line = UINT32_MAX;
5766       }
5767     }
5768 
5769     const int window_width = window.GetWidth();
5770     window.Erase();
5771     window.DrawTitleBox("Sources");
5772     if (!m_title.GetString().empty()) {
5773       window.AttributeOn(A_REVERSE);
5774       window.MoveCursor(1, 1);
5775       window.PutChar(' ');
5776       window.PutCStringTruncated(1, m_title.GetString().str().c_str());
5777       int x = window.GetCursorX();
5778       if (x < window_width - 1) {
5779         window.Printf("%*s", window_width - x - 1, "");
5780       }
5781       window.AttributeOff(A_REVERSE);
5782     }
5783 
5784     Target *target = exe_ctx.GetTargetPtr();
5785     const size_t num_source_lines = GetNumSourceLines();
5786     if (num_source_lines > 0) {
5787       // Display source
5788       BreakpointLines bp_lines;
5789       if (target) {
5790         BreakpointList &bp_list = target->GetBreakpointList();
5791         const size_t num_bps = bp_list.GetSize();
5792         for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
5793           BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
5794           const size_t num_bps_locs = bp_sp->GetNumLocations();
5795           for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
5796             BreakpointLocationSP bp_loc_sp =
5797                 bp_sp->GetLocationAtIndex(bp_loc_idx);
5798             LineEntry bp_loc_line_entry;
5799             if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry(
5800                     bp_loc_line_entry)) {
5801               if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file) {
5802                 bp_lines.insert(bp_loc_line_entry.line);
5803               }
5804             }
5805           }
5806         }
5807       }
5808 
5809       const attr_t selected_highlight_attr = A_REVERSE;
5810       const attr_t pc_highlight_attr = COLOR_PAIR(BlackOnBlue);
5811 
5812       for (size_t i = 0; i < num_visible_lines; ++i) {
5813         const uint32_t curr_line = m_first_visible_line + i;
5814         if (curr_line < num_source_lines) {
5815           const int line_y = m_min_y + i;
5816           window.MoveCursor(1, line_y);
5817           const bool is_pc_line = curr_line == m_pc_line;
5818           const bool line_is_selected = m_selected_line == curr_line;
5819           // Highlight the line as the PC line first, then if the selected line
5820           // isn't the same as the PC line, highlight it differently
5821           attr_t highlight_attr = 0;
5822           attr_t bp_attr = 0;
5823           if (is_pc_line)
5824             highlight_attr = pc_highlight_attr;
5825           else if (line_is_selected)
5826             highlight_attr = selected_highlight_attr;
5827 
5828           if (bp_lines.find(curr_line + 1) != bp_lines.end())
5829             bp_attr = COLOR_PAIR(BlackOnWhite);
5830 
5831           if (bp_attr)
5832             window.AttributeOn(bp_attr);
5833 
5834           window.Printf(" %*u ", m_line_width, curr_line + 1);
5835 
5836           if (bp_attr)
5837             window.AttributeOff(bp_attr);
5838 
5839           window.PutChar(ACS_VLINE);
5840           // Mark the line with the PC with a diamond
5841           if (is_pc_line)
5842             window.PutChar(ACS_DIAMOND);
5843           else
5844             window.PutChar(' ');
5845 
5846           if (highlight_attr)
5847             window.AttributeOn(highlight_attr);
5848 
5849           StreamString lineStream;
5850           m_file_sp->DisplaySourceLines(curr_line + 1, {}, 0, 0, &lineStream);
5851           StringRef line = lineStream.GetString();
5852           if (line.endswith("\n"))
5853             line = line.drop_back();
5854           bool wasWritten = window.OutputColoredStringTruncated(
5855               1, line, m_first_visible_column, line_is_selected);
5856           if (line_is_selected && !wasWritten) {
5857             // Draw an empty space to show the selected line if empty,
5858             // or draw '<' if nothing is visible because of scrolling too much
5859             // to the right.
5860             window.PutCStringTruncated(
5861                 1, line.empty() && m_first_visible_column == 0 ? " " : "<");
5862           }
5863 
5864           if (is_pc_line && frame_sp &&
5865               frame_sp->GetConcreteFrameIndex() == 0) {
5866             StopInfoSP stop_info_sp;
5867             if (thread)
5868               stop_info_sp = thread->GetStopInfo();
5869             if (stop_info_sp) {
5870               const char *stop_description = stop_info_sp->GetDescription();
5871               if (stop_description && stop_description[0]) {
5872                 size_t stop_description_len = strlen(stop_description);
5873                 int desc_x = window_width - stop_description_len - 16;
5874                 if (desc_x - window.GetCursorX() > 0)
5875                   window.Printf("%*s", desc_x - window.GetCursorX(), "");
5876                 window.MoveCursor(window_width - stop_description_len - 16,
5877                                   line_y);
5878                 const attr_t stop_reason_attr = COLOR_PAIR(WhiteOnBlue);
5879                 window.AttributeOn(stop_reason_attr);
5880                 window.PrintfTruncated(1, " <<< Thread %u: %s ",
5881                                        thread->GetIndexID(), stop_description);
5882                 window.AttributeOff(stop_reason_attr);
5883               }
5884             } else {
5885               window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
5886             }
5887           }
5888           if (highlight_attr)
5889             window.AttributeOff(highlight_attr);
5890         } else {
5891           break;
5892         }
5893       }
5894     } else {
5895       size_t num_disassembly_lines = GetNumDisassemblyLines();
5896       if (num_disassembly_lines > 0) {
5897         // Display disassembly
5898         BreakpointAddrs bp_file_addrs;
5899         Target *target = exe_ctx.GetTargetPtr();
5900         if (target) {
5901           BreakpointList &bp_list = target->GetBreakpointList();
5902           const size_t num_bps = bp_list.GetSize();
5903           for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
5904             BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
5905             const size_t num_bps_locs = bp_sp->GetNumLocations();
5906             for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs;
5907                  ++bp_loc_idx) {
5908               BreakpointLocationSP bp_loc_sp =
5909                   bp_sp->GetLocationAtIndex(bp_loc_idx);
5910               LineEntry bp_loc_line_entry;
5911               const lldb::addr_t file_addr =
5912                   bp_loc_sp->GetAddress().GetFileAddress();
5913               if (file_addr != LLDB_INVALID_ADDRESS) {
5914                 if (m_disassembly_range.ContainsFileAddress(file_addr))
5915                   bp_file_addrs.insert(file_addr);
5916               }
5917             }
5918           }
5919         }
5920 
5921         const attr_t selected_highlight_attr = A_REVERSE;
5922         const attr_t pc_highlight_attr = COLOR_PAIR(WhiteOnBlue);
5923 
5924         StreamString strm;
5925 
5926         InstructionList &insts = m_disassembly_sp->GetInstructionList();
5927         Address pc_address;
5928 
5929         if (frame_sp)
5930           pc_address = frame_sp->GetFrameCodeAddress();
5931         const uint32_t pc_idx =
5932             pc_address.IsValid()
5933                 ? insts.GetIndexOfInstructionAtAddress(pc_address)
5934                 : UINT32_MAX;
5935         if (set_selected_line_to_pc) {
5936           m_selected_line = pc_idx;
5937         }
5938 
5939         const uint32_t non_visible_pc_offset = (num_visible_lines / 5);
5940         if (static_cast<size_t>(m_first_visible_line) >= num_disassembly_lines)
5941           m_first_visible_line = 0;
5942 
5943         if (pc_idx < num_disassembly_lines) {
5944           if (pc_idx < static_cast<uint32_t>(m_first_visible_line) ||
5945               pc_idx >= m_first_visible_line + num_visible_lines)
5946             m_first_visible_line = pc_idx - non_visible_pc_offset;
5947         }
5948 
5949         for (size_t i = 0; i < num_visible_lines; ++i) {
5950           const uint32_t inst_idx = m_first_visible_line + i;
5951           Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get();
5952           if (!inst)
5953             break;
5954 
5955           const int line_y = m_min_y + i;
5956           window.MoveCursor(1, line_y);
5957           const bool is_pc_line = frame_sp && inst_idx == pc_idx;
5958           const bool line_is_selected = m_selected_line == inst_idx;
5959           // Highlight the line as the PC line first, then if the selected line
5960           // isn't the same as the PC line, highlight it differently
5961           attr_t highlight_attr = 0;
5962           attr_t bp_attr = 0;
5963           if (is_pc_line)
5964             highlight_attr = pc_highlight_attr;
5965           else if (line_is_selected)
5966             highlight_attr = selected_highlight_attr;
5967 
5968           if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) !=
5969               bp_file_addrs.end())
5970             bp_attr = COLOR_PAIR(BlackOnWhite);
5971 
5972           if (bp_attr)
5973             window.AttributeOn(bp_attr);
5974 
5975           window.Printf(" 0x%16.16llx ",
5976                         static_cast<unsigned long long>(
5977                             inst->GetAddress().GetLoadAddress(target)));
5978 
5979           if (bp_attr)
5980             window.AttributeOff(bp_attr);
5981 
5982           window.PutChar(ACS_VLINE);
5983           // Mark the line with the PC with a diamond
5984           if (is_pc_line)
5985             window.PutChar(ACS_DIAMOND);
5986           else
5987             window.PutChar(' ');
5988 
5989           if (highlight_attr)
5990             window.AttributeOn(highlight_attr);
5991 
5992           const char *mnemonic = inst->GetMnemonic(&exe_ctx);
5993           const char *operands = inst->GetOperands(&exe_ctx);
5994           const char *comment = inst->GetComment(&exe_ctx);
5995 
5996           if (mnemonic != nullptr && mnemonic[0] == '\0')
5997             mnemonic = nullptr;
5998           if (operands != nullptr && operands[0] == '\0')
5999             operands = nullptr;
6000           if (comment != nullptr && comment[0] == '\0')
6001             comment = nullptr;
6002 
6003           strm.Clear();
6004 
6005           if (mnemonic != nullptr && operands != nullptr && comment != nullptr)
6006             strm.Printf("%-8s %-25s ; %s", mnemonic, operands, comment);
6007           else if (mnemonic != nullptr && operands != nullptr)
6008             strm.Printf("%-8s %s", mnemonic, operands);
6009           else if (mnemonic != nullptr)
6010             strm.Printf("%s", mnemonic);
6011 
6012           int right_pad = 1;
6013           window.PutCStringTruncated(
6014               right_pad,
6015               strm.GetString().substr(m_first_visible_column).data());
6016 
6017           if (is_pc_line && frame_sp &&
6018               frame_sp->GetConcreteFrameIndex() == 0) {
6019             StopInfoSP stop_info_sp;
6020             if (thread)
6021               stop_info_sp = thread->GetStopInfo();
6022             if (stop_info_sp) {
6023               const char *stop_description = stop_info_sp->GetDescription();
6024               if (stop_description && stop_description[0]) {
6025                 size_t stop_description_len = strlen(stop_description);
6026                 int desc_x = window_width - stop_description_len - 16;
6027                 if (desc_x - window.GetCursorX() > 0)
6028                   window.Printf("%*s", desc_x - window.GetCursorX(), "");
6029                 window.MoveCursor(window_width - stop_description_len - 15,
6030                                   line_y);
6031                 window.PrintfTruncated(1, "<<< Thread %u: %s ",
6032                                        thread->GetIndexID(), stop_description);
6033               }
6034             } else {
6035               window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
6036             }
6037           }
6038           if (highlight_attr)
6039             window.AttributeOff(highlight_attr);
6040         }
6041       }
6042     }
6043     return true; // Drawing handled
6044   }
6045 
6046   size_t GetNumLines() {
6047     size_t num_lines = GetNumSourceLines();
6048     if (num_lines == 0)
6049       num_lines = GetNumDisassemblyLines();
6050     return num_lines;
6051   }
6052 
6053   size_t GetNumSourceLines() const {
6054     if (m_file_sp)
6055       return m_file_sp->GetNumLines();
6056     return 0;
6057   }
6058 
6059   size_t GetNumDisassemblyLines() const {
6060     if (m_disassembly_sp)
6061       return m_disassembly_sp->GetInstructionList().GetSize();
6062     return 0;
6063   }
6064 
6065   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
6066     const uint32_t num_visible_lines = NumVisibleLines();
6067     const size_t num_lines = GetNumLines();
6068 
6069     switch (c) {
6070     case ',':
6071     case KEY_PPAGE:
6072       // Page up key
6073       if (static_cast<uint32_t>(m_first_visible_line) > num_visible_lines)
6074         m_first_visible_line -= num_visible_lines;
6075       else
6076         m_first_visible_line = 0;
6077       m_selected_line = m_first_visible_line;
6078       return eKeyHandled;
6079 
6080     case '.':
6081     case KEY_NPAGE:
6082       // Page down key
6083       {
6084         if (m_first_visible_line + num_visible_lines < num_lines)
6085           m_first_visible_line += num_visible_lines;
6086         else if (num_lines < num_visible_lines)
6087           m_first_visible_line = 0;
6088         else
6089           m_first_visible_line = num_lines - num_visible_lines;
6090         m_selected_line = m_first_visible_line;
6091       }
6092       return eKeyHandled;
6093 
6094     case KEY_UP:
6095       if (m_selected_line > 0) {
6096         m_selected_line--;
6097         if (static_cast<size_t>(m_first_visible_line) > m_selected_line)
6098           m_first_visible_line = m_selected_line;
6099       }
6100       return eKeyHandled;
6101 
6102     case KEY_DOWN:
6103       if (m_selected_line + 1 < num_lines) {
6104         m_selected_line++;
6105         if (m_first_visible_line + num_visible_lines < m_selected_line)
6106           m_first_visible_line++;
6107       }
6108       return eKeyHandled;
6109 
6110     case KEY_LEFT:
6111       if (m_first_visible_column > 0)
6112         --m_first_visible_column;
6113       return eKeyHandled;
6114 
6115     case KEY_RIGHT:
6116       ++m_first_visible_column;
6117       return eKeyHandled;
6118 
6119     case '\r':
6120     case '\n':
6121     case KEY_ENTER:
6122       // Set a breakpoint and run to the line using a one shot breakpoint
6123       if (GetNumSourceLines() > 0) {
6124         ExecutionContext exe_ctx =
6125             m_debugger.GetCommandInterpreter().GetExecutionContext();
6126         if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive()) {
6127           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
6128               nullptr, // Don't limit the breakpoint to certain modules
6129               m_file_sp->GetFileSpec(), // Source file
6130               m_selected_line +
6131                   1, // Source line number (m_selected_line is zero based)
6132               0,     // Unspecified column.
6133               0,     // No offset
6134               eLazyBoolCalculate,  // Check inlines using global setting
6135               eLazyBoolCalculate,  // Skip prologue using global setting,
6136               false,               // internal
6137               false,               // request_hardware
6138               eLazyBoolCalculate); // move_to_nearest_code
6139           // Make breakpoint one shot
6140           bp_sp->GetOptions().SetOneShot(true);
6141           exe_ctx.GetProcessRef().Resume();
6142         }
6143       } else if (m_selected_line < GetNumDisassemblyLines()) {
6144         const Instruction *inst = m_disassembly_sp->GetInstructionList()
6145                                       .GetInstructionAtIndex(m_selected_line)
6146                                       .get();
6147         ExecutionContext exe_ctx =
6148             m_debugger.GetCommandInterpreter().GetExecutionContext();
6149         if (exe_ctx.HasTargetScope()) {
6150           Address addr = inst->GetAddress();
6151           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
6152               addr,   // lldb_private::Address
6153               false,  // internal
6154               false); // request_hardware
6155           // Make breakpoint one shot
6156           bp_sp->GetOptions().SetOneShot(true);
6157           exe_ctx.GetProcessRef().Resume();
6158         }
6159       }
6160       return eKeyHandled;
6161 
6162     case 'b': // 'b' == toggle breakpoint on currently selected line
6163       ToggleBreakpointOnSelectedLine();
6164       return eKeyHandled;
6165 
6166     case 'D': // 'D' == detach and keep stopped
6167     {
6168       ExecutionContext exe_ctx =
6169           m_debugger.GetCommandInterpreter().GetExecutionContext();
6170       if (exe_ctx.HasProcessScope())
6171         exe_ctx.GetProcessRef().Detach(true);
6172     }
6173       return eKeyHandled;
6174 
6175     case 'c':
6176       // 'c' == continue
6177       {
6178         ExecutionContext exe_ctx =
6179             m_debugger.GetCommandInterpreter().GetExecutionContext();
6180         if (exe_ctx.HasProcessScope())
6181           exe_ctx.GetProcessRef().Resume();
6182       }
6183       return eKeyHandled;
6184 
6185     case 'f':
6186       // 'f' == step out (finish)
6187       {
6188         ExecutionContext exe_ctx =
6189             m_debugger.GetCommandInterpreter().GetExecutionContext();
6190         if (exe_ctx.HasThreadScope() &&
6191             StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
6192           exe_ctx.GetThreadRef().StepOut();
6193         }
6194       }
6195       return eKeyHandled;
6196 
6197     case 'n': // 'n' == step over
6198     case 'N': // 'N' == step over instruction
6199     {
6200       ExecutionContext exe_ctx =
6201           m_debugger.GetCommandInterpreter().GetExecutionContext();
6202       if (exe_ctx.HasThreadScope() &&
6203           StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
6204         bool source_step = (c == 'n');
6205         exe_ctx.GetThreadRef().StepOver(source_step);
6206       }
6207     }
6208       return eKeyHandled;
6209 
6210     case 's': // 's' == step into
6211     case 'S': // 'S' == step into instruction
6212     {
6213       ExecutionContext exe_ctx =
6214           m_debugger.GetCommandInterpreter().GetExecutionContext();
6215       if (exe_ctx.HasThreadScope() &&
6216           StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
6217         bool source_step = (c == 's');
6218         exe_ctx.GetThreadRef().StepIn(source_step);
6219       }
6220     }
6221       return eKeyHandled;
6222 
6223     case 'u': // 'u' == frame up
6224     case 'd': // 'd' == frame down
6225     {
6226       ExecutionContext exe_ctx =
6227           m_debugger.GetCommandInterpreter().GetExecutionContext();
6228       if (exe_ctx.HasThreadScope()) {
6229         Thread *thread = exe_ctx.GetThreadPtr();
6230         uint32_t frame_idx = thread->GetSelectedFrameIndex();
6231         if (frame_idx == UINT32_MAX)
6232           frame_idx = 0;
6233         if (c == 'u' && frame_idx + 1 < thread->GetStackFrameCount())
6234           ++frame_idx;
6235         else if (c == 'd' && frame_idx > 0)
6236           --frame_idx;
6237         if (thread->SetSelectedFrameByIndex(frame_idx, true))
6238           exe_ctx.SetFrameSP(thread->GetSelectedFrame());
6239       }
6240     }
6241       return eKeyHandled;
6242 
6243     case 'h':
6244       window.CreateHelpSubwindow();
6245       return eKeyHandled;
6246 
6247     default:
6248       break;
6249     }
6250     return eKeyNotHandled;
6251   }
6252 
6253   void ToggleBreakpointOnSelectedLine() {
6254     ExecutionContext exe_ctx =
6255         m_debugger.GetCommandInterpreter().GetExecutionContext();
6256     if (!exe_ctx.HasTargetScope())
6257       return;
6258     if (GetNumSourceLines() > 0) {
6259       // Source file breakpoint.
6260       BreakpointList &bp_list = exe_ctx.GetTargetRef().GetBreakpointList();
6261       const size_t num_bps = bp_list.GetSize();
6262       for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
6263         BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
6264         const size_t num_bps_locs = bp_sp->GetNumLocations();
6265         for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
6266           BreakpointLocationSP bp_loc_sp =
6267               bp_sp->GetLocationAtIndex(bp_loc_idx);
6268           LineEntry bp_loc_line_entry;
6269           if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry(
6270                   bp_loc_line_entry)) {
6271             if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file &&
6272                 m_selected_line + 1 == bp_loc_line_entry.line) {
6273               bool removed =
6274                   exe_ctx.GetTargetRef().RemoveBreakpointByID(bp_sp->GetID());
6275               assert(removed);
6276               UNUSED_IF_ASSERT_DISABLED(removed);
6277               return; // Existing breakpoint removed.
6278             }
6279           }
6280         }
6281       }
6282       // No breakpoint found on the location, add it.
6283       BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
6284           nullptr, // Don't limit the breakpoint to certain modules
6285           m_file_sp->GetFileSpec(), // Source file
6286           m_selected_line +
6287               1, // Source line number (m_selected_line is zero based)
6288           0,     // No column specified.
6289           0,     // No offset
6290           eLazyBoolCalculate,  // Check inlines using global setting
6291           eLazyBoolCalculate,  // Skip prologue using global setting,
6292           false,               // internal
6293           false,               // request_hardware
6294           eLazyBoolCalculate); // move_to_nearest_code
6295     } else {
6296       // Disassembly breakpoint.
6297       assert(GetNumDisassemblyLines() > 0);
6298       assert(m_selected_line < GetNumDisassemblyLines());
6299       const Instruction *inst = m_disassembly_sp->GetInstructionList()
6300                                     .GetInstructionAtIndex(m_selected_line)
6301                                     .get();
6302       Address addr = inst->GetAddress();
6303       // Try to find it.
6304       BreakpointList &bp_list = exe_ctx.GetTargetRef().GetBreakpointList();
6305       const size_t num_bps = bp_list.GetSize();
6306       for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
6307         BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
6308         const size_t num_bps_locs = bp_sp->GetNumLocations();
6309         for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
6310           BreakpointLocationSP bp_loc_sp =
6311               bp_sp->GetLocationAtIndex(bp_loc_idx);
6312           LineEntry bp_loc_line_entry;
6313           const lldb::addr_t file_addr =
6314               bp_loc_sp->GetAddress().GetFileAddress();
6315           if (file_addr == addr.GetFileAddress()) {
6316             bool removed =
6317                 exe_ctx.GetTargetRef().RemoveBreakpointByID(bp_sp->GetID());
6318             assert(removed);
6319             UNUSED_IF_ASSERT_DISABLED(removed);
6320             return; // Existing breakpoint removed.
6321           }
6322         }
6323       }
6324       // No breakpoint found on the address, add it.
6325       BreakpointSP bp_sp =
6326           exe_ctx.GetTargetRef().CreateBreakpoint(addr, // lldb_private::Address
6327                                                   false,  // internal
6328                                                   false); // request_hardware
6329     }
6330   }
6331 
6332 protected:
6333   typedef std::set<uint32_t> BreakpointLines;
6334   typedef std::set<lldb::addr_t> BreakpointAddrs;
6335 
6336   Debugger &m_debugger;
6337   SymbolContext m_sc;
6338   SourceManager::FileSP m_file_sp;
6339   SymbolContextScope *m_disassembly_scope;
6340   lldb::DisassemblerSP m_disassembly_sp;
6341   AddressRange m_disassembly_range;
6342   StreamString m_title;
6343   lldb::user_id_t m_tid;
6344   int m_line_width;
6345   uint32_t m_selected_line; // The selected line
6346   uint32_t m_pc_line;       // The line with the PC
6347   uint32_t m_stop_id;
6348   uint32_t m_frame_idx;
6349   int m_first_visible_line;
6350   int m_first_visible_column;
6351   int m_min_x;
6352   int m_min_y;
6353   int m_max_x;
6354   int m_max_y;
6355 };
6356 
6357 DisplayOptions ValueObjectListDelegate::g_options = {true};
6358 
6359 IOHandlerCursesGUI::IOHandlerCursesGUI(Debugger &debugger)
6360     : IOHandler(debugger, IOHandler::Type::Curses) {}
6361 
6362 void IOHandlerCursesGUI::Activate() {
6363   IOHandler::Activate();
6364   if (!m_app_ap) {
6365     m_app_ap = std::make_unique<Application>(GetInputFILE(), GetOutputFILE());
6366 
6367     // This is both a window and a menu delegate
6368     std::shared_ptr<ApplicationDelegate> app_delegate_sp(
6369         new ApplicationDelegate(*m_app_ap, m_debugger));
6370 
6371     MenuDelegateSP app_menu_delegate_sp =
6372         std::static_pointer_cast<MenuDelegate>(app_delegate_sp);
6373     MenuSP lldb_menu_sp(
6374         new Menu("LLDB", "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB));
6375     MenuSP exit_menuitem_sp(
6376         new Menu("Exit", nullptr, 'x', ApplicationDelegate::eMenuID_LLDBExit));
6377     exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit);
6378     lldb_menu_sp->AddSubmenu(MenuSP(new Menu(
6379         "About LLDB", nullptr, 'a', ApplicationDelegate::eMenuID_LLDBAbout)));
6380     lldb_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
6381     lldb_menu_sp->AddSubmenu(exit_menuitem_sp);
6382 
6383     MenuSP target_menu_sp(new Menu("Target", "F2", KEY_F(2),
6384                                    ApplicationDelegate::eMenuID_Target));
6385     target_menu_sp->AddSubmenu(MenuSP(new Menu(
6386         "Create", nullptr, 'c', ApplicationDelegate::eMenuID_TargetCreate)));
6387     target_menu_sp->AddSubmenu(MenuSP(new Menu(
6388         "Delete", nullptr, 'd', ApplicationDelegate::eMenuID_TargetDelete)));
6389 
6390     MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3),
6391                                     ApplicationDelegate::eMenuID_Process));
6392     process_menu_sp->AddSubmenu(MenuSP(new Menu(
6393         "Attach", nullptr, 'a', ApplicationDelegate::eMenuID_ProcessAttach)));
6394     process_menu_sp->AddSubmenu(
6395         MenuSP(new Menu("Detach and resume", nullptr, 'd',
6396                         ApplicationDelegate::eMenuID_ProcessDetachResume)));
6397     process_menu_sp->AddSubmenu(
6398         MenuSP(new Menu("Detach suspended", nullptr, 's',
6399                         ApplicationDelegate::eMenuID_ProcessDetachSuspended)));
6400     process_menu_sp->AddSubmenu(MenuSP(new Menu(
6401         "Launch", nullptr, 'l', ApplicationDelegate::eMenuID_ProcessLaunch)));
6402     process_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
6403     process_menu_sp->AddSubmenu(
6404         MenuSP(new Menu("Continue", nullptr, 'c',
6405                         ApplicationDelegate::eMenuID_ProcessContinue)));
6406     process_menu_sp->AddSubmenu(MenuSP(new Menu(
6407         "Halt", nullptr, 'h', ApplicationDelegate::eMenuID_ProcessHalt)));
6408     process_menu_sp->AddSubmenu(MenuSP(new Menu(
6409         "Kill", nullptr, 'k', ApplicationDelegate::eMenuID_ProcessKill)));
6410 
6411     MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4),
6412                                    ApplicationDelegate::eMenuID_Thread));
6413     thread_menu_sp->AddSubmenu(MenuSP(new Menu(
6414         "Step In", nullptr, 'i', ApplicationDelegate::eMenuID_ThreadStepIn)));
6415     thread_menu_sp->AddSubmenu(
6416         MenuSP(new Menu("Step Over", nullptr, 'v',
6417                         ApplicationDelegate::eMenuID_ThreadStepOver)));
6418     thread_menu_sp->AddSubmenu(MenuSP(new Menu(
6419         "Step Out", nullptr, 'o', ApplicationDelegate::eMenuID_ThreadStepOut)));
6420 
6421     MenuSP view_menu_sp(
6422         new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View));
6423     view_menu_sp->AddSubmenu(
6424         MenuSP(new Menu("Backtrace", nullptr, 'b',
6425                         ApplicationDelegate::eMenuID_ViewBacktrace)));
6426     view_menu_sp->AddSubmenu(
6427         MenuSP(new Menu("Registers", nullptr, 'r',
6428                         ApplicationDelegate::eMenuID_ViewRegisters)));
6429     view_menu_sp->AddSubmenu(MenuSP(new Menu(
6430         "Source", nullptr, 's', ApplicationDelegate::eMenuID_ViewSource)));
6431     view_menu_sp->AddSubmenu(
6432         MenuSP(new Menu("Variables", nullptr, 'v',
6433                         ApplicationDelegate::eMenuID_ViewVariables)));
6434 
6435     MenuSP help_menu_sp(
6436         new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help));
6437     help_menu_sp->AddSubmenu(MenuSP(new Menu(
6438         "GUI Help", nullptr, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp)));
6439 
6440     m_app_ap->Initialize();
6441     WindowSP &main_window_sp = m_app_ap->GetMainWindow();
6442 
6443     MenuSP menubar_sp(new Menu(Menu::Type::Bar));
6444     menubar_sp->AddSubmenu(lldb_menu_sp);
6445     menubar_sp->AddSubmenu(target_menu_sp);
6446     menubar_sp->AddSubmenu(process_menu_sp);
6447     menubar_sp->AddSubmenu(thread_menu_sp);
6448     menubar_sp->AddSubmenu(view_menu_sp);
6449     menubar_sp->AddSubmenu(help_menu_sp);
6450     menubar_sp->SetDelegate(app_menu_delegate_sp);
6451 
6452     Rect content_bounds = main_window_sp->GetFrame();
6453     Rect menubar_bounds = content_bounds.MakeMenuBar();
6454     Rect status_bounds = content_bounds.MakeStatusBar();
6455     Rect source_bounds;
6456     Rect variables_bounds;
6457     Rect threads_bounds;
6458     Rect source_variables_bounds;
6459     content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds,
6460                                            threads_bounds);
6461     source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds,
6462                                                       variables_bounds);
6463 
6464     WindowSP menubar_window_sp =
6465         main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false);
6466     // Let the menubar get keys if the active window doesn't handle the keys
6467     // that are typed so it can respond to menubar key presses.
6468     menubar_window_sp->SetCanBeActive(
6469         false); // Don't let the menubar become the active window
6470     menubar_window_sp->SetDelegate(menubar_sp);
6471 
6472     WindowSP source_window_sp(
6473         main_window_sp->CreateSubWindow("Source", source_bounds, true));
6474     WindowSP variables_window_sp(
6475         main_window_sp->CreateSubWindow("Variables", variables_bounds, false));
6476     WindowSP threads_window_sp(
6477         main_window_sp->CreateSubWindow("Threads", threads_bounds, false));
6478     WindowSP status_window_sp(
6479         main_window_sp->CreateSubWindow("Status", status_bounds, false));
6480     status_window_sp->SetCanBeActive(
6481         false); // Don't let the status bar become the active window
6482     main_window_sp->SetDelegate(
6483         std::static_pointer_cast<WindowDelegate>(app_delegate_sp));
6484     source_window_sp->SetDelegate(
6485         WindowDelegateSP(new SourceFileWindowDelegate(m_debugger)));
6486     variables_window_sp->SetDelegate(
6487         WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
6488     TreeDelegateSP thread_delegate_sp(new ThreadsTreeDelegate(m_debugger));
6489     threads_window_sp->SetDelegate(WindowDelegateSP(
6490         new TreeWindowDelegate(m_debugger, thread_delegate_sp)));
6491     status_window_sp->SetDelegate(
6492         WindowDelegateSP(new StatusBarWindowDelegate(m_debugger)));
6493 
6494     // Show the main help window once the first time the curses GUI is launched
6495     static bool g_showed_help = false;
6496     if (!g_showed_help) {
6497       g_showed_help = true;
6498       main_window_sp->CreateHelpSubwindow();
6499     }
6500 
6501     // All colors with black background.
6502     init_pair(1, COLOR_BLACK, COLOR_BLACK);
6503     init_pair(2, COLOR_RED, COLOR_BLACK);
6504     init_pair(3, COLOR_GREEN, COLOR_BLACK);
6505     init_pair(4, COLOR_YELLOW, COLOR_BLACK);
6506     init_pair(5, COLOR_BLUE, COLOR_BLACK);
6507     init_pair(6, COLOR_MAGENTA, COLOR_BLACK);
6508     init_pair(7, COLOR_CYAN, COLOR_BLACK);
6509     init_pair(8, COLOR_WHITE, COLOR_BLACK);
6510     // All colors with blue background.
6511     init_pair(9, COLOR_BLACK, COLOR_BLUE);
6512     init_pair(10, COLOR_RED, COLOR_BLUE);
6513     init_pair(11, COLOR_GREEN, COLOR_BLUE);
6514     init_pair(12, COLOR_YELLOW, COLOR_BLUE);
6515     init_pair(13, COLOR_BLUE, COLOR_BLUE);
6516     init_pair(14, COLOR_MAGENTA, COLOR_BLUE);
6517     init_pair(15, COLOR_CYAN, COLOR_BLUE);
6518     init_pair(16, COLOR_WHITE, COLOR_BLUE);
6519     // These must match the order in the color indexes enum.
6520     init_pair(17, COLOR_BLACK, COLOR_WHITE);
6521     init_pair(18, COLOR_MAGENTA, COLOR_WHITE);
6522     static_assert(LastColorPairIndex == 18, "Color indexes do not match.");
6523 
6524     define_key("\033[Z", KEY_SHIFT_TAB);
6525   }
6526 }
6527 
6528 void IOHandlerCursesGUI::Deactivate() { m_app_ap->Terminate(); }
6529 
6530 void IOHandlerCursesGUI::Run() {
6531   m_app_ap->Run(m_debugger);
6532   SetIsDone(true);
6533 }
6534 
6535 IOHandlerCursesGUI::~IOHandlerCursesGUI() = default;
6536 
6537 void IOHandlerCursesGUI::Cancel() {}
6538 
6539 bool IOHandlerCursesGUI::Interrupt() { return false; }
6540 
6541 void IOHandlerCursesGUI::GotEOF() {}
6542 
6543 void IOHandlerCursesGUI::TerminalSizeChanged() {
6544   m_app_ap->TerminalSizeChanged();
6545 }
6546 
6547 #endif // LLDB_ENABLE_CURSES
6548