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