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