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