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