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