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