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