1dda28197Spatrick //===-- IOHandlerCursesGUI.cpp --------------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick
9061da546Spatrick #include "lldb/Core/IOHandlerCursesGUI.h"
10061da546Spatrick #include "lldb/Host/Config.h"
11061da546Spatrick
12061da546Spatrick #if LLDB_ENABLE_CURSES
13be691f3bSpatrick #if CURSES_HAVE_NCURSES_CURSES_H
14be691f3bSpatrick #include <ncurses/curses.h>
15be691f3bSpatrick #include <ncurses/panel.h>
16be691f3bSpatrick #else
17061da546Spatrick #include <curses.h>
18061da546Spatrick #include <panel.h>
19061da546Spatrick #endif
20be691f3bSpatrick #endif
21061da546Spatrick
22061da546Spatrick #if defined(__APPLE__)
23061da546Spatrick #include <deque>
24061da546Spatrick #endif
25061da546Spatrick #include <string>
26061da546Spatrick
27061da546Spatrick #include "lldb/Core/Debugger.h"
28061da546Spatrick #include "lldb/Core/StreamFile.h"
29be691f3bSpatrick #include "lldb/Core/ValueObjectUpdater.h"
30061da546Spatrick #include "lldb/Host/File.h"
31*f6aab3d8Srobert #include "lldb/Utility/AnsiTerminal.h"
32061da546Spatrick #include "lldb/Utility/Predicate.h"
33061da546Spatrick #include "lldb/Utility/Status.h"
34061da546Spatrick #include "lldb/Utility/StreamString.h"
35061da546Spatrick #include "lldb/Utility/StringList.h"
36061da546Spatrick #include "lldb/lldb-forward.h"
37061da546Spatrick
38061da546Spatrick #include "lldb/Interpreter/CommandCompletions.h"
39061da546Spatrick #include "lldb/Interpreter/CommandInterpreter.h"
40*f6aab3d8Srobert #include "lldb/Interpreter/OptionGroupPlatform.h"
41061da546Spatrick
42061da546Spatrick #if LLDB_ENABLE_CURSES
43061da546Spatrick #include "lldb/Breakpoint/BreakpointLocation.h"
44061da546Spatrick #include "lldb/Core/Module.h"
45be691f3bSpatrick #include "lldb/Core/PluginManager.h"
46061da546Spatrick #include "lldb/Core/ValueObject.h"
47061da546Spatrick #include "lldb/Core/ValueObjectRegister.h"
48061da546Spatrick #include "lldb/Symbol/Block.h"
49*f6aab3d8Srobert #include "lldb/Symbol/CompileUnit.h"
50061da546Spatrick #include "lldb/Symbol/Function.h"
51061da546Spatrick #include "lldb/Symbol/Symbol.h"
52061da546Spatrick #include "lldb/Symbol/VariableList.h"
53061da546Spatrick #include "lldb/Target/Process.h"
54061da546Spatrick #include "lldb/Target/RegisterContext.h"
55061da546Spatrick #include "lldb/Target/StackFrame.h"
56061da546Spatrick #include "lldb/Target/StopInfo.h"
57061da546Spatrick #include "lldb/Target/Target.h"
58061da546Spatrick #include "lldb/Target/Thread.h"
59061da546Spatrick #include "lldb/Utility/State.h"
60061da546Spatrick #endif
61061da546Spatrick
62061da546Spatrick #include "llvm/ADT/StringRef.h"
63061da546Spatrick
64061da546Spatrick #ifdef _WIN32
65061da546Spatrick #include "lldb/Host/windows/windows.h"
66061da546Spatrick #endif
67061da546Spatrick
68061da546Spatrick #include <memory>
69061da546Spatrick #include <mutex>
70061da546Spatrick
71be691f3bSpatrick #include <cassert>
72be691f3bSpatrick #include <cctype>
73be691f3bSpatrick #include <cerrno>
74be691f3bSpatrick #include <cstdint>
75be691f3bSpatrick #include <cstdio>
76be691f3bSpatrick #include <cstring>
77be691f3bSpatrick #include <functional>
78*f6aab3d8Srobert #include <optional>
79061da546Spatrick #include <type_traits>
80061da546Spatrick
81061da546Spatrick using namespace lldb;
82061da546Spatrick using namespace lldb_private;
83061da546Spatrick using llvm::StringRef;
84061da546Spatrick
85061da546Spatrick // we may want curses to be disabled for some builds for instance, windows
86061da546Spatrick #if LLDB_ENABLE_CURSES
87061da546Spatrick
88*f6aab3d8Srobert #define KEY_CTRL_A 1
89*f6aab3d8Srobert #define KEY_CTRL_E 5
90*f6aab3d8Srobert #define KEY_CTRL_K 11
91061da546Spatrick #define KEY_RETURN 10
92061da546Spatrick #define KEY_ESCAPE 27
93*f6aab3d8Srobert #define KEY_DELETE 127
94061da546Spatrick
95be691f3bSpatrick #define KEY_SHIFT_TAB (KEY_MAX + 1)
96*f6aab3d8Srobert #define KEY_ALT_ENTER (KEY_MAX + 2)
97be691f3bSpatrick
98061da546Spatrick namespace curses {
99061da546Spatrick class Menu;
100061da546Spatrick class MenuDelegate;
101061da546Spatrick class Window;
102061da546Spatrick class WindowDelegate;
103061da546Spatrick typedef std::shared_ptr<Menu> MenuSP;
104061da546Spatrick typedef std::shared_ptr<MenuDelegate> MenuDelegateSP;
105061da546Spatrick typedef std::shared_ptr<Window> WindowSP;
106061da546Spatrick typedef std::shared_ptr<WindowDelegate> WindowDelegateSP;
107061da546Spatrick typedef std::vector<MenuSP> Menus;
108061da546Spatrick typedef std::vector<WindowSP> Windows;
109061da546Spatrick typedef std::vector<WindowDelegateSP> WindowDelegates;
110061da546Spatrick
111061da546Spatrick #if 0
112061da546Spatrick type summary add -s "x=${var.x}, y=${var.y}" curses::Point
113061da546Spatrick type summary add -s "w=${var.width}, h=${var.height}" curses::Size
114061da546Spatrick type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect
115061da546Spatrick #endif
116061da546Spatrick
117061da546Spatrick struct Point {
118061da546Spatrick int x;
119061da546Spatrick int y;
120061da546Spatrick
Pointcurses::Point121061da546Spatrick Point(int _x = 0, int _y = 0) : x(_x), y(_y) {}
122061da546Spatrick
Clearcurses::Point123061da546Spatrick void Clear() {
124061da546Spatrick x = 0;
125061da546Spatrick y = 0;
126061da546Spatrick }
127061da546Spatrick
operator +=curses::Point128061da546Spatrick Point &operator+=(const Point &rhs) {
129061da546Spatrick x += rhs.x;
130061da546Spatrick y += rhs.y;
131061da546Spatrick return *this;
132061da546Spatrick }
133061da546Spatrick
Dumpcurses::Point134061da546Spatrick void Dump() { printf("(x=%i, y=%i)\n", x, y); }
135061da546Spatrick };
136061da546Spatrick
operator ==(const Point & lhs,const Point & rhs)137061da546Spatrick bool operator==(const Point &lhs, const Point &rhs) {
138061da546Spatrick return lhs.x == rhs.x && lhs.y == rhs.y;
139061da546Spatrick }
140061da546Spatrick
operator !=(const Point & lhs,const Point & rhs)141061da546Spatrick bool operator!=(const Point &lhs, const Point &rhs) {
142061da546Spatrick return lhs.x != rhs.x || lhs.y != rhs.y;
143061da546Spatrick }
144061da546Spatrick
145061da546Spatrick struct Size {
146061da546Spatrick int width;
147061da546Spatrick int height;
Sizecurses::Size148061da546Spatrick Size(int w = 0, int h = 0) : width(w), height(h) {}
149061da546Spatrick
Clearcurses::Size150061da546Spatrick void Clear() {
151061da546Spatrick width = 0;
152061da546Spatrick height = 0;
153061da546Spatrick }
154061da546Spatrick
Dumpcurses::Size155061da546Spatrick void Dump() { printf("(w=%i, h=%i)\n", width, height); }
156061da546Spatrick };
157061da546Spatrick
operator ==(const Size & lhs,const Size & rhs)158061da546Spatrick bool operator==(const Size &lhs, const Size &rhs) {
159061da546Spatrick return lhs.width == rhs.width && lhs.height == rhs.height;
160061da546Spatrick }
161061da546Spatrick
operator !=(const Size & lhs,const Size & rhs)162061da546Spatrick bool operator!=(const Size &lhs, const Size &rhs) {
163061da546Spatrick return lhs.width != rhs.width || lhs.height != rhs.height;
164061da546Spatrick }
165061da546Spatrick
166061da546Spatrick struct Rect {
167061da546Spatrick Point origin;
168061da546Spatrick Size size;
169061da546Spatrick
Rectcurses::Rect170061da546Spatrick Rect() : origin(), size() {}
171061da546Spatrick
Rectcurses::Rect172061da546Spatrick Rect(const Point &p, const Size &s) : origin(p), size(s) {}
173061da546Spatrick
Clearcurses::Rect174061da546Spatrick void Clear() {
175061da546Spatrick origin.Clear();
176061da546Spatrick size.Clear();
177061da546Spatrick }
178061da546Spatrick
Dumpcurses::Rect179061da546Spatrick void Dump() {
180061da546Spatrick printf("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width,
181061da546Spatrick size.height);
182061da546Spatrick }
183061da546Spatrick
Insetcurses::Rect184061da546Spatrick void Inset(int w, int h) {
185061da546Spatrick if (size.width > w * 2)
186061da546Spatrick size.width -= w * 2;
187061da546Spatrick origin.x += w;
188061da546Spatrick
189061da546Spatrick if (size.height > h * 2)
190061da546Spatrick size.height -= h * 2;
191061da546Spatrick origin.y += h;
192061da546Spatrick }
193061da546Spatrick
194061da546Spatrick // Return a status bar rectangle which is the last line of this rectangle.
195061da546Spatrick // This rectangle will be modified to not include the status bar area.
MakeStatusBarcurses::Rect196061da546Spatrick Rect MakeStatusBar() {
197061da546Spatrick Rect status_bar;
198061da546Spatrick if (size.height > 1) {
199061da546Spatrick status_bar.origin.x = origin.x;
200061da546Spatrick status_bar.origin.y = size.height;
201061da546Spatrick status_bar.size.width = size.width;
202061da546Spatrick status_bar.size.height = 1;
203061da546Spatrick --size.height;
204061da546Spatrick }
205061da546Spatrick return status_bar;
206061da546Spatrick }
207061da546Spatrick
208061da546Spatrick // Return a menubar rectangle which is the first line of this rectangle. This
209061da546Spatrick // rectangle will be modified to not include the menubar area.
MakeMenuBarcurses::Rect210061da546Spatrick Rect MakeMenuBar() {
211061da546Spatrick Rect menubar;
212061da546Spatrick if (size.height > 1) {
213061da546Spatrick menubar.origin.x = origin.x;
214061da546Spatrick menubar.origin.y = origin.y;
215061da546Spatrick menubar.size.width = size.width;
216061da546Spatrick menubar.size.height = 1;
217061da546Spatrick ++origin.y;
218061da546Spatrick --size.height;
219061da546Spatrick }
220061da546Spatrick return menubar;
221061da546Spatrick }
222061da546Spatrick
HorizontalSplitPercentagecurses::Rect223061da546Spatrick void HorizontalSplitPercentage(float top_percentage, Rect &top,
224061da546Spatrick Rect &bottom) const {
225061da546Spatrick float top_height = top_percentage * size.height;
226061da546Spatrick HorizontalSplit(top_height, top, bottom);
227061da546Spatrick }
228061da546Spatrick
HorizontalSplitcurses::Rect229061da546Spatrick void HorizontalSplit(int top_height, Rect &top, Rect &bottom) const {
230061da546Spatrick top = *this;
231061da546Spatrick if (top_height < size.height) {
232061da546Spatrick top.size.height = top_height;
233061da546Spatrick bottom.origin.x = origin.x;
234061da546Spatrick bottom.origin.y = origin.y + top.size.height;
235061da546Spatrick bottom.size.width = size.width;
236061da546Spatrick bottom.size.height = size.height - top.size.height;
237061da546Spatrick } else {
238061da546Spatrick bottom.Clear();
239061da546Spatrick }
240061da546Spatrick }
241061da546Spatrick
VerticalSplitPercentagecurses::Rect242061da546Spatrick void VerticalSplitPercentage(float left_percentage, Rect &left,
243061da546Spatrick Rect &right) const {
244061da546Spatrick float left_width = left_percentage * size.width;
245061da546Spatrick VerticalSplit(left_width, left, right);
246061da546Spatrick }
247061da546Spatrick
VerticalSplitcurses::Rect248061da546Spatrick void VerticalSplit(int left_width, Rect &left, Rect &right) const {
249061da546Spatrick left = *this;
250061da546Spatrick if (left_width < size.width) {
251061da546Spatrick left.size.width = left_width;
252061da546Spatrick right.origin.x = origin.x + left.size.width;
253061da546Spatrick right.origin.y = origin.y;
254061da546Spatrick right.size.width = size.width - left.size.width;
255061da546Spatrick right.size.height = size.height;
256061da546Spatrick } else {
257061da546Spatrick right.Clear();
258061da546Spatrick }
259061da546Spatrick }
260061da546Spatrick };
261061da546Spatrick
operator ==(const Rect & lhs,const Rect & rhs)262061da546Spatrick bool operator==(const Rect &lhs, const Rect &rhs) {
263061da546Spatrick return lhs.origin == rhs.origin && lhs.size == rhs.size;
264061da546Spatrick }
265061da546Spatrick
operator !=(const Rect & lhs,const Rect & rhs)266061da546Spatrick bool operator!=(const Rect &lhs, const Rect &rhs) {
267061da546Spatrick return lhs.origin != rhs.origin || lhs.size != rhs.size;
268061da546Spatrick }
269061da546Spatrick
270061da546Spatrick enum HandleCharResult {
271061da546Spatrick eKeyNotHandled = 0,
272061da546Spatrick eKeyHandled = 1,
273061da546Spatrick eQuitApplication = 2
274061da546Spatrick };
275061da546Spatrick
276061da546Spatrick enum class MenuActionResult {
277061da546Spatrick Handled,
278061da546Spatrick NotHandled,
279061da546Spatrick Quit // Exit all menus and quit
280061da546Spatrick };
281061da546Spatrick
282061da546Spatrick struct KeyHelp {
283061da546Spatrick int ch;
284061da546Spatrick const char *description;
285061da546Spatrick };
286061da546Spatrick
287be691f3bSpatrick // COLOR_PAIR index names
288be691f3bSpatrick enum {
289be691f3bSpatrick // First 16 colors are 8 black background and 8 blue background colors,
290be691f3bSpatrick // needed by OutputColoredStringTruncated().
291be691f3bSpatrick BlackOnBlack = 1,
292be691f3bSpatrick RedOnBlack,
293be691f3bSpatrick GreenOnBlack,
294be691f3bSpatrick YellowOnBlack,
295be691f3bSpatrick BlueOnBlack,
296be691f3bSpatrick MagentaOnBlack,
297be691f3bSpatrick CyanOnBlack,
298be691f3bSpatrick WhiteOnBlack,
299be691f3bSpatrick BlackOnBlue,
300be691f3bSpatrick RedOnBlue,
301be691f3bSpatrick GreenOnBlue,
302be691f3bSpatrick YellowOnBlue,
303be691f3bSpatrick BlueOnBlue,
304be691f3bSpatrick MagentaOnBlue,
305be691f3bSpatrick CyanOnBlue,
306be691f3bSpatrick WhiteOnBlue,
307be691f3bSpatrick // Other colors, as needed.
308be691f3bSpatrick BlackOnWhite,
309be691f3bSpatrick MagentaOnWhite,
310be691f3bSpatrick LastColorPairIndex = MagentaOnWhite
311be691f3bSpatrick };
312be691f3bSpatrick
313061da546Spatrick class WindowDelegate {
314061da546Spatrick public:
315061da546Spatrick virtual ~WindowDelegate() = default;
316061da546Spatrick
WindowDelegateDraw(Window & window,bool force)317061da546Spatrick virtual bool WindowDelegateDraw(Window &window, bool force) {
318061da546Spatrick return false; // Drawing not handled
319061da546Spatrick }
320061da546Spatrick
WindowDelegateHandleChar(Window & window,int key)321061da546Spatrick virtual HandleCharResult WindowDelegateHandleChar(Window &window, int key) {
322061da546Spatrick return eKeyNotHandled;
323061da546Spatrick }
324061da546Spatrick
WindowDelegateGetHelpText()325061da546Spatrick virtual const char *WindowDelegateGetHelpText() { return nullptr; }
326061da546Spatrick
WindowDelegateGetKeyHelp()327061da546Spatrick virtual KeyHelp *WindowDelegateGetKeyHelp() { return nullptr; }
328061da546Spatrick };
329061da546Spatrick
330061da546Spatrick class HelpDialogDelegate : public WindowDelegate {
331061da546Spatrick public:
332061da546Spatrick HelpDialogDelegate(const char *text, KeyHelp *key_help_array);
333061da546Spatrick
334061da546Spatrick ~HelpDialogDelegate() override;
335061da546Spatrick
336061da546Spatrick bool WindowDelegateDraw(Window &window, bool force) override;
337061da546Spatrick
338061da546Spatrick HandleCharResult WindowDelegateHandleChar(Window &window, int key) override;
339061da546Spatrick
GetNumLines() const340061da546Spatrick size_t GetNumLines() const { return m_text.GetSize(); }
341061da546Spatrick
GetMaxLineLength() const342061da546Spatrick size_t GetMaxLineLength() const { return m_text.GetMaxStringLength(); }
343061da546Spatrick
344061da546Spatrick protected:
345061da546Spatrick StringList m_text;
346*f6aab3d8Srobert int m_first_visible_line = 0;
347061da546Spatrick };
348061da546Spatrick
349be691f3bSpatrick // A surface is an abstraction for something than can be drawn on. The surface
350be691f3bSpatrick // have a width, a height, a cursor position, and a multitude of drawing
351be691f3bSpatrick // operations. This type should be sub-classed to get an actually useful ncurses
352*f6aab3d8Srobert // object, such as a Window or a Pad.
353be691f3bSpatrick class Surface {
354be691f3bSpatrick public:
355*f6aab3d8Srobert enum class Type { Window, Pad };
356*f6aab3d8Srobert
Surface(Surface::Type type)357*f6aab3d8Srobert Surface(Surface::Type type) : m_type(type) {}
358be691f3bSpatrick
get()359be691f3bSpatrick WINDOW *get() { return m_window; }
360be691f3bSpatrick
operator WINDOW*()361be691f3bSpatrick operator WINDOW *() { return m_window; }
362be691f3bSpatrick
SubSurface(Rect bounds)363*f6aab3d8Srobert Surface SubSurface(Rect bounds) {
364*f6aab3d8Srobert Surface subSurface(m_type);
365*f6aab3d8Srobert if (m_type == Type::Pad)
366*f6aab3d8Srobert subSurface.m_window =
367*f6aab3d8Srobert ::subpad(m_window, bounds.size.height, bounds.size.width,
368*f6aab3d8Srobert bounds.origin.y, bounds.origin.x);
369*f6aab3d8Srobert else
370*f6aab3d8Srobert subSurface.m_window =
371*f6aab3d8Srobert ::derwin(m_window, bounds.size.height, bounds.size.width,
372*f6aab3d8Srobert bounds.origin.y, bounds.origin.x);
373*f6aab3d8Srobert return subSurface;
374*f6aab3d8Srobert }
375*f6aab3d8Srobert
376be691f3bSpatrick // Copy a region of the surface to another surface.
CopyToSurface(Surface & target,Point source_origin,Point target_origin,Size size)377be691f3bSpatrick void CopyToSurface(Surface &target, Point source_origin, Point target_origin,
378be691f3bSpatrick Size size) {
379be691f3bSpatrick ::copywin(m_window, target.get(), source_origin.y, source_origin.x,
380be691f3bSpatrick target_origin.y, target_origin.x,
381be691f3bSpatrick target_origin.y + size.height - 1,
382be691f3bSpatrick target_origin.x + size.width - 1, false);
383be691f3bSpatrick }
384be691f3bSpatrick
GetCursorX() const385be691f3bSpatrick int GetCursorX() const { return getcurx(m_window); }
GetCursorY() const386be691f3bSpatrick int GetCursorY() const { return getcury(m_window); }
MoveCursor(int x,int y)387be691f3bSpatrick void MoveCursor(int x, int y) { ::wmove(m_window, y, x); }
388be691f3bSpatrick
AttributeOn(attr_t attr)389be691f3bSpatrick void AttributeOn(attr_t attr) { ::wattron(m_window, attr); }
AttributeOff(attr_t attr)390be691f3bSpatrick void AttributeOff(attr_t attr) { ::wattroff(m_window, attr); }
391be691f3bSpatrick
GetMaxX() const392be691f3bSpatrick int GetMaxX() const { return getmaxx(m_window); }
GetMaxY() const393be691f3bSpatrick int GetMaxY() const { return getmaxy(m_window); }
GetWidth() const394be691f3bSpatrick int GetWidth() const { return GetMaxX(); }
GetHeight() const395be691f3bSpatrick int GetHeight() const { return GetMaxY(); }
GetSize() const396be691f3bSpatrick Size GetSize() const { return Size(GetWidth(), GetHeight()); }
397be691f3bSpatrick // Get a zero origin rectangle width the surface size.
GetFrame() const398be691f3bSpatrick Rect GetFrame() const { return Rect(Point(), GetSize()); }
399be691f3bSpatrick
Clear()400be691f3bSpatrick void Clear() { ::wclear(m_window); }
Erase()401be691f3bSpatrick void Erase() { ::werase(m_window); }
402be691f3bSpatrick
SetBackground(int color_pair_idx)403be691f3bSpatrick void SetBackground(int color_pair_idx) {
404be691f3bSpatrick ::wbkgd(m_window, COLOR_PAIR(color_pair_idx));
405be691f3bSpatrick }
406be691f3bSpatrick
PutChar(int ch)407be691f3bSpatrick void PutChar(int ch) { ::waddch(m_window, ch); }
PutCString(const char * s,int len=-1)408be691f3bSpatrick void PutCString(const char *s, int len = -1) { ::waddnstr(m_window, s, len); }
409be691f3bSpatrick
PutCStringTruncated(int right_pad,const char * s,int len=-1)410be691f3bSpatrick void PutCStringTruncated(int right_pad, const char *s, int len = -1) {
411be691f3bSpatrick int bytes_left = GetWidth() - GetCursorX();
412be691f3bSpatrick if (bytes_left > right_pad) {
413be691f3bSpatrick bytes_left -= right_pad;
414be691f3bSpatrick ::waddnstr(m_window, s, len < 0 ? bytes_left : std::min(bytes_left, len));
415be691f3bSpatrick }
416be691f3bSpatrick }
417be691f3bSpatrick
Printf(const char * format,...)418be691f3bSpatrick void Printf(const char *format, ...) __attribute__((format(printf, 2, 3))) {
419be691f3bSpatrick va_list args;
420be691f3bSpatrick va_start(args, format);
421be691f3bSpatrick vw_printw(m_window, format, args);
422be691f3bSpatrick va_end(args);
423be691f3bSpatrick }
424be691f3bSpatrick
PrintfTruncated(int right_pad,const char * format,...)425be691f3bSpatrick void PrintfTruncated(int right_pad, const char *format, ...)
426be691f3bSpatrick __attribute__((format(printf, 3, 4))) {
427be691f3bSpatrick va_list args;
428be691f3bSpatrick va_start(args, format);
429be691f3bSpatrick StreamString strm;
430be691f3bSpatrick strm.PrintfVarArg(format, args);
431be691f3bSpatrick va_end(args);
432be691f3bSpatrick PutCStringTruncated(right_pad, strm.GetData());
433be691f3bSpatrick }
434be691f3bSpatrick
VerticalLine(int n,chtype v_char=ACS_VLINE)435be691f3bSpatrick void VerticalLine(int n, chtype v_char = ACS_VLINE) {
436be691f3bSpatrick ::wvline(m_window, v_char, n);
437be691f3bSpatrick }
HorizontalLine(int n,chtype h_char=ACS_HLINE)438be691f3bSpatrick void HorizontalLine(int n, chtype h_char = ACS_HLINE) {
439be691f3bSpatrick ::whline(m_window, h_char, n);
440be691f3bSpatrick }
Box(chtype v_char=ACS_VLINE,chtype h_char=ACS_HLINE)441be691f3bSpatrick void Box(chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) {
442be691f3bSpatrick ::box(m_window, v_char, h_char);
443be691f3bSpatrick }
444be691f3bSpatrick
TitledBox(const char * title,chtype v_char=ACS_VLINE,chtype h_char=ACS_HLINE)445be691f3bSpatrick void TitledBox(const char *title, chtype v_char = ACS_VLINE,
446be691f3bSpatrick chtype h_char = ACS_HLINE) {
447be691f3bSpatrick Box(v_char, h_char);
448be691f3bSpatrick int title_offset = 2;
449be691f3bSpatrick MoveCursor(title_offset, 0);
450be691f3bSpatrick PutChar('[');
451be691f3bSpatrick PutCString(title, GetWidth() - title_offset);
452be691f3bSpatrick PutChar(']');
453be691f3bSpatrick }
454be691f3bSpatrick
Box(const Rect & bounds,chtype v_char=ACS_VLINE,chtype h_char=ACS_HLINE)455be691f3bSpatrick void Box(const Rect &bounds, chtype v_char = ACS_VLINE,
456be691f3bSpatrick chtype h_char = ACS_HLINE) {
457be691f3bSpatrick MoveCursor(bounds.origin.x, bounds.origin.y);
458be691f3bSpatrick VerticalLine(bounds.size.height);
459be691f3bSpatrick HorizontalLine(bounds.size.width);
460be691f3bSpatrick PutChar(ACS_ULCORNER);
461be691f3bSpatrick
462be691f3bSpatrick MoveCursor(bounds.origin.x + bounds.size.width - 1, bounds.origin.y);
463be691f3bSpatrick VerticalLine(bounds.size.height);
464be691f3bSpatrick PutChar(ACS_URCORNER);
465be691f3bSpatrick
466be691f3bSpatrick MoveCursor(bounds.origin.x, bounds.origin.y + bounds.size.height - 1);
467be691f3bSpatrick HorizontalLine(bounds.size.width);
468be691f3bSpatrick PutChar(ACS_LLCORNER);
469be691f3bSpatrick
470be691f3bSpatrick MoveCursor(bounds.origin.x + bounds.size.width - 1,
471be691f3bSpatrick bounds.origin.y + bounds.size.height - 1);
472be691f3bSpatrick PutChar(ACS_LRCORNER);
473be691f3bSpatrick }
474be691f3bSpatrick
TitledBox(const Rect & bounds,const char * title,chtype v_char=ACS_VLINE,chtype h_char=ACS_HLINE)475be691f3bSpatrick void TitledBox(const Rect &bounds, const char *title,
476be691f3bSpatrick chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) {
477be691f3bSpatrick Box(bounds, v_char, h_char);
478be691f3bSpatrick int title_offset = 2;
479be691f3bSpatrick MoveCursor(bounds.origin.x + title_offset, bounds.origin.y);
480be691f3bSpatrick PutChar('[');
481be691f3bSpatrick PutCString(title, bounds.size.width - title_offset);
482be691f3bSpatrick PutChar(']');
483be691f3bSpatrick }
484be691f3bSpatrick
485be691f3bSpatrick // Curses doesn't allow direct output of color escape sequences, but that's
486be691f3bSpatrick // how we get source lines from the Highligher class. Read the line and
487be691f3bSpatrick // convert color escape sequences to curses color attributes. Use
488be691f3bSpatrick // first_skip_count to skip leading visible characters. Returns false if all
489be691f3bSpatrick // visible characters were skipped due to first_skip_count.
OutputColoredStringTruncated(int right_pad,StringRef string,size_t skip_first_count,bool use_blue_background)490be691f3bSpatrick bool OutputColoredStringTruncated(int right_pad, StringRef string,
491be691f3bSpatrick size_t skip_first_count,
492be691f3bSpatrick bool use_blue_background) {
493be691f3bSpatrick attr_t saved_attr;
494be691f3bSpatrick short saved_pair;
495be691f3bSpatrick bool result = false;
496be691f3bSpatrick wattr_get(m_window, &saved_attr, &saved_pair, nullptr);
497be691f3bSpatrick if (use_blue_background)
498be691f3bSpatrick ::wattron(m_window, COLOR_PAIR(WhiteOnBlue));
499be691f3bSpatrick while (!string.empty()) {
500*f6aab3d8Srobert size_t esc_pos = string.find(ANSI_ESC_START);
501be691f3bSpatrick if (esc_pos == StringRef::npos) {
502be691f3bSpatrick string = string.substr(skip_first_count);
503be691f3bSpatrick if (!string.empty()) {
504be691f3bSpatrick PutCStringTruncated(right_pad, string.data(), string.size());
505be691f3bSpatrick result = true;
506be691f3bSpatrick }
507be691f3bSpatrick break;
508be691f3bSpatrick }
509be691f3bSpatrick if (esc_pos > 0) {
510be691f3bSpatrick if (skip_first_count > 0) {
511be691f3bSpatrick int skip = std::min(esc_pos, skip_first_count);
512be691f3bSpatrick string = string.substr(skip);
513be691f3bSpatrick skip_first_count -= skip;
514be691f3bSpatrick esc_pos -= skip;
515be691f3bSpatrick }
516be691f3bSpatrick if (esc_pos > 0) {
517be691f3bSpatrick PutCStringTruncated(right_pad, string.data(), esc_pos);
518be691f3bSpatrick result = true;
519be691f3bSpatrick string = string.drop_front(esc_pos);
520be691f3bSpatrick }
521be691f3bSpatrick }
522*f6aab3d8Srobert bool consumed = string.consume_front(ANSI_ESC_START);
523be691f3bSpatrick assert(consumed);
524be691f3bSpatrick UNUSED_IF_ASSERT_DISABLED(consumed);
525be691f3bSpatrick // This is written to match our Highlighter classes, which seem to
526be691f3bSpatrick // generate only foreground color escape sequences. If necessary, this
527be691f3bSpatrick // will need to be extended.
528*f6aab3d8Srobert // Only 8 basic foreground colors, underline and reset, our Highlighter
529*f6aab3d8Srobert // doesn't use anything else.
530be691f3bSpatrick int value;
531be691f3bSpatrick if (!!string.consumeInteger(10, value) || // Returns false on success.
532*f6aab3d8Srobert !(value == 0 || value == ANSI_CTRL_UNDERLINE ||
533*f6aab3d8Srobert (value >= ANSI_FG_COLOR_BLACK && value <= ANSI_FG_COLOR_WHITE))) {
534be691f3bSpatrick llvm::errs() << "No valid color code in color escape sequence.\n";
535be691f3bSpatrick continue;
536be691f3bSpatrick }
537*f6aab3d8Srobert if (!string.consume_front(ANSI_ESC_END)) {
538*f6aab3d8Srobert llvm::errs() << "Missing '" << ANSI_ESC_END
539*f6aab3d8Srobert << "' in color escape sequence.\n";
540be691f3bSpatrick continue;
541be691f3bSpatrick }
542be691f3bSpatrick if (value == 0) { // Reset.
543be691f3bSpatrick wattr_set(m_window, saved_attr, saved_pair, nullptr);
544be691f3bSpatrick if (use_blue_background)
545be691f3bSpatrick ::wattron(m_window, COLOR_PAIR(WhiteOnBlue));
546*f6aab3d8Srobert } else if (value == ANSI_CTRL_UNDERLINE) {
547*f6aab3d8Srobert ::wattron(m_window, A_UNDERLINE);
548be691f3bSpatrick } else {
549be691f3bSpatrick // Mapped directly to first 16 color pairs (black/blue background).
550*f6aab3d8Srobert ::wattron(m_window, COLOR_PAIR(value - ANSI_FG_COLOR_BLACK + 1 +
551*f6aab3d8Srobert (use_blue_background ? 8 : 0)));
552be691f3bSpatrick }
553be691f3bSpatrick }
554be691f3bSpatrick wattr_set(m_window, saved_attr, saved_pair, nullptr);
555be691f3bSpatrick return result;
556be691f3bSpatrick }
557be691f3bSpatrick
558be691f3bSpatrick protected:
559*f6aab3d8Srobert Type m_type;
560*f6aab3d8Srobert WINDOW *m_window = nullptr;
561be691f3bSpatrick };
562be691f3bSpatrick
563be691f3bSpatrick class Pad : public Surface {
564be691f3bSpatrick public:
Pad(Size size)565*f6aab3d8Srobert Pad(Size size) : Surface(Surface::Type::Pad) {
566*f6aab3d8Srobert m_window = ::newpad(size.height, size.width);
567*f6aab3d8Srobert }
568be691f3bSpatrick
~Pad()569be691f3bSpatrick ~Pad() { ::delwin(m_window); }
570be691f3bSpatrick };
571be691f3bSpatrick
572be691f3bSpatrick class Window : public Surface {
573061da546Spatrick public:
Window(const char * name)574061da546Spatrick Window(const char *name)
575*f6aab3d8Srobert : Surface(Surface::Type::Window), m_name(name), m_panel(nullptr),
576*f6aab3d8Srobert m_parent(nullptr), m_subwindows(), m_delegate_sp(),
577*f6aab3d8Srobert m_curr_active_window_idx(UINT32_MAX),
578061da546Spatrick m_prev_active_window_idx(UINT32_MAX), m_delete(false),
579061da546Spatrick m_needs_update(true), m_can_activate(true), m_is_subwin(false) {}
580061da546Spatrick
Window(const char * name,WINDOW * w,bool del=true)581061da546Spatrick Window(const char *name, WINDOW *w, bool del = true)
582*f6aab3d8Srobert : Surface(Surface::Type::Window), m_name(name), m_panel(nullptr),
583*f6aab3d8Srobert m_parent(nullptr), m_subwindows(), m_delegate_sp(),
584*f6aab3d8Srobert m_curr_active_window_idx(UINT32_MAX),
585061da546Spatrick m_prev_active_window_idx(UINT32_MAX), m_delete(del),
586061da546Spatrick m_needs_update(true), m_can_activate(true), m_is_subwin(false) {
587061da546Spatrick if (w)
588061da546Spatrick Reset(w);
589061da546Spatrick }
590061da546Spatrick
Window(const char * name,const Rect & bounds)591061da546Spatrick Window(const char *name, const Rect &bounds)
592*f6aab3d8Srobert : Surface(Surface::Type::Window), m_name(name), m_panel(nullptr),
593*f6aab3d8Srobert m_parent(nullptr), m_subwindows(), m_delegate_sp(),
594be691f3bSpatrick m_curr_active_window_idx(UINT32_MAX),
595*f6aab3d8Srobert m_prev_active_window_idx(UINT32_MAX), m_delete(false),
596061da546Spatrick m_needs_update(true), m_can_activate(true), m_is_subwin(false) {
597061da546Spatrick Reset(::newwin(bounds.size.height, bounds.size.width, bounds.origin.y,
598061da546Spatrick bounds.origin.y));
599061da546Spatrick }
600061da546Spatrick
~Window()601061da546Spatrick virtual ~Window() {
602061da546Spatrick RemoveSubWindows();
603061da546Spatrick Reset();
604061da546Spatrick }
605061da546Spatrick
Reset(WINDOW * w=nullptr,bool del=true)606061da546Spatrick void Reset(WINDOW *w = nullptr, bool del = true) {
607061da546Spatrick if (m_window == w)
608061da546Spatrick return;
609061da546Spatrick
610061da546Spatrick if (m_panel) {
611061da546Spatrick ::del_panel(m_panel);
612061da546Spatrick m_panel = nullptr;
613061da546Spatrick }
614061da546Spatrick if (m_window && m_delete) {
615061da546Spatrick ::delwin(m_window);
616061da546Spatrick m_window = nullptr;
617061da546Spatrick m_delete = false;
618061da546Spatrick }
619061da546Spatrick if (w) {
620061da546Spatrick m_window = w;
621061da546Spatrick m_panel = ::new_panel(m_window);
622061da546Spatrick m_delete = del;
623061da546Spatrick }
624061da546Spatrick }
625061da546Spatrick
626be691f3bSpatrick // Get the rectangle in our parent window
GetBounds() const627be691f3bSpatrick Rect GetBounds() const { return Rect(GetParentOrigin(), GetSize()); }
628be691f3bSpatrick
GetCenteredRect(int width,int height)629be691f3bSpatrick Rect GetCenteredRect(int width, int height) {
630be691f3bSpatrick Size size = GetSize();
631be691f3bSpatrick width = std::min(size.width, width);
632be691f3bSpatrick height = std::min(size.height, height);
633be691f3bSpatrick int x = (size.width - width) / 2;
634be691f3bSpatrick int y = (size.height - height) / 2;
635be691f3bSpatrick return Rect(Point(x, y), Size(width, height));
636061da546Spatrick }
637be691f3bSpatrick
GetChar()638061da546Spatrick int GetChar() { return ::wgetch(m_window); }
GetParentOrigin() const639be691f3bSpatrick Point GetParentOrigin() const { return Point(GetParentX(), GetParentY()); }
GetParentX() const640be691f3bSpatrick int GetParentX() const { return getparx(m_window); }
GetParentY() const641be691f3bSpatrick int GetParentY() const { return getpary(m_window); }
MoveWindow(int x,int y)642061da546Spatrick void MoveWindow(int x, int y) { MoveWindow(Point(x, y)); }
Resize(int w,int h)643061da546Spatrick void Resize(int w, int h) { ::wresize(m_window, h, w); }
Resize(const Size & size)644061da546Spatrick void Resize(const Size &size) {
645061da546Spatrick ::wresize(m_window, size.height, size.width);
646061da546Spatrick }
MoveWindow(const Point & origin)647061da546Spatrick void MoveWindow(const Point &origin) {
648061da546Spatrick const bool moving_window = origin != GetParentOrigin();
649061da546Spatrick if (m_is_subwin && moving_window) {
650061da546Spatrick // Can't move subwindows, must delete and re-create
651061da546Spatrick Size size = GetSize();
652061da546Spatrick Reset(::subwin(m_parent->m_window, size.height, size.width, origin.y,
653061da546Spatrick origin.x),
654061da546Spatrick true);
655061da546Spatrick } else {
656061da546Spatrick ::mvwin(m_window, origin.y, origin.x);
657061da546Spatrick }
658061da546Spatrick }
659061da546Spatrick
SetBounds(const Rect & bounds)660061da546Spatrick void SetBounds(const Rect &bounds) {
661061da546Spatrick const bool moving_window = bounds.origin != GetParentOrigin();
662061da546Spatrick if (m_is_subwin && moving_window) {
663061da546Spatrick // Can't move subwindows, must delete and re-create
664061da546Spatrick Reset(::subwin(m_parent->m_window, bounds.size.height, bounds.size.width,
665061da546Spatrick bounds.origin.y, bounds.origin.x),
666061da546Spatrick true);
667061da546Spatrick } else {
668061da546Spatrick if (moving_window)
669061da546Spatrick MoveWindow(bounds.origin);
670061da546Spatrick Resize(bounds.size);
671061da546Spatrick }
672061da546Spatrick }
673061da546Spatrick
Touch()674061da546Spatrick void Touch() {
675061da546Spatrick ::touchwin(m_window);
676061da546Spatrick if (m_parent)
677061da546Spatrick m_parent->Touch();
678061da546Spatrick }
679061da546Spatrick
CreateSubWindow(const char * name,const Rect & bounds,bool make_active)680061da546Spatrick WindowSP CreateSubWindow(const char *name, const Rect &bounds,
681061da546Spatrick bool make_active) {
682061da546Spatrick auto get_window = [this, &bounds]() {
683061da546Spatrick return m_window
684061da546Spatrick ? ::subwin(m_window, bounds.size.height, bounds.size.width,
685061da546Spatrick bounds.origin.y, bounds.origin.x)
686061da546Spatrick : ::newwin(bounds.size.height, bounds.size.width,
687061da546Spatrick bounds.origin.y, bounds.origin.x);
688061da546Spatrick };
689061da546Spatrick WindowSP subwindow_sp = std::make_shared<Window>(name, get_window(), true);
690061da546Spatrick subwindow_sp->m_is_subwin = subwindow_sp.operator bool();
691061da546Spatrick subwindow_sp->m_parent = this;
692061da546Spatrick if (make_active) {
693061da546Spatrick m_prev_active_window_idx = m_curr_active_window_idx;
694061da546Spatrick m_curr_active_window_idx = m_subwindows.size();
695061da546Spatrick }
696061da546Spatrick m_subwindows.push_back(subwindow_sp);
697061da546Spatrick ::top_panel(subwindow_sp->m_panel);
698061da546Spatrick m_needs_update = true;
699061da546Spatrick return subwindow_sp;
700061da546Spatrick }
701061da546Spatrick
RemoveSubWindow(Window * window)702061da546Spatrick bool RemoveSubWindow(Window *window) {
703061da546Spatrick Windows::iterator pos, end = m_subwindows.end();
704061da546Spatrick size_t i = 0;
705061da546Spatrick for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) {
706061da546Spatrick if ((*pos).get() == window) {
707061da546Spatrick if (m_prev_active_window_idx == i)
708061da546Spatrick m_prev_active_window_idx = UINT32_MAX;
709061da546Spatrick else if (m_prev_active_window_idx != UINT32_MAX &&
710061da546Spatrick m_prev_active_window_idx > i)
711061da546Spatrick --m_prev_active_window_idx;
712061da546Spatrick
713061da546Spatrick if (m_curr_active_window_idx == i)
714061da546Spatrick m_curr_active_window_idx = UINT32_MAX;
715061da546Spatrick else if (m_curr_active_window_idx != UINT32_MAX &&
716061da546Spatrick m_curr_active_window_idx > i)
717061da546Spatrick --m_curr_active_window_idx;
718061da546Spatrick window->Erase();
719061da546Spatrick m_subwindows.erase(pos);
720061da546Spatrick m_needs_update = true;
721061da546Spatrick if (m_parent)
722061da546Spatrick m_parent->Touch();
723061da546Spatrick else
724061da546Spatrick ::touchwin(stdscr);
725061da546Spatrick return true;
726061da546Spatrick }
727061da546Spatrick }
728061da546Spatrick return false;
729061da546Spatrick }
730061da546Spatrick
FindSubWindow(const char * name)731061da546Spatrick WindowSP FindSubWindow(const char *name) {
732061da546Spatrick Windows::iterator pos, end = m_subwindows.end();
733061da546Spatrick size_t i = 0;
734061da546Spatrick for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) {
735061da546Spatrick if ((*pos)->m_name == name)
736061da546Spatrick return *pos;
737061da546Spatrick }
738061da546Spatrick return WindowSP();
739061da546Spatrick }
740061da546Spatrick
RemoveSubWindows()741061da546Spatrick void RemoveSubWindows() {
742061da546Spatrick m_curr_active_window_idx = UINT32_MAX;
743061da546Spatrick m_prev_active_window_idx = UINT32_MAX;
744061da546Spatrick for (Windows::iterator pos = m_subwindows.begin();
745061da546Spatrick pos != m_subwindows.end(); pos = m_subwindows.erase(pos)) {
746061da546Spatrick (*pos)->Erase();
747061da546Spatrick }
748061da546Spatrick if (m_parent)
749061da546Spatrick m_parent->Touch();
750061da546Spatrick else
751061da546Spatrick ::touchwin(stdscr);
752061da546Spatrick }
753061da546Spatrick
754061da546Spatrick // Window drawing utilities
DrawTitleBox(const char * title,const char * bottom_message=nullptr)755061da546Spatrick void DrawTitleBox(const char *title, const char *bottom_message = nullptr) {
756061da546Spatrick attr_t attr = 0;
757061da546Spatrick if (IsActive())
758be691f3bSpatrick attr = A_BOLD | COLOR_PAIR(BlackOnWhite);
759061da546Spatrick else
760061da546Spatrick attr = 0;
761061da546Spatrick if (attr)
762061da546Spatrick AttributeOn(attr);
763061da546Spatrick
764061da546Spatrick Box();
765061da546Spatrick MoveCursor(3, 0);
766061da546Spatrick
767061da546Spatrick if (title && title[0]) {
768061da546Spatrick PutChar('<');
769061da546Spatrick PutCString(title);
770061da546Spatrick PutChar('>');
771061da546Spatrick }
772061da546Spatrick
773061da546Spatrick if (bottom_message && bottom_message[0]) {
774061da546Spatrick int bottom_message_length = strlen(bottom_message);
775061da546Spatrick int x = GetWidth() - 3 - (bottom_message_length + 2);
776061da546Spatrick
777061da546Spatrick if (x > 0) {
778061da546Spatrick MoveCursor(x, GetHeight() - 1);
779061da546Spatrick PutChar('[');
780061da546Spatrick PutCString(bottom_message);
781061da546Spatrick PutChar(']');
782061da546Spatrick } else {
783061da546Spatrick MoveCursor(1, GetHeight() - 1);
784061da546Spatrick PutChar('[');
785be691f3bSpatrick PutCStringTruncated(1, bottom_message);
786061da546Spatrick }
787061da546Spatrick }
788061da546Spatrick if (attr)
789061da546Spatrick AttributeOff(attr);
790061da546Spatrick }
791061da546Spatrick
Draw(bool force)792061da546Spatrick virtual void Draw(bool force) {
793061da546Spatrick if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw(*this, force))
794061da546Spatrick return;
795061da546Spatrick
796061da546Spatrick for (auto &subwindow_sp : m_subwindows)
797061da546Spatrick subwindow_sp->Draw(force);
798061da546Spatrick }
799061da546Spatrick
CreateHelpSubwindow()800061da546Spatrick bool CreateHelpSubwindow() {
801061da546Spatrick if (m_delegate_sp) {
802061da546Spatrick const char *text = m_delegate_sp->WindowDelegateGetHelpText();
803061da546Spatrick KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp();
804061da546Spatrick if ((text && text[0]) || key_help) {
805061da546Spatrick std::unique_ptr<HelpDialogDelegate> help_delegate_up(
806061da546Spatrick new HelpDialogDelegate(text, key_help));
807061da546Spatrick const size_t num_lines = help_delegate_up->GetNumLines();
808061da546Spatrick const size_t max_length = help_delegate_up->GetMaxLineLength();
809061da546Spatrick Rect bounds = GetBounds();
810061da546Spatrick bounds.Inset(1, 1);
811061da546Spatrick if (max_length + 4 < static_cast<size_t>(bounds.size.width)) {
812061da546Spatrick bounds.origin.x += (bounds.size.width - max_length + 4) / 2;
813061da546Spatrick bounds.size.width = max_length + 4;
814061da546Spatrick } else {
815061da546Spatrick if (bounds.size.width > 100) {
816061da546Spatrick const int inset_w = bounds.size.width / 4;
817061da546Spatrick bounds.origin.x += inset_w;
818061da546Spatrick bounds.size.width -= 2 * inset_w;
819061da546Spatrick }
820061da546Spatrick }
821061da546Spatrick
822061da546Spatrick if (num_lines + 2 < static_cast<size_t>(bounds.size.height)) {
823061da546Spatrick bounds.origin.y += (bounds.size.height - num_lines + 2) / 2;
824061da546Spatrick bounds.size.height = num_lines + 2;
825061da546Spatrick } else {
826061da546Spatrick if (bounds.size.height > 100) {
827061da546Spatrick const int inset_h = bounds.size.height / 4;
828061da546Spatrick bounds.origin.y += inset_h;
829061da546Spatrick bounds.size.height -= 2 * inset_h;
830061da546Spatrick }
831061da546Spatrick }
832061da546Spatrick WindowSP help_window_sp;
833061da546Spatrick Window *parent_window = GetParent();
834061da546Spatrick if (parent_window)
835061da546Spatrick help_window_sp = parent_window->CreateSubWindow("Help", bounds, true);
836061da546Spatrick else
837061da546Spatrick help_window_sp = CreateSubWindow("Help", bounds, true);
838061da546Spatrick help_window_sp->SetDelegate(
839061da546Spatrick WindowDelegateSP(help_delegate_up.release()));
840061da546Spatrick return true;
841061da546Spatrick }
842061da546Spatrick }
843061da546Spatrick return false;
844061da546Spatrick }
845061da546Spatrick
HandleChar(int key)846061da546Spatrick virtual HandleCharResult HandleChar(int key) {
847061da546Spatrick // Always check the active window first
848061da546Spatrick HandleCharResult result = eKeyNotHandled;
849061da546Spatrick WindowSP active_window_sp = GetActiveWindow();
850061da546Spatrick if (active_window_sp) {
851061da546Spatrick result = active_window_sp->HandleChar(key);
852061da546Spatrick if (result != eKeyNotHandled)
853061da546Spatrick return result;
854061da546Spatrick }
855061da546Spatrick
856061da546Spatrick if (m_delegate_sp) {
857061da546Spatrick result = m_delegate_sp->WindowDelegateHandleChar(*this, key);
858061da546Spatrick if (result != eKeyNotHandled)
859061da546Spatrick return result;
860061da546Spatrick }
861061da546Spatrick
862061da546Spatrick // Then check for any windows that want any keys that weren't handled. This
863061da546Spatrick // is typically only for a menubar. Make a copy of the subwindows in case
864061da546Spatrick // any HandleChar() functions muck with the subwindows. If we don't do
865061da546Spatrick // this, we can crash when iterating over the subwindows.
866061da546Spatrick Windows subwindows(m_subwindows);
867061da546Spatrick for (auto subwindow_sp : subwindows) {
868061da546Spatrick if (!subwindow_sp->m_can_activate) {
869061da546Spatrick HandleCharResult result = subwindow_sp->HandleChar(key);
870061da546Spatrick if (result != eKeyNotHandled)
871061da546Spatrick return result;
872061da546Spatrick }
873061da546Spatrick }
874061da546Spatrick
875061da546Spatrick return eKeyNotHandled;
876061da546Spatrick }
877061da546Spatrick
GetActiveWindow()878061da546Spatrick WindowSP GetActiveWindow() {
879061da546Spatrick if (!m_subwindows.empty()) {
880061da546Spatrick if (m_curr_active_window_idx >= m_subwindows.size()) {
881061da546Spatrick if (m_prev_active_window_idx < m_subwindows.size()) {
882061da546Spatrick m_curr_active_window_idx = m_prev_active_window_idx;
883061da546Spatrick m_prev_active_window_idx = UINT32_MAX;
884061da546Spatrick } else if (IsActive()) {
885061da546Spatrick m_prev_active_window_idx = UINT32_MAX;
886061da546Spatrick m_curr_active_window_idx = UINT32_MAX;
887061da546Spatrick
888061da546Spatrick // Find first window that wants to be active if this window is active
889061da546Spatrick const size_t num_subwindows = m_subwindows.size();
890061da546Spatrick for (size_t i = 0; i < num_subwindows; ++i) {
891061da546Spatrick if (m_subwindows[i]->GetCanBeActive()) {
892061da546Spatrick m_curr_active_window_idx = i;
893061da546Spatrick break;
894061da546Spatrick }
895061da546Spatrick }
896061da546Spatrick }
897061da546Spatrick }
898061da546Spatrick
899061da546Spatrick if (m_curr_active_window_idx < m_subwindows.size())
900061da546Spatrick return m_subwindows[m_curr_active_window_idx];
901061da546Spatrick }
902061da546Spatrick return WindowSP();
903061da546Spatrick }
904061da546Spatrick
GetCanBeActive() const905061da546Spatrick bool GetCanBeActive() const { return m_can_activate; }
906061da546Spatrick
SetCanBeActive(bool b)907061da546Spatrick void SetCanBeActive(bool b) { m_can_activate = b; }
908061da546Spatrick
SetDelegate(const WindowDelegateSP & delegate_sp)909061da546Spatrick void SetDelegate(const WindowDelegateSP &delegate_sp) {
910061da546Spatrick m_delegate_sp = delegate_sp;
911061da546Spatrick }
912061da546Spatrick
GetParent() const913061da546Spatrick Window *GetParent() const { return m_parent; }
914061da546Spatrick
IsActive() const915061da546Spatrick bool IsActive() const {
916061da546Spatrick if (m_parent)
917061da546Spatrick return m_parent->GetActiveWindow().get() == this;
918061da546Spatrick else
919061da546Spatrick return true; // Top level window is always active
920061da546Spatrick }
921061da546Spatrick
SelectNextWindowAsActive()922061da546Spatrick void SelectNextWindowAsActive() {
923061da546Spatrick // Move active focus to next window
924be691f3bSpatrick const int num_subwindows = m_subwindows.size();
925be691f3bSpatrick int start_idx = 0;
926be691f3bSpatrick if (m_curr_active_window_idx != UINT32_MAX) {
927061da546Spatrick m_prev_active_window_idx = m_curr_active_window_idx;
928be691f3bSpatrick start_idx = m_curr_active_window_idx + 1;
929be691f3bSpatrick }
930be691f3bSpatrick for (int idx = start_idx; idx < num_subwindows; ++idx) {
931061da546Spatrick if (m_subwindows[idx]->GetCanBeActive()) {
932061da546Spatrick m_curr_active_window_idx = idx;
933be691f3bSpatrick return;
934061da546Spatrick }
935061da546Spatrick }
936be691f3bSpatrick for (int idx = 0; idx < start_idx; ++idx) {
937061da546Spatrick if (m_subwindows[idx]->GetCanBeActive()) {
938061da546Spatrick m_curr_active_window_idx = idx;
939061da546Spatrick break;
940061da546Spatrick }
941061da546Spatrick }
942061da546Spatrick }
943be691f3bSpatrick
SelectPreviousWindowAsActive()944be691f3bSpatrick void SelectPreviousWindowAsActive() {
945be691f3bSpatrick // Move active focus to previous window
946be691f3bSpatrick const int num_subwindows = m_subwindows.size();
947be691f3bSpatrick int start_idx = num_subwindows - 1;
948be691f3bSpatrick if (m_curr_active_window_idx != UINT32_MAX) {
949061da546Spatrick m_prev_active_window_idx = m_curr_active_window_idx;
950be691f3bSpatrick start_idx = m_curr_active_window_idx - 1;
951be691f3bSpatrick }
952be691f3bSpatrick for (int idx = start_idx; idx >= 0; --idx) {
953be691f3bSpatrick if (m_subwindows[idx]->GetCanBeActive()) {
954be691f3bSpatrick m_curr_active_window_idx = idx;
955be691f3bSpatrick return;
956be691f3bSpatrick }
957be691f3bSpatrick }
958be691f3bSpatrick for (int idx = num_subwindows - 1; idx > start_idx; --idx) {
959061da546Spatrick if (m_subwindows[idx]->GetCanBeActive()) {
960061da546Spatrick m_curr_active_window_idx = idx;
961061da546Spatrick break;
962061da546Spatrick }
963061da546Spatrick }
964061da546Spatrick }
965061da546Spatrick
GetName() const966061da546Spatrick const char *GetName() const { return m_name.c_str(); }
967061da546Spatrick
968061da546Spatrick protected:
969061da546Spatrick std::string m_name;
970061da546Spatrick PANEL *m_panel;
971061da546Spatrick Window *m_parent;
972061da546Spatrick Windows m_subwindows;
973061da546Spatrick WindowDelegateSP m_delegate_sp;
974061da546Spatrick uint32_t m_curr_active_window_idx;
975061da546Spatrick uint32_t m_prev_active_window_idx;
976061da546Spatrick bool m_delete;
977061da546Spatrick bool m_needs_update;
978061da546Spatrick bool m_can_activate;
979061da546Spatrick bool m_is_subwin;
980061da546Spatrick
981061da546Spatrick private:
982dda28197Spatrick Window(const Window &) = delete;
983dda28197Spatrick const Window &operator=(const Window &) = delete;
984061da546Spatrick };
985061da546Spatrick
986be691f3bSpatrick /////////
987be691f3bSpatrick // Forms
988be691f3bSpatrick /////////
989be691f3bSpatrick
990be691f3bSpatrick // A scroll context defines a vertical region that needs to be visible in a
991be691f3bSpatrick // scrolling area. The region is defined by the index of the start and end lines
992be691f3bSpatrick // of the region. The start and end lines may be equal, in which case, the
993be691f3bSpatrick // region is a single line.
994be691f3bSpatrick struct ScrollContext {
995be691f3bSpatrick int start;
996be691f3bSpatrick int end;
997be691f3bSpatrick
ScrollContextcurses::ScrollContext998be691f3bSpatrick ScrollContext(int line) : start(line), end(line) {}
ScrollContextcurses::ScrollContext999be691f3bSpatrick ScrollContext(int _start, int _end) : start(_start), end(_end) {}
1000be691f3bSpatrick
Offsetcurses::ScrollContext1001be691f3bSpatrick void Offset(int offset) {
1002be691f3bSpatrick start += offset;
1003be691f3bSpatrick end += offset;
1004be691f3bSpatrick }
1005be691f3bSpatrick };
1006be691f3bSpatrick
1007be691f3bSpatrick class FieldDelegate {
1008be691f3bSpatrick public:
1009be691f3bSpatrick virtual ~FieldDelegate() = default;
1010be691f3bSpatrick
1011be691f3bSpatrick // Returns the number of lines needed to draw the field. The draw method will
1012be691f3bSpatrick // be given a surface that have exactly this number of lines.
1013be691f3bSpatrick virtual int FieldDelegateGetHeight() = 0;
1014be691f3bSpatrick
1015be691f3bSpatrick // Returns the scroll context in the local coordinates of the field. By
1016be691f3bSpatrick // default, the scroll context spans the whole field. Bigger fields with
1017be691f3bSpatrick // internal navigation should override this method to provide a finer context.
1018be691f3bSpatrick // Typical override methods would first get the scroll context of the internal
1019be691f3bSpatrick // element then add the offset of the element in the field.
FieldDelegateGetScrollContext()1020be691f3bSpatrick virtual ScrollContext FieldDelegateGetScrollContext() {
1021be691f3bSpatrick return ScrollContext(0, FieldDelegateGetHeight() - 1);
1022be691f3bSpatrick }
1023be691f3bSpatrick
1024be691f3bSpatrick // Draw the field in the given subpad surface. The surface have a height that
1025be691f3bSpatrick // is equal to the height returned by FieldDelegateGetHeight(). If the field
1026be691f3bSpatrick // is selected in the form window, then is_selected will be true.
1027*f6aab3d8Srobert virtual void FieldDelegateDraw(Surface &surface, bool is_selected) = 0;
1028be691f3bSpatrick
1029be691f3bSpatrick // Handle the key that wasn't handled by the form window or a container field.
FieldDelegateHandleChar(int key)1030be691f3bSpatrick virtual HandleCharResult FieldDelegateHandleChar(int key) {
1031be691f3bSpatrick return eKeyNotHandled;
1032be691f3bSpatrick }
1033be691f3bSpatrick
1034be691f3bSpatrick // This is executed once the user exists the field, that is, once the user
1035be691f3bSpatrick // navigates to the next or the previous field. This is particularly useful to
1036be691f3bSpatrick // do in-field validation and error setting. Fields with internal navigation
1037be691f3bSpatrick // should call this method on their fields.
FieldDelegateExitCallback()1038*f6aab3d8Srobert virtual void FieldDelegateExitCallback() {}
1039be691f3bSpatrick
1040be691f3bSpatrick // Fields may have internal navigation, for instance, a List Field have
1041be691f3bSpatrick // multiple internal elements, which needs to be navigated. To allow for this
1042be691f3bSpatrick // mechanism, the window shouldn't handle the navigation keys all the time,
1043be691f3bSpatrick // and instead call the key handing method of the selected field. It should
1044be691f3bSpatrick // only handle the navigation keys when the field contains a single element or
1045be691f3bSpatrick // have the last or first element selected depending on if the user is
1046be691f3bSpatrick // navigating forward or backward. Additionally, once a field is selected in
1047be691f3bSpatrick // the forward or backward direction, its first or last internal element
1048be691f3bSpatrick // should be selected. The following methods implements those mechanisms.
1049be691f3bSpatrick
1050be691f3bSpatrick // Returns true if the first element in the field is selected or if the field
1051be691f3bSpatrick // contains a single element.
FieldDelegateOnFirstOrOnlyElement()1052be691f3bSpatrick virtual bool FieldDelegateOnFirstOrOnlyElement() { return true; }
1053be691f3bSpatrick
1054be691f3bSpatrick // Returns true if the last element in the field is selected or if the field
1055be691f3bSpatrick // contains a single element.
FieldDelegateOnLastOrOnlyElement()1056be691f3bSpatrick virtual bool FieldDelegateOnLastOrOnlyElement() { return true; }
1057be691f3bSpatrick
1058be691f3bSpatrick // Select the first element in the field if multiple elements exists.
FieldDelegateSelectFirstElement()1059*f6aab3d8Srobert virtual void FieldDelegateSelectFirstElement() {}
1060be691f3bSpatrick
1061be691f3bSpatrick // Select the last element in the field if multiple elements exists.
FieldDelegateSelectLastElement()1062*f6aab3d8Srobert virtual void FieldDelegateSelectLastElement() {}
1063be691f3bSpatrick
1064be691f3bSpatrick // Returns true if the field has an error, false otherwise.
FieldDelegateHasError()1065be691f3bSpatrick virtual bool FieldDelegateHasError() { return false; }
1066be691f3bSpatrick
FieldDelegateIsVisible()1067be691f3bSpatrick bool FieldDelegateIsVisible() { return m_is_visible; }
1068be691f3bSpatrick
FieldDelegateHide()1069be691f3bSpatrick void FieldDelegateHide() { m_is_visible = false; }
1070be691f3bSpatrick
FieldDelegateShow()1071be691f3bSpatrick void FieldDelegateShow() { m_is_visible = true; }
1072be691f3bSpatrick
1073be691f3bSpatrick protected:
1074be691f3bSpatrick bool m_is_visible = true;
1075be691f3bSpatrick };
1076be691f3bSpatrick
1077be691f3bSpatrick typedef std::unique_ptr<FieldDelegate> FieldDelegateUP;
1078be691f3bSpatrick
1079be691f3bSpatrick class TextFieldDelegate : public FieldDelegate {
1080be691f3bSpatrick public:
TextFieldDelegate(const char * label,const char * content,bool required)1081be691f3bSpatrick TextFieldDelegate(const char *label, const char *content, bool required)
1082*f6aab3d8Srobert : m_label(label), m_required(required) {
1083be691f3bSpatrick if (content)
1084be691f3bSpatrick m_content = content;
1085be691f3bSpatrick }
1086be691f3bSpatrick
1087be691f3bSpatrick // Text fields are drawn as titled boxes of a single line, with a possible
1088be691f3bSpatrick // error messages at the end.
1089be691f3bSpatrick //
1090be691f3bSpatrick // __[Label]___________
1091be691f3bSpatrick // | |
1092be691f3bSpatrick // |__________________|
1093be691f3bSpatrick // - Error message if it exists.
1094be691f3bSpatrick
1095be691f3bSpatrick // The text field has a height of 3 lines. 2 lines for borders and 1 line for
1096be691f3bSpatrick // the content.
GetFieldHeight()1097be691f3bSpatrick int GetFieldHeight() { return 3; }
1098be691f3bSpatrick
1099be691f3bSpatrick // The text field has a full height of 3 or 4 lines. 3 lines for the actual
1100be691f3bSpatrick // field and an optional line for an error if it exists.
FieldDelegateGetHeight()1101be691f3bSpatrick int FieldDelegateGetHeight() override {
1102be691f3bSpatrick int height = GetFieldHeight();
1103be691f3bSpatrick if (FieldDelegateHasError())
1104be691f3bSpatrick height++;
1105be691f3bSpatrick return height;
1106be691f3bSpatrick }
1107be691f3bSpatrick
1108be691f3bSpatrick // Get the cursor X position in the surface coordinate.
GetCursorXPosition()1109be691f3bSpatrick int GetCursorXPosition() { return m_cursor_position - m_first_visibile_char; }
1110be691f3bSpatrick
GetContentLength()1111be691f3bSpatrick int GetContentLength() { return m_content.length(); }
1112be691f3bSpatrick
DrawContent(Surface & surface,bool is_selected)1113*f6aab3d8Srobert void DrawContent(Surface &surface, bool is_selected) {
1114*f6aab3d8Srobert UpdateScrolling(surface.GetWidth());
1115*f6aab3d8Srobert
1116be691f3bSpatrick surface.MoveCursor(0, 0);
1117be691f3bSpatrick const char *text = m_content.c_str() + m_first_visibile_char;
1118be691f3bSpatrick surface.PutCString(text, surface.GetWidth());
1119be691f3bSpatrick
1120be691f3bSpatrick // Highlight the cursor.
1121be691f3bSpatrick surface.MoveCursor(GetCursorXPosition(), 0);
1122be691f3bSpatrick if (is_selected)
1123be691f3bSpatrick surface.AttributeOn(A_REVERSE);
1124be691f3bSpatrick if (m_cursor_position == GetContentLength())
1125be691f3bSpatrick // Cursor is past the last character. Highlight an empty space.
1126be691f3bSpatrick surface.PutChar(' ');
1127be691f3bSpatrick else
1128be691f3bSpatrick surface.PutChar(m_content[m_cursor_position]);
1129be691f3bSpatrick if (is_selected)
1130be691f3bSpatrick surface.AttributeOff(A_REVERSE);
1131be691f3bSpatrick }
1132be691f3bSpatrick
DrawField(Surface & surface,bool is_selected)1133*f6aab3d8Srobert void DrawField(Surface &surface, bool is_selected) {
1134be691f3bSpatrick surface.TitledBox(m_label.c_str());
1135be691f3bSpatrick
1136be691f3bSpatrick Rect content_bounds = surface.GetFrame();
1137be691f3bSpatrick content_bounds.Inset(1, 1);
1138*f6aab3d8Srobert Surface content_surface = surface.SubSurface(content_bounds);
1139be691f3bSpatrick
1140be691f3bSpatrick DrawContent(content_surface, is_selected);
1141be691f3bSpatrick }
1142be691f3bSpatrick
DrawError(Surface & surface)1143*f6aab3d8Srobert void DrawError(Surface &surface) {
1144be691f3bSpatrick if (!FieldDelegateHasError())
1145be691f3bSpatrick return;
1146be691f3bSpatrick surface.MoveCursor(0, 0);
1147be691f3bSpatrick surface.AttributeOn(COLOR_PAIR(RedOnBlack));
1148be691f3bSpatrick surface.PutChar(ACS_DIAMOND);
1149be691f3bSpatrick surface.PutChar(' ');
1150be691f3bSpatrick surface.PutCStringTruncated(1, GetError().c_str());
1151be691f3bSpatrick surface.AttributeOff(COLOR_PAIR(RedOnBlack));
1152be691f3bSpatrick }
1153be691f3bSpatrick
FieldDelegateDraw(Surface & surface,bool is_selected)1154*f6aab3d8Srobert void FieldDelegateDraw(Surface &surface, bool is_selected) override {
1155be691f3bSpatrick Rect frame = surface.GetFrame();
1156be691f3bSpatrick Rect field_bounds, error_bounds;
1157be691f3bSpatrick frame.HorizontalSplit(GetFieldHeight(), field_bounds, error_bounds);
1158*f6aab3d8Srobert Surface field_surface = surface.SubSurface(field_bounds);
1159*f6aab3d8Srobert Surface error_surface = surface.SubSurface(error_bounds);
1160be691f3bSpatrick
1161be691f3bSpatrick DrawField(field_surface, is_selected);
1162be691f3bSpatrick DrawError(error_surface);
1163be691f3bSpatrick }
1164be691f3bSpatrick
1165*f6aab3d8Srobert // Get the position of the last visible character.
GetLastVisibleCharPosition(int width)1166*f6aab3d8Srobert int GetLastVisibleCharPosition(int width) {
1167*f6aab3d8Srobert int position = m_first_visibile_char + width - 1;
1168*f6aab3d8Srobert return std::min(position, GetContentLength());
1169*f6aab3d8Srobert }
1170*f6aab3d8Srobert
UpdateScrolling(int width)1171*f6aab3d8Srobert void UpdateScrolling(int width) {
1172*f6aab3d8Srobert if (m_cursor_position < m_first_visibile_char) {
1173*f6aab3d8Srobert m_first_visibile_char = m_cursor_position;
1174*f6aab3d8Srobert return;
1175*f6aab3d8Srobert }
1176*f6aab3d8Srobert
1177*f6aab3d8Srobert if (m_cursor_position > GetLastVisibleCharPosition(width))
1178*f6aab3d8Srobert m_first_visibile_char = m_cursor_position - (width - 1);
1179*f6aab3d8Srobert }
1180*f6aab3d8Srobert
1181be691f3bSpatrick // The cursor is allowed to move one character past the string.
1182be691f3bSpatrick // m_cursor_position is in range [0, GetContentLength()].
MoveCursorRight()1183be691f3bSpatrick void MoveCursorRight() {
1184be691f3bSpatrick if (m_cursor_position < GetContentLength())
1185be691f3bSpatrick m_cursor_position++;
1186be691f3bSpatrick }
1187be691f3bSpatrick
MoveCursorLeft()1188be691f3bSpatrick void MoveCursorLeft() {
1189be691f3bSpatrick if (m_cursor_position > 0)
1190be691f3bSpatrick m_cursor_position--;
1191be691f3bSpatrick }
1192be691f3bSpatrick
MoveCursorToStart()1193*f6aab3d8Srobert void MoveCursorToStart() { m_cursor_position = 0; }
1194*f6aab3d8Srobert
MoveCursorToEnd()1195*f6aab3d8Srobert void MoveCursorToEnd() { m_cursor_position = GetContentLength(); }
1196be691f3bSpatrick
ScrollLeft()1197be691f3bSpatrick void ScrollLeft() {
1198be691f3bSpatrick if (m_first_visibile_char > 0)
1199be691f3bSpatrick m_first_visibile_char--;
1200be691f3bSpatrick }
1201be691f3bSpatrick
1202*f6aab3d8Srobert // Insert a character at the current cursor position and advance the cursor
1203*f6aab3d8Srobert // position.
InsertChar(char character)1204be691f3bSpatrick void InsertChar(char character) {
1205be691f3bSpatrick m_content.insert(m_cursor_position, 1, character);
1206be691f3bSpatrick m_cursor_position++;
1207*f6aab3d8Srobert ClearError();
1208be691f3bSpatrick }
1209be691f3bSpatrick
1210be691f3bSpatrick // Remove the character before the cursor position, retreat the cursor
1211*f6aab3d8Srobert // position, and scroll left.
RemovePreviousChar()1212*f6aab3d8Srobert void RemovePreviousChar() {
1213be691f3bSpatrick if (m_cursor_position == 0)
1214be691f3bSpatrick return;
1215be691f3bSpatrick
1216be691f3bSpatrick m_content.erase(m_cursor_position - 1, 1);
1217be691f3bSpatrick m_cursor_position--;
1218be691f3bSpatrick ScrollLeft();
1219*f6aab3d8Srobert ClearError();
1220*f6aab3d8Srobert }
1221*f6aab3d8Srobert
1222*f6aab3d8Srobert // Remove the character after the cursor position.
RemoveNextChar()1223*f6aab3d8Srobert void RemoveNextChar() {
1224*f6aab3d8Srobert if (m_cursor_position == GetContentLength())
1225*f6aab3d8Srobert return;
1226*f6aab3d8Srobert
1227*f6aab3d8Srobert m_content.erase(m_cursor_position, 1);
1228*f6aab3d8Srobert ClearError();
1229*f6aab3d8Srobert }
1230*f6aab3d8Srobert
1231*f6aab3d8Srobert // Clear characters from the current cursor position to the end.
ClearToEnd()1232*f6aab3d8Srobert void ClearToEnd() {
1233*f6aab3d8Srobert m_content.erase(m_cursor_position);
1234*f6aab3d8Srobert ClearError();
1235*f6aab3d8Srobert }
1236*f6aab3d8Srobert
Clear()1237*f6aab3d8Srobert void Clear() {
1238*f6aab3d8Srobert m_content.clear();
1239*f6aab3d8Srobert m_cursor_position = 0;
1240*f6aab3d8Srobert ClearError();
1241be691f3bSpatrick }
1242be691f3bSpatrick
1243be691f3bSpatrick // True if the key represents a char that can be inserted in the field
1244be691f3bSpatrick // content, false otherwise.
IsAcceptableChar(int key)1245*f6aab3d8Srobert virtual bool IsAcceptableChar(int key) {
1246*f6aab3d8Srobert // The behavior of isprint is undefined when the value is not representable
1247*f6aab3d8Srobert // as an unsigned char. So explicitly check for non-ascii key codes.
1248*f6aab3d8Srobert if (key > 127)
1249*f6aab3d8Srobert return false;
1250*f6aab3d8Srobert return isprint(key);
1251*f6aab3d8Srobert }
1252be691f3bSpatrick
FieldDelegateHandleChar(int key)1253be691f3bSpatrick HandleCharResult FieldDelegateHandleChar(int key) override {
1254be691f3bSpatrick if (IsAcceptableChar(key)) {
1255be691f3bSpatrick ClearError();
1256be691f3bSpatrick InsertChar((char)key);
1257be691f3bSpatrick return eKeyHandled;
1258be691f3bSpatrick }
1259be691f3bSpatrick
1260be691f3bSpatrick switch (key) {
1261*f6aab3d8Srobert case KEY_HOME:
1262*f6aab3d8Srobert case KEY_CTRL_A:
1263*f6aab3d8Srobert MoveCursorToStart();
1264*f6aab3d8Srobert return eKeyHandled;
1265*f6aab3d8Srobert case KEY_END:
1266*f6aab3d8Srobert case KEY_CTRL_E:
1267*f6aab3d8Srobert MoveCursorToEnd();
1268*f6aab3d8Srobert return eKeyHandled;
1269be691f3bSpatrick case KEY_RIGHT:
1270*f6aab3d8Srobert case KEY_SF:
1271be691f3bSpatrick MoveCursorRight();
1272be691f3bSpatrick return eKeyHandled;
1273be691f3bSpatrick case KEY_LEFT:
1274*f6aab3d8Srobert case KEY_SR:
1275be691f3bSpatrick MoveCursorLeft();
1276be691f3bSpatrick return eKeyHandled;
1277be691f3bSpatrick case KEY_BACKSPACE:
1278*f6aab3d8Srobert case KEY_DELETE:
1279*f6aab3d8Srobert RemovePreviousChar();
1280*f6aab3d8Srobert return eKeyHandled;
1281*f6aab3d8Srobert case KEY_DC:
1282*f6aab3d8Srobert RemoveNextChar();
1283*f6aab3d8Srobert return eKeyHandled;
1284*f6aab3d8Srobert case KEY_EOL:
1285*f6aab3d8Srobert case KEY_CTRL_K:
1286*f6aab3d8Srobert ClearToEnd();
1287*f6aab3d8Srobert return eKeyHandled;
1288*f6aab3d8Srobert case KEY_DL:
1289*f6aab3d8Srobert case KEY_CLEAR:
1290*f6aab3d8Srobert Clear();
1291be691f3bSpatrick return eKeyHandled;
1292be691f3bSpatrick default:
1293be691f3bSpatrick break;
1294be691f3bSpatrick }
1295be691f3bSpatrick return eKeyNotHandled;
1296be691f3bSpatrick }
1297be691f3bSpatrick
FieldDelegateHasError()1298be691f3bSpatrick bool FieldDelegateHasError() override { return !m_error.empty(); }
1299be691f3bSpatrick
FieldDelegateExitCallback()1300be691f3bSpatrick void FieldDelegateExitCallback() override {
1301be691f3bSpatrick if (!IsSpecified() && m_required)
1302be691f3bSpatrick SetError("This field is required!");
1303be691f3bSpatrick }
1304be691f3bSpatrick
IsSpecified()1305be691f3bSpatrick bool IsSpecified() { return !m_content.empty(); }
1306be691f3bSpatrick
ClearError()1307be691f3bSpatrick void ClearError() { m_error.clear(); }
1308be691f3bSpatrick
GetError()1309be691f3bSpatrick const std::string &GetError() { return m_error; }
1310be691f3bSpatrick
SetError(const char * error)1311be691f3bSpatrick void SetError(const char *error) { m_error = error; }
1312be691f3bSpatrick
GetText()1313be691f3bSpatrick const std::string &GetText() { return m_content; }
1314be691f3bSpatrick
SetText(const char * text)1315*f6aab3d8Srobert void SetText(const char *text) {
1316*f6aab3d8Srobert if (text == nullptr) {
1317*f6aab3d8Srobert m_content.clear();
1318*f6aab3d8Srobert return;
1319*f6aab3d8Srobert }
1320*f6aab3d8Srobert m_content = text;
1321*f6aab3d8Srobert }
1322*f6aab3d8Srobert
1323be691f3bSpatrick protected:
1324be691f3bSpatrick std::string m_label;
1325be691f3bSpatrick bool m_required;
1326be691f3bSpatrick // The position of the top left corner character of the border.
1327be691f3bSpatrick std::string m_content;
1328be691f3bSpatrick // The cursor position in the content string itself. Can be in the range
1329be691f3bSpatrick // [0, GetContentLength()].
1330*f6aab3d8Srobert int m_cursor_position = 0;
1331be691f3bSpatrick // The index of the first visible character in the content.
1332*f6aab3d8Srobert int m_first_visibile_char = 0;
1333be691f3bSpatrick // Optional error message. If empty, field is considered to have no error.
1334be691f3bSpatrick std::string m_error;
1335be691f3bSpatrick };
1336be691f3bSpatrick
1337be691f3bSpatrick class IntegerFieldDelegate : public TextFieldDelegate {
1338be691f3bSpatrick public:
IntegerFieldDelegate(const char * label,int content,bool required)1339be691f3bSpatrick IntegerFieldDelegate(const char *label, int content, bool required)
1340be691f3bSpatrick : TextFieldDelegate(label, std::to_string(content).c_str(), required) {}
1341be691f3bSpatrick
1342be691f3bSpatrick // Only accept digits.
IsAcceptableChar(int key)1343be691f3bSpatrick bool IsAcceptableChar(int key) override { return isdigit(key); }
1344be691f3bSpatrick
1345be691f3bSpatrick // Returns the integer content of the field.
GetInteger()1346be691f3bSpatrick int GetInteger() { return std::stoi(m_content); }
1347be691f3bSpatrick };
1348be691f3bSpatrick
1349be691f3bSpatrick class FileFieldDelegate : public TextFieldDelegate {
1350be691f3bSpatrick public:
FileFieldDelegate(const char * label,const char * content,bool need_to_exist,bool required)1351be691f3bSpatrick FileFieldDelegate(const char *label, const char *content, bool need_to_exist,
1352be691f3bSpatrick bool required)
1353be691f3bSpatrick : TextFieldDelegate(label, content, required),
1354be691f3bSpatrick m_need_to_exist(need_to_exist) {}
1355be691f3bSpatrick
FieldDelegateExitCallback()1356be691f3bSpatrick void FieldDelegateExitCallback() override {
1357be691f3bSpatrick TextFieldDelegate::FieldDelegateExitCallback();
1358be691f3bSpatrick if (!IsSpecified())
1359be691f3bSpatrick return;
1360be691f3bSpatrick
1361be691f3bSpatrick if (!m_need_to_exist)
1362be691f3bSpatrick return;
1363be691f3bSpatrick
1364be691f3bSpatrick FileSpec file = GetResolvedFileSpec();
1365be691f3bSpatrick if (!FileSystem::Instance().Exists(file)) {
1366be691f3bSpatrick SetError("File doesn't exist!");
1367be691f3bSpatrick return;
1368be691f3bSpatrick }
1369be691f3bSpatrick if (FileSystem::Instance().IsDirectory(file)) {
1370be691f3bSpatrick SetError("Not a file!");
1371be691f3bSpatrick return;
1372be691f3bSpatrick }
1373be691f3bSpatrick }
1374be691f3bSpatrick
GetFileSpec()1375be691f3bSpatrick FileSpec GetFileSpec() {
1376be691f3bSpatrick FileSpec file_spec(GetPath());
1377be691f3bSpatrick return file_spec;
1378be691f3bSpatrick }
1379be691f3bSpatrick
GetResolvedFileSpec()1380be691f3bSpatrick FileSpec GetResolvedFileSpec() {
1381be691f3bSpatrick FileSpec file_spec(GetPath());
1382be691f3bSpatrick FileSystem::Instance().Resolve(file_spec);
1383be691f3bSpatrick return file_spec;
1384be691f3bSpatrick }
1385be691f3bSpatrick
GetPath()1386be691f3bSpatrick const std::string &GetPath() { return m_content; }
1387be691f3bSpatrick
1388be691f3bSpatrick protected:
1389be691f3bSpatrick bool m_need_to_exist;
1390be691f3bSpatrick };
1391be691f3bSpatrick
1392be691f3bSpatrick class DirectoryFieldDelegate : public TextFieldDelegate {
1393be691f3bSpatrick public:
DirectoryFieldDelegate(const char * label,const char * content,bool need_to_exist,bool required)1394be691f3bSpatrick DirectoryFieldDelegate(const char *label, const char *content,
1395be691f3bSpatrick bool need_to_exist, bool required)
1396be691f3bSpatrick : TextFieldDelegate(label, content, required),
1397be691f3bSpatrick m_need_to_exist(need_to_exist) {}
1398be691f3bSpatrick
FieldDelegateExitCallback()1399be691f3bSpatrick void FieldDelegateExitCallback() override {
1400be691f3bSpatrick TextFieldDelegate::FieldDelegateExitCallback();
1401be691f3bSpatrick if (!IsSpecified())
1402be691f3bSpatrick return;
1403be691f3bSpatrick
1404be691f3bSpatrick if (!m_need_to_exist)
1405be691f3bSpatrick return;
1406be691f3bSpatrick
1407be691f3bSpatrick FileSpec file = GetResolvedFileSpec();
1408be691f3bSpatrick if (!FileSystem::Instance().Exists(file)) {
1409be691f3bSpatrick SetError("Directory doesn't exist!");
1410be691f3bSpatrick return;
1411be691f3bSpatrick }
1412be691f3bSpatrick if (!FileSystem::Instance().IsDirectory(file)) {
1413be691f3bSpatrick SetError("Not a directory!");
1414be691f3bSpatrick return;
1415be691f3bSpatrick }
1416be691f3bSpatrick }
1417be691f3bSpatrick
GetFileSpec()1418be691f3bSpatrick FileSpec GetFileSpec() {
1419be691f3bSpatrick FileSpec file_spec(GetPath());
1420be691f3bSpatrick return file_spec;
1421be691f3bSpatrick }
1422be691f3bSpatrick
GetResolvedFileSpec()1423be691f3bSpatrick FileSpec GetResolvedFileSpec() {
1424be691f3bSpatrick FileSpec file_spec(GetPath());
1425be691f3bSpatrick FileSystem::Instance().Resolve(file_spec);
1426be691f3bSpatrick return file_spec;
1427be691f3bSpatrick }
1428be691f3bSpatrick
GetPath()1429be691f3bSpatrick const std::string &GetPath() { return m_content; }
1430be691f3bSpatrick
1431be691f3bSpatrick protected:
1432be691f3bSpatrick bool m_need_to_exist;
1433be691f3bSpatrick };
1434be691f3bSpatrick
1435be691f3bSpatrick class ArchFieldDelegate : public TextFieldDelegate {
1436be691f3bSpatrick public:
ArchFieldDelegate(const char * label,const char * content,bool required)1437be691f3bSpatrick ArchFieldDelegate(const char *label, const char *content, bool required)
1438be691f3bSpatrick : TextFieldDelegate(label, content, required) {}
1439be691f3bSpatrick
FieldDelegateExitCallback()1440be691f3bSpatrick void FieldDelegateExitCallback() override {
1441be691f3bSpatrick TextFieldDelegate::FieldDelegateExitCallback();
1442be691f3bSpatrick if (!IsSpecified())
1443be691f3bSpatrick return;
1444be691f3bSpatrick
1445be691f3bSpatrick if (!GetArchSpec().IsValid())
1446be691f3bSpatrick SetError("Not a valid arch!");
1447be691f3bSpatrick }
1448be691f3bSpatrick
GetArchString()1449be691f3bSpatrick const std::string &GetArchString() { return m_content; }
1450be691f3bSpatrick
GetArchSpec()1451be691f3bSpatrick ArchSpec GetArchSpec() { return ArchSpec(GetArchString()); }
1452be691f3bSpatrick };
1453be691f3bSpatrick
1454be691f3bSpatrick class BooleanFieldDelegate : public FieldDelegate {
1455be691f3bSpatrick public:
BooleanFieldDelegate(const char * label,bool content)1456be691f3bSpatrick BooleanFieldDelegate(const char *label, bool content)
1457be691f3bSpatrick : m_label(label), m_content(content) {}
1458be691f3bSpatrick
1459be691f3bSpatrick // Boolean fields are drawn as checkboxes.
1460be691f3bSpatrick //
1461be691f3bSpatrick // [X] Label or [ ] Label
1462be691f3bSpatrick
1463be691f3bSpatrick // Boolean fields are have a single line.
FieldDelegateGetHeight()1464be691f3bSpatrick int FieldDelegateGetHeight() override { return 1; }
1465be691f3bSpatrick
FieldDelegateDraw(Surface & surface,bool is_selected)1466*f6aab3d8Srobert void FieldDelegateDraw(Surface &surface, bool is_selected) override {
1467be691f3bSpatrick surface.MoveCursor(0, 0);
1468be691f3bSpatrick surface.PutChar('[');
1469be691f3bSpatrick if (is_selected)
1470be691f3bSpatrick surface.AttributeOn(A_REVERSE);
1471be691f3bSpatrick surface.PutChar(m_content ? ACS_DIAMOND : ' ');
1472be691f3bSpatrick if (is_selected)
1473be691f3bSpatrick surface.AttributeOff(A_REVERSE);
1474be691f3bSpatrick surface.PutChar(']');
1475be691f3bSpatrick surface.PutChar(' ');
1476be691f3bSpatrick surface.PutCString(m_label.c_str());
1477be691f3bSpatrick }
1478be691f3bSpatrick
ToggleContent()1479be691f3bSpatrick void ToggleContent() { m_content = !m_content; }
1480be691f3bSpatrick
SetContentToTrue()1481be691f3bSpatrick void SetContentToTrue() { m_content = true; }
1482be691f3bSpatrick
SetContentToFalse()1483be691f3bSpatrick void SetContentToFalse() { m_content = false; }
1484be691f3bSpatrick
FieldDelegateHandleChar(int key)1485be691f3bSpatrick HandleCharResult FieldDelegateHandleChar(int key) override {
1486be691f3bSpatrick switch (key) {
1487be691f3bSpatrick case 't':
1488be691f3bSpatrick case '1':
1489be691f3bSpatrick SetContentToTrue();
1490be691f3bSpatrick return eKeyHandled;
1491be691f3bSpatrick case 'f':
1492be691f3bSpatrick case '0':
1493be691f3bSpatrick SetContentToFalse();
1494be691f3bSpatrick return eKeyHandled;
1495be691f3bSpatrick case ' ':
1496be691f3bSpatrick case '\r':
1497be691f3bSpatrick case '\n':
1498be691f3bSpatrick case KEY_ENTER:
1499be691f3bSpatrick ToggleContent();
1500be691f3bSpatrick return eKeyHandled;
1501be691f3bSpatrick default:
1502be691f3bSpatrick break;
1503be691f3bSpatrick }
1504be691f3bSpatrick return eKeyNotHandled;
1505be691f3bSpatrick }
1506be691f3bSpatrick
1507be691f3bSpatrick // Returns the boolean content of the field.
GetBoolean()1508be691f3bSpatrick bool GetBoolean() { return m_content; }
1509be691f3bSpatrick
1510be691f3bSpatrick protected:
1511be691f3bSpatrick std::string m_label;
1512be691f3bSpatrick bool m_content;
1513be691f3bSpatrick };
1514be691f3bSpatrick
1515be691f3bSpatrick class ChoicesFieldDelegate : public FieldDelegate {
1516be691f3bSpatrick public:
ChoicesFieldDelegate(const char * label,int number_of_visible_choices,std::vector<std::string> choices)1517be691f3bSpatrick ChoicesFieldDelegate(const char *label, int number_of_visible_choices,
1518be691f3bSpatrick std::vector<std::string> choices)
1519be691f3bSpatrick : m_label(label), m_number_of_visible_choices(number_of_visible_choices),
1520*f6aab3d8Srobert m_choices(choices) {}
1521be691f3bSpatrick
1522be691f3bSpatrick // Choices fields are drawn as titles boxses of a number of visible choices.
1523be691f3bSpatrick // The rest of the choices become visible as the user scroll. The selected
1524be691f3bSpatrick // choice is denoted by a diamond as the first character.
1525be691f3bSpatrick //
1526be691f3bSpatrick // __[Label]___________
1527be691f3bSpatrick // |-Choice 1 |
1528be691f3bSpatrick // | Choice 2 |
1529be691f3bSpatrick // | Choice 3 |
1530be691f3bSpatrick // |__________________|
1531be691f3bSpatrick
1532be691f3bSpatrick // Choices field have two border characters plus the number of visible
1533be691f3bSpatrick // choices.
FieldDelegateGetHeight()1534be691f3bSpatrick int FieldDelegateGetHeight() override {
1535be691f3bSpatrick return m_number_of_visible_choices + 2;
1536be691f3bSpatrick }
1537be691f3bSpatrick
GetNumberOfChoices()1538be691f3bSpatrick int GetNumberOfChoices() { return m_choices.size(); }
1539be691f3bSpatrick
1540be691f3bSpatrick // Get the index of the last visible choice.
GetLastVisibleChoice()1541be691f3bSpatrick int GetLastVisibleChoice() {
1542be691f3bSpatrick int index = m_first_visibile_choice + m_number_of_visible_choices;
1543be691f3bSpatrick return std::min(index, GetNumberOfChoices()) - 1;
1544be691f3bSpatrick }
1545be691f3bSpatrick
DrawContent(Surface & surface,bool is_selected)1546*f6aab3d8Srobert void DrawContent(Surface &surface, bool is_selected) {
1547be691f3bSpatrick int choices_to_draw = GetLastVisibleChoice() - m_first_visibile_choice + 1;
1548be691f3bSpatrick for (int i = 0; i < choices_to_draw; i++) {
1549be691f3bSpatrick surface.MoveCursor(0, i);
1550be691f3bSpatrick int current_choice = m_first_visibile_choice + i;
1551be691f3bSpatrick const char *text = m_choices[current_choice].c_str();
1552be691f3bSpatrick bool highlight = is_selected && current_choice == m_choice;
1553be691f3bSpatrick if (highlight)
1554be691f3bSpatrick surface.AttributeOn(A_REVERSE);
1555be691f3bSpatrick surface.PutChar(current_choice == m_choice ? ACS_DIAMOND : ' ');
1556be691f3bSpatrick surface.PutCString(text);
1557be691f3bSpatrick if (highlight)
1558be691f3bSpatrick surface.AttributeOff(A_REVERSE);
1559be691f3bSpatrick }
1560be691f3bSpatrick }
1561be691f3bSpatrick
FieldDelegateDraw(Surface & surface,bool is_selected)1562*f6aab3d8Srobert void FieldDelegateDraw(Surface &surface, bool is_selected) override {
1563be691f3bSpatrick UpdateScrolling();
1564be691f3bSpatrick
1565be691f3bSpatrick surface.TitledBox(m_label.c_str());
1566be691f3bSpatrick
1567be691f3bSpatrick Rect content_bounds = surface.GetFrame();
1568be691f3bSpatrick content_bounds.Inset(1, 1);
1569*f6aab3d8Srobert Surface content_surface = surface.SubSurface(content_bounds);
1570be691f3bSpatrick
1571be691f3bSpatrick DrawContent(content_surface, is_selected);
1572be691f3bSpatrick }
1573be691f3bSpatrick
SelectPrevious()1574be691f3bSpatrick void SelectPrevious() {
1575be691f3bSpatrick if (m_choice > 0)
1576be691f3bSpatrick m_choice--;
1577be691f3bSpatrick }
1578be691f3bSpatrick
SelectNext()1579be691f3bSpatrick void SelectNext() {
1580be691f3bSpatrick if (m_choice < GetNumberOfChoices() - 1)
1581be691f3bSpatrick m_choice++;
1582be691f3bSpatrick }
1583be691f3bSpatrick
UpdateScrolling()1584be691f3bSpatrick void UpdateScrolling() {
1585be691f3bSpatrick if (m_choice > GetLastVisibleChoice()) {
1586be691f3bSpatrick m_first_visibile_choice = m_choice - (m_number_of_visible_choices - 1);
1587be691f3bSpatrick return;
1588be691f3bSpatrick }
1589be691f3bSpatrick
1590be691f3bSpatrick if (m_choice < m_first_visibile_choice)
1591be691f3bSpatrick m_first_visibile_choice = m_choice;
1592be691f3bSpatrick }
1593be691f3bSpatrick
FieldDelegateHandleChar(int key)1594be691f3bSpatrick HandleCharResult FieldDelegateHandleChar(int key) override {
1595be691f3bSpatrick switch (key) {
1596be691f3bSpatrick case KEY_UP:
1597be691f3bSpatrick SelectPrevious();
1598be691f3bSpatrick return eKeyHandled;
1599be691f3bSpatrick case KEY_DOWN:
1600be691f3bSpatrick SelectNext();
1601be691f3bSpatrick return eKeyHandled;
1602be691f3bSpatrick default:
1603be691f3bSpatrick break;
1604be691f3bSpatrick }
1605be691f3bSpatrick return eKeyNotHandled;
1606be691f3bSpatrick }
1607be691f3bSpatrick
1608be691f3bSpatrick // Returns the content of the choice as a string.
GetChoiceContent()1609be691f3bSpatrick std::string GetChoiceContent() { return m_choices[m_choice]; }
1610be691f3bSpatrick
1611be691f3bSpatrick // Returns the index of the choice.
GetChoice()1612be691f3bSpatrick int GetChoice() { return m_choice; }
1613be691f3bSpatrick
SetChoice(llvm::StringRef choice)1614*f6aab3d8Srobert void SetChoice(llvm::StringRef choice) {
1615be691f3bSpatrick for (int i = 0; i < GetNumberOfChoices(); i++) {
1616be691f3bSpatrick if (choice == m_choices[i]) {
1617be691f3bSpatrick m_choice = i;
1618be691f3bSpatrick return;
1619be691f3bSpatrick }
1620be691f3bSpatrick }
1621be691f3bSpatrick }
1622be691f3bSpatrick
1623be691f3bSpatrick protected:
1624be691f3bSpatrick std::string m_label;
1625be691f3bSpatrick int m_number_of_visible_choices;
1626be691f3bSpatrick std::vector<std::string> m_choices;
1627be691f3bSpatrick // The index of the selected choice.
1628*f6aab3d8Srobert int m_choice = 0;
1629be691f3bSpatrick // The index of the first visible choice in the field.
1630*f6aab3d8Srobert int m_first_visibile_choice = 0;
1631be691f3bSpatrick };
1632be691f3bSpatrick
1633be691f3bSpatrick class PlatformPluginFieldDelegate : public ChoicesFieldDelegate {
1634be691f3bSpatrick public:
PlatformPluginFieldDelegate(Debugger & debugger)1635be691f3bSpatrick PlatformPluginFieldDelegate(Debugger &debugger)
1636be691f3bSpatrick : ChoicesFieldDelegate("Platform Plugin", 3, GetPossiblePluginNames()) {
1637be691f3bSpatrick PlatformSP platform_sp = debugger.GetPlatformList().GetSelectedPlatform();
1638be691f3bSpatrick if (platform_sp)
1639*f6aab3d8Srobert SetChoice(platform_sp->GetPluginName());
1640be691f3bSpatrick }
1641be691f3bSpatrick
GetPossiblePluginNames()1642be691f3bSpatrick std::vector<std::string> GetPossiblePluginNames() {
1643be691f3bSpatrick std::vector<std::string> names;
1644be691f3bSpatrick size_t i = 0;
1645*f6aab3d8Srobert for (llvm::StringRef name =
1646*f6aab3d8Srobert PluginManager::GetPlatformPluginNameAtIndex(i++);
1647*f6aab3d8Srobert !name.empty(); name = PluginManager::GetProcessPluginNameAtIndex(i++))
1648*f6aab3d8Srobert names.push_back(name.str());
1649be691f3bSpatrick return names;
1650be691f3bSpatrick }
1651be691f3bSpatrick
GetPluginName()1652be691f3bSpatrick std::string GetPluginName() {
1653be691f3bSpatrick std::string plugin_name = GetChoiceContent();
1654be691f3bSpatrick return plugin_name;
1655be691f3bSpatrick }
1656be691f3bSpatrick };
1657be691f3bSpatrick
1658be691f3bSpatrick class ProcessPluginFieldDelegate : public ChoicesFieldDelegate {
1659be691f3bSpatrick public:
ProcessPluginFieldDelegate()1660be691f3bSpatrick ProcessPluginFieldDelegate()
1661be691f3bSpatrick : ChoicesFieldDelegate("Process Plugin", 3, GetPossiblePluginNames()) {}
1662be691f3bSpatrick
GetPossiblePluginNames()1663be691f3bSpatrick std::vector<std::string> GetPossiblePluginNames() {
1664be691f3bSpatrick std::vector<std::string> names;
1665be691f3bSpatrick names.push_back("<default>");
1666be691f3bSpatrick
1667be691f3bSpatrick size_t i = 0;
1668*f6aab3d8Srobert for (llvm::StringRef name = PluginManager::GetProcessPluginNameAtIndex(i++);
1669*f6aab3d8Srobert !name.empty(); name = PluginManager::GetProcessPluginNameAtIndex(i++))
1670*f6aab3d8Srobert names.push_back(name.str());
1671be691f3bSpatrick return names;
1672be691f3bSpatrick }
1673be691f3bSpatrick
GetPluginName()1674be691f3bSpatrick std::string GetPluginName() {
1675be691f3bSpatrick std::string plugin_name = GetChoiceContent();
1676be691f3bSpatrick if (plugin_name == "<default>")
1677be691f3bSpatrick return "";
1678be691f3bSpatrick return plugin_name;
1679be691f3bSpatrick }
1680be691f3bSpatrick };
1681be691f3bSpatrick
1682*f6aab3d8Srobert class LazyBooleanFieldDelegate : public ChoicesFieldDelegate {
1683*f6aab3d8Srobert public:
LazyBooleanFieldDelegate(const char * label,const char * calculate_label)1684*f6aab3d8Srobert LazyBooleanFieldDelegate(const char *label, const char *calculate_label)
1685*f6aab3d8Srobert : ChoicesFieldDelegate(label, 3, GetPossibleOptions(calculate_label)) {}
1686*f6aab3d8Srobert
1687*f6aab3d8Srobert static constexpr const char *kNo = "No";
1688*f6aab3d8Srobert static constexpr const char *kYes = "Yes";
1689*f6aab3d8Srobert
GetPossibleOptions(const char * calculate_label)1690*f6aab3d8Srobert std::vector<std::string> GetPossibleOptions(const char *calculate_label) {
1691*f6aab3d8Srobert std::vector<std::string> options;
1692*f6aab3d8Srobert options.push_back(calculate_label);
1693*f6aab3d8Srobert options.push_back(kYes);
1694*f6aab3d8Srobert options.push_back(kNo);
1695*f6aab3d8Srobert return options;
1696*f6aab3d8Srobert }
1697*f6aab3d8Srobert
GetLazyBoolean()1698*f6aab3d8Srobert LazyBool GetLazyBoolean() {
1699*f6aab3d8Srobert std::string choice = GetChoiceContent();
1700*f6aab3d8Srobert if (choice == kNo)
1701*f6aab3d8Srobert return eLazyBoolNo;
1702*f6aab3d8Srobert else if (choice == kYes)
1703*f6aab3d8Srobert return eLazyBoolYes;
1704*f6aab3d8Srobert else
1705*f6aab3d8Srobert return eLazyBoolCalculate;
1706*f6aab3d8Srobert }
1707*f6aab3d8Srobert };
1708*f6aab3d8Srobert
1709be691f3bSpatrick template <class T> class ListFieldDelegate : public FieldDelegate {
1710be691f3bSpatrick public:
ListFieldDelegate(const char * label,T default_field)1711be691f3bSpatrick ListFieldDelegate(const char *label, T default_field)
1712*f6aab3d8Srobert : m_label(label), m_default_field(default_field),
1713be691f3bSpatrick m_selection_type(SelectionType::NewButton) {}
1714be691f3bSpatrick
1715be691f3bSpatrick // Signify which element is selected. If a field or a remove button is
1716be691f3bSpatrick // selected, then m_selection_index signifies the particular field that
1717be691f3bSpatrick // is selected or the field that the remove button belongs to.
1718be691f3bSpatrick enum class SelectionType { Field, RemoveButton, NewButton };
1719be691f3bSpatrick
1720be691f3bSpatrick // A List field is drawn as a titled box of a number of other fields of the
1721be691f3bSpatrick // same type. Each field has a Remove button next to it that removes the
1722be691f3bSpatrick // corresponding field. Finally, the last line contains a New button to add a
1723be691f3bSpatrick // new field.
1724be691f3bSpatrick //
1725be691f3bSpatrick // __[Label]___________
1726be691f3bSpatrick // | Field 0 [Remove] |
1727be691f3bSpatrick // | Field 1 [Remove] |
1728be691f3bSpatrick // | Field 2 [Remove] |
1729be691f3bSpatrick // | [New] |
1730be691f3bSpatrick // |__________________|
1731be691f3bSpatrick
1732be691f3bSpatrick // List fields have two lines for border characters, 1 line for the New
1733be691f3bSpatrick // button, and the total height of the available fields.
FieldDelegateGetHeight()1734be691f3bSpatrick int FieldDelegateGetHeight() override {
1735be691f3bSpatrick // 2 border characters.
1736be691f3bSpatrick int height = 2;
1737be691f3bSpatrick // Total height of the fields.
1738be691f3bSpatrick for (int i = 0; i < GetNumberOfFields(); i++) {
1739be691f3bSpatrick height += m_fields[i].FieldDelegateGetHeight();
1740be691f3bSpatrick }
1741be691f3bSpatrick // A line for the New button.
1742be691f3bSpatrick height++;
1743be691f3bSpatrick return height;
1744be691f3bSpatrick }
1745be691f3bSpatrick
FieldDelegateGetScrollContext()1746be691f3bSpatrick ScrollContext FieldDelegateGetScrollContext() override {
1747be691f3bSpatrick int height = FieldDelegateGetHeight();
1748be691f3bSpatrick if (m_selection_type == SelectionType::NewButton)
1749be691f3bSpatrick return ScrollContext(height - 2, height - 1);
1750be691f3bSpatrick
1751be691f3bSpatrick FieldDelegate &field = m_fields[m_selection_index];
1752be691f3bSpatrick ScrollContext context = field.FieldDelegateGetScrollContext();
1753be691f3bSpatrick
1754be691f3bSpatrick // Start at 1 because of the top border.
1755be691f3bSpatrick int offset = 1;
1756be691f3bSpatrick for (int i = 0; i < m_selection_index; i++) {
1757be691f3bSpatrick offset += m_fields[i].FieldDelegateGetHeight();
1758be691f3bSpatrick }
1759be691f3bSpatrick context.Offset(offset);
1760be691f3bSpatrick
1761be691f3bSpatrick // If the scroll context is touching the top border, include it in the
1762be691f3bSpatrick // context to show the label.
1763be691f3bSpatrick if (context.start == 1)
1764be691f3bSpatrick context.start--;
1765be691f3bSpatrick
1766be691f3bSpatrick // If the scroll context is touching the new button, include it as well as
1767be691f3bSpatrick // the bottom border in the context.
1768be691f3bSpatrick if (context.end == height - 3)
1769be691f3bSpatrick context.end += 2;
1770be691f3bSpatrick
1771be691f3bSpatrick return context;
1772be691f3bSpatrick }
1773be691f3bSpatrick
DrawRemoveButton(Surface & surface,int highlight)1774*f6aab3d8Srobert void DrawRemoveButton(Surface &surface, int highlight) {
1775be691f3bSpatrick surface.MoveCursor(1, surface.GetHeight() / 2);
1776be691f3bSpatrick if (highlight)
1777be691f3bSpatrick surface.AttributeOn(A_REVERSE);
1778be691f3bSpatrick surface.PutCString("[Remove]");
1779be691f3bSpatrick if (highlight)
1780be691f3bSpatrick surface.AttributeOff(A_REVERSE);
1781be691f3bSpatrick }
1782be691f3bSpatrick
DrawFields(Surface & surface,bool is_selected)1783*f6aab3d8Srobert void DrawFields(Surface &surface, bool is_selected) {
1784be691f3bSpatrick int line = 0;
1785be691f3bSpatrick int width = surface.GetWidth();
1786be691f3bSpatrick for (int i = 0; i < GetNumberOfFields(); i++) {
1787be691f3bSpatrick int height = m_fields[i].FieldDelegateGetHeight();
1788be691f3bSpatrick Rect bounds = Rect(Point(0, line), Size(width, height));
1789be691f3bSpatrick Rect field_bounds, remove_button_bounds;
1790be691f3bSpatrick bounds.VerticalSplit(bounds.size.width - sizeof(" [Remove]"),
1791be691f3bSpatrick field_bounds, remove_button_bounds);
1792*f6aab3d8Srobert Surface field_surface = surface.SubSurface(field_bounds);
1793*f6aab3d8Srobert Surface remove_button_surface = surface.SubSurface(remove_button_bounds);
1794be691f3bSpatrick
1795be691f3bSpatrick bool is_element_selected = m_selection_index == i && is_selected;
1796be691f3bSpatrick bool is_field_selected =
1797be691f3bSpatrick is_element_selected && m_selection_type == SelectionType::Field;
1798be691f3bSpatrick bool is_remove_button_selected =
1799be691f3bSpatrick is_element_selected &&
1800be691f3bSpatrick m_selection_type == SelectionType::RemoveButton;
1801be691f3bSpatrick m_fields[i].FieldDelegateDraw(field_surface, is_field_selected);
1802be691f3bSpatrick DrawRemoveButton(remove_button_surface, is_remove_button_selected);
1803be691f3bSpatrick
1804be691f3bSpatrick line += height;
1805be691f3bSpatrick }
1806be691f3bSpatrick }
1807be691f3bSpatrick
DrawNewButton(Surface & surface,bool is_selected)1808*f6aab3d8Srobert void DrawNewButton(Surface &surface, bool is_selected) {
1809be691f3bSpatrick const char *button_text = "[New]";
1810be691f3bSpatrick int x = (surface.GetWidth() - sizeof(button_text) - 1) / 2;
1811be691f3bSpatrick surface.MoveCursor(x, 0);
1812be691f3bSpatrick bool highlight =
1813be691f3bSpatrick is_selected && m_selection_type == SelectionType::NewButton;
1814be691f3bSpatrick if (highlight)
1815be691f3bSpatrick surface.AttributeOn(A_REVERSE);
1816be691f3bSpatrick surface.PutCString(button_text);
1817be691f3bSpatrick if (highlight)
1818be691f3bSpatrick surface.AttributeOff(A_REVERSE);
1819be691f3bSpatrick }
1820be691f3bSpatrick
FieldDelegateDraw(Surface & surface,bool is_selected)1821*f6aab3d8Srobert void FieldDelegateDraw(Surface &surface, bool is_selected) override {
1822be691f3bSpatrick surface.TitledBox(m_label.c_str());
1823be691f3bSpatrick
1824be691f3bSpatrick Rect content_bounds = surface.GetFrame();
1825be691f3bSpatrick content_bounds.Inset(1, 1);
1826be691f3bSpatrick Rect fields_bounds, new_button_bounds;
1827be691f3bSpatrick content_bounds.HorizontalSplit(content_bounds.size.height - 1,
1828be691f3bSpatrick fields_bounds, new_button_bounds);
1829*f6aab3d8Srobert Surface fields_surface = surface.SubSurface(fields_bounds);
1830*f6aab3d8Srobert Surface new_button_surface = surface.SubSurface(new_button_bounds);
1831be691f3bSpatrick
1832be691f3bSpatrick DrawFields(fields_surface, is_selected);
1833be691f3bSpatrick DrawNewButton(new_button_surface, is_selected);
1834be691f3bSpatrick }
1835be691f3bSpatrick
AddNewField()1836be691f3bSpatrick void AddNewField() {
1837be691f3bSpatrick m_fields.push_back(m_default_field);
1838be691f3bSpatrick m_selection_index = GetNumberOfFields() - 1;
1839be691f3bSpatrick m_selection_type = SelectionType::Field;
1840be691f3bSpatrick FieldDelegate &field = m_fields[m_selection_index];
1841be691f3bSpatrick field.FieldDelegateSelectFirstElement();
1842be691f3bSpatrick }
1843be691f3bSpatrick
RemoveField()1844be691f3bSpatrick void RemoveField() {
1845be691f3bSpatrick m_fields.erase(m_fields.begin() + m_selection_index);
1846be691f3bSpatrick if (m_selection_index != 0)
1847be691f3bSpatrick m_selection_index--;
1848be691f3bSpatrick
1849be691f3bSpatrick if (GetNumberOfFields() > 0) {
1850be691f3bSpatrick m_selection_type = SelectionType::Field;
1851be691f3bSpatrick FieldDelegate &field = m_fields[m_selection_index];
1852be691f3bSpatrick field.FieldDelegateSelectFirstElement();
1853be691f3bSpatrick } else
1854be691f3bSpatrick m_selection_type = SelectionType::NewButton;
1855be691f3bSpatrick }
1856be691f3bSpatrick
SelectNext(int key)1857be691f3bSpatrick HandleCharResult SelectNext(int key) {
1858be691f3bSpatrick if (m_selection_type == SelectionType::NewButton)
1859be691f3bSpatrick return eKeyNotHandled;
1860be691f3bSpatrick
1861be691f3bSpatrick if (m_selection_type == SelectionType::RemoveButton) {
1862be691f3bSpatrick if (m_selection_index == GetNumberOfFields() - 1) {
1863be691f3bSpatrick m_selection_type = SelectionType::NewButton;
1864be691f3bSpatrick return eKeyHandled;
1865be691f3bSpatrick }
1866be691f3bSpatrick m_selection_index++;
1867be691f3bSpatrick m_selection_type = SelectionType::Field;
1868be691f3bSpatrick FieldDelegate &next_field = m_fields[m_selection_index];
1869be691f3bSpatrick next_field.FieldDelegateSelectFirstElement();
1870be691f3bSpatrick return eKeyHandled;
1871be691f3bSpatrick }
1872be691f3bSpatrick
1873be691f3bSpatrick FieldDelegate &field = m_fields[m_selection_index];
1874be691f3bSpatrick if (!field.FieldDelegateOnLastOrOnlyElement()) {
1875be691f3bSpatrick return field.FieldDelegateHandleChar(key);
1876be691f3bSpatrick }
1877be691f3bSpatrick
1878be691f3bSpatrick field.FieldDelegateExitCallback();
1879be691f3bSpatrick
1880be691f3bSpatrick m_selection_type = SelectionType::RemoveButton;
1881be691f3bSpatrick return eKeyHandled;
1882be691f3bSpatrick }
1883be691f3bSpatrick
SelectPrevious(int key)1884be691f3bSpatrick HandleCharResult SelectPrevious(int key) {
1885be691f3bSpatrick if (FieldDelegateOnFirstOrOnlyElement())
1886be691f3bSpatrick return eKeyNotHandled;
1887be691f3bSpatrick
1888be691f3bSpatrick if (m_selection_type == SelectionType::RemoveButton) {
1889be691f3bSpatrick m_selection_type = SelectionType::Field;
1890be691f3bSpatrick FieldDelegate &field = m_fields[m_selection_index];
1891be691f3bSpatrick field.FieldDelegateSelectLastElement();
1892be691f3bSpatrick return eKeyHandled;
1893be691f3bSpatrick }
1894be691f3bSpatrick
1895be691f3bSpatrick if (m_selection_type == SelectionType::NewButton) {
1896be691f3bSpatrick m_selection_type = SelectionType::RemoveButton;
1897be691f3bSpatrick m_selection_index = GetNumberOfFields() - 1;
1898be691f3bSpatrick return eKeyHandled;
1899be691f3bSpatrick }
1900be691f3bSpatrick
1901be691f3bSpatrick FieldDelegate &field = m_fields[m_selection_index];
1902be691f3bSpatrick if (!field.FieldDelegateOnFirstOrOnlyElement()) {
1903be691f3bSpatrick return field.FieldDelegateHandleChar(key);
1904be691f3bSpatrick }
1905be691f3bSpatrick
1906be691f3bSpatrick field.FieldDelegateExitCallback();
1907be691f3bSpatrick
1908be691f3bSpatrick m_selection_type = SelectionType::RemoveButton;
1909be691f3bSpatrick m_selection_index--;
1910be691f3bSpatrick return eKeyHandled;
1911be691f3bSpatrick }
1912be691f3bSpatrick
1913*f6aab3d8Srobert // If the last element of the field is selected and it didn't handle the key.
1914*f6aab3d8Srobert // Select the next field or new button if the selected field is the last one.
SelectNextInList(int key)1915*f6aab3d8Srobert HandleCharResult SelectNextInList(int key) {
1916*f6aab3d8Srobert assert(m_selection_type == SelectionType::Field);
1917*f6aab3d8Srobert
1918*f6aab3d8Srobert FieldDelegate &field = m_fields[m_selection_index];
1919*f6aab3d8Srobert if (field.FieldDelegateHandleChar(key) == eKeyHandled)
1920*f6aab3d8Srobert return eKeyHandled;
1921*f6aab3d8Srobert
1922*f6aab3d8Srobert if (!field.FieldDelegateOnLastOrOnlyElement())
1923*f6aab3d8Srobert return eKeyNotHandled;
1924*f6aab3d8Srobert
1925*f6aab3d8Srobert field.FieldDelegateExitCallback();
1926*f6aab3d8Srobert
1927*f6aab3d8Srobert if (m_selection_index == GetNumberOfFields() - 1) {
1928*f6aab3d8Srobert m_selection_type = SelectionType::NewButton;
1929*f6aab3d8Srobert return eKeyHandled;
1930*f6aab3d8Srobert }
1931*f6aab3d8Srobert
1932*f6aab3d8Srobert m_selection_index++;
1933*f6aab3d8Srobert FieldDelegate &next_field = m_fields[m_selection_index];
1934*f6aab3d8Srobert next_field.FieldDelegateSelectFirstElement();
1935*f6aab3d8Srobert return eKeyHandled;
1936*f6aab3d8Srobert }
1937*f6aab3d8Srobert
FieldDelegateHandleChar(int key)1938be691f3bSpatrick HandleCharResult FieldDelegateHandleChar(int key) override {
1939be691f3bSpatrick switch (key) {
1940be691f3bSpatrick case '\r':
1941be691f3bSpatrick case '\n':
1942be691f3bSpatrick case KEY_ENTER:
1943be691f3bSpatrick switch (m_selection_type) {
1944be691f3bSpatrick case SelectionType::NewButton:
1945be691f3bSpatrick AddNewField();
1946be691f3bSpatrick return eKeyHandled;
1947be691f3bSpatrick case SelectionType::RemoveButton:
1948be691f3bSpatrick RemoveField();
1949be691f3bSpatrick return eKeyHandled;
1950*f6aab3d8Srobert case SelectionType::Field:
1951*f6aab3d8Srobert return SelectNextInList(key);
1952be691f3bSpatrick }
1953be691f3bSpatrick break;
1954be691f3bSpatrick case '\t':
1955*f6aab3d8Srobert return SelectNext(key);
1956be691f3bSpatrick case KEY_SHIFT_TAB:
1957*f6aab3d8Srobert return SelectPrevious(key);
1958be691f3bSpatrick default:
1959be691f3bSpatrick break;
1960be691f3bSpatrick }
1961be691f3bSpatrick
1962be691f3bSpatrick // If the key wasn't handled and one of the fields is selected, pass the key
1963be691f3bSpatrick // to that field.
1964be691f3bSpatrick if (m_selection_type == SelectionType::Field) {
1965be691f3bSpatrick return m_fields[m_selection_index].FieldDelegateHandleChar(key);
1966be691f3bSpatrick }
1967be691f3bSpatrick
1968be691f3bSpatrick return eKeyNotHandled;
1969be691f3bSpatrick }
1970be691f3bSpatrick
FieldDelegateOnLastOrOnlyElement()1971be691f3bSpatrick bool FieldDelegateOnLastOrOnlyElement() override {
1972be691f3bSpatrick if (m_selection_type == SelectionType::NewButton) {
1973be691f3bSpatrick return true;
1974be691f3bSpatrick }
1975be691f3bSpatrick return false;
1976be691f3bSpatrick }
1977be691f3bSpatrick
FieldDelegateOnFirstOrOnlyElement()1978be691f3bSpatrick bool FieldDelegateOnFirstOrOnlyElement() override {
1979be691f3bSpatrick if (m_selection_type == SelectionType::NewButton &&
1980be691f3bSpatrick GetNumberOfFields() == 0)
1981be691f3bSpatrick return true;
1982be691f3bSpatrick
1983be691f3bSpatrick if (m_selection_type == SelectionType::Field && m_selection_index == 0) {
1984be691f3bSpatrick FieldDelegate &field = m_fields[m_selection_index];
1985be691f3bSpatrick return field.FieldDelegateOnFirstOrOnlyElement();
1986be691f3bSpatrick }
1987be691f3bSpatrick
1988be691f3bSpatrick return false;
1989be691f3bSpatrick }
1990be691f3bSpatrick
FieldDelegateSelectFirstElement()1991be691f3bSpatrick void FieldDelegateSelectFirstElement() override {
1992be691f3bSpatrick if (GetNumberOfFields() == 0) {
1993be691f3bSpatrick m_selection_type = SelectionType::NewButton;
1994be691f3bSpatrick return;
1995be691f3bSpatrick }
1996be691f3bSpatrick
1997be691f3bSpatrick m_selection_type = SelectionType::Field;
1998be691f3bSpatrick m_selection_index = 0;
1999be691f3bSpatrick }
2000be691f3bSpatrick
FieldDelegateSelectLastElement()2001be691f3bSpatrick void FieldDelegateSelectLastElement() override {
2002be691f3bSpatrick m_selection_type = SelectionType::NewButton;
2003be691f3bSpatrick }
2004be691f3bSpatrick
GetNumberOfFields()2005be691f3bSpatrick int GetNumberOfFields() { return m_fields.size(); }
2006be691f3bSpatrick
2007be691f3bSpatrick // Returns the form delegate at the current index.
GetField(int index)2008be691f3bSpatrick T &GetField(int index) { return m_fields[index]; }
2009be691f3bSpatrick
2010be691f3bSpatrick protected:
2011be691f3bSpatrick std::string m_label;
2012be691f3bSpatrick // The default field delegate instance from which new field delegates will be
2013be691f3bSpatrick // created though a copy.
2014be691f3bSpatrick T m_default_field;
2015be691f3bSpatrick std::vector<T> m_fields;
2016*f6aab3d8Srobert int m_selection_index = 0;
2017be691f3bSpatrick // See SelectionType class enum.
2018be691f3bSpatrick SelectionType m_selection_type;
2019be691f3bSpatrick };
2020be691f3bSpatrick
2021*f6aab3d8Srobert class ArgumentsFieldDelegate : public ListFieldDelegate<TextFieldDelegate> {
2022*f6aab3d8Srobert public:
ArgumentsFieldDelegate()2023*f6aab3d8Srobert ArgumentsFieldDelegate()
2024*f6aab3d8Srobert : ListFieldDelegate("Arguments",
2025*f6aab3d8Srobert TextFieldDelegate("Argument", "", false)) {}
2026*f6aab3d8Srobert
GetArguments()2027*f6aab3d8Srobert Args GetArguments() {
2028*f6aab3d8Srobert Args arguments;
2029*f6aab3d8Srobert for (int i = 0; i < GetNumberOfFields(); i++) {
2030*f6aab3d8Srobert arguments.AppendArgument(GetField(i).GetText());
2031*f6aab3d8Srobert }
2032*f6aab3d8Srobert return arguments;
2033*f6aab3d8Srobert }
2034*f6aab3d8Srobert
AddArguments(const Args & arguments)2035*f6aab3d8Srobert void AddArguments(const Args &arguments) {
2036*f6aab3d8Srobert for (size_t i = 0; i < arguments.GetArgumentCount(); i++) {
2037*f6aab3d8Srobert AddNewField();
2038*f6aab3d8Srobert TextFieldDelegate &field = GetField(GetNumberOfFields() - 1);
2039*f6aab3d8Srobert field.SetText(arguments.GetArgumentAtIndex(i));
2040*f6aab3d8Srobert }
2041*f6aab3d8Srobert }
2042*f6aab3d8Srobert };
2043*f6aab3d8Srobert
2044*f6aab3d8Srobert template <class KeyFieldDelegateType, class ValueFieldDelegateType>
2045*f6aab3d8Srobert class MappingFieldDelegate : public FieldDelegate {
2046*f6aab3d8Srobert public:
MappingFieldDelegate(KeyFieldDelegateType key_field,ValueFieldDelegateType value_field)2047*f6aab3d8Srobert MappingFieldDelegate(KeyFieldDelegateType key_field,
2048*f6aab3d8Srobert ValueFieldDelegateType value_field)
2049*f6aab3d8Srobert : m_key_field(key_field), m_value_field(value_field),
2050*f6aab3d8Srobert m_selection_type(SelectionType::Key) {}
2051*f6aab3d8Srobert
2052*f6aab3d8Srobert // Signify which element is selected. The key field or its value field.
2053*f6aab3d8Srobert enum class SelectionType { Key, Value };
2054*f6aab3d8Srobert
2055*f6aab3d8Srobert // A mapping field is drawn as two text fields with a right arrow in between.
2056*f6aab3d8Srobert // The first field stores the key of the mapping and the second stores the
2057*f6aab3d8Srobert // value if the mapping.
2058*f6aab3d8Srobert //
2059*f6aab3d8Srobert // __[Key]_____________ __[Value]___________
2060*f6aab3d8Srobert // | | > | |
2061*f6aab3d8Srobert // |__________________| |__________________|
2062*f6aab3d8Srobert // - Error message if it exists.
2063*f6aab3d8Srobert
2064*f6aab3d8Srobert // The mapping field has a height that is equal to the maximum height between
2065*f6aab3d8Srobert // the key and value fields.
FieldDelegateGetHeight()2066*f6aab3d8Srobert int FieldDelegateGetHeight() override {
2067*f6aab3d8Srobert return std::max(m_key_field.FieldDelegateGetHeight(),
2068*f6aab3d8Srobert m_value_field.FieldDelegateGetHeight());
2069*f6aab3d8Srobert }
2070*f6aab3d8Srobert
DrawArrow(Surface & surface)2071*f6aab3d8Srobert void DrawArrow(Surface &surface) {
2072*f6aab3d8Srobert surface.MoveCursor(0, 1);
2073*f6aab3d8Srobert surface.PutChar(ACS_RARROW);
2074*f6aab3d8Srobert }
2075*f6aab3d8Srobert
FieldDelegateDraw(Surface & surface,bool is_selected)2076*f6aab3d8Srobert void FieldDelegateDraw(Surface &surface, bool is_selected) override {
2077*f6aab3d8Srobert Rect bounds = surface.GetFrame();
2078*f6aab3d8Srobert Rect key_field_bounds, arrow_and_value_field_bounds;
2079*f6aab3d8Srobert bounds.VerticalSplit(bounds.size.width / 2, key_field_bounds,
2080*f6aab3d8Srobert arrow_and_value_field_bounds);
2081*f6aab3d8Srobert Rect arrow_bounds, value_field_bounds;
2082*f6aab3d8Srobert arrow_and_value_field_bounds.VerticalSplit(1, arrow_bounds,
2083*f6aab3d8Srobert value_field_bounds);
2084*f6aab3d8Srobert
2085*f6aab3d8Srobert Surface key_field_surface = surface.SubSurface(key_field_bounds);
2086*f6aab3d8Srobert Surface arrow_surface = surface.SubSurface(arrow_bounds);
2087*f6aab3d8Srobert Surface value_field_surface = surface.SubSurface(value_field_bounds);
2088*f6aab3d8Srobert
2089*f6aab3d8Srobert bool key_is_selected =
2090*f6aab3d8Srobert m_selection_type == SelectionType::Key && is_selected;
2091*f6aab3d8Srobert m_key_field.FieldDelegateDraw(key_field_surface, key_is_selected);
2092*f6aab3d8Srobert DrawArrow(arrow_surface);
2093*f6aab3d8Srobert bool value_is_selected =
2094*f6aab3d8Srobert m_selection_type == SelectionType::Value && is_selected;
2095*f6aab3d8Srobert m_value_field.FieldDelegateDraw(value_field_surface, value_is_selected);
2096*f6aab3d8Srobert }
2097*f6aab3d8Srobert
SelectNext(int key)2098*f6aab3d8Srobert HandleCharResult SelectNext(int key) {
2099*f6aab3d8Srobert if (FieldDelegateOnLastOrOnlyElement())
2100*f6aab3d8Srobert return eKeyNotHandled;
2101*f6aab3d8Srobert
2102*f6aab3d8Srobert if (!m_key_field.FieldDelegateOnLastOrOnlyElement()) {
2103*f6aab3d8Srobert return m_key_field.FieldDelegateHandleChar(key);
2104*f6aab3d8Srobert }
2105*f6aab3d8Srobert
2106*f6aab3d8Srobert m_key_field.FieldDelegateExitCallback();
2107*f6aab3d8Srobert m_selection_type = SelectionType::Value;
2108*f6aab3d8Srobert m_value_field.FieldDelegateSelectFirstElement();
2109*f6aab3d8Srobert return eKeyHandled;
2110*f6aab3d8Srobert }
2111*f6aab3d8Srobert
SelectPrevious(int key)2112*f6aab3d8Srobert HandleCharResult SelectPrevious(int key) {
2113*f6aab3d8Srobert if (FieldDelegateOnFirstOrOnlyElement())
2114*f6aab3d8Srobert return eKeyNotHandled;
2115*f6aab3d8Srobert
2116*f6aab3d8Srobert if (!m_value_field.FieldDelegateOnFirstOrOnlyElement()) {
2117*f6aab3d8Srobert return m_value_field.FieldDelegateHandleChar(key);
2118*f6aab3d8Srobert }
2119*f6aab3d8Srobert
2120*f6aab3d8Srobert m_value_field.FieldDelegateExitCallback();
2121*f6aab3d8Srobert m_selection_type = SelectionType::Key;
2122*f6aab3d8Srobert m_key_field.FieldDelegateSelectLastElement();
2123*f6aab3d8Srobert return eKeyHandled;
2124*f6aab3d8Srobert }
2125*f6aab3d8Srobert
2126*f6aab3d8Srobert // If the value field is selected, pass the key to it. If the key field is
2127*f6aab3d8Srobert // selected, its last element is selected, and it didn't handle the key, then
2128*f6aab3d8Srobert // select its corresponding value field.
SelectNextField(int key)2129*f6aab3d8Srobert HandleCharResult SelectNextField(int key) {
2130*f6aab3d8Srobert if (m_selection_type == SelectionType::Value) {
2131*f6aab3d8Srobert return m_value_field.FieldDelegateHandleChar(key);
2132*f6aab3d8Srobert }
2133*f6aab3d8Srobert
2134*f6aab3d8Srobert if (m_key_field.FieldDelegateHandleChar(key) == eKeyHandled)
2135*f6aab3d8Srobert return eKeyHandled;
2136*f6aab3d8Srobert
2137*f6aab3d8Srobert if (!m_key_field.FieldDelegateOnLastOrOnlyElement())
2138*f6aab3d8Srobert return eKeyNotHandled;
2139*f6aab3d8Srobert
2140*f6aab3d8Srobert m_key_field.FieldDelegateExitCallback();
2141*f6aab3d8Srobert m_selection_type = SelectionType::Value;
2142*f6aab3d8Srobert m_value_field.FieldDelegateSelectFirstElement();
2143*f6aab3d8Srobert return eKeyHandled;
2144*f6aab3d8Srobert }
2145*f6aab3d8Srobert
FieldDelegateHandleChar(int key)2146*f6aab3d8Srobert HandleCharResult FieldDelegateHandleChar(int key) override {
2147*f6aab3d8Srobert switch (key) {
2148*f6aab3d8Srobert case KEY_RETURN:
2149*f6aab3d8Srobert return SelectNextField(key);
2150*f6aab3d8Srobert case '\t':
2151*f6aab3d8Srobert return SelectNext(key);
2152*f6aab3d8Srobert case KEY_SHIFT_TAB:
2153*f6aab3d8Srobert return SelectPrevious(key);
2154*f6aab3d8Srobert default:
2155*f6aab3d8Srobert break;
2156*f6aab3d8Srobert }
2157*f6aab3d8Srobert
2158*f6aab3d8Srobert // If the key wasn't handled, pass the key to the selected field.
2159*f6aab3d8Srobert if (m_selection_type == SelectionType::Key)
2160*f6aab3d8Srobert return m_key_field.FieldDelegateHandleChar(key);
2161*f6aab3d8Srobert else
2162*f6aab3d8Srobert return m_value_field.FieldDelegateHandleChar(key);
2163*f6aab3d8Srobert
2164*f6aab3d8Srobert return eKeyNotHandled;
2165*f6aab3d8Srobert }
2166*f6aab3d8Srobert
FieldDelegateOnFirstOrOnlyElement()2167*f6aab3d8Srobert bool FieldDelegateOnFirstOrOnlyElement() override {
2168*f6aab3d8Srobert return m_selection_type == SelectionType::Key;
2169*f6aab3d8Srobert }
2170*f6aab3d8Srobert
FieldDelegateOnLastOrOnlyElement()2171*f6aab3d8Srobert bool FieldDelegateOnLastOrOnlyElement() override {
2172*f6aab3d8Srobert return m_selection_type == SelectionType::Value;
2173*f6aab3d8Srobert }
2174*f6aab3d8Srobert
FieldDelegateSelectFirstElement()2175*f6aab3d8Srobert void FieldDelegateSelectFirstElement() override {
2176*f6aab3d8Srobert m_selection_type = SelectionType::Key;
2177*f6aab3d8Srobert }
2178*f6aab3d8Srobert
FieldDelegateSelectLastElement()2179*f6aab3d8Srobert void FieldDelegateSelectLastElement() override {
2180*f6aab3d8Srobert m_selection_type = SelectionType::Value;
2181*f6aab3d8Srobert }
2182*f6aab3d8Srobert
FieldDelegateHasError()2183*f6aab3d8Srobert bool FieldDelegateHasError() override {
2184*f6aab3d8Srobert return m_key_field.FieldDelegateHasError() ||
2185*f6aab3d8Srobert m_value_field.FieldDelegateHasError();
2186*f6aab3d8Srobert }
2187*f6aab3d8Srobert
GetKeyField()2188*f6aab3d8Srobert KeyFieldDelegateType &GetKeyField() { return m_key_field; }
2189*f6aab3d8Srobert
GetValueField()2190*f6aab3d8Srobert ValueFieldDelegateType &GetValueField() { return m_value_field; }
2191*f6aab3d8Srobert
2192*f6aab3d8Srobert protected:
2193*f6aab3d8Srobert KeyFieldDelegateType m_key_field;
2194*f6aab3d8Srobert ValueFieldDelegateType m_value_field;
2195*f6aab3d8Srobert // See SelectionType class enum.
2196*f6aab3d8Srobert SelectionType m_selection_type;
2197*f6aab3d8Srobert };
2198*f6aab3d8Srobert
2199*f6aab3d8Srobert class EnvironmentVariableNameFieldDelegate : public TextFieldDelegate {
2200*f6aab3d8Srobert public:
EnvironmentVariableNameFieldDelegate(const char * content)2201*f6aab3d8Srobert EnvironmentVariableNameFieldDelegate(const char *content)
2202*f6aab3d8Srobert : TextFieldDelegate("Name", content, true) {}
2203*f6aab3d8Srobert
2204*f6aab3d8Srobert // Environment variable names can't contain an equal sign.
IsAcceptableChar(int key)2205*f6aab3d8Srobert bool IsAcceptableChar(int key) override {
2206*f6aab3d8Srobert return TextFieldDelegate::IsAcceptableChar(key) && key != '=';
2207*f6aab3d8Srobert }
2208*f6aab3d8Srobert
GetName()2209*f6aab3d8Srobert const std::string &GetName() { return m_content; }
2210*f6aab3d8Srobert };
2211*f6aab3d8Srobert
2212*f6aab3d8Srobert class EnvironmentVariableFieldDelegate
2213*f6aab3d8Srobert : public MappingFieldDelegate<EnvironmentVariableNameFieldDelegate,
2214*f6aab3d8Srobert TextFieldDelegate> {
2215*f6aab3d8Srobert public:
EnvironmentVariableFieldDelegate()2216*f6aab3d8Srobert EnvironmentVariableFieldDelegate()
2217*f6aab3d8Srobert : MappingFieldDelegate(
2218*f6aab3d8Srobert EnvironmentVariableNameFieldDelegate(""),
2219*f6aab3d8Srobert TextFieldDelegate("Value", "", /*required=*/false)) {}
2220*f6aab3d8Srobert
GetName()2221*f6aab3d8Srobert const std::string &GetName() { return GetKeyField().GetName(); }
2222*f6aab3d8Srobert
GetValue()2223*f6aab3d8Srobert const std::string &GetValue() { return GetValueField().GetText(); }
2224*f6aab3d8Srobert
SetName(const char * name)2225*f6aab3d8Srobert void SetName(const char *name) { return GetKeyField().SetText(name); }
2226*f6aab3d8Srobert
SetValue(const char * value)2227*f6aab3d8Srobert void SetValue(const char *value) { return GetValueField().SetText(value); }
2228*f6aab3d8Srobert };
2229*f6aab3d8Srobert
2230*f6aab3d8Srobert class EnvironmentVariableListFieldDelegate
2231*f6aab3d8Srobert : public ListFieldDelegate<EnvironmentVariableFieldDelegate> {
2232*f6aab3d8Srobert public:
EnvironmentVariableListFieldDelegate(const char * label)2233*f6aab3d8Srobert EnvironmentVariableListFieldDelegate(const char *label)
2234*f6aab3d8Srobert : ListFieldDelegate(label, EnvironmentVariableFieldDelegate()) {}
2235*f6aab3d8Srobert
GetEnvironment()2236*f6aab3d8Srobert Environment GetEnvironment() {
2237*f6aab3d8Srobert Environment environment;
2238*f6aab3d8Srobert for (int i = 0; i < GetNumberOfFields(); i++) {
2239*f6aab3d8Srobert environment.insert(
2240*f6aab3d8Srobert std::make_pair(GetField(i).GetName(), GetField(i).GetValue()));
2241*f6aab3d8Srobert }
2242*f6aab3d8Srobert return environment;
2243*f6aab3d8Srobert }
2244*f6aab3d8Srobert
AddEnvironmentVariables(const Environment & environment)2245*f6aab3d8Srobert void AddEnvironmentVariables(const Environment &environment) {
2246*f6aab3d8Srobert for (auto &variable : environment) {
2247*f6aab3d8Srobert AddNewField();
2248*f6aab3d8Srobert EnvironmentVariableFieldDelegate &field =
2249*f6aab3d8Srobert GetField(GetNumberOfFields() - 1);
2250*f6aab3d8Srobert field.SetName(variable.getKey().str().c_str());
2251*f6aab3d8Srobert field.SetValue(variable.getValue().c_str());
2252*f6aab3d8Srobert }
2253*f6aab3d8Srobert }
2254*f6aab3d8Srobert };
2255*f6aab3d8Srobert
2256be691f3bSpatrick class FormAction {
2257be691f3bSpatrick public:
FormAction(const char * label,std::function<void (Window &)> action)2258be691f3bSpatrick FormAction(const char *label, std::function<void(Window &)> action)
2259be691f3bSpatrick : m_action(action) {
2260be691f3bSpatrick if (label)
2261be691f3bSpatrick m_label = label;
2262be691f3bSpatrick }
2263be691f3bSpatrick
2264be691f3bSpatrick // Draw a centered [Label].
Draw(Surface & surface,bool is_selected)2265*f6aab3d8Srobert void Draw(Surface &surface, bool is_selected) {
2266be691f3bSpatrick int x = (surface.GetWidth() - m_label.length()) / 2;
2267be691f3bSpatrick surface.MoveCursor(x, 0);
2268be691f3bSpatrick if (is_selected)
2269be691f3bSpatrick surface.AttributeOn(A_REVERSE);
2270be691f3bSpatrick surface.PutChar('[');
2271be691f3bSpatrick surface.PutCString(m_label.c_str());
2272be691f3bSpatrick surface.PutChar(']');
2273be691f3bSpatrick if (is_selected)
2274be691f3bSpatrick surface.AttributeOff(A_REVERSE);
2275be691f3bSpatrick }
2276be691f3bSpatrick
Execute(Window & window)2277be691f3bSpatrick void Execute(Window &window) { m_action(window); }
2278be691f3bSpatrick
GetLabel()2279be691f3bSpatrick const std::string &GetLabel() { return m_label; }
2280be691f3bSpatrick
2281be691f3bSpatrick protected:
2282be691f3bSpatrick std::string m_label;
2283be691f3bSpatrick std::function<void(Window &)> m_action;
2284be691f3bSpatrick };
2285be691f3bSpatrick
2286be691f3bSpatrick class FormDelegate {
2287be691f3bSpatrick public:
2288*f6aab3d8Srobert FormDelegate() = default;
2289be691f3bSpatrick
2290be691f3bSpatrick virtual ~FormDelegate() = default;
2291be691f3bSpatrick
2292be691f3bSpatrick virtual std::string GetName() = 0;
2293be691f3bSpatrick
UpdateFieldsVisibility()2294*f6aab3d8Srobert virtual void UpdateFieldsVisibility() {}
2295be691f3bSpatrick
GetField(uint32_t field_index)2296be691f3bSpatrick FieldDelegate *GetField(uint32_t field_index) {
2297be691f3bSpatrick if (field_index < m_fields.size())
2298be691f3bSpatrick return m_fields[field_index].get();
2299be691f3bSpatrick return nullptr;
2300be691f3bSpatrick }
2301be691f3bSpatrick
GetAction(int action_index)2302be691f3bSpatrick FormAction &GetAction(int action_index) { return m_actions[action_index]; }
2303be691f3bSpatrick
GetNumberOfFields()2304be691f3bSpatrick int GetNumberOfFields() { return m_fields.size(); }
2305be691f3bSpatrick
GetNumberOfActions()2306be691f3bSpatrick int GetNumberOfActions() { return m_actions.size(); }
2307be691f3bSpatrick
HasError()2308be691f3bSpatrick bool HasError() { return !m_error.empty(); }
2309be691f3bSpatrick
ClearError()2310be691f3bSpatrick void ClearError() { m_error.clear(); }
2311be691f3bSpatrick
GetError()2312be691f3bSpatrick const std::string &GetError() { return m_error; }
2313be691f3bSpatrick
SetError(const char * error)2314be691f3bSpatrick void SetError(const char *error) { m_error = error; }
2315be691f3bSpatrick
2316be691f3bSpatrick // If all fields are valid, true is returned. Otherwise, an error message is
2317be691f3bSpatrick // set and false is returned. This method is usually called at the start of an
2318be691f3bSpatrick // action that requires valid fields.
CheckFieldsValidity()2319be691f3bSpatrick bool CheckFieldsValidity() {
2320be691f3bSpatrick for (int i = 0; i < GetNumberOfFields(); i++) {
2321*f6aab3d8Srobert GetField(i)->FieldDelegateExitCallback();
2322be691f3bSpatrick if (GetField(i)->FieldDelegateHasError()) {
2323be691f3bSpatrick SetError("Some fields are invalid!");
2324be691f3bSpatrick return false;
2325be691f3bSpatrick }
2326be691f3bSpatrick }
2327be691f3bSpatrick return true;
2328be691f3bSpatrick }
2329be691f3bSpatrick
2330be691f3bSpatrick // Factory methods to create and add fields of specific types.
2331be691f3bSpatrick
AddTextField(const char * label,const char * content,bool required)2332be691f3bSpatrick TextFieldDelegate *AddTextField(const char *label, const char *content,
2333be691f3bSpatrick bool required) {
2334be691f3bSpatrick TextFieldDelegate *delegate =
2335be691f3bSpatrick new TextFieldDelegate(label, content, required);
2336be691f3bSpatrick m_fields.push_back(FieldDelegateUP(delegate));
2337be691f3bSpatrick return delegate;
2338be691f3bSpatrick }
2339be691f3bSpatrick
AddFileField(const char * label,const char * content,bool need_to_exist,bool required)2340be691f3bSpatrick FileFieldDelegate *AddFileField(const char *label, const char *content,
2341be691f3bSpatrick bool need_to_exist, bool required) {
2342be691f3bSpatrick FileFieldDelegate *delegate =
2343be691f3bSpatrick new FileFieldDelegate(label, content, need_to_exist, required);
2344be691f3bSpatrick m_fields.push_back(FieldDelegateUP(delegate));
2345be691f3bSpatrick return delegate;
2346be691f3bSpatrick }
2347be691f3bSpatrick
AddDirectoryField(const char * label,const char * content,bool need_to_exist,bool required)2348be691f3bSpatrick DirectoryFieldDelegate *AddDirectoryField(const char *label,
2349be691f3bSpatrick const char *content,
2350be691f3bSpatrick bool need_to_exist, bool required) {
2351be691f3bSpatrick DirectoryFieldDelegate *delegate =
2352be691f3bSpatrick new DirectoryFieldDelegate(label, content, need_to_exist, required);
2353be691f3bSpatrick m_fields.push_back(FieldDelegateUP(delegate));
2354be691f3bSpatrick return delegate;
2355be691f3bSpatrick }
2356be691f3bSpatrick
AddArchField(const char * label,const char * content,bool required)2357be691f3bSpatrick ArchFieldDelegate *AddArchField(const char *label, const char *content,
2358be691f3bSpatrick bool required) {
2359be691f3bSpatrick ArchFieldDelegate *delegate =
2360be691f3bSpatrick new ArchFieldDelegate(label, content, required);
2361be691f3bSpatrick m_fields.push_back(FieldDelegateUP(delegate));
2362be691f3bSpatrick return delegate;
2363be691f3bSpatrick }
2364be691f3bSpatrick
AddIntegerField(const char * label,int content,bool required)2365be691f3bSpatrick IntegerFieldDelegate *AddIntegerField(const char *label, int content,
2366be691f3bSpatrick bool required) {
2367be691f3bSpatrick IntegerFieldDelegate *delegate =
2368be691f3bSpatrick new IntegerFieldDelegate(label, content, required);
2369be691f3bSpatrick m_fields.push_back(FieldDelegateUP(delegate));
2370be691f3bSpatrick return delegate;
2371be691f3bSpatrick }
2372be691f3bSpatrick
AddBooleanField(const char * label,bool content)2373be691f3bSpatrick BooleanFieldDelegate *AddBooleanField(const char *label, bool content) {
2374be691f3bSpatrick BooleanFieldDelegate *delegate = new BooleanFieldDelegate(label, content);
2375be691f3bSpatrick m_fields.push_back(FieldDelegateUP(delegate));
2376be691f3bSpatrick return delegate;
2377be691f3bSpatrick }
2378be691f3bSpatrick
AddLazyBooleanField(const char * label,const char * calculate_label)2379*f6aab3d8Srobert LazyBooleanFieldDelegate *AddLazyBooleanField(const char *label,
2380*f6aab3d8Srobert const char *calculate_label) {
2381*f6aab3d8Srobert LazyBooleanFieldDelegate *delegate =
2382*f6aab3d8Srobert new LazyBooleanFieldDelegate(label, calculate_label);
2383*f6aab3d8Srobert m_fields.push_back(FieldDelegateUP(delegate));
2384*f6aab3d8Srobert return delegate;
2385*f6aab3d8Srobert }
2386*f6aab3d8Srobert
AddChoicesField(const char * label,int height,std::vector<std::string> choices)2387be691f3bSpatrick ChoicesFieldDelegate *AddChoicesField(const char *label, int height,
2388be691f3bSpatrick std::vector<std::string> choices) {
2389be691f3bSpatrick ChoicesFieldDelegate *delegate =
2390be691f3bSpatrick new ChoicesFieldDelegate(label, height, choices);
2391be691f3bSpatrick m_fields.push_back(FieldDelegateUP(delegate));
2392be691f3bSpatrick return delegate;
2393be691f3bSpatrick }
2394be691f3bSpatrick
AddPlatformPluginField(Debugger & debugger)2395be691f3bSpatrick PlatformPluginFieldDelegate *AddPlatformPluginField(Debugger &debugger) {
2396be691f3bSpatrick PlatformPluginFieldDelegate *delegate =
2397be691f3bSpatrick new PlatformPluginFieldDelegate(debugger);
2398be691f3bSpatrick m_fields.push_back(FieldDelegateUP(delegate));
2399be691f3bSpatrick return delegate;
2400be691f3bSpatrick }
2401be691f3bSpatrick
AddProcessPluginField()2402be691f3bSpatrick ProcessPluginFieldDelegate *AddProcessPluginField() {
2403be691f3bSpatrick ProcessPluginFieldDelegate *delegate = new ProcessPluginFieldDelegate();
2404be691f3bSpatrick m_fields.push_back(FieldDelegateUP(delegate));
2405be691f3bSpatrick return delegate;
2406be691f3bSpatrick }
2407be691f3bSpatrick
2408be691f3bSpatrick template <class T>
AddListField(const char * label,T default_field)2409be691f3bSpatrick ListFieldDelegate<T> *AddListField(const char *label, T default_field) {
2410be691f3bSpatrick ListFieldDelegate<T> *delegate =
2411be691f3bSpatrick new ListFieldDelegate<T>(label, default_field);
2412be691f3bSpatrick m_fields.push_back(FieldDelegateUP(delegate));
2413be691f3bSpatrick return delegate;
2414be691f3bSpatrick }
2415be691f3bSpatrick
AddArgumentsField()2416*f6aab3d8Srobert ArgumentsFieldDelegate *AddArgumentsField() {
2417*f6aab3d8Srobert ArgumentsFieldDelegate *delegate = new ArgumentsFieldDelegate();
2418*f6aab3d8Srobert m_fields.push_back(FieldDelegateUP(delegate));
2419*f6aab3d8Srobert return delegate;
2420*f6aab3d8Srobert }
2421*f6aab3d8Srobert
2422*f6aab3d8Srobert template <class K, class V>
AddMappingField(K key_field,V value_field)2423*f6aab3d8Srobert MappingFieldDelegate<K, V> *AddMappingField(K key_field, V value_field) {
2424*f6aab3d8Srobert MappingFieldDelegate<K, V> *delegate =
2425*f6aab3d8Srobert new MappingFieldDelegate<K, V>(key_field, value_field);
2426*f6aab3d8Srobert m_fields.push_back(FieldDelegateUP(delegate));
2427*f6aab3d8Srobert return delegate;
2428*f6aab3d8Srobert }
2429*f6aab3d8Srobert
2430*f6aab3d8Srobert EnvironmentVariableNameFieldDelegate *
AddEnvironmentVariableNameField(const char * content)2431*f6aab3d8Srobert AddEnvironmentVariableNameField(const char *content) {
2432*f6aab3d8Srobert EnvironmentVariableNameFieldDelegate *delegate =
2433*f6aab3d8Srobert new EnvironmentVariableNameFieldDelegate(content);
2434*f6aab3d8Srobert m_fields.push_back(FieldDelegateUP(delegate));
2435*f6aab3d8Srobert return delegate;
2436*f6aab3d8Srobert }
2437*f6aab3d8Srobert
AddEnvironmentVariableField()2438*f6aab3d8Srobert EnvironmentVariableFieldDelegate *AddEnvironmentVariableField() {
2439*f6aab3d8Srobert EnvironmentVariableFieldDelegate *delegate =
2440*f6aab3d8Srobert new EnvironmentVariableFieldDelegate();
2441*f6aab3d8Srobert m_fields.push_back(FieldDelegateUP(delegate));
2442*f6aab3d8Srobert return delegate;
2443*f6aab3d8Srobert }
2444*f6aab3d8Srobert
2445*f6aab3d8Srobert EnvironmentVariableListFieldDelegate *
AddEnvironmentVariableListField(const char * label)2446*f6aab3d8Srobert AddEnvironmentVariableListField(const char *label) {
2447*f6aab3d8Srobert EnvironmentVariableListFieldDelegate *delegate =
2448*f6aab3d8Srobert new EnvironmentVariableListFieldDelegate(label);
2449*f6aab3d8Srobert m_fields.push_back(FieldDelegateUP(delegate));
2450*f6aab3d8Srobert return delegate;
2451*f6aab3d8Srobert }
2452*f6aab3d8Srobert
2453be691f3bSpatrick // Factory methods for adding actions.
2454be691f3bSpatrick
AddAction(const char * label,std::function<void (Window &)> action)2455be691f3bSpatrick void AddAction(const char *label, std::function<void(Window &)> action) {
2456be691f3bSpatrick m_actions.push_back(FormAction(label, action));
2457be691f3bSpatrick }
2458be691f3bSpatrick
2459be691f3bSpatrick protected:
2460be691f3bSpatrick std::vector<FieldDelegateUP> m_fields;
2461be691f3bSpatrick std::vector<FormAction> m_actions;
2462be691f3bSpatrick // Optional error message. If empty, form is considered to have no error.
2463be691f3bSpatrick std::string m_error;
2464be691f3bSpatrick };
2465be691f3bSpatrick
2466be691f3bSpatrick typedef std::shared_ptr<FormDelegate> FormDelegateSP;
2467be691f3bSpatrick
2468be691f3bSpatrick class FormWindowDelegate : public WindowDelegate {
2469be691f3bSpatrick public:
FormWindowDelegate(FormDelegateSP & delegate_sp)2470*f6aab3d8Srobert FormWindowDelegate(FormDelegateSP &delegate_sp) : m_delegate_sp(delegate_sp) {
2471be691f3bSpatrick assert(m_delegate_sp->GetNumberOfActions() > 0);
2472be691f3bSpatrick if (m_delegate_sp->GetNumberOfFields() > 0)
2473be691f3bSpatrick m_selection_type = SelectionType::Field;
2474be691f3bSpatrick else
2475be691f3bSpatrick m_selection_type = SelectionType::Action;
2476be691f3bSpatrick }
2477be691f3bSpatrick
2478be691f3bSpatrick // Signify which element is selected. If a field or an action is selected,
2479be691f3bSpatrick // then m_selection_index signifies the particular field or action that is
2480be691f3bSpatrick // selected.
2481be691f3bSpatrick enum class SelectionType { Field, Action };
2482be691f3bSpatrick
2483be691f3bSpatrick // A form window is padded by one character from all sides. First, if an error
2484be691f3bSpatrick // message exists, it is drawn followed by a separator. Then one or more
2485be691f3bSpatrick // fields are drawn. Finally, all available actions are drawn on a single
2486be691f3bSpatrick // line.
2487be691f3bSpatrick //
2488be691f3bSpatrick // ___<Form Name>_________________________________________________
2489be691f3bSpatrick // | |
2490be691f3bSpatrick // | - Error message if it exists. |
2491be691f3bSpatrick // |-------------------------------------------------------------|
2492be691f3bSpatrick // | Form elements here. |
2493be691f3bSpatrick // | Form actions here. |
2494be691f3bSpatrick // | |
2495be691f3bSpatrick // |______________________________________[Press Esc to cancel]__|
2496be691f3bSpatrick //
2497be691f3bSpatrick
2498be691f3bSpatrick // One line for the error and another for the horizontal line.
GetErrorHeight()2499be691f3bSpatrick int GetErrorHeight() {
2500be691f3bSpatrick if (m_delegate_sp->HasError())
2501be691f3bSpatrick return 2;
2502be691f3bSpatrick return 0;
2503be691f3bSpatrick }
2504be691f3bSpatrick
2505be691f3bSpatrick // Actions span a single line.
GetActionsHeight()2506be691f3bSpatrick int GetActionsHeight() {
2507be691f3bSpatrick if (m_delegate_sp->GetNumberOfActions() > 0)
2508be691f3bSpatrick return 1;
2509be691f3bSpatrick return 0;
2510be691f3bSpatrick }
2511be691f3bSpatrick
2512be691f3bSpatrick // Get the total number of needed lines to draw the contents.
GetContentHeight()2513be691f3bSpatrick int GetContentHeight() {
2514be691f3bSpatrick int height = 0;
2515be691f3bSpatrick height += GetErrorHeight();
2516be691f3bSpatrick for (int i = 0; i < m_delegate_sp->GetNumberOfFields(); i++) {
2517be691f3bSpatrick if (!m_delegate_sp->GetField(i)->FieldDelegateIsVisible())
2518be691f3bSpatrick continue;
2519be691f3bSpatrick height += m_delegate_sp->GetField(i)->FieldDelegateGetHeight();
2520be691f3bSpatrick }
2521be691f3bSpatrick height += GetActionsHeight();
2522be691f3bSpatrick return height;
2523be691f3bSpatrick }
2524be691f3bSpatrick
GetScrollContext()2525be691f3bSpatrick ScrollContext GetScrollContext() {
2526be691f3bSpatrick if (m_selection_type == SelectionType::Action)
2527be691f3bSpatrick return ScrollContext(GetContentHeight() - 1);
2528be691f3bSpatrick
2529be691f3bSpatrick FieldDelegate *field = m_delegate_sp->GetField(m_selection_index);
2530be691f3bSpatrick ScrollContext context = field->FieldDelegateGetScrollContext();
2531be691f3bSpatrick
2532be691f3bSpatrick int offset = GetErrorHeight();
2533be691f3bSpatrick for (int i = 0; i < m_selection_index; i++) {
2534be691f3bSpatrick if (!m_delegate_sp->GetField(i)->FieldDelegateIsVisible())
2535be691f3bSpatrick continue;
2536be691f3bSpatrick offset += m_delegate_sp->GetField(i)->FieldDelegateGetHeight();
2537be691f3bSpatrick }
2538be691f3bSpatrick context.Offset(offset);
2539be691f3bSpatrick
2540be691f3bSpatrick // If the context is touching the error, include the error in the context as
2541be691f3bSpatrick // well.
2542be691f3bSpatrick if (context.start == GetErrorHeight())
2543be691f3bSpatrick context.start = 0;
2544be691f3bSpatrick
2545be691f3bSpatrick return context;
2546be691f3bSpatrick }
2547be691f3bSpatrick
UpdateScrolling(Surface & surface)2548*f6aab3d8Srobert void UpdateScrolling(Surface &surface) {
2549be691f3bSpatrick ScrollContext context = GetScrollContext();
2550be691f3bSpatrick int content_height = GetContentHeight();
2551be691f3bSpatrick int surface_height = surface.GetHeight();
2552be691f3bSpatrick int visible_height = std::min(content_height, surface_height);
2553be691f3bSpatrick int last_visible_line = m_first_visible_line + visible_height - 1;
2554be691f3bSpatrick
2555be691f3bSpatrick // If the last visible line is bigger than the content, then it is invalid
2556be691f3bSpatrick // and needs to be set to the last line in the content. This can happen when
2557be691f3bSpatrick // a field has shrunk in height.
2558be691f3bSpatrick if (last_visible_line > content_height - 1) {
2559be691f3bSpatrick m_first_visible_line = content_height - visible_height;
2560be691f3bSpatrick }
2561be691f3bSpatrick
2562be691f3bSpatrick if (context.start < m_first_visible_line) {
2563be691f3bSpatrick m_first_visible_line = context.start;
2564be691f3bSpatrick return;
2565be691f3bSpatrick }
2566be691f3bSpatrick
2567be691f3bSpatrick if (context.end > last_visible_line) {
2568be691f3bSpatrick m_first_visible_line = context.end - visible_height + 1;
2569be691f3bSpatrick }
2570be691f3bSpatrick }
2571be691f3bSpatrick
DrawError(Surface & surface)2572*f6aab3d8Srobert void DrawError(Surface &surface) {
2573be691f3bSpatrick if (!m_delegate_sp->HasError())
2574be691f3bSpatrick return;
2575be691f3bSpatrick surface.MoveCursor(0, 0);
2576be691f3bSpatrick surface.AttributeOn(COLOR_PAIR(RedOnBlack));
2577be691f3bSpatrick surface.PutChar(ACS_DIAMOND);
2578be691f3bSpatrick surface.PutChar(' ');
2579be691f3bSpatrick surface.PutCStringTruncated(1, m_delegate_sp->GetError().c_str());
2580be691f3bSpatrick surface.AttributeOff(COLOR_PAIR(RedOnBlack));
2581be691f3bSpatrick
2582be691f3bSpatrick surface.MoveCursor(0, 1);
2583be691f3bSpatrick surface.HorizontalLine(surface.GetWidth());
2584be691f3bSpatrick }
2585be691f3bSpatrick
DrawFields(Surface & surface)2586*f6aab3d8Srobert void DrawFields(Surface &surface) {
2587be691f3bSpatrick int line = 0;
2588be691f3bSpatrick int width = surface.GetWidth();
2589be691f3bSpatrick bool a_field_is_selected = m_selection_type == SelectionType::Field;
2590be691f3bSpatrick for (int i = 0; i < m_delegate_sp->GetNumberOfFields(); i++) {
2591be691f3bSpatrick FieldDelegate *field = m_delegate_sp->GetField(i);
2592be691f3bSpatrick if (!field->FieldDelegateIsVisible())
2593be691f3bSpatrick continue;
2594be691f3bSpatrick bool is_field_selected = a_field_is_selected && m_selection_index == i;
2595be691f3bSpatrick int height = field->FieldDelegateGetHeight();
2596be691f3bSpatrick Rect bounds = Rect(Point(0, line), Size(width, height));
2597*f6aab3d8Srobert Surface field_surface = surface.SubSurface(bounds);
2598be691f3bSpatrick field->FieldDelegateDraw(field_surface, is_field_selected);
2599be691f3bSpatrick line += height;
2600be691f3bSpatrick }
2601be691f3bSpatrick }
2602be691f3bSpatrick
DrawActions(Surface & surface)2603*f6aab3d8Srobert void DrawActions(Surface &surface) {
2604be691f3bSpatrick int number_of_actions = m_delegate_sp->GetNumberOfActions();
2605be691f3bSpatrick int width = surface.GetWidth() / number_of_actions;
2606be691f3bSpatrick bool an_action_is_selected = m_selection_type == SelectionType::Action;
2607be691f3bSpatrick int x = 0;
2608be691f3bSpatrick for (int i = 0; i < number_of_actions; i++) {
2609be691f3bSpatrick bool is_action_selected = an_action_is_selected && m_selection_index == i;
2610be691f3bSpatrick FormAction &action = m_delegate_sp->GetAction(i);
2611be691f3bSpatrick Rect bounds = Rect(Point(x, 0), Size(width, 1));
2612*f6aab3d8Srobert Surface action_surface = surface.SubSurface(bounds);
2613be691f3bSpatrick action.Draw(action_surface, is_action_selected);
2614be691f3bSpatrick x += width;
2615be691f3bSpatrick }
2616be691f3bSpatrick }
2617be691f3bSpatrick
DrawElements(Surface & surface)2618*f6aab3d8Srobert void DrawElements(Surface &surface) {
2619be691f3bSpatrick Rect frame = surface.GetFrame();
2620be691f3bSpatrick Rect fields_bounds, actions_bounds;
2621be691f3bSpatrick frame.HorizontalSplit(surface.GetHeight() - GetActionsHeight(),
2622be691f3bSpatrick fields_bounds, actions_bounds);
2623*f6aab3d8Srobert Surface fields_surface = surface.SubSurface(fields_bounds);
2624*f6aab3d8Srobert Surface actions_surface = surface.SubSurface(actions_bounds);
2625be691f3bSpatrick
2626be691f3bSpatrick DrawFields(fields_surface);
2627be691f3bSpatrick DrawActions(actions_surface);
2628be691f3bSpatrick }
2629be691f3bSpatrick
2630be691f3bSpatrick // Contents are first drawn on a pad. Then a subset of that pad is copied to
2631be691f3bSpatrick // the derived window starting at the first visible line. This essentially
2632be691f3bSpatrick // provides scrolling functionality.
DrawContent(Surface & surface)2633*f6aab3d8Srobert void DrawContent(Surface &surface) {
2634be691f3bSpatrick UpdateScrolling(surface);
2635be691f3bSpatrick
2636be691f3bSpatrick int width = surface.GetWidth();
2637be691f3bSpatrick int height = GetContentHeight();
2638be691f3bSpatrick Pad pad = Pad(Size(width, height));
2639be691f3bSpatrick
2640be691f3bSpatrick Rect frame = pad.GetFrame();
2641be691f3bSpatrick Rect error_bounds, elements_bounds;
2642be691f3bSpatrick frame.HorizontalSplit(GetErrorHeight(), error_bounds, elements_bounds);
2643*f6aab3d8Srobert Surface error_surface = pad.SubSurface(error_bounds);
2644*f6aab3d8Srobert Surface elements_surface = pad.SubSurface(elements_bounds);
2645be691f3bSpatrick
2646be691f3bSpatrick DrawError(error_surface);
2647be691f3bSpatrick DrawElements(elements_surface);
2648be691f3bSpatrick
2649be691f3bSpatrick int copy_height = std::min(surface.GetHeight(), pad.GetHeight());
2650be691f3bSpatrick pad.CopyToSurface(surface, Point(0, m_first_visible_line), Point(),
2651be691f3bSpatrick Size(width, copy_height));
2652be691f3bSpatrick }
2653be691f3bSpatrick
DrawSubmitHint(Surface & surface,bool is_active)2654*f6aab3d8Srobert void DrawSubmitHint(Surface &surface, bool is_active) {
2655*f6aab3d8Srobert surface.MoveCursor(2, surface.GetHeight() - 1);
2656*f6aab3d8Srobert if (is_active)
2657*f6aab3d8Srobert surface.AttributeOn(A_BOLD | COLOR_PAIR(BlackOnWhite));
2658*f6aab3d8Srobert surface.Printf("[Press Alt+Enter to %s]",
2659*f6aab3d8Srobert m_delegate_sp->GetAction(0).GetLabel().c_str());
2660*f6aab3d8Srobert if (is_active)
2661*f6aab3d8Srobert surface.AttributeOff(A_BOLD | COLOR_PAIR(BlackOnWhite));
2662*f6aab3d8Srobert }
2663*f6aab3d8Srobert
WindowDelegateDraw(Window & window,bool force)2664be691f3bSpatrick bool WindowDelegateDraw(Window &window, bool force) override {
2665be691f3bSpatrick m_delegate_sp->UpdateFieldsVisibility();
2666be691f3bSpatrick
2667be691f3bSpatrick window.Erase();
2668be691f3bSpatrick
2669be691f3bSpatrick window.DrawTitleBox(m_delegate_sp->GetName().c_str(),
2670*f6aab3d8Srobert "Press Esc to Cancel");
2671*f6aab3d8Srobert DrawSubmitHint(window, window.IsActive());
2672be691f3bSpatrick
2673be691f3bSpatrick Rect content_bounds = window.GetFrame();
2674be691f3bSpatrick content_bounds.Inset(2, 2);
2675*f6aab3d8Srobert Surface content_surface = window.SubSurface(content_bounds);
2676be691f3bSpatrick
2677be691f3bSpatrick DrawContent(content_surface);
2678be691f3bSpatrick return true;
2679be691f3bSpatrick }
2680be691f3bSpatrick
SkipNextHiddenFields()2681be691f3bSpatrick void SkipNextHiddenFields() {
2682be691f3bSpatrick while (true) {
2683be691f3bSpatrick if (m_delegate_sp->GetField(m_selection_index)->FieldDelegateIsVisible())
2684be691f3bSpatrick return;
2685be691f3bSpatrick
2686be691f3bSpatrick if (m_selection_index == m_delegate_sp->GetNumberOfFields() - 1) {
2687be691f3bSpatrick m_selection_type = SelectionType::Action;
2688be691f3bSpatrick m_selection_index = 0;
2689be691f3bSpatrick return;
2690be691f3bSpatrick }
2691be691f3bSpatrick
2692be691f3bSpatrick m_selection_index++;
2693be691f3bSpatrick }
2694be691f3bSpatrick }
2695be691f3bSpatrick
SelectNext(int key)2696be691f3bSpatrick HandleCharResult SelectNext(int key) {
2697be691f3bSpatrick if (m_selection_type == SelectionType::Action) {
2698be691f3bSpatrick if (m_selection_index < m_delegate_sp->GetNumberOfActions() - 1) {
2699be691f3bSpatrick m_selection_index++;
2700be691f3bSpatrick return eKeyHandled;
2701be691f3bSpatrick }
2702be691f3bSpatrick
2703be691f3bSpatrick m_selection_index = 0;
2704be691f3bSpatrick m_selection_type = SelectionType::Field;
2705be691f3bSpatrick SkipNextHiddenFields();
2706be691f3bSpatrick if (m_selection_type == SelectionType::Field) {
2707be691f3bSpatrick FieldDelegate *next_field = m_delegate_sp->GetField(m_selection_index);
2708be691f3bSpatrick next_field->FieldDelegateSelectFirstElement();
2709be691f3bSpatrick }
2710be691f3bSpatrick return eKeyHandled;
2711be691f3bSpatrick }
2712be691f3bSpatrick
2713be691f3bSpatrick FieldDelegate *field = m_delegate_sp->GetField(m_selection_index);
2714be691f3bSpatrick if (!field->FieldDelegateOnLastOrOnlyElement()) {
2715be691f3bSpatrick return field->FieldDelegateHandleChar(key);
2716be691f3bSpatrick }
2717be691f3bSpatrick
2718be691f3bSpatrick field->FieldDelegateExitCallback();
2719be691f3bSpatrick
2720be691f3bSpatrick if (m_selection_index == m_delegate_sp->GetNumberOfFields() - 1) {
2721be691f3bSpatrick m_selection_type = SelectionType::Action;
2722be691f3bSpatrick m_selection_index = 0;
2723be691f3bSpatrick return eKeyHandled;
2724be691f3bSpatrick }
2725be691f3bSpatrick
2726be691f3bSpatrick m_selection_index++;
2727be691f3bSpatrick SkipNextHiddenFields();
2728be691f3bSpatrick
2729be691f3bSpatrick if (m_selection_type == SelectionType::Field) {
2730be691f3bSpatrick FieldDelegate *next_field = m_delegate_sp->GetField(m_selection_index);
2731be691f3bSpatrick next_field->FieldDelegateSelectFirstElement();
2732be691f3bSpatrick }
2733be691f3bSpatrick
2734be691f3bSpatrick return eKeyHandled;
2735be691f3bSpatrick }
2736be691f3bSpatrick
SkipPreviousHiddenFields()2737be691f3bSpatrick void SkipPreviousHiddenFields() {
2738be691f3bSpatrick while (true) {
2739be691f3bSpatrick if (m_delegate_sp->GetField(m_selection_index)->FieldDelegateIsVisible())
2740be691f3bSpatrick return;
2741be691f3bSpatrick
2742be691f3bSpatrick if (m_selection_index == 0) {
2743be691f3bSpatrick m_selection_type = SelectionType::Action;
2744be691f3bSpatrick m_selection_index = 0;
2745be691f3bSpatrick return;
2746be691f3bSpatrick }
2747be691f3bSpatrick
2748be691f3bSpatrick m_selection_index--;
2749be691f3bSpatrick }
2750be691f3bSpatrick }
2751be691f3bSpatrick
SelectPrevious(int key)2752be691f3bSpatrick HandleCharResult SelectPrevious(int key) {
2753be691f3bSpatrick if (m_selection_type == SelectionType::Action) {
2754be691f3bSpatrick if (m_selection_index > 0) {
2755be691f3bSpatrick m_selection_index--;
2756be691f3bSpatrick return eKeyHandled;
2757be691f3bSpatrick }
2758be691f3bSpatrick m_selection_index = m_delegate_sp->GetNumberOfFields() - 1;
2759be691f3bSpatrick m_selection_type = SelectionType::Field;
2760be691f3bSpatrick SkipPreviousHiddenFields();
2761be691f3bSpatrick if (m_selection_type == SelectionType::Field) {
2762be691f3bSpatrick FieldDelegate *previous_field =
2763be691f3bSpatrick m_delegate_sp->GetField(m_selection_index);
2764be691f3bSpatrick previous_field->FieldDelegateSelectLastElement();
2765be691f3bSpatrick }
2766be691f3bSpatrick return eKeyHandled;
2767be691f3bSpatrick }
2768be691f3bSpatrick
2769be691f3bSpatrick FieldDelegate *field = m_delegate_sp->GetField(m_selection_index);
2770be691f3bSpatrick if (!field->FieldDelegateOnFirstOrOnlyElement()) {
2771be691f3bSpatrick return field->FieldDelegateHandleChar(key);
2772be691f3bSpatrick }
2773be691f3bSpatrick
2774be691f3bSpatrick field->FieldDelegateExitCallback();
2775be691f3bSpatrick
2776be691f3bSpatrick if (m_selection_index == 0) {
2777be691f3bSpatrick m_selection_type = SelectionType::Action;
2778be691f3bSpatrick m_selection_index = m_delegate_sp->GetNumberOfActions() - 1;
2779be691f3bSpatrick return eKeyHandled;
2780be691f3bSpatrick }
2781be691f3bSpatrick
2782be691f3bSpatrick m_selection_index--;
2783be691f3bSpatrick SkipPreviousHiddenFields();
2784be691f3bSpatrick
2785be691f3bSpatrick if (m_selection_type == SelectionType::Field) {
2786be691f3bSpatrick FieldDelegate *previous_field =
2787be691f3bSpatrick m_delegate_sp->GetField(m_selection_index);
2788be691f3bSpatrick previous_field->FieldDelegateSelectLastElement();
2789be691f3bSpatrick }
2790be691f3bSpatrick
2791be691f3bSpatrick return eKeyHandled;
2792be691f3bSpatrick }
2793be691f3bSpatrick
ExecuteAction(Window & window,int index)2794*f6aab3d8Srobert void ExecuteAction(Window &window, int index) {
2795*f6aab3d8Srobert FormAction &action = m_delegate_sp->GetAction(index);
2796be691f3bSpatrick action.Execute(window);
2797be691f3bSpatrick if (m_delegate_sp->HasError()) {
2798be691f3bSpatrick m_first_visible_line = 0;
2799be691f3bSpatrick m_selection_index = 0;
2800be691f3bSpatrick m_selection_type = SelectionType::Field;
2801be691f3bSpatrick }
2802be691f3bSpatrick }
2803be691f3bSpatrick
2804*f6aab3d8Srobert // Always return eKeyHandled to absorb all events since forms are always
2805*f6aab3d8Srobert // added as pop-ups that should take full control until canceled or submitted.
WindowDelegateHandleChar(Window & window,int key)2806be691f3bSpatrick HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
2807be691f3bSpatrick switch (key) {
2808be691f3bSpatrick case '\r':
2809be691f3bSpatrick case '\n':
2810be691f3bSpatrick case KEY_ENTER:
2811be691f3bSpatrick if (m_selection_type == SelectionType::Action) {
2812*f6aab3d8Srobert ExecuteAction(window, m_selection_index);
2813be691f3bSpatrick return eKeyHandled;
2814be691f3bSpatrick }
2815be691f3bSpatrick break;
2816*f6aab3d8Srobert case KEY_ALT_ENTER:
2817*f6aab3d8Srobert ExecuteAction(window, 0);
2818*f6aab3d8Srobert return eKeyHandled;
2819be691f3bSpatrick case '\t':
2820*f6aab3d8Srobert SelectNext(key);
2821*f6aab3d8Srobert return eKeyHandled;
2822be691f3bSpatrick case KEY_SHIFT_TAB:
2823*f6aab3d8Srobert SelectPrevious(key);
2824*f6aab3d8Srobert return eKeyHandled;
2825be691f3bSpatrick case KEY_ESCAPE:
2826be691f3bSpatrick window.GetParent()->RemoveSubWindow(&window);
2827be691f3bSpatrick return eKeyHandled;
2828be691f3bSpatrick default:
2829be691f3bSpatrick break;
2830be691f3bSpatrick }
2831be691f3bSpatrick
2832be691f3bSpatrick // If the key wasn't handled and one of the fields is selected, pass the key
2833be691f3bSpatrick // to that field.
2834be691f3bSpatrick if (m_selection_type == SelectionType::Field) {
2835be691f3bSpatrick FieldDelegate *field = m_delegate_sp->GetField(m_selection_index);
2836*f6aab3d8Srobert if (field->FieldDelegateHandleChar(key) == eKeyHandled)
2837*f6aab3d8Srobert return eKeyHandled;
2838be691f3bSpatrick }
2839be691f3bSpatrick
2840*f6aab3d8Srobert // If the key wasn't handled by the possibly selected field, handle some
2841*f6aab3d8Srobert // extra keys for navigation.
2842*f6aab3d8Srobert switch (key) {
2843*f6aab3d8Srobert case KEY_DOWN:
2844*f6aab3d8Srobert SelectNext(key);
2845*f6aab3d8Srobert return eKeyHandled;
2846*f6aab3d8Srobert case KEY_UP:
2847*f6aab3d8Srobert SelectPrevious(key);
2848*f6aab3d8Srobert return eKeyHandled;
2849*f6aab3d8Srobert default:
2850*f6aab3d8Srobert break;
2851*f6aab3d8Srobert }
2852*f6aab3d8Srobert
2853*f6aab3d8Srobert return eKeyHandled;
2854be691f3bSpatrick }
2855be691f3bSpatrick
2856be691f3bSpatrick protected:
2857be691f3bSpatrick FormDelegateSP m_delegate_sp;
2858be691f3bSpatrick // The index of the currently selected SelectionType.
2859*f6aab3d8Srobert int m_selection_index = 0;
2860be691f3bSpatrick // See SelectionType class enum.
2861be691f3bSpatrick SelectionType m_selection_type;
2862be691f3bSpatrick // The first visible line from the pad.
2863*f6aab3d8Srobert int m_first_visible_line = 0;
2864be691f3bSpatrick };
2865be691f3bSpatrick
2866be691f3bSpatrick ///////////////////////////
2867be691f3bSpatrick // Form Delegate Instances
2868be691f3bSpatrick ///////////////////////////
2869be691f3bSpatrick
2870be691f3bSpatrick class DetachOrKillProcessFormDelegate : public FormDelegate {
2871be691f3bSpatrick public:
DetachOrKillProcessFormDelegate(Process * process)2872be691f3bSpatrick DetachOrKillProcessFormDelegate(Process *process) : m_process(process) {
2873be691f3bSpatrick SetError("There is a running process, either detach or kill it.");
2874be691f3bSpatrick
2875be691f3bSpatrick m_keep_stopped_field =
2876be691f3bSpatrick AddBooleanField("Keep process stopped when detaching.", false);
2877be691f3bSpatrick
2878be691f3bSpatrick AddAction("Detach", [this](Window &window) { Detach(window); });
2879be691f3bSpatrick AddAction("Kill", [this](Window &window) { Kill(window); });
2880be691f3bSpatrick }
2881be691f3bSpatrick
GetName()2882be691f3bSpatrick std::string GetName() override { return "Detach/Kill Process"; }
2883be691f3bSpatrick
Kill(Window & window)2884be691f3bSpatrick void Kill(Window &window) {
2885be691f3bSpatrick Status destroy_status(m_process->Destroy(false));
2886be691f3bSpatrick if (destroy_status.Fail()) {
2887be691f3bSpatrick SetError("Failed to kill process.");
2888be691f3bSpatrick return;
2889be691f3bSpatrick }
2890be691f3bSpatrick window.GetParent()->RemoveSubWindow(&window);
2891be691f3bSpatrick }
2892be691f3bSpatrick
Detach(Window & window)2893be691f3bSpatrick void Detach(Window &window) {
2894be691f3bSpatrick Status detach_status(m_process->Detach(m_keep_stopped_field->GetBoolean()));
2895be691f3bSpatrick if (detach_status.Fail()) {
2896be691f3bSpatrick SetError("Failed to detach from process.");
2897be691f3bSpatrick return;
2898be691f3bSpatrick }
2899be691f3bSpatrick window.GetParent()->RemoveSubWindow(&window);
2900be691f3bSpatrick }
2901be691f3bSpatrick
2902be691f3bSpatrick protected:
2903be691f3bSpatrick Process *m_process;
2904be691f3bSpatrick BooleanFieldDelegate *m_keep_stopped_field;
2905be691f3bSpatrick };
2906be691f3bSpatrick
2907be691f3bSpatrick class ProcessAttachFormDelegate : public FormDelegate {
2908be691f3bSpatrick public:
ProcessAttachFormDelegate(Debugger & debugger,WindowSP main_window_sp)2909be691f3bSpatrick ProcessAttachFormDelegate(Debugger &debugger, WindowSP main_window_sp)
2910be691f3bSpatrick : m_debugger(debugger), m_main_window_sp(main_window_sp) {
2911be691f3bSpatrick std::vector<std::string> types;
2912be691f3bSpatrick types.push_back(std::string("Name"));
2913be691f3bSpatrick types.push_back(std::string("PID"));
2914be691f3bSpatrick m_type_field = AddChoicesField("Attach By", 2, types);
2915be691f3bSpatrick m_pid_field = AddIntegerField("PID", 0, true);
2916be691f3bSpatrick m_name_field =
2917be691f3bSpatrick AddTextField("Process Name", GetDefaultProcessName().c_str(), true);
2918be691f3bSpatrick m_continue_field = AddBooleanField("Continue once attached.", false);
2919be691f3bSpatrick m_wait_for_field = AddBooleanField("Wait for process to launch.", false);
2920be691f3bSpatrick m_include_existing_field =
2921be691f3bSpatrick AddBooleanField("Include existing processes.", false);
2922be691f3bSpatrick m_show_advanced_field = AddBooleanField("Show advanced settings.", false);
2923be691f3bSpatrick m_plugin_field = AddProcessPluginField();
2924be691f3bSpatrick
2925be691f3bSpatrick AddAction("Attach", [this](Window &window) { Attach(window); });
2926be691f3bSpatrick }
2927be691f3bSpatrick
GetName()2928be691f3bSpatrick std::string GetName() override { return "Attach Process"; }
2929be691f3bSpatrick
UpdateFieldsVisibility()2930be691f3bSpatrick void UpdateFieldsVisibility() override {
2931be691f3bSpatrick if (m_type_field->GetChoiceContent() == "Name") {
2932be691f3bSpatrick m_pid_field->FieldDelegateHide();
2933be691f3bSpatrick m_name_field->FieldDelegateShow();
2934be691f3bSpatrick m_wait_for_field->FieldDelegateShow();
2935be691f3bSpatrick if (m_wait_for_field->GetBoolean())
2936be691f3bSpatrick m_include_existing_field->FieldDelegateShow();
2937be691f3bSpatrick else
2938be691f3bSpatrick m_include_existing_field->FieldDelegateHide();
2939be691f3bSpatrick } else {
2940be691f3bSpatrick m_pid_field->FieldDelegateShow();
2941be691f3bSpatrick m_name_field->FieldDelegateHide();
2942be691f3bSpatrick m_wait_for_field->FieldDelegateHide();
2943be691f3bSpatrick m_include_existing_field->FieldDelegateHide();
2944be691f3bSpatrick }
2945be691f3bSpatrick if (m_show_advanced_field->GetBoolean())
2946be691f3bSpatrick m_plugin_field->FieldDelegateShow();
2947be691f3bSpatrick else
2948be691f3bSpatrick m_plugin_field->FieldDelegateHide();
2949be691f3bSpatrick }
2950be691f3bSpatrick
2951be691f3bSpatrick // Get the basename of the target's main executable if available, empty string
2952be691f3bSpatrick // otherwise.
GetDefaultProcessName()2953be691f3bSpatrick std::string GetDefaultProcessName() {
2954be691f3bSpatrick Target *target = m_debugger.GetSelectedTarget().get();
2955be691f3bSpatrick if (target == nullptr)
2956be691f3bSpatrick return "";
2957be691f3bSpatrick
2958be691f3bSpatrick ModuleSP module_sp = target->GetExecutableModule();
2959be691f3bSpatrick if (!module_sp->IsExecutable())
2960be691f3bSpatrick return "";
2961be691f3bSpatrick
2962be691f3bSpatrick return module_sp->GetFileSpec().GetFilename().AsCString();
2963be691f3bSpatrick }
2964be691f3bSpatrick
StopRunningProcess()2965be691f3bSpatrick bool StopRunningProcess() {
2966be691f3bSpatrick ExecutionContext exe_ctx =
2967be691f3bSpatrick m_debugger.GetCommandInterpreter().GetExecutionContext();
2968be691f3bSpatrick
2969be691f3bSpatrick if (!exe_ctx.HasProcessScope())
2970be691f3bSpatrick return false;
2971be691f3bSpatrick
2972be691f3bSpatrick Process *process = exe_ctx.GetProcessPtr();
2973be691f3bSpatrick if (!(process && process->IsAlive()))
2974be691f3bSpatrick return false;
2975be691f3bSpatrick
2976be691f3bSpatrick FormDelegateSP form_delegate_sp =
2977be691f3bSpatrick FormDelegateSP(new DetachOrKillProcessFormDelegate(process));
2978be691f3bSpatrick Rect bounds = m_main_window_sp->GetCenteredRect(85, 8);
2979be691f3bSpatrick WindowSP form_window_sp = m_main_window_sp->CreateSubWindow(
2980be691f3bSpatrick form_delegate_sp->GetName().c_str(), bounds, true);
2981be691f3bSpatrick WindowDelegateSP window_delegate_sp =
2982be691f3bSpatrick WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
2983be691f3bSpatrick form_window_sp->SetDelegate(window_delegate_sp);
2984be691f3bSpatrick
2985be691f3bSpatrick return true;
2986be691f3bSpatrick }
2987be691f3bSpatrick
GetTarget()2988be691f3bSpatrick Target *GetTarget() {
2989be691f3bSpatrick Target *target = m_debugger.GetSelectedTarget().get();
2990be691f3bSpatrick
2991be691f3bSpatrick if (target != nullptr)
2992be691f3bSpatrick return target;
2993be691f3bSpatrick
2994be691f3bSpatrick TargetSP new_target_sp;
2995be691f3bSpatrick m_debugger.GetTargetList().CreateTarget(
2996be691f3bSpatrick m_debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp);
2997be691f3bSpatrick
2998be691f3bSpatrick target = new_target_sp.get();
2999be691f3bSpatrick
3000be691f3bSpatrick if (target == nullptr)
3001be691f3bSpatrick SetError("Failed to create target.");
3002be691f3bSpatrick
3003be691f3bSpatrick m_debugger.GetTargetList().SetSelectedTarget(new_target_sp);
3004be691f3bSpatrick
3005be691f3bSpatrick return target;
3006be691f3bSpatrick }
3007be691f3bSpatrick
GetAttachInfo()3008be691f3bSpatrick ProcessAttachInfo GetAttachInfo() {
3009be691f3bSpatrick ProcessAttachInfo attach_info;
3010be691f3bSpatrick attach_info.SetContinueOnceAttached(m_continue_field->GetBoolean());
3011be691f3bSpatrick if (m_type_field->GetChoiceContent() == "Name") {
3012be691f3bSpatrick attach_info.GetExecutableFile().SetFile(m_name_field->GetText(),
3013be691f3bSpatrick FileSpec::Style::native);
3014be691f3bSpatrick attach_info.SetWaitForLaunch(m_wait_for_field->GetBoolean());
3015be691f3bSpatrick if (m_wait_for_field->GetBoolean())
3016be691f3bSpatrick attach_info.SetIgnoreExisting(!m_include_existing_field->GetBoolean());
3017be691f3bSpatrick } else {
3018be691f3bSpatrick attach_info.SetProcessID(m_pid_field->GetInteger());
3019be691f3bSpatrick }
3020be691f3bSpatrick attach_info.SetProcessPluginName(m_plugin_field->GetPluginName());
3021be691f3bSpatrick
3022be691f3bSpatrick return attach_info;
3023be691f3bSpatrick }
3024be691f3bSpatrick
Attach(Window & window)3025be691f3bSpatrick void Attach(Window &window) {
3026be691f3bSpatrick ClearError();
3027be691f3bSpatrick
3028be691f3bSpatrick bool all_fields_are_valid = CheckFieldsValidity();
3029be691f3bSpatrick if (!all_fields_are_valid)
3030be691f3bSpatrick return;
3031be691f3bSpatrick
3032be691f3bSpatrick bool process_is_running = StopRunningProcess();
3033be691f3bSpatrick if (process_is_running)
3034be691f3bSpatrick return;
3035be691f3bSpatrick
3036be691f3bSpatrick Target *target = GetTarget();
3037be691f3bSpatrick if (HasError())
3038be691f3bSpatrick return;
3039be691f3bSpatrick
3040be691f3bSpatrick StreamString stream;
3041be691f3bSpatrick ProcessAttachInfo attach_info = GetAttachInfo();
3042be691f3bSpatrick Status status = target->Attach(attach_info, &stream);
3043be691f3bSpatrick
3044be691f3bSpatrick if (status.Fail()) {
3045be691f3bSpatrick SetError(status.AsCString());
3046be691f3bSpatrick return;
3047be691f3bSpatrick }
3048be691f3bSpatrick
3049be691f3bSpatrick ProcessSP process_sp(target->GetProcessSP());
3050be691f3bSpatrick if (!process_sp) {
3051be691f3bSpatrick SetError("Attached sucessfully but target has no process.");
3052be691f3bSpatrick return;
3053be691f3bSpatrick }
3054be691f3bSpatrick
3055be691f3bSpatrick if (attach_info.GetContinueOnceAttached())
3056be691f3bSpatrick process_sp->Resume();
3057be691f3bSpatrick
3058be691f3bSpatrick window.GetParent()->RemoveSubWindow(&window);
3059be691f3bSpatrick }
3060be691f3bSpatrick
3061be691f3bSpatrick protected:
3062be691f3bSpatrick Debugger &m_debugger;
3063be691f3bSpatrick WindowSP m_main_window_sp;
3064be691f3bSpatrick
3065be691f3bSpatrick ChoicesFieldDelegate *m_type_field;
3066be691f3bSpatrick IntegerFieldDelegate *m_pid_field;
3067be691f3bSpatrick TextFieldDelegate *m_name_field;
3068be691f3bSpatrick BooleanFieldDelegate *m_continue_field;
3069be691f3bSpatrick BooleanFieldDelegate *m_wait_for_field;
3070be691f3bSpatrick BooleanFieldDelegate *m_include_existing_field;
3071be691f3bSpatrick BooleanFieldDelegate *m_show_advanced_field;
3072be691f3bSpatrick ProcessPluginFieldDelegate *m_plugin_field;
3073be691f3bSpatrick };
3074be691f3bSpatrick
3075*f6aab3d8Srobert class TargetCreateFormDelegate : public FormDelegate {
3076*f6aab3d8Srobert public:
TargetCreateFormDelegate(Debugger & debugger)3077*f6aab3d8Srobert TargetCreateFormDelegate(Debugger &debugger) : m_debugger(debugger) {
3078*f6aab3d8Srobert m_executable_field = AddFileField("Executable", "", /*need_to_exist=*/true,
3079*f6aab3d8Srobert /*required=*/true);
3080*f6aab3d8Srobert m_core_file_field = AddFileField("Core File", "", /*need_to_exist=*/true,
3081*f6aab3d8Srobert /*required=*/false);
3082*f6aab3d8Srobert m_symbol_file_field = AddFileField(
3083*f6aab3d8Srobert "Symbol File", "", /*need_to_exist=*/true, /*required=*/false);
3084*f6aab3d8Srobert m_show_advanced_field = AddBooleanField("Show advanced settings.", false);
3085*f6aab3d8Srobert m_remote_file_field = AddFileField(
3086*f6aab3d8Srobert "Remote File", "", /*need_to_exist=*/false, /*required=*/false);
3087*f6aab3d8Srobert m_arch_field = AddArchField("Architecture", "", /*required=*/false);
3088*f6aab3d8Srobert m_platform_field = AddPlatformPluginField(debugger);
3089*f6aab3d8Srobert m_load_dependent_files_field =
3090*f6aab3d8Srobert AddChoicesField("Load Dependents", 3, GetLoadDependentFilesChoices());
3091*f6aab3d8Srobert
3092*f6aab3d8Srobert AddAction("Create", [this](Window &window) { CreateTarget(window); });
3093*f6aab3d8Srobert }
3094*f6aab3d8Srobert
GetName()3095*f6aab3d8Srobert std::string GetName() override { return "Create Target"; }
3096*f6aab3d8Srobert
UpdateFieldsVisibility()3097*f6aab3d8Srobert void UpdateFieldsVisibility() override {
3098*f6aab3d8Srobert if (m_show_advanced_field->GetBoolean()) {
3099*f6aab3d8Srobert m_remote_file_field->FieldDelegateShow();
3100*f6aab3d8Srobert m_arch_field->FieldDelegateShow();
3101*f6aab3d8Srobert m_platform_field->FieldDelegateShow();
3102*f6aab3d8Srobert m_load_dependent_files_field->FieldDelegateShow();
3103*f6aab3d8Srobert } else {
3104*f6aab3d8Srobert m_remote_file_field->FieldDelegateHide();
3105*f6aab3d8Srobert m_arch_field->FieldDelegateHide();
3106*f6aab3d8Srobert m_platform_field->FieldDelegateHide();
3107*f6aab3d8Srobert m_load_dependent_files_field->FieldDelegateHide();
3108*f6aab3d8Srobert }
3109*f6aab3d8Srobert }
3110*f6aab3d8Srobert
3111*f6aab3d8Srobert static constexpr const char *kLoadDependentFilesNo = "No";
3112*f6aab3d8Srobert static constexpr const char *kLoadDependentFilesYes = "Yes";
3113*f6aab3d8Srobert static constexpr const char *kLoadDependentFilesExecOnly = "Executable only";
3114*f6aab3d8Srobert
GetLoadDependentFilesChoices()3115*f6aab3d8Srobert std::vector<std::string> GetLoadDependentFilesChoices() {
3116*f6aab3d8Srobert std::vector<std::string> load_dependents_options;
3117*f6aab3d8Srobert load_dependents_options.push_back(kLoadDependentFilesExecOnly);
3118*f6aab3d8Srobert load_dependents_options.push_back(kLoadDependentFilesYes);
3119*f6aab3d8Srobert load_dependents_options.push_back(kLoadDependentFilesNo);
3120*f6aab3d8Srobert return load_dependents_options;
3121*f6aab3d8Srobert }
3122*f6aab3d8Srobert
GetLoadDependentFiles()3123*f6aab3d8Srobert LoadDependentFiles GetLoadDependentFiles() {
3124*f6aab3d8Srobert std::string choice = m_load_dependent_files_field->GetChoiceContent();
3125*f6aab3d8Srobert if (choice == kLoadDependentFilesNo)
3126*f6aab3d8Srobert return eLoadDependentsNo;
3127*f6aab3d8Srobert if (choice == kLoadDependentFilesYes)
3128*f6aab3d8Srobert return eLoadDependentsYes;
3129*f6aab3d8Srobert return eLoadDependentsDefault;
3130*f6aab3d8Srobert }
3131*f6aab3d8Srobert
GetPlatformOptions()3132*f6aab3d8Srobert OptionGroupPlatform GetPlatformOptions() {
3133*f6aab3d8Srobert OptionGroupPlatform platform_options(false);
3134*f6aab3d8Srobert platform_options.SetPlatformName(m_platform_field->GetPluginName().c_str());
3135*f6aab3d8Srobert return platform_options;
3136*f6aab3d8Srobert }
3137*f6aab3d8Srobert
GetTarget()3138*f6aab3d8Srobert TargetSP GetTarget() {
3139*f6aab3d8Srobert OptionGroupPlatform platform_options = GetPlatformOptions();
3140*f6aab3d8Srobert TargetSP target_sp;
3141*f6aab3d8Srobert Status status = m_debugger.GetTargetList().CreateTarget(
3142*f6aab3d8Srobert m_debugger, m_executable_field->GetPath(),
3143*f6aab3d8Srobert m_arch_field->GetArchString(), GetLoadDependentFiles(),
3144*f6aab3d8Srobert &platform_options, target_sp);
3145*f6aab3d8Srobert
3146*f6aab3d8Srobert if (status.Fail()) {
3147*f6aab3d8Srobert SetError(status.AsCString());
3148*f6aab3d8Srobert return nullptr;
3149*f6aab3d8Srobert }
3150*f6aab3d8Srobert
3151*f6aab3d8Srobert m_debugger.GetTargetList().SetSelectedTarget(target_sp);
3152*f6aab3d8Srobert
3153*f6aab3d8Srobert return target_sp;
3154*f6aab3d8Srobert }
3155*f6aab3d8Srobert
SetSymbolFile(TargetSP target_sp)3156*f6aab3d8Srobert void SetSymbolFile(TargetSP target_sp) {
3157*f6aab3d8Srobert if (!m_symbol_file_field->IsSpecified())
3158*f6aab3d8Srobert return;
3159*f6aab3d8Srobert
3160*f6aab3d8Srobert ModuleSP module_sp(target_sp->GetExecutableModule());
3161*f6aab3d8Srobert if (!module_sp)
3162*f6aab3d8Srobert return;
3163*f6aab3d8Srobert
3164*f6aab3d8Srobert module_sp->SetSymbolFileFileSpec(
3165*f6aab3d8Srobert m_symbol_file_field->GetResolvedFileSpec());
3166*f6aab3d8Srobert }
3167*f6aab3d8Srobert
SetCoreFile(TargetSP target_sp)3168*f6aab3d8Srobert void SetCoreFile(TargetSP target_sp) {
3169*f6aab3d8Srobert if (!m_core_file_field->IsSpecified())
3170*f6aab3d8Srobert return;
3171*f6aab3d8Srobert
3172*f6aab3d8Srobert FileSpec core_file_spec = m_core_file_field->GetResolvedFileSpec();
3173*f6aab3d8Srobert
3174*f6aab3d8Srobert FileSpec core_file_directory_spec;
3175*f6aab3d8Srobert core_file_directory_spec.SetDirectory(core_file_spec.GetDirectory());
3176*f6aab3d8Srobert target_sp->AppendExecutableSearchPaths(core_file_directory_spec);
3177*f6aab3d8Srobert
3178*f6aab3d8Srobert ProcessSP process_sp(target_sp->CreateProcess(
3179*f6aab3d8Srobert m_debugger.GetListener(), llvm::StringRef(), &core_file_spec, false));
3180*f6aab3d8Srobert
3181*f6aab3d8Srobert if (!process_sp) {
3182*f6aab3d8Srobert SetError("Unable to find process plug-in for core file!");
3183*f6aab3d8Srobert return;
3184*f6aab3d8Srobert }
3185*f6aab3d8Srobert
3186*f6aab3d8Srobert Status status = process_sp->LoadCore();
3187*f6aab3d8Srobert if (status.Fail()) {
3188*f6aab3d8Srobert SetError("Can't find plug-in for core file!");
3189*f6aab3d8Srobert return;
3190*f6aab3d8Srobert }
3191*f6aab3d8Srobert }
3192*f6aab3d8Srobert
SetRemoteFile(TargetSP target_sp)3193*f6aab3d8Srobert void SetRemoteFile(TargetSP target_sp) {
3194*f6aab3d8Srobert if (!m_remote_file_field->IsSpecified())
3195*f6aab3d8Srobert return;
3196*f6aab3d8Srobert
3197*f6aab3d8Srobert ModuleSP module_sp(target_sp->GetExecutableModule());
3198*f6aab3d8Srobert if (!module_sp)
3199*f6aab3d8Srobert return;
3200*f6aab3d8Srobert
3201*f6aab3d8Srobert FileSpec remote_file_spec = m_remote_file_field->GetFileSpec();
3202*f6aab3d8Srobert module_sp->SetPlatformFileSpec(remote_file_spec);
3203*f6aab3d8Srobert }
3204*f6aab3d8Srobert
RemoveTarget(TargetSP target_sp)3205*f6aab3d8Srobert void RemoveTarget(TargetSP target_sp) {
3206*f6aab3d8Srobert m_debugger.GetTargetList().DeleteTarget(target_sp);
3207*f6aab3d8Srobert }
3208*f6aab3d8Srobert
CreateTarget(Window & window)3209*f6aab3d8Srobert void CreateTarget(Window &window) {
3210*f6aab3d8Srobert ClearError();
3211*f6aab3d8Srobert
3212*f6aab3d8Srobert bool all_fields_are_valid = CheckFieldsValidity();
3213*f6aab3d8Srobert if (!all_fields_are_valid)
3214*f6aab3d8Srobert return;
3215*f6aab3d8Srobert
3216*f6aab3d8Srobert TargetSP target_sp = GetTarget();
3217*f6aab3d8Srobert if (HasError())
3218*f6aab3d8Srobert return;
3219*f6aab3d8Srobert
3220*f6aab3d8Srobert SetSymbolFile(target_sp);
3221*f6aab3d8Srobert if (HasError()) {
3222*f6aab3d8Srobert RemoveTarget(target_sp);
3223*f6aab3d8Srobert return;
3224*f6aab3d8Srobert }
3225*f6aab3d8Srobert
3226*f6aab3d8Srobert SetCoreFile(target_sp);
3227*f6aab3d8Srobert if (HasError()) {
3228*f6aab3d8Srobert RemoveTarget(target_sp);
3229*f6aab3d8Srobert return;
3230*f6aab3d8Srobert }
3231*f6aab3d8Srobert
3232*f6aab3d8Srobert SetRemoteFile(target_sp);
3233*f6aab3d8Srobert if (HasError()) {
3234*f6aab3d8Srobert RemoveTarget(target_sp);
3235*f6aab3d8Srobert return;
3236*f6aab3d8Srobert }
3237*f6aab3d8Srobert
3238*f6aab3d8Srobert window.GetParent()->RemoveSubWindow(&window);
3239*f6aab3d8Srobert }
3240*f6aab3d8Srobert
3241*f6aab3d8Srobert protected:
3242*f6aab3d8Srobert Debugger &m_debugger;
3243*f6aab3d8Srobert
3244*f6aab3d8Srobert FileFieldDelegate *m_executable_field;
3245*f6aab3d8Srobert FileFieldDelegate *m_core_file_field;
3246*f6aab3d8Srobert FileFieldDelegate *m_symbol_file_field;
3247*f6aab3d8Srobert BooleanFieldDelegate *m_show_advanced_field;
3248*f6aab3d8Srobert FileFieldDelegate *m_remote_file_field;
3249*f6aab3d8Srobert ArchFieldDelegate *m_arch_field;
3250*f6aab3d8Srobert PlatformPluginFieldDelegate *m_platform_field;
3251*f6aab3d8Srobert ChoicesFieldDelegate *m_load_dependent_files_field;
3252*f6aab3d8Srobert };
3253*f6aab3d8Srobert
3254*f6aab3d8Srobert class ProcessLaunchFormDelegate : public FormDelegate {
3255*f6aab3d8Srobert public:
ProcessLaunchFormDelegate(Debugger & debugger,WindowSP main_window_sp)3256*f6aab3d8Srobert ProcessLaunchFormDelegate(Debugger &debugger, WindowSP main_window_sp)
3257*f6aab3d8Srobert : m_debugger(debugger), m_main_window_sp(main_window_sp) {
3258*f6aab3d8Srobert
3259*f6aab3d8Srobert m_arguments_field = AddArgumentsField();
3260*f6aab3d8Srobert SetArgumentsFieldDefaultValue();
3261*f6aab3d8Srobert m_target_environment_field =
3262*f6aab3d8Srobert AddEnvironmentVariableListField("Target Environment Variables");
3263*f6aab3d8Srobert SetTargetEnvironmentFieldDefaultValue();
3264*f6aab3d8Srobert m_working_directory_field = AddDirectoryField(
3265*f6aab3d8Srobert "Working Directory", GetDefaultWorkingDirectory().c_str(), true, false);
3266*f6aab3d8Srobert
3267*f6aab3d8Srobert m_show_advanced_field = AddBooleanField("Show advanced settings.", false);
3268*f6aab3d8Srobert
3269*f6aab3d8Srobert m_stop_at_entry_field = AddBooleanField("Stop at entry point.", false);
3270*f6aab3d8Srobert m_detach_on_error_field =
3271*f6aab3d8Srobert AddBooleanField("Detach on error.", GetDefaultDetachOnError());
3272*f6aab3d8Srobert m_disable_aslr_field =
3273*f6aab3d8Srobert AddBooleanField("Disable ASLR", GetDefaultDisableASLR());
3274*f6aab3d8Srobert m_plugin_field = AddProcessPluginField();
3275*f6aab3d8Srobert m_arch_field = AddArchField("Architecture", "", false);
3276*f6aab3d8Srobert m_shell_field = AddFileField("Shell", "", true, false);
3277*f6aab3d8Srobert m_expand_shell_arguments_field =
3278*f6aab3d8Srobert AddBooleanField("Expand shell arguments.", false);
3279*f6aab3d8Srobert
3280*f6aab3d8Srobert m_disable_standard_io_field =
3281*f6aab3d8Srobert AddBooleanField("Disable Standard IO", GetDefaultDisableStandardIO());
3282*f6aab3d8Srobert m_standard_output_field =
3283*f6aab3d8Srobert AddFileField("Standard Output File", "", /*need_to_exist=*/false,
3284*f6aab3d8Srobert /*required=*/false);
3285*f6aab3d8Srobert m_standard_error_field =
3286*f6aab3d8Srobert AddFileField("Standard Error File", "", /*need_to_exist=*/false,
3287*f6aab3d8Srobert /*required=*/false);
3288*f6aab3d8Srobert m_standard_input_field =
3289*f6aab3d8Srobert AddFileField("Standard Input File", "", /*need_to_exist=*/false,
3290*f6aab3d8Srobert /*required=*/false);
3291*f6aab3d8Srobert
3292*f6aab3d8Srobert m_show_inherited_environment_field =
3293*f6aab3d8Srobert AddBooleanField("Show inherited environment variables.", false);
3294*f6aab3d8Srobert m_inherited_environment_field =
3295*f6aab3d8Srobert AddEnvironmentVariableListField("Inherited Environment Variables");
3296*f6aab3d8Srobert SetInheritedEnvironmentFieldDefaultValue();
3297*f6aab3d8Srobert
3298*f6aab3d8Srobert AddAction("Launch", [this](Window &window) { Launch(window); });
3299*f6aab3d8Srobert }
3300*f6aab3d8Srobert
GetName()3301*f6aab3d8Srobert std::string GetName() override { return "Launch Process"; }
3302*f6aab3d8Srobert
UpdateFieldsVisibility()3303*f6aab3d8Srobert void UpdateFieldsVisibility() override {
3304*f6aab3d8Srobert if (m_show_advanced_field->GetBoolean()) {
3305*f6aab3d8Srobert m_stop_at_entry_field->FieldDelegateShow();
3306*f6aab3d8Srobert m_detach_on_error_field->FieldDelegateShow();
3307*f6aab3d8Srobert m_disable_aslr_field->FieldDelegateShow();
3308*f6aab3d8Srobert m_plugin_field->FieldDelegateShow();
3309*f6aab3d8Srobert m_arch_field->FieldDelegateShow();
3310*f6aab3d8Srobert m_shell_field->FieldDelegateShow();
3311*f6aab3d8Srobert m_expand_shell_arguments_field->FieldDelegateShow();
3312*f6aab3d8Srobert m_disable_standard_io_field->FieldDelegateShow();
3313*f6aab3d8Srobert if (m_disable_standard_io_field->GetBoolean()) {
3314*f6aab3d8Srobert m_standard_input_field->FieldDelegateHide();
3315*f6aab3d8Srobert m_standard_output_field->FieldDelegateHide();
3316*f6aab3d8Srobert m_standard_error_field->FieldDelegateHide();
3317*f6aab3d8Srobert } else {
3318*f6aab3d8Srobert m_standard_input_field->FieldDelegateShow();
3319*f6aab3d8Srobert m_standard_output_field->FieldDelegateShow();
3320*f6aab3d8Srobert m_standard_error_field->FieldDelegateShow();
3321*f6aab3d8Srobert }
3322*f6aab3d8Srobert m_show_inherited_environment_field->FieldDelegateShow();
3323*f6aab3d8Srobert if (m_show_inherited_environment_field->GetBoolean())
3324*f6aab3d8Srobert m_inherited_environment_field->FieldDelegateShow();
3325*f6aab3d8Srobert else
3326*f6aab3d8Srobert m_inherited_environment_field->FieldDelegateHide();
3327*f6aab3d8Srobert } else {
3328*f6aab3d8Srobert m_stop_at_entry_field->FieldDelegateHide();
3329*f6aab3d8Srobert m_detach_on_error_field->FieldDelegateHide();
3330*f6aab3d8Srobert m_disable_aslr_field->FieldDelegateHide();
3331*f6aab3d8Srobert m_plugin_field->FieldDelegateHide();
3332*f6aab3d8Srobert m_arch_field->FieldDelegateHide();
3333*f6aab3d8Srobert m_shell_field->FieldDelegateHide();
3334*f6aab3d8Srobert m_expand_shell_arguments_field->FieldDelegateHide();
3335*f6aab3d8Srobert m_disable_standard_io_field->FieldDelegateHide();
3336*f6aab3d8Srobert m_standard_input_field->FieldDelegateHide();
3337*f6aab3d8Srobert m_standard_output_field->FieldDelegateHide();
3338*f6aab3d8Srobert m_standard_error_field->FieldDelegateHide();
3339*f6aab3d8Srobert m_show_inherited_environment_field->FieldDelegateHide();
3340*f6aab3d8Srobert m_inherited_environment_field->FieldDelegateHide();
3341*f6aab3d8Srobert }
3342*f6aab3d8Srobert }
3343*f6aab3d8Srobert
3344*f6aab3d8Srobert // Methods for setting the default value of the fields.
3345*f6aab3d8Srobert
SetArgumentsFieldDefaultValue()3346*f6aab3d8Srobert void SetArgumentsFieldDefaultValue() {
3347*f6aab3d8Srobert TargetSP target = m_debugger.GetSelectedTarget();
3348*f6aab3d8Srobert if (target == nullptr)
3349*f6aab3d8Srobert return;
3350*f6aab3d8Srobert
3351*f6aab3d8Srobert const Args &target_arguments =
3352*f6aab3d8Srobert target->GetProcessLaunchInfo().GetArguments();
3353*f6aab3d8Srobert m_arguments_field->AddArguments(target_arguments);
3354*f6aab3d8Srobert }
3355*f6aab3d8Srobert
SetTargetEnvironmentFieldDefaultValue()3356*f6aab3d8Srobert void SetTargetEnvironmentFieldDefaultValue() {
3357*f6aab3d8Srobert TargetSP target = m_debugger.GetSelectedTarget();
3358*f6aab3d8Srobert if (target == nullptr)
3359*f6aab3d8Srobert return;
3360*f6aab3d8Srobert
3361*f6aab3d8Srobert const Environment &target_environment = target->GetTargetEnvironment();
3362*f6aab3d8Srobert m_target_environment_field->AddEnvironmentVariables(target_environment);
3363*f6aab3d8Srobert }
3364*f6aab3d8Srobert
SetInheritedEnvironmentFieldDefaultValue()3365*f6aab3d8Srobert void SetInheritedEnvironmentFieldDefaultValue() {
3366*f6aab3d8Srobert TargetSP target = m_debugger.GetSelectedTarget();
3367*f6aab3d8Srobert if (target == nullptr)
3368*f6aab3d8Srobert return;
3369*f6aab3d8Srobert
3370*f6aab3d8Srobert const Environment &inherited_environment =
3371*f6aab3d8Srobert target->GetInheritedEnvironment();
3372*f6aab3d8Srobert m_inherited_environment_field->AddEnvironmentVariables(
3373*f6aab3d8Srobert inherited_environment);
3374*f6aab3d8Srobert }
3375*f6aab3d8Srobert
GetDefaultWorkingDirectory()3376*f6aab3d8Srobert std::string GetDefaultWorkingDirectory() {
3377*f6aab3d8Srobert TargetSP target = m_debugger.GetSelectedTarget();
3378*f6aab3d8Srobert if (target == nullptr)
3379*f6aab3d8Srobert return "";
3380*f6aab3d8Srobert
3381*f6aab3d8Srobert PlatformSP platform = target->GetPlatform();
3382*f6aab3d8Srobert return platform->GetWorkingDirectory().GetPath();
3383*f6aab3d8Srobert }
3384*f6aab3d8Srobert
GetDefaultDisableASLR()3385*f6aab3d8Srobert bool GetDefaultDisableASLR() {
3386*f6aab3d8Srobert TargetSP target = m_debugger.GetSelectedTarget();
3387*f6aab3d8Srobert if (target == nullptr)
3388*f6aab3d8Srobert return false;
3389*f6aab3d8Srobert
3390*f6aab3d8Srobert return target->GetDisableASLR();
3391*f6aab3d8Srobert }
3392*f6aab3d8Srobert
GetDefaultDisableStandardIO()3393*f6aab3d8Srobert bool GetDefaultDisableStandardIO() {
3394*f6aab3d8Srobert TargetSP target = m_debugger.GetSelectedTarget();
3395*f6aab3d8Srobert if (target == nullptr)
3396*f6aab3d8Srobert return true;
3397*f6aab3d8Srobert
3398*f6aab3d8Srobert return target->GetDisableSTDIO();
3399*f6aab3d8Srobert }
3400*f6aab3d8Srobert
GetDefaultDetachOnError()3401*f6aab3d8Srobert bool GetDefaultDetachOnError() {
3402*f6aab3d8Srobert TargetSP target = m_debugger.GetSelectedTarget();
3403*f6aab3d8Srobert if (target == nullptr)
3404*f6aab3d8Srobert return true;
3405*f6aab3d8Srobert
3406*f6aab3d8Srobert return target->GetDetachOnError();
3407*f6aab3d8Srobert }
3408*f6aab3d8Srobert
3409*f6aab3d8Srobert // Methods for getting the necessary information and setting them to the
3410*f6aab3d8Srobert // ProcessLaunchInfo.
3411*f6aab3d8Srobert
GetExecutableSettings(ProcessLaunchInfo & launch_info)3412*f6aab3d8Srobert void GetExecutableSettings(ProcessLaunchInfo &launch_info) {
3413*f6aab3d8Srobert TargetSP target = m_debugger.GetSelectedTarget();
3414*f6aab3d8Srobert ModuleSP executable_module = target->GetExecutableModule();
3415*f6aab3d8Srobert llvm::StringRef target_settings_argv0 = target->GetArg0();
3416*f6aab3d8Srobert
3417*f6aab3d8Srobert if (!target_settings_argv0.empty()) {
3418*f6aab3d8Srobert launch_info.GetArguments().AppendArgument(target_settings_argv0);
3419*f6aab3d8Srobert launch_info.SetExecutableFile(executable_module->GetPlatformFileSpec(),
3420*f6aab3d8Srobert false);
3421*f6aab3d8Srobert return;
3422*f6aab3d8Srobert }
3423*f6aab3d8Srobert
3424*f6aab3d8Srobert launch_info.SetExecutableFile(executable_module->GetPlatformFileSpec(),
3425*f6aab3d8Srobert true);
3426*f6aab3d8Srobert }
3427*f6aab3d8Srobert
GetArguments(ProcessLaunchInfo & launch_info)3428*f6aab3d8Srobert void GetArguments(ProcessLaunchInfo &launch_info) {
3429*f6aab3d8Srobert TargetSP target = m_debugger.GetSelectedTarget();
3430*f6aab3d8Srobert Args arguments = m_arguments_field->GetArguments();
3431*f6aab3d8Srobert launch_info.GetArguments().AppendArguments(arguments);
3432*f6aab3d8Srobert }
3433*f6aab3d8Srobert
GetEnvironment(ProcessLaunchInfo & launch_info)3434*f6aab3d8Srobert void GetEnvironment(ProcessLaunchInfo &launch_info) {
3435*f6aab3d8Srobert Environment target_environment =
3436*f6aab3d8Srobert m_target_environment_field->GetEnvironment();
3437*f6aab3d8Srobert Environment inherited_environment =
3438*f6aab3d8Srobert m_inherited_environment_field->GetEnvironment();
3439*f6aab3d8Srobert launch_info.GetEnvironment().insert(target_environment.begin(),
3440*f6aab3d8Srobert target_environment.end());
3441*f6aab3d8Srobert launch_info.GetEnvironment().insert(inherited_environment.begin(),
3442*f6aab3d8Srobert inherited_environment.end());
3443*f6aab3d8Srobert }
3444*f6aab3d8Srobert
GetWorkingDirectory(ProcessLaunchInfo & launch_info)3445*f6aab3d8Srobert void GetWorkingDirectory(ProcessLaunchInfo &launch_info) {
3446*f6aab3d8Srobert if (m_working_directory_field->IsSpecified())
3447*f6aab3d8Srobert launch_info.SetWorkingDirectory(
3448*f6aab3d8Srobert m_working_directory_field->GetResolvedFileSpec());
3449*f6aab3d8Srobert }
3450*f6aab3d8Srobert
GetStopAtEntry(ProcessLaunchInfo & launch_info)3451*f6aab3d8Srobert void GetStopAtEntry(ProcessLaunchInfo &launch_info) {
3452*f6aab3d8Srobert if (m_stop_at_entry_field->GetBoolean())
3453*f6aab3d8Srobert launch_info.GetFlags().Set(eLaunchFlagStopAtEntry);
3454*f6aab3d8Srobert else
3455*f6aab3d8Srobert launch_info.GetFlags().Clear(eLaunchFlagStopAtEntry);
3456*f6aab3d8Srobert }
3457*f6aab3d8Srobert
GetDetachOnError(ProcessLaunchInfo & launch_info)3458*f6aab3d8Srobert void GetDetachOnError(ProcessLaunchInfo &launch_info) {
3459*f6aab3d8Srobert if (m_detach_on_error_field->GetBoolean())
3460*f6aab3d8Srobert launch_info.GetFlags().Set(eLaunchFlagDetachOnError);
3461*f6aab3d8Srobert else
3462*f6aab3d8Srobert launch_info.GetFlags().Clear(eLaunchFlagDetachOnError);
3463*f6aab3d8Srobert }
3464*f6aab3d8Srobert
GetDisableASLR(ProcessLaunchInfo & launch_info)3465*f6aab3d8Srobert void GetDisableASLR(ProcessLaunchInfo &launch_info) {
3466*f6aab3d8Srobert if (m_disable_aslr_field->GetBoolean())
3467*f6aab3d8Srobert launch_info.GetFlags().Set(eLaunchFlagDisableASLR);
3468*f6aab3d8Srobert else
3469*f6aab3d8Srobert launch_info.GetFlags().Clear(eLaunchFlagDisableASLR);
3470*f6aab3d8Srobert }
3471*f6aab3d8Srobert
GetPlugin(ProcessLaunchInfo & launch_info)3472*f6aab3d8Srobert void GetPlugin(ProcessLaunchInfo &launch_info) {
3473*f6aab3d8Srobert launch_info.SetProcessPluginName(m_plugin_field->GetPluginName());
3474*f6aab3d8Srobert }
3475*f6aab3d8Srobert
GetArch(ProcessLaunchInfo & launch_info)3476*f6aab3d8Srobert void GetArch(ProcessLaunchInfo &launch_info) {
3477*f6aab3d8Srobert if (!m_arch_field->IsSpecified())
3478*f6aab3d8Srobert return;
3479*f6aab3d8Srobert
3480*f6aab3d8Srobert TargetSP target_sp = m_debugger.GetSelectedTarget();
3481*f6aab3d8Srobert PlatformSP platform_sp =
3482*f6aab3d8Srobert target_sp ? target_sp->GetPlatform() : PlatformSP();
3483*f6aab3d8Srobert launch_info.GetArchitecture() = Platform::GetAugmentedArchSpec(
3484*f6aab3d8Srobert platform_sp.get(), m_arch_field->GetArchString());
3485*f6aab3d8Srobert }
3486*f6aab3d8Srobert
GetShell(ProcessLaunchInfo & launch_info)3487*f6aab3d8Srobert void GetShell(ProcessLaunchInfo &launch_info) {
3488*f6aab3d8Srobert if (!m_shell_field->IsSpecified())
3489*f6aab3d8Srobert return;
3490*f6aab3d8Srobert
3491*f6aab3d8Srobert launch_info.SetShell(m_shell_field->GetResolvedFileSpec());
3492*f6aab3d8Srobert launch_info.SetShellExpandArguments(
3493*f6aab3d8Srobert m_expand_shell_arguments_field->GetBoolean());
3494*f6aab3d8Srobert }
3495*f6aab3d8Srobert
GetStandardIO(ProcessLaunchInfo & launch_info)3496*f6aab3d8Srobert void GetStandardIO(ProcessLaunchInfo &launch_info) {
3497*f6aab3d8Srobert if (m_disable_standard_io_field->GetBoolean()) {
3498*f6aab3d8Srobert launch_info.GetFlags().Set(eLaunchFlagDisableSTDIO);
3499*f6aab3d8Srobert return;
3500*f6aab3d8Srobert }
3501*f6aab3d8Srobert
3502*f6aab3d8Srobert FileAction action;
3503*f6aab3d8Srobert if (m_standard_input_field->IsSpecified()) {
3504*f6aab3d8Srobert if (action.Open(STDIN_FILENO, m_standard_input_field->GetFileSpec(), true,
3505*f6aab3d8Srobert false))
3506*f6aab3d8Srobert launch_info.AppendFileAction(action);
3507*f6aab3d8Srobert }
3508*f6aab3d8Srobert if (m_standard_output_field->IsSpecified()) {
3509*f6aab3d8Srobert if (action.Open(STDOUT_FILENO, m_standard_output_field->GetFileSpec(),
3510*f6aab3d8Srobert false, true))
3511*f6aab3d8Srobert launch_info.AppendFileAction(action);
3512*f6aab3d8Srobert }
3513*f6aab3d8Srobert if (m_standard_error_field->IsSpecified()) {
3514*f6aab3d8Srobert if (action.Open(STDERR_FILENO, m_standard_error_field->GetFileSpec(),
3515*f6aab3d8Srobert false, true))
3516*f6aab3d8Srobert launch_info.AppendFileAction(action);
3517*f6aab3d8Srobert }
3518*f6aab3d8Srobert }
3519*f6aab3d8Srobert
GetInheritTCC(ProcessLaunchInfo & launch_info)3520*f6aab3d8Srobert void GetInheritTCC(ProcessLaunchInfo &launch_info) {
3521*f6aab3d8Srobert if (m_debugger.GetSelectedTarget()->GetInheritTCC())
3522*f6aab3d8Srobert launch_info.GetFlags().Set(eLaunchFlagInheritTCCFromParent);
3523*f6aab3d8Srobert }
3524*f6aab3d8Srobert
GetLaunchInfo()3525*f6aab3d8Srobert ProcessLaunchInfo GetLaunchInfo() {
3526*f6aab3d8Srobert ProcessLaunchInfo launch_info;
3527*f6aab3d8Srobert
3528*f6aab3d8Srobert GetExecutableSettings(launch_info);
3529*f6aab3d8Srobert GetArguments(launch_info);
3530*f6aab3d8Srobert GetEnvironment(launch_info);
3531*f6aab3d8Srobert GetWorkingDirectory(launch_info);
3532*f6aab3d8Srobert GetStopAtEntry(launch_info);
3533*f6aab3d8Srobert GetDetachOnError(launch_info);
3534*f6aab3d8Srobert GetDisableASLR(launch_info);
3535*f6aab3d8Srobert GetPlugin(launch_info);
3536*f6aab3d8Srobert GetArch(launch_info);
3537*f6aab3d8Srobert GetShell(launch_info);
3538*f6aab3d8Srobert GetStandardIO(launch_info);
3539*f6aab3d8Srobert GetInheritTCC(launch_info);
3540*f6aab3d8Srobert
3541*f6aab3d8Srobert return launch_info;
3542*f6aab3d8Srobert }
3543*f6aab3d8Srobert
StopRunningProcess()3544*f6aab3d8Srobert bool StopRunningProcess() {
3545*f6aab3d8Srobert ExecutionContext exe_ctx =
3546*f6aab3d8Srobert m_debugger.GetCommandInterpreter().GetExecutionContext();
3547*f6aab3d8Srobert
3548*f6aab3d8Srobert if (!exe_ctx.HasProcessScope())
3549*f6aab3d8Srobert return false;
3550*f6aab3d8Srobert
3551*f6aab3d8Srobert Process *process = exe_ctx.GetProcessPtr();
3552*f6aab3d8Srobert if (!(process && process->IsAlive()))
3553*f6aab3d8Srobert return false;
3554*f6aab3d8Srobert
3555*f6aab3d8Srobert FormDelegateSP form_delegate_sp =
3556*f6aab3d8Srobert FormDelegateSP(new DetachOrKillProcessFormDelegate(process));
3557*f6aab3d8Srobert Rect bounds = m_main_window_sp->GetCenteredRect(85, 8);
3558*f6aab3d8Srobert WindowSP form_window_sp = m_main_window_sp->CreateSubWindow(
3559*f6aab3d8Srobert form_delegate_sp->GetName().c_str(), bounds, true);
3560*f6aab3d8Srobert WindowDelegateSP window_delegate_sp =
3561*f6aab3d8Srobert WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
3562*f6aab3d8Srobert form_window_sp->SetDelegate(window_delegate_sp);
3563*f6aab3d8Srobert
3564*f6aab3d8Srobert return true;
3565*f6aab3d8Srobert }
3566*f6aab3d8Srobert
GetTarget()3567*f6aab3d8Srobert Target *GetTarget() {
3568*f6aab3d8Srobert Target *target = m_debugger.GetSelectedTarget().get();
3569*f6aab3d8Srobert
3570*f6aab3d8Srobert if (target == nullptr) {
3571*f6aab3d8Srobert SetError("No target exists!");
3572*f6aab3d8Srobert return nullptr;
3573*f6aab3d8Srobert }
3574*f6aab3d8Srobert
3575*f6aab3d8Srobert ModuleSP exe_module_sp = target->GetExecutableModule();
3576*f6aab3d8Srobert
3577*f6aab3d8Srobert if (exe_module_sp == nullptr) {
3578*f6aab3d8Srobert SetError("No executable in target!");
3579*f6aab3d8Srobert return nullptr;
3580*f6aab3d8Srobert }
3581*f6aab3d8Srobert
3582*f6aab3d8Srobert return target;
3583*f6aab3d8Srobert }
3584*f6aab3d8Srobert
Launch(Window & window)3585*f6aab3d8Srobert void Launch(Window &window) {
3586*f6aab3d8Srobert ClearError();
3587*f6aab3d8Srobert
3588*f6aab3d8Srobert bool all_fields_are_valid = CheckFieldsValidity();
3589*f6aab3d8Srobert if (!all_fields_are_valid)
3590*f6aab3d8Srobert return;
3591*f6aab3d8Srobert
3592*f6aab3d8Srobert bool process_is_running = StopRunningProcess();
3593*f6aab3d8Srobert if (process_is_running)
3594*f6aab3d8Srobert return;
3595*f6aab3d8Srobert
3596*f6aab3d8Srobert Target *target = GetTarget();
3597*f6aab3d8Srobert if (HasError())
3598*f6aab3d8Srobert return;
3599*f6aab3d8Srobert
3600*f6aab3d8Srobert StreamString stream;
3601*f6aab3d8Srobert ProcessLaunchInfo launch_info = GetLaunchInfo();
3602*f6aab3d8Srobert Status status = target->Launch(launch_info, &stream);
3603*f6aab3d8Srobert
3604*f6aab3d8Srobert if (status.Fail()) {
3605*f6aab3d8Srobert SetError(status.AsCString());
3606*f6aab3d8Srobert return;
3607*f6aab3d8Srobert }
3608*f6aab3d8Srobert
3609*f6aab3d8Srobert ProcessSP process_sp(target->GetProcessSP());
3610*f6aab3d8Srobert if (!process_sp) {
3611*f6aab3d8Srobert SetError("Launched successfully but target has no process!");
3612*f6aab3d8Srobert return;
3613*f6aab3d8Srobert }
3614*f6aab3d8Srobert
3615*f6aab3d8Srobert window.GetParent()->RemoveSubWindow(&window);
3616*f6aab3d8Srobert }
3617*f6aab3d8Srobert
3618*f6aab3d8Srobert protected:
3619*f6aab3d8Srobert Debugger &m_debugger;
3620*f6aab3d8Srobert WindowSP m_main_window_sp;
3621*f6aab3d8Srobert
3622*f6aab3d8Srobert ArgumentsFieldDelegate *m_arguments_field;
3623*f6aab3d8Srobert EnvironmentVariableListFieldDelegate *m_target_environment_field;
3624*f6aab3d8Srobert DirectoryFieldDelegate *m_working_directory_field;
3625*f6aab3d8Srobert
3626*f6aab3d8Srobert BooleanFieldDelegate *m_show_advanced_field;
3627*f6aab3d8Srobert
3628*f6aab3d8Srobert BooleanFieldDelegate *m_stop_at_entry_field;
3629*f6aab3d8Srobert BooleanFieldDelegate *m_detach_on_error_field;
3630*f6aab3d8Srobert BooleanFieldDelegate *m_disable_aslr_field;
3631*f6aab3d8Srobert ProcessPluginFieldDelegate *m_plugin_field;
3632*f6aab3d8Srobert ArchFieldDelegate *m_arch_field;
3633*f6aab3d8Srobert FileFieldDelegate *m_shell_field;
3634*f6aab3d8Srobert BooleanFieldDelegate *m_expand_shell_arguments_field;
3635*f6aab3d8Srobert BooleanFieldDelegate *m_disable_standard_io_field;
3636*f6aab3d8Srobert FileFieldDelegate *m_standard_input_field;
3637*f6aab3d8Srobert FileFieldDelegate *m_standard_output_field;
3638*f6aab3d8Srobert FileFieldDelegate *m_standard_error_field;
3639*f6aab3d8Srobert
3640*f6aab3d8Srobert BooleanFieldDelegate *m_show_inherited_environment_field;
3641*f6aab3d8Srobert EnvironmentVariableListFieldDelegate *m_inherited_environment_field;
3642*f6aab3d8Srobert };
3643*f6aab3d8Srobert
3644*f6aab3d8Srobert ////////////
3645*f6aab3d8Srobert // Searchers
3646*f6aab3d8Srobert ////////////
3647*f6aab3d8Srobert
3648*f6aab3d8Srobert class SearcherDelegate {
3649*f6aab3d8Srobert public:
3650*f6aab3d8Srobert SearcherDelegate() = default;
3651*f6aab3d8Srobert
3652*f6aab3d8Srobert virtual ~SearcherDelegate() = default;
3653*f6aab3d8Srobert
3654*f6aab3d8Srobert virtual int GetNumberOfMatches() = 0;
3655*f6aab3d8Srobert
3656*f6aab3d8Srobert // Get the string that will be displayed for the match at the input index.
3657*f6aab3d8Srobert virtual const std::string &GetMatchTextAtIndex(int index) = 0;
3658*f6aab3d8Srobert
3659*f6aab3d8Srobert // Update the matches of the search. This is executed every time the text
3660*f6aab3d8Srobert // field handles an event.
3661*f6aab3d8Srobert virtual void UpdateMatches(const std::string &text) = 0;
3662*f6aab3d8Srobert
3663*f6aab3d8Srobert // Execute the user callback given the index of some match. This is executed
3664*f6aab3d8Srobert // once the user selects a match.
3665*f6aab3d8Srobert virtual void ExecuteCallback(int match_index) = 0;
3666*f6aab3d8Srobert };
3667*f6aab3d8Srobert
3668*f6aab3d8Srobert typedef std::shared_ptr<SearcherDelegate> SearcherDelegateSP;
3669*f6aab3d8Srobert
3670*f6aab3d8Srobert class SearcherWindowDelegate : public WindowDelegate {
3671*f6aab3d8Srobert public:
SearcherWindowDelegate(SearcherDelegateSP & delegate_sp)3672*f6aab3d8Srobert SearcherWindowDelegate(SearcherDelegateSP &delegate_sp)
3673*f6aab3d8Srobert : m_delegate_sp(delegate_sp), m_text_field("Search", "", false) {
3674*f6aab3d8Srobert ;
3675*f6aab3d8Srobert }
3676*f6aab3d8Srobert
3677*f6aab3d8Srobert // A completion window is padded by one character from all sides. A text field
3678*f6aab3d8Srobert // is first drawn for inputting the searcher request, then a list of matches
3679*f6aab3d8Srobert // are displayed in a scrollable list.
3680*f6aab3d8Srobert //
3681*f6aab3d8Srobert // ___<Searcher Window Name>____________________________
3682*f6aab3d8Srobert // | |
3683*f6aab3d8Srobert // | __[Search]_______________________________________ |
3684*f6aab3d8Srobert // | | | |
3685*f6aab3d8Srobert // | |_______________________________________________| |
3686*f6aab3d8Srobert // | - Match 1. |
3687*f6aab3d8Srobert // | - Match 2. |
3688*f6aab3d8Srobert // | - ... |
3689*f6aab3d8Srobert // | |
3690*f6aab3d8Srobert // |____________________________[Press Esc to Cancel]__|
3691*f6aab3d8Srobert //
3692*f6aab3d8Srobert
3693*f6aab3d8Srobert // Get the index of the last visible match. Assuming at least one match
3694*f6aab3d8Srobert // exists.
GetLastVisibleMatch(int height)3695*f6aab3d8Srobert int GetLastVisibleMatch(int height) {
3696*f6aab3d8Srobert int index = m_first_visible_match + height;
3697*f6aab3d8Srobert return std::min(index, m_delegate_sp->GetNumberOfMatches()) - 1;
3698*f6aab3d8Srobert }
3699*f6aab3d8Srobert
GetNumberOfVisibleMatches(int height)3700*f6aab3d8Srobert int GetNumberOfVisibleMatches(int height) {
3701*f6aab3d8Srobert return GetLastVisibleMatch(height) - m_first_visible_match + 1;
3702*f6aab3d8Srobert }
3703*f6aab3d8Srobert
UpdateScrolling(Surface & surface)3704*f6aab3d8Srobert void UpdateScrolling(Surface &surface) {
3705*f6aab3d8Srobert if (m_selected_match < m_first_visible_match) {
3706*f6aab3d8Srobert m_first_visible_match = m_selected_match;
3707*f6aab3d8Srobert return;
3708*f6aab3d8Srobert }
3709*f6aab3d8Srobert
3710*f6aab3d8Srobert int height = surface.GetHeight();
3711*f6aab3d8Srobert int last_visible_match = GetLastVisibleMatch(height);
3712*f6aab3d8Srobert if (m_selected_match > last_visible_match) {
3713*f6aab3d8Srobert m_first_visible_match = m_selected_match - height + 1;
3714*f6aab3d8Srobert }
3715*f6aab3d8Srobert }
3716*f6aab3d8Srobert
DrawMatches(Surface & surface)3717*f6aab3d8Srobert void DrawMatches(Surface &surface) {
3718*f6aab3d8Srobert if (m_delegate_sp->GetNumberOfMatches() == 0)
3719*f6aab3d8Srobert return;
3720*f6aab3d8Srobert
3721*f6aab3d8Srobert UpdateScrolling(surface);
3722*f6aab3d8Srobert
3723*f6aab3d8Srobert int count = GetNumberOfVisibleMatches(surface.GetHeight());
3724*f6aab3d8Srobert for (int i = 0; i < count; i++) {
3725*f6aab3d8Srobert surface.MoveCursor(1, i);
3726*f6aab3d8Srobert int current_match = m_first_visible_match + i;
3727*f6aab3d8Srobert if (current_match == m_selected_match)
3728*f6aab3d8Srobert surface.AttributeOn(A_REVERSE);
3729*f6aab3d8Srobert surface.PutCString(
3730*f6aab3d8Srobert m_delegate_sp->GetMatchTextAtIndex(current_match).c_str());
3731*f6aab3d8Srobert if (current_match == m_selected_match)
3732*f6aab3d8Srobert surface.AttributeOff(A_REVERSE);
3733*f6aab3d8Srobert }
3734*f6aab3d8Srobert }
3735*f6aab3d8Srobert
DrawContent(Surface & surface)3736*f6aab3d8Srobert void DrawContent(Surface &surface) {
3737*f6aab3d8Srobert Rect content_bounds = surface.GetFrame();
3738*f6aab3d8Srobert Rect text_field_bounds, matchs_bounds;
3739*f6aab3d8Srobert content_bounds.HorizontalSplit(m_text_field.FieldDelegateGetHeight(),
3740*f6aab3d8Srobert text_field_bounds, matchs_bounds);
3741*f6aab3d8Srobert Surface text_field_surface = surface.SubSurface(text_field_bounds);
3742*f6aab3d8Srobert Surface matches_surface = surface.SubSurface(matchs_bounds);
3743*f6aab3d8Srobert
3744*f6aab3d8Srobert m_text_field.FieldDelegateDraw(text_field_surface, true);
3745*f6aab3d8Srobert DrawMatches(matches_surface);
3746*f6aab3d8Srobert }
3747*f6aab3d8Srobert
WindowDelegateDraw(Window & window,bool force)3748*f6aab3d8Srobert bool WindowDelegateDraw(Window &window, bool force) override {
3749*f6aab3d8Srobert window.Erase();
3750*f6aab3d8Srobert
3751*f6aab3d8Srobert window.DrawTitleBox(window.GetName(), "Press Esc to Cancel");
3752*f6aab3d8Srobert
3753*f6aab3d8Srobert Rect content_bounds = window.GetFrame();
3754*f6aab3d8Srobert content_bounds.Inset(2, 2);
3755*f6aab3d8Srobert Surface content_surface = window.SubSurface(content_bounds);
3756*f6aab3d8Srobert
3757*f6aab3d8Srobert DrawContent(content_surface);
3758*f6aab3d8Srobert return true;
3759*f6aab3d8Srobert }
3760*f6aab3d8Srobert
SelectNext()3761*f6aab3d8Srobert void SelectNext() {
3762*f6aab3d8Srobert if (m_selected_match != m_delegate_sp->GetNumberOfMatches() - 1)
3763*f6aab3d8Srobert m_selected_match++;
3764*f6aab3d8Srobert }
3765*f6aab3d8Srobert
SelectPrevious()3766*f6aab3d8Srobert void SelectPrevious() {
3767*f6aab3d8Srobert if (m_selected_match != 0)
3768*f6aab3d8Srobert m_selected_match--;
3769*f6aab3d8Srobert }
3770*f6aab3d8Srobert
ExecuteCallback(Window & window)3771*f6aab3d8Srobert void ExecuteCallback(Window &window) {
3772*f6aab3d8Srobert m_delegate_sp->ExecuteCallback(m_selected_match);
3773*f6aab3d8Srobert window.GetParent()->RemoveSubWindow(&window);
3774*f6aab3d8Srobert }
3775*f6aab3d8Srobert
UpdateMatches()3776*f6aab3d8Srobert void UpdateMatches() {
3777*f6aab3d8Srobert m_delegate_sp->UpdateMatches(m_text_field.GetText());
3778*f6aab3d8Srobert m_selected_match = 0;
3779*f6aab3d8Srobert }
3780*f6aab3d8Srobert
WindowDelegateHandleChar(Window & window,int key)3781*f6aab3d8Srobert HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
3782*f6aab3d8Srobert switch (key) {
3783*f6aab3d8Srobert case '\r':
3784*f6aab3d8Srobert case '\n':
3785*f6aab3d8Srobert case KEY_ENTER:
3786*f6aab3d8Srobert ExecuteCallback(window);
3787*f6aab3d8Srobert return eKeyHandled;
3788*f6aab3d8Srobert case '\t':
3789*f6aab3d8Srobert case KEY_DOWN:
3790*f6aab3d8Srobert SelectNext();
3791*f6aab3d8Srobert return eKeyHandled;
3792*f6aab3d8Srobert case KEY_SHIFT_TAB:
3793*f6aab3d8Srobert case KEY_UP:
3794*f6aab3d8Srobert SelectPrevious();
3795*f6aab3d8Srobert return eKeyHandled;
3796*f6aab3d8Srobert case KEY_ESCAPE:
3797*f6aab3d8Srobert window.GetParent()->RemoveSubWindow(&window);
3798*f6aab3d8Srobert return eKeyHandled;
3799*f6aab3d8Srobert default:
3800*f6aab3d8Srobert break;
3801*f6aab3d8Srobert }
3802*f6aab3d8Srobert
3803*f6aab3d8Srobert if (m_text_field.FieldDelegateHandleChar(key) == eKeyHandled)
3804*f6aab3d8Srobert UpdateMatches();
3805*f6aab3d8Srobert
3806*f6aab3d8Srobert return eKeyHandled;
3807*f6aab3d8Srobert }
3808*f6aab3d8Srobert
3809*f6aab3d8Srobert protected:
3810*f6aab3d8Srobert SearcherDelegateSP m_delegate_sp;
3811*f6aab3d8Srobert TextFieldDelegate m_text_field;
3812*f6aab3d8Srobert // The index of the currently selected match.
3813*f6aab3d8Srobert int m_selected_match = 0;
3814*f6aab3d8Srobert // The index of the first visible match.
3815*f6aab3d8Srobert int m_first_visible_match = 0;
3816*f6aab3d8Srobert };
3817*f6aab3d8Srobert
3818*f6aab3d8Srobert //////////////////////////////
3819*f6aab3d8Srobert // Searcher Delegate Instances
3820*f6aab3d8Srobert //////////////////////////////
3821*f6aab3d8Srobert
3822*f6aab3d8Srobert // This is a searcher delegate wrapper around CommandCompletions common
3823*f6aab3d8Srobert // callbacks. The callbacks are only given the match string. The completion_mask
3824*f6aab3d8Srobert // can be a combination of CommonCompletionTypes.
3825*f6aab3d8Srobert class CommonCompletionSearcherDelegate : public SearcherDelegate {
3826*f6aab3d8Srobert public:
3827*f6aab3d8Srobert typedef std::function<void(const std::string &)> CallbackType;
3828*f6aab3d8Srobert
CommonCompletionSearcherDelegate(Debugger & debugger,uint32_t completion_mask,CallbackType callback)3829*f6aab3d8Srobert CommonCompletionSearcherDelegate(Debugger &debugger, uint32_t completion_mask,
3830*f6aab3d8Srobert CallbackType callback)
3831*f6aab3d8Srobert : m_debugger(debugger), m_completion_mask(completion_mask),
3832*f6aab3d8Srobert m_callback(callback) {}
3833*f6aab3d8Srobert
GetNumberOfMatches()3834*f6aab3d8Srobert int GetNumberOfMatches() override { return m_matches.GetSize(); }
3835*f6aab3d8Srobert
GetMatchTextAtIndex(int index)3836*f6aab3d8Srobert const std::string &GetMatchTextAtIndex(int index) override {
3837*f6aab3d8Srobert return m_matches[index];
3838*f6aab3d8Srobert }
3839*f6aab3d8Srobert
UpdateMatches(const std::string & text)3840*f6aab3d8Srobert void UpdateMatches(const std::string &text) override {
3841*f6aab3d8Srobert CompletionResult result;
3842*f6aab3d8Srobert CompletionRequest request(text.c_str(), text.size(), result);
3843*f6aab3d8Srobert CommandCompletions::InvokeCommonCompletionCallbacks(
3844*f6aab3d8Srobert m_debugger.GetCommandInterpreter(), m_completion_mask, request,
3845*f6aab3d8Srobert nullptr);
3846*f6aab3d8Srobert result.GetMatches(m_matches);
3847*f6aab3d8Srobert }
3848*f6aab3d8Srobert
ExecuteCallback(int match_index)3849*f6aab3d8Srobert void ExecuteCallback(int match_index) override {
3850*f6aab3d8Srobert m_callback(m_matches[match_index]);
3851*f6aab3d8Srobert }
3852*f6aab3d8Srobert
3853*f6aab3d8Srobert protected:
3854*f6aab3d8Srobert Debugger &m_debugger;
3855*f6aab3d8Srobert // A compound mask from CommonCompletionTypes.
3856*f6aab3d8Srobert uint32_t m_completion_mask;
3857*f6aab3d8Srobert // A callback to execute once the user selects a match. The match is passed to
3858*f6aab3d8Srobert // the callback as a string.
3859*f6aab3d8Srobert CallbackType m_callback;
3860*f6aab3d8Srobert StringList m_matches;
3861*f6aab3d8Srobert };
3862*f6aab3d8Srobert
3863*f6aab3d8Srobert ////////
3864*f6aab3d8Srobert // Menus
3865*f6aab3d8Srobert ////////
3866*f6aab3d8Srobert
3867061da546Spatrick class MenuDelegate {
3868061da546Spatrick public:
3869061da546Spatrick virtual ~MenuDelegate() = default;
3870061da546Spatrick
3871061da546Spatrick virtual MenuActionResult MenuDelegateAction(Menu &menu) = 0;
3872061da546Spatrick };
3873061da546Spatrick
3874061da546Spatrick class Menu : public WindowDelegate {
3875061da546Spatrick public:
3876061da546Spatrick enum class Type { Invalid, Bar, Item, Separator };
3877061da546Spatrick
3878061da546Spatrick // Menubar or separator constructor
3879061da546Spatrick Menu(Type type);
3880061da546Spatrick
3881061da546Spatrick // Menuitem constructor
3882061da546Spatrick Menu(const char *name, const char *key_name, int key_value,
3883061da546Spatrick uint64_t identifier);
3884061da546Spatrick
3885061da546Spatrick ~Menu() override = default;
3886061da546Spatrick
GetDelegate() const3887061da546Spatrick const MenuDelegateSP &GetDelegate() const { return m_delegate_sp; }
3888061da546Spatrick
SetDelegate(const MenuDelegateSP & delegate_sp)3889061da546Spatrick void SetDelegate(const MenuDelegateSP &delegate_sp) {
3890061da546Spatrick m_delegate_sp = delegate_sp;
3891061da546Spatrick }
3892061da546Spatrick
3893061da546Spatrick void RecalculateNameLengths();
3894061da546Spatrick
3895061da546Spatrick void AddSubmenu(const MenuSP &menu_sp);
3896061da546Spatrick
3897061da546Spatrick int DrawAndRunMenu(Window &window);
3898061da546Spatrick
3899061da546Spatrick void DrawMenuTitle(Window &window, bool highlight);
3900061da546Spatrick
3901061da546Spatrick bool WindowDelegateDraw(Window &window, bool force) override;
3902061da546Spatrick
3903061da546Spatrick HandleCharResult WindowDelegateHandleChar(Window &window, int key) override;
3904061da546Spatrick
ActionPrivate(Menu & menu)3905061da546Spatrick MenuActionResult ActionPrivate(Menu &menu) {
3906061da546Spatrick MenuActionResult result = MenuActionResult::NotHandled;
3907061da546Spatrick if (m_delegate_sp) {
3908061da546Spatrick result = m_delegate_sp->MenuDelegateAction(menu);
3909061da546Spatrick if (result != MenuActionResult::NotHandled)
3910061da546Spatrick return result;
3911061da546Spatrick } else if (m_parent) {
3912061da546Spatrick result = m_parent->ActionPrivate(menu);
3913061da546Spatrick if (result != MenuActionResult::NotHandled)
3914061da546Spatrick return result;
3915061da546Spatrick }
3916061da546Spatrick return m_canned_result;
3917061da546Spatrick }
3918061da546Spatrick
Action()3919061da546Spatrick MenuActionResult Action() {
3920061da546Spatrick // Call the recursive action so it can try to handle it with the menu
3921061da546Spatrick // delegate, and if not, try our parent menu
3922061da546Spatrick return ActionPrivate(*this);
3923061da546Spatrick }
3924061da546Spatrick
SetCannedResult(MenuActionResult result)3925061da546Spatrick void SetCannedResult(MenuActionResult result) { m_canned_result = result; }
3926061da546Spatrick
GetSubmenus()3927061da546Spatrick Menus &GetSubmenus() { return m_submenus; }
3928061da546Spatrick
GetSubmenus() const3929061da546Spatrick const Menus &GetSubmenus() const { return m_submenus; }
3930061da546Spatrick
GetSelectedSubmenuIndex() const3931061da546Spatrick int GetSelectedSubmenuIndex() const { return m_selected; }
3932061da546Spatrick
SetSelectedSubmenuIndex(int idx)3933061da546Spatrick void SetSelectedSubmenuIndex(int idx) { m_selected = idx; }
3934061da546Spatrick
GetType() const3935061da546Spatrick Type GetType() const { return m_type; }
3936061da546Spatrick
GetStartingColumn() const3937061da546Spatrick int GetStartingColumn() const { return m_start_col; }
3938061da546Spatrick
SetStartingColumn(int col)3939061da546Spatrick void SetStartingColumn(int col) { m_start_col = col; }
3940061da546Spatrick
GetKeyValue() const3941061da546Spatrick int GetKeyValue() const { return m_key_value; }
3942061da546Spatrick
GetName()3943061da546Spatrick std::string &GetName() { return m_name; }
3944061da546Spatrick
GetDrawWidth() const3945061da546Spatrick int GetDrawWidth() const {
3946061da546Spatrick return m_max_submenu_name_length + m_max_submenu_key_name_length + 8;
3947061da546Spatrick }
3948061da546Spatrick
GetIdentifier() const3949061da546Spatrick uint64_t GetIdentifier() const { return m_identifier; }
3950061da546Spatrick
SetIdentifier(uint64_t identifier)3951061da546Spatrick void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
3952061da546Spatrick
3953061da546Spatrick protected:
3954061da546Spatrick std::string m_name;
3955061da546Spatrick std::string m_key_name;
3956061da546Spatrick uint64_t m_identifier;
3957061da546Spatrick Type m_type;
3958061da546Spatrick int m_key_value;
3959061da546Spatrick int m_start_col;
3960061da546Spatrick int m_max_submenu_name_length;
3961061da546Spatrick int m_max_submenu_key_name_length;
3962061da546Spatrick int m_selected;
3963061da546Spatrick Menu *m_parent;
3964061da546Spatrick Menus m_submenus;
3965061da546Spatrick WindowSP m_menu_window_sp;
3966061da546Spatrick MenuActionResult m_canned_result;
3967061da546Spatrick MenuDelegateSP m_delegate_sp;
3968061da546Spatrick };
3969061da546Spatrick
3970061da546Spatrick // Menubar or separator constructor
Menu(Type type)3971061da546Spatrick Menu::Menu(Type type)
3972061da546Spatrick : m_name(), m_key_name(), m_identifier(0), m_type(type), m_key_value(0),
3973061da546Spatrick m_start_col(0), m_max_submenu_name_length(0),
3974061da546Spatrick m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr),
3975061da546Spatrick m_submenus(), m_canned_result(MenuActionResult::NotHandled),
3976061da546Spatrick m_delegate_sp() {}
3977061da546Spatrick
3978061da546Spatrick // Menuitem constructor
Menu(const char * name,const char * key_name,int key_value,uint64_t identifier)3979061da546Spatrick Menu::Menu(const char *name, const char *key_name, int key_value,
3980061da546Spatrick uint64_t identifier)
3981061da546Spatrick : m_name(), m_key_name(), m_identifier(identifier), m_type(Type::Invalid),
3982061da546Spatrick m_key_value(key_value), m_start_col(0), m_max_submenu_name_length(0),
3983061da546Spatrick m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr),
3984061da546Spatrick m_submenus(), m_canned_result(MenuActionResult::NotHandled),
3985061da546Spatrick m_delegate_sp() {
3986061da546Spatrick if (name && name[0]) {
3987061da546Spatrick m_name = name;
3988061da546Spatrick m_type = Type::Item;
3989061da546Spatrick if (key_name && key_name[0])
3990061da546Spatrick m_key_name = key_name;
3991061da546Spatrick } else {
3992061da546Spatrick m_type = Type::Separator;
3993061da546Spatrick }
3994061da546Spatrick }
3995061da546Spatrick
RecalculateNameLengths()3996061da546Spatrick void Menu::RecalculateNameLengths() {
3997061da546Spatrick m_max_submenu_name_length = 0;
3998061da546Spatrick m_max_submenu_key_name_length = 0;
3999061da546Spatrick Menus &submenus = GetSubmenus();
4000061da546Spatrick const size_t num_submenus = submenus.size();
4001061da546Spatrick for (size_t i = 0; i < num_submenus; ++i) {
4002061da546Spatrick Menu *submenu = submenus[i].get();
4003061da546Spatrick if (static_cast<size_t>(m_max_submenu_name_length) < submenu->m_name.size())
4004061da546Spatrick m_max_submenu_name_length = submenu->m_name.size();
4005061da546Spatrick if (static_cast<size_t>(m_max_submenu_key_name_length) <
4006061da546Spatrick submenu->m_key_name.size())
4007061da546Spatrick m_max_submenu_key_name_length = submenu->m_key_name.size();
4008061da546Spatrick }
4009061da546Spatrick }
4010061da546Spatrick
AddSubmenu(const MenuSP & menu_sp)4011061da546Spatrick void Menu::AddSubmenu(const MenuSP &menu_sp) {
4012061da546Spatrick menu_sp->m_parent = this;
4013061da546Spatrick if (static_cast<size_t>(m_max_submenu_name_length) < menu_sp->m_name.size())
4014061da546Spatrick m_max_submenu_name_length = menu_sp->m_name.size();
4015061da546Spatrick if (static_cast<size_t>(m_max_submenu_key_name_length) <
4016061da546Spatrick menu_sp->m_key_name.size())
4017061da546Spatrick m_max_submenu_key_name_length = menu_sp->m_key_name.size();
4018061da546Spatrick m_submenus.push_back(menu_sp);
4019061da546Spatrick }
4020061da546Spatrick
DrawMenuTitle(Window & window,bool highlight)4021061da546Spatrick void Menu::DrawMenuTitle(Window &window, bool highlight) {
4022061da546Spatrick if (m_type == Type::Separator) {
4023061da546Spatrick window.MoveCursor(0, window.GetCursorY());
4024061da546Spatrick window.PutChar(ACS_LTEE);
4025061da546Spatrick int width = window.GetWidth();
4026061da546Spatrick if (width > 2) {
4027061da546Spatrick width -= 2;
4028061da546Spatrick for (int i = 0; i < width; ++i)
4029061da546Spatrick window.PutChar(ACS_HLINE);
4030061da546Spatrick }
4031061da546Spatrick window.PutChar(ACS_RTEE);
4032061da546Spatrick } else {
4033061da546Spatrick const int shortcut_key = m_key_value;
4034061da546Spatrick bool underlined_shortcut = false;
4035be691f3bSpatrick const attr_t highlight_attr = A_REVERSE;
4036061da546Spatrick if (highlight)
4037be691f3bSpatrick window.AttributeOn(highlight_attr);
4038dda28197Spatrick if (llvm::isPrint(shortcut_key)) {
4039061da546Spatrick size_t lower_pos = m_name.find(tolower(shortcut_key));
4040061da546Spatrick size_t upper_pos = m_name.find(toupper(shortcut_key));
4041061da546Spatrick const char *name = m_name.c_str();
4042061da546Spatrick size_t pos = std::min<size_t>(lower_pos, upper_pos);
4043061da546Spatrick if (pos != std::string::npos) {
4044061da546Spatrick underlined_shortcut = true;
4045061da546Spatrick if (pos > 0) {
4046061da546Spatrick window.PutCString(name, pos);
4047061da546Spatrick name += pos;
4048061da546Spatrick }
4049061da546Spatrick const attr_t shortcut_attr = A_UNDERLINE | A_BOLD;
4050061da546Spatrick window.AttributeOn(shortcut_attr);
4051061da546Spatrick window.PutChar(name[0]);
4052061da546Spatrick window.AttributeOff(shortcut_attr);
4053061da546Spatrick name++;
4054061da546Spatrick if (name[0])
4055061da546Spatrick window.PutCString(name);
4056061da546Spatrick }
4057061da546Spatrick }
4058061da546Spatrick
4059061da546Spatrick if (!underlined_shortcut) {
4060061da546Spatrick window.PutCString(m_name.c_str());
4061061da546Spatrick }
4062061da546Spatrick
4063061da546Spatrick if (highlight)
4064be691f3bSpatrick window.AttributeOff(highlight_attr);
4065061da546Spatrick
4066061da546Spatrick if (m_key_name.empty()) {
4067dda28197Spatrick if (!underlined_shortcut && llvm::isPrint(m_key_value)) {
4068be691f3bSpatrick window.AttributeOn(COLOR_PAIR(MagentaOnWhite));
4069061da546Spatrick window.Printf(" (%c)", m_key_value);
4070be691f3bSpatrick window.AttributeOff(COLOR_PAIR(MagentaOnWhite));
4071061da546Spatrick }
4072061da546Spatrick } else {
4073be691f3bSpatrick window.AttributeOn(COLOR_PAIR(MagentaOnWhite));
4074061da546Spatrick window.Printf(" (%s)", m_key_name.c_str());
4075be691f3bSpatrick window.AttributeOff(COLOR_PAIR(MagentaOnWhite));
4076061da546Spatrick }
4077061da546Spatrick }
4078061da546Spatrick }
4079061da546Spatrick
WindowDelegateDraw(Window & window,bool force)4080061da546Spatrick bool Menu::WindowDelegateDraw(Window &window, bool force) {
4081061da546Spatrick Menus &submenus = GetSubmenus();
4082061da546Spatrick const size_t num_submenus = submenus.size();
4083061da546Spatrick const int selected_idx = GetSelectedSubmenuIndex();
4084061da546Spatrick Menu::Type menu_type = GetType();
4085061da546Spatrick switch (menu_type) {
4086061da546Spatrick case Menu::Type::Bar: {
4087be691f3bSpatrick window.SetBackground(BlackOnWhite);
4088061da546Spatrick window.MoveCursor(0, 0);
4089061da546Spatrick for (size_t i = 0; i < num_submenus; ++i) {
4090061da546Spatrick Menu *menu = submenus[i].get();
4091061da546Spatrick if (i > 0)
4092061da546Spatrick window.PutChar(' ');
4093061da546Spatrick menu->SetStartingColumn(window.GetCursorX());
4094061da546Spatrick window.PutCString("| ");
4095061da546Spatrick menu->DrawMenuTitle(window, false);
4096061da546Spatrick }
4097061da546Spatrick window.PutCString(" |");
4098061da546Spatrick } break;
4099061da546Spatrick
4100061da546Spatrick case Menu::Type::Item: {
4101061da546Spatrick int y = 1;
4102061da546Spatrick int x = 3;
4103061da546Spatrick // Draw the menu
4104061da546Spatrick int cursor_x = 0;
4105061da546Spatrick int cursor_y = 0;
4106061da546Spatrick window.Erase();
4107be691f3bSpatrick window.SetBackground(BlackOnWhite);
4108061da546Spatrick window.Box();
4109061da546Spatrick for (size_t i = 0; i < num_submenus; ++i) {
4110061da546Spatrick const bool is_selected = (i == static_cast<size_t>(selected_idx));
4111061da546Spatrick window.MoveCursor(x, y + i);
4112061da546Spatrick if (is_selected) {
4113061da546Spatrick // Remember where we want the cursor to be
4114061da546Spatrick cursor_x = x - 1;
4115061da546Spatrick cursor_y = y + i;
4116061da546Spatrick }
4117061da546Spatrick submenus[i]->DrawMenuTitle(window, is_selected);
4118061da546Spatrick }
4119061da546Spatrick window.MoveCursor(cursor_x, cursor_y);
4120061da546Spatrick } break;
4121061da546Spatrick
4122061da546Spatrick default:
4123061da546Spatrick case Menu::Type::Separator:
4124061da546Spatrick break;
4125061da546Spatrick }
4126061da546Spatrick return true; // Drawing handled...
4127061da546Spatrick }
4128061da546Spatrick
WindowDelegateHandleChar(Window & window,int key)4129061da546Spatrick HandleCharResult Menu::WindowDelegateHandleChar(Window &window, int key) {
4130061da546Spatrick HandleCharResult result = eKeyNotHandled;
4131061da546Spatrick
4132061da546Spatrick Menus &submenus = GetSubmenus();
4133061da546Spatrick const size_t num_submenus = submenus.size();
4134061da546Spatrick const int selected_idx = GetSelectedSubmenuIndex();
4135061da546Spatrick Menu::Type menu_type = GetType();
4136061da546Spatrick if (menu_type == Menu::Type::Bar) {
4137061da546Spatrick MenuSP run_menu_sp;
4138061da546Spatrick switch (key) {
4139061da546Spatrick case KEY_DOWN:
4140061da546Spatrick case KEY_UP:
4141061da546Spatrick // Show last menu or first menu
4142061da546Spatrick if (selected_idx < static_cast<int>(num_submenus))
4143061da546Spatrick run_menu_sp = submenus[selected_idx];
4144061da546Spatrick else if (!submenus.empty())
4145061da546Spatrick run_menu_sp = submenus.front();
4146061da546Spatrick result = eKeyHandled;
4147061da546Spatrick break;
4148061da546Spatrick
4149061da546Spatrick case KEY_RIGHT:
4150061da546Spatrick ++m_selected;
4151061da546Spatrick if (m_selected >= static_cast<int>(num_submenus))
4152061da546Spatrick m_selected = 0;
4153061da546Spatrick if (m_selected < static_cast<int>(num_submenus))
4154061da546Spatrick run_menu_sp = submenus[m_selected];
4155061da546Spatrick else if (!submenus.empty())
4156061da546Spatrick run_menu_sp = submenus.front();
4157061da546Spatrick result = eKeyHandled;
4158061da546Spatrick break;
4159061da546Spatrick
4160061da546Spatrick case KEY_LEFT:
4161061da546Spatrick --m_selected;
4162061da546Spatrick if (m_selected < 0)
4163061da546Spatrick m_selected = num_submenus - 1;
4164061da546Spatrick if (m_selected < static_cast<int>(num_submenus))
4165061da546Spatrick run_menu_sp = submenus[m_selected];
4166061da546Spatrick else if (!submenus.empty())
4167061da546Spatrick run_menu_sp = submenus.front();
4168061da546Spatrick result = eKeyHandled;
4169061da546Spatrick break;
4170061da546Spatrick
4171061da546Spatrick default:
4172061da546Spatrick for (size_t i = 0; i < num_submenus; ++i) {
4173061da546Spatrick if (submenus[i]->GetKeyValue() == key) {
4174061da546Spatrick SetSelectedSubmenuIndex(i);
4175061da546Spatrick run_menu_sp = submenus[i];
4176061da546Spatrick result = eKeyHandled;
4177061da546Spatrick break;
4178061da546Spatrick }
4179061da546Spatrick }
4180061da546Spatrick break;
4181061da546Spatrick }
4182061da546Spatrick
4183061da546Spatrick if (run_menu_sp) {
4184061da546Spatrick // Run the action on this menu in case we need to populate the menu with
4185061da546Spatrick // dynamic content and also in case check marks, and any other menu
4186061da546Spatrick // decorations need to be calculated
4187061da546Spatrick if (run_menu_sp->Action() == MenuActionResult::Quit)
4188061da546Spatrick return eQuitApplication;
4189061da546Spatrick
4190061da546Spatrick Rect menu_bounds;
4191061da546Spatrick menu_bounds.origin.x = run_menu_sp->GetStartingColumn();
4192061da546Spatrick menu_bounds.origin.y = 1;
4193061da546Spatrick menu_bounds.size.width = run_menu_sp->GetDrawWidth();
4194061da546Spatrick menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2;
4195061da546Spatrick if (m_menu_window_sp)
4196061da546Spatrick window.GetParent()->RemoveSubWindow(m_menu_window_sp.get());
4197061da546Spatrick
4198061da546Spatrick m_menu_window_sp = window.GetParent()->CreateSubWindow(
4199061da546Spatrick run_menu_sp->GetName().c_str(), menu_bounds, true);
4200061da546Spatrick m_menu_window_sp->SetDelegate(run_menu_sp);
4201061da546Spatrick }
4202061da546Spatrick } else if (menu_type == Menu::Type::Item) {
4203061da546Spatrick switch (key) {
4204061da546Spatrick case KEY_DOWN:
4205061da546Spatrick if (m_submenus.size() > 1) {
4206061da546Spatrick const int start_select = m_selected;
4207061da546Spatrick while (++m_selected != start_select) {
4208061da546Spatrick if (static_cast<size_t>(m_selected) >= num_submenus)
4209061da546Spatrick m_selected = 0;
4210061da546Spatrick if (m_submenus[m_selected]->GetType() == Type::Separator)
4211061da546Spatrick continue;
4212061da546Spatrick else
4213061da546Spatrick break;
4214061da546Spatrick }
4215061da546Spatrick return eKeyHandled;
4216061da546Spatrick }
4217061da546Spatrick break;
4218061da546Spatrick
4219061da546Spatrick case KEY_UP:
4220061da546Spatrick if (m_submenus.size() > 1) {
4221061da546Spatrick const int start_select = m_selected;
4222061da546Spatrick while (--m_selected != start_select) {
4223061da546Spatrick if (m_selected < static_cast<int>(0))
4224061da546Spatrick m_selected = num_submenus - 1;
4225061da546Spatrick if (m_submenus[m_selected]->GetType() == Type::Separator)
4226061da546Spatrick continue;
4227061da546Spatrick else
4228061da546Spatrick break;
4229061da546Spatrick }
4230061da546Spatrick return eKeyHandled;
4231061da546Spatrick }
4232061da546Spatrick break;
4233061da546Spatrick
4234061da546Spatrick case KEY_RETURN:
4235061da546Spatrick if (static_cast<size_t>(selected_idx) < num_submenus) {
4236061da546Spatrick if (submenus[selected_idx]->Action() == MenuActionResult::Quit)
4237061da546Spatrick return eQuitApplication;
4238061da546Spatrick window.GetParent()->RemoveSubWindow(&window);
4239061da546Spatrick return eKeyHandled;
4240061da546Spatrick }
4241061da546Spatrick break;
4242061da546Spatrick
4243061da546Spatrick case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in
4244061da546Spatrick // case other chars are entered for escaped sequences
4245061da546Spatrick window.GetParent()->RemoveSubWindow(&window);
4246061da546Spatrick return eKeyHandled;
4247061da546Spatrick
4248061da546Spatrick default:
4249061da546Spatrick for (size_t i = 0; i < num_submenus; ++i) {
4250061da546Spatrick Menu *menu = submenus[i].get();
4251061da546Spatrick if (menu->GetKeyValue() == key) {
4252061da546Spatrick SetSelectedSubmenuIndex(i);
4253061da546Spatrick window.GetParent()->RemoveSubWindow(&window);
4254061da546Spatrick if (menu->Action() == MenuActionResult::Quit)
4255061da546Spatrick return eQuitApplication;
4256061da546Spatrick return eKeyHandled;
4257061da546Spatrick }
4258061da546Spatrick }
4259061da546Spatrick break;
4260061da546Spatrick }
4261061da546Spatrick } else if (menu_type == Menu::Type::Separator) {
4262061da546Spatrick }
4263061da546Spatrick return result;
4264061da546Spatrick }
4265061da546Spatrick
4266061da546Spatrick class Application {
4267061da546Spatrick public:
Application(FILE * in,FILE * out)4268*f6aab3d8Srobert Application(FILE *in, FILE *out) : m_window_sp(), m_in(in), m_out(out) {}
4269061da546Spatrick
~Application()4270061da546Spatrick ~Application() {
4271061da546Spatrick m_window_delegates.clear();
4272061da546Spatrick m_window_sp.reset();
4273061da546Spatrick if (m_screen) {
4274061da546Spatrick ::delscreen(m_screen);
4275061da546Spatrick m_screen = nullptr;
4276061da546Spatrick }
4277061da546Spatrick }
4278061da546Spatrick
Initialize()4279061da546Spatrick void Initialize() {
4280061da546Spatrick m_screen = ::newterm(nullptr, m_out, m_in);
4281061da546Spatrick ::start_color();
4282061da546Spatrick ::curs_set(0);
4283061da546Spatrick ::noecho();
4284061da546Spatrick ::keypad(stdscr, TRUE);
4285061da546Spatrick }
4286061da546Spatrick
Terminate()4287061da546Spatrick void Terminate() { ::endwin(); }
4288061da546Spatrick
Run(Debugger & debugger)4289061da546Spatrick void Run(Debugger &debugger) {
4290061da546Spatrick bool done = false;
4291061da546Spatrick int delay_in_tenths_of_a_second = 1;
4292061da546Spatrick
4293*f6aab3d8Srobert // Alas the threading model in curses is a bit lame so we need to resort
4294*f6aab3d8Srobert // to polling every 0.5 seconds. We could poll for stdin ourselves and
4295*f6aab3d8Srobert // then pass the keys down but then we need to translate all of the escape
4296061da546Spatrick // sequences ourselves. So we resort to polling for input because we need
4297061da546Spatrick // to receive async process events while in this loop.
4298061da546Spatrick
4299*f6aab3d8Srobert halfdelay(delay_in_tenths_of_a_second); // Poll using some number of
4300*f6aab3d8Srobert // tenths of seconds seconds when
4301*f6aab3d8Srobert // calling Window::GetChar()
4302061da546Spatrick
4303061da546Spatrick ListenerSP listener_sp(
4304061da546Spatrick Listener::MakeListener("lldb.IOHandler.curses.Application"));
4305061da546Spatrick ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass());
4306061da546Spatrick debugger.EnableForwardEvents(listener_sp);
4307061da546Spatrick
4308be691f3bSpatrick m_update_screen = true;
4309061da546Spatrick #if defined(__APPLE__)
4310061da546Spatrick std::deque<int> escape_chars;
4311061da546Spatrick #endif
4312061da546Spatrick
4313061da546Spatrick while (!done) {
4314be691f3bSpatrick if (m_update_screen) {
4315061da546Spatrick m_window_sp->Draw(false);
4316061da546Spatrick // All windows should be calling Window::DeferredRefresh() instead of
4317061da546Spatrick // Window::Refresh() so we can do a single update and avoid any screen
4318061da546Spatrick // blinking
4319061da546Spatrick update_panels();
4320061da546Spatrick
4321061da546Spatrick // Cursor hiding isn't working on MacOSX, so hide it in the top left
4322061da546Spatrick // corner
4323061da546Spatrick m_window_sp->MoveCursor(0, 0);
4324061da546Spatrick
4325061da546Spatrick doupdate();
4326be691f3bSpatrick m_update_screen = false;
4327061da546Spatrick }
4328061da546Spatrick
4329061da546Spatrick #if defined(__APPLE__)
4330061da546Spatrick // Terminal.app doesn't map its function keys correctly, F1-F4 default
4331061da546Spatrick // to: \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if
4332061da546Spatrick // possible
4333061da546Spatrick int ch;
4334061da546Spatrick if (escape_chars.empty())
4335061da546Spatrick ch = m_window_sp->GetChar();
4336061da546Spatrick else {
4337061da546Spatrick ch = escape_chars.front();
4338061da546Spatrick escape_chars.pop_front();
4339061da546Spatrick }
4340061da546Spatrick if (ch == KEY_ESCAPE) {
4341061da546Spatrick int ch2 = m_window_sp->GetChar();
4342061da546Spatrick if (ch2 == 'O') {
4343061da546Spatrick int ch3 = m_window_sp->GetChar();
4344061da546Spatrick switch (ch3) {
4345061da546Spatrick case 'P':
4346061da546Spatrick ch = KEY_F(1);
4347061da546Spatrick break;
4348061da546Spatrick case 'Q':
4349061da546Spatrick ch = KEY_F(2);
4350061da546Spatrick break;
4351061da546Spatrick case 'R':
4352061da546Spatrick ch = KEY_F(3);
4353061da546Spatrick break;
4354061da546Spatrick case 'S':
4355061da546Spatrick ch = KEY_F(4);
4356061da546Spatrick break;
4357061da546Spatrick default:
4358061da546Spatrick escape_chars.push_back(ch2);
4359061da546Spatrick if (ch3 != -1)
4360061da546Spatrick escape_chars.push_back(ch3);
4361061da546Spatrick break;
4362061da546Spatrick }
4363061da546Spatrick } else if (ch2 != -1)
4364061da546Spatrick escape_chars.push_back(ch2);
4365061da546Spatrick }
4366061da546Spatrick #else
4367061da546Spatrick int ch = m_window_sp->GetChar();
4368061da546Spatrick
4369061da546Spatrick #endif
4370061da546Spatrick if (ch == -1) {
4371061da546Spatrick if (feof(m_in) || ferror(m_in)) {
4372061da546Spatrick done = true;
4373061da546Spatrick } else {
4374061da546Spatrick // Just a timeout from using halfdelay(), check for events
4375061da546Spatrick EventSP event_sp;
4376061da546Spatrick while (listener_sp->PeekAtNextEvent()) {
4377061da546Spatrick listener_sp->GetEvent(event_sp, std::chrono::seconds(0));
4378061da546Spatrick
4379061da546Spatrick if (event_sp) {
4380061da546Spatrick Broadcaster *broadcaster = event_sp->GetBroadcaster();
4381061da546Spatrick if (broadcaster) {
4382061da546Spatrick // uint32_t event_type = event_sp->GetType();
4383061da546Spatrick ConstString broadcaster_class(
4384061da546Spatrick broadcaster->GetBroadcasterClass());
4385061da546Spatrick if (broadcaster_class == broadcaster_class_process) {
4386be691f3bSpatrick m_update_screen = true;
4387061da546Spatrick continue; // Don't get any key, just update our view
4388061da546Spatrick }
4389061da546Spatrick }
4390061da546Spatrick }
4391061da546Spatrick }
4392061da546Spatrick }
4393061da546Spatrick } else {
4394061da546Spatrick HandleCharResult key_result = m_window_sp->HandleChar(ch);
4395061da546Spatrick switch (key_result) {
4396061da546Spatrick case eKeyHandled:
4397be691f3bSpatrick m_update_screen = true;
4398061da546Spatrick break;
4399061da546Spatrick case eKeyNotHandled:
4400be691f3bSpatrick if (ch == 12) { // Ctrl+L, force full redraw
4401be691f3bSpatrick redrawwin(m_window_sp->get());
4402be691f3bSpatrick m_update_screen = true;
4403be691f3bSpatrick }
4404061da546Spatrick break;
4405061da546Spatrick case eQuitApplication:
4406061da546Spatrick done = true;
4407061da546Spatrick break;
4408061da546Spatrick }
4409061da546Spatrick }
4410061da546Spatrick }
4411061da546Spatrick
4412061da546Spatrick debugger.CancelForwardEvents(listener_sp);
4413061da546Spatrick }
4414061da546Spatrick
GetMainWindow()4415061da546Spatrick WindowSP &GetMainWindow() {
4416061da546Spatrick if (!m_window_sp)
4417061da546Spatrick m_window_sp = std::make_shared<Window>("main", stdscr, false);
4418061da546Spatrick return m_window_sp;
4419061da546Spatrick }
4420061da546Spatrick
TerminalSizeChanged()4421be691f3bSpatrick void TerminalSizeChanged() {
4422be691f3bSpatrick ::endwin();
4423be691f3bSpatrick ::refresh();
4424be691f3bSpatrick Rect content_bounds = m_window_sp->GetFrame();
4425be691f3bSpatrick m_window_sp->SetBounds(content_bounds);
4426be691f3bSpatrick if (WindowSP menubar_window_sp = m_window_sp->FindSubWindow("Menubar"))
4427be691f3bSpatrick menubar_window_sp->SetBounds(content_bounds.MakeMenuBar());
4428be691f3bSpatrick if (WindowSP status_window_sp = m_window_sp->FindSubWindow("Status"))
4429be691f3bSpatrick status_window_sp->SetBounds(content_bounds.MakeStatusBar());
4430be691f3bSpatrick
4431be691f3bSpatrick WindowSP source_window_sp = m_window_sp->FindSubWindow("Source");
4432be691f3bSpatrick WindowSP variables_window_sp = m_window_sp->FindSubWindow("Variables");
4433be691f3bSpatrick WindowSP registers_window_sp = m_window_sp->FindSubWindow("Registers");
4434be691f3bSpatrick WindowSP threads_window_sp = m_window_sp->FindSubWindow("Threads");
4435be691f3bSpatrick
4436be691f3bSpatrick Rect threads_bounds;
4437be691f3bSpatrick Rect source_variables_bounds;
4438be691f3bSpatrick content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds,
4439be691f3bSpatrick threads_bounds);
4440be691f3bSpatrick if (threads_window_sp)
4441be691f3bSpatrick threads_window_sp->SetBounds(threads_bounds);
4442be691f3bSpatrick else
4443be691f3bSpatrick source_variables_bounds = content_bounds;
4444be691f3bSpatrick
4445be691f3bSpatrick Rect source_bounds;
4446be691f3bSpatrick Rect variables_registers_bounds;
4447be691f3bSpatrick source_variables_bounds.HorizontalSplitPercentage(
4448be691f3bSpatrick 0.70, source_bounds, variables_registers_bounds);
4449be691f3bSpatrick if (variables_window_sp || registers_window_sp) {
4450be691f3bSpatrick if (variables_window_sp && registers_window_sp) {
4451be691f3bSpatrick Rect variables_bounds;
4452be691f3bSpatrick Rect registers_bounds;
4453be691f3bSpatrick variables_registers_bounds.VerticalSplitPercentage(
4454be691f3bSpatrick 0.50, variables_bounds, registers_bounds);
4455be691f3bSpatrick variables_window_sp->SetBounds(variables_bounds);
4456be691f3bSpatrick registers_window_sp->SetBounds(registers_bounds);
4457be691f3bSpatrick } else if (variables_window_sp) {
4458be691f3bSpatrick variables_window_sp->SetBounds(variables_registers_bounds);
4459be691f3bSpatrick } else {
4460be691f3bSpatrick registers_window_sp->SetBounds(variables_registers_bounds);
4461be691f3bSpatrick }
4462be691f3bSpatrick } else {
4463be691f3bSpatrick source_bounds = source_variables_bounds;
4464be691f3bSpatrick }
4465be691f3bSpatrick
4466be691f3bSpatrick source_window_sp->SetBounds(source_bounds);
4467be691f3bSpatrick
4468be691f3bSpatrick touchwin(stdscr);
4469be691f3bSpatrick redrawwin(m_window_sp->get());
4470be691f3bSpatrick m_update_screen = true;
4471be691f3bSpatrick }
4472be691f3bSpatrick
4473061da546Spatrick protected:
4474061da546Spatrick WindowSP m_window_sp;
4475061da546Spatrick WindowDelegates m_window_delegates;
4476*f6aab3d8Srobert SCREEN *m_screen = nullptr;
4477061da546Spatrick FILE *m_in;
4478061da546Spatrick FILE *m_out;
4479be691f3bSpatrick bool m_update_screen = false;
4480061da546Spatrick };
4481061da546Spatrick
4482061da546Spatrick } // namespace curses
4483061da546Spatrick
4484061da546Spatrick using namespace curses;
4485061da546Spatrick
4486061da546Spatrick struct Row {
4487be691f3bSpatrick ValueObjectUpdater value;
4488061da546Spatrick Row *parent;
4489061da546Spatrick // The process stop ID when the children were calculated.
4490be691f3bSpatrick uint32_t children_stop_id = 0;
4491be691f3bSpatrick int row_idx = 0;
4492be691f3bSpatrick int x = 1;
4493be691f3bSpatrick int y = 1;
4494061da546Spatrick bool might_have_children;
4495be691f3bSpatrick bool expanded = false;
4496be691f3bSpatrick bool calculated_children = false;
4497061da546Spatrick std::vector<Row> children;
4498061da546Spatrick
RowRow4499061da546Spatrick Row(const ValueObjectSP &v, Row *p)
4500be691f3bSpatrick : value(v), parent(p),
4501be691f3bSpatrick might_have_children(v ? v->MightHaveChildren() : false) {}
4502061da546Spatrick
GetDepthRow4503061da546Spatrick size_t GetDepth() const {
4504061da546Spatrick if (parent)
4505061da546Spatrick return 1 + parent->GetDepth();
4506061da546Spatrick return 0;
4507061da546Spatrick }
4508061da546Spatrick
ExpandRow4509061da546Spatrick void Expand() { expanded = true; }
4510061da546Spatrick
GetChildrenRow4511061da546Spatrick std::vector<Row> &GetChildren() {
4512061da546Spatrick ProcessSP process_sp = value.GetProcessSP();
4513061da546Spatrick auto stop_id = process_sp->GetStopID();
4514061da546Spatrick if (process_sp && stop_id != children_stop_id) {
4515061da546Spatrick children_stop_id = stop_id;
4516061da546Spatrick calculated_children = false;
4517061da546Spatrick }
4518061da546Spatrick if (!calculated_children) {
4519061da546Spatrick children.clear();
4520061da546Spatrick calculated_children = true;
4521061da546Spatrick ValueObjectSP valobj = value.GetSP();
4522061da546Spatrick if (valobj) {
4523061da546Spatrick const size_t num_children = valobj->GetNumChildren();
4524061da546Spatrick for (size_t i = 0; i < num_children; ++i) {
4525061da546Spatrick children.push_back(Row(valobj->GetChildAtIndex(i, true), this));
4526061da546Spatrick }
4527061da546Spatrick }
4528061da546Spatrick }
4529061da546Spatrick return children;
4530061da546Spatrick }
4531061da546Spatrick
UnexpandRow4532061da546Spatrick void Unexpand() {
4533061da546Spatrick expanded = false;
4534061da546Spatrick calculated_children = false;
4535061da546Spatrick children.clear();
4536061da546Spatrick }
4537061da546Spatrick
DrawTreeRow4538061da546Spatrick void DrawTree(Window &window) {
4539061da546Spatrick if (parent)
4540061da546Spatrick parent->DrawTreeForChild(window, this, 0);
4541061da546Spatrick
4542*f6aab3d8Srobert if (might_have_children &&
4543*f6aab3d8Srobert (!calculated_children || !GetChildren().empty())) {
4544061da546Spatrick // It we can get UTF8 characters to work we should try to use the
4545061da546Spatrick // "symbol" UTF8 string below
4546061da546Spatrick // const char *symbol = "";
4547061da546Spatrick // if (row.expanded)
4548061da546Spatrick // symbol = "\xe2\x96\xbd ";
4549061da546Spatrick // else
4550061da546Spatrick // symbol = "\xe2\x96\xb7 ";
4551061da546Spatrick // window.PutCString (symbol);
4552061da546Spatrick
4553061da546Spatrick // The ACS_DARROW and ACS_RARROW don't look very nice they are just a 'v'
4554061da546Spatrick // or '>' character...
4555061da546Spatrick // if (expanded)
4556061da546Spatrick // window.PutChar (ACS_DARROW);
4557061da546Spatrick // else
4558061da546Spatrick // window.PutChar (ACS_RARROW);
4559061da546Spatrick // Since we can't find any good looking right arrow/down arrow symbols,
4560061da546Spatrick // just use a diamond...
4561061da546Spatrick window.PutChar(ACS_DIAMOND);
4562061da546Spatrick window.PutChar(ACS_HLINE);
4563061da546Spatrick }
4564061da546Spatrick }
4565061da546Spatrick
DrawTreeForChildRow4566061da546Spatrick void DrawTreeForChild(Window &window, Row *child, uint32_t reverse_depth) {
4567061da546Spatrick if (parent)
4568061da546Spatrick parent->DrawTreeForChild(window, this, reverse_depth + 1);
4569061da546Spatrick
4570061da546Spatrick if (&GetChildren().back() == child) {
4571061da546Spatrick // Last child
4572061da546Spatrick if (reverse_depth == 0) {
4573061da546Spatrick window.PutChar(ACS_LLCORNER);
4574061da546Spatrick window.PutChar(ACS_HLINE);
4575061da546Spatrick } else {
4576061da546Spatrick window.PutChar(' ');
4577061da546Spatrick window.PutChar(' ');
4578061da546Spatrick }
4579061da546Spatrick } else {
4580061da546Spatrick if (reverse_depth == 0) {
4581061da546Spatrick window.PutChar(ACS_LTEE);
4582061da546Spatrick window.PutChar(ACS_HLINE);
4583061da546Spatrick } else {
4584061da546Spatrick window.PutChar(ACS_VLINE);
4585061da546Spatrick window.PutChar(' ');
4586061da546Spatrick }
4587061da546Spatrick }
4588061da546Spatrick }
4589061da546Spatrick };
4590061da546Spatrick
4591061da546Spatrick struct DisplayOptions {
4592061da546Spatrick bool show_types;
4593061da546Spatrick };
4594061da546Spatrick
4595061da546Spatrick class TreeItem;
4596061da546Spatrick
4597061da546Spatrick class TreeDelegate {
4598061da546Spatrick public:
4599061da546Spatrick TreeDelegate() = default;
4600061da546Spatrick virtual ~TreeDelegate() = default;
4601061da546Spatrick
4602061da546Spatrick virtual void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) = 0;
4603061da546Spatrick virtual void TreeDelegateGenerateChildren(TreeItem &item) = 0;
TreeDelegateUpdateSelection(TreeItem & root,int & selection_index,TreeItem * & selected_item)4604be691f3bSpatrick virtual void TreeDelegateUpdateSelection(TreeItem &root, int &selection_index,
4605*f6aab3d8Srobert TreeItem *&selected_item) {}
4606*f6aab3d8Srobert // This is invoked when a tree item is selected. If true is returned, the
4607*f6aab3d8Srobert // views are updated.
4608*f6aab3d8Srobert virtual bool TreeDelegateItemSelected(TreeItem &item) = 0;
TreeDelegateExpandRootByDefault()4609be691f3bSpatrick virtual bool TreeDelegateExpandRootByDefault() { return false; }
4610*f6aab3d8Srobert // This is mostly useful for root tree delegates. If false is returned,
4611*f6aab3d8Srobert // drawing will be skipped completely. This is needed, for instance, in
4612*f6aab3d8Srobert // skipping drawing of the threads tree if there is no running process.
TreeDelegateShouldDraw()4613*f6aab3d8Srobert virtual bool TreeDelegateShouldDraw() { return true; }
4614061da546Spatrick };
4615061da546Spatrick
4616061da546Spatrick typedef std::shared_ptr<TreeDelegate> TreeDelegateSP;
4617061da546Spatrick
4618061da546Spatrick class TreeItem {
4619061da546Spatrick public:
TreeItem(TreeItem * parent,TreeDelegate & delegate,bool might_have_children)4620061da546Spatrick TreeItem(TreeItem *parent, TreeDelegate &delegate, bool might_have_children)
4621*f6aab3d8Srobert : m_parent(parent), m_delegate(delegate), m_children(),
4622*f6aab3d8Srobert m_might_have_children(might_have_children) {
4623be691f3bSpatrick if (m_parent == nullptr)
4624be691f3bSpatrick m_is_expanded = m_delegate.TreeDelegateExpandRootByDefault();
4625be691f3bSpatrick }
4626061da546Spatrick
operator =(const TreeItem & rhs)4627061da546Spatrick TreeItem &operator=(const TreeItem &rhs) {
4628061da546Spatrick if (this != &rhs) {
4629061da546Spatrick m_parent = rhs.m_parent;
4630061da546Spatrick m_delegate = rhs.m_delegate;
4631061da546Spatrick m_user_data = rhs.m_user_data;
4632061da546Spatrick m_identifier = rhs.m_identifier;
4633061da546Spatrick m_row_idx = rhs.m_row_idx;
4634061da546Spatrick m_children = rhs.m_children;
4635061da546Spatrick m_might_have_children = rhs.m_might_have_children;
4636061da546Spatrick m_is_expanded = rhs.m_is_expanded;
4637061da546Spatrick }
4638061da546Spatrick return *this;
4639061da546Spatrick }
4640061da546Spatrick
4641061da546Spatrick TreeItem(const TreeItem &) = default;
4642061da546Spatrick
GetDepth() const4643061da546Spatrick size_t GetDepth() const {
4644061da546Spatrick if (m_parent)
4645061da546Spatrick return 1 + m_parent->GetDepth();
4646061da546Spatrick return 0;
4647061da546Spatrick }
4648061da546Spatrick
GetRowIndex() const4649061da546Spatrick int GetRowIndex() const { return m_row_idx; }
4650061da546Spatrick
ClearChildren()4651061da546Spatrick void ClearChildren() { m_children.clear(); }
4652061da546Spatrick
Resize(size_t n,const TreeItem & t)4653061da546Spatrick void Resize(size_t n, const TreeItem &t) { m_children.resize(n, t); }
4654061da546Spatrick
operator [](size_t i)4655061da546Spatrick TreeItem &operator[](size_t i) { return m_children[i]; }
4656061da546Spatrick
SetRowIndex(int row_idx)4657061da546Spatrick void SetRowIndex(int row_idx) { m_row_idx = row_idx; }
4658061da546Spatrick
GetNumChildren()4659061da546Spatrick size_t GetNumChildren() {
4660061da546Spatrick m_delegate.TreeDelegateGenerateChildren(*this);
4661061da546Spatrick return m_children.size();
4662061da546Spatrick }
4663061da546Spatrick
ItemWasSelected()4664061da546Spatrick void ItemWasSelected() { m_delegate.TreeDelegateItemSelected(*this); }
4665061da546Spatrick
CalculateRowIndexes(int & row_idx)4666061da546Spatrick void CalculateRowIndexes(int &row_idx) {
4667061da546Spatrick SetRowIndex(row_idx);
4668061da546Spatrick ++row_idx;
4669061da546Spatrick
4670061da546Spatrick const bool expanded = IsExpanded();
4671061da546Spatrick
4672061da546Spatrick // The root item must calculate its children, or we must calculate the
4673061da546Spatrick // number of children if the item is expanded
4674061da546Spatrick if (m_parent == nullptr || expanded)
4675061da546Spatrick GetNumChildren();
4676061da546Spatrick
4677061da546Spatrick for (auto &item : m_children) {
4678061da546Spatrick if (expanded)
4679061da546Spatrick item.CalculateRowIndexes(row_idx);
4680061da546Spatrick else
4681061da546Spatrick item.SetRowIndex(-1);
4682061da546Spatrick }
4683061da546Spatrick }
4684061da546Spatrick
GetParent()4685061da546Spatrick TreeItem *GetParent() { return m_parent; }
4686061da546Spatrick
IsExpanded() const4687061da546Spatrick bool IsExpanded() const { return m_is_expanded; }
4688061da546Spatrick
Expand()4689061da546Spatrick void Expand() { m_is_expanded = true; }
4690061da546Spatrick
Unexpand()4691061da546Spatrick void Unexpand() { m_is_expanded = false; }
4692061da546Spatrick
Draw(Window & window,const int first_visible_row,const uint32_t selected_row_idx,int & row_idx,int & num_rows_left)4693061da546Spatrick bool Draw(Window &window, const int first_visible_row,
4694061da546Spatrick const uint32_t selected_row_idx, int &row_idx, int &num_rows_left) {
4695061da546Spatrick if (num_rows_left <= 0)
4696061da546Spatrick return false;
4697061da546Spatrick
4698061da546Spatrick if (m_row_idx >= first_visible_row) {
4699061da546Spatrick window.MoveCursor(2, row_idx + 1);
4700061da546Spatrick
4701061da546Spatrick if (m_parent)
4702061da546Spatrick m_parent->DrawTreeForChild(window, this, 0);
4703061da546Spatrick
4704061da546Spatrick if (m_might_have_children) {
4705061da546Spatrick // It we can get UTF8 characters to work we should try to use the
4706061da546Spatrick // "symbol" UTF8 string below
4707061da546Spatrick // const char *symbol = "";
4708061da546Spatrick // if (row.expanded)
4709061da546Spatrick // symbol = "\xe2\x96\xbd ";
4710061da546Spatrick // else
4711061da546Spatrick // symbol = "\xe2\x96\xb7 ";
4712061da546Spatrick // window.PutCString (symbol);
4713061da546Spatrick
4714061da546Spatrick // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
4715061da546Spatrick // 'v' or '>' character...
4716061da546Spatrick // if (expanded)
4717061da546Spatrick // window.PutChar (ACS_DARROW);
4718061da546Spatrick // else
4719061da546Spatrick // window.PutChar (ACS_RARROW);
4720061da546Spatrick // Since we can't find any good looking right arrow/down arrow symbols,
4721061da546Spatrick // just use a diamond...
4722061da546Spatrick window.PutChar(ACS_DIAMOND);
4723061da546Spatrick window.PutChar(ACS_HLINE);
4724061da546Spatrick }
4725061da546Spatrick bool highlight = (selected_row_idx == static_cast<size_t>(m_row_idx)) &&
4726061da546Spatrick window.IsActive();
4727061da546Spatrick
4728061da546Spatrick if (highlight)
4729061da546Spatrick window.AttributeOn(A_REVERSE);
4730061da546Spatrick
4731061da546Spatrick m_delegate.TreeDelegateDrawTreeItem(*this, window);
4732061da546Spatrick
4733061da546Spatrick if (highlight)
4734061da546Spatrick window.AttributeOff(A_REVERSE);
4735061da546Spatrick ++row_idx;
4736061da546Spatrick --num_rows_left;
4737061da546Spatrick }
4738061da546Spatrick
4739061da546Spatrick if (num_rows_left <= 0)
4740061da546Spatrick return false; // We are done drawing...
4741061da546Spatrick
4742061da546Spatrick if (IsExpanded()) {
4743061da546Spatrick for (auto &item : m_children) {
4744061da546Spatrick // If we displayed all the rows and item.Draw() returns false we are
4745061da546Spatrick // done drawing and can exit this for loop
4746061da546Spatrick if (!item.Draw(window, first_visible_row, selected_row_idx, row_idx,
4747061da546Spatrick num_rows_left))
4748061da546Spatrick break;
4749061da546Spatrick }
4750061da546Spatrick }
4751061da546Spatrick return num_rows_left >= 0; // Return true if not done drawing yet
4752061da546Spatrick }
4753061da546Spatrick
DrawTreeForChild(Window & window,TreeItem * child,uint32_t reverse_depth)4754061da546Spatrick void DrawTreeForChild(Window &window, TreeItem *child,
4755061da546Spatrick uint32_t reverse_depth) {
4756061da546Spatrick if (m_parent)
4757061da546Spatrick m_parent->DrawTreeForChild(window, this, reverse_depth + 1);
4758061da546Spatrick
4759061da546Spatrick if (&m_children.back() == child) {
4760061da546Spatrick // Last child
4761061da546Spatrick if (reverse_depth == 0) {
4762061da546Spatrick window.PutChar(ACS_LLCORNER);
4763061da546Spatrick window.PutChar(ACS_HLINE);
4764061da546Spatrick } else {
4765061da546Spatrick window.PutChar(' ');
4766061da546Spatrick window.PutChar(' ');
4767061da546Spatrick }
4768061da546Spatrick } else {
4769061da546Spatrick if (reverse_depth == 0) {
4770061da546Spatrick window.PutChar(ACS_LTEE);
4771061da546Spatrick window.PutChar(ACS_HLINE);
4772061da546Spatrick } else {
4773061da546Spatrick window.PutChar(ACS_VLINE);
4774061da546Spatrick window.PutChar(' ');
4775061da546Spatrick }
4776061da546Spatrick }
4777061da546Spatrick }
4778061da546Spatrick
GetItemForRowIndex(uint32_t row_idx)4779061da546Spatrick TreeItem *GetItemForRowIndex(uint32_t row_idx) {
4780061da546Spatrick if (static_cast<uint32_t>(m_row_idx) == row_idx)
4781061da546Spatrick return this;
4782061da546Spatrick if (m_children.empty())
4783061da546Spatrick return nullptr;
4784061da546Spatrick if (IsExpanded()) {
4785061da546Spatrick for (auto &item : m_children) {
4786061da546Spatrick TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx);
4787061da546Spatrick if (selected_item_ptr)
4788061da546Spatrick return selected_item_ptr;
4789061da546Spatrick }
4790061da546Spatrick }
4791061da546Spatrick return nullptr;
4792061da546Spatrick }
4793061da546Spatrick
GetUserData() const4794061da546Spatrick void *GetUserData() const { return m_user_data; }
4795061da546Spatrick
SetUserData(void * user_data)4796061da546Spatrick void SetUserData(void *user_data) { m_user_data = user_data; }
4797061da546Spatrick
GetIdentifier() const4798061da546Spatrick uint64_t GetIdentifier() const { return m_identifier; }
4799061da546Spatrick
SetIdentifier(uint64_t identifier)4800061da546Spatrick void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
4801061da546Spatrick
GetText() const4802*f6aab3d8Srobert const std::string &GetText() const { return m_text; }
4803*f6aab3d8Srobert
SetText(const char * text)4804*f6aab3d8Srobert void SetText(const char *text) {
4805*f6aab3d8Srobert if (text == nullptr) {
4806*f6aab3d8Srobert m_text.clear();
4807*f6aab3d8Srobert return;
4808*f6aab3d8Srobert }
4809*f6aab3d8Srobert m_text = text;
4810*f6aab3d8Srobert }
4811*f6aab3d8Srobert
SetMightHaveChildren(bool b)4812061da546Spatrick void SetMightHaveChildren(bool b) { m_might_have_children = b; }
4813061da546Spatrick
4814061da546Spatrick protected:
4815061da546Spatrick TreeItem *m_parent;
4816061da546Spatrick TreeDelegate &m_delegate;
4817*f6aab3d8Srobert void *m_user_data = nullptr;
4818*f6aab3d8Srobert uint64_t m_identifier = 0;
4819*f6aab3d8Srobert std::string m_text;
4820*f6aab3d8Srobert int m_row_idx = -1; // Zero based visible row index, -1 if not visible or for
4821*f6aab3d8Srobert // the root item
4822061da546Spatrick std::vector<TreeItem> m_children;
4823061da546Spatrick bool m_might_have_children;
4824*f6aab3d8Srobert bool m_is_expanded = false;
4825061da546Spatrick };
4826061da546Spatrick
4827061da546Spatrick class TreeWindowDelegate : public WindowDelegate {
4828061da546Spatrick public:
TreeWindowDelegate(Debugger & debugger,const TreeDelegateSP & delegate_sp)4829061da546Spatrick TreeWindowDelegate(Debugger &debugger, const TreeDelegateSP &delegate_sp)
4830061da546Spatrick : m_debugger(debugger), m_delegate_sp(delegate_sp),
4831*f6aab3d8Srobert m_root(nullptr, *delegate_sp, true) {}
4832061da546Spatrick
NumVisibleRows() const4833061da546Spatrick int NumVisibleRows() const { return m_max_y - m_min_y; }
4834061da546Spatrick
WindowDelegateDraw(Window & window,bool force)4835061da546Spatrick bool WindowDelegateDraw(Window &window, bool force) override {
4836061da546Spatrick m_min_x = 2;
4837061da546Spatrick m_min_y = 1;
4838061da546Spatrick m_max_x = window.GetWidth() - 1;
4839061da546Spatrick m_max_y = window.GetHeight() - 1;
4840061da546Spatrick
4841061da546Spatrick window.Erase();
4842061da546Spatrick window.DrawTitleBox(window.GetName());
4843061da546Spatrick
4844*f6aab3d8Srobert if (!m_delegate_sp->TreeDelegateShouldDraw()) {
4845*f6aab3d8Srobert m_selected_item = nullptr;
4846*f6aab3d8Srobert return true;
4847*f6aab3d8Srobert }
4848*f6aab3d8Srobert
4849061da546Spatrick const int num_visible_rows = NumVisibleRows();
4850061da546Spatrick m_num_rows = 0;
4851061da546Spatrick m_root.CalculateRowIndexes(m_num_rows);
4852be691f3bSpatrick m_delegate_sp->TreeDelegateUpdateSelection(m_root, m_selected_row_idx,
4853be691f3bSpatrick m_selected_item);
4854061da546Spatrick
4855061da546Spatrick // If we unexpanded while having something selected our total number of
4856061da546Spatrick // rows is less than the num visible rows, then make sure we show all the
4857061da546Spatrick // rows by setting the first visible row accordingly.
4858061da546Spatrick if (m_first_visible_row > 0 && m_num_rows < num_visible_rows)
4859061da546Spatrick m_first_visible_row = 0;
4860061da546Spatrick
4861061da546Spatrick // Make sure the selected row is always visible
4862061da546Spatrick if (m_selected_row_idx < m_first_visible_row)
4863061da546Spatrick m_first_visible_row = m_selected_row_idx;
4864061da546Spatrick else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
4865061da546Spatrick m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
4866061da546Spatrick
4867061da546Spatrick int row_idx = 0;
4868061da546Spatrick int num_rows_left = num_visible_rows;
4869061da546Spatrick m_root.Draw(window, m_first_visible_row, m_selected_row_idx, row_idx,
4870061da546Spatrick num_rows_left);
4871061da546Spatrick // Get the selected row
4872061da546Spatrick m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4873061da546Spatrick
4874061da546Spatrick return true; // Drawing handled
4875061da546Spatrick }
4876061da546Spatrick
WindowDelegateGetHelpText()4877061da546Spatrick const char *WindowDelegateGetHelpText() override {
4878061da546Spatrick return "Thread window keyboard shortcuts:";
4879061da546Spatrick }
4880061da546Spatrick
WindowDelegateGetKeyHelp()4881061da546Spatrick KeyHelp *WindowDelegateGetKeyHelp() override {
4882061da546Spatrick static curses::KeyHelp g_source_view_key_help[] = {
4883061da546Spatrick {KEY_UP, "Select previous item"},
4884061da546Spatrick {KEY_DOWN, "Select next item"},
4885061da546Spatrick {KEY_RIGHT, "Expand the selected item"},
4886061da546Spatrick {KEY_LEFT,
4887061da546Spatrick "Unexpand the selected item or select parent if not expanded"},
4888061da546Spatrick {KEY_PPAGE, "Page up"},
4889061da546Spatrick {KEY_NPAGE, "Page down"},
4890061da546Spatrick {'h', "Show help dialog"},
4891061da546Spatrick {' ', "Toggle item expansion"},
4892061da546Spatrick {',', "Page up"},
4893061da546Spatrick {'.', "Page down"},
4894061da546Spatrick {'\0', nullptr}};
4895061da546Spatrick return g_source_view_key_help;
4896061da546Spatrick }
4897061da546Spatrick
WindowDelegateHandleChar(Window & window,int c)4898061da546Spatrick HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
4899061da546Spatrick switch (c) {
4900061da546Spatrick case ',':
4901061da546Spatrick case KEY_PPAGE:
4902061da546Spatrick // Page up key
4903061da546Spatrick if (m_first_visible_row > 0) {
4904061da546Spatrick if (m_first_visible_row > m_max_y)
4905061da546Spatrick m_first_visible_row -= m_max_y;
4906061da546Spatrick else
4907061da546Spatrick m_first_visible_row = 0;
4908061da546Spatrick m_selected_row_idx = m_first_visible_row;
4909061da546Spatrick m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4910061da546Spatrick if (m_selected_item)
4911061da546Spatrick m_selected_item->ItemWasSelected();
4912061da546Spatrick }
4913061da546Spatrick return eKeyHandled;
4914061da546Spatrick
4915061da546Spatrick case '.':
4916061da546Spatrick case KEY_NPAGE:
4917061da546Spatrick // Page down key
4918061da546Spatrick if (m_num_rows > m_max_y) {
4919061da546Spatrick if (m_first_visible_row + m_max_y < m_num_rows) {
4920061da546Spatrick m_first_visible_row += m_max_y;
4921061da546Spatrick m_selected_row_idx = m_first_visible_row;
4922061da546Spatrick m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4923061da546Spatrick if (m_selected_item)
4924061da546Spatrick m_selected_item->ItemWasSelected();
4925061da546Spatrick }
4926061da546Spatrick }
4927061da546Spatrick return eKeyHandled;
4928061da546Spatrick
4929061da546Spatrick case KEY_UP:
4930061da546Spatrick if (m_selected_row_idx > 0) {
4931061da546Spatrick --m_selected_row_idx;
4932061da546Spatrick m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4933061da546Spatrick if (m_selected_item)
4934061da546Spatrick m_selected_item->ItemWasSelected();
4935061da546Spatrick }
4936061da546Spatrick return eKeyHandled;
4937061da546Spatrick
4938061da546Spatrick case KEY_DOWN:
4939061da546Spatrick if (m_selected_row_idx + 1 < m_num_rows) {
4940061da546Spatrick ++m_selected_row_idx;
4941061da546Spatrick m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4942061da546Spatrick if (m_selected_item)
4943061da546Spatrick m_selected_item->ItemWasSelected();
4944061da546Spatrick }
4945061da546Spatrick return eKeyHandled;
4946061da546Spatrick
4947061da546Spatrick case KEY_RIGHT:
4948061da546Spatrick if (m_selected_item) {
4949061da546Spatrick if (!m_selected_item->IsExpanded())
4950061da546Spatrick m_selected_item->Expand();
4951061da546Spatrick }
4952061da546Spatrick return eKeyHandled;
4953061da546Spatrick
4954061da546Spatrick case KEY_LEFT:
4955061da546Spatrick if (m_selected_item) {
4956061da546Spatrick if (m_selected_item->IsExpanded())
4957061da546Spatrick m_selected_item->Unexpand();
4958061da546Spatrick else if (m_selected_item->GetParent()) {
4959061da546Spatrick m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex();
4960061da546Spatrick m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4961061da546Spatrick if (m_selected_item)
4962061da546Spatrick m_selected_item->ItemWasSelected();
4963061da546Spatrick }
4964061da546Spatrick }
4965061da546Spatrick return eKeyHandled;
4966061da546Spatrick
4967061da546Spatrick case ' ':
4968061da546Spatrick // Toggle expansion state when SPACE is pressed
4969061da546Spatrick if (m_selected_item) {
4970061da546Spatrick if (m_selected_item->IsExpanded())
4971061da546Spatrick m_selected_item->Unexpand();
4972061da546Spatrick else
4973061da546Spatrick m_selected_item->Expand();
4974061da546Spatrick }
4975061da546Spatrick return eKeyHandled;
4976061da546Spatrick
4977061da546Spatrick case 'h':
4978061da546Spatrick window.CreateHelpSubwindow();
4979061da546Spatrick return eKeyHandled;
4980061da546Spatrick
4981061da546Spatrick default:
4982061da546Spatrick break;
4983061da546Spatrick }
4984061da546Spatrick return eKeyNotHandled;
4985061da546Spatrick }
4986061da546Spatrick
4987061da546Spatrick protected:
4988061da546Spatrick Debugger &m_debugger;
4989061da546Spatrick TreeDelegateSP m_delegate_sp;
4990061da546Spatrick TreeItem m_root;
4991*f6aab3d8Srobert TreeItem *m_selected_item = nullptr;
4992*f6aab3d8Srobert int m_num_rows = 0;
4993*f6aab3d8Srobert int m_selected_row_idx = 0;
4994*f6aab3d8Srobert int m_first_visible_row = 0;
4995*f6aab3d8Srobert int m_min_x = 0;
4996*f6aab3d8Srobert int m_min_y = 0;
4997*f6aab3d8Srobert int m_max_x = 0;
4998*f6aab3d8Srobert int m_max_y = 0;
4999*f6aab3d8Srobert };
5000*f6aab3d8Srobert
5001*f6aab3d8Srobert // A tree delegate that just draws the text member of the tree item, it doesn't
5002*f6aab3d8Srobert // have any children or actions.
5003*f6aab3d8Srobert class TextTreeDelegate : public TreeDelegate {
5004*f6aab3d8Srobert public:
TextTreeDelegate()5005*f6aab3d8Srobert TextTreeDelegate() : TreeDelegate() {}
5006*f6aab3d8Srobert
5007*f6aab3d8Srobert ~TextTreeDelegate() override = default;
5008*f6aab3d8Srobert
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5009*f6aab3d8Srobert void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5010*f6aab3d8Srobert window.PutCStringTruncated(1, item.GetText().c_str());
5011*f6aab3d8Srobert }
5012*f6aab3d8Srobert
TreeDelegateGenerateChildren(TreeItem & item)5013*f6aab3d8Srobert void TreeDelegateGenerateChildren(TreeItem &item) override {}
5014*f6aab3d8Srobert
TreeDelegateItemSelected(TreeItem & item)5015*f6aab3d8Srobert bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5016061da546Spatrick };
5017061da546Spatrick
5018061da546Spatrick class FrameTreeDelegate : public TreeDelegate {
5019061da546Spatrick public:
FrameTreeDelegate()5020061da546Spatrick FrameTreeDelegate() : TreeDelegate() {
5021061da546Spatrick FormatEntity::Parse(
5022*f6aab3d8Srobert "#${frame.index}: {${function.name}${function.pc-offset}}}", m_format);
5023061da546Spatrick }
5024061da546Spatrick
5025061da546Spatrick ~FrameTreeDelegate() override = default;
5026061da546Spatrick
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5027061da546Spatrick void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5028061da546Spatrick Thread *thread = (Thread *)item.GetUserData();
5029061da546Spatrick if (thread) {
5030061da546Spatrick const uint64_t frame_idx = item.GetIdentifier();
5031061da546Spatrick StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_idx);
5032061da546Spatrick if (frame_sp) {
5033061da546Spatrick StreamString strm;
5034061da546Spatrick const SymbolContext &sc =
5035061da546Spatrick frame_sp->GetSymbolContext(eSymbolContextEverything);
5036061da546Spatrick ExecutionContext exe_ctx(frame_sp);
5037061da546Spatrick if (FormatEntity::Format(m_format, strm, &sc, &exe_ctx, nullptr,
5038061da546Spatrick nullptr, false, false)) {
5039061da546Spatrick int right_pad = 1;
5040be691f3bSpatrick window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
5041061da546Spatrick }
5042061da546Spatrick }
5043061da546Spatrick }
5044061da546Spatrick }
5045061da546Spatrick
TreeDelegateGenerateChildren(TreeItem & item)5046061da546Spatrick void TreeDelegateGenerateChildren(TreeItem &item) override {
5047061da546Spatrick // No children for frames yet...
5048061da546Spatrick }
5049061da546Spatrick
TreeDelegateItemSelected(TreeItem & item)5050061da546Spatrick bool TreeDelegateItemSelected(TreeItem &item) override {
5051061da546Spatrick Thread *thread = (Thread *)item.GetUserData();
5052061da546Spatrick if (thread) {
5053061da546Spatrick thread->GetProcess()->GetThreadList().SetSelectedThreadByID(
5054061da546Spatrick thread->GetID());
5055061da546Spatrick const uint64_t frame_idx = item.GetIdentifier();
5056061da546Spatrick thread->SetSelectedFrameByIndex(frame_idx);
5057061da546Spatrick return true;
5058061da546Spatrick }
5059061da546Spatrick return false;
5060061da546Spatrick }
5061061da546Spatrick
5062061da546Spatrick protected:
5063061da546Spatrick FormatEntity::Entry m_format;
5064061da546Spatrick };
5065061da546Spatrick
5066061da546Spatrick class ThreadTreeDelegate : public TreeDelegate {
5067061da546Spatrick public:
ThreadTreeDelegate(Debugger & debugger)5068061da546Spatrick ThreadTreeDelegate(Debugger &debugger)
5069*f6aab3d8Srobert : TreeDelegate(), m_debugger(debugger) {
5070061da546Spatrick FormatEntity::Parse("thread #${thread.index}: tid = ${thread.id}{, stop "
5071061da546Spatrick "reason = ${thread.stop-reason}}",
5072061da546Spatrick m_format);
5073061da546Spatrick }
5074061da546Spatrick
5075061da546Spatrick ~ThreadTreeDelegate() override = default;
5076061da546Spatrick
GetProcess()5077061da546Spatrick ProcessSP GetProcess() {
5078061da546Spatrick return m_debugger.GetCommandInterpreter()
5079061da546Spatrick .GetExecutionContext()
5080061da546Spatrick .GetProcessSP();
5081061da546Spatrick }
5082061da546Spatrick
GetThread(const TreeItem & item)5083061da546Spatrick ThreadSP GetThread(const TreeItem &item) {
5084061da546Spatrick ProcessSP process_sp = GetProcess();
5085061da546Spatrick if (process_sp)
5086061da546Spatrick return process_sp->GetThreadList().FindThreadByID(item.GetIdentifier());
5087061da546Spatrick return ThreadSP();
5088061da546Spatrick }
5089061da546Spatrick
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5090061da546Spatrick void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5091061da546Spatrick ThreadSP thread_sp = GetThread(item);
5092061da546Spatrick if (thread_sp) {
5093061da546Spatrick StreamString strm;
5094061da546Spatrick ExecutionContext exe_ctx(thread_sp);
5095061da546Spatrick if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
5096061da546Spatrick nullptr, false, false)) {
5097061da546Spatrick int right_pad = 1;
5098be691f3bSpatrick window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
5099061da546Spatrick }
5100061da546Spatrick }
5101061da546Spatrick }
5102061da546Spatrick
TreeDelegateGenerateChildren(TreeItem & item)5103061da546Spatrick void TreeDelegateGenerateChildren(TreeItem &item) override {
5104061da546Spatrick ProcessSP process_sp = GetProcess();
5105061da546Spatrick if (process_sp && process_sp->IsAlive()) {
5106061da546Spatrick StateType state = process_sp->GetState();
5107061da546Spatrick if (StateIsStoppedState(state, true)) {
5108061da546Spatrick ThreadSP thread_sp = GetThread(item);
5109061da546Spatrick if (thread_sp) {
5110061da546Spatrick if (m_stop_id == process_sp->GetStopID() &&
5111061da546Spatrick thread_sp->GetID() == m_tid)
5112061da546Spatrick return; // Children are already up to date
5113061da546Spatrick if (!m_frame_delegate_sp) {
5114061da546Spatrick // Always expand the thread item the first time we show it
5115061da546Spatrick m_frame_delegate_sp = std::make_shared<FrameTreeDelegate>();
5116061da546Spatrick }
5117061da546Spatrick
5118061da546Spatrick m_stop_id = process_sp->GetStopID();
5119061da546Spatrick m_tid = thread_sp->GetID();
5120061da546Spatrick
5121061da546Spatrick TreeItem t(&item, *m_frame_delegate_sp, false);
5122061da546Spatrick size_t num_frames = thread_sp->GetStackFrameCount();
5123061da546Spatrick item.Resize(num_frames, t);
5124061da546Spatrick for (size_t i = 0; i < num_frames; ++i) {
5125061da546Spatrick item[i].SetUserData(thread_sp.get());
5126061da546Spatrick item[i].SetIdentifier(i);
5127061da546Spatrick }
5128061da546Spatrick }
5129061da546Spatrick return;
5130061da546Spatrick }
5131061da546Spatrick }
5132061da546Spatrick item.ClearChildren();
5133061da546Spatrick }
5134061da546Spatrick
TreeDelegateItemSelected(TreeItem & item)5135061da546Spatrick bool TreeDelegateItemSelected(TreeItem &item) override {
5136061da546Spatrick ProcessSP process_sp = GetProcess();
5137061da546Spatrick if (process_sp && process_sp->IsAlive()) {
5138061da546Spatrick StateType state = process_sp->GetState();
5139061da546Spatrick if (StateIsStoppedState(state, true)) {
5140061da546Spatrick ThreadSP thread_sp = GetThread(item);
5141061da546Spatrick if (thread_sp) {
5142061da546Spatrick ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList();
5143061da546Spatrick std::lock_guard<std::recursive_mutex> guard(thread_list.GetMutex());
5144061da546Spatrick ThreadSP selected_thread_sp = thread_list.GetSelectedThread();
5145061da546Spatrick if (selected_thread_sp->GetID() != thread_sp->GetID()) {
5146061da546Spatrick thread_list.SetSelectedThreadByID(thread_sp->GetID());
5147061da546Spatrick return true;
5148061da546Spatrick }
5149061da546Spatrick }
5150061da546Spatrick }
5151061da546Spatrick }
5152061da546Spatrick return false;
5153061da546Spatrick }
5154061da546Spatrick
5155061da546Spatrick protected:
5156061da546Spatrick Debugger &m_debugger;
5157061da546Spatrick std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp;
5158*f6aab3d8Srobert lldb::user_id_t m_tid = LLDB_INVALID_THREAD_ID;
5159*f6aab3d8Srobert uint32_t m_stop_id = UINT32_MAX;
5160061da546Spatrick FormatEntity::Entry m_format;
5161061da546Spatrick };
5162061da546Spatrick
5163061da546Spatrick class ThreadsTreeDelegate : public TreeDelegate {
5164061da546Spatrick public:
ThreadsTreeDelegate(Debugger & debugger)5165061da546Spatrick ThreadsTreeDelegate(Debugger &debugger)
5166*f6aab3d8Srobert : TreeDelegate(), m_thread_delegate_sp(), m_debugger(debugger) {
5167061da546Spatrick FormatEntity::Parse("process ${process.id}{, name = ${process.name}}",
5168061da546Spatrick m_format);
5169061da546Spatrick }
5170061da546Spatrick
5171061da546Spatrick ~ThreadsTreeDelegate() override = default;
5172061da546Spatrick
GetProcess()5173061da546Spatrick ProcessSP GetProcess() {
5174061da546Spatrick return m_debugger.GetCommandInterpreter()
5175061da546Spatrick .GetExecutionContext()
5176061da546Spatrick .GetProcessSP();
5177061da546Spatrick }
5178061da546Spatrick
TreeDelegateShouldDraw()5179*f6aab3d8Srobert bool TreeDelegateShouldDraw() override {
5180*f6aab3d8Srobert ProcessSP process = GetProcess();
5181*f6aab3d8Srobert if (!process)
5182*f6aab3d8Srobert return false;
5183*f6aab3d8Srobert
5184*f6aab3d8Srobert if (StateIsRunningState(process->GetState()))
5185*f6aab3d8Srobert return false;
5186*f6aab3d8Srobert
5187*f6aab3d8Srobert return true;
5188*f6aab3d8Srobert }
5189*f6aab3d8Srobert
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5190061da546Spatrick void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5191061da546Spatrick ProcessSP process_sp = GetProcess();
5192061da546Spatrick if (process_sp && process_sp->IsAlive()) {
5193061da546Spatrick StreamString strm;
5194061da546Spatrick ExecutionContext exe_ctx(process_sp);
5195061da546Spatrick if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
5196061da546Spatrick nullptr, false, false)) {
5197061da546Spatrick int right_pad = 1;
5198be691f3bSpatrick window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
5199061da546Spatrick }
5200061da546Spatrick }
5201061da546Spatrick }
5202061da546Spatrick
TreeDelegateGenerateChildren(TreeItem & item)5203061da546Spatrick void TreeDelegateGenerateChildren(TreeItem &item) override {
5204061da546Spatrick ProcessSP process_sp = GetProcess();
5205be691f3bSpatrick m_update_selection = false;
5206061da546Spatrick if (process_sp && process_sp->IsAlive()) {
5207061da546Spatrick StateType state = process_sp->GetState();
5208061da546Spatrick if (StateIsStoppedState(state, true)) {
5209061da546Spatrick const uint32_t stop_id = process_sp->GetStopID();
5210061da546Spatrick if (m_stop_id == stop_id)
5211061da546Spatrick return; // Children are already up to date
5212061da546Spatrick
5213061da546Spatrick m_stop_id = stop_id;
5214be691f3bSpatrick m_update_selection = true;
5215061da546Spatrick
5216061da546Spatrick if (!m_thread_delegate_sp) {
5217061da546Spatrick // Always expand the thread item the first time we show it
5218061da546Spatrick // item.Expand();
5219061da546Spatrick m_thread_delegate_sp =
5220061da546Spatrick std::make_shared<ThreadTreeDelegate>(m_debugger);
5221061da546Spatrick }
5222061da546Spatrick
5223061da546Spatrick TreeItem t(&item, *m_thread_delegate_sp, false);
5224061da546Spatrick ThreadList &threads = process_sp->GetThreadList();
5225061da546Spatrick std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
5226be691f3bSpatrick ThreadSP selected_thread = threads.GetSelectedThread();
5227061da546Spatrick size_t num_threads = threads.GetSize();
5228061da546Spatrick item.Resize(num_threads, t);
5229061da546Spatrick for (size_t i = 0; i < num_threads; ++i) {
5230be691f3bSpatrick ThreadSP thread = threads.GetThreadAtIndex(i);
5231be691f3bSpatrick item[i].SetIdentifier(thread->GetID());
5232061da546Spatrick item[i].SetMightHaveChildren(true);
5233be691f3bSpatrick if (selected_thread->GetID() == thread->GetID())
5234be691f3bSpatrick item[i].Expand();
5235061da546Spatrick }
5236061da546Spatrick return;
5237061da546Spatrick }
5238061da546Spatrick }
5239061da546Spatrick item.ClearChildren();
5240061da546Spatrick }
5241061da546Spatrick
TreeDelegateUpdateSelection(TreeItem & root,int & selection_index,TreeItem * & selected_item)5242be691f3bSpatrick void TreeDelegateUpdateSelection(TreeItem &root, int &selection_index,
5243be691f3bSpatrick TreeItem *&selected_item) override {
5244be691f3bSpatrick if (!m_update_selection)
5245be691f3bSpatrick return;
5246be691f3bSpatrick
5247be691f3bSpatrick ProcessSP process_sp = GetProcess();
5248be691f3bSpatrick if (!(process_sp && process_sp->IsAlive()))
5249be691f3bSpatrick return;
5250be691f3bSpatrick
5251be691f3bSpatrick StateType state = process_sp->GetState();
5252be691f3bSpatrick if (!StateIsStoppedState(state, true))
5253be691f3bSpatrick return;
5254be691f3bSpatrick
5255be691f3bSpatrick ThreadList &threads = process_sp->GetThreadList();
5256be691f3bSpatrick std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
5257be691f3bSpatrick ThreadSP selected_thread = threads.GetSelectedThread();
5258be691f3bSpatrick size_t num_threads = threads.GetSize();
5259be691f3bSpatrick for (size_t i = 0; i < num_threads; ++i) {
5260be691f3bSpatrick ThreadSP thread = threads.GetThreadAtIndex(i);
5261be691f3bSpatrick if (selected_thread->GetID() == thread->GetID()) {
5262be691f3bSpatrick selected_item = &root[i][thread->GetSelectedFrameIndex()];
5263be691f3bSpatrick selection_index = selected_item->GetRowIndex();
5264be691f3bSpatrick return;
5265be691f3bSpatrick }
5266be691f3bSpatrick }
5267be691f3bSpatrick }
5268be691f3bSpatrick
TreeDelegateItemSelected(TreeItem & item)5269061da546Spatrick bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5270061da546Spatrick
TreeDelegateExpandRootByDefault()5271be691f3bSpatrick bool TreeDelegateExpandRootByDefault() override { return true; }
5272be691f3bSpatrick
5273061da546Spatrick protected:
5274061da546Spatrick std::shared_ptr<ThreadTreeDelegate> m_thread_delegate_sp;
5275061da546Spatrick Debugger &m_debugger;
5276*f6aab3d8Srobert uint32_t m_stop_id = UINT32_MAX;
5277*f6aab3d8Srobert bool m_update_selection = false;
5278061da546Spatrick FormatEntity::Entry m_format;
5279061da546Spatrick };
5280061da546Spatrick
5281*f6aab3d8Srobert class BreakpointLocationTreeDelegate : public TreeDelegate {
5282*f6aab3d8Srobert public:
BreakpointLocationTreeDelegate(Debugger & debugger)5283*f6aab3d8Srobert BreakpointLocationTreeDelegate(Debugger &debugger)
5284*f6aab3d8Srobert : TreeDelegate(), m_debugger(debugger) {}
5285*f6aab3d8Srobert
5286*f6aab3d8Srobert ~BreakpointLocationTreeDelegate() override = default;
5287*f6aab3d8Srobert
GetProcess()5288*f6aab3d8Srobert Process *GetProcess() {
5289*f6aab3d8Srobert ExecutionContext exe_ctx(
5290*f6aab3d8Srobert m_debugger.GetCommandInterpreter().GetExecutionContext());
5291*f6aab3d8Srobert return exe_ctx.GetProcessPtr();
5292*f6aab3d8Srobert }
5293*f6aab3d8Srobert
GetBreakpointLocation(const TreeItem & item)5294*f6aab3d8Srobert BreakpointLocationSP GetBreakpointLocation(const TreeItem &item) {
5295*f6aab3d8Srobert Breakpoint *breakpoint = (Breakpoint *)item.GetUserData();
5296*f6aab3d8Srobert return breakpoint->GetLocationAtIndex(item.GetIdentifier());
5297*f6aab3d8Srobert }
5298*f6aab3d8Srobert
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5299*f6aab3d8Srobert void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5300*f6aab3d8Srobert BreakpointLocationSP breakpoint_location = GetBreakpointLocation(item);
5301*f6aab3d8Srobert Process *process = GetProcess();
5302*f6aab3d8Srobert StreamString stream;
5303*f6aab3d8Srobert stream.Printf("%i.%i: ", breakpoint_location->GetBreakpoint().GetID(),
5304*f6aab3d8Srobert breakpoint_location->GetID());
5305*f6aab3d8Srobert Address address = breakpoint_location->GetAddress();
5306*f6aab3d8Srobert address.Dump(&stream, process, Address::DumpStyleResolvedDescription,
5307*f6aab3d8Srobert Address::DumpStyleInvalid);
5308*f6aab3d8Srobert window.PutCStringTruncated(1, stream.GetString().str().c_str());
5309*f6aab3d8Srobert }
5310*f6aab3d8Srobert
ComputeDetailsList(BreakpointLocationSP breakpoint_location)5311*f6aab3d8Srobert StringList ComputeDetailsList(BreakpointLocationSP breakpoint_location) {
5312*f6aab3d8Srobert StringList details;
5313*f6aab3d8Srobert
5314*f6aab3d8Srobert Address address = breakpoint_location->GetAddress();
5315*f6aab3d8Srobert SymbolContext symbol_context;
5316*f6aab3d8Srobert address.CalculateSymbolContext(&symbol_context);
5317*f6aab3d8Srobert
5318*f6aab3d8Srobert if (symbol_context.module_sp) {
5319*f6aab3d8Srobert StreamString module_stream;
5320*f6aab3d8Srobert module_stream.PutCString("module = ");
5321*f6aab3d8Srobert symbol_context.module_sp->GetFileSpec().Dump(
5322*f6aab3d8Srobert module_stream.AsRawOstream());
5323*f6aab3d8Srobert details.AppendString(module_stream.GetString());
5324*f6aab3d8Srobert }
5325*f6aab3d8Srobert
5326*f6aab3d8Srobert if (symbol_context.comp_unit != nullptr) {
5327*f6aab3d8Srobert StreamString compile_unit_stream;
5328*f6aab3d8Srobert compile_unit_stream.PutCString("compile unit = ");
5329*f6aab3d8Srobert symbol_context.comp_unit->GetPrimaryFile().GetFilename().Dump(
5330*f6aab3d8Srobert &compile_unit_stream);
5331*f6aab3d8Srobert details.AppendString(compile_unit_stream.GetString());
5332*f6aab3d8Srobert
5333*f6aab3d8Srobert if (symbol_context.function != nullptr) {
5334*f6aab3d8Srobert StreamString function_stream;
5335*f6aab3d8Srobert function_stream.PutCString("function = ");
5336*f6aab3d8Srobert function_stream.PutCString(
5337*f6aab3d8Srobert symbol_context.function->GetName().AsCString("<unknown>"));
5338*f6aab3d8Srobert details.AppendString(function_stream.GetString());
5339*f6aab3d8Srobert }
5340*f6aab3d8Srobert
5341*f6aab3d8Srobert if (symbol_context.line_entry.line > 0) {
5342*f6aab3d8Srobert StreamString location_stream;
5343*f6aab3d8Srobert location_stream.PutCString("location = ");
5344*f6aab3d8Srobert symbol_context.line_entry.DumpStopContext(&location_stream, true);
5345*f6aab3d8Srobert details.AppendString(location_stream.GetString());
5346*f6aab3d8Srobert }
5347*f6aab3d8Srobert
5348*f6aab3d8Srobert } else {
5349*f6aab3d8Srobert if (symbol_context.symbol) {
5350*f6aab3d8Srobert StreamString symbol_stream;
5351*f6aab3d8Srobert if (breakpoint_location->IsReExported())
5352*f6aab3d8Srobert symbol_stream.PutCString("re-exported target = ");
5353*f6aab3d8Srobert else
5354*f6aab3d8Srobert symbol_stream.PutCString("symbol = ");
5355*f6aab3d8Srobert symbol_stream.PutCString(
5356*f6aab3d8Srobert symbol_context.symbol->GetName().AsCString("<unknown>"));
5357*f6aab3d8Srobert details.AppendString(symbol_stream.GetString());
5358*f6aab3d8Srobert }
5359*f6aab3d8Srobert }
5360*f6aab3d8Srobert
5361*f6aab3d8Srobert Process *process = GetProcess();
5362*f6aab3d8Srobert
5363*f6aab3d8Srobert StreamString address_stream;
5364*f6aab3d8Srobert address.Dump(&address_stream, process, Address::DumpStyleLoadAddress,
5365*f6aab3d8Srobert Address::DumpStyleModuleWithFileAddress);
5366*f6aab3d8Srobert details.AppendString(address_stream.GetString());
5367*f6aab3d8Srobert
5368*f6aab3d8Srobert BreakpointSiteSP breakpoint_site = breakpoint_location->GetBreakpointSite();
5369*f6aab3d8Srobert if (breakpoint_location->IsIndirect() && breakpoint_site) {
5370*f6aab3d8Srobert Address resolved_address;
5371*f6aab3d8Srobert resolved_address.SetLoadAddress(breakpoint_site->GetLoadAddress(),
5372*f6aab3d8Srobert &breakpoint_location->GetTarget());
5373*f6aab3d8Srobert Symbol *resolved_symbol = resolved_address.CalculateSymbolContextSymbol();
5374*f6aab3d8Srobert if (resolved_symbol) {
5375*f6aab3d8Srobert StreamString indirect_target_stream;
5376*f6aab3d8Srobert indirect_target_stream.PutCString("indirect target = ");
5377*f6aab3d8Srobert indirect_target_stream.PutCString(
5378*f6aab3d8Srobert resolved_symbol->GetName().GetCString());
5379*f6aab3d8Srobert details.AppendString(indirect_target_stream.GetString());
5380*f6aab3d8Srobert }
5381*f6aab3d8Srobert }
5382*f6aab3d8Srobert
5383*f6aab3d8Srobert bool is_resolved = breakpoint_location->IsResolved();
5384*f6aab3d8Srobert StreamString resolved_stream;
5385*f6aab3d8Srobert resolved_stream.Printf("resolved = %s", is_resolved ? "true" : "false");
5386*f6aab3d8Srobert details.AppendString(resolved_stream.GetString());
5387*f6aab3d8Srobert
5388*f6aab3d8Srobert bool is_hardware = is_resolved && breakpoint_site->IsHardware();
5389*f6aab3d8Srobert StreamString hardware_stream;
5390*f6aab3d8Srobert hardware_stream.Printf("hardware = %s", is_hardware ? "true" : "false");
5391*f6aab3d8Srobert details.AppendString(hardware_stream.GetString());
5392*f6aab3d8Srobert
5393*f6aab3d8Srobert StreamString hit_count_stream;
5394*f6aab3d8Srobert hit_count_stream.Printf("hit count = %-4u",
5395*f6aab3d8Srobert breakpoint_location->GetHitCount());
5396*f6aab3d8Srobert details.AppendString(hit_count_stream.GetString());
5397*f6aab3d8Srobert
5398*f6aab3d8Srobert return details;
5399*f6aab3d8Srobert }
5400*f6aab3d8Srobert
TreeDelegateGenerateChildren(TreeItem & item)5401*f6aab3d8Srobert void TreeDelegateGenerateChildren(TreeItem &item) override {
5402*f6aab3d8Srobert BreakpointLocationSP breakpoint_location = GetBreakpointLocation(item);
5403*f6aab3d8Srobert StringList details = ComputeDetailsList(breakpoint_location);
5404*f6aab3d8Srobert
5405*f6aab3d8Srobert if (!m_string_delegate_sp)
5406*f6aab3d8Srobert m_string_delegate_sp = std::make_shared<TextTreeDelegate>();
5407*f6aab3d8Srobert TreeItem details_tree_item(&item, *m_string_delegate_sp, false);
5408*f6aab3d8Srobert
5409*f6aab3d8Srobert item.Resize(details.GetSize(), details_tree_item);
5410*f6aab3d8Srobert for (size_t i = 0; i < details.GetSize(); i++) {
5411*f6aab3d8Srobert item[i].SetText(details.GetStringAtIndex(i));
5412*f6aab3d8Srobert }
5413*f6aab3d8Srobert }
5414*f6aab3d8Srobert
TreeDelegateItemSelected(TreeItem & item)5415*f6aab3d8Srobert bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5416*f6aab3d8Srobert
5417*f6aab3d8Srobert protected:
5418*f6aab3d8Srobert Debugger &m_debugger;
5419*f6aab3d8Srobert std::shared_ptr<TextTreeDelegate> m_string_delegate_sp;
5420*f6aab3d8Srobert };
5421*f6aab3d8Srobert
5422*f6aab3d8Srobert class BreakpointTreeDelegate : public TreeDelegate {
5423*f6aab3d8Srobert public:
BreakpointTreeDelegate(Debugger & debugger)5424*f6aab3d8Srobert BreakpointTreeDelegate(Debugger &debugger)
5425*f6aab3d8Srobert : TreeDelegate(), m_debugger(debugger),
5426*f6aab3d8Srobert m_breakpoint_location_delegate_sp() {}
5427*f6aab3d8Srobert
5428*f6aab3d8Srobert ~BreakpointTreeDelegate() override = default;
5429*f6aab3d8Srobert
GetBreakpoint(const TreeItem & item)5430*f6aab3d8Srobert BreakpointSP GetBreakpoint(const TreeItem &item) {
5431*f6aab3d8Srobert TargetSP target = m_debugger.GetSelectedTarget();
5432*f6aab3d8Srobert BreakpointList &breakpoints = target->GetBreakpointList(false);
5433*f6aab3d8Srobert return breakpoints.GetBreakpointAtIndex(item.GetIdentifier());
5434*f6aab3d8Srobert }
5435*f6aab3d8Srobert
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5436*f6aab3d8Srobert void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5437*f6aab3d8Srobert BreakpointSP breakpoint = GetBreakpoint(item);
5438*f6aab3d8Srobert StreamString stream;
5439*f6aab3d8Srobert stream.Format("{0}: ", breakpoint->GetID());
5440*f6aab3d8Srobert breakpoint->GetResolverDescription(&stream);
5441*f6aab3d8Srobert breakpoint->GetFilterDescription(&stream);
5442*f6aab3d8Srobert window.PutCStringTruncated(1, stream.GetString().str().c_str());
5443*f6aab3d8Srobert }
5444*f6aab3d8Srobert
TreeDelegateGenerateChildren(TreeItem & item)5445*f6aab3d8Srobert void TreeDelegateGenerateChildren(TreeItem &item) override {
5446*f6aab3d8Srobert BreakpointSP breakpoint = GetBreakpoint(item);
5447*f6aab3d8Srobert
5448*f6aab3d8Srobert if (!m_breakpoint_location_delegate_sp)
5449*f6aab3d8Srobert m_breakpoint_location_delegate_sp =
5450*f6aab3d8Srobert std::make_shared<BreakpointLocationTreeDelegate>(m_debugger);
5451*f6aab3d8Srobert TreeItem breakpoint_location_tree_item(
5452*f6aab3d8Srobert &item, *m_breakpoint_location_delegate_sp, true);
5453*f6aab3d8Srobert
5454*f6aab3d8Srobert item.Resize(breakpoint->GetNumLocations(), breakpoint_location_tree_item);
5455*f6aab3d8Srobert for (size_t i = 0; i < breakpoint->GetNumLocations(); i++) {
5456*f6aab3d8Srobert item[i].SetIdentifier(i);
5457*f6aab3d8Srobert item[i].SetUserData(breakpoint.get());
5458*f6aab3d8Srobert }
5459*f6aab3d8Srobert }
5460*f6aab3d8Srobert
TreeDelegateItemSelected(TreeItem & item)5461*f6aab3d8Srobert bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5462*f6aab3d8Srobert
5463*f6aab3d8Srobert protected:
5464*f6aab3d8Srobert Debugger &m_debugger;
5465*f6aab3d8Srobert std::shared_ptr<BreakpointLocationTreeDelegate>
5466*f6aab3d8Srobert m_breakpoint_location_delegate_sp;
5467*f6aab3d8Srobert };
5468*f6aab3d8Srobert
5469*f6aab3d8Srobert class BreakpointsTreeDelegate : public TreeDelegate {
5470*f6aab3d8Srobert public:
BreakpointsTreeDelegate(Debugger & debugger)5471*f6aab3d8Srobert BreakpointsTreeDelegate(Debugger &debugger)
5472*f6aab3d8Srobert : TreeDelegate(), m_debugger(debugger), m_breakpoint_delegate_sp() {}
5473*f6aab3d8Srobert
5474*f6aab3d8Srobert ~BreakpointsTreeDelegate() override = default;
5475*f6aab3d8Srobert
TreeDelegateShouldDraw()5476*f6aab3d8Srobert bool TreeDelegateShouldDraw() override {
5477*f6aab3d8Srobert TargetSP target = m_debugger.GetSelectedTarget();
5478*f6aab3d8Srobert if (!target)
5479*f6aab3d8Srobert return false;
5480*f6aab3d8Srobert
5481*f6aab3d8Srobert return true;
5482*f6aab3d8Srobert }
5483*f6aab3d8Srobert
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5484*f6aab3d8Srobert void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5485*f6aab3d8Srobert window.PutCString("Breakpoints");
5486*f6aab3d8Srobert }
5487*f6aab3d8Srobert
TreeDelegateGenerateChildren(TreeItem & item)5488*f6aab3d8Srobert void TreeDelegateGenerateChildren(TreeItem &item) override {
5489*f6aab3d8Srobert TargetSP target = m_debugger.GetSelectedTarget();
5490*f6aab3d8Srobert
5491*f6aab3d8Srobert BreakpointList &breakpoints = target->GetBreakpointList(false);
5492*f6aab3d8Srobert std::unique_lock<std::recursive_mutex> lock;
5493*f6aab3d8Srobert breakpoints.GetListMutex(lock);
5494*f6aab3d8Srobert
5495*f6aab3d8Srobert if (!m_breakpoint_delegate_sp)
5496*f6aab3d8Srobert m_breakpoint_delegate_sp =
5497*f6aab3d8Srobert std::make_shared<BreakpointTreeDelegate>(m_debugger);
5498*f6aab3d8Srobert TreeItem breakpoint_tree_item(&item, *m_breakpoint_delegate_sp, true);
5499*f6aab3d8Srobert
5500*f6aab3d8Srobert item.Resize(breakpoints.GetSize(), breakpoint_tree_item);
5501*f6aab3d8Srobert for (size_t i = 0; i < breakpoints.GetSize(); i++) {
5502*f6aab3d8Srobert item[i].SetIdentifier(i);
5503*f6aab3d8Srobert }
5504*f6aab3d8Srobert }
5505*f6aab3d8Srobert
TreeDelegateItemSelected(TreeItem & item)5506*f6aab3d8Srobert bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5507*f6aab3d8Srobert
TreeDelegateExpandRootByDefault()5508*f6aab3d8Srobert bool TreeDelegateExpandRootByDefault() override { return true; }
5509*f6aab3d8Srobert
5510*f6aab3d8Srobert protected:
5511*f6aab3d8Srobert Debugger &m_debugger;
5512*f6aab3d8Srobert std::shared_ptr<BreakpointTreeDelegate> m_breakpoint_delegate_sp;
5513*f6aab3d8Srobert };
5514*f6aab3d8Srobert
5515061da546Spatrick class ValueObjectListDelegate : public WindowDelegate {
5516061da546Spatrick public:
ValueObjectListDelegate()5517be691f3bSpatrick ValueObjectListDelegate() : m_rows() {}
5518061da546Spatrick
ValueObjectListDelegate(ValueObjectList & valobj_list)5519*f6aab3d8Srobert ValueObjectListDelegate(ValueObjectList &valobj_list) : m_rows() {
5520061da546Spatrick SetValues(valobj_list);
5521061da546Spatrick }
5522061da546Spatrick
5523061da546Spatrick ~ValueObjectListDelegate() override = default;
5524061da546Spatrick
SetValues(ValueObjectList & valobj_list)5525061da546Spatrick void SetValues(ValueObjectList &valobj_list) {
5526061da546Spatrick m_selected_row = nullptr;
5527061da546Spatrick m_selected_row_idx = 0;
5528061da546Spatrick m_first_visible_row = 0;
5529061da546Spatrick m_num_rows = 0;
5530061da546Spatrick m_rows.clear();
5531061da546Spatrick for (auto &valobj_sp : valobj_list.GetObjects())
5532061da546Spatrick m_rows.push_back(Row(valobj_sp, nullptr));
5533061da546Spatrick }
5534061da546Spatrick
WindowDelegateDraw(Window & window,bool force)5535061da546Spatrick bool WindowDelegateDraw(Window &window, bool force) override {
5536061da546Spatrick m_num_rows = 0;
5537061da546Spatrick m_min_x = 2;
5538061da546Spatrick m_min_y = 1;
5539061da546Spatrick m_max_x = window.GetWidth() - 1;
5540061da546Spatrick m_max_y = window.GetHeight() - 1;
5541061da546Spatrick
5542061da546Spatrick window.Erase();
5543061da546Spatrick window.DrawTitleBox(window.GetName());
5544061da546Spatrick
5545061da546Spatrick const int num_visible_rows = NumVisibleRows();
5546061da546Spatrick const int num_rows = CalculateTotalNumberRows(m_rows);
5547061da546Spatrick
5548061da546Spatrick // If we unexpanded while having something selected our total number of
5549061da546Spatrick // rows is less than the num visible rows, then make sure we show all the
5550061da546Spatrick // rows by setting the first visible row accordingly.
5551061da546Spatrick if (m_first_visible_row > 0 && num_rows < num_visible_rows)
5552061da546Spatrick m_first_visible_row = 0;
5553061da546Spatrick
5554061da546Spatrick // Make sure the selected row is always visible
5555061da546Spatrick if (m_selected_row_idx < m_first_visible_row)
5556061da546Spatrick m_first_visible_row = m_selected_row_idx;
5557061da546Spatrick else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
5558061da546Spatrick m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
5559061da546Spatrick
5560061da546Spatrick DisplayRows(window, m_rows, g_options);
5561061da546Spatrick
5562061da546Spatrick // Get the selected row
5563061da546Spatrick m_selected_row = GetRowForRowIndex(m_selected_row_idx);
5564061da546Spatrick // Keep the cursor on the selected row so the highlight and the cursor are
5565061da546Spatrick // always on the same line
5566061da546Spatrick if (m_selected_row)
5567061da546Spatrick window.MoveCursor(m_selected_row->x, m_selected_row->y);
5568061da546Spatrick
5569061da546Spatrick return true; // Drawing handled
5570061da546Spatrick }
5571061da546Spatrick
WindowDelegateGetKeyHelp()5572061da546Spatrick KeyHelp *WindowDelegateGetKeyHelp() override {
5573061da546Spatrick static curses::KeyHelp g_source_view_key_help[] = {
5574061da546Spatrick {KEY_UP, "Select previous item"},
5575061da546Spatrick {KEY_DOWN, "Select next item"},
5576061da546Spatrick {KEY_RIGHT, "Expand selected item"},
5577061da546Spatrick {KEY_LEFT, "Unexpand selected item or select parent if not expanded"},
5578061da546Spatrick {KEY_PPAGE, "Page up"},
5579061da546Spatrick {KEY_NPAGE, "Page down"},
5580061da546Spatrick {'A', "Format as annotated address"},
5581061da546Spatrick {'b', "Format as binary"},
5582061da546Spatrick {'B', "Format as hex bytes with ASCII"},
5583061da546Spatrick {'c', "Format as character"},
5584061da546Spatrick {'d', "Format as a signed integer"},
5585061da546Spatrick {'D', "Format selected value using the default format for the type"},
5586061da546Spatrick {'f', "Format as float"},
5587061da546Spatrick {'h', "Show help dialog"},
5588061da546Spatrick {'i', "Format as instructions"},
5589061da546Spatrick {'o', "Format as octal"},
5590061da546Spatrick {'p', "Format as pointer"},
5591061da546Spatrick {'s', "Format as C string"},
5592061da546Spatrick {'t', "Toggle showing/hiding type names"},
5593061da546Spatrick {'u', "Format as an unsigned integer"},
5594061da546Spatrick {'x', "Format as hex"},
5595061da546Spatrick {'X', "Format as uppercase hex"},
5596061da546Spatrick {' ', "Toggle item expansion"},
5597061da546Spatrick {',', "Page up"},
5598061da546Spatrick {'.', "Page down"},
5599061da546Spatrick {'\0', nullptr}};
5600061da546Spatrick return g_source_view_key_help;
5601061da546Spatrick }
5602061da546Spatrick
WindowDelegateHandleChar(Window & window,int c)5603061da546Spatrick HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
5604061da546Spatrick switch (c) {
5605061da546Spatrick case 'x':
5606061da546Spatrick case 'X':
5607061da546Spatrick case 'o':
5608061da546Spatrick case 's':
5609061da546Spatrick case 'u':
5610061da546Spatrick case 'd':
5611061da546Spatrick case 'D':
5612061da546Spatrick case 'i':
5613061da546Spatrick case 'A':
5614061da546Spatrick case 'p':
5615061da546Spatrick case 'c':
5616061da546Spatrick case 'b':
5617061da546Spatrick case 'B':
5618061da546Spatrick case 'f':
5619061da546Spatrick // Change the format for the currently selected item
5620061da546Spatrick if (m_selected_row) {
5621061da546Spatrick auto valobj_sp = m_selected_row->value.GetSP();
5622061da546Spatrick if (valobj_sp)
5623061da546Spatrick valobj_sp->SetFormat(FormatForChar(c));
5624061da546Spatrick }
5625061da546Spatrick return eKeyHandled;
5626061da546Spatrick
5627061da546Spatrick case 't':
5628061da546Spatrick // Toggle showing type names
5629061da546Spatrick g_options.show_types = !g_options.show_types;
5630061da546Spatrick return eKeyHandled;
5631061da546Spatrick
5632061da546Spatrick case ',':
5633061da546Spatrick case KEY_PPAGE:
5634061da546Spatrick // Page up key
5635061da546Spatrick if (m_first_visible_row > 0) {
5636061da546Spatrick if (static_cast<int>(m_first_visible_row) > m_max_y)
5637061da546Spatrick m_first_visible_row -= m_max_y;
5638061da546Spatrick else
5639061da546Spatrick m_first_visible_row = 0;
5640061da546Spatrick m_selected_row_idx = m_first_visible_row;
5641061da546Spatrick }
5642061da546Spatrick return eKeyHandled;
5643061da546Spatrick
5644061da546Spatrick case '.':
5645061da546Spatrick case KEY_NPAGE:
5646061da546Spatrick // Page down key
5647061da546Spatrick if (m_num_rows > static_cast<size_t>(m_max_y)) {
5648061da546Spatrick if (m_first_visible_row + m_max_y < m_num_rows) {
5649061da546Spatrick m_first_visible_row += m_max_y;
5650061da546Spatrick m_selected_row_idx = m_first_visible_row;
5651061da546Spatrick }
5652061da546Spatrick }
5653061da546Spatrick return eKeyHandled;
5654061da546Spatrick
5655061da546Spatrick case KEY_UP:
5656061da546Spatrick if (m_selected_row_idx > 0)
5657061da546Spatrick --m_selected_row_idx;
5658061da546Spatrick return eKeyHandled;
5659061da546Spatrick
5660061da546Spatrick case KEY_DOWN:
5661061da546Spatrick if (m_selected_row_idx + 1 < m_num_rows)
5662061da546Spatrick ++m_selected_row_idx;
5663061da546Spatrick return eKeyHandled;
5664061da546Spatrick
5665061da546Spatrick case KEY_RIGHT:
5666061da546Spatrick if (m_selected_row) {
5667061da546Spatrick if (!m_selected_row->expanded)
5668061da546Spatrick m_selected_row->Expand();
5669061da546Spatrick }
5670061da546Spatrick return eKeyHandled;
5671061da546Spatrick
5672061da546Spatrick case KEY_LEFT:
5673061da546Spatrick if (m_selected_row) {
5674061da546Spatrick if (m_selected_row->expanded)
5675061da546Spatrick m_selected_row->Unexpand();
5676061da546Spatrick else if (m_selected_row->parent)
5677061da546Spatrick m_selected_row_idx = m_selected_row->parent->row_idx;
5678061da546Spatrick }
5679061da546Spatrick return eKeyHandled;
5680061da546Spatrick
5681061da546Spatrick case ' ':
5682061da546Spatrick // Toggle expansion state when SPACE is pressed
5683061da546Spatrick if (m_selected_row) {
5684061da546Spatrick if (m_selected_row->expanded)
5685061da546Spatrick m_selected_row->Unexpand();
5686061da546Spatrick else
5687061da546Spatrick m_selected_row->Expand();
5688061da546Spatrick }
5689061da546Spatrick return eKeyHandled;
5690061da546Spatrick
5691061da546Spatrick case 'h':
5692061da546Spatrick window.CreateHelpSubwindow();
5693061da546Spatrick return eKeyHandled;
5694061da546Spatrick
5695061da546Spatrick default:
5696061da546Spatrick break;
5697061da546Spatrick }
5698061da546Spatrick return eKeyNotHandled;
5699061da546Spatrick }
5700061da546Spatrick
5701061da546Spatrick protected:
5702061da546Spatrick std::vector<Row> m_rows;
5703be691f3bSpatrick Row *m_selected_row = nullptr;
5704be691f3bSpatrick uint32_t m_selected_row_idx = 0;
5705be691f3bSpatrick uint32_t m_first_visible_row = 0;
5706be691f3bSpatrick uint32_t m_num_rows = 0;
5707*f6aab3d8Srobert int m_min_x = 0;
5708*f6aab3d8Srobert int m_min_y = 0;
5709be691f3bSpatrick int m_max_x = 0;
5710be691f3bSpatrick int m_max_y = 0;
5711061da546Spatrick
FormatForChar(int c)5712061da546Spatrick static Format FormatForChar(int c) {
5713061da546Spatrick switch (c) {
5714061da546Spatrick case 'x':
5715061da546Spatrick return eFormatHex;
5716061da546Spatrick case 'X':
5717061da546Spatrick return eFormatHexUppercase;
5718061da546Spatrick case 'o':
5719061da546Spatrick return eFormatOctal;
5720061da546Spatrick case 's':
5721061da546Spatrick return eFormatCString;
5722061da546Spatrick case 'u':
5723061da546Spatrick return eFormatUnsigned;
5724061da546Spatrick case 'd':
5725061da546Spatrick return eFormatDecimal;
5726061da546Spatrick case 'D':
5727061da546Spatrick return eFormatDefault;
5728061da546Spatrick case 'i':
5729061da546Spatrick return eFormatInstruction;
5730061da546Spatrick case 'A':
5731061da546Spatrick return eFormatAddressInfo;
5732061da546Spatrick case 'p':
5733061da546Spatrick return eFormatPointer;
5734061da546Spatrick case 'c':
5735061da546Spatrick return eFormatChar;
5736061da546Spatrick case 'b':
5737061da546Spatrick return eFormatBinary;
5738061da546Spatrick case 'B':
5739061da546Spatrick return eFormatBytesWithASCII;
5740061da546Spatrick case 'f':
5741061da546Spatrick return eFormatFloat;
5742061da546Spatrick }
5743061da546Spatrick return eFormatDefault;
5744061da546Spatrick }
5745061da546Spatrick
DisplayRowObject(Window & window,Row & row,DisplayOptions & options,bool highlight,bool last_child)5746061da546Spatrick bool DisplayRowObject(Window &window, Row &row, DisplayOptions &options,
5747061da546Spatrick bool highlight, bool last_child) {
5748061da546Spatrick ValueObject *valobj = row.value.GetSP().get();
5749061da546Spatrick
5750061da546Spatrick if (valobj == nullptr)
5751061da546Spatrick return false;
5752061da546Spatrick
5753061da546Spatrick const char *type_name =
5754061da546Spatrick options.show_types ? valobj->GetTypeName().GetCString() : nullptr;
5755061da546Spatrick const char *name = valobj->GetName().GetCString();
5756061da546Spatrick const char *value = valobj->GetValueAsCString();
5757061da546Spatrick const char *summary = valobj->GetSummaryAsCString();
5758061da546Spatrick
5759061da546Spatrick window.MoveCursor(row.x, row.y);
5760061da546Spatrick
5761061da546Spatrick row.DrawTree(window);
5762061da546Spatrick
5763061da546Spatrick if (highlight)
5764061da546Spatrick window.AttributeOn(A_REVERSE);
5765061da546Spatrick
5766061da546Spatrick if (type_name && type_name[0])
5767be691f3bSpatrick window.PrintfTruncated(1, "(%s) ", type_name);
5768061da546Spatrick
5769061da546Spatrick if (name && name[0])
5770be691f3bSpatrick window.PutCStringTruncated(1, name);
5771061da546Spatrick
5772061da546Spatrick attr_t changd_attr = 0;
5773061da546Spatrick if (valobj->GetValueDidChange())
5774be691f3bSpatrick changd_attr = COLOR_PAIR(RedOnBlack) | A_BOLD;
5775061da546Spatrick
5776061da546Spatrick if (value && value[0]) {
5777be691f3bSpatrick window.PutCStringTruncated(1, " = ");
5778061da546Spatrick if (changd_attr)
5779061da546Spatrick window.AttributeOn(changd_attr);
5780be691f3bSpatrick window.PutCStringTruncated(1, value);
5781061da546Spatrick if (changd_attr)
5782061da546Spatrick window.AttributeOff(changd_attr);
5783061da546Spatrick }
5784061da546Spatrick
5785061da546Spatrick if (summary && summary[0]) {
5786be691f3bSpatrick window.PutCStringTruncated(1, " ");
5787061da546Spatrick if (changd_attr)
5788061da546Spatrick window.AttributeOn(changd_attr);
5789be691f3bSpatrick window.PutCStringTruncated(1, summary);
5790061da546Spatrick if (changd_attr)
5791061da546Spatrick window.AttributeOff(changd_attr);
5792061da546Spatrick }
5793061da546Spatrick
5794061da546Spatrick if (highlight)
5795061da546Spatrick window.AttributeOff(A_REVERSE);
5796061da546Spatrick
5797061da546Spatrick return true;
5798061da546Spatrick }
5799061da546Spatrick
DisplayRows(Window & window,std::vector<Row> & rows,DisplayOptions & options)5800061da546Spatrick void DisplayRows(Window &window, std::vector<Row> &rows,
5801061da546Spatrick DisplayOptions &options) {
5802061da546Spatrick // > 0x25B7
5803061da546Spatrick // \/ 0x25BD
5804061da546Spatrick
5805061da546Spatrick bool window_is_active = window.IsActive();
5806061da546Spatrick for (auto &row : rows) {
5807061da546Spatrick const bool last_child = row.parent && &rows[rows.size() - 1] == &row;
5808061da546Spatrick // Save the row index in each Row structure
5809061da546Spatrick row.row_idx = m_num_rows;
5810061da546Spatrick if ((m_num_rows >= m_first_visible_row) &&
5811061da546Spatrick ((m_num_rows - m_first_visible_row) <
5812061da546Spatrick static_cast<size_t>(NumVisibleRows()))) {
5813061da546Spatrick row.x = m_min_x;
5814061da546Spatrick row.y = m_num_rows - m_first_visible_row + 1;
5815061da546Spatrick if (DisplayRowObject(window, row, options,
5816061da546Spatrick window_is_active &&
5817061da546Spatrick m_num_rows == m_selected_row_idx,
5818061da546Spatrick last_child)) {
5819061da546Spatrick ++m_num_rows;
5820061da546Spatrick } else {
5821061da546Spatrick row.x = 0;
5822061da546Spatrick row.y = 0;
5823061da546Spatrick }
5824061da546Spatrick } else {
5825061da546Spatrick row.x = 0;
5826061da546Spatrick row.y = 0;
5827061da546Spatrick ++m_num_rows;
5828061da546Spatrick }
5829061da546Spatrick
5830*f6aab3d8Srobert if (row.expanded) {
5831061da546Spatrick auto &children = row.GetChildren();
5832*f6aab3d8Srobert if (!children.empty()) {
5833061da546Spatrick DisplayRows(window, children, options);
5834061da546Spatrick }
5835061da546Spatrick }
5836061da546Spatrick }
5837*f6aab3d8Srobert }
5838061da546Spatrick
CalculateTotalNumberRows(std::vector<Row> & rows)5839061da546Spatrick int CalculateTotalNumberRows(std::vector<Row> &rows) {
5840061da546Spatrick int row_count = 0;
5841061da546Spatrick for (auto &row : rows) {
5842061da546Spatrick ++row_count;
5843061da546Spatrick if (row.expanded)
5844061da546Spatrick row_count += CalculateTotalNumberRows(row.GetChildren());
5845061da546Spatrick }
5846061da546Spatrick return row_count;
5847061da546Spatrick }
5848061da546Spatrick
GetRowForRowIndexImpl(std::vector<Row> & rows,size_t & row_index)5849061da546Spatrick static Row *GetRowForRowIndexImpl(std::vector<Row> &rows, size_t &row_index) {
5850061da546Spatrick for (auto &row : rows) {
5851061da546Spatrick if (row_index == 0)
5852061da546Spatrick return &row;
5853061da546Spatrick else {
5854061da546Spatrick --row_index;
5855*f6aab3d8Srobert if (row.expanded) {
5856061da546Spatrick auto &children = row.GetChildren();
5857*f6aab3d8Srobert if (!children.empty()) {
5858061da546Spatrick Row *result = GetRowForRowIndexImpl(children, row_index);
5859061da546Spatrick if (result)
5860061da546Spatrick return result;
5861061da546Spatrick }
5862061da546Spatrick }
5863061da546Spatrick }
5864*f6aab3d8Srobert }
5865061da546Spatrick return nullptr;
5866061da546Spatrick }
5867061da546Spatrick
GetRowForRowIndex(size_t row_index)5868061da546Spatrick Row *GetRowForRowIndex(size_t row_index) {
5869061da546Spatrick return GetRowForRowIndexImpl(m_rows, row_index);
5870061da546Spatrick }
5871061da546Spatrick
NumVisibleRows() const5872061da546Spatrick int NumVisibleRows() const { return m_max_y - m_min_y; }
5873061da546Spatrick
5874061da546Spatrick static DisplayOptions g_options;
5875061da546Spatrick };
5876061da546Spatrick
5877061da546Spatrick class FrameVariablesWindowDelegate : public ValueObjectListDelegate {
5878061da546Spatrick public:
FrameVariablesWindowDelegate(Debugger & debugger)5879061da546Spatrick FrameVariablesWindowDelegate(Debugger &debugger)
5880*f6aab3d8Srobert : ValueObjectListDelegate(), m_debugger(debugger) {}
5881061da546Spatrick
5882061da546Spatrick ~FrameVariablesWindowDelegate() override = default;
5883061da546Spatrick
WindowDelegateGetHelpText()5884061da546Spatrick const char *WindowDelegateGetHelpText() override {
5885061da546Spatrick return "Frame variable window keyboard shortcuts:";
5886061da546Spatrick }
5887061da546Spatrick
WindowDelegateDraw(Window & window,bool force)5888061da546Spatrick bool WindowDelegateDraw(Window &window, bool force) override {
5889061da546Spatrick ExecutionContext exe_ctx(
5890061da546Spatrick m_debugger.GetCommandInterpreter().GetExecutionContext());
5891061da546Spatrick Process *process = exe_ctx.GetProcessPtr();
5892061da546Spatrick Block *frame_block = nullptr;
5893061da546Spatrick StackFrame *frame = nullptr;
5894061da546Spatrick
5895061da546Spatrick if (process) {
5896061da546Spatrick StateType state = process->GetState();
5897061da546Spatrick if (StateIsStoppedState(state, true)) {
5898061da546Spatrick frame = exe_ctx.GetFramePtr();
5899061da546Spatrick if (frame)
5900061da546Spatrick frame_block = frame->GetFrameBlock();
5901061da546Spatrick } else if (StateIsRunningState(state)) {
5902061da546Spatrick return true; // Don't do any updating when we are running
5903061da546Spatrick }
5904061da546Spatrick }
5905061da546Spatrick
5906061da546Spatrick ValueObjectList local_values;
5907061da546Spatrick if (frame_block) {
5908061da546Spatrick // Only update the variables if they have changed
5909061da546Spatrick if (m_frame_block != frame_block) {
5910061da546Spatrick m_frame_block = frame_block;
5911061da546Spatrick
5912*f6aab3d8Srobert VariableList *locals = frame->GetVariableList(true, nullptr);
5913061da546Spatrick if (locals) {
5914061da546Spatrick const DynamicValueType use_dynamic = eDynamicDontRunTarget;
5915061da546Spatrick for (const VariableSP &local_sp : *locals) {
5916061da546Spatrick ValueObjectSP value_sp =
5917061da546Spatrick frame->GetValueObjectForFrameVariable(local_sp, use_dynamic);
5918061da546Spatrick if (value_sp) {
5919061da546Spatrick ValueObjectSP synthetic_value_sp = value_sp->GetSyntheticValue();
5920061da546Spatrick if (synthetic_value_sp)
5921061da546Spatrick local_values.Append(synthetic_value_sp);
5922061da546Spatrick else
5923061da546Spatrick local_values.Append(value_sp);
5924061da546Spatrick }
5925061da546Spatrick }
5926061da546Spatrick // Update the values
5927061da546Spatrick SetValues(local_values);
5928061da546Spatrick }
5929061da546Spatrick }
5930061da546Spatrick } else {
5931061da546Spatrick m_frame_block = nullptr;
5932061da546Spatrick // Update the values with an empty list if there is no frame
5933061da546Spatrick SetValues(local_values);
5934061da546Spatrick }
5935061da546Spatrick
5936061da546Spatrick return ValueObjectListDelegate::WindowDelegateDraw(window, force);
5937061da546Spatrick }
5938061da546Spatrick
5939061da546Spatrick protected:
5940061da546Spatrick Debugger &m_debugger;
5941*f6aab3d8Srobert Block *m_frame_block = nullptr;
5942061da546Spatrick };
5943061da546Spatrick
5944061da546Spatrick class RegistersWindowDelegate : public ValueObjectListDelegate {
5945061da546Spatrick public:
RegistersWindowDelegate(Debugger & debugger)5946061da546Spatrick RegistersWindowDelegate(Debugger &debugger)
5947061da546Spatrick : ValueObjectListDelegate(), m_debugger(debugger) {}
5948061da546Spatrick
5949061da546Spatrick ~RegistersWindowDelegate() override = default;
5950061da546Spatrick
WindowDelegateGetHelpText()5951061da546Spatrick const char *WindowDelegateGetHelpText() override {
5952061da546Spatrick return "Register window keyboard shortcuts:";
5953061da546Spatrick }
5954061da546Spatrick
WindowDelegateDraw(Window & window,bool force)5955061da546Spatrick bool WindowDelegateDraw(Window &window, bool force) override {
5956061da546Spatrick ExecutionContext exe_ctx(
5957061da546Spatrick m_debugger.GetCommandInterpreter().GetExecutionContext());
5958061da546Spatrick StackFrame *frame = exe_ctx.GetFramePtr();
5959061da546Spatrick
5960061da546Spatrick ValueObjectList value_list;
5961061da546Spatrick if (frame) {
5962061da546Spatrick if (frame->GetStackID() != m_stack_id) {
5963061da546Spatrick m_stack_id = frame->GetStackID();
5964061da546Spatrick RegisterContextSP reg_ctx(frame->GetRegisterContext());
5965061da546Spatrick if (reg_ctx) {
5966061da546Spatrick const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
5967061da546Spatrick for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) {
5968061da546Spatrick value_list.Append(
5969061da546Spatrick ValueObjectRegisterSet::Create(frame, reg_ctx, set_idx));
5970061da546Spatrick }
5971061da546Spatrick }
5972061da546Spatrick SetValues(value_list);
5973061da546Spatrick }
5974061da546Spatrick } else {
5975061da546Spatrick Process *process = exe_ctx.GetProcessPtr();
5976061da546Spatrick if (process && process->IsAlive())
5977061da546Spatrick return true; // Don't do any updating if we are running
5978061da546Spatrick else {
5979061da546Spatrick // Update the values with an empty list if there is no process or the
5980061da546Spatrick // process isn't alive anymore
5981061da546Spatrick SetValues(value_list);
5982061da546Spatrick }
5983061da546Spatrick }
5984061da546Spatrick return ValueObjectListDelegate::WindowDelegateDraw(window, force);
5985061da546Spatrick }
5986061da546Spatrick
5987061da546Spatrick protected:
5988061da546Spatrick Debugger &m_debugger;
5989061da546Spatrick StackID m_stack_id;
5990061da546Spatrick };
5991061da546Spatrick
CursesKeyToCString(int ch)5992061da546Spatrick static const char *CursesKeyToCString(int ch) {
5993061da546Spatrick static char g_desc[32];
5994061da546Spatrick if (ch >= KEY_F0 && ch < KEY_F0 + 64) {
5995061da546Spatrick snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0);
5996061da546Spatrick return g_desc;
5997061da546Spatrick }
5998061da546Spatrick switch (ch) {
5999061da546Spatrick case KEY_DOWN:
6000061da546Spatrick return "down";
6001061da546Spatrick case KEY_UP:
6002061da546Spatrick return "up";
6003061da546Spatrick case KEY_LEFT:
6004061da546Spatrick return "left";
6005061da546Spatrick case KEY_RIGHT:
6006061da546Spatrick return "right";
6007061da546Spatrick case KEY_HOME:
6008061da546Spatrick return "home";
6009061da546Spatrick case KEY_BACKSPACE:
6010061da546Spatrick return "backspace";
6011061da546Spatrick case KEY_DL:
6012061da546Spatrick return "delete-line";
6013061da546Spatrick case KEY_IL:
6014061da546Spatrick return "insert-line";
6015061da546Spatrick case KEY_DC:
6016061da546Spatrick return "delete-char";
6017061da546Spatrick case KEY_IC:
6018061da546Spatrick return "insert-char";
6019061da546Spatrick case KEY_CLEAR:
6020061da546Spatrick return "clear";
6021061da546Spatrick case KEY_EOS:
6022061da546Spatrick return "clear-to-eos";
6023061da546Spatrick case KEY_EOL:
6024061da546Spatrick return "clear-to-eol";
6025061da546Spatrick case KEY_SF:
6026061da546Spatrick return "scroll-forward";
6027061da546Spatrick case KEY_SR:
6028061da546Spatrick return "scroll-backward";
6029061da546Spatrick case KEY_NPAGE:
6030061da546Spatrick return "page-down";
6031061da546Spatrick case KEY_PPAGE:
6032061da546Spatrick return "page-up";
6033061da546Spatrick case KEY_STAB:
6034061da546Spatrick return "set-tab";
6035061da546Spatrick case KEY_CTAB:
6036061da546Spatrick return "clear-tab";
6037061da546Spatrick case KEY_CATAB:
6038061da546Spatrick return "clear-all-tabs";
6039061da546Spatrick case KEY_ENTER:
6040061da546Spatrick return "enter";
6041061da546Spatrick case KEY_PRINT:
6042061da546Spatrick return "print";
6043061da546Spatrick case KEY_LL:
6044061da546Spatrick return "lower-left key";
6045061da546Spatrick case KEY_A1:
6046061da546Spatrick return "upper left of keypad";
6047061da546Spatrick case KEY_A3:
6048061da546Spatrick return "upper right of keypad";
6049061da546Spatrick case KEY_B2:
6050061da546Spatrick return "center of keypad";
6051061da546Spatrick case KEY_C1:
6052061da546Spatrick return "lower left of keypad";
6053061da546Spatrick case KEY_C3:
6054061da546Spatrick return "lower right of keypad";
6055061da546Spatrick case KEY_BTAB:
6056061da546Spatrick return "back-tab key";
6057061da546Spatrick case KEY_BEG:
6058061da546Spatrick return "begin key";
6059061da546Spatrick case KEY_CANCEL:
6060061da546Spatrick return "cancel key";
6061061da546Spatrick case KEY_CLOSE:
6062061da546Spatrick return "close key";
6063061da546Spatrick case KEY_COMMAND:
6064061da546Spatrick return "command key";
6065061da546Spatrick case KEY_COPY:
6066061da546Spatrick return "copy key";
6067061da546Spatrick case KEY_CREATE:
6068061da546Spatrick return "create key";
6069061da546Spatrick case KEY_END:
6070061da546Spatrick return "end key";
6071061da546Spatrick case KEY_EXIT:
6072061da546Spatrick return "exit key";
6073061da546Spatrick case KEY_FIND:
6074061da546Spatrick return "find key";
6075061da546Spatrick case KEY_HELP:
6076061da546Spatrick return "help key";
6077061da546Spatrick case KEY_MARK:
6078061da546Spatrick return "mark key";
6079061da546Spatrick case KEY_MESSAGE:
6080061da546Spatrick return "message key";
6081061da546Spatrick case KEY_MOVE:
6082061da546Spatrick return "move key";
6083061da546Spatrick case KEY_NEXT:
6084061da546Spatrick return "next key";
6085061da546Spatrick case KEY_OPEN:
6086061da546Spatrick return "open key";
6087061da546Spatrick case KEY_OPTIONS:
6088061da546Spatrick return "options key";
6089061da546Spatrick case KEY_PREVIOUS:
6090061da546Spatrick return "previous key";
6091061da546Spatrick case KEY_REDO:
6092061da546Spatrick return "redo key";
6093061da546Spatrick case KEY_REFERENCE:
6094061da546Spatrick return "reference key";
6095061da546Spatrick case KEY_REFRESH:
6096061da546Spatrick return "refresh key";
6097061da546Spatrick case KEY_REPLACE:
6098061da546Spatrick return "replace key";
6099061da546Spatrick case KEY_RESTART:
6100061da546Spatrick return "restart key";
6101061da546Spatrick case KEY_RESUME:
6102061da546Spatrick return "resume key";
6103061da546Spatrick case KEY_SAVE:
6104061da546Spatrick return "save key";
6105061da546Spatrick case KEY_SBEG:
6106061da546Spatrick return "shifted begin key";
6107061da546Spatrick case KEY_SCANCEL:
6108061da546Spatrick return "shifted cancel key";
6109061da546Spatrick case KEY_SCOMMAND:
6110061da546Spatrick return "shifted command key";
6111061da546Spatrick case KEY_SCOPY:
6112061da546Spatrick return "shifted copy key";
6113061da546Spatrick case KEY_SCREATE:
6114061da546Spatrick return "shifted create key";
6115061da546Spatrick case KEY_SDC:
6116061da546Spatrick return "shifted delete-character key";
6117061da546Spatrick case KEY_SDL:
6118061da546Spatrick return "shifted delete-line key";
6119061da546Spatrick case KEY_SELECT:
6120061da546Spatrick return "select key";
6121061da546Spatrick case KEY_SEND:
6122061da546Spatrick return "shifted end key";
6123061da546Spatrick case KEY_SEOL:
6124061da546Spatrick return "shifted clear-to-end-of-line key";
6125061da546Spatrick case KEY_SEXIT:
6126061da546Spatrick return "shifted exit key";
6127061da546Spatrick case KEY_SFIND:
6128061da546Spatrick return "shifted find key";
6129061da546Spatrick case KEY_SHELP:
6130061da546Spatrick return "shifted help key";
6131061da546Spatrick case KEY_SHOME:
6132061da546Spatrick return "shifted home key";
6133061da546Spatrick case KEY_SIC:
6134061da546Spatrick return "shifted insert-character key";
6135061da546Spatrick case KEY_SLEFT:
6136061da546Spatrick return "shifted left-arrow key";
6137061da546Spatrick case KEY_SMESSAGE:
6138061da546Spatrick return "shifted message key";
6139061da546Spatrick case KEY_SMOVE:
6140061da546Spatrick return "shifted move key";
6141061da546Spatrick case KEY_SNEXT:
6142061da546Spatrick return "shifted next key";
6143061da546Spatrick case KEY_SOPTIONS:
6144061da546Spatrick return "shifted options key";
6145061da546Spatrick case KEY_SPREVIOUS:
6146061da546Spatrick return "shifted previous key";
6147061da546Spatrick case KEY_SPRINT:
6148061da546Spatrick return "shifted print key";
6149061da546Spatrick case KEY_SREDO:
6150061da546Spatrick return "shifted redo key";
6151061da546Spatrick case KEY_SREPLACE:
6152061da546Spatrick return "shifted replace key";
6153061da546Spatrick case KEY_SRIGHT:
6154061da546Spatrick return "shifted right-arrow key";
6155061da546Spatrick case KEY_SRSUME:
6156061da546Spatrick return "shifted resume key";
6157061da546Spatrick case KEY_SSAVE:
6158061da546Spatrick return "shifted save key";
6159061da546Spatrick case KEY_SSUSPEND:
6160061da546Spatrick return "shifted suspend key";
6161061da546Spatrick case KEY_SUNDO:
6162061da546Spatrick return "shifted undo key";
6163061da546Spatrick case KEY_SUSPEND:
6164061da546Spatrick return "suspend key";
6165061da546Spatrick case KEY_UNDO:
6166061da546Spatrick return "undo key";
6167061da546Spatrick case KEY_MOUSE:
6168061da546Spatrick return "Mouse event has occurred";
6169061da546Spatrick case KEY_RESIZE:
6170061da546Spatrick return "Terminal resize event";
6171061da546Spatrick #ifdef KEY_EVENT
6172061da546Spatrick case KEY_EVENT:
6173061da546Spatrick return "We were interrupted by an event";
6174061da546Spatrick #endif
6175061da546Spatrick case KEY_RETURN:
6176061da546Spatrick return "return";
6177061da546Spatrick case ' ':
6178061da546Spatrick return "space";
6179061da546Spatrick case '\t':
6180061da546Spatrick return "tab";
6181061da546Spatrick case KEY_ESCAPE:
6182061da546Spatrick return "escape";
6183061da546Spatrick default:
6184dda28197Spatrick if (llvm::isPrint(ch))
6185061da546Spatrick snprintf(g_desc, sizeof(g_desc), "%c", ch);
6186061da546Spatrick else
6187061da546Spatrick snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch);
6188061da546Spatrick return g_desc;
6189061da546Spatrick }
6190061da546Spatrick return nullptr;
6191061da546Spatrick }
6192061da546Spatrick
HelpDialogDelegate(const char * text,KeyHelp * key_help_array)6193061da546Spatrick HelpDialogDelegate::HelpDialogDelegate(const char *text,
6194061da546Spatrick KeyHelp *key_help_array)
6195*f6aab3d8Srobert : m_text() {
6196061da546Spatrick if (text && text[0]) {
6197061da546Spatrick m_text.SplitIntoLines(text);
6198061da546Spatrick m_text.AppendString("");
6199061da546Spatrick }
6200061da546Spatrick if (key_help_array) {
6201061da546Spatrick for (KeyHelp *key = key_help_array; key->ch; ++key) {
6202061da546Spatrick StreamString key_description;
6203061da546Spatrick key_description.Printf("%10s - %s", CursesKeyToCString(key->ch),
6204061da546Spatrick key->description);
6205061da546Spatrick m_text.AppendString(key_description.GetString());
6206061da546Spatrick }
6207061da546Spatrick }
6208061da546Spatrick }
6209061da546Spatrick
6210061da546Spatrick HelpDialogDelegate::~HelpDialogDelegate() = default;
6211061da546Spatrick
WindowDelegateDraw(Window & window,bool force)6212061da546Spatrick bool HelpDialogDelegate::WindowDelegateDraw(Window &window, bool force) {
6213061da546Spatrick window.Erase();
6214061da546Spatrick const int window_height = window.GetHeight();
6215061da546Spatrick int x = 2;
6216061da546Spatrick int y = 1;
6217061da546Spatrick const int min_y = y;
6218061da546Spatrick const int max_y = window_height - 1 - y;
6219061da546Spatrick const size_t num_visible_lines = max_y - min_y + 1;
6220061da546Spatrick const size_t num_lines = m_text.GetSize();
6221061da546Spatrick const char *bottom_message;
6222061da546Spatrick if (num_lines <= num_visible_lines)
6223061da546Spatrick bottom_message = "Press any key to exit";
6224061da546Spatrick else
6225061da546Spatrick bottom_message = "Use arrows to scroll, any other key to exit";
6226061da546Spatrick window.DrawTitleBox(window.GetName(), bottom_message);
6227061da546Spatrick while (y <= max_y) {
6228061da546Spatrick window.MoveCursor(x, y);
6229061da546Spatrick window.PutCStringTruncated(
6230be691f3bSpatrick 1, m_text.GetStringAtIndex(m_first_visible_line + y - min_y));
6231061da546Spatrick ++y;
6232061da546Spatrick }
6233061da546Spatrick return true;
6234061da546Spatrick }
6235061da546Spatrick
WindowDelegateHandleChar(Window & window,int key)6236061da546Spatrick HandleCharResult HelpDialogDelegate::WindowDelegateHandleChar(Window &window,
6237061da546Spatrick int key) {
6238061da546Spatrick bool done = false;
6239061da546Spatrick const size_t num_lines = m_text.GetSize();
6240061da546Spatrick const size_t num_visible_lines = window.GetHeight() - 2;
6241061da546Spatrick
6242061da546Spatrick if (num_lines <= num_visible_lines) {
6243061da546Spatrick done = true;
6244061da546Spatrick // If we have all lines visible and don't need scrolling, then any key
6245061da546Spatrick // press will cause us to exit
6246061da546Spatrick } else {
6247061da546Spatrick switch (key) {
6248061da546Spatrick case KEY_UP:
6249061da546Spatrick if (m_first_visible_line > 0)
6250061da546Spatrick --m_first_visible_line;
6251061da546Spatrick break;
6252061da546Spatrick
6253061da546Spatrick case KEY_DOWN:
6254061da546Spatrick if (m_first_visible_line + num_visible_lines < num_lines)
6255061da546Spatrick ++m_first_visible_line;
6256061da546Spatrick break;
6257061da546Spatrick
6258061da546Spatrick case KEY_PPAGE:
6259061da546Spatrick case ',':
6260061da546Spatrick if (m_first_visible_line > 0) {
6261061da546Spatrick if (static_cast<size_t>(m_first_visible_line) >= num_visible_lines)
6262061da546Spatrick m_first_visible_line -= num_visible_lines;
6263061da546Spatrick else
6264061da546Spatrick m_first_visible_line = 0;
6265061da546Spatrick }
6266061da546Spatrick break;
6267061da546Spatrick
6268061da546Spatrick case KEY_NPAGE:
6269061da546Spatrick case '.':
6270061da546Spatrick if (m_first_visible_line + num_visible_lines < num_lines) {
6271061da546Spatrick m_first_visible_line += num_visible_lines;
6272061da546Spatrick if (static_cast<size_t>(m_first_visible_line) > num_lines)
6273061da546Spatrick m_first_visible_line = num_lines - num_visible_lines;
6274061da546Spatrick }
6275061da546Spatrick break;
6276061da546Spatrick
6277061da546Spatrick default:
6278061da546Spatrick done = true;
6279061da546Spatrick break;
6280061da546Spatrick }
6281061da546Spatrick }
6282061da546Spatrick if (done)
6283061da546Spatrick window.GetParent()->RemoveSubWindow(&window);
6284061da546Spatrick return eKeyHandled;
6285061da546Spatrick }
6286061da546Spatrick
6287061da546Spatrick class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
6288061da546Spatrick public:
6289061da546Spatrick enum {
6290061da546Spatrick eMenuID_LLDB = 1,
6291061da546Spatrick eMenuID_LLDBAbout,
6292061da546Spatrick eMenuID_LLDBExit,
6293061da546Spatrick
6294061da546Spatrick eMenuID_Target,
6295061da546Spatrick eMenuID_TargetCreate,
6296061da546Spatrick eMenuID_TargetDelete,
6297061da546Spatrick
6298061da546Spatrick eMenuID_Process,
6299061da546Spatrick eMenuID_ProcessAttach,
6300be691f3bSpatrick eMenuID_ProcessDetachResume,
6301be691f3bSpatrick eMenuID_ProcessDetachSuspended,
6302061da546Spatrick eMenuID_ProcessLaunch,
6303061da546Spatrick eMenuID_ProcessContinue,
6304061da546Spatrick eMenuID_ProcessHalt,
6305061da546Spatrick eMenuID_ProcessKill,
6306061da546Spatrick
6307061da546Spatrick eMenuID_Thread,
6308061da546Spatrick eMenuID_ThreadStepIn,
6309061da546Spatrick eMenuID_ThreadStepOver,
6310061da546Spatrick eMenuID_ThreadStepOut,
6311061da546Spatrick
6312061da546Spatrick eMenuID_View,
6313061da546Spatrick eMenuID_ViewBacktrace,
6314061da546Spatrick eMenuID_ViewRegisters,
6315061da546Spatrick eMenuID_ViewSource,
6316061da546Spatrick eMenuID_ViewVariables,
6317*f6aab3d8Srobert eMenuID_ViewBreakpoints,
6318061da546Spatrick
6319061da546Spatrick eMenuID_Help,
6320061da546Spatrick eMenuID_HelpGUIHelp
6321061da546Spatrick };
6322061da546Spatrick
ApplicationDelegate(Application & app,Debugger & debugger)6323061da546Spatrick ApplicationDelegate(Application &app, Debugger &debugger)
6324061da546Spatrick : WindowDelegate(), MenuDelegate(), m_app(app), m_debugger(debugger) {}
6325061da546Spatrick
6326061da546Spatrick ~ApplicationDelegate() override = default;
6327061da546Spatrick
WindowDelegateDraw(Window & window,bool force)6328061da546Spatrick bool WindowDelegateDraw(Window &window, bool force) override {
6329061da546Spatrick return false; // Drawing not handled, let standard window drawing happen
6330061da546Spatrick }
6331061da546Spatrick
WindowDelegateHandleChar(Window & window,int key)6332061da546Spatrick HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
6333061da546Spatrick switch (key) {
6334061da546Spatrick case '\t':
6335061da546Spatrick window.SelectNextWindowAsActive();
6336061da546Spatrick return eKeyHandled;
6337061da546Spatrick
6338be691f3bSpatrick case KEY_SHIFT_TAB:
6339be691f3bSpatrick window.SelectPreviousWindowAsActive();
6340be691f3bSpatrick return eKeyHandled;
6341be691f3bSpatrick
6342061da546Spatrick case 'h':
6343061da546Spatrick window.CreateHelpSubwindow();
6344061da546Spatrick return eKeyHandled;
6345061da546Spatrick
6346061da546Spatrick case KEY_ESCAPE:
6347061da546Spatrick return eQuitApplication;
6348061da546Spatrick
6349061da546Spatrick default:
6350061da546Spatrick break;
6351061da546Spatrick }
6352061da546Spatrick return eKeyNotHandled;
6353061da546Spatrick }
6354061da546Spatrick
WindowDelegateGetHelpText()6355061da546Spatrick const char *WindowDelegateGetHelpText() override {
6356061da546Spatrick return "Welcome to the LLDB curses GUI.\n\n"
6357061da546Spatrick "Press the TAB key to change the selected view.\n"
6358061da546Spatrick "Each view has its own keyboard shortcuts, press 'h' to open a "
6359061da546Spatrick "dialog to display them.\n\n"
6360061da546Spatrick "Common key bindings for all views:";
6361061da546Spatrick }
6362061da546Spatrick
WindowDelegateGetKeyHelp()6363061da546Spatrick KeyHelp *WindowDelegateGetKeyHelp() override {
6364061da546Spatrick static curses::KeyHelp g_source_view_key_help[] = {
6365061da546Spatrick {'\t', "Select next view"},
6366be691f3bSpatrick {KEY_BTAB, "Select previous view"},
6367061da546Spatrick {'h', "Show help dialog with view specific key bindings"},
6368061da546Spatrick {',', "Page up"},
6369061da546Spatrick {'.', "Page down"},
6370061da546Spatrick {KEY_UP, "Select previous"},
6371061da546Spatrick {KEY_DOWN, "Select next"},
6372061da546Spatrick {KEY_LEFT, "Unexpand or select parent"},
6373061da546Spatrick {KEY_RIGHT, "Expand"},
6374061da546Spatrick {KEY_PPAGE, "Page up"},
6375061da546Spatrick {KEY_NPAGE, "Page down"},
6376061da546Spatrick {'\0', nullptr}};
6377061da546Spatrick return g_source_view_key_help;
6378061da546Spatrick }
6379061da546Spatrick
MenuDelegateAction(Menu & menu)6380061da546Spatrick MenuActionResult MenuDelegateAction(Menu &menu) override {
6381061da546Spatrick switch (menu.GetIdentifier()) {
6382*f6aab3d8Srobert case eMenuID_TargetCreate: {
6383*f6aab3d8Srobert WindowSP main_window_sp = m_app.GetMainWindow();
6384*f6aab3d8Srobert FormDelegateSP form_delegate_sp =
6385*f6aab3d8Srobert FormDelegateSP(new TargetCreateFormDelegate(m_debugger));
6386*f6aab3d8Srobert Rect bounds = main_window_sp->GetCenteredRect(80, 19);
6387*f6aab3d8Srobert WindowSP form_window_sp = main_window_sp->CreateSubWindow(
6388*f6aab3d8Srobert form_delegate_sp->GetName().c_str(), bounds, true);
6389*f6aab3d8Srobert WindowDelegateSP window_delegate_sp =
6390*f6aab3d8Srobert WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
6391*f6aab3d8Srobert form_window_sp->SetDelegate(window_delegate_sp);
6392*f6aab3d8Srobert return MenuActionResult::Handled;
6393*f6aab3d8Srobert }
6394061da546Spatrick case eMenuID_ThreadStepIn: {
6395061da546Spatrick ExecutionContext exe_ctx =
6396061da546Spatrick m_debugger.GetCommandInterpreter().GetExecutionContext();
6397061da546Spatrick if (exe_ctx.HasThreadScope()) {
6398061da546Spatrick Process *process = exe_ctx.GetProcessPtr();
6399061da546Spatrick if (process && process->IsAlive() &&
6400061da546Spatrick StateIsStoppedState(process->GetState(), true))
6401061da546Spatrick exe_ctx.GetThreadRef().StepIn(true);
6402061da546Spatrick }
6403061da546Spatrick }
6404061da546Spatrick return MenuActionResult::Handled;
6405061da546Spatrick
6406061da546Spatrick case eMenuID_ThreadStepOut: {
6407061da546Spatrick ExecutionContext exe_ctx =
6408061da546Spatrick m_debugger.GetCommandInterpreter().GetExecutionContext();
6409061da546Spatrick if (exe_ctx.HasThreadScope()) {
6410061da546Spatrick Process *process = exe_ctx.GetProcessPtr();
6411061da546Spatrick if (process && process->IsAlive() &&
6412*f6aab3d8Srobert StateIsStoppedState(process->GetState(), true)) {
6413*f6aab3d8Srobert Thread *thread = exe_ctx.GetThreadPtr();
6414*f6aab3d8Srobert uint32_t frame_idx = thread->GetSelectedFrameIndex();
6415*f6aab3d8Srobert exe_ctx.GetThreadRef().StepOut(frame_idx);
6416*f6aab3d8Srobert }
6417061da546Spatrick }
6418061da546Spatrick }
6419061da546Spatrick return MenuActionResult::Handled;
6420061da546Spatrick
6421061da546Spatrick case eMenuID_ThreadStepOver: {
6422061da546Spatrick ExecutionContext exe_ctx =
6423061da546Spatrick m_debugger.GetCommandInterpreter().GetExecutionContext();
6424061da546Spatrick if (exe_ctx.HasThreadScope()) {
6425061da546Spatrick Process *process = exe_ctx.GetProcessPtr();
6426061da546Spatrick if (process && process->IsAlive() &&
6427061da546Spatrick StateIsStoppedState(process->GetState(), true))
6428061da546Spatrick exe_ctx.GetThreadRef().StepOver(true);
6429061da546Spatrick }
6430061da546Spatrick }
6431061da546Spatrick return MenuActionResult::Handled;
6432061da546Spatrick
6433be691f3bSpatrick case eMenuID_ProcessAttach: {
6434be691f3bSpatrick WindowSP main_window_sp = m_app.GetMainWindow();
6435be691f3bSpatrick FormDelegateSP form_delegate_sp = FormDelegateSP(
6436be691f3bSpatrick new ProcessAttachFormDelegate(m_debugger, main_window_sp));
6437be691f3bSpatrick Rect bounds = main_window_sp->GetCenteredRect(80, 22);
6438be691f3bSpatrick WindowSP form_window_sp = main_window_sp->CreateSubWindow(
6439be691f3bSpatrick form_delegate_sp->GetName().c_str(), bounds, true);
6440be691f3bSpatrick WindowDelegateSP window_delegate_sp =
6441be691f3bSpatrick WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
6442be691f3bSpatrick form_window_sp->SetDelegate(window_delegate_sp);
6443be691f3bSpatrick return MenuActionResult::Handled;
6444be691f3bSpatrick }
6445*f6aab3d8Srobert case eMenuID_ProcessLaunch: {
6446*f6aab3d8Srobert WindowSP main_window_sp = m_app.GetMainWindow();
6447*f6aab3d8Srobert FormDelegateSP form_delegate_sp = FormDelegateSP(
6448*f6aab3d8Srobert new ProcessLaunchFormDelegate(m_debugger, main_window_sp));
6449*f6aab3d8Srobert Rect bounds = main_window_sp->GetCenteredRect(80, 22);
6450*f6aab3d8Srobert WindowSP form_window_sp = main_window_sp->CreateSubWindow(
6451*f6aab3d8Srobert form_delegate_sp->GetName().c_str(), bounds, true);
6452*f6aab3d8Srobert WindowDelegateSP window_delegate_sp =
6453*f6aab3d8Srobert WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
6454*f6aab3d8Srobert form_window_sp->SetDelegate(window_delegate_sp);
6455*f6aab3d8Srobert return MenuActionResult::Handled;
6456*f6aab3d8Srobert }
6457be691f3bSpatrick
6458061da546Spatrick case eMenuID_ProcessContinue: {
6459061da546Spatrick ExecutionContext exe_ctx =
6460061da546Spatrick m_debugger.GetCommandInterpreter().GetExecutionContext();
6461061da546Spatrick if (exe_ctx.HasProcessScope()) {
6462061da546Spatrick Process *process = exe_ctx.GetProcessPtr();
6463061da546Spatrick if (process && process->IsAlive() &&
6464061da546Spatrick StateIsStoppedState(process->GetState(), true))
6465061da546Spatrick process->Resume();
6466061da546Spatrick }
6467061da546Spatrick }
6468061da546Spatrick return MenuActionResult::Handled;
6469061da546Spatrick
6470061da546Spatrick case eMenuID_ProcessKill: {
6471061da546Spatrick ExecutionContext exe_ctx =
6472061da546Spatrick m_debugger.GetCommandInterpreter().GetExecutionContext();
6473061da546Spatrick if (exe_ctx.HasProcessScope()) {
6474061da546Spatrick Process *process = exe_ctx.GetProcessPtr();
6475061da546Spatrick if (process && process->IsAlive())
6476061da546Spatrick process->Destroy(false);
6477061da546Spatrick }
6478061da546Spatrick }
6479061da546Spatrick return MenuActionResult::Handled;
6480061da546Spatrick
6481061da546Spatrick case eMenuID_ProcessHalt: {
6482061da546Spatrick ExecutionContext exe_ctx =
6483061da546Spatrick m_debugger.GetCommandInterpreter().GetExecutionContext();
6484061da546Spatrick if (exe_ctx.HasProcessScope()) {
6485061da546Spatrick Process *process = exe_ctx.GetProcessPtr();
6486061da546Spatrick if (process && process->IsAlive())
6487061da546Spatrick process->Halt();
6488061da546Spatrick }
6489061da546Spatrick }
6490061da546Spatrick return MenuActionResult::Handled;
6491061da546Spatrick
6492be691f3bSpatrick case eMenuID_ProcessDetachResume:
6493be691f3bSpatrick case eMenuID_ProcessDetachSuspended: {
6494061da546Spatrick ExecutionContext exe_ctx =
6495061da546Spatrick m_debugger.GetCommandInterpreter().GetExecutionContext();
6496061da546Spatrick if (exe_ctx.HasProcessScope()) {
6497061da546Spatrick Process *process = exe_ctx.GetProcessPtr();
6498061da546Spatrick if (process && process->IsAlive())
6499be691f3bSpatrick process->Detach(menu.GetIdentifier() ==
6500be691f3bSpatrick eMenuID_ProcessDetachSuspended);
6501061da546Spatrick }
6502061da546Spatrick }
6503061da546Spatrick return MenuActionResult::Handled;
6504061da546Spatrick
6505061da546Spatrick case eMenuID_Process: {
6506061da546Spatrick // Populate the menu with all of the threads if the process is stopped
6507061da546Spatrick // when the Process menu gets selected and is about to display its
6508061da546Spatrick // submenu.
6509061da546Spatrick Menus &submenus = menu.GetSubmenus();
6510061da546Spatrick ExecutionContext exe_ctx =
6511061da546Spatrick m_debugger.GetCommandInterpreter().GetExecutionContext();
6512061da546Spatrick Process *process = exe_ctx.GetProcessPtr();
6513061da546Spatrick if (process && process->IsAlive() &&
6514061da546Spatrick StateIsStoppedState(process->GetState(), true)) {
6515061da546Spatrick if (submenus.size() == 7)
6516061da546Spatrick menu.AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
6517061da546Spatrick else if (submenus.size() > 8)
6518061da546Spatrick submenus.erase(submenus.begin() + 8, submenus.end());
6519061da546Spatrick
6520061da546Spatrick ThreadList &threads = process->GetThreadList();
6521061da546Spatrick std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
6522061da546Spatrick size_t num_threads = threads.GetSize();
6523061da546Spatrick for (size_t i = 0; i < num_threads; ++i) {
6524061da546Spatrick ThreadSP thread_sp = threads.GetThreadAtIndex(i);
6525061da546Spatrick char menu_char = '\0';
6526061da546Spatrick if (i < 9)
6527061da546Spatrick menu_char = '1' + i;
6528061da546Spatrick StreamString thread_menu_title;
6529061da546Spatrick thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID());
6530061da546Spatrick const char *thread_name = thread_sp->GetName();
6531061da546Spatrick if (thread_name && thread_name[0])
6532061da546Spatrick thread_menu_title.Printf(" %s", thread_name);
6533061da546Spatrick else {
6534061da546Spatrick const char *queue_name = thread_sp->GetQueueName();
6535061da546Spatrick if (queue_name && queue_name[0])
6536061da546Spatrick thread_menu_title.Printf(" %s", queue_name);
6537061da546Spatrick }
6538061da546Spatrick menu.AddSubmenu(
6539061da546Spatrick MenuSP(new Menu(thread_menu_title.GetString().str().c_str(),
6540061da546Spatrick nullptr, menu_char, thread_sp->GetID())));
6541061da546Spatrick }
6542061da546Spatrick } else if (submenus.size() > 7) {
6543061da546Spatrick // Remove the separator and any other thread submenu items that were
6544061da546Spatrick // previously added
6545061da546Spatrick submenus.erase(submenus.begin() + 7, submenus.end());
6546061da546Spatrick }
6547*f6aab3d8Srobert // Since we are adding and removing items we need to recalculate the
6548*f6aab3d8Srobert // name lengths
6549061da546Spatrick menu.RecalculateNameLengths();
6550061da546Spatrick }
6551061da546Spatrick return MenuActionResult::Handled;
6552061da546Spatrick
6553061da546Spatrick case eMenuID_ViewVariables: {
6554061da546Spatrick WindowSP main_window_sp = m_app.GetMainWindow();
6555061da546Spatrick WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
6556061da546Spatrick WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
6557061da546Spatrick WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
6558061da546Spatrick const Rect source_bounds = source_window_sp->GetBounds();
6559061da546Spatrick
6560061da546Spatrick if (variables_window_sp) {
6561061da546Spatrick const Rect variables_bounds = variables_window_sp->GetBounds();
6562061da546Spatrick
6563061da546Spatrick main_window_sp->RemoveSubWindow(variables_window_sp.get());
6564061da546Spatrick
6565061da546Spatrick if (registers_window_sp) {
6566061da546Spatrick // We have a registers window, so give all the area back to the
6567061da546Spatrick // registers window
6568061da546Spatrick Rect registers_bounds = variables_bounds;
6569061da546Spatrick registers_bounds.size.width = source_bounds.size.width;
6570061da546Spatrick registers_window_sp->SetBounds(registers_bounds);
6571061da546Spatrick } else {
6572061da546Spatrick // We have no registers window showing so give the bottom area back
6573061da546Spatrick // to the source view
6574061da546Spatrick source_window_sp->Resize(source_bounds.size.width,
6575061da546Spatrick source_bounds.size.height +
6576061da546Spatrick variables_bounds.size.height);
6577061da546Spatrick }
6578061da546Spatrick } else {
6579061da546Spatrick Rect new_variables_rect;
6580061da546Spatrick if (registers_window_sp) {
6581061da546Spatrick // We have a registers window so split the area of the registers
6582061da546Spatrick // window into two columns where the left hand side will be the
6583061da546Spatrick // variables and the right hand side will be the registers
6584061da546Spatrick const Rect variables_bounds = registers_window_sp->GetBounds();
6585061da546Spatrick Rect new_registers_rect;
6586061da546Spatrick variables_bounds.VerticalSplitPercentage(0.50, new_variables_rect,
6587061da546Spatrick new_registers_rect);
6588061da546Spatrick registers_window_sp->SetBounds(new_registers_rect);
6589061da546Spatrick } else {
6590be691f3bSpatrick // No registers window, grab the bottom part of the source window
6591061da546Spatrick Rect new_source_rect;
6592061da546Spatrick source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
6593061da546Spatrick new_variables_rect);
6594061da546Spatrick source_window_sp->SetBounds(new_source_rect);
6595061da546Spatrick }
6596061da546Spatrick WindowSP new_window_sp = main_window_sp->CreateSubWindow(
6597061da546Spatrick "Variables", new_variables_rect, false);
6598061da546Spatrick new_window_sp->SetDelegate(
6599061da546Spatrick WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
6600061da546Spatrick }
6601061da546Spatrick touchwin(stdscr);
6602061da546Spatrick }
6603061da546Spatrick return MenuActionResult::Handled;
6604061da546Spatrick
6605061da546Spatrick case eMenuID_ViewRegisters: {
6606061da546Spatrick WindowSP main_window_sp = m_app.GetMainWindow();
6607061da546Spatrick WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
6608061da546Spatrick WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
6609061da546Spatrick WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
6610061da546Spatrick const Rect source_bounds = source_window_sp->GetBounds();
6611061da546Spatrick
6612061da546Spatrick if (registers_window_sp) {
6613061da546Spatrick if (variables_window_sp) {
6614061da546Spatrick const Rect variables_bounds = variables_window_sp->GetBounds();
6615061da546Spatrick
6616061da546Spatrick // We have a variables window, so give all the area back to the
6617061da546Spatrick // variables window
6618061da546Spatrick variables_window_sp->Resize(variables_bounds.size.width +
6619061da546Spatrick registers_window_sp->GetWidth(),
6620061da546Spatrick variables_bounds.size.height);
6621061da546Spatrick } else {
6622061da546Spatrick // We have no variables window showing so give the bottom area back
6623061da546Spatrick // to the source view
6624061da546Spatrick source_window_sp->Resize(source_bounds.size.width,
6625061da546Spatrick source_bounds.size.height +
6626061da546Spatrick registers_window_sp->GetHeight());
6627061da546Spatrick }
6628061da546Spatrick main_window_sp->RemoveSubWindow(registers_window_sp.get());
6629061da546Spatrick } else {
6630061da546Spatrick Rect new_regs_rect;
6631061da546Spatrick if (variables_window_sp) {
6632061da546Spatrick // We have a variables window, split it into two columns where the
6633061da546Spatrick // left hand side will be the variables and the right hand side will
6634061da546Spatrick // be the registers
6635061da546Spatrick const Rect variables_bounds = variables_window_sp->GetBounds();
6636061da546Spatrick Rect new_vars_rect;
6637061da546Spatrick variables_bounds.VerticalSplitPercentage(0.50, new_vars_rect,
6638061da546Spatrick new_regs_rect);
6639061da546Spatrick variables_window_sp->SetBounds(new_vars_rect);
6640061da546Spatrick } else {
6641be691f3bSpatrick // No variables window, grab the bottom part of the source window
6642061da546Spatrick Rect new_source_rect;
6643061da546Spatrick source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
6644061da546Spatrick new_regs_rect);
6645061da546Spatrick source_window_sp->SetBounds(new_source_rect);
6646061da546Spatrick }
6647061da546Spatrick WindowSP new_window_sp =
6648061da546Spatrick main_window_sp->CreateSubWindow("Registers", new_regs_rect, false);
6649061da546Spatrick new_window_sp->SetDelegate(
6650061da546Spatrick WindowDelegateSP(new RegistersWindowDelegate(m_debugger)));
6651061da546Spatrick }
6652061da546Spatrick touchwin(stdscr);
6653061da546Spatrick }
6654061da546Spatrick return MenuActionResult::Handled;
6655061da546Spatrick
6656*f6aab3d8Srobert case eMenuID_ViewBreakpoints: {
6657*f6aab3d8Srobert WindowSP main_window_sp = m_app.GetMainWindow();
6658*f6aab3d8Srobert WindowSP threads_window_sp = main_window_sp->FindSubWindow("Threads");
6659*f6aab3d8Srobert WindowSP breakpoints_window_sp =
6660*f6aab3d8Srobert main_window_sp->FindSubWindow("Breakpoints");
6661*f6aab3d8Srobert const Rect threads_bounds = threads_window_sp->GetBounds();
6662*f6aab3d8Srobert
6663*f6aab3d8Srobert // If a breakpoints window already exists, remove it and give the area
6664*f6aab3d8Srobert // it used to occupy to the threads window. If it doesn't exist, split
6665*f6aab3d8Srobert // the threads window horizontally into two windows where the top window
6666*f6aab3d8Srobert // is the threads window and the bottom window is a newly added
6667*f6aab3d8Srobert // breakpoints window.
6668*f6aab3d8Srobert if (breakpoints_window_sp) {
6669*f6aab3d8Srobert threads_window_sp->Resize(threads_bounds.size.width,
6670*f6aab3d8Srobert threads_bounds.size.height +
6671*f6aab3d8Srobert breakpoints_window_sp->GetHeight());
6672*f6aab3d8Srobert main_window_sp->RemoveSubWindow(breakpoints_window_sp.get());
6673*f6aab3d8Srobert } else {
6674*f6aab3d8Srobert Rect new_threads_bounds, breakpoints_bounds;
6675*f6aab3d8Srobert threads_bounds.HorizontalSplitPercentage(0.70, new_threads_bounds,
6676*f6aab3d8Srobert breakpoints_bounds);
6677*f6aab3d8Srobert threads_window_sp->SetBounds(new_threads_bounds);
6678*f6aab3d8Srobert breakpoints_window_sp = main_window_sp->CreateSubWindow(
6679*f6aab3d8Srobert "Breakpoints", breakpoints_bounds, false);
6680*f6aab3d8Srobert TreeDelegateSP breakpoints_delegate_sp(
6681*f6aab3d8Srobert new BreakpointsTreeDelegate(m_debugger));
6682*f6aab3d8Srobert breakpoints_window_sp->SetDelegate(WindowDelegateSP(
6683*f6aab3d8Srobert new TreeWindowDelegate(m_debugger, breakpoints_delegate_sp)));
6684*f6aab3d8Srobert }
6685*f6aab3d8Srobert touchwin(stdscr);
6686*f6aab3d8Srobert return MenuActionResult::Handled;
6687*f6aab3d8Srobert }
6688*f6aab3d8Srobert
6689061da546Spatrick case eMenuID_HelpGUIHelp:
6690061da546Spatrick m_app.GetMainWindow()->CreateHelpSubwindow();
6691061da546Spatrick return MenuActionResult::Handled;
6692061da546Spatrick
6693061da546Spatrick default:
6694061da546Spatrick break;
6695061da546Spatrick }
6696061da546Spatrick
6697061da546Spatrick return MenuActionResult::NotHandled;
6698061da546Spatrick }
6699061da546Spatrick
6700061da546Spatrick protected:
6701061da546Spatrick Application &m_app;
6702061da546Spatrick Debugger &m_debugger;
6703061da546Spatrick };
6704061da546Spatrick
6705061da546Spatrick class StatusBarWindowDelegate : public WindowDelegate {
6706061da546Spatrick public:
StatusBarWindowDelegate(Debugger & debugger)6707061da546Spatrick StatusBarWindowDelegate(Debugger &debugger) : m_debugger(debugger) {
6708061da546Spatrick FormatEntity::Parse("Thread: ${thread.id%tid}", m_format);
6709061da546Spatrick }
6710061da546Spatrick
6711061da546Spatrick ~StatusBarWindowDelegate() override = default;
6712061da546Spatrick
WindowDelegateDraw(Window & window,bool force)6713061da546Spatrick bool WindowDelegateDraw(Window &window, bool force) override {
6714061da546Spatrick ExecutionContext exe_ctx =
6715061da546Spatrick m_debugger.GetCommandInterpreter().GetExecutionContext();
6716061da546Spatrick Process *process = exe_ctx.GetProcessPtr();
6717061da546Spatrick Thread *thread = exe_ctx.GetThreadPtr();
6718061da546Spatrick StackFrame *frame = exe_ctx.GetFramePtr();
6719061da546Spatrick window.Erase();
6720be691f3bSpatrick window.SetBackground(BlackOnWhite);
6721061da546Spatrick window.MoveCursor(0, 0);
6722061da546Spatrick if (process) {
6723061da546Spatrick const StateType state = process->GetState();
6724061da546Spatrick window.Printf("Process: %5" PRIu64 " %10s", process->GetID(),
6725061da546Spatrick StateAsCString(state));
6726061da546Spatrick
6727061da546Spatrick if (StateIsStoppedState(state, true)) {
6728061da546Spatrick StreamString strm;
6729061da546Spatrick if (thread && FormatEntity::Format(m_format, strm, nullptr, &exe_ctx,
6730061da546Spatrick nullptr, nullptr, false, false)) {
6731061da546Spatrick window.MoveCursor(40, 0);
6732be691f3bSpatrick window.PutCStringTruncated(1, strm.GetString().str().c_str());
6733061da546Spatrick }
6734061da546Spatrick
6735061da546Spatrick window.MoveCursor(60, 0);
6736061da546Spatrick if (frame)
6737061da546Spatrick window.Printf("Frame: %3u PC = 0x%16.16" PRIx64,
6738061da546Spatrick frame->GetFrameIndex(),
6739061da546Spatrick frame->GetFrameCodeAddress().GetOpcodeLoadAddress(
6740061da546Spatrick exe_ctx.GetTargetPtr()));
6741061da546Spatrick } else if (state == eStateExited) {
6742061da546Spatrick const char *exit_desc = process->GetExitDescription();
6743061da546Spatrick const int exit_status = process->GetExitStatus();
6744061da546Spatrick if (exit_desc && exit_desc[0])
6745061da546Spatrick window.Printf(" with status = %i (%s)", exit_status, exit_desc);
6746061da546Spatrick else
6747061da546Spatrick window.Printf(" with status = %i", exit_status);
6748061da546Spatrick }
6749061da546Spatrick }
6750061da546Spatrick return true;
6751061da546Spatrick }
6752061da546Spatrick
6753061da546Spatrick protected:
6754061da546Spatrick Debugger &m_debugger;
6755061da546Spatrick FormatEntity::Entry m_format;
6756061da546Spatrick };
6757061da546Spatrick
6758061da546Spatrick class SourceFileWindowDelegate : public WindowDelegate {
6759061da546Spatrick public:
SourceFileWindowDelegate(Debugger & debugger)6760061da546Spatrick SourceFileWindowDelegate(Debugger &debugger)
6761061da546Spatrick : WindowDelegate(), m_debugger(debugger), m_sc(), m_file_sp(),
6762*f6aab3d8Srobert m_disassembly_sp(), m_disassembly_range(), m_title() {}
6763061da546Spatrick
6764061da546Spatrick ~SourceFileWindowDelegate() override = default;
6765061da546Spatrick
Update(const SymbolContext & sc)6766061da546Spatrick void Update(const SymbolContext &sc) { m_sc = sc; }
6767061da546Spatrick
NumVisibleLines() const6768061da546Spatrick uint32_t NumVisibleLines() const { return m_max_y - m_min_y; }
6769061da546Spatrick
WindowDelegateGetHelpText()6770061da546Spatrick const char *WindowDelegateGetHelpText() override {
6771061da546Spatrick return "Source/Disassembly window keyboard shortcuts:";
6772061da546Spatrick }
6773061da546Spatrick
WindowDelegateGetKeyHelp()6774061da546Spatrick KeyHelp *WindowDelegateGetKeyHelp() override {
6775061da546Spatrick static curses::KeyHelp g_source_view_key_help[] = {
6776061da546Spatrick {KEY_RETURN, "Run to selected line with one shot breakpoint"},
6777061da546Spatrick {KEY_UP, "Select previous source line"},
6778061da546Spatrick {KEY_DOWN, "Select next source line"},
6779be691f3bSpatrick {KEY_LEFT, "Scroll to the left"},
6780be691f3bSpatrick {KEY_RIGHT, "Scroll to the right"},
6781061da546Spatrick {KEY_PPAGE, "Page up"},
6782061da546Spatrick {KEY_NPAGE, "Page down"},
6783061da546Spatrick {'b', "Set breakpoint on selected source/disassembly line"},
6784061da546Spatrick {'c', "Continue process"},
6785061da546Spatrick {'D', "Detach with process suspended"},
6786061da546Spatrick {'h', "Show help dialog"},
6787061da546Spatrick {'n', "Step over (source line)"},
6788061da546Spatrick {'N', "Step over (single instruction)"},
6789be691f3bSpatrick {'f', "Step out (finish)"},
6790061da546Spatrick {'s', "Step in (source line)"},
6791061da546Spatrick {'S', "Step in (single instruction)"},
6792be691f3bSpatrick {'u', "Frame up"},
6793be691f3bSpatrick {'d', "Frame down"},
6794061da546Spatrick {',', "Page up"},
6795061da546Spatrick {'.', "Page down"},
6796061da546Spatrick {'\0', nullptr}};
6797061da546Spatrick return g_source_view_key_help;
6798061da546Spatrick }
6799061da546Spatrick
WindowDelegateDraw(Window & window,bool force)6800061da546Spatrick bool WindowDelegateDraw(Window &window, bool force) override {
6801061da546Spatrick ExecutionContext exe_ctx =
6802061da546Spatrick m_debugger.GetCommandInterpreter().GetExecutionContext();
6803061da546Spatrick Process *process = exe_ctx.GetProcessPtr();
6804061da546Spatrick Thread *thread = nullptr;
6805061da546Spatrick
6806061da546Spatrick bool update_location = false;
6807061da546Spatrick if (process) {
6808061da546Spatrick StateType state = process->GetState();
6809061da546Spatrick if (StateIsStoppedState(state, true)) {
6810061da546Spatrick // We are stopped, so it is ok to
6811061da546Spatrick update_location = true;
6812061da546Spatrick }
6813061da546Spatrick }
6814061da546Spatrick
6815061da546Spatrick m_min_x = 1;
6816061da546Spatrick m_min_y = 2;
6817061da546Spatrick m_max_x = window.GetMaxX() - 1;
6818061da546Spatrick m_max_y = window.GetMaxY() - 1;
6819061da546Spatrick
6820061da546Spatrick const uint32_t num_visible_lines = NumVisibleLines();
6821061da546Spatrick StackFrameSP frame_sp;
6822061da546Spatrick bool set_selected_line_to_pc = false;
6823061da546Spatrick
6824061da546Spatrick if (update_location) {
6825*f6aab3d8Srobert const bool process_alive = process->IsAlive();
6826061da546Spatrick bool thread_changed = false;
6827061da546Spatrick if (process_alive) {
6828061da546Spatrick thread = exe_ctx.GetThreadPtr();
6829061da546Spatrick if (thread) {
6830061da546Spatrick frame_sp = thread->GetSelectedFrame();
6831061da546Spatrick auto tid = thread->GetID();
6832061da546Spatrick thread_changed = tid != m_tid;
6833061da546Spatrick m_tid = tid;
6834061da546Spatrick } else {
6835061da546Spatrick if (m_tid != LLDB_INVALID_THREAD_ID) {
6836061da546Spatrick thread_changed = true;
6837061da546Spatrick m_tid = LLDB_INVALID_THREAD_ID;
6838061da546Spatrick }
6839061da546Spatrick }
6840061da546Spatrick }
6841061da546Spatrick const uint32_t stop_id = process ? process->GetStopID() : 0;
6842061da546Spatrick const bool stop_id_changed = stop_id != m_stop_id;
6843061da546Spatrick bool frame_changed = false;
6844061da546Spatrick m_stop_id = stop_id;
6845061da546Spatrick m_title.Clear();
6846061da546Spatrick if (frame_sp) {
6847061da546Spatrick m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
6848061da546Spatrick if (m_sc.module_sp) {
6849061da546Spatrick m_title.Printf(
6850061da546Spatrick "%s", m_sc.module_sp->GetFileSpec().GetFilename().GetCString());
6851061da546Spatrick ConstString func_name = m_sc.GetFunctionName();
6852061da546Spatrick if (func_name)
6853061da546Spatrick m_title.Printf("`%s", func_name.GetCString());
6854061da546Spatrick }
6855061da546Spatrick const uint32_t frame_idx = frame_sp->GetFrameIndex();
6856061da546Spatrick frame_changed = frame_idx != m_frame_idx;
6857061da546Spatrick m_frame_idx = frame_idx;
6858061da546Spatrick } else {
6859061da546Spatrick m_sc.Clear(true);
6860061da546Spatrick frame_changed = m_frame_idx != UINT32_MAX;
6861061da546Spatrick m_frame_idx = UINT32_MAX;
6862061da546Spatrick }
6863061da546Spatrick
6864061da546Spatrick const bool context_changed =
6865061da546Spatrick thread_changed || frame_changed || stop_id_changed;
6866061da546Spatrick
6867061da546Spatrick if (process_alive) {
6868061da546Spatrick if (m_sc.line_entry.IsValid()) {
6869061da546Spatrick m_pc_line = m_sc.line_entry.line;
6870061da546Spatrick if (m_pc_line != UINT32_MAX)
6871061da546Spatrick --m_pc_line; // Convert to zero based line number...
6872061da546Spatrick // Update the selected line if the stop ID changed...
6873061da546Spatrick if (context_changed)
6874061da546Spatrick m_selected_line = m_pc_line;
6875061da546Spatrick
6876061da546Spatrick if (m_file_sp && m_file_sp->GetFileSpec() == m_sc.line_entry.file) {
6877*f6aab3d8Srobert // Same file, nothing to do, we should either have the lines or
6878*f6aab3d8Srobert // not (source file missing)
6879061da546Spatrick if (m_selected_line >= static_cast<size_t>(m_first_visible_line)) {
6880061da546Spatrick if (m_selected_line >= m_first_visible_line + num_visible_lines)
6881061da546Spatrick m_first_visible_line = m_selected_line - 10;
6882061da546Spatrick } else {
6883061da546Spatrick if (m_selected_line > 10)
6884061da546Spatrick m_first_visible_line = m_selected_line - 10;
6885061da546Spatrick else
6886061da546Spatrick m_first_visible_line = 0;
6887061da546Spatrick }
6888061da546Spatrick } else {
6889061da546Spatrick // File changed, set selected line to the line with the PC
6890061da546Spatrick m_selected_line = m_pc_line;
6891061da546Spatrick m_file_sp =
6892061da546Spatrick m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file);
6893061da546Spatrick if (m_file_sp) {
6894061da546Spatrick const size_t num_lines = m_file_sp->GetNumLines();
6895061da546Spatrick m_line_width = 1;
6896061da546Spatrick for (size_t n = num_lines; n >= 10; n = n / 10)
6897061da546Spatrick ++m_line_width;
6898061da546Spatrick
6899061da546Spatrick if (num_lines < num_visible_lines ||
6900061da546Spatrick m_selected_line < num_visible_lines)
6901061da546Spatrick m_first_visible_line = 0;
6902061da546Spatrick else
6903061da546Spatrick m_first_visible_line = m_selected_line - 10;
6904061da546Spatrick }
6905061da546Spatrick }
6906061da546Spatrick } else {
6907061da546Spatrick m_file_sp.reset();
6908061da546Spatrick }
6909061da546Spatrick
6910061da546Spatrick if (!m_file_sp || m_file_sp->GetNumLines() == 0) {
6911061da546Spatrick // Show disassembly
6912061da546Spatrick bool prefer_file_cache = false;
6913061da546Spatrick if (m_sc.function) {
6914061da546Spatrick if (m_disassembly_scope != m_sc.function) {
6915061da546Spatrick m_disassembly_scope = m_sc.function;
6916061da546Spatrick m_disassembly_sp = m_sc.function->GetInstructions(
6917be691f3bSpatrick exe_ctx, nullptr, !prefer_file_cache);
6918061da546Spatrick if (m_disassembly_sp) {
6919061da546Spatrick set_selected_line_to_pc = true;
6920061da546Spatrick m_disassembly_range = m_sc.function->GetAddressRange();
6921061da546Spatrick } else {
6922061da546Spatrick m_disassembly_range.Clear();
6923061da546Spatrick }
6924061da546Spatrick } else {
6925061da546Spatrick set_selected_line_to_pc = context_changed;
6926061da546Spatrick }
6927061da546Spatrick } else if (m_sc.symbol) {
6928061da546Spatrick if (m_disassembly_scope != m_sc.symbol) {
6929061da546Spatrick m_disassembly_scope = m_sc.symbol;
6930061da546Spatrick m_disassembly_sp = m_sc.symbol->GetInstructions(
6931061da546Spatrick exe_ctx, nullptr, prefer_file_cache);
6932061da546Spatrick if (m_disassembly_sp) {
6933061da546Spatrick set_selected_line_to_pc = true;
6934061da546Spatrick m_disassembly_range.GetBaseAddress() =
6935061da546Spatrick m_sc.symbol->GetAddress();
6936061da546Spatrick m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize());
6937061da546Spatrick } else {
6938061da546Spatrick m_disassembly_range.Clear();
6939061da546Spatrick }
6940061da546Spatrick } else {
6941061da546Spatrick set_selected_line_to_pc = context_changed;
6942061da546Spatrick }
6943061da546Spatrick }
6944061da546Spatrick }
6945061da546Spatrick } else {
6946061da546Spatrick m_pc_line = UINT32_MAX;
6947061da546Spatrick }
6948061da546Spatrick }
6949061da546Spatrick
6950061da546Spatrick const int window_width = window.GetWidth();
6951061da546Spatrick window.Erase();
6952061da546Spatrick window.DrawTitleBox("Sources");
6953061da546Spatrick if (!m_title.GetString().empty()) {
6954061da546Spatrick window.AttributeOn(A_REVERSE);
6955061da546Spatrick window.MoveCursor(1, 1);
6956061da546Spatrick window.PutChar(' ');
6957be691f3bSpatrick window.PutCStringTruncated(1, m_title.GetString().str().c_str());
6958061da546Spatrick int x = window.GetCursorX();
6959061da546Spatrick if (x < window_width - 1) {
6960061da546Spatrick window.Printf("%*s", window_width - x - 1, "");
6961061da546Spatrick }
6962061da546Spatrick window.AttributeOff(A_REVERSE);
6963061da546Spatrick }
6964061da546Spatrick
6965061da546Spatrick Target *target = exe_ctx.GetTargetPtr();
6966061da546Spatrick const size_t num_source_lines = GetNumSourceLines();
6967061da546Spatrick if (num_source_lines > 0) {
6968061da546Spatrick // Display source
6969061da546Spatrick BreakpointLines bp_lines;
6970061da546Spatrick if (target) {
6971061da546Spatrick BreakpointList &bp_list = target->GetBreakpointList();
6972061da546Spatrick const size_t num_bps = bp_list.GetSize();
6973061da546Spatrick for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
6974061da546Spatrick BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
6975061da546Spatrick const size_t num_bps_locs = bp_sp->GetNumLocations();
6976061da546Spatrick for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
6977061da546Spatrick BreakpointLocationSP bp_loc_sp =
6978061da546Spatrick bp_sp->GetLocationAtIndex(bp_loc_idx);
6979061da546Spatrick LineEntry bp_loc_line_entry;
6980061da546Spatrick if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry(
6981061da546Spatrick bp_loc_line_entry)) {
6982061da546Spatrick if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file) {
6983061da546Spatrick bp_lines.insert(bp_loc_line_entry.line);
6984061da546Spatrick }
6985061da546Spatrick }
6986061da546Spatrick }
6987061da546Spatrick }
6988061da546Spatrick }
6989061da546Spatrick
6990061da546Spatrick for (size_t i = 0; i < num_visible_lines; ++i) {
6991061da546Spatrick const uint32_t curr_line = m_first_visible_line + i;
6992061da546Spatrick if (curr_line < num_source_lines) {
6993061da546Spatrick const int line_y = m_min_y + i;
6994061da546Spatrick window.MoveCursor(1, line_y);
6995061da546Spatrick const bool is_pc_line = curr_line == m_pc_line;
6996061da546Spatrick const bool line_is_selected = m_selected_line == curr_line;
6997*f6aab3d8Srobert // Highlight the line as the PC line first (done by passing
6998*f6aab3d8Srobert // argument to OutputColoredStringTruncated()), then if the selected
6999*f6aab3d8Srobert // line isn't the same as the PC line, highlight it differently.
7000061da546Spatrick attr_t highlight_attr = 0;
7001061da546Spatrick attr_t bp_attr = 0;
7002*f6aab3d8Srobert if (line_is_selected && !is_pc_line)
7003*f6aab3d8Srobert highlight_attr = A_REVERSE;
7004061da546Spatrick
7005061da546Spatrick if (bp_lines.find(curr_line + 1) != bp_lines.end())
7006be691f3bSpatrick bp_attr = COLOR_PAIR(BlackOnWhite);
7007061da546Spatrick
7008061da546Spatrick if (bp_attr)
7009061da546Spatrick window.AttributeOn(bp_attr);
7010061da546Spatrick
7011061da546Spatrick window.Printf(" %*u ", m_line_width, curr_line + 1);
7012061da546Spatrick
7013061da546Spatrick if (bp_attr)
7014061da546Spatrick window.AttributeOff(bp_attr);
7015061da546Spatrick
7016061da546Spatrick window.PutChar(ACS_VLINE);
7017061da546Spatrick // Mark the line with the PC with a diamond
7018061da546Spatrick if (is_pc_line)
7019061da546Spatrick window.PutChar(ACS_DIAMOND);
7020061da546Spatrick else
7021061da546Spatrick window.PutChar(' ');
7022061da546Spatrick
7023061da546Spatrick if (highlight_attr)
7024061da546Spatrick window.AttributeOn(highlight_attr);
7025be691f3bSpatrick
7026be691f3bSpatrick StreamString lineStream;
7027*f6aab3d8Srobert
7028*f6aab3d8Srobert std::optional<size_t> column;
7029*f6aab3d8Srobert if (is_pc_line && m_sc.line_entry.IsValid() && m_sc.line_entry.column)
7030*f6aab3d8Srobert column = m_sc.line_entry.column - 1;
7031*f6aab3d8Srobert m_file_sp->DisplaySourceLines(curr_line + 1, column, 0, 0,
7032*f6aab3d8Srobert &lineStream);
7033be691f3bSpatrick StringRef line = lineStream.GetString();
7034be691f3bSpatrick if (line.endswith("\n"))
7035be691f3bSpatrick line = line.drop_back();
7036be691f3bSpatrick bool wasWritten = window.OutputColoredStringTruncated(
7037*f6aab3d8Srobert 1, line, m_first_visible_column, is_pc_line);
7038*f6aab3d8Srobert if (!wasWritten && (line_is_selected || is_pc_line)) {
7039*f6aab3d8Srobert // Draw an empty space to show the selected/PC line if empty,
7040be691f3bSpatrick // or draw '<' if nothing is visible because of scrolling too much
7041be691f3bSpatrick // to the right.
7042be691f3bSpatrick window.PutCStringTruncated(
7043be691f3bSpatrick 1, line.empty() && m_first_visible_column == 0 ? " " : "<");
7044be691f3bSpatrick }
7045061da546Spatrick
7046061da546Spatrick if (is_pc_line && frame_sp &&
7047061da546Spatrick frame_sp->GetConcreteFrameIndex() == 0) {
7048061da546Spatrick StopInfoSP stop_info_sp;
7049061da546Spatrick if (thread)
7050061da546Spatrick stop_info_sp = thread->GetStopInfo();
7051061da546Spatrick if (stop_info_sp) {
7052061da546Spatrick const char *stop_description = stop_info_sp->GetDescription();
7053061da546Spatrick if (stop_description && stop_description[0]) {
7054061da546Spatrick size_t stop_description_len = strlen(stop_description);
7055061da546Spatrick int desc_x = window_width - stop_description_len - 16;
7056be691f3bSpatrick if (desc_x - window.GetCursorX() > 0)
7057061da546Spatrick window.Printf("%*s", desc_x - window.GetCursorX(), "");
7058be691f3bSpatrick window.MoveCursor(window_width - stop_description_len - 16,
7059be691f3bSpatrick line_y);
7060be691f3bSpatrick const attr_t stop_reason_attr = COLOR_PAIR(WhiteOnBlue);
7061be691f3bSpatrick window.AttributeOn(stop_reason_attr);
7062be691f3bSpatrick window.PrintfTruncated(1, " <<< Thread %u: %s ",
7063be691f3bSpatrick thread->GetIndexID(), stop_description);
7064be691f3bSpatrick window.AttributeOff(stop_reason_attr);
7065061da546Spatrick }
7066061da546Spatrick } else {
7067061da546Spatrick window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
7068061da546Spatrick }
7069061da546Spatrick }
7070061da546Spatrick if (highlight_attr)
7071061da546Spatrick window.AttributeOff(highlight_attr);
7072061da546Spatrick } else {
7073061da546Spatrick break;
7074061da546Spatrick }
7075061da546Spatrick }
7076061da546Spatrick } else {
7077061da546Spatrick size_t num_disassembly_lines = GetNumDisassemblyLines();
7078061da546Spatrick if (num_disassembly_lines > 0) {
7079061da546Spatrick // Display disassembly
7080061da546Spatrick BreakpointAddrs bp_file_addrs;
7081061da546Spatrick Target *target = exe_ctx.GetTargetPtr();
7082061da546Spatrick if (target) {
7083061da546Spatrick BreakpointList &bp_list = target->GetBreakpointList();
7084061da546Spatrick const size_t num_bps = bp_list.GetSize();
7085061da546Spatrick for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
7086061da546Spatrick BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
7087061da546Spatrick const size_t num_bps_locs = bp_sp->GetNumLocations();
7088061da546Spatrick for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs;
7089061da546Spatrick ++bp_loc_idx) {
7090061da546Spatrick BreakpointLocationSP bp_loc_sp =
7091061da546Spatrick bp_sp->GetLocationAtIndex(bp_loc_idx);
7092061da546Spatrick LineEntry bp_loc_line_entry;
7093061da546Spatrick const lldb::addr_t file_addr =
7094061da546Spatrick bp_loc_sp->GetAddress().GetFileAddress();
7095061da546Spatrick if (file_addr != LLDB_INVALID_ADDRESS) {
7096061da546Spatrick if (m_disassembly_range.ContainsFileAddress(file_addr))
7097061da546Spatrick bp_file_addrs.insert(file_addr);
7098061da546Spatrick }
7099061da546Spatrick }
7100061da546Spatrick }
7101061da546Spatrick }
7102061da546Spatrick
7103061da546Spatrick const attr_t selected_highlight_attr = A_REVERSE;
7104be691f3bSpatrick const attr_t pc_highlight_attr = COLOR_PAIR(WhiteOnBlue);
7105061da546Spatrick
7106061da546Spatrick StreamString strm;
7107061da546Spatrick
7108061da546Spatrick InstructionList &insts = m_disassembly_sp->GetInstructionList();
7109061da546Spatrick Address pc_address;
7110061da546Spatrick
7111061da546Spatrick if (frame_sp)
7112061da546Spatrick pc_address = frame_sp->GetFrameCodeAddress();
7113061da546Spatrick const uint32_t pc_idx =
7114061da546Spatrick pc_address.IsValid()
7115061da546Spatrick ? insts.GetIndexOfInstructionAtAddress(pc_address)
7116061da546Spatrick : UINT32_MAX;
7117061da546Spatrick if (set_selected_line_to_pc) {
7118061da546Spatrick m_selected_line = pc_idx;
7119061da546Spatrick }
7120061da546Spatrick
7121061da546Spatrick const uint32_t non_visible_pc_offset = (num_visible_lines / 5);
7122061da546Spatrick if (static_cast<size_t>(m_first_visible_line) >= num_disassembly_lines)
7123061da546Spatrick m_first_visible_line = 0;
7124061da546Spatrick
7125061da546Spatrick if (pc_idx < num_disassembly_lines) {
7126061da546Spatrick if (pc_idx < static_cast<uint32_t>(m_first_visible_line) ||
7127061da546Spatrick pc_idx >= m_first_visible_line + num_visible_lines)
7128061da546Spatrick m_first_visible_line = pc_idx - non_visible_pc_offset;
7129061da546Spatrick }
7130061da546Spatrick
7131061da546Spatrick for (size_t i = 0; i < num_visible_lines; ++i) {
7132061da546Spatrick const uint32_t inst_idx = m_first_visible_line + i;
7133061da546Spatrick Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get();
7134061da546Spatrick if (!inst)
7135061da546Spatrick break;
7136061da546Spatrick
7137061da546Spatrick const int line_y = m_min_y + i;
7138061da546Spatrick window.MoveCursor(1, line_y);
7139061da546Spatrick const bool is_pc_line = frame_sp && inst_idx == pc_idx;
7140061da546Spatrick const bool line_is_selected = m_selected_line == inst_idx;
7141*f6aab3d8Srobert // Highlight the line as the PC line first, then if the selected
7142*f6aab3d8Srobert // line isn't the same as the PC line, highlight it differently
7143061da546Spatrick attr_t highlight_attr = 0;
7144061da546Spatrick attr_t bp_attr = 0;
7145061da546Spatrick if (is_pc_line)
7146061da546Spatrick highlight_attr = pc_highlight_attr;
7147061da546Spatrick else if (line_is_selected)
7148061da546Spatrick highlight_attr = selected_highlight_attr;
7149061da546Spatrick
7150061da546Spatrick if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) !=
7151061da546Spatrick bp_file_addrs.end())
7152be691f3bSpatrick bp_attr = COLOR_PAIR(BlackOnWhite);
7153061da546Spatrick
7154061da546Spatrick if (bp_attr)
7155061da546Spatrick window.AttributeOn(bp_attr);
7156061da546Spatrick
7157061da546Spatrick window.Printf(" 0x%16.16llx ",
7158061da546Spatrick static_cast<unsigned long long>(
7159061da546Spatrick inst->GetAddress().GetLoadAddress(target)));
7160061da546Spatrick
7161061da546Spatrick if (bp_attr)
7162061da546Spatrick window.AttributeOff(bp_attr);
7163061da546Spatrick
7164061da546Spatrick window.PutChar(ACS_VLINE);
7165061da546Spatrick // Mark the line with the PC with a diamond
7166061da546Spatrick if (is_pc_line)
7167061da546Spatrick window.PutChar(ACS_DIAMOND);
7168061da546Spatrick else
7169061da546Spatrick window.PutChar(' ');
7170061da546Spatrick
7171061da546Spatrick if (highlight_attr)
7172061da546Spatrick window.AttributeOn(highlight_attr);
7173061da546Spatrick
7174061da546Spatrick const char *mnemonic = inst->GetMnemonic(&exe_ctx);
7175061da546Spatrick const char *operands = inst->GetOperands(&exe_ctx);
7176061da546Spatrick const char *comment = inst->GetComment(&exe_ctx);
7177061da546Spatrick
7178061da546Spatrick if (mnemonic != nullptr && mnemonic[0] == '\0')
7179061da546Spatrick mnemonic = nullptr;
7180061da546Spatrick if (operands != nullptr && operands[0] == '\0')
7181061da546Spatrick operands = nullptr;
7182061da546Spatrick if (comment != nullptr && comment[0] == '\0')
7183061da546Spatrick comment = nullptr;
7184061da546Spatrick
7185061da546Spatrick strm.Clear();
7186061da546Spatrick
7187061da546Spatrick if (mnemonic != nullptr && operands != nullptr && comment != nullptr)
7188061da546Spatrick strm.Printf("%-8s %-25s ; %s", mnemonic, operands, comment);
7189061da546Spatrick else if (mnemonic != nullptr && operands != nullptr)
7190061da546Spatrick strm.Printf("%-8s %s", mnemonic, operands);
7191061da546Spatrick else if (mnemonic != nullptr)
7192061da546Spatrick strm.Printf("%s", mnemonic);
7193061da546Spatrick
7194061da546Spatrick int right_pad = 1;
7195be691f3bSpatrick window.PutCStringTruncated(
7196be691f3bSpatrick right_pad,
7197be691f3bSpatrick strm.GetString().substr(m_first_visible_column).data());
7198061da546Spatrick
7199061da546Spatrick if (is_pc_line && frame_sp &&
7200061da546Spatrick frame_sp->GetConcreteFrameIndex() == 0) {
7201061da546Spatrick StopInfoSP stop_info_sp;
7202061da546Spatrick if (thread)
7203061da546Spatrick stop_info_sp = thread->GetStopInfo();
7204061da546Spatrick if (stop_info_sp) {
7205061da546Spatrick const char *stop_description = stop_info_sp->GetDescription();
7206061da546Spatrick if (stop_description && stop_description[0]) {
7207061da546Spatrick size_t stop_description_len = strlen(stop_description);
7208061da546Spatrick int desc_x = window_width - stop_description_len - 16;
7209be691f3bSpatrick if (desc_x - window.GetCursorX() > 0)
7210061da546Spatrick window.Printf("%*s", desc_x - window.GetCursorX(), "");
7211be691f3bSpatrick window.MoveCursor(window_width - stop_description_len - 15,
7212be691f3bSpatrick line_y);
7213*f6aab3d8Srobert if (thread)
7214be691f3bSpatrick window.PrintfTruncated(1, "<<< Thread %u: %s ",
7215*f6aab3d8Srobert thread->GetIndexID(),
7216*f6aab3d8Srobert stop_description);
7217061da546Spatrick }
7218061da546Spatrick } else {
7219061da546Spatrick window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
7220061da546Spatrick }
7221061da546Spatrick }
7222061da546Spatrick if (highlight_attr)
7223061da546Spatrick window.AttributeOff(highlight_attr);
7224061da546Spatrick }
7225061da546Spatrick }
7226061da546Spatrick }
7227061da546Spatrick return true; // Drawing handled
7228061da546Spatrick }
7229061da546Spatrick
GetNumLines()7230061da546Spatrick size_t GetNumLines() {
7231061da546Spatrick size_t num_lines = GetNumSourceLines();
7232061da546Spatrick if (num_lines == 0)
7233061da546Spatrick num_lines = GetNumDisassemblyLines();
7234061da546Spatrick return num_lines;
7235061da546Spatrick }
7236061da546Spatrick
GetNumSourceLines() const7237061da546Spatrick size_t GetNumSourceLines() const {
7238061da546Spatrick if (m_file_sp)
7239061da546Spatrick return m_file_sp->GetNumLines();
7240061da546Spatrick return 0;
7241061da546Spatrick }
7242061da546Spatrick
GetNumDisassemblyLines() const7243061da546Spatrick size_t GetNumDisassemblyLines() const {
7244061da546Spatrick if (m_disassembly_sp)
7245061da546Spatrick return m_disassembly_sp->GetInstructionList().GetSize();
7246061da546Spatrick return 0;
7247061da546Spatrick }
7248061da546Spatrick
WindowDelegateHandleChar(Window & window,int c)7249061da546Spatrick HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
7250061da546Spatrick const uint32_t num_visible_lines = NumVisibleLines();
7251061da546Spatrick const size_t num_lines = GetNumLines();
7252061da546Spatrick
7253061da546Spatrick switch (c) {
7254061da546Spatrick case ',':
7255061da546Spatrick case KEY_PPAGE:
7256061da546Spatrick // Page up key
7257061da546Spatrick if (static_cast<uint32_t>(m_first_visible_line) > num_visible_lines)
7258061da546Spatrick m_first_visible_line -= num_visible_lines;
7259061da546Spatrick else
7260061da546Spatrick m_first_visible_line = 0;
7261061da546Spatrick m_selected_line = m_first_visible_line;
7262061da546Spatrick return eKeyHandled;
7263061da546Spatrick
7264061da546Spatrick case '.':
7265061da546Spatrick case KEY_NPAGE:
7266061da546Spatrick // Page down key
7267061da546Spatrick {
7268061da546Spatrick if (m_first_visible_line + num_visible_lines < num_lines)
7269061da546Spatrick m_first_visible_line += num_visible_lines;
7270061da546Spatrick else if (num_lines < num_visible_lines)
7271061da546Spatrick m_first_visible_line = 0;
7272061da546Spatrick else
7273061da546Spatrick m_first_visible_line = num_lines - num_visible_lines;
7274061da546Spatrick m_selected_line = m_first_visible_line;
7275061da546Spatrick }
7276061da546Spatrick return eKeyHandled;
7277061da546Spatrick
7278061da546Spatrick case KEY_UP:
7279061da546Spatrick if (m_selected_line > 0) {
7280061da546Spatrick m_selected_line--;
7281061da546Spatrick if (static_cast<size_t>(m_first_visible_line) > m_selected_line)
7282061da546Spatrick m_first_visible_line = m_selected_line;
7283061da546Spatrick }
7284061da546Spatrick return eKeyHandled;
7285061da546Spatrick
7286061da546Spatrick case KEY_DOWN:
7287061da546Spatrick if (m_selected_line + 1 < num_lines) {
7288061da546Spatrick m_selected_line++;
7289061da546Spatrick if (m_first_visible_line + num_visible_lines < m_selected_line)
7290061da546Spatrick m_first_visible_line++;
7291061da546Spatrick }
7292061da546Spatrick return eKeyHandled;
7293061da546Spatrick
7294be691f3bSpatrick case KEY_LEFT:
7295be691f3bSpatrick if (m_first_visible_column > 0)
7296be691f3bSpatrick --m_first_visible_column;
7297be691f3bSpatrick return eKeyHandled;
7298be691f3bSpatrick
7299be691f3bSpatrick case KEY_RIGHT:
7300be691f3bSpatrick ++m_first_visible_column;
7301be691f3bSpatrick return eKeyHandled;
7302be691f3bSpatrick
7303061da546Spatrick case '\r':
7304061da546Spatrick case '\n':
7305061da546Spatrick case KEY_ENTER:
7306061da546Spatrick // Set a breakpoint and run to the line using a one shot breakpoint
7307061da546Spatrick if (GetNumSourceLines() > 0) {
7308061da546Spatrick ExecutionContext exe_ctx =
7309061da546Spatrick m_debugger.GetCommandInterpreter().GetExecutionContext();
7310061da546Spatrick if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive()) {
7311061da546Spatrick BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
7312061da546Spatrick nullptr, // Don't limit the breakpoint to certain modules
7313061da546Spatrick m_file_sp->GetFileSpec(), // Source file
7314061da546Spatrick m_selected_line +
7315061da546Spatrick 1, // Source line number (m_selected_line is zero based)
7316061da546Spatrick 0, // Unspecified column.
7317061da546Spatrick 0, // No offset
7318061da546Spatrick eLazyBoolCalculate, // Check inlines using global setting
7319061da546Spatrick eLazyBoolCalculate, // Skip prologue using global setting,
7320061da546Spatrick false, // internal
7321061da546Spatrick false, // request_hardware
7322061da546Spatrick eLazyBoolCalculate); // move_to_nearest_code
7323061da546Spatrick // Make breakpoint one shot
7324be691f3bSpatrick bp_sp->GetOptions().SetOneShot(true);
7325061da546Spatrick exe_ctx.GetProcessRef().Resume();
7326061da546Spatrick }
7327061da546Spatrick } else if (m_selected_line < GetNumDisassemblyLines()) {
7328061da546Spatrick const Instruction *inst = m_disassembly_sp->GetInstructionList()
7329061da546Spatrick .GetInstructionAtIndex(m_selected_line)
7330061da546Spatrick .get();
7331061da546Spatrick ExecutionContext exe_ctx =
7332061da546Spatrick m_debugger.GetCommandInterpreter().GetExecutionContext();
7333061da546Spatrick if (exe_ctx.HasTargetScope()) {
7334061da546Spatrick Address addr = inst->GetAddress();
7335061da546Spatrick BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
7336061da546Spatrick addr, // lldb_private::Address
7337061da546Spatrick false, // internal
7338061da546Spatrick false); // request_hardware
7339061da546Spatrick // Make breakpoint one shot
7340be691f3bSpatrick bp_sp->GetOptions().SetOneShot(true);
7341061da546Spatrick exe_ctx.GetProcessRef().Resume();
7342061da546Spatrick }
7343061da546Spatrick }
7344061da546Spatrick return eKeyHandled;
7345061da546Spatrick
7346061da546Spatrick case 'b': // 'b' == toggle breakpoint on currently selected line
7347be691f3bSpatrick ToggleBreakpointOnSelectedLine();
7348061da546Spatrick return eKeyHandled;
7349061da546Spatrick
7350061da546Spatrick case 'D': // 'D' == detach and keep stopped
7351061da546Spatrick {
7352061da546Spatrick ExecutionContext exe_ctx =
7353061da546Spatrick m_debugger.GetCommandInterpreter().GetExecutionContext();
7354061da546Spatrick if (exe_ctx.HasProcessScope())
7355be691f3bSpatrick exe_ctx.GetProcessRef().Detach(true);
7356061da546Spatrick }
7357061da546Spatrick return eKeyHandled;
7358061da546Spatrick
7359061da546Spatrick case 'c':
7360061da546Spatrick // 'c' == continue
7361061da546Spatrick {
7362061da546Spatrick ExecutionContext exe_ctx =
7363061da546Spatrick m_debugger.GetCommandInterpreter().GetExecutionContext();
7364061da546Spatrick if (exe_ctx.HasProcessScope())
7365061da546Spatrick exe_ctx.GetProcessRef().Resume();
7366061da546Spatrick }
7367061da546Spatrick return eKeyHandled;
7368061da546Spatrick
7369be691f3bSpatrick case 'f':
7370be691f3bSpatrick // 'f' == step out (finish)
7371061da546Spatrick {
7372061da546Spatrick ExecutionContext exe_ctx =
7373061da546Spatrick m_debugger.GetCommandInterpreter().GetExecutionContext();
7374061da546Spatrick if (exe_ctx.HasThreadScope() &&
7375061da546Spatrick StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
7376*f6aab3d8Srobert Thread *thread = exe_ctx.GetThreadPtr();
7377*f6aab3d8Srobert uint32_t frame_idx = thread->GetSelectedFrameIndex();
7378*f6aab3d8Srobert exe_ctx.GetThreadRef().StepOut(frame_idx);
7379061da546Spatrick }
7380061da546Spatrick }
7381061da546Spatrick return eKeyHandled;
7382061da546Spatrick
7383061da546Spatrick case 'n': // 'n' == step over
7384061da546Spatrick case 'N': // 'N' == step over instruction
7385061da546Spatrick {
7386061da546Spatrick ExecutionContext exe_ctx =
7387061da546Spatrick m_debugger.GetCommandInterpreter().GetExecutionContext();
7388061da546Spatrick if (exe_ctx.HasThreadScope() &&
7389061da546Spatrick StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
7390061da546Spatrick bool source_step = (c == 'n');
7391061da546Spatrick exe_ctx.GetThreadRef().StepOver(source_step);
7392061da546Spatrick }
7393061da546Spatrick }
7394061da546Spatrick return eKeyHandled;
7395061da546Spatrick
7396061da546Spatrick case 's': // 's' == step into
7397061da546Spatrick case 'S': // 'S' == step into instruction
7398061da546Spatrick {
7399061da546Spatrick ExecutionContext exe_ctx =
7400061da546Spatrick m_debugger.GetCommandInterpreter().GetExecutionContext();
7401061da546Spatrick if (exe_ctx.HasThreadScope() &&
7402061da546Spatrick StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
7403061da546Spatrick bool source_step = (c == 's');
7404061da546Spatrick exe_ctx.GetThreadRef().StepIn(source_step);
7405061da546Spatrick }
7406061da546Spatrick }
7407061da546Spatrick return eKeyHandled;
7408061da546Spatrick
7409be691f3bSpatrick case 'u': // 'u' == frame up
7410be691f3bSpatrick case 'd': // 'd' == frame down
7411be691f3bSpatrick {
7412be691f3bSpatrick ExecutionContext exe_ctx =
7413be691f3bSpatrick m_debugger.GetCommandInterpreter().GetExecutionContext();
7414be691f3bSpatrick if (exe_ctx.HasThreadScope()) {
7415be691f3bSpatrick Thread *thread = exe_ctx.GetThreadPtr();
7416be691f3bSpatrick uint32_t frame_idx = thread->GetSelectedFrameIndex();
7417be691f3bSpatrick if (frame_idx == UINT32_MAX)
7418be691f3bSpatrick frame_idx = 0;
7419be691f3bSpatrick if (c == 'u' && frame_idx + 1 < thread->GetStackFrameCount())
7420be691f3bSpatrick ++frame_idx;
7421be691f3bSpatrick else if (c == 'd' && frame_idx > 0)
7422be691f3bSpatrick --frame_idx;
7423be691f3bSpatrick if (thread->SetSelectedFrameByIndex(frame_idx, true))
7424be691f3bSpatrick exe_ctx.SetFrameSP(thread->GetSelectedFrame());
7425be691f3bSpatrick }
7426be691f3bSpatrick }
7427be691f3bSpatrick return eKeyHandled;
7428be691f3bSpatrick
7429061da546Spatrick case 'h':
7430061da546Spatrick window.CreateHelpSubwindow();
7431061da546Spatrick return eKeyHandled;
7432061da546Spatrick
7433061da546Spatrick default:
7434061da546Spatrick break;
7435061da546Spatrick }
7436061da546Spatrick return eKeyNotHandled;
7437061da546Spatrick }
7438061da546Spatrick
ToggleBreakpointOnSelectedLine()7439be691f3bSpatrick void ToggleBreakpointOnSelectedLine() {
7440be691f3bSpatrick ExecutionContext exe_ctx =
7441be691f3bSpatrick m_debugger.GetCommandInterpreter().GetExecutionContext();
7442be691f3bSpatrick if (!exe_ctx.HasTargetScope())
7443be691f3bSpatrick return;
7444be691f3bSpatrick if (GetNumSourceLines() > 0) {
7445be691f3bSpatrick // Source file breakpoint.
7446be691f3bSpatrick BreakpointList &bp_list = exe_ctx.GetTargetRef().GetBreakpointList();
7447be691f3bSpatrick const size_t num_bps = bp_list.GetSize();
7448be691f3bSpatrick for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
7449be691f3bSpatrick BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
7450be691f3bSpatrick const size_t num_bps_locs = bp_sp->GetNumLocations();
7451be691f3bSpatrick for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
7452be691f3bSpatrick BreakpointLocationSP bp_loc_sp =
7453be691f3bSpatrick bp_sp->GetLocationAtIndex(bp_loc_idx);
7454be691f3bSpatrick LineEntry bp_loc_line_entry;
7455be691f3bSpatrick if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry(
7456be691f3bSpatrick bp_loc_line_entry)) {
7457be691f3bSpatrick if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file &&
7458be691f3bSpatrick m_selected_line + 1 == bp_loc_line_entry.line) {
7459be691f3bSpatrick bool removed =
7460be691f3bSpatrick exe_ctx.GetTargetRef().RemoveBreakpointByID(bp_sp->GetID());
7461be691f3bSpatrick assert(removed);
7462be691f3bSpatrick UNUSED_IF_ASSERT_DISABLED(removed);
7463be691f3bSpatrick return; // Existing breakpoint removed.
7464be691f3bSpatrick }
7465be691f3bSpatrick }
7466be691f3bSpatrick }
7467be691f3bSpatrick }
7468be691f3bSpatrick // No breakpoint found on the location, add it.
7469be691f3bSpatrick BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
7470be691f3bSpatrick nullptr, // Don't limit the breakpoint to certain modules
7471be691f3bSpatrick m_file_sp->GetFileSpec(), // Source file
7472be691f3bSpatrick m_selected_line +
7473be691f3bSpatrick 1, // Source line number (m_selected_line is zero based)
7474be691f3bSpatrick 0, // No column specified.
7475be691f3bSpatrick 0, // No offset
7476be691f3bSpatrick eLazyBoolCalculate, // Check inlines using global setting
7477be691f3bSpatrick eLazyBoolCalculate, // Skip prologue using global setting,
7478be691f3bSpatrick false, // internal
7479be691f3bSpatrick false, // request_hardware
7480be691f3bSpatrick eLazyBoolCalculate); // move_to_nearest_code
7481be691f3bSpatrick } else {
7482be691f3bSpatrick // Disassembly breakpoint.
7483be691f3bSpatrick assert(GetNumDisassemblyLines() > 0);
7484be691f3bSpatrick assert(m_selected_line < GetNumDisassemblyLines());
7485be691f3bSpatrick const Instruction *inst = m_disassembly_sp->GetInstructionList()
7486be691f3bSpatrick .GetInstructionAtIndex(m_selected_line)
7487be691f3bSpatrick .get();
7488be691f3bSpatrick Address addr = inst->GetAddress();
7489be691f3bSpatrick // Try to find it.
7490be691f3bSpatrick BreakpointList &bp_list = exe_ctx.GetTargetRef().GetBreakpointList();
7491be691f3bSpatrick const size_t num_bps = bp_list.GetSize();
7492be691f3bSpatrick for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
7493be691f3bSpatrick BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
7494be691f3bSpatrick const size_t num_bps_locs = bp_sp->GetNumLocations();
7495be691f3bSpatrick for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
7496be691f3bSpatrick BreakpointLocationSP bp_loc_sp =
7497be691f3bSpatrick bp_sp->GetLocationAtIndex(bp_loc_idx);
7498be691f3bSpatrick LineEntry bp_loc_line_entry;
7499be691f3bSpatrick const lldb::addr_t file_addr =
7500be691f3bSpatrick bp_loc_sp->GetAddress().GetFileAddress();
7501be691f3bSpatrick if (file_addr == addr.GetFileAddress()) {
7502be691f3bSpatrick bool removed =
7503be691f3bSpatrick exe_ctx.GetTargetRef().RemoveBreakpointByID(bp_sp->GetID());
7504be691f3bSpatrick assert(removed);
7505be691f3bSpatrick UNUSED_IF_ASSERT_DISABLED(removed);
7506be691f3bSpatrick return; // Existing breakpoint removed.
7507be691f3bSpatrick }
7508be691f3bSpatrick }
7509be691f3bSpatrick }
7510be691f3bSpatrick // No breakpoint found on the address, add it.
7511be691f3bSpatrick BreakpointSP bp_sp =
7512be691f3bSpatrick exe_ctx.GetTargetRef().CreateBreakpoint(addr, // lldb_private::Address
7513be691f3bSpatrick false, // internal
7514be691f3bSpatrick false); // request_hardware
7515be691f3bSpatrick }
7516be691f3bSpatrick }
7517be691f3bSpatrick
7518061da546Spatrick protected:
7519061da546Spatrick typedef std::set<uint32_t> BreakpointLines;
7520061da546Spatrick typedef std::set<lldb::addr_t> BreakpointAddrs;
7521061da546Spatrick
7522061da546Spatrick Debugger &m_debugger;
7523061da546Spatrick SymbolContext m_sc;
7524061da546Spatrick SourceManager::FileSP m_file_sp;
7525*f6aab3d8Srobert SymbolContextScope *m_disassembly_scope = nullptr;
7526061da546Spatrick lldb::DisassemblerSP m_disassembly_sp;
7527061da546Spatrick AddressRange m_disassembly_range;
7528061da546Spatrick StreamString m_title;
7529*f6aab3d8Srobert lldb::user_id_t m_tid = LLDB_INVALID_THREAD_ID;
7530*f6aab3d8Srobert int m_line_width = 4;
7531*f6aab3d8Srobert uint32_t m_selected_line = 0; // The selected line
7532*f6aab3d8Srobert uint32_t m_pc_line = 0; // The line with the PC
7533*f6aab3d8Srobert uint32_t m_stop_id = 0;
7534*f6aab3d8Srobert uint32_t m_frame_idx = UINT32_MAX;
7535*f6aab3d8Srobert int m_first_visible_line = 0;
7536*f6aab3d8Srobert int m_first_visible_column = 0;
7537*f6aab3d8Srobert int m_min_x = 0;
7538*f6aab3d8Srobert int m_min_y = 0;
7539*f6aab3d8Srobert int m_max_x = 0;
7540*f6aab3d8Srobert int m_max_y = 0;
7541061da546Spatrick };
7542061da546Spatrick
7543061da546Spatrick DisplayOptions ValueObjectListDelegate::g_options = {true};
7544061da546Spatrick
IOHandlerCursesGUI(Debugger & debugger)7545061da546Spatrick IOHandlerCursesGUI::IOHandlerCursesGUI(Debugger &debugger)
7546061da546Spatrick : IOHandler(debugger, IOHandler::Type::Curses) {}
7547061da546Spatrick
Activate()7548061da546Spatrick void IOHandlerCursesGUI::Activate() {
7549061da546Spatrick IOHandler::Activate();
7550061da546Spatrick if (!m_app_ap) {
7551dda28197Spatrick m_app_ap = std::make_unique<Application>(GetInputFILE(), GetOutputFILE());
7552061da546Spatrick
7553061da546Spatrick // This is both a window and a menu delegate
7554061da546Spatrick std::shared_ptr<ApplicationDelegate> app_delegate_sp(
7555061da546Spatrick new ApplicationDelegate(*m_app_ap, m_debugger));
7556061da546Spatrick
7557061da546Spatrick MenuDelegateSP app_menu_delegate_sp =
7558061da546Spatrick std::static_pointer_cast<MenuDelegate>(app_delegate_sp);
7559061da546Spatrick MenuSP lldb_menu_sp(
7560061da546Spatrick new Menu("LLDB", "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB));
7561061da546Spatrick MenuSP exit_menuitem_sp(
7562061da546Spatrick new Menu("Exit", nullptr, 'x', ApplicationDelegate::eMenuID_LLDBExit));
7563061da546Spatrick exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit);
7564061da546Spatrick lldb_menu_sp->AddSubmenu(MenuSP(new Menu(
7565061da546Spatrick "About LLDB", nullptr, 'a', ApplicationDelegate::eMenuID_LLDBAbout)));
7566061da546Spatrick lldb_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
7567061da546Spatrick lldb_menu_sp->AddSubmenu(exit_menuitem_sp);
7568061da546Spatrick
7569061da546Spatrick MenuSP target_menu_sp(new Menu("Target", "F2", KEY_F(2),
7570061da546Spatrick ApplicationDelegate::eMenuID_Target));
7571061da546Spatrick target_menu_sp->AddSubmenu(MenuSP(new Menu(
7572061da546Spatrick "Create", nullptr, 'c', ApplicationDelegate::eMenuID_TargetCreate)));
7573061da546Spatrick target_menu_sp->AddSubmenu(MenuSP(new Menu(
7574061da546Spatrick "Delete", nullptr, 'd', ApplicationDelegate::eMenuID_TargetDelete)));
7575061da546Spatrick
7576061da546Spatrick MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3),
7577061da546Spatrick ApplicationDelegate::eMenuID_Process));
7578061da546Spatrick process_menu_sp->AddSubmenu(MenuSP(new Menu(
7579061da546Spatrick "Attach", nullptr, 'a', ApplicationDelegate::eMenuID_ProcessAttach)));
7580be691f3bSpatrick process_menu_sp->AddSubmenu(
7581be691f3bSpatrick MenuSP(new Menu("Detach and resume", nullptr, 'd',
7582be691f3bSpatrick ApplicationDelegate::eMenuID_ProcessDetachResume)));
7583be691f3bSpatrick process_menu_sp->AddSubmenu(
7584be691f3bSpatrick MenuSP(new Menu("Detach suspended", nullptr, 's',
7585be691f3bSpatrick ApplicationDelegate::eMenuID_ProcessDetachSuspended)));
7586061da546Spatrick process_menu_sp->AddSubmenu(MenuSP(new Menu(
7587061da546Spatrick "Launch", nullptr, 'l', ApplicationDelegate::eMenuID_ProcessLaunch)));
7588061da546Spatrick process_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
7589061da546Spatrick process_menu_sp->AddSubmenu(
7590061da546Spatrick MenuSP(new Menu("Continue", nullptr, 'c',
7591061da546Spatrick ApplicationDelegate::eMenuID_ProcessContinue)));
7592061da546Spatrick process_menu_sp->AddSubmenu(MenuSP(new Menu(
7593061da546Spatrick "Halt", nullptr, 'h', ApplicationDelegate::eMenuID_ProcessHalt)));
7594061da546Spatrick process_menu_sp->AddSubmenu(MenuSP(new Menu(
7595061da546Spatrick "Kill", nullptr, 'k', ApplicationDelegate::eMenuID_ProcessKill)));
7596061da546Spatrick
7597061da546Spatrick MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4),
7598061da546Spatrick ApplicationDelegate::eMenuID_Thread));
7599061da546Spatrick thread_menu_sp->AddSubmenu(MenuSP(new Menu(
7600061da546Spatrick "Step In", nullptr, 'i', ApplicationDelegate::eMenuID_ThreadStepIn)));
7601061da546Spatrick thread_menu_sp->AddSubmenu(
7602061da546Spatrick MenuSP(new Menu("Step Over", nullptr, 'v',
7603061da546Spatrick ApplicationDelegate::eMenuID_ThreadStepOver)));
7604061da546Spatrick thread_menu_sp->AddSubmenu(MenuSP(new Menu(
7605061da546Spatrick "Step Out", nullptr, 'o', ApplicationDelegate::eMenuID_ThreadStepOut)));
7606061da546Spatrick
7607061da546Spatrick MenuSP view_menu_sp(
7608061da546Spatrick new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View));
7609061da546Spatrick view_menu_sp->AddSubmenu(
7610*f6aab3d8Srobert MenuSP(new Menu("Backtrace", nullptr, 't',
7611061da546Spatrick ApplicationDelegate::eMenuID_ViewBacktrace)));
7612061da546Spatrick view_menu_sp->AddSubmenu(
7613061da546Spatrick MenuSP(new Menu("Registers", nullptr, 'r',
7614061da546Spatrick ApplicationDelegate::eMenuID_ViewRegisters)));
7615061da546Spatrick view_menu_sp->AddSubmenu(MenuSP(new Menu(
7616061da546Spatrick "Source", nullptr, 's', ApplicationDelegate::eMenuID_ViewSource)));
7617061da546Spatrick view_menu_sp->AddSubmenu(
7618061da546Spatrick MenuSP(new Menu("Variables", nullptr, 'v',
7619061da546Spatrick ApplicationDelegate::eMenuID_ViewVariables)));
7620*f6aab3d8Srobert view_menu_sp->AddSubmenu(
7621*f6aab3d8Srobert MenuSP(new Menu("Breakpoints", nullptr, 'b',
7622*f6aab3d8Srobert ApplicationDelegate::eMenuID_ViewBreakpoints)));
7623061da546Spatrick
7624061da546Spatrick MenuSP help_menu_sp(
7625061da546Spatrick new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help));
7626061da546Spatrick help_menu_sp->AddSubmenu(MenuSP(new Menu(
7627061da546Spatrick "GUI Help", nullptr, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp)));
7628061da546Spatrick
7629061da546Spatrick m_app_ap->Initialize();
7630061da546Spatrick WindowSP &main_window_sp = m_app_ap->GetMainWindow();
7631061da546Spatrick
7632061da546Spatrick MenuSP menubar_sp(new Menu(Menu::Type::Bar));
7633061da546Spatrick menubar_sp->AddSubmenu(lldb_menu_sp);
7634061da546Spatrick menubar_sp->AddSubmenu(target_menu_sp);
7635061da546Spatrick menubar_sp->AddSubmenu(process_menu_sp);
7636061da546Spatrick menubar_sp->AddSubmenu(thread_menu_sp);
7637061da546Spatrick menubar_sp->AddSubmenu(view_menu_sp);
7638061da546Spatrick menubar_sp->AddSubmenu(help_menu_sp);
7639061da546Spatrick menubar_sp->SetDelegate(app_menu_delegate_sp);
7640061da546Spatrick
7641061da546Spatrick Rect content_bounds = main_window_sp->GetFrame();
7642061da546Spatrick Rect menubar_bounds = content_bounds.MakeMenuBar();
7643061da546Spatrick Rect status_bounds = content_bounds.MakeStatusBar();
7644061da546Spatrick Rect source_bounds;
7645061da546Spatrick Rect variables_bounds;
7646061da546Spatrick Rect threads_bounds;
7647061da546Spatrick Rect source_variables_bounds;
7648061da546Spatrick content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds,
7649061da546Spatrick threads_bounds);
7650061da546Spatrick source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds,
7651061da546Spatrick variables_bounds);
7652061da546Spatrick
7653061da546Spatrick WindowSP menubar_window_sp =
7654061da546Spatrick main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false);
7655061da546Spatrick // Let the menubar get keys if the active window doesn't handle the keys
7656061da546Spatrick // that are typed so it can respond to menubar key presses.
7657061da546Spatrick menubar_window_sp->SetCanBeActive(
7658061da546Spatrick false); // Don't let the menubar become the active window
7659061da546Spatrick menubar_window_sp->SetDelegate(menubar_sp);
7660061da546Spatrick
7661061da546Spatrick WindowSP source_window_sp(
7662061da546Spatrick main_window_sp->CreateSubWindow("Source", source_bounds, true));
7663061da546Spatrick WindowSP variables_window_sp(
7664061da546Spatrick main_window_sp->CreateSubWindow("Variables", variables_bounds, false));
7665061da546Spatrick WindowSP threads_window_sp(
7666061da546Spatrick main_window_sp->CreateSubWindow("Threads", threads_bounds, false));
7667061da546Spatrick WindowSP status_window_sp(
7668061da546Spatrick main_window_sp->CreateSubWindow("Status", status_bounds, false));
7669061da546Spatrick status_window_sp->SetCanBeActive(
7670061da546Spatrick false); // Don't let the status bar become the active window
7671061da546Spatrick main_window_sp->SetDelegate(
7672061da546Spatrick std::static_pointer_cast<WindowDelegate>(app_delegate_sp));
7673061da546Spatrick source_window_sp->SetDelegate(
7674061da546Spatrick WindowDelegateSP(new SourceFileWindowDelegate(m_debugger)));
7675061da546Spatrick variables_window_sp->SetDelegate(
7676061da546Spatrick WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
7677061da546Spatrick TreeDelegateSP thread_delegate_sp(new ThreadsTreeDelegate(m_debugger));
7678061da546Spatrick threads_window_sp->SetDelegate(WindowDelegateSP(
7679061da546Spatrick new TreeWindowDelegate(m_debugger, thread_delegate_sp)));
7680061da546Spatrick status_window_sp->SetDelegate(
7681061da546Spatrick WindowDelegateSP(new StatusBarWindowDelegate(m_debugger)));
7682061da546Spatrick
7683be691f3bSpatrick // All colors with black background.
7684be691f3bSpatrick init_pair(1, COLOR_BLACK, COLOR_BLACK);
7685be691f3bSpatrick init_pair(2, COLOR_RED, COLOR_BLACK);
7686be691f3bSpatrick init_pair(3, COLOR_GREEN, COLOR_BLACK);
7687be691f3bSpatrick init_pair(4, COLOR_YELLOW, COLOR_BLACK);
7688be691f3bSpatrick init_pair(5, COLOR_BLUE, COLOR_BLACK);
7689be691f3bSpatrick init_pair(6, COLOR_MAGENTA, COLOR_BLACK);
7690be691f3bSpatrick init_pair(7, COLOR_CYAN, COLOR_BLACK);
7691be691f3bSpatrick init_pair(8, COLOR_WHITE, COLOR_BLACK);
7692be691f3bSpatrick // All colors with blue background.
7693be691f3bSpatrick init_pair(9, COLOR_BLACK, COLOR_BLUE);
7694be691f3bSpatrick init_pair(10, COLOR_RED, COLOR_BLUE);
7695be691f3bSpatrick init_pair(11, COLOR_GREEN, COLOR_BLUE);
7696be691f3bSpatrick init_pair(12, COLOR_YELLOW, COLOR_BLUE);
7697be691f3bSpatrick init_pair(13, COLOR_BLUE, COLOR_BLUE);
7698be691f3bSpatrick init_pair(14, COLOR_MAGENTA, COLOR_BLUE);
7699be691f3bSpatrick init_pair(15, COLOR_CYAN, COLOR_BLUE);
7700be691f3bSpatrick init_pair(16, COLOR_WHITE, COLOR_BLUE);
7701be691f3bSpatrick // These must match the order in the color indexes enum.
7702be691f3bSpatrick init_pair(17, COLOR_BLACK, COLOR_WHITE);
7703be691f3bSpatrick init_pair(18, COLOR_MAGENTA, COLOR_WHITE);
7704be691f3bSpatrick static_assert(LastColorPairIndex == 18, "Color indexes do not match.");
7705be691f3bSpatrick
7706be691f3bSpatrick define_key("\033[Z", KEY_SHIFT_TAB);
7707*f6aab3d8Srobert define_key("\033\015", KEY_ALT_ENTER);
7708061da546Spatrick }
7709061da546Spatrick }
7710061da546Spatrick
Deactivate()7711061da546Spatrick void IOHandlerCursesGUI::Deactivate() { m_app_ap->Terminate(); }
7712061da546Spatrick
Run()7713061da546Spatrick void IOHandlerCursesGUI::Run() {
7714061da546Spatrick m_app_ap->Run(m_debugger);
7715061da546Spatrick SetIsDone(true);
7716061da546Spatrick }
7717061da546Spatrick
7718061da546Spatrick IOHandlerCursesGUI::~IOHandlerCursesGUI() = default;
7719061da546Spatrick
Cancel()7720061da546Spatrick void IOHandlerCursesGUI::Cancel() {}
7721061da546Spatrick
Interrupt()7722*f6aab3d8Srobert bool IOHandlerCursesGUI::Interrupt() {
7723*f6aab3d8Srobert return m_debugger.GetCommandInterpreter().IOHandlerInterrupt(*this);
7724*f6aab3d8Srobert }
7725061da546Spatrick
GotEOF()7726061da546Spatrick void IOHandlerCursesGUI::GotEOF() {}
7727061da546Spatrick
TerminalSizeChanged()7728be691f3bSpatrick void IOHandlerCursesGUI::TerminalSizeChanged() {
7729be691f3bSpatrick m_app_ap->TerminalSizeChanged();
7730be691f3bSpatrick }
7731be691f3bSpatrick
7732061da546Spatrick #endif // LLDB_ENABLE_CURSES
7733