1cfca06d7SDimitry Andric //===-- IOHandlerCursesGUI.cpp --------------------------------------------===//
2706b4fc4SDimitry Andric //
3706b4fc4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4706b4fc4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5706b4fc4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6706b4fc4SDimitry Andric //
7706b4fc4SDimitry Andric //===----------------------------------------------------------------------===//
8706b4fc4SDimitry Andric
9706b4fc4SDimitry Andric #include "lldb/Core/IOHandlerCursesGUI.h"
10706b4fc4SDimitry Andric #include "lldb/Host/Config.h"
11706b4fc4SDimitry Andric
12706b4fc4SDimitry Andric #if LLDB_ENABLE_CURSES
13b60736ecSDimitry Andric #if CURSES_HAVE_NCURSES_CURSES_H
14b60736ecSDimitry Andric #include <ncurses/curses.h>
15b60736ecSDimitry Andric #include <ncurses/panel.h>
16b60736ecSDimitry Andric #else
17706b4fc4SDimitry Andric #include <curses.h>
18706b4fc4SDimitry Andric #include <panel.h>
19706b4fc4SDimitry Andric #endif
20b60736ecSDimitry Andric #endif
21706b4fc4SDimitry Andric
22706b4fc4SDimitry Andric #if defined(__APPLE__)
23706b4fc4SDimitry Andric #include <deque>
24706b4fc4SDimitry Andric #endif
25706b4fc4SDimitry Andric #include <string>
26706b4fc4SDimitry Andric
27706b4fc4SDimitry Andric #include "lldb/Core/Debugger.h"
28344a3780SDimitry Andric #include "lldb/Core/ValueObjectUpdater.h"
29706b4fc4SDimitry Andric #include "lldb/Host/File.h"
30145449b1SDimitry Andric #include "lldb/Utility/AnsiTerminal.h"
31706b4fc4SDimitry Andric #include "lldb/Utility/Predicate.h"
32706b4fc4SDimitry Andric #include "lldb/Utility/Status.h"
33706b4fc4SDimitry Andric #include "lldb/Utility/StreamString.h"
34706b4fc4SDimitry Andric #include "lldb/Utility/StringList.h"
35706b4fc4SDimitry Andric #include "lldb/lldb-forward.h"
36706b4fc4SDimitry Andric
37706b4fc4SDimitry Andric #include "lldb/Interpreter/CommandCompletions.h"
38706b4fc4SDimitry Andric #include "lldb/Interpreter/CommandInterpreter.h"
39c0981da4SDimitry Andric #include "lldb/Interpreter/OptionGroupPlatform.h"
40706b4fc4SDimitry Andric
41706b4fc4SDimitry Andric #if LLDB_ENABLE_CURSES
42706b4fc4SDimitry Andric #include "lldb/Breakpoint/BreakpointLocation.h"
43706b4fc4SDimitry Andric #include "lldb/Core/Module.h"
44344a3780SDimitry Andric #include "lldb/Core/PluginManager.h"
45706b4fc4SDimitry Andric #include "lldb/Core/ValueObject.h"
46706b4fc4SDimitry Andric #include "lldb/Core/ValueObjectRegister.h"
47706b4fc4SDimitry Andric #include "lldb/Symbol/Block.h"
48c0981da4SDimitry Andric #include "lldb/Symbol/CompileUnit.h"
49706b4fc4SDimitry Andric #include "lldb/Symbol/Function.h"
50706b4fc4SDimitry Andric #include "lldb/Symbol/Symbol.h"
51706b4fc4SDimitry Andric #include "lldb/Symbol/VariableList.h"
52706b4fc4SDimitry Andric #include "lldb/Target/Process.h"
53706b4fc4SDimitry Andric #include "lldb/Target/RegisterContext.h"
54706b4fc4SDimitry Andric #include "lldb/Target/StackFrame.h"
55706b4fc4SDimitry Andric #include "lldb/Target/StopInfo.h"
56706b4fc4SDimitry Andric #include "lldb/Target/Target.h"
57706b4fc4SDimitry Andric #include "lldb/Target/Thread.h"
58706b4fc4SDimitry Andric #include "lldb/Utility/State.h"
59706b4fc4SDimitry Andric #endif
60706b4fc4SDimitry Andric
61706b4fc4SDimitry Andric #include "llvm/ADT/StringRef.h"
62706b4fc4SDimitry Andric
63706b4fc4SDimitry Andric #ifdef _WIN32
64706b4fc4SDimitry Andric #include "lldb/Host/windows/windows.h"
65706b4fc4SDimitry Andric #endif
66706b4fc4SDimitry Andric
67706b4fc4SDimitry Andric #include <memory>
68706b4fc4SDimitry Andric #include <mutex>
69706b4fc4SDimitry Andric
70344a3780SDimitry Andric #include <cassert>
71344a3780SDimitry Andric #include <cctype>
72344a3780SDimitry Andric #include <cerrno>
73344a3780SDimitry Andric #include <cstdint>
74344a3780SDimitry Andric #include <cstdio>
75344a3780SDimitry Andric #include <cstring>
76344a3780SDimitry Andric #include <functional>
77e3b55780SDimitry Andric #include <optional>
78706b4fc4SDimitry Andric #include <type_traits>
79706b4fc4SDimitry Andric
80706b4fc4SDimitry Andric using namespace lldb;
81706b4fc4SDimitry Andric using namespace lldb_private;
82706b4fc4SDimitry Andric using llvm::StringRef;
83706b4fc4SDimitry Andric
84706b4fc4SDimitry Andric // we may want curses to be disabled for some builds for instance, windows
85706b4fc4SDimitry Andric #if LLDB_ENABLE_CURSES
86706b4fc4SDimitry Andric
87c0981da4SDimitry Andric #define KEY_CTRL_A 1
88c0981da4SDimitry Andric #define KEY_CTRL_E 5
89c0981da4SDimitry Andric #define KEY_CTRL_K 11
90706b4fc4SDimitry Andric #define KEY_RETURN 10
91706b4fc4SDimitry Andric #define KEY_ESCAPE 27
92c0981da4SDimitry Andric #define KEY_DELETE 127
93706b4fc4SDimitry Andric
94344a3780SDimitry Andric #define KEY_SHIFT_TAB (KEY_MAX + 1)
95c0981da4SDimitry Andric #define KEY_ALT_ENTER (KEY_MAX + 2)
96344a3780SDimitry Andric
97706b4fc4SDimitry Andric namespace curses {
98706b4fc4SDimitry Andric class Menu;
99706b4fc4SDimitry Andric class MenuDelegate;
100706b4fc4SDimitry Andric class Window;
101706b4fc4SDimitry Andric class WindowDelegate;
102706b4fc4SDimitry Andric typedef std::shared_ptr<Menu> MenuSP;
103706b4fc4SDimitry Andric typedef std::shared_ptr<MenuDelegate> MenuDelegateSP;
104706b4fc4SDimitry Andric typedef std::shared_ptr<Window> WindowSP;
105706b4fc4SDimitry Andric typedef std::shared_ptr<WindowDelegate> WindowDelegateSP;
106706b4fc4SDimitry Andric typedef std::vector<MenuSP> Menus;
107706b4fc4SDimitry Andric typedef std::vector<WindowSP> Windows;
108706b4fc4SDimitry Andric typedef std::vector<WindowDelegateSP> WindowDelegates;
109706b4fc4SDimitry Andric
110706b4fc4SDimitry Andric #if 0
111706b4fc4SDimitry Andric type summary add -s "x=${var.x}, y=${var.y}" curses::Point
112706b4fc4SDimitry Andric type summary add -s "w=${var.width}, h=${var.height}" curses::Size
113706b4fc4SDimitry Andric type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect
114706b4fc4SDimitry Andric #endif
115706b4fc4SDimitry Andric
116706b4fc4SDimitry Andric struct Point {
117706b4fc4SDimitry Andric int x;
118706b4fc4SDimitry Andric int y;
119706b4fc4SDimitry Andric
Pointcurses::Point120706b4fc4SDimitry Andric Point(int _x = 0, int _y = 0) : x(_x), y(_y) {}
121706b4fc4SDimitry Andric
Clearcurses::Point122706b4fc4SDimitry Andric void Clear() {
123706b4fc4SDimitry Andric x = 0;
124706b4fc4SDimitry Andric y = 0;
125706b4fc4SDimitry Andric }
126706b4fc4SDimitry Andric
operator +=curses::Point127706b4fc4SDimitry Andric Point &operator+=(const Point &rhs) {
128706b4fc4SDimitry Andric x += rhs.x;
129706b4fc4SDimitry Andric y += rhs.y;
130706b4fc4SDimitry Andric return *this;
131706b4fc4SDimitry Andric }
132706b4fc4SDimitry Andric
Dumpcurses::Point133706b4fc4SDimitry Andric void Dump() { printf("(x=%i, y=%i)\n", x, y); }
134706b4fc4SDimitry Andric };
135706b4fc4SDimitry Andric
operator ==(const Point & lhs,const Point & rhs)136706b4fc4SDimitry Andric bool operator==(const Point &lhs, const Point &rhs) {
137706b4fc4SDimitry Andric return lhs.x == rhs.x && lhs.y == rhs.y;
138706b4fc4SDimitry Andric }
139706b4fc4SDimitry Andric
operator !=(const Point & lhs,const Point & rhs)140706b4fc4SDimitry Andric bool operator!=(const Point &lhs, const Point &rhs) {
141706b4fc4SDimitry Andric return lhs.x != rhs.x || lhs.y != rhs.y;
142706b4fc4SDimitry Andric }
143706b4fc4SDimitry Andric
144706b4fc4SDimitry Andric struct Size {
145706b4fc4SDimitry Andric int width;
146706b4fc4SDimitry Andric int height;
Sizecurses::Size147706b4fc4SDimitry Andric Size(int w = 0, int h = 0) : width(w), height(h) {}
148706b4fc4SDimitry Andric
Clearcurses::Size149706b4fc4SDimitry Andric void Clear() {
150706b4fc4SDimitry Andric width = 0;
151706b4fc4SDimitry Andric height = 0;
152706b4fc4SDimitry Andric }
153706b4fc4SDimitry Andric
Dumpcurses::Size154706b4fc4SDimitry Andric void Dump() { printf("(w=%i, h=%i)\n", width, height); }
155706b4fc4SDimitry Andric };
156706b4fc4SDimitry Andric
operator ==(const Size & lhs,const Size & rhs)157706b4fc4SDimitry Andric bool operator==(const Size &lhs, const Size &rhs) {
158706b4fc4SDimitry Andric return lhs.width == rhs.width && lhs.height == rhs.height;
159706b4fc4SDimitry Andric }
160706b4fc4SDimitry Andric
operator !=(const Size & lhs,const Size & rhs)161706b4fc4SDimitry Andric bool operator!=(const Size &lhs, const Size &rhs) {
162706b4fc4SDimitry Andric return lhs.width != rhs.width || lhs.height != rhs.height;
163706b4fc4SDimitry Andric }
164706b4fc4SDimitry Andric
165706b4fc4SDimitry Andric struct Rect {
166706b4fc4SDimitry Andric Point origin;
167706b4fc4SDimitry Andric Size size;
168706b4fc4SDimitry Andric
Rectcurses::Rect169706b4fc4SDimitry Andric Rect() : origin(), size() {}
170706b4fc4SDimitry Andric
Rectcurses::Rect171706b4fc4SDimitry Andric Rect(const Point &p, const Size &s) : origin(p), size(s) {}
172706b4fc4SDimitry Andric
Clearcurses::Rect173706b4fc4SDimitry Andric void Clear() {
174706b4fc4SDimitry Andric origin.Clear();
175706b4fc4SDimitry Andric size.Clear();
176706b4fc4SDimitry Andric }
177706b4fc4SDimitry Andric
Dumpcurses::Rect178706b4fc4SDimitry Andric void Dump() {
179706b4fc4SDimitry Andric printf("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width,
180706b4fc4SDimitry Andric size.height);
181706b4fc4SDimitry Andric }
182706b4fc4SDimitry Andric
Insetcurses::Rect183706b4fc4SDimitry Andric void Inset(int w, int h) {
184706b4fc4SDimitry Andric if (size.width > w * 2)
185706b4fc4SDimitry Andric size.width -= w * 2;
186706b4fc4SDimitry Andric origin.x += w;
187706b4fc4SDimitry Andric
188706b4fc4SDimitry Andric if (size.height > h * 2)
189706b4fc4SDimitry Andric size.height -= h * 2;
190706b4fc4SDimitry Andric origin.y += h;
191706b4fc4SDimitry Andric }
192706b4fc4SDimitry Andric
193706b4fc4SDimitry Andric // Return a status bar rectangle which is the last line of this rectangle.
194706b4fc4SDimitry Andric // This rectangle will be modified to not include the status bar area.
MakeStatusBarcurses::Rect195706b4fc4SDimitry Andric Rect MakeStatusBar() {
196706b4fc4SDimitry Andric Rect status_bar;
197706b4fc4SDimitry Andric if (size.height > 1) {
198706b4fc4SDimitry Andric status_bar.origin.x = origin.x;
199706b4fc4SDimitry Andric status_bar.origin.y = size.height;
200706b4fc4SDimitry Andric status_bar.size.width = size.width;
201706b4fc4SDimitry Andric status_bar.size.height = 1;
202706b4fc4SDimitry Andric --size.height;
203706b4fc4SDimitry Andric }
204706b4fc4SDimitry Andric return status_bar;
205706b4fc4SDimitry Andric }
206706b4fc4SDimitry Andric
207706b4fc4SDimitry Andric // Return a menubar rectangle which is the first line of this rectangle. This
208706b4fc4SDimitry Andric // rectangle will be modified to not include the menubar area.
MakeMenuBarcurses::Rect209706b4fc4SDimitry Andric Rect MakeMenuBar() {
210706b4fc4SDimitry Andric Rect menubar;
211706b4fc4SDimitry Andric if (size.height > 1) {
212706b4fc4SDimitry Andric menubar.origin.x = origin.x;
213706b4fc4SDimitry Andric menubar.origin.y = origin.y;
214706b4fc4SDimitry Andric menubar.size.width = size.width;
215706b4fc4SDimitry Andric menubar.size.height = 1;
216706b4fc4SDimitry Andric ++origin.y;
217706b4fc4SDimitry Andric --size.height;
218706b4fc4SDimitry Andric }
219706b4fc4SDimitry Andric return menubar;
220706b4fc4SDimitry Andric }
221706b4fc4SDimitry Andric
HorizontalSplitPercentagecurses::Rect222706b4fc4SDimitry Andric void HorizontalSplitPercentage(float top_percentage, Rect &top,
223706b4fc4SDimitry Andric Rect &bottom) const {
224706b4fc4SDimitry Andric float top_height = top_percentage * size.height;
225706b4fc4SDimitry Andric HorizontalSplit(top_height, top, bottom);
226706b4fc4SDimitry Andric }
227706b4fc4SDimitry Andric
HorizontalSplitcurses::Rect228706b4fc4SDimitry Andric void HorizontalSplit(int top_height, Rect &top, Rect &bottom) const {
229706b4fc4SDimitry Andric top = *this;
230706b4fc4SDimitry Andric if (top_height < size.height) {
231706b4fc4SDimitry Andric top.size.height = top_height;
232706b4fc4SDimitry Andric bottom.origin.x = origin.x;
233706b4fc4SDimitry Andric bottom.origin.y = origin.y + top.size.height;
234706b4fc4SDimitry Andric bottom.size.width = size.width;
235706b4fc4SDimitry Andric bottom.size.height = size.height - top.size.height;
236706b4fc4SDimitry Andric } else {
237706b4fc4SDimitry Andric bottom.Clear();
238706b4fc4SDimitry Andric }
239706b4fc4SDimitry Andric }
240706b4fc4SDimitry Andric
VerticalSplitPercentagecurses::Rect241706b4fc4SDimitry Andric void VerticalSplitPercentage(float left_percentage, Rect &left,
242706b4fc4SDimitry Andric Rect &right) const {
243706b4fc4SDimitry Andric float left_width = left_percentage * size.width;
244706b4fc4SDimitry Andric VerticalSplit(left_width, left, right);
245706b4fc4SDimitry Andric }
246706b4fc4SDimitry Andric
VerticalSplitcurses::Rect247706b4fc4SDimitry Andric void VerticalSplit(int left_width, Rect &left, Rect &right) const {
248706b4fc4SDimitry Andric left = *this;
249706b4fc4SDimitry Andric if (left_width < size.width) {
250706b4fc4SDimitry Andric left.size.width = left_width;
251706b4fc4SDimitry Andric right.origin.x = origin.x + left.size.width;
252706b4fc4SDimitry Andric right.origin.y = origin.y;
253706b4fc4SDimitry Andric right.size.width = size.width - left.size.width;
254706b4fc4SDimitry Andric right.size.height = size.height;
255706b4fc4SDimitry Andric } else {
256706b4fc4SDimitry Andric right.Clear();
257706b4fc4SDimitry Andric }
258706b4fc4SDimitry Andric }
259706b4fc4SDimitry Andric };
260706b4fc4SDimitry Andric
operator ==(const Rect & lhs,const Rect & rhs)261706b4fc4SDimitry Andric bool operator==(const Rect &lhs, const Rect &rhs) {
262706b4fc4SDimitry Andric return lhs.origin == rhs.origin && lhs.size == rhs.size;
263706b4fc4SDimitry Andric }
264706b4fc4SDimitry Andric
operator !=(const Rect & lhs,const Rect & rhs)265706b4fc4SDimitry Andric bool operator!=(const Rect &lhs, const Rect &rhs) {
266706b4fc4SDimitry Andric return lhs.origin != rhs.origin || lhs.size != rhs.size;
267706b4fc4SDimitry Andric }
268706b4fc4SDimitry Andric
269706b4fc4SDimitry Andric enum HandleCharResult {
270706b4fc4SDimitry Andric eKeyNotHandled = 0,
271706b4fc4SDimitry Andric eKeyHandled = 1,
272706b4fc4SDimitry Andric eQuitApplication = 2
273706b4fc4SDimitry Andric };
274706b4fc4SDimitry Andric
275706b4fc4SDimitry Andric enum class MenuActionResult {
276706b4fc4SDimitry Andric Handled,
277706b4fc4SDimitry Andric NotHandled,
278706b4fc4SDimitry Andric Quit // Exit all menus and quit
279706b4fc4SDimitry Andric };
280706b4fc4SDimitry Andric
281706b4fc4SDimitry Andric struct KeyHelp {
282706b4fc4SDimitry Andric int ch;
283706b4fc4SDimitry Andric const char *description;
284706b4fc4SDimitry Andric };
285706b4fc4SDimitry Andric
286b60736ecSDimitry Andric // COLOR_PAIR index names
287b60736ecSDimitry Andric enum {
288b60736ecSDimitry Andric // First 16 colors are 8 black background and 8 blue background colors,
289b60736ecSDimitry Andric // needed by OutputColoredStringTruncated().
290b60736ecSDimitry Andric BlackOnBlack = 1,
291b60736ecSDimitry Andric RedOnBlack,
292b60736ecSDimitry Andric GreenOnBlack,
293b60736ecSDimitry Andric YellowOnBlack,
294b60736ecSDimitry Andric BlueOnBlack,
295b60736ecSDimitry Andric MagentaOnBlack,
296b60736ecSDimitry Andric CyanOnBlack,
297b60736ecSDimitry Andric WhiteOnBlack,
298b60736ecSDimitry Andric BlackOnBlue,
299b60736ecSDimitry Andric RedOnBlue,
300b60736ecSDimitry Andric GreenOnBlue,
301b60736ecSDimitry Andric YellowOnBlue,
302b60736ecSDimitry Andric BlueOnBlue,
303b60736ecSDimitry Andric MagentaOnBlue,
304b60736ecSDimitry Andric CyanOnBlue,
305b60736ecSDimitry Andric WhiteOnBlue,
306b60736ecSDimitry Andric // Other colors, as needed.
307b60736ecSDimitry Andric BlackOnWhite,
308b60736ecSDimitry Andric MagentaOnWhite,
309b60736ecSDimitry Andric LastColorPairIndex = MagentaOnWhite
310b60736ecSDimitry Andric };
311b60736ecSDimitry Andric
312706b4fc4SDimitry Andric class WindowDelegate {
313706b4fc4SDimitry Andric public:
314706b4fc4SDimitry Andric virtual ~WindowDelegate() = default;
315706b4fc4SDimitry Andric
WindowDelegateDraw(Window & window,bool force)316706b4fc4SDimitry Andric virtual bool WindowDelegateDraw(Window &window, bool force) {
317706b4fc4SDimitry Andric return false; // Drawing not handled
318706b4fc4SDimitry Andric }
319706b4fc4SDimitry Andric
WindowDelegateHandleChar(Window & window,int key)320706b4fc4SDimitry Andric virtual HandleCharResult WindowDelegateHandleChar(Window &window, int key) {
321706b4fc4SDimitry Andric return eKeyNotHandled;
322706b4fc4SDimitry Andric }
323706b4fc4SDimitry Andric
WindowDelegateGetHelpText()324706b4fc4SDimitry Andric virtual const char *WindowDelegateGetHelpText() { return nullptr; }
325706b4fc4SDimitry Andric
WindowDelegateGetKeyHelp()326706b4fc4SDimitry Andric virtual KeyHelp *WindowDelegateGetKeyHelp() { return nullptr; }
327706b4fc4SDimitry Andric };
328706b4fc4SDimitry Andric
329706b4fc4SDimitry Andric class HelpDialogDelegate : public WindowDelegate {
330706b4fc4SDimitry Andric public:
331706b4fc4SDimitry Andric HelpDialogDelegate(const char *text, KeyHelp *key_help_array);
332706b4fc4SDimitry Andric
333706b4fc4SDimitry Andric ~HelpDialogDelegate() override;
334706b4fc4SDimitry Andric
335706b4fc4SDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override;
336706b4fc4SDimitry Andric
337706b4fc4SDimitry Andric HandleCharResult WindowDelegateHandleChar(Window &window, int key) override;
338706b4fc4SDimitry Andric
GetNumLines() const339706b4fc4SDimitry Andric size_t GetNumLines() const { return m_text.GetSize(); }
340706b4fc4SDimitry Andric
GetMaxLineLength() const341706b4fc4SDimitry Andric size_t GetMaxLineLength() const { return m_text.GetMaxStringLength(); }
342706b4fc4SDimitry Andric
343706b4fc4SDimitry Andric protected:
344706b4fc4SDimitry Andric StringList m_text;
345145449b1SDimitry Andric int m_first_visible_line = 0;
346706b4fc4SDimitry Andric };
347706b4fc4SDimitry Andric
348344a3780SDimitry Andric // A surface is an abstraction for something than can be drawn on. The surface
349344a3780SDimitry Andric // have a width, a height, a cursor position, and a multitude of drawing
350344a3780SDimitry Andric // operations. This type should be sub-classed to get an actually useful ncurses
351c0981da4SDimitry Andric // object, such as a Window or a Pad.
352344a3780SDimitry Andric class Surface {
353706b4fc4SDimitry Andric public:
354c0981da4SDimitry Andric enum class Type { Window, Pad };
355c0981da4SDimitry Andric
Surface(Surface::Type type)356145449b1SDimitry Andric Surface(Surface::Type type) : m_type(type) {}
357706b4fc4SDimitry Andric
get()358344a3780SDimitry Andric WINDOW *get() { return m_window; }
359344a3780SDimitry Andric
operator WINDOW*()360344a3780SDimitry Andric operator WINDOW *() { return m_window; }
361344a3780SDimitry Andric
SubSurface(Rect bounds)362c0981da4SDimitry Andric Surface SubSurface(Rect bounds) {
363c0981da4SDimitry Andric Surface subSurface(m_type);
364c0981da4SDimitry Andric if (m_type == Type::Pad)
365c0981da4SDimitry Andric subSurface.m_window =
366c0981da4SDimitry Andric ::subpad(m_window, bounds.size.height, bounds.size.width,
367c0981da4SDimitry Andric bounds.origin.y, bounds.origin.x);
368c0981da4SDimitry Andric else
369c0981da4SDimitry Andric subSurface.m_window =
370c0981da4SDimitry Andric ::derwin(m_window, bounds.size.height, bounds.size.width,
371c0981da4SDimitry Andric bounds.origin.y, bounds.origin.x);
372c0981da4SDimitry Andric return subSurface;
373c0981da4SDimitry Andric }
374c0981da4SDimitry Andric
375344a3780SDimitry Andric // Copy a region of the surface to another surface.
CopyToSurface(Surface & target,Point source_origin,Point target_origin,Size size)376344a3780SDimitry Andric void CopyToSurface(Surface &target, Point source_origin, Point target_origin,
377344a3780SDimitry Andric Size size) {
378344a3780SDimitry Andric ::copywin(m_window, target.get(), source_origin.y, source_origin.x,
379344a3780SDimitry Andric target_origin.y, target_origin.x,
380344a3780SDimitry Andric target_origin.y + size.height - 1,
381344a3780SDimitry Andric target_origin.x + size.width - 1, false);
382706b4fc4SDimitry Andric }
383706b4fc4SDimitry Andric
GetCursorX() const384344a3780SDimitry Andric int GetCursorX() const { return getcurx(m_window); }
GetCursorY() const385344a3780SDimitry Andric int GetCursorY() const { return getcury(m_window); }
MoveCursor(int x,int y)386344a3780SDimitry Andric void MoveCursor(int x, int y) { ::wmove(m_window, y, x); }
387706b4fc4SDimitry Andric
AttributeOn(attr_t attr)388706b4fc4SDimitry Andric void AttributeOn(attr_t attr) { ::wattron(m_window, attr); }
AttributeOff(attr_t attr)389706b4fc4SDimitry Andric void AttributeOff(attr_t attr) { ::wattroff(m_window, attr); }
390344a3780SDimitry Andric
GetMaxX() const391b60736ecSDimitry Andric int GetMaxX() const { return getmaxx(m_window); }
GetMaxY() const392b60736ecSDimitry Andric int GetMaxY() const { return getmaxy(m_window); }
GetWidth() const393b60736ecSDimitry Andric int GetWidth() const { return GetMaxX(); }
GetHeight() const394b60736ecSDimitry Andric int GetHeight() const { return GetMaxY(); }
GetSize() const395344a3780SDimitry Andric Size GetSize() const { return Size(GetWidth(), GetHeight()); }
396344a3780SDimitry Andric // Get a zero origin rectangle width the surface size.
GetFrame() const397344a3780SDimitry Andric Rect GetFrame() const { return Rect(Point(), GetSize()); }
398344a3780SDimitry Andric
Clear()399344a3780SDimitry Andric void Clear() { ::wclear(m_window); }
Erase()400344a3780SDimitry Andric void Erase() { ::werase(m_window); }
401344a3780SDimitry Andric
SetBackground(int color_pair_idx)402706b4fc4SDimitry Andric void SetBackground(int color_pair_idx) {
403706b4fc4SDimitry Andric ::wbkgd(m_window, COLOR_PAIR(color_pair_idx));
404706b4fc4SDimitry Andric }
405706b4fc4SDimitry Andric
PutChar(int ch)406344a3780SDimitry Andric void PutChar(int ch) { ::waddch(m_window, ch); }
PutCString(const char * s,int len=-1)407344a3780SDimitry Andric void PutCString(const char *s, int len = -1) { ::waddnstr(m_window, s, len); }
408344a3780SDimitry Andric
PutCStringTruncated(int right_pad,const char * s,int len=-1)409b60736ecSDimitry Andric void PutCStringTruncated(int right_pad, const char *s, int len = -1) {
410706b4fc4SDimitry Andric int bytes_left = GetWidth() - GetCursorX();
411706b4fc4SDimitry Andric if (bytes_left > right_pad) {
412706b4fc4SDimitry Andric bytes_left -= right_pad;
413b60736ecSDimitry Andric ::waddnstr(m_window, s, len < 0 ? bytes_left : std::min(bytes_left, len));
414706b4fc4SDimitry Andric }
415706b4fc4SDimitry Andric }
416706b4fc4SDimitry Andric
Printf(const char * format,...)417706b4fc4SDimitry Andric void Printf(const char *format, ...) __attribute__((format(printf, 2, 3))) {
418706b4fc4SDimitry Andric va_list args;
419706b4fc4SDimitry Andric va_start(args, format);
420344a3780SDimitry Andric vw_printw(m_window, format, args);
421706b4fc4SDimitry Andric va_end(args);
422706b4fc4SDimitry Andric }
423706b4fc4SDimitry Andric
PrintfTruncated(int right_pad,const char * format,...)424b60736ecSDimitry Andric void PrintfTruncated(int right_pad, const char *format, ...)
425b60736ecSDimitry Andric __attribute__((format(printf, 3, 4))) {
426b60736ecSDimitry Andric va_list args;
427b60736ecSDimitry Andric va_start(args, format);
428b60736ecSDimitry Andric StreamString strm;
429b60736ecSDimitry Andric strm.PrintfVarArg(format, args);
430b60736ecSDimitry Andric va_end(args);
431b60736ecSDimitry Andric PutCStringTruncated(right_pad, strm.GetData());
432b60736ecSDimitry Andric }
433b60736ecSDimitry Andric
VerticalLine(int n,chtype v_char=ACS_VLINE)434344a3780SDimitry Andric void VerticalLine(int n, chtype v_char = ACS_VLINE) {
435344a3780SDimitry Andric ::wvline(m_window, v_char, n);
436344a3780SDimitry Andric }
HorizontalLine(int n,chtype h_char=ACS_HLINE)437344a3780SDimitry Andric void HorizontalLine(int n, chtype h_char = ACS_HLINE) {
438344a3780SDimitry Andric ::whline(m_window, h_char, n);
439344a3780SDimitry Andric }
Box(chtype v_char=ACS_VLINE,chtype h_char=ACS_HLINE)440344a3780SDimitry Andric void Box(chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) {
441344a3780SDimitry Andric ::box(m_window, v_char, h_char);
442344a3780SDimitry Andric }
443344a3780SDimitry Andric
TitledBox(const char * title,chtype v_char=ACS_VLINE,chtype h_char=ACS_HLINE)444344a3780SDimitry Andric void TitledBox(const char *title, chtype v_char = ACS_VLINE,
445344a3780SDimitry Andric chtype h_char = ACS_HLINE) {
446344a3780SDimitry Andric Box(v_char, h_char);
447344a3780SDimitry Andric int title_offset = 2;
448344a3780SDimitry Andric MoveCursor(title_offset, 0);
449344a3780SDimitry Andric PutChar('[');
450344a3780SDimitry Andric PutCString(title, GetWidth() - title_offset);
451344a3780SDimitry Andric PutChar(']');
452344a3780SDimitry Andric }
453344a3780SDimitry Andric
Box(const Rect & bounds,chtype v_char=ACS_VLINE,chtype h_char=ACS_HLINE)454344a3780SDimitry Andric void Box(const Rect &bounds, chtype v_char = ACS_VLINE,
455344a3780SDimitry Andric chtype h_char = ACS_HLINE) {
456344a3780SDimitry Andric MoveCursor(bounds.origin.x, bounds.origin.y);
457344a3780SDimitry Andric VerticalLine(bounds.size.height);
458344a3780SDimitry Andric HorizontalLine(bounds.size.width);
459344a3780SDimitry Andric PutChar(ACS_ULCORNER);
460344a3780SDimitry Andric
461344a3780SDimitry Andric MoveCursor(bounds.origin.x + bounds.size.width - 1, bounds.origin.y);
462344a3780SDimitry Andric VerticalLine(bounds.size.height);
463344a3780SDimitry Andric PutChar(ACS_URCORNER);
464344a3780SDimitry Andric
465344a3780SDimitry Andric MoveCursor(bounds.origin.x, bounds.origin.y + bounds.size.height - 1);
466344a3780SDimitry Andric HorizontalLine(bounds.size.width);
467344a3780SDimitry Andric PutChar(ACS_LLCORNER);
468344a3780SDimitry Andric
469344a3780SDimitry Andric MoveCursor(bounds.origin.x + bounds.size.width - 1,
470344a3780SDimitry Andric bounds.origin.y + bounds.size.height - 1);
471344a3780SDimitry Andric PutChar(ACS_LRCORNER);
472344a3780SDimitry Andric }
473344a3780SDimitry Andric
TitledBox(const Rect & bounds,const char * title,chtype v_char=ACS_VLINE,chtype h_char=ACS_HLINE)474344a3780SDimitry Andric void TitledBox(const Rect &bounds, const char *title,
475344a3780SDimitry Andric chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) {
476344a3780SDimitry Andric Box(bounds, v_char, h_char);
477344a3780SDimitry Andric int title_offset = 2;
478344a3780SDimitry Andric MoveCursor(bounds.origin.x + title_offset, bounds.origin.y);
479344a3780SDimitry Andric PutChar('[');
480344a3780SDimitry Andric PutCString(title, bounds.size.width - title_offset);
481344a3780SDimitry Andric PutChar(']');
482b60736ecSDimitry Andric }
483b60736ecSDimitry Andric
484b60736ecSDimitry Andric // Curses doesn't allow direct output of color escape sequences, but that's
485b60736ecSDimitry Andric // how we get source lines from the Highligher class. Read the line and
486b60736ecSDimitry Andric // convert color escape sequences to curses color attributes. Use
487b60736ecSDimitry Andric // first_skip_count to skip leading visible characters. Returns false if all
488b60736ecSDimitry Andric // visible characters were skipped due to first_skip_count.
OutputColoredStringTruncated(int right_pad,StringRef string,size_t skip_first_count,bool use_blue_background)489b60736ecSDimitry Andric bool OutputColoredStringTruncated(int right_pad, StringRef string,
490b60736ecSDimitry Andric size_t skip_first_count,
491b60736ecSDimitry Andric bool use_blue_background) {
492b60736ecSDimitry Andric attr_t saved_attr;
493b60736ecSDimitry Andric short saved_pair;
494b60736ecSDimitry Andric bool result = false;
495b60736ecSDimitry Andric wattr_get(m_window, &saved_attr, &saved_pair, nullptr);
496b60736ecSDimitry Andric if (use_blue_background)
497b60736ecSDimitry Andric ::wattron(m_window, COLOR_PAIR(WhiteOnBlue));
498b60736ecSDimitry Andric while (!string.empty()) {
499145449b1SDimitry Andric size_t esc_pos = string.find(ANSI_ESC_START);
500b60736ecSDimitry Andric if (esc_pos == StringRef::npos) {
501b60736ecSDimitry Andric string = string.substr(skip_first_count);
502b60736ecSDimitry Andric if (!string.empty()) {
503b60736ecSDimitry Andric PutCStringTruncated(right_pad, string.data(), string.size());
504b60736ecSDimitry Andric result = true;
505b60736ecSDimitry Andric }
506b60736ecSDimitry Andric break;
507b60736ecSDimitry Andric }
508b60736ecSDimitry Andric if (esc_pos > 0) {
509b60736ecSDimitry Andric if (skip_first_count > 0) {
510b60736ecSDimitry Andric int skip = std::min(esc_pos, skip_first_count);
511b60736ecSDimitry Andric string = string.substr(skip);
512b60736ecSDimitry Andric skip_first_count -= skip;
513b60736ecSDimitry Andric esc_pos -= skip;
514b60736ecSDimitry Andric }
515b60736ecSDimitry Andric if (esc_pos > 0) {
516b60736ecSDimitry Andric PutCStringTruncated(right_pad, string.data(), esc_pos);
517b60736ecSDimitry Andric result = true;
518b60736ecSDimitry Andric string = string.drop_front(esc_pos);
519b60736ecSDimitry Andric }
520b60736ecSDimitry Andric }
521145449b1SDimitry Andric bool consumed = string.consume_front(ANSI_ESC_START);
522b60736ecSDimitry Andric assert(consumed);
523b60736ecSDimitry Andric UNUSED_IF_ASSERT_DISABLED(consumed);
524b60736ecSDimitry Andric // This is written to match our Highlighter classes, which seem to
525b60736ecSDimitry Andric // generate only foreground color escape sequences. If necessary, this
526b60736ecSDimitry Andric // will need to be extended.
527145449b1SDimitry Andric // Only 8 basic foreground colors, underline and reset, our Highlighter
528145449b1SDimitry Andric // doesn't use anything else.
529b60736ecSDimitry Andric int value;
530b60736ecSDimitry Andric if (!!string.consumeInteger(10, value) || // Returns false on success.
531145449b1SDimitry Andric !(value == 0 || value == ANSI_CTRL_UNDERLINE ||
532145449b1SDimitry Andric (value >= ANSI_FG_COLOR_BLACK && value <= ANSI_FG_COLOR_WHITE))) {
533b60736ecSDimitry Andric llvm::errs() << "No valid color code in color escape sequence.\n";
534b60736ecSDimitry Andric continue;
535b60736ecSDimitry Andric }
536145449b1SDimitry Andric if (!string.consume_front(ANSI_ESC_END)) {
537145449b1SDimitry Andric llvm::errs() << "Missing '" << ANSI_ESC_END
538145449b1SDimitry Andric << "' in color escape sequence.\n";
539b60736ecSDimitry Andric continue;
540b60736ecSDimitry Andric }
541b60736ecSDimitry Andric if (value == 0) { // Reset.
542b60736ecSDimitry Andric wattr_set(m_window, saved_attr, saved_pair, nullptr);
543b60736ecSDimitry Andric if (use_blue_background)
544b60736ecSDimitry Andric ::wattron(m_window, COLOR_PAIR(WhiteOnBlue));
545145449b1SDimitry Andric } else if (value == ANSI_CTRL_UNDERLINE) {
546145449b1SDimitry Andric ::wattron(m_window, A_UNDERLINE);
547b60736ecSDimitry Andric } else {
548b60736ecSDimitry Andric // Mapped directly to first 16 color pairs (black/blue background).
549145449b1SDimitry Andric ::wattron(m_window, COLOR_PAIR(value - ANSI_FG_COLOR_BLACK + 1 +
550145449b1SDimitry Andric (use_blue_background ? 8 : 0)));
551b60736ecSDimitry Andric }
552b60736ecSDimitry Andric }
553b60736ecSDimitry Andric wattr_set(m_window, saved_attr, saved_pair, nullptr);
554b60736ecSDimitry Andric return result;
555b60736ecSDimitry Andric }
556b60736ecSDimitry Andric
557344a3780SDimitry Andric protected:
558c0981da4SDimitry Andric Type m_type;
559145449b1SDimitry Andric WINDOW *m_window = nullptr;
560344a3780SDimitry Andric };
561344a3780SDimitry Andric
562344a3780SDimitry Andric class Pad : public Surface {
563344a3780SDimitry Andric public:
Pad(Size size)564c0981da4SDimitry Andric Pad(Size size) : Surface(Surface::Type::Pad) {
565c0981da4SDimitry Andric m_window = ::newpad(size.height, size.width);
566c0981da4SDimitry Andric }
567344a3780SDimitry Andric
~Pad()568344a3780SDimitry Andric ~Pad() { ::delwin(m_window); }
569344a3780SDimitry Andric };
570344a3780SDimitry Andric
571344a3780SDimitry Andric class Window : public Surface {
572344a3780SDimitry Andric public:
Window(const char * name)573344a3780SDimitry Andric Window(const char *name)
574c0981da4SDimitry Andric : Surface(Surface::Type::Window), m_name(name), m_panel(nullptr),
575c0981da4SDimitry Andric m_parent(nullptr), m_subwindows(), m_delegate_sp(),
576c0981da4SDimitry Andric m_curr_active_window_idx(UINT32_MAX),
577344a3780SDimitry Andric m_prev_active_window_idx(UINT32_MAX), m_delete(false),
578344a3780SDimitry Andric m_needs_update(true), m_can_activate(true), m_is_subwin(false) {}
579344a3780SDimitry Andric
Window(const char * name,WINDOW * w,bool del=true)580344a3780SDimitry Andric Window(const char *name, WINDOW *w, bool del = true)
581c0981da4SDimitry Andric : Surface(Surface::Type::Window), m_name(name), m_panel(nullptr),
582c0981da4SDimitry Andric m_parent(nullptr), m_subwindows(), m_delegate_sp(),
583c0981da4SDimitry Andric m_curr_active_window_idx(UINT32_MAX),
584344a3780SDimitry Andric m_prev_active_window_idx(UINT32_MAX), m_delete(del),
585344a3780SDimitry Andric m_needs_update(true), m_can_activate(true), m_is_subwin(false) {
586344a3780SDimitry Andric if (w)
587344a3780SDimitry Andric Reset(w);
588344a3780SDimitry Andric }
589344a3780SDimitry Andric
Window(const char * name,const Rect & bounds)590344a3780SDimitry Andric Window(const char *name, const Rect &bounds)
591e3b55780SDimitry Andric : Surface(Surface::Type::Window), m_name(name), m_panel(nullptr),
592e3b55780SDimitry Andric m_parent(nullptr), m_subwindows(), m_delegate_sp(),
593e3b55780SDimitry Andric m_curr_active_window_idx(UINT32_MAX),
594e3b55780SDimitry Andric m_prev_active_window_idx(UINT32_MAX), m_delete(false),
595344a3780SDimitry Andric m_needs_update(true), m_can_activate(true), m_is_subwin(false) {
596344a3780SDimitry Andric Reset(::newwin(bounds.size.height, bounds.size.width, bounds.origin.y,
597344a3780SDimitry Andric bounds.origin.y));
598344a3780SDimitry Andric }
599344a3780SDimitry Andric
~Window()600344a3780SDimitry Andric virtual ~Window() {
601344a3780SDimitry Andric RemoveSubWindows();
602344a3780SDimitry Andric Reset();
603344a3780SDimitry Andric }
604344a3780SDimitry Andric
Reset(WINDOW * w=nullptr,bool del=true)605344a3780SDimitry Andric void Reset(WINDOW *w = nullptr, bool del = true) {
606344a3780SDimitry Andric if (m_window == w)
607344a3780SDimitry Andric return;
608344a3780SDimitry Andric
609344a3780SDimitry Andric if (m_panel) {
610344a3780SDimitry Andric ::del_panel(m_panel);
611344a3780SDimitry Andric m_panel = nullptr;
612344a3780SDimitry Andric }
613344a3780SDimitry Andric if (m_window && m_delete) {
614344a3780SDimitry Andric ::delwin(m_window);
615344a3780SDimitry Andric m_window = nullptr;
616344a3780SDimitry Andric m_delete = false;
617344a3780SDimitry Andric }
618344a3780SDimitry Andric if (w) {
619344a3780SDimitry Andric m_window = w;
620344a3780SDimitry Andric m_panel = ::new_panel(m_window);
621344a3780SDimitry Andric m_delete = del;
622344a3780SDimitry Andric }
623344a3780SDimitry Andric }
624344a3780SDimitry Andric
625344a3780SDimitry Andric // Get the rectangle in our parent window
GetBounds() const626344a3780SDimitry Andric Rect GetBounds() const { return Rect(GetParentOrigin(), GetSize()); }
627344a3780SDimitry Andric
GetCenteredRect(int width,int height)628344a3780SDimitry Andric Rect GetCenteredRect(int width, int height) {
629344a3780SDimitry Andric Size size = GetSize();
630344a3780SDimitry Andric width = std::min(size.width, width);
631344a3780SDimitry Andric height = std::min(size.height, height);
632344a3780SDimitry Andric int x = (size.width - width) / 2;
633344a3780SDimitry Andric int y = (size.height - height) / 2;
634344a3780SDimitry Andric return Rect(Point(x, y), Size(width, height));
635344a3780SDimitry Andric }
636344a3780SDimitry Andric
GetChar()637344a3780SDimitry Andric int GetChar() { return ::wgetch(m_window); }
GetParentOrigin() const638344a3780SDimitry Andric Point GetParentOrigin() const { return Point(GetParentX(), GetParentY()); }
GetParentX() const639344a3780SDimitry Andric int GetParentX() const { return getparx(m_window); }
GetParentY() const640344a3780SDimitry Andric int GetParentY() const { return getpary(m_window); }
MoveWindow(int x,int y)641344a3780SDimitry Andric void MoveWindow(int x, int y) { MoveWindow(Point(x, y)); }
Resize(int w,int h)642344a3780SDimitry Andric void Resize(int w, int h) { ::wresize(m_window, h, w); }
Resize(const Size & size)643344a3780SDimitry Andric void Resize(const Size &size) {
644344a3780SDimitry Andric ::wresize(m_window, size.height, size.width);
645344a3780SDimitry Andric }
MoveWindow(const Point & origin)646344a3780SDimitry Andric void MoveWindow(const Point &origin) {
647344a3780SDimitry Andric const bool moving_window = origin != GetParentOrigin();
648344a3780SDimitry Andric if (m_is_subwin && moving_window) {
649344a3780SDimitry Andric // Can't move subwindows, must delete and re-create
650344a3780SDimitry Andric Size size = GetSize();
651344a3780SDimitry Andric Reset(::subwin(m_parent->m_window, size.height, size.width, origin.y,
652344a3780SDimitry Andric origin.x),
653344a3780SDimitry Andric true);
654344a3780SDimitry Andric } else {
655344a3780SDimitry Andric ::mvwin(m_window, origin.y, origin.x);
656344a3780SDimitry Andric }
657344a3780SDimitry Andric }
658344a3780SDimitry Andric
SetBounds(const Rect & bounds)659344a3780SDimitry Andric void SetBounds(const Rect &bounds) {
660344a3780SDimitry Andric const bool moving_window = bounds.origin != GetParentOrigin();
661344a3780SDimitry Andric if (m_is_subwin && moving_window) {
662344a3780SDimitry Andric // Can't move subwindows, must delete and re-create
663344a3780SDimitry Andric Reset(::subwin(m_parent->m_window, bounds.size.height, bounds.size.width,
664344a3780SDimitry Andric bounds.origin.y, bounds.origin.x),
665344a3780SDimitry Andric true);
666344a3780SDimitry Andric } else {
667344a3780SDimitry Andric if (moving_window)
668344a3780SDimitry Andric MoveWindow(bounds.origin);
669344a3780SDimitry Andric Resize(bounds.size);
670344a3780SDimitry Andric }
671344a3780SDimitry Andric }
672344a3780SDimitry Andric
Touch()673706b4fc4SDimitry Andric void Touch() {
674706b4fc4SDimitry Andric ::touchwin(m_window);
675706b4fc4SDimitry Andric if (m_parent)
676706b4fc4SDimitry Andric m_parent->Touch();
677706b4fc4SDimitry Andric }
678706b4fc4SDimitry Andric
CreateSubWindow(const char * name,const Rect & bounds,bool make_active)679706b4fc4SDimitry Andric WindowSP CreateSubWindow(const char *name, const Rect &bounds,
680706b4fc4SDimitry Andric bool make_active) {
681706b4fc4SDimitry Andric auto get_window = [this, &bounds]() {
682706b4fc4SDimitry Andric return m_window
683706b4fc4SDimitry Andric ? ::subwin(m_window, bounds.size.height, bounds.size.width,
684706b4fc4SDimitry Andric bounds.origin.y, bounds.origin.x)
685706b4fc4SDimitry Andric : ::newwin(bounds.size.height, bounds.size.width,
686706b4fc4SDimitry Andric bounds.origin.y, bounds.origin.x);
687706b4fc4SDimitry Andric };
688706b4fc4SDimitry Andric WindowSP subwindow_sp = std::make_shared<Window>(name, get_window(), true);
689706b4fc4SDimitry Andric subwindow_sp->m_is_subwin = subwindow_sp.operator bool();
690706b4fc4SDimitry Andric subwindow_sp->m_parent = this;
691706b4fc4SDimitry Andric if (make_active) {
692706b4fc4SDimitry Andric m_prev_active_window_idx = m_curr_active_window_idx;
693706b4fc4SDimitry Andric m_curr_active_window_idx = m_subwindows.size();
694706b4fc4SDimitry Andric }
695706b4fc4SDimitry Andric m_subwindows.push_back(subwindow_sp);
696706b4fc4SDimitry Andric ::top_panel(subwindow_sp->m_panel);
697706b4fc4SDimitry Andric m_needs_update = true;
698706b4fc4SDimitry Andric return subwindow_sp;
699706b4fc4SDimitry Andric }
700706b4fc4SDimitry Andric
RemoveSubWindow(Window * window)701706b4fc4SDimitry Andric bool RemoveSubWindow(Window *window) {
702706b4fc4SDimitry Andric Windows::iterator pos, end = m_subwindows.end();
703706b4fc4SDimitry Andric size_t i = 0;
704706b4fc4SDimitry Andric for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) {
705706b4fc4SDimitry Andric if ((*pos).get() == window) {
706706b4fc4SDimitry Andric if (m_prev_active_window_idx == i)
707706b4fc4SDimitry Andric m_prev_active_window_idx = UINT32_MAX;
708706b4fc4SDimitry Andric else if (m_prev_active_window_idx != UINT32_MAX &&
709706b4fc4SDimitry Andric m_prev_active_window_idx > i)
710706b4fc4SDimitry Andric --m_prev_active_window_idx;
711706b4fc4SDimitry Andric
712706b4fc4SDimitry Andric if (m_curr_active_window_idx == i)
713706b4fc4SDimitry Andric m_curr_active_window_idx = UINT32_MAX;
714706b4fc4SDimitry Andric else if (m_curr_active_window_idx != UINT32_MAX &&
715706b4fc4SDimitry Andric m_curr_active_window_idx > i)
716706b4fc4SDimitry Andric --m_curr_active_window_idx;
717706b4fc4SDimitry Andric window->Erase();
718706b4fc4SDimitry Andric m_subwindows.erase(pos);
719706b4fc4SDimitry Andric m_needs_update = true;
720706b4fc4SDimitry Andric if (m_parent)
721706b4fc4SDimitry Andric m_parent->Touch();
722706b4fc4SDimitry Andric else
723706b4fc4SDimitry Andric ::touchwin(stdscr);
724706b4fc4SDimitry Andric return true;
725706b4fc4SDimitry Andric }
726706b4fc4SDimitry Andric }
727706b4fc4SDimitry Andric return false;
728706b4fc4SDimitry Andric }
729706b4fc4SDimitry Andric
FindSubWindow(const char * name)730706b4fc4SDimitry Andric WindowSP FindSubWindow(const char *name) {
731706b4fc4SDimitry Andric Windows::iterator pos, end = m_subwindows.end();
732706b4fc4SDimitry Andric size_t i = 0;
733706b4fc4SDimitry Andric for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) {
734706b4fc4SDimitry Andric if ((*pos)->m_name == name)
735706b4fc4SDimitry Andric return *pos;
736706b4fc4SDimitry Andric }
737706b4fc4SDimitry Andric return WindowSP();
738706b4fc4SDimitry Andric }
739706b4fc4SDimitry Andric
RemoveSubWindows()740706b4fc4SDimitry Andric void RemoveSubWindows() {
741706b4fc4SDimitry Andric m_curr_active_window_idx = UINT32_MAX;
742706b4fc4SDimitry Andric m_prev_active_window_idx = UINT32_MAX;
743706b4fc4SDimitry Andric for (Windows::iterator pos = m_subwindows.begin();
744706b4fc4SDimitry Andric pos != m_subwindows.end(); pos = m_subwindows.erase(pos)) {
745706b4fc4SDimitry Andric (*pos)->Erase();
746706b4fc4SDimitry Andric }
747706b4fc4SDimitry Andric if (m_parent)
748706b4fc4SDimitry Andric m_parent->Touch();
749706b4fc4SDimitry Andric else
750706b4fc4SDimitry Andric ::touchwin(stdscr);
751706b4fc4SDimitry Andric }
752706b4fc4SDimitry Andric
753706b4fc4SDimitry Andric // Window drawing utilities
DrawTitleBox(const char * title,const char * bottom_message=nullptr)754706b4fc4SDimitry Andric void DrawTitleBox(const char *title, const char *bottom_message = nullptr) {
755706b4fc4SDimitry Andric attr_t attr = 0;
756706b4fc4SDimitry Andric if (IsActive())
757b60736ecSDimitry Andric attr = A_BOLD | COLOR_PAIR(BlackOnWhite);
758706b4fc4SDimitry Andric else
759706b4fc4SDimitry Andric attr = 0;
760706b4fc4SDimitry Andric if (attr)
761706b4fc4SDimitry Andric AttributeOn(attr);
762706b4fc4SDimitry Andric
763706b4fc4SDimitry Andric Box();
764706b4fc4SDimitry Andric MoveCursor(3, 0);
765706b4fc4SDimitry Andric
766706b4fc4SDimitry Andric if (title && title[0]) {
767706b4fc4SDimitry Andric PutChar('<');
768706b4fc4SDimitry Andric PutCString(title);
769706b4fc4SDimitry Andric PutChar('>');
770706b4fc4SDimitry Andric }
771706b4fc4SDimitry Andric
772706b4fc4SDimitry Andric if (bottom_message && bottom_message[0]) {
773706b4fc4SDimitry Andric int bottom_message_length = strlen(bottom_message);
774706b4fc4SDimitry Andric int x = GetWidth() - 3 - (bottom_message_length + 2);
775706b4fc4SDimitry Andric
776706b4fc4SDimitry Andric if (x > 0) {
777706b4fc4SDimitry Andric MoveCursor(x, GetHeight() - 1);
778706b4fc4SDimitry Andric PutChar('[');
779706b4fc4SDimitry Andric PutCString(bottom_message);
780706b4fc4SDimitry Andric PutChar(']');
781706b4fc4SDimitry Andric } else {
782706b4fc4SDimitry Andric MoveCursor(1, GetHeight() - 1);
783706b4fc4SDimitry Andric PutChar('[');
784b60736ecSDimitry Andric PutCStringTruncated(1, bottom_message);
785706b4fc4SDimitry Andric }
786706b4fc4SDimitry Andric }
787706b4fc4SDimitry Andric if (attr)
788706b4fc4SDimitry Andric AttributeOff(attr);
789706b4fc4SDimitry Andric }
790706b4fc4SDimitry Andric
Draw(bool force)791706b4fc4SDimitry Andric virtual void Draw(bool force) {
792706b4fc4SDimitry Andric if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw(*this, force))
793706b4fc4SDimitry Andric return;
794706b4fc4SDimitry Andric
795706b4fc4SDimitry Andric for (auto &subwindow_sp : m_subwindows)
796706b4fc4SDimitry Andric subwindow_sp->Draw(force);
797706b4fc4SDimitry Andric }
798706b4fc4SDimitry Andric
CreateHelpSubwindow()799706b4fc4SDimitry Andric bool CreateHelpSubwindow() {
800706b4fc4SDimitry Andric if (m_delegate_sp) {
801706b4fc4SDimitry Andric const char *text = m_delegate_sp->WindowDelegateGetHelpText();
802706b4fc4SDimitry Andric KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp();
803706b4fc4SDimitry Andric if ((text && text[0]) || key_help) {
804706b4fc4SDimitry Andric std::unique_ptr<HelpDialogDelegate> help_delegate_up(
805706b4fc4SDimitry Andric new HelpDialogDelegate(text, key_help));
806706b4fc4SDimitry Andric const size_t num_lines = help_delegate_up->GetNumLines();
807706b4fc4SDimitry Andric const size_t max_length = help_delegate_up->GetMaxLineLength();
808706b4fc4SDimitry Andric Rect bounds = GetBounds();
809706b4fc4SDimitry Andric bounds.Inset(1, 1);
810706b4fc4SDimitry Andric if (max_length + 4 < static_cast<size_t>(bounds.size.width)) {
811706b4fc4SDimitry Andric bounds.origin.x += (bounds.size.width - max_length + 4) / 2;
812706b4fc4SDimitry Andric bounds.size.width = max_length + 4;
813706b4fc4SDimitry Andric } else {
814706b4fc4SDimitry Andric if (bounds.size.width > 100) {
815706b4fc4SDimitry Andric const int inset_w = bounds.size.width / 4;
816706b4fc4SDimitry Andric bounds.origin.x += inset_w;
817706b4fc4SDimitry Andric bounds.size.width -= 2 * inset_w;
818706b4fc4SDimitry Andric }
819706b4fc4SDimitry Andric }
820706b4fc4SDimitry Andric
821706b4fc4SDimitry Andric if (num_lines + 2 < static_cast<size_t>(bounds.size.height)) {
822706b4fc4SDimitry Andric bounds.origin.y += (bounds.size.height - num_lines + 2) / 2;
823706b4fc4SDimitry Andric bounds.size.height = num_lines + 2;
824706b4fc4SDimitry Andric } else {
825706b4fc4SDimitry Andric if (bounds.size.height > 100) {
826706b4fc4SDimitry Andric const int inset_h = bounds.size.height / 4;
827706b4fc4SDimitry Andric bounds.origin.y += inset_h;
828706b4fc4SDimitry Andric bounds.size.height -= 2 * inset_h;
829706b4fc4SDimitry Andric }
830706b4fc4SDimitry Andric }
831706b4fc4SDimitry Andric WindowSP help_window_sp;
832706b4fc4SDimitry Andric Window *parent_window = GetParent();
833706b4fc4SDimitry Andric if (parent_window)
834706b4fc4SDimitry Andric help_window_sp = parent_window->CreateSubWindow("Help", bounds, true);
835706b4fc4SDimitry Andric else
836706b4fc4SDimitry Andric help_window_sp = CreateSubWindow("Help", bounds, true);
837706b4fc4SDimitry Andric help_window_sp->SetDelegate(
838706b4fc4SDimitry Andric WindowDelegateSP(help_delegate_up.release()));
839706b4fc4SDimitry Andric return true;
840706b4fc4SDimitry Andric }
841706b4fc4SDimitry Andric }
842706b4fc4SDimitry Andric return false;
843706b4fc4SDimitry Andric }
844706b4fc4SDimitry Andric
HandleChar(int key)845706b4fc4SDimitry Andric virtual HandleCharResult HandleChar(int key) {
846706b4fc4SDimitry Andric // Always check the active window first
847706b4fc4SDimitry Andric HandleCharResult result = eKeyNotHandled;
848706b4fc4SDimitry Andric WindowSP active_window_sp = GetActiveWindow();
849706b4fc4SDimitry Andric if (active_window_sp) {
850706b4fc4SDimitry Andric result = active_window_sp->HandleChar(key);
851706b4fc4SDimitry Andric if (result != eKeyNotHandled)
852706b4fc4SDimitry Andric return result;
853706b4fc4SDimitry Andric }
854706b4fc4SDimitry Andric
855706b4fc4SDimitry Andric if (m_delegate_sp) {
856706b4fc4SDimitry Andric result = m_delegate_sp->WindowDelegateHandleChar(*this, key);
857706b4fc4SDimitry Andric if (result != eKeyNotHandled)
858706b4fc4SDimitry Andric return result;
859706b4fc4SDimitry Andric }
860706b4fc4SDimitry Andric
861706b4fc4SDimitry Andric // Then check for any windows that want any keys that weren't handled. This
862706b4fc4SDimitry Andric // is typically only for a menubar. Make a copy of the subwindows in case
863706b4fc4SDimitry Andric // any HandleChar() functions muck with the subwindows. If we don't do
864706b4fc4SDimitry Andric // this, we can crash when iterating over the subwindows.
865706b4fc4SDimitry Andric Windows subwindows(m_subwindows);
866706b4fc4SDimitry Andric for (auto subwindow_sp : subwindows) {
867706b4fc4SDimitry Andric if (!subwindow_sp->m_can_activate) {
868706b4fc4SDimitry Andric HandleCharResult result = subwindow_sp->HandleChar(key);
869706b4fc4SDimitry Andric if (result != eKeyNotHandled)
870706b4fc4SDimitry Andric return result;
871706b4fc4SDimitry Andric }
872706b4fc4SDimitry Andric }
873706b4fc4SDimitry Andric
874706b4fc4SDimitry Andric return eKeyNotHandled;
875706b4fc4SDimitry Andric }
876706b4fc4SDimitry Andric
GetActiveWindow()877706b4fc4SDimitry Andric WindowSP GetActiveWindow() {
878706b4fc4SDimitry Andric if (!m_subwindows.empty()) {
879706b4fc4SDimitry Andric if (m_curr_active_window_idx >= m_subwindows.size()) {
880706b4fc4SDimitry Andric if (m_prev_active_window_idx < m_subwindows.size()) {
881706b4fc4SDimitry Andric m_curr_active_window_idx = m_prev_active_window_idx;
882706b4fc4SDimitry Andric m_prev_active_window_idx = UINT32_MAX;
883706b4fc4SDimitry Andric } else if (IsActive()) {
884706b4fc4SDimitry Andric m_prev_active_window_idx = UINT32_MAX;
885706b4fc4SDimitry Andric m_curr_active_window_idx = UINT32_MAX;
886706b4fc4SDimitry Andric
887706b4fc4SDimitry Andric // Find first window that wants to be active if this window is active
888706b4fc4SDimitry Andric const size_t num_subwindows = m_subwindows.size();
889706b4fc4SDimitry Andric for (size_t i = 0; i < num_subwindows; ++i) {
890706b4fc4SDimitry Andric if (m_subwindows[i]->GetCanBeActive()) {
891706b4fc4SDimitry Andric m_curr_active_window_idx = i;
892706b4fc4SDimitry Andric break;
893706b4fc4SDimitry Andric }
894706b4fc4SDimitry Andric }
895706b4fc4SDimitry Andric }
896706b4fc4SDimitry Andric }
897706b4fc4SDimitry Andric
898706b4fc4SDimitry Andric if (m_curr_active_window_idx < m_subwindows.size())
899706b4fc4SDimitry Andric return m_subwindows[m_curr_active_window_idx];
900706b4fc4SDimitry Andric }
901706b4fc4SDimitry Andric return WindowSP();
902706b4fc4SDimitry Andric }
903706b4fc4SDimitry Andric
GetCanBeActive() const904706b4fc4SDimitry Andric bool GetCanBeActive() const { return m_can_activate; }
905706b4fc4SDimitry Andric
SetCanBeActive(bool b)906706b4fc4SDimitry Andric void SetCanBeActive(bool b) { m_can_activate = b; }
907706b4fc4SDimitry Andric
SetDelegate(const WindowDelegateSP & delegate_sp)908706b4fc4SDimitry Andric void SetDelegate(const WindowDelegateSP &delegate_sp) {
909706b4fc4SDimitry Andric m_delegate_sp = delegate_sp;
910706b4fc4SDimitry Andric }
911706b4fc4SDimitry Andric
GetParent() const912706b4fc4SDimitry Andric Window *GetParent() const { return m_parent; }
913706b4fc4SDimitry Andric
IsActive() const914706b4fc4SDimitry Andric bool IsActive() const {
915706b4fc4SDimitry Andric if (m_parent)
916706b4fc4SDimitry Andric return m_parent->GetActiveWindow().get() == this;
917706b4fc4SDimitry Andric else
918706b4fc4SDimitry Andric return true; // Top level window is always active
919706b4fc4SDimitry Andric }
920706b4fc4SDimitry Andric
SelectNextWindowAsActive()921706b4fc4SDimitry Andric void SelectNextWindowAsActive() {
922706b4fc4SDimitry Andric // Move active focus to next window
923b60736ecSDimitry Andric const int num_subwindows = m_subwindows.size();
924b60736ecSDimitry Andric int start_idx = 0;
925b60736ecSDimitry Andric if (m_curr_active_window_idx != UINT32_MAX) {
926706b4fc4SDimitry Andric m_prev_active_window_idx = m_curr_active_window_idx;
927b60736ecSDimitry Andric start_idx = m_curr_active_window_idx + 1;
928b60736ecSDimitry Andric }
929b60736ecSDimitry Andric for (int idx = start_idx; idx < num_subwindows; ++idx) {
930706b4fc4SDimitry Andric if (m_subwindows[idx]->GetCanBeActive()) {
931706b4fc4SDimitry Andric m_curr_active_window_idx = idx;
932b60736ecSDimitry Andric return;
933706b4fc4SDimitry Andric }
934706b4fc4SDimitry Andric }
935b60736ecSDimitry Andric for (int idx = 0; idx < start_idx; ++idx) {
936706b4fc4SDimitry Andric if (m_subwindows[idx]->GetCanBeActive()) {
937706b4fc4SDimitry Andric m_curr_active_window_idx = idx;
938706b4fc4SDimitry Andric break;
939706b4fc4SDimitry Andric }
940706b4fc4SDimitry Andric }
941706b4fc4SDimitry Andric }
942b60736ecSDimitry Andric
SelectPreviousWindowAsActive()943b60736ecSDimitry Andric void SelectPreviousWindowAsActive() {
944b60736ecSDimitry Andric // Move active focus to previous window
945b60736ecSDimitry Andric const int num_subwindows = m_subwindows.size();
946b60736ecSDimitry Andric int start_idx = num_subwindows - 1;
947b60736ecSDimitry Andric if (m_curr_active_window_idx != UINT32_MAX) {
948706b4fc4SDimitry Andric m_prev_active_window_idx = m_curr_active_window_idx;
949b60736ecSDimitry Andric start_idx = m_curr_active_window_idx - 1;
950b60736ecSDimitry Andric }
951b60736ecSDimitry Andric for (int idx = start_idx; idx >= 0; --idx) {
952b60736ecSDimitry Andric if (m_subwindows[idx]->GetCanBeActive()) {
953b60736ecSDimitry Andric m_curr_active_window_idx = idx;
954b60736ecSDimitry Andric return;
955b60736ecSDimitry Andric }
956b60736ecSDimitry Andric }
957b60736ecSDimitry Andric for (int idx = num_subwindows - 1; idx > start_idx; --idx) {
958706b4fc4SDimitry Andric if (m_subwindows[idx]->GetCanBeActive()) {
959706b4fc4SDimitry Andric m_curr_active_window_idx = idx;
960706b4fc4SDimitry Andric break;
961706b4fc4SDimitry Andric }
962706b4fc4SDimitry Andric }
963706b4fc4SDimitry Andric }
964706b4fc4SDimitry Andric
GetName() const965706b4fc4SDimitry Andric const char *GetName() const { return m_name.c_str(); }
966706b4fc4SDimitry Andric
967706b4fc4SDimitry Andric protected:
968706b4fc4SDimitry Andric std::string m_name;
969706b4fc4SDimitry Andric PANEL *m_panel;
970706b4fc4SDimitry Andric Window *m_parent;
971706b4fc4SDimitry Andric Windows m_subwindows;
972706b4fc4SDimitry Andric WindowDelegateSP m_delegate_sp;
973706b4fc4SDimitry Andric uint32_t m_curr_active_window_idx;
974706b4fc4SDimitry Andric uint32_t m_prev_active_window_idx;
975706b4fc4SDimitry Andric bool m_delete;
976706b4fc4SDimitry Andric bool m_needs_update;
977706b4fc4SDimitry Andric bool m_can_activate;
978706b4fc4SDimitry Andric bool m_is_subwin;
979706b4fc4SDimitry Andric
980706b4fc4SDimitry Andric private:
981cfca06d7SDimitry Andric Window(const Window &) = delete;
982cfca06d7SDimitry Andric const Window &operator=(const Window &) = delete;
983706b4fc4SDimitry Andric };
984706b4fc4SDimitry Andric
985344a3780SDimitry Andric /////////
986344a3780SDimitry Andric // Forms
987344a3780SDimitry Andric /////////
988344a3780SDimitry Andric
989344a3780SDimitry Andric // A scroll context defines a vertical region that needs to be visible in a
990344a3780SDimitry Andric // scrolling area. The region is defined by the index of the start and end lines
991344a3780SDimitry Andric // of the region. The start and end lines may be equal, in which case, the
992344a3780SDimitry Andric // region is a single line.
993344a3780SDimitry Andric struct ScrollContext {
994344a3780SDimitry Andric int start;
995344a3780SDimitry Andric int end;
996344a3780SDimitry Andric
ScrollContextcurses::ScrollContext997344a3780SDimitry Andric ScrollContext(int line) : start(line), end(line) {}
ScrollContextcurses::ScrollContext998344a3780SDimitry Andric ScrollContext(int _start, int _end) : start(_start), end(_end) {}
999344a3780SDimitry Andric
Offsetcurses::ScrollContext1000344a3780SDimitry Andric void Offset(int offset) {
1001344a3780SDimitry Andric start += offset;
1002344a3780SDimitry Andric end += offset;
1003344a3780SDimitry Andric }
1004344a3780SDimitry Andric };
1005344a3780SDimitry Andric
1006344a3780SDimitry Andric class FieldDelegate {
1007344a3780SDimitry Andric public:
1008344a3780SDimitry Andric virtual ~FieldDelegate() = default;
1009344a3780SDimitry Andric
1010344a3780SDimitry Andric // Returns the number of lines needed to draw the field. The draw method will
1011344a3780SDimitry Andric // be given a surface that have exactly this number of lines.
1012344a3780SDimitry Andric virtual int FieldDelegateGetHeight() = 0;
1013344a3780SDimitry Andric
1014344a3780SDimitry Andric // Returns the scroll context in the local coordinates of the field. By
1015344a3780SDimitry Andric // default, the scroll context spans the whole field. Bigger fields with
1016344a3780SDimitry Andric // internal navigation should override this method to provide a finer context.
1017344a3780SDimitry Andric // Typical override methods would first get the scroll context of the internal
1018344a3780SDimitry Andric // element then add the offset of the element in the field.
FieldDelegateGetScrollContext()1019344a3780SDimitry Andric virtual ScrollContext FieldDelegateGetScrollContext() {
1020344a3780SDimitry Andric return ScrollContext(0, FieldDelegateGetHeight() - 1);
1021344a3780SDimitry Andric }
1022344a3780SDimitry Andric
1023344a3780SDimitry Andric // Draw the field in the given subpad surface. The surface have a height that
1024344a3780SDimitry Andric // is equal to the height returned by FieldDelegateGetHeight(). If the field
1025344a3780SDimitry Andric // is selected in the form window, then is_selected will be true.
1026c0981da4SDimitry Andric virtual void FieldDelegateDraw(Surface &surface, bool is_selected) = 0;
1027344a3780SDimitry Andric
1028344a3780SDimitry Andric // Handle the key that wasn't handled by the form window or a container field.
FieldDelegateHandleChar(int key)1029344a3780SDimitry Andric virtual HandleCharResult FieldDelegateHandleChar(int key) {
1030344a3780SDimitry Andric return eKeyNotHandled;
1031344a3780SDimitry Andric }
1032344a3780SDimitry Andric
1033344a3780SDimitry Andric // This is executed once the user exists the field, that is, once the user
1034344a3780SDimitry Andric // navigates to the next or the previous field. This is particularly useful to
1035344a3780SDimitry Andric // do in-field validation and error setting. Fields with internal navigation
1036344a3780SDimitry Andric // should call this method on their fields.
FieldDelegateExitCallback()103777fc4c14SDimitry Andric virtual void FieldDelegateExitCallback() {}
1038344a3780SDimitry Andric
1039344a3780SDimitry Andric // Fields may have internal navigation, for instance, a List Field have
1040344a3780SDimitry Andric // multiple internal elements, which needs to be navigated. To allow for this
1041344a3780SDimitry Andric // mechanism, the window shouldn't handle the navigation keys all the time,
1042344a3780SDimitry Andric // and instead call the key handing method of the selected field. It should
1043344a3780SDimitry Andric // only handle the navigation keys when the field contains a single element or
1044344a3780SDimitry Andric // have the last or first element selected depending on if the user is
1045344a3780SDimitry Andric // navigating forward or backward. Additionally, once a field is selected in
1046344a3780SDimitry Andric // the forward or backward direction, its first or last internal element
1047344a3780SDimitry Andric // should be selected. The following methods implements those mechanisms.
1048344a3780SDimitry Andric
1049344a3780SDimitry Andric // Returns true if the first element in the field is selected or if the field
1050344a3780SDimitry Andric // contains a single element.
FieldDelegateOnFirstOrOnlyElement()1051344a3780SDimitry Andric virtual bool FieldDelegateOnFirstOrOnlyElement() { return true; }
1052344a3780SDimitry Andric
1053344a3780SDimitry Andric // Returns true if the last element in the field is selected or if the field
1054344a3780SDimitry Andric // contains a single element.
FieldDelegateOnLastOrOnlyElement()1055344a3780SDimitry Andric virtual bool FieldDelegateOnLastOrOnlyElement() { return true; }
1056344a3780SDimitry Andric
1057344a3780SDimitry Andric // Select the first element in the field if multiple elements exists.
FieldDelegateSelectFirstElement()105877fc4c14SDimitry Andric virtual void FieldDelegateSelectFirstElement() {}
1059344a3780SDimitry Andric
1060344a3780SDimitry Andric // Select the last element in the field if multiple elements exists.
FieldDelegateSelectLastElement()106177fc4c14SDimitry Andric virtual void FieldDelegateSelectLastElement() {}
1062344a3780SDimitry Andric
1063344a3780SDimitry Andric // Returns true if the field has an error, false otherwise.
FieldDelegateHasError()1064344a3780SDimitry Andric virtual bool FieldDelegateHasError() { return false; }
1065344a3780SDimitry Andric
FieldDelegateIsVisible()1066344a3780SDimitry Andric bool FieldDelegateIsVisible() { return m_is_visible; }
1067344a3780SDimitry Andric
FieldDelegateHide()1068344a3780SDimitry Andric void FieldDelegateHide() { m_is_visible = false; }
1069344a3780SDimitry Andric
FieldDelegateShow()1070344a3780SDimitry Andric void FieldDelegateShow() { m_is_visible = true; }
1071344a3780SDimitry Andric
1072344a3780SDimitry Andric protected:
1073344a3780SDimitry Andric bool m_is_visible = true;
1074344a3780SDimitry Andric };
1075344a3780SDimitry Andric
1076344a3780SDimitry Andric typedef std::unique_ptr<FieldDelegate> FieldDelegateUP;
1077344a3780SDimitry Andric
1078344a3780SDimitry Andric class TextFieldDelegate : public FieldDelegate {
1079344a3780SDimitry Andric public:
TextFieldDelegate(const char * label,const char * content,bool required)1080344a3780SDimitry Andric TextFieldDelegate(const char *label, const char *content, bool required)
1081145449b1SDimitry Andric : m_label(label), m_required(required) {
1082344a3780SDimitry Andric if (content)
1083344a3780SDimitry Andric m_content = content;
1084344a3780SDimitry Andric }
1085344a3780SDimitry Andric
1086344a3780SDimitry Andric // Text fields are drawn as titled boxes of a single line, with a possible
1087344a3780SDimitry Andric // error messages at the end.
1088344a3780SDimitry Andric //
1089344a3780SDimitry Andric // __[Label]___________
1090344a3780SDimitry Andric // | |
1091344a3780SDimitry Andric // |__________________|
1092344a3780SDimitry Andric // - Error message if it exists.
1093344a3780SDimitry Andric
1094344a3780SDimitry Andric // The text field has a height of 3 lines. 2 lines for borders and 1 line for
1095344a3780SDimitry Andric // the content.
GetFieldHeight()1096344a3780SDimitry Andric int GetFieldHeight() { return 3; }
1097344a3780SDimitry Andric
1098344a3780SDimitry Andric // The text field has a full height of 3 or 4 lines. 3 lines for the actual
1099344a3780SDimitry Andric // field and an optional line for an error if it exists.
FieldDelegateGetHeight()1100344a3780SDimitry Andric int FieldDelegateGetHeight() override {
1101344a3780SDimitry Andric int height = GetFieldHeight();
1102344a3780SDimitry Andric if (FieldDelegateHasError())
1103344a3780SDimitry Andric height++;
1104344a3780SDimitry Andric return height;
1105344a3780SDimitry Andric }
1106344a3780SDimitry Andric
1107344a3780SDimitry Andric // Get the cursor X position in the surface coordinate.
GetCursorXPosition()1108344a3780SDimitry Andric int GetCursorXPosition() { return m_cursor_position - m_first_visibile_char; }
1109344a3780SDimitry Andric
GetContentLength()1110344a3780SDimitry Andric int GetContentLength() { return m_content.length(); }
1111344a3780SDimitry Andric
DrawContent(Surface & surface,bool is_selected)1112c0981da4SDimitry Andric void DrawContent(Surface &surface, bool is_selected) {
1113c0981da4SDimitry Andric UpdateScrolling(surface.GetWidth());
1114c0981da4SDimitry Andric
1115344a3780SDimitry Andric surface.MoveCursor(0, 0);
1116344a3780SDimitry Andric const char *text = m_content.c_str() + m_first_visibile_char;
1117344a3780SDimitry Andric surface.PutCString(text, surface.GetWidth());
1118344a3780SDimitry Andric
1119344a3780SDimitry Andric // Highlight the cursor.
1120344a3780SDimitry Andric surface.MoveCursor(GetCursorXPosition(), 0);
1121344a3780SDimitry Andric if (is_selected)
1122344a3780SDimitry Andric surface.AttributeOn(A_REVERSE);
1123344a3780SDimitry Andric if (m_cursor_position == GetContentLength())
1124344a3780SDimitry Andric // Cursor is past the last character. Highlight an empty space.
1125344a3780SDimitry Andric surface.PutChar(' ');
1126344a3780SDimitry Andric else
1127344a3780SDimitry Andric surface.PutChar(m_content[m_cursor_position]);
1128344a3780SDimitry Andric if (is_selected)
1129344a3780SDimitry Andric surface.AttributeOff(A_REVERSE);
1130344a3780SDimitry Andric }
1131344a3780SDimitry Andric
DrawField(Surface & surface,bool is_selected)1132c0981da4SDimitry Andric void DrawField(Surface &surface, bool is_selected) {
1133344a3780SDimitry Andric surface.TitledBox(m_label.c_str());
1134344a3780SDimitry Andric
1135344a3780SDimitry Andric Rect content_bounds = surface.GetFrame();
1136344a3780SDimitry Andric content_bounds.Inset(1, 1);
1137c0981da4SDimitry Andric Surface content_surface = surface.SubSurface(content_bounds);
1138344a3780SDimitry Andric
1139344a3780SDimitry Andric DrawContent(content_surface, is_selected);
1140344a3780SDimitry Andric }
1141344a3780SDimitry Andric
DrawError(Surface & surface)1142c0981da4SDimitry Andric void DrawError(Surface &surface) {
1143344a3780SDimitry Andric if (!FieldDelegateHasError())
1144344a3780SDimitry Andric return;
1145344a3780SDimitry Andric surface.MoveCursor(0, 0);
1146344a3780SDimitry Andric surface.AttributeOn(COLOR_PAIR(RedOnBlack));
1147344a3780SDimitry Andric surface.PutChar(ACS_DIAMOND);
1148344a3780SDimitry Andric surface.PutChar(' ');
1149344a3780SDimitry Andric surface.PutCStringTruncated(1, GetError().c_str());
1150344a3780SDimitry Andric surface.AttributeOff(COLOR_PAIR(RedOnBlack));
1151344a3780SDimitry Andric }
1152344a3780SDimitry Andric
FieldDelegateDraw(Surface & surface,bool is_selected)1153c0981da4SDimitry Andric void FieldDelegateDraw(Surface &surface, bool is_selected) override {
1154344a3780SDimitry Andric Rect frame = surface.GetFrame();
1155344a3780SDimitry Andric Rect field_bounds, error_bounds;
1156344a3780SDimitry Andric frame.HorizontalSplit(GetFieldHeight(), field_bounds, error_bounds);
1157c0981da4SDimitry Andric Surface field_surface = surface.SubSurface(field_bounds);
1158c0981da4SDimitry Andric Surface error_surface = surface.SubSurface(error_bounds);
1159344a3780SDimitry Andric
1160344a3780SDimitry Andric DrawField(field_surface, is_selected);
1161344a3780SDimitry Andric DrawError(error_surface);
1162344a3780SDimitry Andric }
1163344a3780SDimitry Andric
1164c0981da4SDimitry Andric // Get the position of the last visible character.
GetLastVisibleCharPosition(int width)1165c0981da4SDimitry Andric int GetLastVisibleCharPosition(int width) {
1166c0981da4SDimitry Andric int position = m_first_visibile_char + width - 1;
1167c0981da4SDimitry Andric return std::min(position, GetContentLength());
1168c0981da4SDimitry Andric }
1169c0981da4SDimitry Andric
UpdateScrolling(int width)1170c0981da4SDimitry Andric void UpdateScrolling(int width) {
1171c0981da4SDimitry Andric if (m_cursor_position < m_first_visibile_char) {
1172c0981da4SDimitry Andric m_first_visibile_char = m_cursor_position;
1173c0981da4SDimitry Andric return;
1174c0981da4SDimitry Andric }
1175c0981da4SDimitry Andric
1176c0981da4SDimitry Andric if (m_cursor_position > GetLastVisibleCharPosition(width))
1177c0981da4SDimitry Andric m_first_visibile_char = m_cursor_position - (width - 1);
1178c0981da4SDimitry Andric }
1179c0981da4SDimitry Andric
1180344a3780SDimitry Andric // The cursor is allowed to move one character past the string.
1181344a3780SDimitry Andric // m_cursor_position is in range [0, GetContentLength()].
MoveCursorRight()1182344a3780SDimitry Andric void MoveCursorRight() {
1183344a3780SDimitry Andric if (m_cursor_position < GetContentLength())
1184344a3780SDimitry Andric m_cursor_position++;
1185344a3780SDimitry Andric }
1186344a3780SDimitry Andric
MoveCursorLeft()1187344a3780SDimitry Andric void MoveCursorLeft() {
1188344a3780SDimitry Andric if (m_cursor_position > 0)
1189344a3780SDimitry Andric m_cursor_position--;
1190344a3780SDimitry Andric }
1191344a3780SDimitry Andric
MoveCursorToStart()1192c0981da4SDimitry Andric void MoveCursorToStart() { m_cursor_position = 0; }
1193c0981da4SDimitry Andric
MoveCursorToEnd()1194c0981da4SDimitry Andric void MoveCursorToEnd() { m_cursor_position = GetContentLength(); }
1195344a3780SDimitry Andric
ScrollLeft()1196344a3780SDimitry Andric void ScrollLeft() {
1197344a3780SDimitry Andric if (m_first_visibile_char > 0)
1198344a3780SDimitry Andric m_first_visibile_char--;
1199344a3780SDimitry Andric }
1200344a3780SDimitry Andric
1201c0981da4SDimitry Andric // Insert a character at the current cursor position and advance the cursor
1202c0981da4SDimitry Andric // position.
InsertChar(char character)1203344a3780SDimitry Andric void InsertChar(char character) {
1204344a3780SDimitry Andric m_content.insert(m_cursor_position, 1, character);
1205344a3780SDimitry Andric m_cursor_position++;
1206c0981da4SDimitry Andric ClearError();
1207344a3780SDimitry Andric }
1208344a3780SDimitry Andric
1209344a3780SDimitry Andric // Remove the character before the cursor position, retreat the cursor
1210c0981da4SDimitry Andric // position, and scroll left.
RemovePreviousChar()1211c0981da4SDimitry Andric void RemovePreviousChar() {
1212344a3780SDimitry Andric if (m_cursor_position == 0)
1213344a3780SDimitry Andric return;
1214344a3780SDimitry Andric
1215344a3780SDimitry Andric m_content.erase(m_cursor_position - 1, 1);
1216344a3780SDimitry Andric m_cursor_position--;
1217344a3780SDimitry Andric ScrollLeft();
1218c0981da4SDimitry Andric ClearError();
1219c0981da4SDimitry Andric }
1220c0981da4SDimitry Andric
1221c0981da4SDimitry Andric // Remove the character after the cursor position.
RemoveNextChar()1222c0981da4SDimitry Andric void RemoveNextChar() {
1223c0981da4SDimitry Andric if (m_cursor_position == GetContentLength())
1224c0981da4SDimitry Andric return;
1225c0981da4SDimitry Andric
1226c0981da4SDimitry Andric m_content.erase(m_cursor_position, 1);
1227c0981da4SDimitry Andric ClearError();
1228c0981da4SDimitry Andric }
1229c0981da4SDimitry Andric
1230c0981da4SDimitry Andric // Clear characters from the current cursor position to the end.
ClearToEnd()1231c0981da4SDimitry Andric void ClearToEnd() {
1232c0981da4SDimitry Andric m_content.erase(m_cursor_position);
1233c0981da4SDimitry Andric ClearError();
1234c0981da4SDimitry Andric }
1235c0981da4SDimitry Andric
Clear()1236c0981da4SDimitry Andric void Clear() {
1237c0981da4SDimitry Andric m_content.clear();
1238c0981da4SDimitry Andric m_cursor_position = 0;
1239c0981da4SDimitry Andric ClearError();
1240344a3780SDimitry Andric }
1241344a3780SDimitry Andric
1242344a3780SDimitry Andric // True if the key represents a char that can be inserted in the field
1243344a3780SDimitry Andric // content, false otherwise.
IsAcceptableChar(int key)1244c0981da4SDimitry Andric virtual bool IsAcceptableChar(int key) {
1245c0981da4SDimitry Andric // The behavior of isprint is undefined when the value is not representable
1246c0981da4SDimitry Andric // as an unsigned char. So explicitly check for non-ascii key codes.
1247c0981da4SDimitry Andric if (key > 127)
1248c0981da4SDimitry Andric return false;
1249c0981da4SDimitry Andric return isprint(key);
1250c0981da4SDimitry Andric }
1251344a3780SDimitry Andric
FieldDelegateHandleChar(int key)1252344a3780SDimitry Andric HandleCharResult FieldDelegateHandleChar(int key) override {
1253344a3780SDimitry Andric if (IsAcceptableChar(key)) {
1254344a3780SDimitry Andric ClearError();
1255344a3780SDimitry Andric InsertChar((char)key);
1256344a3780SDimitry Andric return eKeyHandled;
1257344a3780SDimitry Andric }
1258344a3780SDimitry Andric
1259344a3780SDimitry Andric switch (key) {
1260c0981da4SDimitry Andric case KEY_HOME:
1261c0981da4SDimitry Andric case KEY_CTRL_A:
1262c0981da4SDimitry Andric MoveCursorToStart();
1263c0981da4SDimitry Andric return eKeyHandled;
1264c0981da4SDimitry Andric case KEY_END:
1265c0981da4SDimitry Andric case KEY_CTRL_E:
1266c0981da4SDimitry Andric MoveCursorToEnd();
1267c0981da4SDimitry Andric return eKeyHandled;
1268344a3780SDimitry Andric case KEY_RIGHT:
1269c0981da4SDimitry Andric case KEY_SF:
1270344a3780SDimitry Andric MoveCursorRight();
1271344a3780SDimitry Andric return eKeyHandled;
1272344a3780SDimitry Andric case KEY_LEFT:
1273c0981da4SDimitry Andric case KEY_SR:
1274344a3780SDimitry Andric MoveCursorLeft();
1275344a3780SDimitry Andric return eKeyHandled;
1276344a3780SDimitry Andric case KEY_BACKSPACE:
1277c0981da4SDimitry Andric case KEY_DELETE:
1278c0981da4SDimitry Andric RemovePreviousChar();
1279c0981da4SDimitry Andric return eKeyHandled;
1280c0981da4SDimitry Andric case KEY_DC:
1281c0981da4SDimitry Andric RemoveNextChar();
1282c0981da4SDimitry Andric return eKeyHandled;
1283c0981da4SDimitry Andric case KEY_EOL:
1284c0981da4SDimitry Andric case KEY_CTRL_K:
1285c0981da4SDimitry Andric ClearToEnd();
1286c0981da4SDimitry Andric return eKeyHandled;
1287c0981da4SDimitry Andric case KEY_DL:
1288c0981da4SDimitry Andric case KEY_CLEAR:
1289c0981da4SDimitry Andric Clear();
1290344a3780SDimitry Andric return eKeyHandled;
1291344a3780SDimitry Andric default:
1292344a3780SDimitry Andric break;
1293344a3780SDimitry Andric }
1294344a3780SDimitry Andric return eKeyNotHandled;
1295344a3780SDimitry Andric }
1296344a3780SDimitry Andric
FieldDelegateHasError()1297344a3780SDimitry Andric bool FieldDelegateHasError() override { return !m_error.empty(); }
1298344a3780SDimitry Andric
FieldDelegateExitCallback()1299344a3780SDimitry Andric void FieldDelegateExitCallback() override {
1300344a3780SDimitry Andric if (!IsSpecified() && m_required)
1301344a3780SDimitry Andric SetError("This field is required!");
1302344a3780SDimitry Andric }
1303344a3780SDimitry Andric
IsSpecified()1304344a3780SDimitry Andric bool IsSpecified() { return !m_content.empty(); }
1305344a3780SDimitry Andric
ClearError()1306344a3780SDimitry Andric void ClearError() { m_error.clear(); }
1307344a3780SDimitry Andric
GetError()1308344a3780SDimitry Andric const std::string &GetError() { return m_error; }
1309344a3780SDimitry Andric
SetError(const char * error)1310344a3780SDimitry Andric void SetError(const char *error) { m_error = error; }
1311344a3780SDimitry Andric
GetText()1312344a3780SDimitry Andric const std::string &GetText() { return m_content; }
1313344a3780SDimitry Andric
SetText(const char * text)1314c0981da4SDimitry Andric void SetText(const char *text) {
1315c0981da4SDimitry Andric if (text == nullptr) {
1316c0981da4SDimitry Andric m_content.clear();
1317c0981da4SDimitry Andric return;
1318c0981da4SDimitry Andric }
1319c0981da4SDimitry Andric m_content = text;
1320c0981da4SDimitry Andric }
1321c0981da4SDimitry Andric
1322344a3780SDimitry Andric protected:
1323344a3780SDimitry Andric std::string m_label;
1324344a3780SDimitry Andric bool m_required;
1325344a3780SDimitry Andric // The position of the top left corner character of the border.
1326344a3780SDimitry Andric std::string m_content;
1327344a3780SDimitry Andric // The cursor position in the content string itself. Can be in the range
1328344a3780SDimitry Andric // [0, GetContentLength()].
1329145449b1SDimitry Andric int m_cursor_position = 0;
1330344a3780SDimitry Andric // The index of the first visible character in the content.
1331145449b1SDimitry Andric int m_first_visibile_char = 0;
1332344a3780SDimitry Andric // Optional error message. If empty, field is considered to have no error.
1333344a3780SDimitry Andric std::string m_error;
1334344a3780SDimitry Andric };
1335344a3780SDimitry Andric
1336344a3780SDimitry Andric class IntegerFieldDelegate : public TextFieldDelegate {
1337344a3780SDimitry Andric public:
IntegerFieldDelegate(const char * label,int content,bool required)1338344a3780SDimitry Andric IntegerFieldDelegate(const char *label, int content, bool required)
1339344a3780SDimitry Andric : TextFieldDelegate(label, std::to_string(content).c_str(), required) {}
1340344a3780SDimitry Andric
1341344a3780SDimitry Andric // Only accept digits.
IsAcceptableChar(int key)1342344a3780SDimitry Andric bool IsAcceptableChar(int key) override { return isdigit(key); }
1343344a3780SDimitry Andric
1344344a3780SDimitry Andric // Returns the integer content of the field.
GetInteger()1345344a3780SDimitry Andric int GetInteger() { return std::stoi(m_content); }
1346344a3780SDimitry Andric };
1347344a3780SDimitry Andric
1348344a3780SDimitry Andric class FileFieldDelegate : public TextFieldDelegate {
1349344a3780SDimitry Andric public:
FileFieldDelegate(const char * label,const char * content,bool need_to_exist,bool required)1350344a3780SDimitry Andric FileFieldDelegate(const char *label, const char *content, bool need_to_exist,
1351344a3780SDimitry Andric bool required)
1352344a3780SDimitry Andric : TextFieldDelegate(label, content, required),
1353344a3780SDimitry Andric m_need_to_exist(need_to_exist) {}
1354344a3780SDimitry Andric
FieldDelegateExitCallback()1355344a3780SDimitry Andric void FieldDelegateExitCallback() override {
1356344a3780SDimitry Andric TextFieldDelegate::FieldDelegateExitCallback();
1357344a3780SDimitry Andric if (!IsSpecified())
1358344a3780SDimitry Andric return;
1359344a3780SDimitry Andric
1360344a3780SDimitry Andric if (!m_need_to_exist)
1361344a3780SDimitry Andric return;
1362344a3780SDimitry Andric
1363344a3780SDimitry Andric FileSpec file = GetResolvedFileSpec();
1364344a3780SDimitry Andric if (!FileSystem::Instance().Exists(file)) {
1365344a3780SDimitry Andric SetError("File doesn't exist!");
1366344a3780SDimitry Andric return;
1367344a3780SDimitry Andric }
1368344a3780SDimitry Andric if (FileSystem::Instance().IsDirectory(file)) {
1369344a3780SDimitry Andric SetError("Not a file!");
1370344a3780SDimitry Andric return;
1371344a3780SDimitry Andric }
1372344a3780SDimitry Andric }
1373344a3780SDimitry Andric
GetFileSpec()1374344a3780SDimitry Andric FileSpec GetFileSpec() {
1375344a3780SDimitry Andric FileSpec file_spec(GetPath());
1376344a3780SDimitry Andric return file_spec;
1377344a3780SDimitry Andric }
1378344a3780SDimitry Andric
GetResolvedFileSpec()1379344a3780SDimitry Andric FileSpec GetResolvedFileSpec() {
1380344a3780SDimitry Andric FileSpec file_spec(GetPath());
1381344a3780SDimitry Andric FileSystem::Instance().Resolve(file_spec);
1382344a3780SDimitry Andric return file_spec;
1383344a3780SDimitry Andric }
1384344a3780SDimitry Andric
GetPath()1385344a3780SDimitry Andric const std::string &GetPath() { return m_content; }
1386344a3780SDimitry Andric
1387344a3780SDimitry Andric protected:
1388344a3780SDimitry Andric bool m_need_to_exist;
1389344a3780SDimitry Andric };
1390344a3780SDimitry Andric
1391344a3780SDimitry Andric class DirectoryFieldDelegate : public TextFieldDelegate {
1392344a3780SDimitry Andric public:
DirectoryFieldDelegate(const char * label,const char * content,bool need_to_exist,bool required)1393344a3780SDimitry Andric DirectoryFieldDelegate(const char *label, const char *content,
1394344a3780SDimitry Andric bool need_to_exist, bool required)
1395344a3780SDimitry Andric : TextFieldDelegate(label, content, required),
1396344a3780SDimitry Andric m_need_to_exist(need_to_exist) {}
1397344a3780SDimitry Andric
FieldDelegateExitCallback()1398344a3780SDimitry Andric void FieldDelegateExitCallback() override {
1399344a3780SDimitry Andric TextFieldDelegate::FieldDelegateExitCallback();
1400344a3780SDimitry Andric if (!IsSpecified())
1401344a3780SDimitry Andric return;
1402344a3780SDimitry Andric
1403344a3780SDimitry Andric if (!m_need_to_exist)
1404344a3780SDimitry Andric return;
1405344a3780SDimitry Andric
1406344a3780SDimitry Andric FileSpec file = GetResolvedFileSpec();
1407344a3780SDimitry Andric if (!FileSystem::Instance().Exists(file)) {
1408344a3780SDimitry Andric SetError("Directory doesn't exist!");
1409344a3780SDimitry Andric return;
1410344a3780SDimitry Andric }
1411344a3780SDimitry Andric if (!FileSystem::Instance().IsDirectory(file)) {
1412344a3780SDimitry Andric SetError("Not a directory!");
1413344a3780SDimitry Andric return;
1414344a3780SDimitry Andric }
1415344a3780SDimitry Andric }
1416344a3780SDimitry Andric
GetFileSpec()1417344a3780SDimitry Andric FileSpec GetFileSpec() {
1418344a3780SDimitry Andric FileSpec file_spec(GetPath());
1419344a3780SDimitry Andric return file_spec;
1420344a3780SDimitry Andric }
1421344a3780SDimitry Andric
GetResolvedFileSpec()1422344a3780SDimitry Andric FileSpec GetResolvedFileSpec() {
1423344a3780SDimitry Andric FileSpec file_spec(GetPath());
1424344a3780SDimitry Andric FileSystem::Instance().Resolve(file_spec);
1425344a3780SDimitry Andric return file_spec;
1426344a3780SDimitry Andric }
1427344a3780SDimitry Andric
GetPath()1428344a3780SDimitry Andric const std::string &GetPath() { return m_content; }
1429344a3780SDimitry Andric
1430344a3780SDimitry Andric protected:
1431344a3780SDimitry Andric bool m_need_to_exist;
1432344a3780SDimitry Andric };
1433344a3780SDimitry Andric
1434344a3780SDimitry Andric class ArchFieldDelegate : public TextFieldDelegate {
1435344a3780SDimitry Andric public:
ArchFieldDelegate(const char * label,const char * content,bool required)1436344a3780SDimitry Andric ArchFieldDelegate(const char *label, const char *content, bool required)
1437344a3780SDimitry Andric : TextFieldDelegate(label, content, required) {}
1438344a3780SDimitry Andric
FieldDelegateExitCallback()1439344a3780SDimitry Andric void FieldDelegateExitCallback() override {
1440344a3780SDimitry Andric TextFieldDelegate::FieldDelegateExitCallback();
1441344a3780SDimitry Andric if (!IsSpecified())
1442344a3780SDimitry Andric return;
1443344a3780SDimitry Andric
1444344a3780SDimitry Andric if (!GetArchSpec().IsValid())
1445344a3780SDimitry Andric SetError("Not a valid arch!");
1446344a3780SDimitry Andric }
1447344a3780SDimitry Andric
GetArchString()1448344a3780SDimitry Andric const std::string &GetArchString() { return m_content; }
1449344a3780SDimitry Andric
GetArchSpec()1450344a3780SDimitry Andric ArchSpec GetArchSpec() { return ArchSpec(GetArchString()); }
1451344a3780SDimitry Andric };
1452344a3780SDimitry Andric
1453344a3780SDimitry Andric class BooleanFieldDelegate : public FieldDelegate {
1454344a3780SDimitry Andric public:
BooleanFieldDelegate(const char * label,bool content)1455344a3780SDimitry Andric BooleanFieldDelegate(const char *label, bool content)
1456344a3780SDimitry Andric : m_label(label), m_content(content) {}
1457344a3780SDimitry Andric
1458344a3780SDimitry Andric // Boolean fields are drawn as checkboxes.
1459344a3780SDimitry Andric //
1460344a3780SDimitry Andric // [X] Label or [ ] Label
1461344a3780SDimitry Andric
1462344a3780SDimitry Andric // Boolean fields are have a single line.
FieldDelegateGetHeight()1463344a3780SDimitry Andric int FieldDelegateGetHeight() override { return 1; }
1464344a3780SDimitry Andric
FieldDelegateDraw(Surface & surface,bool is_selected)1465c0981da4SDimitry Andric void FieldDelegateDraw(Surface &surface, bool is_selected) override {
1466344a3780SDimitry Andric surface.MoveCursor(0, 0);
1467344a3780SDimitry Andric surface.PutChar('[');
1468344a3780SDimitry Andric if (is_selected)
1469344a3780SDimitry Andric surface.AttributeOn(A_REVERSE);
1470344a3780SDimitry Andric surface.PutChar(m_content ? ACS_DIAMOND : ' ');
1471344a3780SDimitry Andric if (is_selected)
1472344a3780SDimitry Andric surface.AttributeOff(A_REVERSE);
1473344a3780SDimitry Andric surface.PutChar(']');
1474344a3780SDimitry Andric surface.PutChar(' ');
1475344a3780SDimitry Andric surface.PutCString(m_label.c_str());
1476344a3780SDimitry Andric }
1477344a3780SDimitry Andric
ToggleContent()1478344a3780SDimitry Andric void ToggleContent() { m_content = !m_content; }
1479344a3780SDimitry Andric
SetContentToTrue()1480344a3780SDimitry Andric void SetContentToTrue() { m_content = true; }
1481344a3780SDimitry Andric
SetContentToFalse()1482344a3780SDimitry Andric void SetContentToFalse() { m_content = false; }
1483344a3780SDimitry Andric
FieldDelegateHandleChar(int key)1484344a3780SDimitry Andric HandleCharResult FieldDelegateHandleChar(int key) override {
1485344a3780SDimitry Andric switch (key) {
1486344a3780SDimitry Andric case 't':
1487344a3780SDimitry Andric case '1':
1488344a3780SDimitry Andric SetContentToTrue();
1489344a3780SDimitry Andric return eKeyHandled;
1490344a3780SDimitry Andric case 'f':
1491344a3780SDimitry Andric case '0':
1492344a3780SDimitry Andric SetContentToFalse();
1493344a3780SDimitry Andric return eKeyHandled;
1494344a3780SDimitry Andric case ' ':
1495344a3780SDimitry Andric case '\r':
1496344a3780SDimitry Andric case '\n':
1497344a3780SDimitry Andric case KEY_ENTER:
1498344a3780SDimitry Andric ToggleContent();
1499344a3780SDimitry Andric return eKeyHandled;
1500344a3780SDimitry Andric default:
1501344a3780SDimitry Andric break;
1502344a3780SDimitry Andric }
1503344a3780SDimitry Andric return eKeyNotHandled;
1504344a3780SDimitry Andric }
1505344a3780SDimitry Andric
1506344a3780SDimitry Andric // Returns the boolean content of the field.
GetBoolean()1507344a3780SDimitry Andric bool GetBoolean() { return m_content; }
1508344a3780SDimitry Andric
1509344a3780SDimitry Andric protected:
1510344a3780SDimitry Andric std::string m_label;
1511344a3780SDimitry Andric bool m_content;
1512344a3780SDimitry Andric };
1513344a3780SDimitry Andric
1514344a3780SDimitry Andric class ChoicesFieldDelegate : public FieldDelegate {
1515344a3780SDimitry Andric public:
ChoicesFieldDelegate(const char * label,int number_of_visible_choices,std::vector<std::string> choices)1516344a3780SDimitry Andric ChoicesFieldDelegate(const char *label, int number_of_visible_choices,
1517344a3780SDimitry Andric std::vector<std::string> choices)
1518344a3780SDimitry Andric : m_label(label), m_number_of_visible_choices(number_of_visible_choices),
1519145449b1SDimitry Andric m_choices(choices) {}
1520344a3780SDimitry Andric
1521344a3780SDimitry Andric // Choices fields are drawn as titles boxses of a number of visible choices.
1522344a3780SDimitry Andric // The rest of the choices become visible as the user scroll. The selected
1523344a3780SDimitry Andric // choice is denoted by a diamond as the first character.
1524344a3780SDimitry Andric //
1525344a3780SDimitry Andric // __[Label]___________
1526344a3780SDimitry Andric // |-Choice 1 |
1527344a3780SDimitry Andric // | Choice 2 |
1528344a3780SDimitry Andric // | Choice 3 |
1529344a3780SDimitry Andric // |__________________|
1530344a3780SDimitry Andric
1531344a3780SDimitry Andric // Choices field have two border characters plus the number of visible
1532344a3780SDimitry Andric // choices.
FieldDelegateGetHeight()1533344a3780SDimitry Andric int FieldDelegateGetHeight() override {
1534344a3780SDimitry Andric return m_number_of_visible_choices + 2;
1535344a3780SDimitry Andric }
1536344a3780SDimitry Andric
GetNumberOfChoices()1537344a3780SDimitry Andric int GetNumberOfChoices() { return m_choices.size(); }
1538344a3780SDimitry Andric
1539344a3780SDimitry Andric // Get the index of the last visible choice.
GetLastVisibleChoice()1540344a3780SDimitry Andric int GetLastVisibleChoice() {
1541344a3780SDimitry Andric int index = m_first_visibile_choice + m_number_of_visible_choices;
1542344a3780SDimitry Andric return std::min(index, GetNumberOfChoices()) - 1;
1543344a3780SDimitry Andric }
1544344a3780SDimitry Andric
DrawContent(Surface & surface,bool is_selected)1545c0981da4SDimitry Andric void DrawContent(Surface &surface, bool is_selected) {
1546344a3780SDimitry Andric int choices_to_draw = GetLastVisibleChoice() - m_first_visibile_choice + 1;
1547344a3780SDimitry Andric for (int i = 0; i < choices_to_draw; i++) {
1548344a3780SDimitry Andric surface.MoveCursor(0, i);
1549344a3780SDimitry Andric int current_choice = m_first_visibile_choice + i;
1550344a3780SDimitry Andric const char *text = m_choices[current_choice].c_str();
1551344a3780SDimitry Andric bool highlight = is_selected && current_choice == m_choice;
1552344a3780SDimitry Andric if (highlight)
1553344a3780SDimitry Andric surface.AttributeOn(A_REVERSE);
1554344a3780SDimitry Andric surface.PutChar(current_choice == m_choice ? ACS_DIAMOND : ' ');
1555344a3780SDimitry Andric surface.PutCString(text);
1556344a3780SDimitry Andric if (highlight)
1557344a3780SDimitry Andric surface.AttributeOff(A_REVERSE);
1558344a3780SDimitry Andric }
1559344a3780SDimitry Andric }
1560344a3780SDimitry Andric
FieldDelegateDraw(Surface & surface,bool is_selected)1561c0981da4SDimitry Andric void FieldDelegateDraw(Surface &surface, bool is_selected) override {
1562344a3780SDimitry Andric UpdateScrolling();
1563344a3780SDimitry Andric
1564344a3780SDimitry Andric surface.TitledBox(m_label.c_str());
1565344a3780SDimitry Andric
1566344a3780SDimitry Andric Rect content_bounds = surface.GetFrame();
1567344a3780SDimitry Andric content_bounds.Inset(1, 1);
1568c0981da4SDimitry Andric Surface content_surface = surface.SubSurface(content_bounds);
1569344a3780SDimitry Andric
1570344a3780SDimitry Andric DrawContent(content_surface, is_selected);
1571344a3780SDimitry Andric }
1572344a3780SDimitry Andric
SelectPrevious()1573344a3780SDimitry Andric void SelectPrevious() {
1574344a3780SDimitry Andric if (m_choice > 0)
1575344a3780SDimitry Andric m_choice--;
1576344a3780SDimitry Andric }
1577344a3780SDimitry Andric
SelectNext()1578344a3780SDimitry Andric void SelectNext() {
1579344a3780SDimitry Andric if (m_choice < GetNumberOfChoices() - 1)
1580344a3780SDimitry Andric m_choice++;
1581344a3780SDimitry Andric }
1582344a3780SDimitry Andric
UpdateScrolling()1583344a3780SDimitry Andric void UpdateScrolling() {
1584344a3780SDimitry Andric if (m_choice > GetLastVisibleChoice()) {
1585344a3780SDimitry Andric m_first_visibile_choice = m_choice - (m_number_of_visible_choices - 1);
1586344a3780SDimitry Andric return;
1587344a3780SDimitry Andric }
1588344a3780SDimitry Andric
1589344a3780SDimitry Andric if (m_choice < m_first_visibile_choice)
1590344a3780SDimitry Andric m_first_visibile_choice = m_choice;
1591344a3780SDimitry Andric }
1592344a3780SDimitry Andric
FieldDelegateHandleChar(int key)1593344a3780SDimitry Andric HandleCharResult FieldDelegateHandleChar(int key) override {
1594344a3780SDimitry Andric switch (key) {
1595344a3780SDimitry Andric case KEY_UP:
1596344a3780SDimitry Andric SelectPrevious();
1597344a3780SDimitry Andric return eKeyHandled;
1598344a3780SDimitry Andric case KEY_DOWN:
1599344a3780SDimitry Andric SelectNext();
1600344a3780SDimitry Andric return eKeyHandled;
1601344a3780SDimitry Andric default:
1602344a3780SDimitry Andric break;
1603344a3780SDimitry Andric }
1604344a3780SDimitry Andric return eKeyNotHandled;
1605344a3780SDimitry Andric }
1606344a3780SDimitry Andric
1607344a3780SDimitry Andric // Returns the content of the choice as a string.
GetChoiceContent()1608344a3780SDimitry Andric std::string GetChoiceContent() { return m_choices[m_choice]; }
1609344a3780SDimitry Andric
1610344a3780SDimitry Andric // Returns the index of the choice.
GetChoice()1611344a3780SDimitry Andric int GetChoice() { return m_choice; }
1612344a3780SDimitry Andric
SetChoice(llvm::StringRef choice)1613145449b1SDimitry Andric void SetChoice(llvm::StringRef choice) {
1614344a3780SDimitry Andric for (int i = 0; i < GetNumberOfChoices(); i++) {
1615344a3780SDimitry Andric if (choice == m_choices[i]) {
1616344a3780SDimitry Andric m_choice = i;
1617344a3780SDimitry Andric return;
1618344a3780SDimitry Andric }
1619344a3780SDimitry Andric }
1620344a3780SDimitry Andric }
1621344a3780SDimitry Andric
1622344a3780SDimitry Andric protected:
1623344a3780SDimitry Andric std::string m_label;
1624344a3780SDimitry Andric int m_number_of_visible_choices;
1625344a3780SDimitry Andric std::vector<std::string> m_choices;
1626344a3780SDimitry Andric // The index of the selected choice.
1627145449b1SDimitry Andric int m_choice = 0;
1628344a3780SDimitry Andric // The index of the first visible choice in the field.
1629145449b1SDimitry Andric int m_first_visibile_choice = 0;
1630344a3780SDimitry Andric };
1631344a3780SDimitry Andric
1632344a3780SDimitry Andric class PlatformPluginFieldDelegate : public ChoicesFieldDelegate {
1633344a3780SDimitry Andric public:
PlatformPluginFieldDelegate(Debugger & debugger)1634344a3780SDimitry Andric PlatformPluginFieldDelegate(Debugger &debugger)
1635344a3780SDimitry Andric : ChoicesFieldDelegate("Platform Plugin", 3, GetPossiblePluginNames()) {
1636344a3780SDimitry Andric PlatformSP platform_sp = debugger.GetPlatformList().GetSelectedPlatform();
1637344a3780SDimitry Andric if (platform_sp)
1638145449b1SDimitry Andric SetChoice(platform_sp->GetPluginName());
1639344a3780SDimitry Andric }
1640344a3780SDimitry Andric
GetPossiblePluginNames()1641344a3780SDimitry Andric std::vector<std::string> GetPossiblePluginNames() {
1642344a3780SDimitry Andric std::vector<std::string> names;
1643344a3780SDimitry Andric size_t i = 0;
1644c0981da4SDimitry Andric for (llvm::StringRef name =
1645c0981da4SDimitry Andric PluginManager::GetPlatformPluginNameAtIndex(i++);
1646c0981da4SDimitry Andric !name.empty(); name = PluginManager::GetProcessPluginNameAtIndex(i++))
1647c0981da4SDimitry Andric names.push_back(name.str());
1648344a3780SDimitry Andric return names;
1649344a3780SDimitry Andric }
1650344a3780SDimitry Andric
GetPluginName()1651344a3780SDimitry Andric std::string GetPluginName() {
1652344a3780SDimitry Andric std::string plugin_name = GetChoiceContent();
1653344a3780SDimitry Andric return plugin_name;
1654344a3780SDimitry Andric }
1655344a3780SDimitry Andric };
1656344a3780SDimitry Andric
1657344a3780SDimitry Andric class ProcessPluginFieldDelegate : public ChoicesFieldDelegate {
1658344a3780SDimitry Andric public:
ProcessPluginFieldDelegate()1659344a3780SDimitry Andric ProcessPluginFieldDelegate()
1660344a3780SDimitry Andric : ChoicesFieldDelegate("Process Plugin", 3, GetPossiblePluginNames()) {}
1661344a3780SDimitry Andric
GetPossiblePluginNames()1662344a3780SDimitry Andric std::vector<std::string> GetPossiblePluginNames() {
1663344a3780SDimitry Andric std::vector<std::string> names;
1664344a3780SDimitry Andric names.push_back("<default>");
1665344a3780SDimitry Andric
1666344a3780SDimitry Andric size_t i = 0;
1667c0981da4SDimitry Andric for (llvm::StringRef name = PluginManager::GetProcessPluginNameAtIndex(i++);
1668c0981da4SDimitry Andric !name.empty(); name = PluginManager::GetProcessPluginNameAtIndex(i++))
1669c0981da4SDimitry Andric names.push_back(name.str());
1670344a3780SDimitry Andric return names;
1671344a3780SDimitry Andric }
1672344a3780SDimitry Andric
GetPluginName()1673344a3780SDimitry Andric std::string GetPluginName() {
1674344a3780SDimitry Andric std::string plugin_name = GetChoiceContent();
1675344a3780SDimitry Andric if (plugin_name == "<default>")
1676344a3780SDimitry Andric return "";
1677344a3780SDimitry Andric return plugin_name;
1678344a3780SDimitry Andric }
1679344a3780SDimitry Andric };
1680344a3780SDimitry Andric
1681c0981da4SDimitry Andric class LazyBooleanFieldDelegate : public ChoicesFieldDelegate {
1682c0981da4SDimitry Andric public:
LazyBooleanFieldDelegate(const char * label,const char * calculate_label)1683c0981da4SDimitry Andric LazyBooleanFieldDelegate(const char *label, const char *calculate_label)
1684c0981da4SDimitry Andric : ChoicesFieldDelegate(label, 3, GetPossibleOptions(calculate_label)) {}
1685c0981da4SDimitry Andric
1686c0981da4SDimitry Andric static constexpr const char *kNo = "No";
1687c0981da4SDimitry Andric static constexpr const char *kYes = "Yes";
1688c0981da4SDimitry Andric
GetPossibleOptions(const char * calculate_label)1689c0981da4SDimitry Andric std::vector<std::string> GetPossibleOptions(const char *calculate_label) {
1690c0981da4SDimitry Andric std::vector<std::string> options;
1691c0981da4SDimitry Andric options.push_back(calculate_label);
1692c0981da4SDimitry Andric options.push_back(kYes);
1693c0981da4SDimitry Andric options.push_back(kNo);
1694c0981da4SDimitry Andric return options;
1695c0981da4SDimitry Andric }
1696c0981da4SDimitry Andric
GetLazyBoolean()1697c0981da4SDimitry Andric LazyBool GetLazyBoolean() {
1698c0981da4SDimitry Andric std::string choice = GetChoiceContent();
1699c0981da4SDimitry Andric if (choice == kNo)
1700c0981da4SDimitry Andric return eLazyBoolNo;
1701c0981da4SDimitry Andric else if (choice == kYes)
1702c0981da4SDimitry Andric return eLazyBoolYes;
1703c0981da4SDimitry Andric else
1704c0981da4SDimitry Andric return eLazyBoolCalculate;
1705c0981da4SDimitry Andric }
1706c0981da4SDimitry Andric };
1707c0981da4SDimitry Andric
1708344a3780SDimitry Andric template <class T> class ListFieldDelegate : public FieldDelegate {
1709344a3780SDimitry Andric public:
ListFieldDelegate(const char * label,T default_field)1710344a3780SDimitry Andric ListFieldDelegate(const char *label, T default_field)
1711145449b1SDimitry Andric : m_label(label), m_default_field(default_field),
1712344a3780SDimitry Andric m_selection_type(SelectionType::NewButton) {}
1713344a3780SDimitry Andric
1714344a3780SDimitry Andric // Signify which element is selected. If a field or a remove button is
1715344a3780SDimitry Andric // selected, then m_selection_index signifies the particular field that
1716344a3780SDimitry Andric // is selected or the field that the remove button belongs to.
1717344a3780SDimitry Andric enum class SelectionType { Field, RemoveButton, NewButton };
1718344a3780SDimitry Andric
1719344a3780SDimitry Andric // A List field is drawn as a titled box of a number of other fields of the
1720344a3780SDimitry Andric // same type. Each field has a Remove button next to it that removes the
1721344a3780SDimitry Andric // corresponding field. Finally, the last line contains a New button to add a
1722344a3780SDimitry Andric // new field.
1723344a3780SDimitry Andric //
1724344a3780SDimitry Andric // __[Label]___________
1725344a3780SDimitry Andric // | Field 0 [Remove] |
1726344a3780SDimitry Andric // | Field 1 [Remove] |
1727344a3780SDimitry Andric // | Field 2 [Remove] |
1728344a3780SDimitry Andric // | [New] |
1729344a3780SDimitry Andric // |__________________|
1730344a3780SDimitry Andric
1731344a3780SDimitry Andric // List fields have two lines for border characters, 1 line for the New
1732344a3780SDimitry Andric // button, and the total height of the available fields.
FieldDelegateGetHeight()1733344a3780SDimitry Andric int FieldDelegateGetHeight() override {
1734344a3780SDimitry Andric // 2 border characters.
1735344a3780SDimitry Andric int height = 2;
1736344a3780SDimitry Andric // Total height of the fields.
1737344a3780SDimitry Andric for (int i = 0; i < GetNumberOfFields(); i++) {
1738344a3780SDimitry Andric height += m_fields[i].FieldDelegateGetHeight();
1739344a3780SDimitry Andric }
1740344a3780SDimitry Andric // A line for the New button.
1741344a3780SDimitry Andric height++;
1742344a3780SDimitry Andric return height;
1743344a3780SDimitry Andric }
1744344a3780SDimitry Andric
FieldDelegateGetScrollContext()1745344a3780SDimitry Andric ScrollContext FieldDelegateGetScrollContext() override {
1746344a3780SDimitry Andric int height = FieldDelegateGetHeight();
1747344a3780SDimitry Andric if (m_selection_type == SelectionType::NewButton)
1748344a3780SDimitry Andric return ScrollContext(height - 2, height - 1);
1749344a3780SDimitry Andric
1750344a3780SDimitry Andric FieldDelegate &field = m_fields[m_selection_index];
1751344a3780SDimitry Andric ScrollContext context = field.FieldDelegateGetScrollContext();
1752344a3780SDimitry Andric
1753344a3780SDimitry Andric // Start at 1 because of the top border.
1754344a3780SDimitry Andric int offset = 1;
1755344a3780SDimitry Andric for (int i = 0; i < m_selection_index; i++) {
1756344a3780SDimitry Andric offset += m_fields[i].FieldDelegateGetHeight();
1757344a3780SDimitry Andric }
1758344a3780SDimitry Andric context.Offset(offset);
1759344a3780SDimitry Andric
1760344a3780SDimitry Andric // If the scroll context is touching the top border, include it in the
1761344a3780SDimitry Andric // context to show the label.
1762344a3780SDimitry Andric if (context.start == 1)
1763344a3780SDimitry Andric context.start--;
1764344a3780SDimitry Andric
1765344a3780SDimitry Andric // If the scroll context is touching the new button, include it as well as
1766344a3780SDimitry Andric // the bottom border in the context.
1767344a3780SDimitry Andric if (context.end == height - 3)
1768344a3780SDimitry Andric context.end += 2;
1769344a3780SDimitry Andric
1770344a3780SDimitry Andric return context;
1771344a3780SDimitry Andric }
1772344a3780SDimitry Andric
DrawRemoveButton(Surface & surface,int highlight)1773c0981da4SDimitry Andric void DrawRemoveButton(Surface &surface, int highlight) {
1774344a3780SDimitry Andric surface.MoveCursor(1, surface.GetHeight() / 2);
1775344a3780SDimitry Andric if (highlight)
1776344a3780SDimitry Andric surface.AttributeOn(A_REVERSE);
1777344a3780SDimitry Andric surface.PutCString("[Remove]");
1778344a3780SDimitry Andric if (highlight)
1779344a3780SDimitry Andric surface.AttributeOff(A_REVERSE);
1780344a3780SDimitry Andric }
1781344a3780SDimitry Andric
DrawFields(Surface & surface,bool is_selected)1782c0981da4SDimitry Andric void DrawFields(Surface &surface, bool is_selected) {
1783344a3780SDimitry Andric int line = 0;
1784344a3780SDimitry Andric int width = surface.GetWidth();
1785344a3780SDimitry Andric for (int i = 0; i < GetNumberOfFields(); i++) {
1786344a3780SDimitry Andric int height = m_fields[i].FieldDelegateGetHeight();
1787344a3780SDimitry Andric Rect bounds = Rect(Point(0, line), Size(width, height));
1788344a3780SDimitry Andric Rect field_bounds, remove_button_bounds;
1789344a3780SDimitry Andric bounds.VerticalSplit(bounds.size.width - sizeof(" [Remove]"),
1790344a3780SDimitry Andric field_bounds, remove_button_bounds);
1791c0981da4SDimitry Andric Surface field_surface = surface.SubSurface(field_bounds);
1792c0981da4SDimitry Andric Surface remove_button_surface = surface.SubSurface(remove_button_bounds);
1793344a3780SDimitry Andric
1794344a3780SDimitry Andric bool is_element_selected = m_selection_index == i && is_selected;
1795344a3780SDimitry Andric bool is_field_selected =
1796344a3780SDimitry Andric is_element_selected && m_selection_type == SelectionType::Field;
1797344a3780SDimitry Andric bool is_remove_button_selected =
1798344a3780SDimitry Andric is_element_selected &&
1799344a3780SDimitry Andric m_selection_type == SelectionType::RemoveButton;
1800344a3780SDimitry Andric m_fields[i].FieldDelegateDraw(field_surface, is_field_selected);
1801344a3780SDimitry Andric DrawRemoveButton(remove_button_surface, is_remove_button_selected);
1802344a3780SDimitry Andric
1803344a3780SDimitry Andric line += height;
1804344a3780SDimitry Andric }
1805344a3780SDimitry Andric }
1806344a3780SDimitry Andric
DrawNewButton(Surface & surface,bool is_selected)1807c0981da4SDimitry Andric void DrawNewButton(Surface &surface, bool is_selected) {
1808344a3780SDimitry Andric const char *button_text = "[New]";
1809344a3780SDimitry Andric int x = (surface.GetWidth() - sizeof(button_text) - 1) / 2;
1810344a3780SDimitry Andric surface.MoveCursor(x, 0);
1811344a3780SDimitry Andric bool highlight =
1812344a3780SDimitry Andric is_selected && m_selection_type == SelectionType::NewButton;
1813344a3780SDimitry Andric if (highlight)
1814344a3780SDimitry Andric surface.AttributeOn(A_REVERSE);
1815344a3780SDimitry Andric surface.PutCString(button_text);
1816344a3780SDimitry Andric if (highlight)
1817344a3780SDimitry Andric surface.AttributeOff(A_REVERSE);
1818344a3780SDimitry Andric }
1819344a3780SDimitry Andric
FieldDelegateDraw(Surface & surface,bool is_selected)1820c0981da4SDimitry Andric void FieldDelegateDraw(Surface &surface, bool is_selected) override {
1821344a3780SDimitry Andric surface.TitledBox(m_label.c_str());
1822344a3780SDimitry Andric
1823344a3780SDimitry Andric Rect content_bounds = surface.GetFrame();
1824344a3780SDimitry Andric content_bounds.Inset(1, 1);
1825344a3780SDimitry Andric Rect fields_bounds, new_button_bounds;
1826344a3780SDimitry Andric content_bounds.HorizontalSplit(content_bounds.size.height - 1,
1827344a3780SDimitry Andric fields_bounds, new_button_bounds);
1828c0981da4SDimitry Andric Surface fields_surface = surface.SubSurface(fields_bounds);
1829c0981da4SDimitry Andric Surface new_button_surface = surface.SubSurface(new_button_bounds);
1830344a3780SDimitry Andric
1831344a3780SDimitry Andric DrawFields(fields_surface, is_selected);
1832344a3780SDimitry Andric DrawNewButton(new_button_surface, is_selected);
1833344a3780SDimitry Andric }
1834344a3780SDimitry Andric
AddNewField()1835344a3780SDimitry Andric void AddNewField() {
1836344a3780SDimitry Andric m_fields.push_back(m_default_field);
1837344a3780SDimitry Andric m_selection_index = GetNumberOfFields() - 1;
1838344a3780SDimitry Andric m_selection_type = SelectionType::Field;
1839344a3780SDimitry Andric FieldDelegate &field = m_fields[m_selection_index];
1840344a3780SDimitry Andric field.FieldDelegateSelectFirstElement();
1841344a3780SDimitry Andric }
1842344a3780SDimitry Andric
RemoveField()1843344a3780SDimitry Andric void RemoveField() {
1844344a3780SDimitry Andric m_fields.erase(m_fields.begin() + m_selection_index);
1845344a3780SDimitry Andric if (m_selection_index != 0)
1846344a3780SDimitry Andric m_selection_index--;
1847344a3780SDimitry Andric
1848344a3780SDimitry Andric if (GetNumberOfFields() > 0) {
1849344a3780SDimitry Andric m_selection_type = SelectionType::Field;
1850344a3780SDimitry Andric FieldDelegate &field = m_fields[m_selection_index];
1851344a3780SDimitry Andric field.FieldDelegateSelectFirstElement();
1852344a3780SDimitry Andric } else
1853344a3780SDimitry Andric m_selection_type = SelectionType::NewButton;
1854344a3780SDimitry Andric }
1855344a3780SDimitry Andric
SelectNext(int key)1856344a3780SDimitry Andric HandleCharResult SelectNext(int key) {
1857344a3780SDimitry Andric if (m_selection_type == SelectionType::NewButton)
1858344a3780SDimitry Andric return eKeyNotHandled;
1859344a3780SDimitry Andric
1860344a3780SDimitry Andric if (m_selection_type == SelectionType::RemoveButton) {
1861344a3780SDimitry Andric if (m_selection_index == GetNumberOfFields() - 1) {
1862344a3780SDimitry Andric m_selection_type = SelectionType::NewButton;
1863344a3780SDimitry Andric return eKeyHandled;
1864344a3780SDimitry Andric }
1865344a3780SDimitry Andric m_selection_index++;
1866344a3780SDimitry Andric m_selection_type = SelectionType::Field;
1867344a3780SDimitry Andric FieldDelegate &next_field = m_fields[m_selection_index];
1868344a3780SDimitry Andric next_field.FieldDelegateSelectFirstElement();
1869344a3780SDimitry Andric return eKeyHandled;
1870344a3780SDimitry Andric }
1871344a3780SDimitry Andric
1872344a3780SDimitry Andric FieldDelegate &field = m_fields[m_selection_index];
1873344a3780SDimitry Andric if (!field.FieldDelegateOnLastOrOnlyElement()) {
1874344a3780SDimitry Andric return field.FieldDelegateHandleChar(key);
1875344a3780SDimitry Andric }
1876344a3780SDimitry Andric
1877344a3780SDimitry Andric field.FieldDelegateExitCallback();
1878344a3780SDimitry Andric
1879344a3780SDimitry Andric m_selection_type = SelectionType::RemoveButton;
1880344a3780SDimitry Andric return eKeyHandled;
1881344a3780SDimitry Andric }
1882344a3780SDimitry Andric
SelectPrevious(int key)1883344a3780SDimitry Andric HandleCharResult SelectPrevious(int key) {
1884344a3780SDimitry Andric if (FieldDelegateOnFirstOrOnlyElement())
1885344a3780SDimitry Andric return eKeyNotHandled;
1886344a3780SDimitry Andric
1887344a3780SDimitry Andric if (m_selection_type == SelectionType::RemoveButton) {
1888344a3780SDimitry Andric m_selection_type = SelectionType::Field;
1889344a3780SDimitry Andric FieldDelegate &field = m_fields[m_selection_index];
1890344a3780SDimitry Andric field.FieldDelegateSelectLastElement();
1891344a3780SDimitry Andric return eKeyHandled;
1892344a3780SDimitry Andric }
1893344a3780SDimitry Andric
1894344a3780SDimitry Andric if (m_selection_type == SelectionType::NewButton) {
1895344a3780SDimitry Andric m_selection_type = SelectionType::RemoveButton;
1896344a3780SDimitry Andric m_selection_index = GetNumberOfFields() - 1;
1897344a3780SDimitry Andric return eKeyHandled;
1898344a3780SDimitry Andric }
1899344a3780SDimitry Andric
1900344a3780SDimitry Andric FieldDelegate &field = m_fields[m_selection_index];
1901344a3780SDimitry Andric if (!field.FieldDelegateOnFirstOrOnlyElement()) {
1902344a3780SDimitry Andric return field.FieldDelegateHandleChar(key);
1903344a3780SDimitry Andric }
1904344a3780SDimitry Andric
1905344a3780SDimitry Andric field.FieldDelegateExitCallback();
1906344a3780SDimitry Andric
1907344a3780SDimitry Andric m_selection_type = SelectionType::RemoveButton;
1908344a3780SDimitry Andric m_selection_index--;
1909344a3780SDimitry Andric return eKeyHandled;
1910344a3780SDimitry Andric }
1911344a3780SDimitry Andric
1912c0981da4SDimitry Andric // If the last element of the field is selected and it didn't handle the key.
1913c0981da4SDimitry Andric // Select the next field or new button if the selected field is the last one.
SelectNextInList(int key)1914c0981da4SDimitry Andric HandleCharResult SelectNextInList(int key) {
1915c0981da4SDimitry Andric assert(m_selection_type == SelectionType::Field);
1916c0981da4SDimitry Andric
1917c0981da4SDimitry Andric FieldDelegate &field = m_fields[m_selection_index];
1918c0981da4SDimitry Andric if (field.FieldDelegateHandleChar(key) == eKeyHandled)
1919c0981da4SDimitry Andric return eKeyHandled;
1920c0981da4SDimitry Andric
1921c0981da4SDimitry Andric if (!field.FieldDelegateOnLastOrOnlyElement())
1922c0981da4SDimitry Andric return eKeyNotHandled;
1923c0981da4SDimitry Andric
1924c0981da4SDimitry Andric field.FieldDelegateExitCallback();
1925c0981da4SDimitry Andric
1926c0981da4SDimitry Andric if (m_selection_index == GetNumberOfFields() - 1) {
1927c0981da4SDimitry Andric m_selection_type = SelectionType::NewButton;
1928c0981da4SDimitry Andric return eKeyHandled;
1929c0981da4SDimitry Andric }
1930c0981da4SDimitry Andric
1931c0981da4SDimitry Andric m_selection_index++;
1932c0981da4SDimitry Andric FieldDelegate &next_field = m_fields[m_selection_index];
1933c0981da4SDimitry Andric next_field.FieldDelegateSelectFirstElement();
1934c0981da4SDimitry Andric return eKeyHandled;
1935c0981da4SDimitry Andric }
1936c0981da4SDimitry Andric
FieldDelegateHandleChar(int key)1937344a3780SDimitry Andric HandleCharResult FieldDelegateHandleChar(int key) override {
1938344a3780SDimitry Andric switch (key) {
1939344a3780SDimitry Andric case '\r':
1940344a3780SDimitry Andric case '\n':
1941344a3780SDimitry Andric case KEY_ENTER:
1942344a3780SDimitry Andric switch (m_selection_type) {
1943344a3780SDimitry Andric case SelectionType::NewButton:
1944344a3780SDimitry Andric AddNewField();
1945344a3780SDimitry Andric return eKeyHandled;
1946344a3780SDimitry Andric case SelectionType::RemoveButton:
1947344a3780SDimitry Andric RemoveField();
1948344a3780SDimitry Andric return eKeyHandled;
1949c0981da4SDimitry Andric case SelectionType::Field:
1950c0981da4SDimitry Andric return SelectNextInList(key);
1951344a3780SDimitry Andric }
1952344a3780SDimitry Andric break;
1953344a3780SDimitry Andric case '\t':
1954c0981da4SDimitry Andric return SelectNext(key);
1955344a3780SDimitry Andric case KEY_SHIFT_TAB:
1956c0981da4SDimitry Andric return SelectPrevious(key);
1957344a3780SDimitry Andric default:
1958344a3780SDimitry Andric break;
1959344a3780SDimitry Andric }
1960344a3780SDimitry Andric
1961344a3780SDimitry Andric // If the key wasn't handled and one of the fields is selected, pass the key
1962344a3780SDimitry Andric // to that field.
1963344a3780SDimitry Andric if (m_selection_type == SelectionType::Field) {
1964344a3780SDimitry Andric return m_fields[m_selection_index].FieldDelegateHandleChar(key);
1965344a3780SDimitry Andric }
1966344a3780SDimitry Andric
1967344a3780SDimitry Andric return eKeyNotHandled;
1968344a3780SDimitry Andric }
1969344a3780SDimitry Andric
FieldDelegateOnLastOrOnlyElement()1970344a3780SDimitry Andric bool FieldDelegateOnLastOrOnlyElement() override {
1971344a3780SDimitry Andric if (m_selection_type == SelectionType::NewButton) {
1972344a3780SDimitry Andric return true;
1973344a3780SDimitry Andric }
1974344a3780SDimitry Andric return false;
1975344a3780SDimitry Andric }
1976344a3780SDimitry Andric
FieldDelegateOnFirstOrOnlyElement()1977344a3780SDimitry Andric bool FieldDelegateOnFirstOrOnlyElement() override {
1978344a3780SDimitry Andric if (m_selection_type == SelectionType::NewButton &&
1979344a3780SDimitry Andric GetNumberOfFields() == 0)
1980344a3780SDimitry Andric return true;
1981344a3780SDimitry Andric
1982344a3780SDimitry Andric if (m_selection_type == SelectionType::Field && m_selection_index == 0) {
1983344a3780SDimitry Andric FieldDelegate &field = m_fields[m_selection_index];
1984344a3780SDimitry Andric return field.FieldDelegateOnFirstOrOnlyElement();
1985344a3780SDimitry Andric }
1986344a3780SDimitry Andric
1987344a3780SDimitry Andric return false;
1988344a3780SDimitry Andric }
1989344a3780SDimitry Andric
FieldDelegateSelectFirstElement()1990344a3780SDimitry Andric void FieldDelegateSelectFirstElement() override {
1991344a3780SDimitry Andric if (GetNumberOfFields() == 0) {
1992344a3780SDimitry Andric m_selection_type = SelectionType::NewButton;
1993344a3780SDimitry Andric return;
1994344a3780SDimitry Andric }
1995344a3780SDimitry Andric
1996344a3780SDimitry Andric m_selection_type = SelectionType::Field;
1997344a3780SDimitry Andric m_selection_index = 0;
1998344a3780SDimitry Andric }
1999344a3780SDimitry Andric
FieldDelegateSelectLastElement()2000344a3780SDimitry Andric void FieldDelegateSelectLastElement() override {
2001344a3780SDimitry Andric m_selection_type = SelectionType::NewButton;
2002344a3780SDimitry Andric }
2003344a3780SDimitry Andric
GetNumberOfFields()2004344a3780SDimitry Andric int GetNumberOfFields() { return m_fields.size(); }
2005344a3780SDimitry Andric
2006344a3780SDimitry Andric // Returns the form delegate at the current index.
GetField(int index)2007344a3780SDimitry Andric T &GetField(int index) { return m_fields[index]; }
2008344a3780SDimitry Andric
2009344a3780SDimitry Andric protected:
2010344a3780SDimitry Andric std::string m_label;
2011344a3780SDimitry Andric // The default field delegate instance from which new field delegates will be
2012344a3780SDimitry Andric // created though a copy.
2013344a3780SDimitry Andric T m_default_field;
2014344a3780SDimitry Andric std::vector<T> m_fields;
2015145449b1SDimitry Andric int m_selection_index = 0;
2016344a3780SDimitry Andric // See SelectionType class enum.
2017344a3780SDimitry Andric SelectionType m_selection_type;
2018344a3780SDimitry Andric };
2019344a3780SDimitry Andric
2020c0981da4SDimitry Andric class ArgumentsFieldDelegate : public ListFieldDelegate<TextFieldDelegate> {
2021c0981da4SDimitry Andric public:
ArgumentsFieldDelegate()2022c0981da4SDimitry Andric ArgumentsFieldDelegate()
2023c0981da4SDimitry Andric : ListFieldDelegate("Arguments",
2024c0981da4SDimitry Andric TextFieldDelegate("Argument", "", false)) {}
2025c0981da4SDimitry Andric
GetArguments()2026c0981da4SDimitry Andric Args GetArguments() {
2027c0981da4SDimitry Andric Args arguments;
2028c0981da4SDimitry Andric for (int i = 0; i < GetNumberOfFields(); i++) {
2029c0981da4SDimitry Andric arguments.AppendArgument(GetField(i).GetText());
2030c0981da4SDimitry Andric }
2031c0981da4SDimitry Andric return arguments;
2032c0981da4SDimitry Andric }
2033c0981da4SDimitry Andric
AddArguments(const Args & arguments)2034c0981da4SDimitry Andric void AddArguments(const Args &arguments) {
2035c0981da4SDimitry Andric for (size_t i = 0; i < arguments.GetArgumentCount(); i++) {
2036c0981da4SDimitry Andric AddNewField();
2037c0981da4SDimitry Andric TextFieldDelegate &field = GetField(GetNumberOfFields() - 1);
2038c0981da4SDimitry Andric field.SetText(arguments.GetArgumentAtIndex(i));
2039c0981da4SDimitry Andric }
2040c0981da4SDimitry Andric }
2041c0981da4SDimitry Andric };
2042c0981da4SDimitry Andric
2043c0981da4SDimitry Andric template <class KeyFieldDelegateType, class ValueFieldDelegateType>
2044c0981da4SDimitry Andric class MappingFieldDelegate : public FieldDelegate {
2045c0981da4SDimitry Andric public:
MappingFieldDelegate(KeyFieldDelegateType key_field,ValueFieldDelegateType value_field)2046c0981da4SDimitry Andric MappingFieldDelegate(KeyFieldDelegateType key_field,
2047c0981da4SDimitry Andric ValueFieldDelegateType value_field)
2048c0981da4SDimitry Andric : m_key_field(key_field), m_value_field(value_field),
2049c0981da4SDimitry Andric m_selection_type(SelectionType::Key) {}
2050c0981da4SDimitry Andric
2051c0981da4SDimitry Andric // Signify which element is selected. The key field or its value field.
2052c0981da4SDimitry Andric enum class SelectionType { Key, Value };
2053c0981da4SDimitry Andric
2054c0981da4SDimitry Andric // A mapping field is drawn as two text fields with a right arrow in between.
2055c0981da4SDimitry Andric // The first field stores the key of the mapping and the second stores the
2056c0981da4SDimitry Andric // value if the mapping.
2057c0981da4SDimitry Andric //
2058c0981da4SDimitry Andric // __[Key]_____________ __[Value]___________
2059c0981da4SDimitry Andric // | | > | |
2060c0981da4SDimitry Andric // |__________________| |__________________|
2061c0981da4SDimitry Andric // - Error message if it exists.
2062c0981da4SDimitry Andric
2063c0981da4SDimitry Andric // The mapping field has a height that is equal to the maximum height between
2064c0981da4SDimitry Andric // the key and value fields.
FieldDelegateGetHeight()2065c0981da4SDimitry Andric int FieldDelegateGetHeight() override {
2066c0981da4SDimitry Andric return std::max(m_key_field.FieldDelegateGetHeight(),
2067c0981da4SDimitry Andric m_value_field.FieldDelegateGetHeight());
2068c0981da4SDimitry Andric }
2069c0981da4SDimitry Andric
DrawArrow(Surface & surface)2070c0981da4SDimitry Andric void DrawArrow(Surface &surface) {
2071c0981da4SDimitry Andric surface.MoveCursor(0, 1);
2072c0981da4SDimitry Andric surface.PutChar(ACS_RARROW);
2073c0981da4SDimitry Andric }
2074c0981da4SDimitry Andric
FieldDelegateDraw(Surface & surface,bool is_selected)2075c0981da4SDimitry Andric void FieldDelegateDraw(Surface &surface, bool is_selected) override {
2076c0981da4SDimitry Andric Rect bounds = surface.GetFrame();
2077c0981da4SDimitry Andric Rect key_field_bounds, arrow_and_value_field_bounds;
2078c0981da4SDimitry Andric bounds.VerticalSplit(bounds.size.width / 2, key_field_bounds,
2079c0981da4SDimitry Andric arrow_and_value_field_bounds);
2080c0981da4SDimitry Andric Rect arrow_bounds, value_field_bounds;
2081c0981da4SDimitry Andric arrow_and_value_field_bounds.VerticalSplit(1, arrow_bounds,
2082c0981da4SDimitry Andric value_field_bounds);
2083c0981da4SDimitry Andric
2084c0981da4SDimitry Andric Surface key_field_surface = surface.SubSurface(key_field_bounds);
2085c0981da4SDimitry Andric Surface arrow_surface = surface.SubSurface(arrow_bounds);
2086c0981da4SDimitry Andric Surface value_field_surface = surface.SubSurface(value_field_bounds);
2087c0981da4SDimitry Andric
2088c0981da4SDimitry Andric bool key_is_selected =
2089c0981da4SDimitry Andric m_selection_type == SelectionType::Key && is_selected;
2090c0981da4SDimitry Andric m_key_field.FieldDelegateDraw(key_field_surface, key_is_selected);
2091c0981da4SDimitry Andric DrawArrow(arrow_surface);
2092c0981da4SDimitry Andric bool value_is_selected =
2093c0981da4SDimitry Andric m_selection_type == SelectionType::Value && is_selected;
2094c0981da4SDimitry Andric m_value_field.FieldDelegateDraw(value_field_surface, value_is_selected);
2095c0981da4SDimitry Andric }
2096c0981da4SDimitry Andric
SelectNext(int key)2097c0981da4SDimitry Andric HandleCharResult SelectNext(int key) {
2098c0981da4SDimitry Andric if (FieldDelegateOnLastOrOnlyElement())
2099c0981da4SDimitry Andric return eKeyNotHandled;
2100c0981da4SDimitry Andric
2101c0981da4SDimitry Andric if (!m_key_field.FieldDelegateOnLastOrOnlyElement()) {
2102c0981da4SDimitry Andric return m_key_field.FieldDelegateHandleChar(key);
2103c0981da4SDimitry Andric }
2104c0981da4SDimitry Andric
2105c0981da4SDimitry Andric m_key_field.FieldDelegateExitCallback();
2106c0981da4SDimitry Andric m_selection_type = SelectionType::Value;
2107c0981da4SDimitry Andric m_value_field.FieldDelegateSelectFirstElement();
2108c0981da4SDimitry Andric return eKeyHandled;
2109c0981da4SDimitry Andric }
2110c0981da4SDimitry Andric
SelectPrevious(int key)2111c0981da4SDimitry Andric HandleCharResult SelectPrevious(int key) {
2112c0981da4SDimitry Andric if (FieldDelegateOnFirstOrOnlyElement())
2113c0981da4SDimitry Andric return eKeyNotHandled;
2114c0981da4SDimitry Andric
2115c0981da4SDimitry Andric if (!m_value_field.FieldDelegateOnFirstOrOnlyElement()) {
2116c0981da4SDimitry Andric return m_value_field.FieldDelegateHandleChar(key);
2117c0981da4SDimitry Andric }
2118c0981da4SDimitry Andric
2119c0981da4SDimitry Andric m_value_field.FieldDelegateExitCallback();
2120c0981da4SDimitry Andric m_selection_type = SelectionType::Key;
2121c0981da4SDimitry Andric m_key_field.FieldDelegateSelectLastElement();
2122c0981da4SDimitry Andric return eKeyHandled;
2123c0981da4SDimitry Andric }
2124c0981da4SDimitry Andric
2125c0981da4SDimitry Andric // If the value field is selected, pass the key to it. If the key field is
2126c0981da4SDimitry Andric // selected, its last element is selected, and it didn't handle the key, then
2127c0981da4SDimitry Andric // select its corresponding value field.
SelectNextField(int key)2128c0981da4SDimitry Andric HandleCharResult SelectNextField(int key) {
2129c0981da4SDimitry Andric if (m_selection_type == SelectionType::Value) {
2130c0981da4SDimitry Andric return m_value_field.FieldDelegateHandleChar(key);
2131c0981da4SDimitry Andric }
2132c0981da4SDimitry Andric
2133c0981da4SDimitry Andric if (m_key_field.FieldDelegateHandleChar(key) == eKeyHandled)
2134c0981da4SDimitry Andric return eKeyHandled;
2135c0981da4SDimitry Andric
2136c0981da4SDimitry Andric if (!m_key_field.FieldDelegateOnLastOrOnlyElement())
2137c0981da4SDimitry Andric return eKeyNotHandled;
2138c0981da4SDimitry Andric
2139c0981da4SDimitry Andric m_key_field.FieldDelegateExitCallback();
2140c0981da4SDimitry Andric m_selection_type = SelectionType::Value;
2141c0981da4SDimitry Andric m_value_field.FieldDelegateSelectFirstElement();
2142c0981da4SDimitry Andric return eKeyHandled;
2143c0981da4SDimitry Andric }
2144c0981da4SDimitry Andric
FieldDelegateHandleChar(int key)2145c0981da4SDimitry Andric HandleCharResult FieldDelegateHandleChar(int key) override {
2146c0981da4SDimitry Andric switch (key) {
2147c0981da4SDimitry Andric case KEY_RETURN:
2148c0981da4SDimitry Andric return SelectNextField(key);
2149c0981da4SDimitry Andric case '\t':
2150c0981da4SDimitry Andric return SelectNext(key);
2151c0981da4SDimitry Andric case KEY_SHIFT_TAB:
2152c0981da4SDimitry Andric return SelectPrevious(key);
2153c0981da4SDimitry Andric default:
2154c0981da4SDimitry Andric break;
2155c0981da4SDimitry Andric }
2156c0981da4SDimitry Andric
2157c0981da4SDimitry Andric // If the key wasn't handled, pass the key to the selected field.
2158c0981da4SDimitry Andric if (m_selection_type == SelectionType::Key)
2159c0981da4SDimitry Andric return m_key_field.FieldDelegateHandleChar(key);
2160c0981da4SDimitry Andric else
2161c0981da4SDimitry Andric return m_value_field.FieldDelegateHandleChar(key);
2162c0981da4SDimitry Andric
2163c0981da4SDimitry Andric return eKeyNotHandled;
2164c0981da4SDimitry Andric }
2165c0981da4SDimitry Andric
FieldDelegateOnFirstOrOnlyElement()2166c0981da4SDimitry Andric bool FieldDelegateOnFirstOrOnlyElement() override {
2167c0981da4SDimitry Andric return m_selection_type == SelectionType::Key;
2168c0981da4SDimitry Andric }
2169c0981da4SDimitry Andric
FieldDelegateOnLastOrOnlyElement()2170c0981da4SDimitry Andric bool FieldDelegateOnLastOrOnlyElement() override {
2171c0981da4SDimitry Andric return m_selection_type == SelectionType::Value;
2172c0981da4SDimitry Andric }
2173c0981da4SDimitry Andric
FieldDelegateSelectFirstElement()2174c0981da4SDimitry Andric void FieldDelegateSelectFirstElement() override {
2175c0981da4SDimitry Andric m_selection_type = SelectionType::Key;
2176c0981da4SDimitry Andric }
2177c0981da4SDimitry Andric
FieldDelegateSelectLastElement()2178c0981da4SDimitry Andric void FieldDelegateSelectLastElement() override {
2179c0981da4SDimitry Andric m_selection_type = SelectionType::Value;
2180c0981da4SDimitry Andric }
2181c0981da4SDimitry Andric
FieldDelegateHasError()2182c0981da4SDimitry Andric bool FieldDelegateHasError() override {
2183c0981da4SDimitry Andric return m_key_field.FieldDelegateHasError() ||
2184c0981da4SDimitry Andric m_value_field.FieldDelegateHasError();
2185c0981da4SDimitry Andric }
2186c0981da4SDimitry Andric
GetKeyField()2187c0981da4SDimitry Andric KeyFieldDelegateType &GetKeyField() { return m_key_field; }
2188c0981da4SDimitry Andric
GetValueField()2189c0981da4SDimitry Andric ValueFieldDelegateType &GetValueField() { return m_value_field; }
2190c0981da4SDimitry Andric
2191c0981da4SDimitry Andric protected:
2192c0981da4SDimitry Andric KeyFieldDelegateType m_key_field;
2193c0981da4SDimitry Andric ValueFieldDelegateType m_value_field;
2194c0981da4SDimitry Andric // See SelectionType class enum.
2195c0981da4SDimitry Andric SelectionType m_selection_type;
2196c0981da4SDimitry Andric };
2197c0981da4SDimitry Andric
2198c0981da4SDimitry Andric class EnvironmentVariableNameFieldDelegate : public TextFieldDelegate {
2199c0981da4SDimitry Andric public:
EnvironmentVariableNameFieldDelegate(const char * content)2200c0981da4SDimitry Andric EnvironmentVariableNameFieldDelegate(const char *content)
2201c0981da4SDimitry Andric : TextFieldDelegate("Name", content, true) {}
2202c0981da4SDimitry Andric
2203c0981da4SDimitry Andric // Environment variable names can't contain an equal sign.
IsAcceptableChar(int key)2204c0981da4SDimitry Andric bool IsAcceptableChar(int key) override {
2205c0981da4SDimitry Andric return TextFieldDelegate::IsAcceptableChar(key) && key != '=';
2206c0981da4SDimitry Andric }
2207c0981da4SDimitry Andric
GetName()2208c0981da4SDimitry Andric const std::string &GetName() { return m_content; }
2209c0981da4SDimitry Andric };
2210c0981da4SDimitry Andric
2211c0981da4SDimitry Andric class EnvironmentVariableFieldDelegate
2212c0981da4SDimitry Andric : public MappingFieldDelegate<EnvironmentVariableNameFieldDelegate,
2213c0981da4SDimitry Andric TextFieldDelegate> {
2214c0981da4SDimitry Andric public:
EnvironmentVariableFieldDelegate()2215c0981da4SDimitry Andric EnvironmentVariableFieldDelegate()
2216c0981da4SDimitry Andric : MappingFieldDelegate(
2217c0981da4SDimitry Andric EnvironmentVariableNameFieldDelegate(""),
2218c0981da4SDimitry Andric TextFieldDelegate("Value", "", /*required=*/false)) {}
2219c0981da4SDimitry Andric
GetName()2220c0981da4SDimitry Andric const std::string &GetName() { return GetKeyField().GetName(); }
2221c0981da4SDimitry Andric
GetValue()2222c0981da4SDimitry Andric const std::string &GetValue() { return GetValueField().GetText(); }
2223c0981da4SDimitry Andric
SetName(const char * name)2224c0981da4SDimitry Andric void SetName(const char *name) { return GetKeyField().SetText(name); }
2225c0981da4SDimitry Andric
SetValue(const char * value)2226c0981da4SDimitry Andric void SetValue(const char *value) { return GetValueField().SetText(value); }
2227c0981da4SDimitry Andric };
2228c0981da4SDimitry Andric
2229c0981da4SDimitry Andric class EnvironmentVariableListFieldDelegate
2230c0981da4SDimitry Andric : public ListFieldDelegate<EnvironmentVariableFieldDelegate> {
2231c0981da4SDimitry Andric public:
EnvironmentVariableListFieldDelegate(const char * label)2232c0981da4SDimitry Andric EnvironmentVariableListFieldDelegate(const char *label)
2233c0981da4SDimitry Andric : ListFieldDelegate(label, EnvironmentVariableFieldDelegate()) {}
2234c0981da4SDimitry Andric
GetEnvironment()2235c0981da4SDimitry Andric Environment GetEnvironment() {
2236c0981da4SDimitry Andric Environment environment;
2237c0981da4SDimitry Andric for (int i = 0; i < GetNumberOfFields(); i++) {
2238c0981da4SDimitry Andric environment.insert(
2239c0981da4SDimitry Andric std::make_pair(GetField(i).GetName(), GetField(i).GetValue()));
2240c0981da4SDimitry Andric }
2241c0981da4SDimitry Andric return environment;
2242c0981da4SDimitry Andric }
2243c0981da4SDimitry Andric
AddEnvironmentVariables(const Environment & environment)2244c0981da4SDimitry Andric void AddEnvironmentVariables(const Environment &environment) {
2245c0981da4SDimitry Andric for (auto &variable : environment) {
2246c0981da4SDimitry Andric AddNewField();
2247c0981da4SDimitry Andric EnvironmentVariableFieldDelegate &field =
2248c0981da4SDimitry Andric GetField(GetNumberOfFields() - 1);
2249c0981da4SDimitry Andric field.SetName(variable.getKey().str().c_str());
2250c0981da4SDimitry Andric field.SetValue(variable.getValue().c_str());
2251c0981da4SDimitry Andric }
2252c0981da4SDimitry Andric }
2253c0981da4SDimitry Andric };
2254c0981da4SDimitry Andric
2255344a3780SDimitry Andric class FormAction {
2256344a3780SDimitry Andric public:
FormAction(const char * label,std::function<void (Window &)> action)2257344a3780SDimitry Andric FormAction(const char *label, std::function<void(Window &)> action)
2258344a3780SDimitry Andric : m_action(action) {
2259344a3780SDimitry Andric if (label)
2260344a3780SDimitry Andric m_label = label;
2261344a3780SDimitry Andric }
2262344a3780SDimitry Andric
2263344a3780SDimitry Andric // Draw a centered [Label].
Draw(Surface & surface,bool is_selected)2264c0981da4SDimitry Andric void Draw(Surface &surface, bool is_selected) {
2265344a3780SDimitry Andric int x = (surface.GetWidth() - m_label.length()) / 2;
2266344a3780SDimitry Andric surface.MoveCursor(x, 0);
2267344a3780SDimitry Andric if (is_selected)
2268344a3780SDimitry Andric surface.AttributeOn(A_REVERSE);
2269344a3780SDimitry Andric surface.PutChar('[');
2270344a3780SDimitry Andric surface.PutCString(m_label.c_str());
2271344a3780SDimitry Andric surface.PutChar(']');
2272344a3780SDimitry Andric if (is_selected)
2273344a3780SDimitry Andric surface.AttributeOff(A_REVERSE);
2274344a3780SDimitry Andric }
2275344a3780SDimitry Andric
Execute(Window & window)2276344a3780SDimitry Andric void Execute(Window &window) { m_action(window); }
2277344a3780SDimitry Andric
GetLabel()2278344a3780SDimitry Andric const std::string &GetLabel() { return m_label; }
2279344a3780SDimitry Andric
2280344a3780SDimitry Andric protected:
2281344a3780SDimitry Andric std::string m_label;
2282344a3780SDimitry Andric std::function<void(Window &)> m_action;
2283344a3780SDimitry Andric };
2284344a3780SDimitry Andric
2285344a3780SDimitry Andric class FormDelegate {
2286344a3780SDimitry Andric public:
2287145449b1SDimitry Andric FormDelegate() = default;
2288344a3780SDimitry Andric
2289344a3780SDimitry Andric virtual ~FormDelegate() = default;
2290344a3780SDimitry Andric
2291344a3780SDimitry Andric virtual std::string GetName() = 0;
2292344a3780SDimitry Andric
UpdateFieldsVisibility()229377fc4c14SDimitry Andric virtual void UpdateFieldsVisibility() {}
2294344a3780SDimitry Andric
GetField(uint32_t field_index)2295344a3780SDimitry Andric FieldDelegate *GetField(uint32_t field_index) {
2296344a3780SDimitry Andric if (field_index < m_fields.size())
2297344a3780SDimitry Andric return m_fields[field_index].get();
2298344a3780SDimitry Andric return nullptr;
2299344a3780SDimitry Andric }
2300344a3780SDimitry Andric
GetAction(int action_index)2301344a3780SDimitry Andric FormAction &GetAction(int action_index) { return m_actions[action_index]; }
2302344a3780SDimitry Andric
GetNumberOfFields()2303344a3780SDimitry Andric int GetNumberOfFields() { return m_fields.size(); }
2304344a3780SDimitry Andric
GetNumberOfActions()2305344a3780SDimitry Andric int GetNumberOfActions() { return m_actions.size(); }
2306344a3780SDimitry Andric
HasError()2307344a3780SDimitry Andric bool HasError() { return !m_error.empty(); }
2308344a3780SDimitry Andric
ClearError()2309344a3780SDimitry Andric void ClearError() { m_error.clear(); }
2310344a3780SDimitry Andric
GetError()2311344a3780SDimitry Andric const std::string &GetError() { return m_error; }
2312344a3780SDimitry Andric
SetError(const char * error)2313344a3780SDimitry Andric void SetError(const char *error) { m_error = error; }
2314344a3780SDimitry Andric
2315344a3780SDimitry Andric // If all fields are valid, true is returned. Otherwise, an error message is
2316344a3780SDimitry Andric // set and false is returned. This method is usually called at the start of an
2317344a3780SDimitry Andric // action that requires valid fields.
CheckFieldsValidity()2318344a3780SDimitry Andric bool CheckFieldsValidity() {
2319344a3780SDimitry Andric for (int i = 0; i < GetNumberOfFields(); i++) {
2320c0981da4SDimitry Andric GetField(i)->FieldDelegateExitCallback();
2321344a3780SDimitry Andric if (GetField(i)->FieldDelegateHasError()) {
2322344a3780SDimitry Andric SetError("Some fields are invalid!");
2323344a3780SDimitry Andric return false;
2324344a3780SDimitry Andric }
2325344a3780SDimitry Andric }
2326344a3780SDimitry Andric return true;
2327344a3780SDimitry Andric }
2328344a3780SDimitry Andric
2329344a3780SDimitry Andric // Factory methods to create and add fields of specific types.
2330344a3780SDimitry Andric
AddTextField(const char * label,const char * content,bool required)2331344a3780SDimitry Andric TextFieldDelegate *AddTextField(const char *label, const char *content,
2332344a3780SDimitry Andric bool required) {
2333344a3780SDimitry Andric TextFieldDelegate *delegate =
2334344a3780SDimitry Andric new TextFieldDelegate(label, content, required);
2335344a3780SDimitry Andric m_fields.push_back(FieldDelegateUP(delegate));
2336344a3780SDimitry Andric return delegate;
2337344a3780SDimitry Andric }
2338344a3780SDimitry Andric
AddFileField(const char * label,const char * content,bool need_to_exist,bool required)2339344a3780SDimitry Andric FileFieldDelegate *AddFileField(const char *label, const char *content,
2340344a3780SDimitry Andric bool need_to_exist, bool required) {
2341344a3780SDimitry Andric FileFieldDelegate *delegate =
2342344a3780SDimitry Andric new FileFieldDelegate(label, content, need_to_exist, required);
2343344a3780SDimitry Andric m_fields.push_back(FieldDelegateUP(delegate));
2344344a3780SDimitry Andric return delegate;
2345344a3780SDimitry Andric }
2346344a3780SDimitry Andric
AddDirectoryField(const char * label,const char * content,bool need_to_exist,bool required)2347344a3780SDimitry Andric DirectoryFieldDelegate *AddDirectoryField(const char *label,
2348344a3780SDimitry Andric const char *content,
2349344a3780SDimitry Andric bool need_to_exist, bool required) {
2350344a3780SDimitry Andric DirectoryFieldDelegate *delegate =
2351344a3780SDimitry Andric new DirectoryFieldDelegate(label, content, need_to_exist, required);
2352344a3780SDimitry Andric m_fields.push_back(FieldDelegateUP(delegate));
2353344a3780SDimitry Andric return delegate;
2354344a3780SDimitry Andric }
2355344a3780SDimitry Andric
AddArchField(const char * label,const char * content,bool required)2356344a3780SDimitry Andric ArchFieldDelegate *AddArchField(const char *label, const char *content,
2357344a3780SDimitry Andric bool required) {
2358344a3780SDimitry Andric ArchFieldDelegate *delegate =
2359344a3780SDimitry Andric new ArchFieldDelegate(label, content, required);
2360344a3780SDimitry Andric m_fields.push_back(FieldDelegateUP(delegate));
2361344a3780SDimitry Andric return delegate;
2362344a3780SDimitry Andric }
2363344a3780SDimitry Andric
AddIntegerField(const char * label,int content,bool required)2364344a3780SDimitry Andric IntegerFieldDelegate *AddIntegerField(const char *label, int content,
2365344a3780SDimitry Andric bool required) {
2366344a3780SDimitry Andric IntegerFieldDelegate *delegate =
2367344a3780SDimitry Andric new IntegerFieldDelegate(label, content, required);
2368344a3780SDimitry Andric m_fields.push_back(FieldDelegateUP(delegate));
2369344a3780SDimitry Andric return delegate;
2370344a3780SDimitry Andric }
2371344a3780SDimitry Andric
AddBooleanField(const char * label,bool content)2372344a3780SDimitry Andric BooleanFieldDelegate *AddBooleanField(const char *label, bool content) {
2373344a3780SDimitry Andric BooleanFieldDelegate *delegate = new BooleanFieldDelegate(label, content);
2374344a3780SDimitry Andric m_fields.push_back(FieldDelegateUP(delegate));
2375344a3780SDimitry Andric return delegate;
2376344a3780SDimitry Andric }
2377344a3780SDimitry Andric
AddLazyBooleanField(const char * label,const char * calculate_label)2378c0981da4SDimitry Andric LazyBooleanFieldDelegate *AddLazyBooleanField(const char *label,
2379c0981da4SDimitry Andric const char *calculate_label) {
2380c0981da4SDimitry Andric LazyBooleanFieldDelegate *delegate =
2381c0981da4SDimitry Andric new LazyBooleanFieldDelegate(label, calculate_label);
2382c0981da4SDimitry Andric m_fields.push_back(FieldDelegateUP(delegate));
2383c0981da4SDimitry Andric return delegate;
2384c0981da4SDimitry Andric }
2385c0981da4SDimitry Andric
AddChoicesField(const char * label,int height,std::vector<std::string> choices)2386344a3780SDimitry Andric ChoicesFieldDelegate *AddChoicesField(const char *label, int height,
2387344a3780SDimitry Andric std::vector<std::string> choices) {
2388344a3780SDimitry Andric ChoicesFieldDelegate *delegate =
2389344a3780SDimitry Andric new ChoicesFieldDelegate(label, height, choices);
2390344a3780SDimitry Andric m_fields.push_back(FieldDelegateUP(delegate));
2391344a3780SDimitry Andric return delegate;
2392344a3780SDimitry Andric }
2393344a3780SDimitry Andric
AddPlatformPluginField(Debugger & debugger)2394344a3780SDimitry Andric PlatformPluginFieldDelegate *AddPlatformPluginField(Debugger &debugger) {
2395344a3780SDimitry Andric PlatformPluginFieldDelegate *delegate =
2396344a3780SDimitry Andric new PlatformPluginFieldDelegate(debugger);
2397344a3780SDimitry Andric m_fields.push_back(FieldDelegateUP(delegate));
2398344a3780SDimitry Andric return delegate;
2399344a3780SDimitry Andric }
2400344a3780SDimitry Andric
AddProcessPluginField()2401344a3780SDimitry Andric ProcessPluginFieldDelegate *AddProcessPluginField() {
2402344a3780SDimitry Andric ProcessPluginFieldDelegate *delegate = new ProcessPluginFieldDelegate();
2403344a3780SDimitry Andric m_fields.push_back(FieldDelegateUP(delegate));
2404344a3780SDimitry Andric return delegate;
2405344a3780SDimitry Andric }
2406344a3780SDimitry Andric
2407344a3780SDimitry Andric template <class T>
AddListField(const char * label,T default_field)2408344a3780SDimitry Andric ListFieldDelegate<T> *AddListField(const char *label, T default_field) {
2409344a3780SDimitry Andric ListFieldDelegate<T> *delegate =
2410344a3780SDimitry Andric new ListFieldDelegate<T>(label, default_field);
2411344a3780SDimitry Andric m_fields.push_back(FieldDelegateUP(delegate));
2412344a3780SDimitry Andric return delegate;
2413344a3780SDimitry Andric }
2414344a3780SDimitry Andric
AddArgumentsField()2415c0981da4SDimitry Andric ArgumentsFieldDelegate *AddArgumentsField() {
2416c0981da4SDimitry Andric ArgumentsFieldDelegate *delegate = new ArgumentsFieldDelegate();
2417c0981da4SDimitry Andric m_fields.push_back(FieldDelegateUP(delegate));
2418c0981da4SDimitry Andric return delegate;
2419c0981da4SDimitry Andric }
2420c0981da4SDimitry Andric
2421c0981da4SDimitry Andric template <class K, class V>
AddMappingField(K key_field,V value_field)2422c0981da4SDimitry Andric MappingFieldDelegate<K, V> *AddMappingField(K key_field, V value_field) {
2423c0981da4SDimitry Andric MappingFieldDelegate<K, V> *delegate =
2424c0981da4SDimitry Andric new MappingFieldDelegate<K, V>(key_field, value_field);
2425c0981da4SDimitry Andric m_fields.push_back(FieldDelegateUP(delegate));
2426c0981da4SDimitry Andric return delegate;
2427c0981da4SDimitry Andric }
2428c0981da4SDimitry Andric
2429c0981da4SDimitry Andric EnvironmentVariableNameFieldDelegate *
AddEnvironmentVariableNameField(const char * content)2430c0981da4SDimitry Andric AddEnvironmentVariableNameField(const char *content) {
2431c0981da4SDimitry Andric EnvironmentVariableNameFieldDelegate *delegate =
2432c0981da4SDimitry Andric new EnvironmentVariableNameFieldDelegate(content);
2433c0981da4SDimitry Andric m_fields.push_back(FieldDelegateUP(delegate));
2434c0981da4SDimitry Andric return delegate;
2435c0981da4SDimitry Andric }
2436c0981da4SDimitry Andric
AddEnvironmentVariableField()2437c0981da4SDimitry Andric EnvironmentVariableFieldDelegate *AddEnvironmentVariableField() {
2438c0981da4SDimitry Andric EnvironmentVariableFieldDelegate *delegate =
2439c0981da4SDimitry Andric new EnvironmentVariableFieldDelegate();
2440c0981da4SDimitry Andric m_fields.push_back(FieldDelegateUP(delegate));
2441c0981da4SDimitry Andric return delegate;
2442c0981da4SDimitry Andric }
2443c0981da4SDimitry Andric
2444c0981da4SDimitry Andric EnvironmentVariableListFieldDelegate *
AddEnvironmentVariableListField(const char * label)2445c0981da4SDimitry Andric AddEnvironmentVariableListField(const char *label) {
2446c0981da4SDimitry Andric EnvironmentVariableListFieldDelegate *delegate =
2447c0981da4SDimitry Andric new EnvironmentVariableListFieldDelegate(label);
2448c0981da4SDimitry Andric m_fields.push_back(FieldDelegateUP(delegate));
2449c0981da4SDimitry Andric return delegate;
2450c0981da4SDimitry Andric }
2451c0981da4SDimitry Andric
2452344a3780SDimitry Andric // Factory methods for adding actions.
2453344a3780SDimitry Andric
AddAction(const char * label,std::function<void (Window &)> action)2454344a3780SDimitry Andric void AddAction(const char *label, std::function<void(Window &)> action) {
2455344a3780SDimitry Andric m_actions.push_back(FormAction(label, action));
2456344a3780SDimitry Andric }
2457344a3780SDimitry Andric
2458344a3780SDimitry Andric protected:
2459344a3780SDimitry Andric std::vector<FieldDelegateUP> m_fields;
2460344a3780SDimitry Andric std::vector<FormAction> m_actions;
2461344a3780SDimitry Andric // Optional error message. If empty, form is considered to have no error.
2462344a3780SDimitry Andric std::string m_error;
2463344a3780SDimitry Andric };
2464344a3780SDimitry Andric
2465344a3780SDimitry Andric typedef std::shared_ptr<FormDelegate> FormDelegateSP;
2466344a3780SDimitry Andric
2467344a3780SDimitry Andric class FormWindowDelegate : public WindowDelegate {
2468344a3780SDimitry Andric public:
FormWindowDelegate(FormDelegateSP & delegate_sp)2469145449b1SDimitry Andric FormWindowDelegate(FormDelegateSP &delegate_sp) : m_delegate_sp(delegate_sp) {
2470344a3780SDimitry Andric assert(m_delegate_sp->GetNumberOfActions() > 0);
2471344a3780SDimitry Andric if (m_delegate_sp->GetNumberOfFields() > 0)
2472344a3780SDimitry Andric m_selection_type = SelectionType::Field;
2473344a3780SDimitry Andric else
2474344a3780SDimitry Andric m_selection_type = SelectionType::Action;
2475344a3780SDimitry Andric }
2476344a3780SDimitry Andric
2477344a3780SDimitry Andric // Signify which element is selected. If a field or an action is selected,
2478344a3780SDimitry Andric // then m_selection_index signifies the particular field or action that is
2479344a3780SDimitry Andric // selected.
2480344a3780SDimitry Andric enum class SelectionType { Field, Action };
2481344a3780SDimitry Andric
2482344a3780SDimitry Andric // A form window is padded by one character from all sides. First, if an error
2483344a3780SDimitry Andric // message exists, it is drawn followed by a separator. Then one or more
2484344a3780SDimitry Andric // fields are drawn. Finally, all available actions are drawn on a single
2485344a3780SDimitry Andric // line.
2486344a3780SDimitry Andric //
2487344a3780SDimitry Andric // ___<Form Name>_________________________________________________
2488344a3780SDimitry Andric // | |
2489344a3780SDimitry Andric // | - Error message if it exists. |
2490344a3780SDimitry Andric // |-------------------------------------------------------------|
2491344a3780SDimitry Andric // | Form elements here. |
2492344a3780SDimitry Andric // | Form actions here. |
2493344a3780SDimitry Andric // | |
2494344a3780SDimitry Andric // |______________________________________[Press Esc to cancel]__|
2495344a3780SDimitry Andric //
2496344a3780SDimitry Andric
2497344a3780SDimitry Andric // One line for the error and another for the horizontal line.
GetErrorHeight()2498344a3780SDimitry Andric int GetErrorHeight() {
2499344a3780SDimitry Andric if (m_delegate_sp->HasError())
2500344a3780SDimitry Andric return 2;
2501344a3780SDimitry Andric return 0;
2502344a3780SDimitry Andric }
2503344a3780SDimitry Andric
2504344a3780SDimitry Andric // Actions span a single line.
GetActionsHeight()2505344a3780SDimitry Andric int GetActionsHeight() {
2506344a3780SDimitry Andric if (m_delegate_sp->GetNumberOfActions() > 0)
2507344a3780SDimitry Andric return 1;
2508344a3780SDimitry Andric return 0;
2509344a3780SDimitry Andric }
2510344a3780SDimitry Andric
2511344a3780SDimitry Andric // Get the total number of needed lines to draw the contents.
GetContentHeight()2512344a3780SDimitry Andric int GetContentHeight() {
2513344a3780SDimitry Andric int height = 0;
2514344a3780SDimitry Andric height += GetErrorHeight();
2515344a3780SDimitry Andric for (int i = 0; i < m_delegate_sp->GetNumberOfFields(); i++) {
2516344a3780SDimitry Andric if (!m_delegate_sp->GetField(i)->FieldDelegateIsVisible())
2517344a3780SDimitry Andric continue;
2518344a3780SDimitry Andric height += m_delegate_sp->GetField(i)->FieldDelegateGetHeight();
2519344a3780SDimitry Andric }
2520344a3780SDimitry Andric height += GetActionsHeight();
2521344a3780SDimitry Andric return height;
2522344a3780SDimitry Andric }
2523344a3780SDimitry Andric
GetScrollContext()2524344a3780SDimitry Andric ScrollContext GetScrollContext() {
2525344a3780SDimitry Andric if (m_selection_type == SelectionType::Action)
2526344a3780SDimitry Andric return ScrollContext(GetContentHeight() - 1);
2527344a3780SDimitry Andric
2528344a3780SDimitry Andric FieldDelegate *field = m_delegate_sp->GetField(m_selection_index);
2529344a3780SDimitry Andric ScrollContext context = field->FieldDelegateGetScrollContext();
2530344a3780SDimitry Andric
2531344a3780SDimitry Andric int offset = GetErrorHeight();
2532344a3780SDimitry Andric for (int i = 0; i < m_selection_index; i++) {
2533344a3780SDimitry Andric if (!m_delegate_sp->GetField(i)->FieldDelegateIsVisible())
2534344a3780SDimitry Andric continue;
2535344a3780SDimitry Andric offset += m_delegate_sp->GetField(i)->FieldDelegateGetHeight();
2536344a3780SDimitry Andric }
2537344a3780SDimitry Andric context.Offset(offset);
2538344a3780SDimitry Andric
2539344a3780SDimitry Andric // If the context is touching the error, include the error in the context as
2540344a3780SDimitry Andric // well.
2541344a3780SDimitry Andric if (context.start == GetErrorHeight())
2542344a3780SDimitry Andric context.start = 0;
2543344a3780SDimitry Andric
2544344a3780SDimitry Andric return context;
2545344a3780SDimitry Andric }
2546344a3780SDimitry Andric
UpdateScrolling(Surface & surface)2547c0981da4SDimitry Andric void UpdateScrolling(Surface &surface) {
2548344a3780SDimitry Andric ScrollContext context = GetScrollContext();
2549344a3780SDimitry Andric int content_height = GetContentHeight();
2550344a3780SDimitry Andric int surface_height = surface.GetHeight();
2551344a3780SDimitry Andric int visible_height = std::min(content_height, surface_height);
2552344a3780SDimitry Andric int last_visible_line = m_first_visible_line + visible_height - 1;
2553344a3780SDimitry Andric
2554344a3780SDimitry Andric // If the last visible line is bigger than the content, then it is invalid
2555344a3780SDimitry Andric // and needs to be set to the last line in the content. This can happen when
2556344a3780SDimitry Andric // a field has shrunk in height.
2557344a3780SDimitry Andric if (last_visible_line > content_height - 1) {
2558344a3780SDimitry Andric m_first_visible_line = content_height - visible_height;
2559344a3780SDimitry Andric }
2560344a3780SDimitry Andric
2561344a3780SDimitry Andric if (context.start < m_first_visible_line) {
2562344a3780SDimitry Andric m_first_visible_line = context.start;
2563344a3780SDimitry Andric return;
2564344a3780SDimitry Andric }
2565344a3780SDimitry Andric
2566344a3780SDimitry Andric if (context.end > last_visible_line) {
2567344a3780SDimitry Andric m_first_visible_line = context.end - visible_height + 1;
2568344a3780SDimitry Andric }
2569344a3780SDimitry Andric }
2570344a3780SDimitry Andric
DrawError(Surface & surface)2571c0981da4SDimitry Andric void DrawError(Surface &surface) {
2572344a3780SDimitry Andric if (!m_delegate_sp->HasError())
2573344a3780SDimitry Andric return;
2574344a3780SDimitry Andric surface.MoveCursor(0, 0);
2575344a3780SDimitry Andric surface.AttributeOn(COLOR_PAIR(RedOnBlack));
2576344a3780SDimitry Andric surface.PutChar(ACS_DIAMOND);
2577344a3780SDimitry Andric surface.PutChar(' ');
2578344a3780SDimitry Andric surface.PutCStringTruncated(1, m_delegate_sp->GetError().c_str());
2579344a3780SDimitry Andric surface.AttributeOff(COLOR_PAIR(RedOnBlack));
2580344a3780SDimitry Andric
2581344a3780SDimitry Andric surface.MoveCursor(0, 1);
2582344a3780SDimitry Andric surface.HorizontalLine(surface.GetWidth());
2583344a3780SDimitry Andric }
2584344a3780SDimitry Andric
DrawFields(Surface & surface)2585c0981da4SDimitry Andric void DrawFields(Surface &surface) {
2586344a3780SDimitry Andric int line = 0;
2587344a3780SDimitry Andric int width = surface.GetWidth();
2588344a3780SDimitry Andric bool a_field_is_selected = m_selection_type == SelectionType::Field;
2589344a3780SDimitry Andric for (int i = 0; i < m_delegate_sp->GetNumberOfFields(); i++) {
2590344a3780SDimitry Andric FieldDelegate *field = m_delegate_sp->GetField(i);
2591344a3780SDimitry Andric if (!field->FieldDelegateIsVisible())
2592344a3780SDimitry Andric continue;
2593344a3780SDimitry Andric bool is_field_selected = a_field_is_selected && m_selection_index == i;
2594344a3780SDimitry Andric int height = field->FieldDelegateGetHeight();
2595344a3780SDimitry Andric Rect bounds = Rect(Point(0, line), Size(width, height));
2596c0981da4SDimitry Andric Surface field_surface = surface.SubSurface(bounds);
2597344a3780SDimitry Andric field->FieldDelegateDraw(field_surface, is_field_selected);
2598344a3780SDimitry Andric line += height;
2599344a3780SDimitry Andric }
2600344a3780SDimitry Andric }
2601344a3780SDimitry Andric
DrawActions(Surface & surface)2602c0981da4SDimitry Andric void DrawActions(Surface &surface) {
2603344a3780SDimitry Andric int number_of_actions = m_delegate_sp->GetNumberOfActions();
2604344a3780SDimitry Andric int width = surface.GetWidth() / number_of_actions;
2605344a3780SDimitry Andric bool an_action_is_selected = m_selection_type == SelectionType::Action;
2606344a3780SDimitry Andric int x = 0;
2607344a3780SDimitry Andric for (int i = 0; i < number_of_actions; i++) {
2608344a3780SDimitry Andric bool is_action_selected = an_action_is_selected && m_selection_index == i;
2609344a3780SDimitry Andric FormAction &action = m_delegate_sp->GetAction(i);
2610344a3780SDimitry Andric Rect bounds = Rect(Point(x, 0), Size(width, 1));
2611c0981da4SDimitry Andric Surface action_surface = surface.SubSurface(bounds);
2612344a3780SDimitry Andric action.Draw(action_surface, is_action_selected);
2613344a3780SDimitry Andric x += width;
2614344a3780SDimitry Andric }
2615344a3780SDimitry Andric }
2616344a3780SDimitry Andric
DrawElements(Surface & surface)2617c0981da4SDimitry Andric void DrawElements(Surface &surface) {
2618344a3780SDimitry Andric Rect frame = surface.GetFrame();
2619344a3780SDimitry Andric Rect fields_bounds, actions_bounds;
2620344a3780SDimitry Andric frame.HorizontalSplit(surface.GetHeight() - GetActionsHeight(),
2621344a3780SDimitry Andric fields_bounds, actions_bounds);
2622c0981da4SDimitry Andric Surface fields_surface = surface.SubSurface(fields_bounds);
2623c0981da4SDimitry Andric Surface actions_surface = surface.SubSurface(actions_bounds);
2624344a3780SDimitry Andric
2625344a3780SDimitry Andric DrawFields(fields_surface);
2626344a3780SDimitry Andric DrawActions(actions_surface);
2627344a3780SDimitry Andric }
2628344a3780SDimitry Andric
2629344a3780SDimitry Andric // Contents are first drawn on a pad. Then a subset of that pad is copied to
2630344a3780SDimitry Andric // the derived window starting at the first visible line. This essentially
2631344a3780SDimitry Andric // provides scrolling functionality.
DrawContent(Surface & surface)2632c0981da4SDimitry Andric void DrawContent(Surface &surface) {
2633344a3780SDimitry Andric UpdateScrolling(surface);
2634344a3780SDimitry Andric
2635344a3780SDimitry Andric int width = surface.GetWidth();
2636344a3780SDimitry Andric int height = GetContentHeight();
2637344a3780SDimitry Andric Pad pad = Pad(Size(width, height));
2638344a3780SDimitry Andric
2639344a3780SDimitry Andric Rect frame = pad.GetFrame();
2640344a3780SDimitry Andric Rect error_bounds, elements_bounds;
2641344a3780SDimitry Andric frame.HorizontalSplit(GetErrorHeight(), error_bounds, elements_bounds);
2642c0981da4SDimitry Andric Surface error_surface = pad.SubSurface(error_bounds);
2643c0981da4SDimitry Andric Surface elements_surface = pad.SubSurface(elements_bounds);
2644344a3780SDimitry Andric
2645344a3780SDimitry Andric DrawError(error_surface);
2646344a3780SDimitry Andric DrawElements(elements_surface);
2647344a3780SDimitry Andric
2648344a3780SDimitry Andric int copy_height = std::min(surface.GetHeight(), pad.GetHeight());
2649344a3780SDimitry Andric pad.CopyToSurface(surface, Point(0, m_first_visible_line), Point(),
2650344a3780SDimitry Andric Size(width, copy_height));
2651344a3780SDimitry Andric }
2652344a3780SDimitry Andric
DrawSubmitHint(Surface & surface,bool is_active)2653c0981da4SDimitry Andric void DrawSubmitHint(Surface &surface, bool is_active) {
2654c0981da4SDimitry Andric surface.MoveCursor(2, surface.GetHeight() - 1);
2655c0981da4SDimitry Andric if (is_active)
2656c0981da4SDimitry Andric surface.AttributeOn(A_BOLD | COLOR_PAIR(BlackOnWhite));
2657c0981da4SDimitry Andric surface.Printf("[Press Alt+Enter to %s]",
2658c0981da4SDimitry Andric m_delegate_sp->GetAction(0).GetLabel().c_str());
2659c0981da4SDimitry Andric if (is_active)
2660c0981da4SDimitry Andric surface.AttributeOff(A_BOLD | COLOR_PAIR(BlackOnWhite));
2661c0981da4SDimitry Andric }
2662c0981da4SDimitry Andric
WindowDelegateDraw(Window & window,bool force)2663344a3780SDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override {
2664344a3780SDimitry Andric m_delegate_sp->UpdateFieldsVisibility();
2665344a3780SDimitry Andric
2666344a3780SDimitry Andric window.Erase();
2667344a3780SDimitry Andric
2668344a3780SDimitry Andric window.DrawTitleBox(m_delegate_sp->GetName().c_str(),
2669c0981da4SDimitry Andric "Press Esc to Cancel");
2670c0981da4SDimitry Andric DrawSubmitHint(window, window.IsActive());
2671344a3780SDimitry Andric
2672344a3780SDimitry Andric Rect content_bounds = window.GetFrame();
2673344a3780SDimitry Andric content_bounds.Inset(2, 2);
2674c0981da4SDimitry Andric Surface content_surface = window.SubSurface(content_bounds);
2675344a3780SDimitry Andric
2676344a3780SDimitry Andric DrawContent(content_surface);
2677344a3780SDimitry Andric return true;
2678344a3780SDimitry Andric }
2679344a3780SDimitry Andric
SkipNextHiddenFields()2680344a3780SDimitry Andric void SkipNextHiddenFields() {
2681344a3780SDimitry Andric while (true) {
2682344a3780SDimitry Andric if (m_delegate_sp->GetField(m_selection_index)->FieldDelegateIsVisible())
2683344a3780SDimitry Andric return;
2684344a3780SDimitry Andric
2685344a3780SDimitry Andric if (m_selection_index == m_delegate_sp->GetNumberOfFields() - 1) {
2686344a3780SDimitry Andric m_selection_type = SelectionType::Action;
2687344a3780SDimitry Andric m_selection_index = 0;
2688344a3780SDimitry Andric return;
2689344a3780SDimitry Andric }
2690344a3780SDimitry Andric
2691344a3780SDimitry Andric m_selection_index++;
2692344a3780SDimitry Andric }
2693344a3780SDimitry Andric }
2694344a3780SDimitry Andric
SelectNext(int key)2695344a3780SDimitry Andric HandleCharResult SelectNext(int key) {
2696344a3780SDimitry Andric if (m_selection_type == SelectionType::Action) {
2697344a3780SDimitry Andric if (m_selection_index < m_delegate_sp->GetNumberOfActions() - 1) {
2698344a3780SDimitry Andric m_selection_index++;
2699344a3780SDimitry Andric return eKeyHandled;
2700344a3780SDimitry Andric }
2701344a3780SDimitry Andric
2702344a3780SDimitry Andric m_selection_index = 0;
2703344a3780SDimitry Andric m_selection_type = SelectionType::Field;
2704344a3780SDimitry Andric SkipNextHiddenFields();
2705344a3780SDimitry Andric if (m_selection_type == SelectionType::Field) {
2706344a3780SDimitry Andric FieldDelegate *next_field = m_delegate_sp->GetField(m_selection_index);
2707344a3780SDimitry Andric next_field->FieldDelegateSelectFirstElement();
2708344a3780SDimitry Andric }
2709344a3780SDimitry Andric return eKeyHandled;
2710344a3780SDimitry Andric }
2711344a3780SDimitry Andric
2712344a3780SDimitry Andric FieldDelegate *field = m_delegate_sp->GetField(m_selection_index);
2713344a3780SDimitry Andric if (!field->FieldDelegateOnLastOrOnlyElement()) {
2714344a3780SDimitry Andric return field->FieldDelegateHandleChar(key);
2715344a3780SDimitry Andric }
2716344a3780SDimitry Andric
2717344a3780SDimitry Andric field->FieldDelegateExitCallback();
2718344a3780SDimitry Andric
2719344a3780SDimitry Andric if (m_selection_index == m_delegate_sp->GetNumberOfFields() - 1) {
2720344a3780SDimitry Andric m_selection_type = SelectionType::Action;
2721344a3780SDimitry Andric m_selection_index = 0;
2722344a3780SDimitry Andric return eKeyHandled;
2723344a3780SDimitry Andric }
2724344a3780SDimitry Andric
2725344a3780SDimitry Andric m_selection_index++;
2726344a3780SDimitry Andric SkipNextHiddenFields();
2727344a3780SDimitry Andric
2728344a3780SDimitry Andric if (m_selection_type == SelectionType::Field) {
2729344a3780SDimitry Andric FieldDelegate *next_field = m_delegate_sp->GetField(m_selection_index);
2730344a3780SDimitry Andric next_field->FieldDelegateSelectFirstElement();
2731344a3780SDimitry Andric }
2732344a3780SDimitry Andric
2733344a3780SDimitry Andric return eKeyHandled;
2734344a3780SDimitry Andric }
2735344a3780SDimitry Andric
SkipPreviousHiddenFields()2736344a3780SDimitry Andric void SkipPreviousHiddenFields() {
2737344a3780SDimitry Andric while (true) {
2738344a3780SDimitry Andric if (m_delegate_sp->GetField(m_selection_index)->FieldDelegateIsVisible())
2739344a3780SDimitry Andric return;
2740344a3780SDimitry Andric
2741344a3780SDimitry Andric if (m_selection_index == 0) {
2742344a3780SDimitry Andric m_selection_type = SelectionType::Action;
2743344a3780SDimitry Andric m_selection_index = 0;
2744344a3780SDimitry Andric return;
2745344a3780SDimitry Andric }
2746344a3780SDimitry Andric
2747344a3780SDimitry Andric m_selection_index--;
2748344a3780SDimitry Andric }
2749344a3780SDimitry Andric }
2750344a3780SDimitry Andric
SelectPrevious(int key)2751344a3780SDimitry Andric HandleCharResult SelectPrevious(int key) {
2752344a3780SDimitry Andric if (m_selection_type == SelectionType::Action) {
2753344a3780SDimitry Andric if (m_selection_index > 0) {
2754344a3780SDimitry Andric m_selection_index--;
2755344a3780SDimitry Andric return eKeyHandled;
2756344a3780SDimitry Andric }
2757344a3780SDimitry Andric m_selection_index = m_delegate_sp->GetNumberOfFields() - 1;
2758344a3780SDimitry Andric m_selection_type = SelectionType::Field;
2759344a3780SDimitry Andric SkipPreviousHiddenFields();
2760344a3780SDimitry Andric if (m_selection_type == SelectionType::Field) {
2761344a3780SDimitry Andric FieldDelegate *previous_field =
2762344a3780SDimitry Andric m_delegate_sp->GetField(m_selection_index);
2763344a3780SDimitry Andric previous_field->FieldDelegateSelectLastElement();
2764344a3780SDimitry Andric }
2765344a3780SDimitry Andric return eKeyHandled;
2766344a3780SDimitry Andric }
2767344a3780SDimitry Andric
2768344a3780SDimitry Andric FieldDelegate *field = m_delegate_sp->GetField(m_selection_index);
2769344a3780SDimitry Andric if (!field->FieldDelegateOnFirstOrOnlyElement()) {
2770344a3780SDimitry Andric return field->FieldDelegateHandleChar(key);
2771344a3780SDimitry Andric }
2772344a3780SDimitry Andric
2773344a3780SDimitry Andric field->FieldDelegateExitCallback();
2774344a3780SDimitry Andric
2775344a3780SDimitry Andric if (m_selection_index == 0) {
2776344a3780SDimitry Andric m_selection_type = SelectionType::Action;
2777344a3780SDimitry Andric m_selection_index = m_delegate_sp->GetNumberOfActions() - 1;
2778344a3780SDimitry Andric return eKeyHandled;
2779344a3780SDimitry Andric }
2780344a3780SDimitry Andric
2781344a3780SDimitry Andric m_selection_index--;
2782344a3780SDimitry Andric SkipPreviousHiddenFields();
2783344a3780SDimitry Andric
2784344a3780SDimitry Andric if (m_selection_type == SelectionType::Field) {
2785344a3780SDimitry Andric FieldDelegate *previous_field =
2786344a3780SDimitry Andric m_delegate_sp->GetField(m_selection_index);
2787344a3780SDimitry Andric previous_field->FieldDelegateSelectLastElement();
2788344a3780SDimitry Andric }
2789344a3780SDimitry Andric
2790344a3780SDimitry Andric return eKeyHandled;
2791344a3780SDimitry Andric }
2792344a3780SDimitry Andric
ExecuteAction(Window & window,int index)2793c0981da4SDimitry Andric void ExecuteAction(Window &window, int index) {
2794c0981da4SDimitry Andric FormAction &action = m_delegate_sp->GetAction(index);
2795344a3780SDimitry Andric action.Execute(window);
2796344a3780SDimitry Andric if (m_delegate_sp->HasError()) {
2797344a3780SDimitry Andric m_first_visible_line = 0;
2798344a3780SDimitry Andric m_selection_index = 0;
2799344a3780SDimitry Andric m_selection_type = SelectionType::Field;
2800344a3780SDimitry Andric }
2801344a3780SDimitry Andric }
2802344a3780SDimitry Andric
2803c0981da4SDimitry Andric // Always return eKeyHandled to absorb all events since forms are always
2804c0981da4SDimitry Andric // added as pop-ups that should take full control until canceled or submitted.
WindowDelegateHandleChar(Window & window,int key)2805344a3780SDimitry Andric HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
2806344a3780SDimitry Andric switch (key) {
2807344a3780SDimitry Andric case '\r':
2808344a3780SDimitry Andric case '\n':
2809344a3780SDimitry Andric case KEY_ENTER:
2810344a3780SDimitry Andric if (m_selection_type == SelectionType::Action) {
2811c0981da4SDimitry Andric ExecuteAction(window, m_selection_index);
2812344a3780SDimitry Andric return eKeyHandled;
2813344a3780SDimitry Andric }
2814344a3780SDimitry Andric break;
2815c0981da4SDimitry Andric case KEY_ALT_ENTER:
2816c0981da4SDimitry Andric ExecuteAction(window, 0);
2817c0981da4SDimitry Andric return eKeyHandled;
2818344a3780SDimitry Andric case '\t':
2819c0981da4SDimitry Andric SelectNext(key);
2820c0981da4SDimitry Andric return eKeyHandled;
2821344a3780SDimitry Andric case KEY_SHIFT_TAB:
2822c0981da4SDimitry Andric SelectPrevious(key);
2823c0981da4SDimitry Andric return eKeyHandled;
2824344a3780SDimitry Andric case KEY_ESCAPE:
2825344a3780SDimitry Andric window.GetParent()->RemoveSubWindow(&window);
2826344a3780SDimitry Andric return eKeyHandled;
2827344a3780SDimitry Andric default:
2828344a3780SDimitry Andric break;
2829344a3780SDimitry Andric }
2830344a3780SDimitry Andric
2831344a3780SDimitry Andric // If the key wasn't handled and one of the fields is selected, pass the key
2832344a3780SDimitry Andric // to that field.
2833344a3780SDimitry Andric if (m_selection_type == SelectionType::Field) {
2834344a3780SDimitry Andric FieldDelegate *field = m_delegate_sp->GetField(m_selection_index);
2835c0981da4SDimitry Andric if (field->FieldDelegateHandleChar(key) == eKeyHandled)
2836c0981da4SDimitry Andric return eKeyHandled;
2837344a3780SDimitry Andric }
2838344a3780SDimitry Andric
2839c0981da4SDimitry Andric // If the key wasn't handled by the possibly selected field, handle some
2840c0981da4SDimitry Andric // extra keys for navigation.
2841c0981da4SDimitry Andric switch (key) {
2842c0981da4SDimitry Andric case KEY_DOWN:
2843c0981da4SDimitry Andric SelectNext(key);
2844c0981da4SDimitry Andric return eKeyHandled;
2845c0981da4SDimitry Andric case KEY_UP:
2846c0981da4SDimitry Andric SelectPrevious(key);
2847c0981da4SDimitry Andric return eKeyHandled;
2848c0981da4SDimitry Andric default:
2849c0981da4SDimitry Andric break;
2850c0981da4SDimitry Andric }
2851c0981da4SDimitry Andric
2852c0981da4SDimitry Andric return eKeyHandled;
2853344a3780SDimitry Andric }
2854344a3780SDimitry Andric
2855344a3780SDimitry Andric protected:
2856344a3780SDimitry Andric FormDelegateSP m_delegate_sp;
2857344a3780SDimitry Andric // The index of the currently selected SelectionType.
2858145449b1SDimitry Andric int m_selection_index = 0;
2859344a3780SDimitry Andric // See SelectionType class enum.
2860344a3780SDimitry Andric SelectionType m_selection_type;
2861344a3780SDimitry Andric // The first visible line from the pad.
2862145449b1SDimitry Andric int m_first_visible_line = 0;
2863344a3780SDimitry Andric };
2864344a3780SDimitry Andric
2865344a3780SDimitry Andric ///////////////////////////
2866344a3780SDimitry Andric // Form Delegate Instances
2867344a3780SDimitry Andric ///////////////////////////
2868344a3780SDimitry Andric
2869344a3780SDimitry Andric class DetachOrKillProcessFormDelegate : public FormDelegate {
2870344a3780SDimitry Andric public:
DetachOrKillProcessFormDelegate(Process * process)2871344a3780SDimitry Andric DetachOrKillProcessFormDelegate(Process *process) : m_process(process) {
2872344a3780SDimitry Andric SetError("There is a running process, either detach or kill it.");
2873344a3780SDimitry Andric
2874344a3780SDimitry Andric m_keep_stopped_field =
2875344a3780SDimitry Andric AddBooleanField("Keep process stopped when detaching.", false);
2876344a3780SDimitry Andric
2877344a3780SDimitry Andric AddAction("Detach", [this](Window &window) { Detach(window); });
2878344a3780SDimitry Andric AddAction("Kill", [this](Window &window) { Kill(window); });
2879344a3780SDimitry Andric }
2880344a3780SDimitry Andric
GetName()2881344a3780SDimitry Andric std::string GetName() override { return "Detach/Kill Process"; }
2882344a3780SDimitry Andric
Kill(Window & window)2883344a3780SDimitry Andric void Kill(Window &window) {
2884344a3780SDimitry Andric Status destroy_status(m_process->Destroy(false));
2885344a3780SDimitry Andric if (destroy_status.Fail()) {
2886344a3780SDimitry Andric SetError("Failed to kill process.");
2887344a3780SDimitry Andric return;
2888344a3780SDimitry Andric }
2889344a3780SDimitry Andric window.GetParent()->RemoveSubWindow(&window);
2890344a3780SDimitry Andric }
2891344a3780SDimitry Andric
Detach(Window & window)2892344a3780SDimitry Andric void Detach(Window &window) {
2893344a3780SDimitry Andric Status detach_status(m_process->Detach(m_keep_stopped_field->GetBoolean()));
2894344a3780SDimitry Andric if (detach_status.Fail()) {
2895344a3780SDimitry Andric SetError("Failed to detach from process.");
2896344a3780SDimitry Andric return;
2897344a3780SDimitry Andric }
2898344a3780SDimitry Andric window.GetParent()->RemoveSubWindow(&window);
2899344a3780SDimitry Andric }
2900344a3780SDimitry Andric
2901344a3780SDimitry Andric protected:
2902344a3780SDimitry Andric Process *m_process;
2903344a3780SDimitry Andric BooleanFieldDelegate *m_keep_stopped_field;
2904344a3780SDimitry Andric };
2905344a3780SDimitry Andric
2906344a3780SDimitry Andric class ProcessAttachFormDelegate : public FormDelegate {
2907344a3780SDimitry Andric public:
ProcessAttachFormDelegate(Debugger & debugger,WindowSP main_window_sp)2908344a3780SDimitry Andric ProcessAttachFormDelegate(Debugger &debugger, WindowSP main_window_sp)
2909344a3780SDimitry Andric : m_debugger(debugger), m_main_window_sp(main_window_sp) {
2910344a3780SDimitry Andric std::vector<std::string> types;
2911344a3780SDimitry Andric types.push_back(std::string("Name"));
2912344a3780SDimitry Andric types.push_back(std::string("PID"));
2913344a3780SDimitry Andric m_type_field = AddChoicesField("Attach By", 2, types);
2914344a3780SDimitry Andric m_pid_field = AddIntegerField("PID", 0, true);
2915344a3780SDimitry Andric m_name_field =
2916344a3780SDimitry Andric AddTextField("Process Name", GetDefaultProcessName().c_str(), true);
2917344a3780SDimitry Andric m_continue_field = AddBooleanField("Continue once attached.", false);
2918344a3780SDimitry Andric m_wait_for_field = AddBooleanField("Wait for process to launch.", false);
2919344a3780SDimitry Andric m_include_existing_field =
2920344a3780SDimitry Andric AddBooleanField("Include existing processes.", false);
2921344a3780SDimitry Andric m_show_advanced_field = AddBooleanField("Show advanced settings.", false);
2922344a3780SDimitry Andric m_plugin_field = AddProcessPluginField();
2923344a3780SDimitry Andric
2924344a3780SDimitry Andric AddAction("Attach", [this](Window &window) { Attach(window); });
2925344a3780SDimitry Andric }
2926344a3780SDimitry Andric
GetName()2927344a3780SDimitry Andric std::string GetName() override { return "Attach Process"; }
2928344a3780SDimitry Andric
UpdateFieldsVisibility()2929344a3780SDimitry Andric void UpdateFieldsVisibility() override {
2930344a3780SDimitry Andric if (m_type_field->GetChoiceContent() == "Name") {
2931344a3780SDimitry Andric m_pid_field->FieldDelegateHide();
2932344a3780SDimitry Andric m_name_field->FieldDelegateShow();
2933344a3780SDimitry Andric m_wait_for_field->FieldDelegateShow();
2934344a3780SDimitry Andric if (m_wait_for_field->GetBoolean())
2935344a3780SDimitry Andric m_include_existing_field->FieldDelegateShow();
2936344a3780SDimitry Andric else
2937344a3780SDimitry Andric m_include_existing_field->FieldDelegateHide();
2938344a3780SDimitry Andric } else {
2939344a3780SDimitry Andric m_pid_field->FieldDelegateShow();
2940344a3780SDimitry Andric m_name_field->FieldDelegateHide();
2941344a3780SDimitry Andric m_wait_for_field->FieldDelegateHide();
2942344a3780SDimitry Andric m_include_existing_field->FieldDelegateHide();
2943344a3780SDimitry Andric }
2944344a3780SDimitry Andric if (m_show_advanced_field->GetBoolean())
2945344a3780SDimitry Andric m_plugin_field->FieldDelegateShow();
2946344a3780SDimitry Andric else
2947344a3780SDimitry Andric m_plugin_field->FieldDelegateHide();
2948344a3780SDimitry Andric }
2949344a3780SDimitry Andric
2950344a3780SDimitry Andric // Get the basename of the target's main executable if available, empty string
2951344a3780SDimitry Andric // otherwise.
GetDefaultProcessName()2952344a3780SDimitry Andric std::string GetDefaultProcessName() {
2953344a3780SDimitry Andric Target *target = m_debugger.GetSelectedTarget().get();
2954344a3780SDimitry Andric if (target == nullptr)
2955344a3780SDimitry Andric return "";
2956344a3780SDimitry Andric
2957344a3780SDimitry Andric ModuleSP module_sp = target->GetExecutableModule();
2958344a3780SDimitry Andric if (!module_sp->IsExecutable())
2959344a3780SDimitry Andric return "";
2960344a3780SDimitry Andric
2961344a3780SDimitry Andric return module_sp->GetFileSpec().GetFilename().AsCString();
2962344a3780SDimitry Andric }
2963344a3780SDimitry Andric
StopRunningProcess()2964344a3780SDimitry Andric bool StopRunningProcess() {
2965344a3780SDimitry Andric ExecutionContext exe_ctx =
2966344a3780SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
2967344a3780SDimitry Andric
2968344a3780SDimitry Andric if (!exe_ctx.HasProcessScope())
2969344a3780SDimitry Andric return false;
2970344a3780SDimitry Andric
2971344a3780SDimitry Andric Process *process = exe_ctx.GetProcessPtr();
2972344a3780SDimitry Andric if (!(process && process->IsAlive()))
2973344a3780SDimitry Andric return false;
2974344a3780SDimitry Andric
2975344a3780SDimitry Andric FormDelegateSP form_delegate_sp =
2976344a3780SDimitry Andric FormDelegateSP(new DetachOrKillProcessFormDelegate(process));
2977344a3780SDimitry Andric Rect bounds = m_main_window_sp->GetCenteredRect(85, 8);
2978344a3780SDimitry Andric WindowSP form_window_sp = m_main_window_sp->CreateSubWindow(
2979344a3780SDimitry Andric form_delegate_sp->GetName().c_str(), bounds, true);
2980344a3780SDimitry Andric WindowDelegateSP window_delegate_sp =
2981344a3780SDimitry Andric WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
2982344a3780SDimitry Andric form_window_sp->SetDelegate(window_delegate_sp);
2983344a3780SDimitry Andric
2984344a3780SDimitry Andric return true;
2985344a3780SDimitry Andric }
2986344a3780SDimitry Andric
GetTarget()2987344a3780SDimitry Andric Target *GetTarget() {
2988344a3780SDimitry Andric Target *target = m_debugger.GetSelectedTarget().get();
2989344a3780SDimitry Andric
2990344a3780SDimitry Andric if (target != nullptr)
2991344a3780SDimitry Andric return target;
2992344a3780SDimitry Andric
2993344a3780SDimitry Andric TargetSP new_target_sp;
2994344a3780SDimitry Andric m_debugger.GetTargetList().CreateTarget(
2995344a3780SDimitry Andric m_debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp);
2996344a3780SDimitry Andric
2997344a3780SDimitry Andric target = new_target_sp.get();
2998344a3780SDimitry Andric
2999344a3780SDimitry Andric if (target == nullptr)
3000344a3780SDimitry Andric SetError("Failed to create target.");
3001344a3780SDimitry Andric
3002344a3780SDimitry Andric m_debugger.GetTargetList().SetSelectedTarget(new_target_sp);
3003344a3780SDimitry Andric
3004344a3780SDimitry Andric return target;
3005344a3780SDimitry Andric }
3006344a3780SDimitry Andric
GetAttachInfo()3007344a3780SDimitry Andric ProcessAttachInfo GetAttachInfo() {
3008344a3780SDimitry Andric ProcessAttachInfo attach_info;
3009344a3780SDimitry Andric attach_info.SetContinueOnceAttached(m_continue_field->GetBoolean());
3010344a3780SDimitry Andric if (m_type_field->GetChoiceContent() == "Name") {
3011344a3780SDimitry Andric attach_info.GetExecutableFile().SetFile(m_name_field->GetText(),
3012344a3780SDimitry Andric FileSpec::Style::native);
3013344a3780SDimitry Andric attach_info.SetWaitForLaunch(m_wait_for_field->GetBoolean());
3014344a3780SDimitry Andric if (m_wait_for_field->GetBoolean())
3015344a3780SDimitry Andric attach_info.SetIgnoreExisting(!m_include_existing_field->GetBoolean());
3016344a3780SDimitry Andric } else {
3017344a3780SDimitry Andric attach_info.SetProcessID(m_pid_field->GetInteger());
3018344a3780SDimitry Andric }
3019344a3780SDimitry Andric attach_info.SetProcessPluginName(m_plugin_field->GetPluginName());
3020344a3780SDimitry Andric
3021344a3780SDimitry Andric return attach_info;
3022344a3780SDimitry Andric }
3023344a3780SDimitry Andric
Attach(Window & window)3024344a3780SDimitry Andric void Attach(Window &window) {
3025344a3780SDimitry Andric ClearError();
3026344a3780SDimitry Andric
3027344a3780SDimitry Andric bool all_fields_are_valid = CheckFieldsValidity();
3028344a3780SDimitry Andric if (!all_fields_are_valid)
3029344a3780SDimitry Andric return;
3030344a3780SDimitry Andric
3031344a3780SDimitry Andric bool process_is_running = StopRunningProcess();
3032344a3780SDimitry Andric if (process_is_running)
3033344a3780SDimitry Andric return;
3034344a3780SDimitry Andric
3035344a3780SDimitry Andric Target *target = GetTarget();
3036344a3780SDimitry Andric if (HasError())
3037344a3780SDimitry Andric return;
3038344a3780SDimitry Andric
3039344a3780SDimitry Andric StreamString stream;
3040344a3780SDimitry Andric ProcessAttachInfo attach_info = GetAttachInfo();
3041344a3780SDimitry Andric Status status = target->Attach(attach_info, &stream);
3042344a3780SDimitry Andric
3043344a3780SDimitry Andric if (status.Fail()) {
3044344a3780SDimitry Andric SetError(status.AsCString());
3045344a3780SDimitry Andric return;
3046344a3780SDimitry Andric }
3047344a3780SDimitry Andric
3048344a3780SDimitry Andric ProcessSP process_sp(target->GetProcessSP());
3049344a3780SDimitry Andric if (!process_sp) {
3050344a3780SDimitry Andric SetError("Attached sucessfully but target has no process.");
3051344a3780SDimitry Andric return;
3052344a3780SDimitry Andric }
3053344a3780SDimitry Andric
3054344a3780SDimitry Andric if (attach_info.GetContinueOnceAttached())
3055344a3780SDimitry Andric process_sp->Resume();
3056344a3780SDimitry Andric
3057344a3780SDimitry Andric window.GetParent()->RemoveSubWindow(&window);
3058344a3780SDimitry Andric }
3059344a3780SDimitry Andric
3060344a3780SDimitry Andric protected:
3061344a3780SDimitry Andric Debugger &m_debugger;
3062344a3780SDimitry Andric WindowSP m_main_window_sp;
3063344a3780SDimitry Andric
3064344a3780SDimitry Andric ChoicesFieldDelegate *m_type_field;
3065344a3780SDimitry Andric IntegerFieldDelegate *m_pid_field;
3066344a3780SDimitry Andric TextFieldDelegate *m_name_field;
3067344a3780SDimitry Andric BooleanFieldDelegate *m_continue_field;
3068344a3780SDimitry Andric BooleanFieldDelegate *m_wait_for_field;
3069344a3780SDimitry Andric BooleanFieldDelegate *m_include_existing_field;
3070344a3780SDimitry Andric BooleanFieldDelegate *m_show_advanced_field;
3071344a3780SDimitry Andric ProcessPluginFieldDelegate *m_plugin_field;
3072344a3780SDimitry Andric };
3073344a3780SDimitry Andric
3074c0981da4SDimitry Andric class TargetCreateFormDelegate : public FormDelegate {
3075c0981da4SDimitry Andric public:
TargetCreateFormDelegate(Debugger & debugger)3076c0981da4SDimitry Andric TargetCreateFormDelegate(Debugger &debugger) : m_debugger(debugger) {
3077c0981da4SDimitry Andric m_executable_field = AddFileField("Executable", "", /*need_to_exist=*/true,
3078c0981da4SDimitry Andric /*required=*/true);
3079c0981da4SDimitry Andric m_core_file_field = AddFileField("Core File", "", /*need_to_exist=*/true,
3080c0981da4SDimitry Andric /*required=*/false);
3081c0981da4SDimitry Andric m_symbol_file_field = AddFileField(
3082c0981da4SDimitry Andric "Symbol File", "", /*need_to_exist=*/true, /*required=*/false);
3083c0981da4SDimitry Andric m_show_advanced_field = AddBooleanField("Show advanced settings.", false);
3084c0981da4SDimitry Andric m_remote_file_field = AddFileField(
3085c0981da4SDimitry Andric "Remote File", "", /*need_to_exist=*/false, /*required=*/false);
3086c0981da4SDimitry Andric m_arch_field = AddArchField("Architecture", "", /*required=*/false);
3087c0981da4SDimitry Andric m_platform_field = AddPlatformPluginField(debugger);
3088c0981da4SDimitry Andric m_load_dependent_files_field =
3089c0981da4SDimitry Andric AddChoicesField("Load Dependents", 3, GetLoadDependentFilesChoices());
3090c0981da4SDimitry Andric
3091c0981da4SDimitry Andric AddAction("Create", [this](Window &window) { CreateTarget(window); });
3092c0981da4SDimitry Andric }
3093c0981da4SDimitry Andric
GetName()3094c0981da4SDimitry Andric std::string GetName() override { return "Create Target"; }
3095c0981da4SDimitry Andric
UpdateFieldsVisibility()3096c0981da4SDimitry Andric void UpdateFieldsVisibility() override {
3097c0981da4SDimitry Andric if (m_show_advanced_field->GetBoolean()) {
3098c0981da4SDimitry Andric m_remote_file_field->FieldDelegateShow();
3099c0981da4SDimitry Andric m_arch_field->FieldDelegateShow();
3100c0981da4SDimitry Andric m_platform_field->FieldDelegateShow();
3101c0981da4SDimitry Andric m_load_dependent_files_field->FieldDelegateShow();
3102c0981da4SDimitry Andric } else {
3103c0981da4SDimitry Andric m_remote_file_field->FieldDelegateHide();
3104c0981da4SDimitry Andric m_arch_field->FieldDelegateHide();
3105c0981da4SDimitry Andric m_platform_field->FieldDelegateHide();
3106c0981da4SDimitry Andric m_load_dependent_files_field->FieldDelegateHide();
3107c0981da4SDimitry Andric }
3108c0981da4SDimitry Andric }
3109c0981da4SDimitry Andric
3110c0981da4SDimitry Andric static constexpr const char *kLoadDependentFilesNo = "No";
3111c0981da4SDimitry Andric static constexpr const char *kLoadDependentFilesYes = "Yes";
3112c0981da4SDimitry Andric static constexpr const char *kLoadDependentFilesExecOnly = "Executable only";
3113c0981da4SDimitry Andric
GetLoadDependentFilesChoices()3114c0981da4SDimitry Andric std::vector<std::string> GetLoadDependentFilesChoices() {
3115e3b55780SDimitry Andric std::vector<std::string> load_dependents_options;
3116e3b55780SDimitry Andric load_dependents_options.push_back(kLoadDependentFilesExecOnly);
3117e3b55780SDimitry Andric load_dependents_options.push_back(kLoadDependentFilesYes);
3118e3b55780SDimitry Andric load_dependents_options.push_back(kLoadDependentFilesNo);
3119e3b55780SDimitry Andric return load_dependents_options;
3120c0981da4SDimitry Andric }
3121c0981da4SDimitry Andric
GetLoadDependentFiles()3122c0981da4SDimitry Andric LoadDependentFiles GetLoadDependentFiles() {
3123c0981da4SDimitry Andric std::string choice = m_load_dependent_files_field->GetChoiceContent();
3124c0981da4SDimitry Andric if (choice == kLoadDependentFilesNo)
3125c0981da4SDimitry Andric return eLoadDependentsNo;
3126c0981da4SDimitry Andric if (choice == kLoadDependentFilesYes)
3127c0981da4SDimitry Andric return eLoadDependentsYes;
3128c0981da4SDimitry Andric return eLoadDependentsDefault;
3129c0981da4SDimitry Andric }
3130c0981da4SDimitry Andric
GetPlatformOptions()3131c0981da4SDimitry Andric OptionGroupPlatform GetPlatformOptions() {
3132c0981da4SDimitry Andric OptionGroupPlatform platform_options(false);
3133c0981da4SDimitry Andric platform_options.SetPlatformName(m_platform_field->GetPluginName().c_str());
3134c0981da4SDimitry Andric return platform_options;
3135c0981da4SDimitry Andric }
3136c0981da4SDimitry Andric
GetTarget()3137c0981da4SDimitry Andric TargetSP GetTarget() {
3138c0981da4SDimitry Andric OptionGroupPlatform platform_options = GetPlatformOptions();
3139c0981da4SDimitry Andric TargetSP target_sp;
3140c0981da4SDimitry Andric Status status = m_debugger.GetTargetList().CreateTarget(
3141c0981da4SDimitry Andric m_debugger, m_executable_field->GetPath(),
3142c0981da4SDimitry Andric m_arch_field->GetArchString(), GetLoadDependentFiles(),
3143c0981da4SDimitry Andric &platform_options, target_sp);
3144c0981da4SDimitry Andric
3145c0981da4SDimitry Andric if (status.Fail()) {
3146c0981da4SDimitry Andric SetError(status.AsCString());
3147c0981da4SDimitry Andric return nullptr;
3148c0981da4SDimitry Andric }
3149c0981da4SDimitry Andric
3150c0981da4SDimitry Andric m_debugger.GetTargetList().SetSelectedTarget(target_sp);
3151c0981da4SDimitry Andric
3152c0981da4SDimitry Andric return target_sp;
3153c0981da4SDimitry Andric }
3154c0981da4SDimitry Andric
SetSymbolFile(TargetSP target_sp)3155c0981da4SDimitry Andric void SetSymbolFile(TargetSP target_sp) {
3156c0981da4SDimitry Andric if (!m_symbol_file_field->IsSpecified())
3157c0981da4SDimitry Andric return;
3158c0981da4SDimitry Andric
3159c0981da4SDimitry Andric ModuleSP module_sp(target_sp->GetExecutableModule());
3160c0981da4SDimitry Andric if (!module_sp)
3161c0981da4SDimitry Andric return;
3162c0981da4SDimitry Andric
3163c0981da4SDimitry Andric module_sp->SetSymbolFileFileSpec(
3164c0981da4SDimitry Andric m_symbol_file_field->GetResolvedFileSpec());
3165c0981da4SDimitry Andric }
3166c0981da4SDimitry Andric
SetCoreFile(TargetSP target_sp)3167c0981da4SDimitry Andric void SetCoreFile(TargetSP target_sp) {
3168c0981da4SDimitry Andric if (!m_core_file_field->IsSpecified())
3169c0981da4SDimitry Andric return;
3170c0981da4SDimitry Andric
3171c0981da4SDimitry Andric FileSpec core_file_spec = m_core_file_field->GetResolvedFileSpec();
3172c0981da4SDimitry Andric
3173c0981da4SDimitry Andric FileSpec core_file_directory_spec;
3174e3b55780SDimitry Andric core_file_directory_spec.SetDirectory(core_file_spec.GetDirectory());
3175c0981da4SDimitry Andric target_sp->AppendExecutableSearchPaths(core_file_directory_spec);
3176c0981da4SDimitry Andric
3177c0981da4SDimitry Andric ProcessSP process_sp(target_sp->CreateProcess(
3178c0981da4SDimitry Andric m_debugger.GetListener(), llvm::StringRef(), &core_file_spec, false));
3179c0981da4SDimitry Andric
3180c0981da4SDimitry Andric if (!process_sp) {
3181b1c73532SDimitry Andric SetError("Unknown core file format!");
3182c0981da4SDimitry Andric return;
3183c0981da4SDimitry Andric }
3184c0981da4SDimitry Andric
3185c0981da4SDimitry Andric Status status = process_sp->LoadCore();
3186c0981da4SDimitry Andric if (status.Fail()) {
3187b1c73532SDimitry Andric SetError("Unknown core file format!");
3188c0981da4SDimitry Andric return;
3189c0981da4SDimitry Andric }
3190c0981da4SDimitry Andric }
3191c0981da4SDimitry Andric
SetRemoteFile(TargetSP target_sp)3192c0981da4SDimitry Andric void SetRemoteFile(TargetSP target_sp) {
3193c0981da4SDimitry Andric if (!m_remote_file_field->IsSpecified())
3194c0981da4SDimitry Andric return;
3195c0981da4SDimitry Andric
3196c0981da4SDimitry Andric ModuleSP module_sp(target_sp->GetExecutableModule());
3197c0981da4SDimitry Andric if (!module_sp)
3198c0981da4SDimitry Andric return;
3199c0981da4SDimitry Andric
3200c0981da4SDimitry Andric FileSpec remote_file_spec = m_remote_file_field->GetFileSpec();
3201c0981da4SDimitry Andric module_sp->SetPlatformFileSpec(remote_file_spec);
3202c0981da4SDimitry Andric }
3203c0981da4SDimitry Andric
RemoveTarget(TargetSP target_sp)3204c0981da4SDimitry Andric void RemoveTarget(TargetSP target_sp) {
3205c0981da4SDimitry Andric m_debugger.GetTargetList().DeleteTarget(target_sp);
3206c0981da4SDimitry Andric }
3207c0981da4SDimitry Andric
CreateTarget(Window & window)3208c0981da4SDimitry Andric void CreateTarget(Window &window) {
3209c0981da4SDimitry Andric ClearError();
3210c0981da4SDimitry Andric
3211c0981da4SDimitry Andric bool all_fields_are_valid = CheckFieldsValidity();
3212c0981da4SDimitry Andric if (!all_fields_are_valid)
3213c0981da4SDimitry Andric return;
3214c0981da4SDimitry Andric
3215c0981da4SDimitry Andric TargetSP target_sp = GetTarget();
3216c0981da4SDimitry Andric if (HasError())
3217c0981da4SDimitry Andric return;
3218c0981da4SDimitry Andric
3219c0981da4SDimitry Andric SetSymbolFile(target_sp);
3220c0981da4SDimitry Andric if (HasError()) {
3221c0981da4SDimitry Andric RemoveTarget(target_sp);
3222c0981da4SDimitry Andric return;
3223c0981da4SDimitry Andric }
3224c0981da4SDimitry Andric
3225c0981da4SDimitry Andric SetCoreFile(target_sp);
3226c0981da4SDimitry Andric if (HasError()) {
3227c0981da4SDimitry Andric RemoveTarget(target_sp);
3228c0981da4SDimitry Andric return;
3229c0981da4SDimitry Andric }
3230c0981da4SDimitry Andric
3231c0981da4SDimitry Andric SetRemoteFile(target_sp);
3232c0981da4SDimitry Andric if (HasError()) {
3233c0981da4SDimitry Andric RemoveTarget(target_sp);
3234c0981da4SDimitry Andric return;
3235c0981da4SDimitry Andric }
3236c0981da4SDimitry Andric
3237c0981da4SDimitry Andric window.GetParent()->RemoveSubWindow(&window);
3238c0981da4SDimitry Andric }
3239c0981da4SDimitry Andric
3240c0981da4SDimitry Andric protected:
3241c0981da4SDimitry Andric Debugger &m_debugger;
3242c0981da4SDimitry Andric
3243c0981da4SDimitry Andric FileFieldDelegate *m_executable_field;
3244c0981da4SDimitry Andric FileFieldDelegate *m_core_file_field;
3245c0981da4SDimitry Andric FileFieldDelegate *m_symbol_file_field;
3246c0981da4SDimitry Andric BooleanFieldDelegate *m_show_advanced_field;
3247c0981da4SDimitry Andric FileFieldDelegate *m_remote_file_field;
3248c0981da4SDimitry Andric ArchFieldDelegate *m_arch_field;
3249c0981da4SDimitry Andric PlatformPluginFieldDelegate *m_platform_field;
3250c0981da4SDimitry Andric ChoicesFieldDelegate *m_load_dependent_files_field;
3251c0981da4SDimitry Andric };
3252c0981da4SDimitry Andric
3253c0981da4SDimitry Andric class ProcessLaunchFormDelegate : public FormDelegate {
3254c0981da4SDimitry Andric public:
ProcessLaunchFormDelegate(Debugger & debugger,WindowSP main_window_sp)3255c0981da4SDimitry Andric ProcessLaunchFormDelegate(Debugger &debugger, WindowSP main_window_sp)
3256c0981da4SDimitry Andric : m_debugger(debugger), m_main_window_sp(main_window_sp) {
3257c0981da4SDimitry Andric
3258c0981da4SDimitry Andric m_arguments_field = AddArgumentsField();
3259c0981da4SDimitry Andric SetArgumentsFieldDefaultValue();
3260c0981da4SDimitry Andric m_target_environment_field =
3261c0981da4SDimitry Andric AddEnvironmentVariableListField("Target Environment Variables");
3262c0981da4SDimitry Andric SetTargetEnvironmentFieldDefaultValue();
3263c0981da4SDimitry Andric m_working_directory_field = AddDirectoryField(
3264c0981da4SDimitry Andric "Working Directory", GetDefaultWorkingDirectory().c_str(), true, false);
3265c0981da4SDimitry Andric
3266c0981da4SDimitry Andric m_show_advanced_field = AddBooleanField("Show advanced settings.", false);
3267c0981da4SDimitry Andric
3268c0981da4SDimitry Andric m_stop_at_entry_field = AddBooleanField("Stop at entry point.", false);
3269c0981da4SDimitry Andric m_detach_on_error_field =
3270c0981da4SDimitry Andric AddBooleanField("Detach on error.", GetDefaultDetachOnError());
3271c0981da4SDimitry Andric m_disable_aslr_field =
3272c0981da4SDimitry Andric AddBooleanField("Disable ASLR", GetDefaultDisableASLR());
3273c0981da4SDimitry Andric m_plugin_field = AddProcessPluginField();
3274c0981da4SDimitry Andric m_arch_field = AddArchField("Architecture", "", false);
3275c0981da4SDimitry Andric m_shell_field = AddFileField("Shell", "", true, false);
3276c0981da4SDimitry Andric m_expand_shell_arguments_field =
3277c0981da4SDimitry Andric AddBooleanField("Expand shell arguments.", false);
3278c0981da4SDimitry Andric
3279c0981da4SDimitry Andric m_disable_standard_io_field =
3280c0981da4SDimitry Andric AddBooleanField("Disable Standard IO", GetDefaultDisableStandardIO());
3281c0981da4SDimitry Andric m_standard_output_field =
3282c0981da4SDimitry Andric AddFileField("Standard Output File", "", /*need_to_exist=*/false,
3283c0981da4SDimitry Andric /*required=*/false);
3284c0981da4SDimitry Andric m_standard_error_field =
3285c0981da4SDimitry Andric AddFileField("Standard Error File", "", /*need_to_exist=*/false,
3286c0981da4SDimitry Andric /*required=*/false);
3287c0981da4SDimitry Andric m_standard_input_field =
3288c0981da4SDimitry Andric AddFileField("Standard Input File", "", /*need_to_exist=*/false,
3289c0981da4SDimitry Andric /*required=*/false);
3290c0981da4SDimitry Andric
3291c0981da4SDimitry Andric m_show_inherited_environment_field =
3292c0981da4SDimitry Andric AddBooleanField("Show inherited environment variables.", false);
3293c0981da4SDimitry Andric m_inherited_environment_field =
3294c0981da4SDimitry Andric AddEnvironmentVariableListField("Inherited Environment Variables");
3295c0981da4SDimitry Andric SetInheritedEnvironmentFieldDefaultValue();
3296c0981da4SDimitry Andric
3297c0981da4SDimitry Andric AddAction("Launch", [this](Window &window) { Launch(window); });
3298c0981da4SDimitry Andric }
3299c0981da4SDimitry Andric
GetName()3300c0981da4SDimitry Andric std::string GetName() override { return "Launch Process"; }
3301c0981da4SDimitry Andric
UpdateFieldsVisibility()3302c0981da4SDimitry Andric void UpdateFieldsVisibility() override {
3303c0981da4SDimitry Andric if (m_show_advanced_field->GetBoolean()) {
3304c0981da4SDimitry Andric m_stop_at_entry_field->FieldDelegateShow();
3305c0981da4SDimitry Andric m_detach_on_error_field->FieldDelegateShow();
3306c0981da4SDimitry Andric m_disable_aslr_field->FieldDelegateShow();
3307c0981da4SDimitry Andric m_plugin_field->FieldDelegateShow();
3308c0981da4SDimitry Andric m_arch_field->FieldDelegateShow();
3309c0981da4SDimitry Andric m_shell_field->FieldDelegateShow();
3310c0981da4SDimitry Andric m_expand_shell_arguments_field->FieldDelegateShow();
3311c0981da4SDimitry Andric m_disable_standard_io_field->FieldDelegateShow();
3312c0981da4SDimitry Andric if (m_disable_standard_io_field->GetBoolean()) {
3313c0981da4SDimitry Andric m_standard_input_field->FieldDelegateHide();
3314c0981da4SDimitry Andric m_standard_output_field->FieldDelegateHide();
3315c0981da4SDimitry Andric m_standard_error_field->FieldDelegateHide();
3316c0981da4SDimitry Andric } else {
3317c0981da4SDimitry Andric m_standard_input_field->FieldDelegateShow();
3318c0981da4SDimitry Andric m_standard_output_field->FieldDelegateShow();
3319c0981da4SDimitry Andric m_standard_error_field->FieldDelegateShow();
3320c0981da4SDimitry Andric }
3321c0981da4SDimitry Andric m_show_inherited_environment_field->FieldDelegateShow();
3322c0981da4SDimitry Andric if (m_show_inherited_environment_field->GetBoolean())
3323c0981da4SDimitry Andric m_inherited_environment_field->FieldDelegateShow();
3324c0981da4SDimitry Andric else
3325c0981da4SDimitry Andric m_inherited_environment_field->FieldDelegateHide();
3326c0981da4SDimitry Andric } else {
3327c0981da4SDimitry Andric m_stop_at_entry_field->FieldDelegateHide();
3328c0981da4SDimitry Andric m_detach_on_error_field->FieldDelegateHide();
3329c0981da4SDimitry Andric m_disable_aslr_field->FieldDelegateHide();
3330c0981da4SDimitry Andric m_plugin_field->FieldDelegateHide();
3331c0981da4SDimitry Andric m_arch_field->FieldDelegateHide();
3332c0981da4SDimitry Andric m_shell_field->FieldDelegateHide();
3333c0981da4SDimitry Andric m_expand_shell_arguments_field->FieldDelegateHide();
3334c0981da4SDimitry Andric m_disable_standard_io_field->FieldDelegateHide();
3335c0981da4SDimitry Andric m_standard_input_field->FieldDelegateHide();
3336c0981da4SDimitry Andric m_standard_output_field->FieldDelegateHide();
3337c0981da4SDimitry Andric m_standard_error_field->FieldDelegateHide();
3338c0981da4SDimitry Andric m_show_inherited_environment_field->FieldDelegateHide();
3339c0981da4SDimitry Andric m_inherited_environment_field->FieldDelegateHide();
3340c0981da4SDimitry Andric }
3341c0981da4SDimitry Andric }
3342c0981da4SDimitry Andric
3343c0981da4SDimitry Andric // Methods for setting the default value of the fields.
3344c0981da4SDimitry Andric
SetArgumentsFieldDefaultValue()3345c0981da4SDimitry Andric void SetArgumentsFieldDefaultValue() {
3346c0981da4SDimitry Andric TargetSP target = m_debugger.GetSelectedTarget();
3347c0981da4SDimitry Andric if (target == nullptr)
3348c0981da4SDimitry Andric return;
3349c0981da4SDimitry Andric
3350c0981da4SDimitry Andric const Args &target_arguments =
3351c0981da4SDimitry Andric target->GetProcessLaunchInfo().GetArguments();
3352c0981da4SDimitry Andric m_arguments_field->AddArguments(target_arguments);
3353c0981da4SDimitry Andric }
3354c0981da4SDimitry Andric
SetTargetEnvironmentFieldDefaultValue()3355c0981da4SDimitry Andric void SetTargetEnvironmentFieldDefaultValue() {
3356c0981da4SDimitry Andric TargetSP target = m_debugger.GetSelectedTarget();
3357c0981da4SDimitry Andric if (target == nullptr)
3358c0981da4SDimitry Andric return;
3359c0981da4SDimitry Andric
3360c0981da4SDimitry Andric const Environment &target_environment = target->GetTargetEnvironment();
3361c0981da4SDimitry Andric m_target_environment_field->AddEnvironmentVariables(target_environment);
3362c0981da4SDimitry Andric }
3363c0981da4SDimitry Andric
SetInheritedEnvironmentFieldDefaultValue()3364c0981da4SDimitry Andric void SetInheritedEnvironmentFieldDefaultValue() {
3365c0981da4SDimitry Andric TargetSP target = m_debugger.GetSelectedTarget();
3366c0981da4SDimitry Andric if (target == nullptr)
3367c0981da4SDimitry Andric return;
3368c0981da4SDimitry Andric
3369c0981da4SDimitry Andric const Environment &inherited_environment =
3370c0981da4SDimitry Andric target->GetInheritedEnvironment();
3371c0981da4SDimitry Andric m_inherited_environment_field->AddEnvironmentVariables(
3372c0981da4SDimitry Andric inherited_environment);
3373c0981da4SDimitry Andric }
3374c0981da4SDimitry Andric
GetDefaultWorkingDirectory()3375c0981da4SDimitry Andric std::string GetDefaultWorkingDirectory() {
3376c0981da4SDimitry Andric TargetSP target = m_debugger.GetSelectedTarget();
3377c0981da4SDimitry Andric if (target == nullptr)
3378c0981da4SDimitry Andric return "";
3379c0981da4SDimitry Andric
3380c0981da4SDimitry Andric PlatformSP platform = target->GetPlatform();
3381c0981da4SDimitry Andric return platform->GetWorkingDirectory().GetPath();
3382c0981da4SDimitry Andric }
3383c0981da4SDimitry Andric
GetDefaultDisableASLR()3384c0981da4SDimitry Andric bool GetDefaultDisableASLR() {
3385c0981da4SDimitry Andric TargetSP target = m_debugger.GetSelectedTarget();
3386c0981da4SDimitry Andric if (target == nullptr)
3387c0981da4SDimitry Andric return false;
3388c0981da4SDimitry Andric
3389c0981da4SDimitry Andric return target->GetDisableASLR();
3390c0981da4SDimitry Andric }
3391c0981da4SDimitry Andric
GetDefaultDisableStandardIO()3392c0981da4SDimitry Andric bool GetDefaultDisableStandardIO() {
3393c0981da4SDimitry Andric TargetSP target = m_debugger.GetSelectedTarget();
3394c0981da4SDimitry Andric if (target == nullptr)
3395c0981da4SDimitry Andric return true;
3396c0981da4SDimitry Andric
3397c0981da4SDimitry Andric return target->GetDisableSTDIO();
3398c0981da4SDimitry Andric }
3399c0981da4SDimitry Andric
GetDefaultDetachOnError()3400c0981da4SDimitry Andric bool GetDefaultDetachOnError() {
3401c0981da4SDimitry Andric TargetSP target = m_debugger.GetSelectedTarget();
3402c0981da4SDimitry Andric if (target == nullptr)
3403c0981da4SDimitry Andric return true;
3404c0981da4SDimitry Andric
3405c0981da4SDimitry Andric return target->GetDetachOnError();
3406c0981da4SDimitry Andric }
3407c0981da4SDimitry Andric
3408c0981da4SDimitry Andric // Methods for getting the necessary information and setting them to the
3409c0981da4SDimitry Andric // ProcessLaunchInfo.
3410c0981da4SDimitry Andric
GetExecutableSettings(ProcessLaunchInfo & launch_info)3411c0981da4SDimitry Andric void GetExecutableSettings(ProcessLaunchInfo &launch_info) {
3412c0981da4SDimitry Andric TargetSP target = m_debugger.GetSelectedTarget();
3413c0981da4SDimitry Andric ModuleSP executable_module = target->GetExecutableModule();
3414c0981da4SDimitry Andric llvm::StringRef target_settings_argv0 = target->GetArg0();
3415c0981da4SDimitry Andric
3416c0981da4SDimitry Andric if (!target_settings_argv0.empty()) {
3417c0981da4SDimitry Andric launch_info.GetArguments().AppendArgument(target_settings_argv0);
3418c0981da4SDimitry Andric launch_info.SetExecutableFile(executable_module->GetPlatformFileSpec(),
3419c0981da4SDimitry Andric false);
3420c0981da4SDimitry Andric return;
3421c0981da4SDimitry Andric }
3422c0981da4SDimitry Andric
3423c0981da4SDimitry Andric launch_info.SetExecutableFile(executable_module->GetPlatformFileSpec(),
3424c0981da4SDimitry Andric true);
3425c0981da4SDimitry Andric }
3426c0981da4SDimitry Andric
GetArguments(ProcessLaunchInfo & launch_info)3427c0981da4SDimitry Andric void GetArguments(ProcessLaunchInfo &launch_info) {
3428c0981da4SDimitry Andric TargetSP target = m_debugger.GetSelectedTarget();
3429c0981da4SDimitry Andric Args arguments = m_arguments_field->GetArguments();
3430c0981da4SDimitry Andric launch_info.GetArguments().AppendArguments(arguments);
3431c0981da4SDimitry Andric }
3432c0981da4SDimitry Andric
GetEnvironment(ProcessLaunchInfo & launch_info)3433c0981da4SDimitry Andric void GetEnvironment(ProcessLaunchInfo &launch_info) {
3434c0981da4SDimitry Andric Environment target_environment =
3435c0981da4SDimitry Andric m_target_environment_field->GetEnvironment();
3436c0981da4SDimitry Andric Environment inherited_environment =
3437c0981da4SDimitry Andric m_inherited_environment_field->GetEnvironment();
3438c0981da4SDimitry Andric launch_info.GetEnvironment().insert(target_environment.begin(),
3439c0981da4SDimitry Andric target_environment.end());
3440c0981da4SDimitry Andric launch_info.GetEnvironment().insert(inherited_environment.begin(),
3441c0981da4SDimitry Andric inherited_environment.end());
3442c0981da4SDimitry Andric }
3443c0981da4SDimitry Andric
GetWorkingDirectory(ProcessLaunchInfo & launch_info)3444c0981da4SDimitry Andric void GetWorkingDirectory(ProcessLaunchInfo &launch_info) {
3445c0981da4SDimitry Andric if (m_working_directory_field->IsSpecified())
3446c0981da4SDimitry Andric launch_info.SetWorkingDirectory(
3447c0981da4SDimitry Andric m_working_directory_field->GetResolvedFileSpec());
3448c0981da4SDimitry Andric }
3449c0981da4SDimitry Andric
GetStopAtEntry(ProcessLaunchInfo & launch_info)3450c0981da4SDimitry Andric void GetStopAtEntry(ProcessLaunchInfo &launch_info) {
3451c0981da4SDimitry Andric if (m_stop_at_entry_field->GetBoolean())
3452c0981da4SDimitry Andric launch_info.GetFlags().Set(eLaunchFlagStopAtEntry);
3453c0981da4SDimitry Andric else
3454c0981da4SDimitry Andric launch_info.GetFlags().Clear(eLaunchFlagStopAtEntry);
3455c0981da4SDimitry Andric }
3456c0981da4SDimitry Andric
GetDetachOnError(ProcessLaunchInfo & launch_info)3457c0981da4SDimitry Andric void GetDetachOnError(ProcessLaunchInfo &launch_info) {
3458c0981da4SDimitry Andric if (m_detach_on_error_field->GetBoolean())
3459c0981da4SDimitry Andric launch_info.GetFlags().Set(eLaunchFlagDetachOnError);
3460c0981da4SDimitry Andric else
3461c0981da4SDimitry Andric launch_info.GetFlags().Clear(eLaunchFlagDetachOnError);
3462c0981da4SDimitry Andric }
3463c0981da4SDimitry Andric
GetDisableASLR(ProcessLaunchInfo & launch_info)3464c0981da4SDimitry Andric void GetDisableASLR(ProcessLaunchInfo &launch_info) {
3465c0981da4SDimitry Andric if (m_disable_aslr_field->GetBoolean())
3466c0981da4SDimitry Andric launch_info.GetFlags().Set(eLaunchFlagDisableASLR);
3467c0981da4SDimitry Andric else
3468c0981da4SDimitry Andric launch_info.GetFlags().Clear(eLaunchFlagDisableASLR);
3469c0981da4SDimitry Andric }
3470c0981da4SDimitry Andric
GetPlugin(ProcessLaunchInfo & launch_info)3471c0981da4SDimitry Andric void GetPlugin(ProcessLaunchInfo &launch_info) {
3472c0981da4SDimitry Andric launch_info.SetProcessPluginName(m_plugin_field->GetPluginName());
3473c0981da4SDimitry Andric }
3474c0981da4SDimitry Andric
GetArch(ProcessLaunchInfo & launch_info)3475c0981da4SDimitry Andric void GetArch(ProcessLaunchInfo &launch_info) {
3476c0981da4SDimitry Andric if (!m_arch_field->IsSpecified())
3477c0981da4SDimitry Andric return;
3478c0981da4SDimitry Andric
3479c0981da4SDimitry Andric TargetSP target_sp = m_debugger.GetSelectedTarget();
3480c0981da4SDimitry Andric PlatformSP platform_sp =
3481c0981da4SDimitry Andric target_sp ? target_sp->GetPlatform() : PlatformSP();
3482c0981da4SDimitry Andric launch_info.GetArchitecture() = Platform::GetAugmentedArchSpec(
3483c0981da4SDimitry Andric platform_sp.get(), m_arch_field->GetArchString());
3484c0981da4SDimitry Andric }
3485c0981da4SDimitry Andric
GetShell(ProcessLaunchInfo & launch_info)3486c0981da4SDimitry Andric void GetShell(ProcessLaunchInfo &launch_info) {
3487c0981da4SDimitry Andric if (!m_shell_field->IsSpecified())
3488c0981da4SDimitry Andric return;
3489c0981da4SDimitry Andric
3490c0981da4SDimitry Andric launch_info.SetShell(m_shell_field->GetResolvedFileSpec());
3491c0981da4SDimitry Andric launch_info.SetShellExpandArguments(
3492c0981da4SDimitry Andric m_expand_shell_arguments_field->GetBoolean());
3493c0981da4SDimitry Andric }
3494c0981da4SDimitry Andric
GetStandardIO(ProcessLaunchInfo & launch_info)3495c0981da4SDimitry Andric void GetStandardIO(ProcessLaunchInfo &launch_info) {
3496c0981da4SDimitry Andric if (m_disable_standard_io_field->GetBoolean()) {
3497c0981da4SDimitry Andric launch_info.GetFlags().Set(eLaunchFlagDisableSTDIO);
3498c0981da4SDimitry Andric return;
3499c0981da4SDimitry Andric }
3500c0981da4SDimitry Andric
3501c0981da4SDimitry Andric FileAction action;
3502c0981da4SDimitry Andric if (m_standard_input_field->IsSpecified()) {
3503e3b55780SDimitry Andric if (action.Open(STDIN_FILENO, m_standard_input_field->GetFileSpec(), true,
3504e3b55780SDimitry Andric false))
3505c0981da4SDimitry Andric launch_info.AppendFileAction(action);
3506c0981da4SDimitry Andric }
3507c0981da4SDimitry Andric if (m_standard_output_field->IsSpecified()) {
3508e3b55780SDimitry Andric if (action.Open(STDOUT_FILENO, m_standard_output_field->GetFileSpec(),
3509e3b55780SDimitry Andric false, true))
3510c0981da4SDimitry Andric launch_info.AppendFileAction(action);
3511c0981da4SDimitry Andric }
3512c0981da4SDimitry Andric if (m_standard_error_field->IsSpecified()) {
3513e3b55780SDimitry Andric if (action.Open(STDERR_FILENO, m_standard_error_field->GetFileSpec(),
3514e3b55780SDimitry Andric false, true))
3515c0981da4SDimitry Andric launch_info.AppendFileAction(action);
3516c0981da4SDimitry Andric }
3517c0981da4SDimitry Andric }
3518c0981da4SDimitry Andric
GetInheritTCC(ProcessLaunchInfo & launch_info)3519c0981da4SDimitry Andric void GetInheritTCC(ProcessLaunchInfo &launch_info) {
3520c0981da4SDimitry Andric if (m_debugger.GetSelectedTarget()->GetInheritTCC())
3521c0981da4SDimitry Andric launch_info.GetFlags().Set(eLaunchFlagInheritTCCFromParent);
3522c0981da4SDimitry Andric }
3523c0981da4SDimitry Andric
GetLaunchInfo()3524c0981da4SDimitry Andric ProcessLaunchInfo GetLaunchInfo() {
3525c0981da4SDimitry Andric ProcessLaunchInfo launch_info;
3526c0981da4SDimitry Andric
3527c0981da4SDimitry Andric GetExecutableSettings(launch_info);
3528c0981da4SDimitry Andric GetArguments(launch_info);
3529c0981da4SDimitry Andric GetEnvironment(launch_info);
3530c0981da4SDimitry Andric GetWorkingDirectory(launch_info);
3531c0981da4SDimitry Andric GetStopAtEntry(launch_info);
3532c0981da4SDimitry Andric GetDetachOnError(launch_info);
3533c0981da4SDimitry Andric GetDisableASLR(launch_info);
3534c0981da4SDimitry Andric GetPlugin(launch_info);
3535c0981da4SDimitry Andric GetArch(launch_info);
3536c0981da4SDimitry Andric GetShell(launch_info);
3537c0981da4SDimitry Andric GetStandardIO(launch_info);
3538c0981da4SDimitry Andric GetInheritTCC(launch_info);
3539c0981da4SDimitry Andric
3540c0981da4SDimitry Andric return launch_info;
3541c0981da4SDimitry Andric }
3542c0981da4SDimitry Andric
StopRunningProcess()3543c0981da4SDimitry Andric bool StopRunningProcess() {
3544c0981da4SDimitry Andric ExecutionContext exe_ctx =
3545c0981da4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
3546c0981da4SDimitry Andric
3547c0981da4SDimitry Andric if (!exe_ctx.HasProcessScope())
3548c0981da4SDimitry Andric return false;
3549c0981da4SDimitry Andric
3550c0981da4SDimitry Andric Process *process = exe_ctx.GetProcessPtr();
3551c0981da4SDimitry Andric if (!(process && process->IsAlive()))
3552c0981da4SDimitry Andric return false;
3553c0981da4SDimitry Andric
3554c0981da4SDimitry Andric FormDelegateSP form_delegate_sp =
3555c0981da4SDimitry Andric FormDelegateSP(new DetachOrKillProcessFormDelegate(process));
3556c0981da4SDimitry Andric Rect bounds = m_main_window_sp->GetCenteredRect(85, 8);
3557c0981da4SDimitry Andric WindowSP form_window_sp = m_main_window_sp->CreateSubWindow(
3558c0981da4SDimitry Andric form_delegate_sp->GetName().c_str(), bounds, true);
3559c0981da4SDimitry Andric WindowDelegateSP window_delegate_sp =
3560c0981da4SDimitry Andric WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
3561c0981da4SDimitry Andric form_window_sp->SetDelegate(window_delegate_sp);
3562c0981da4SDimitry Andric
3563c0981da4SDimitry Andric return true;
3564c0981da4SDimitry Andric }
3565c0981da4SDimitry Andric
GetTarget()3566c0981da4SDimitry Andric Target *GetTarget() {
3567c0981da4SDimitry Andric Target *target = m_debugger.GetSelectedTarget().get();
3568c0981da4SDimitry Andric
3569c0981da4SDimitry Andric if (target == nullptr) {
3570c0981da4SDimitry Andric SetError("No target exists!");
3571c0981da4SDimitry Andric return nullptr;
3572c0981da4SDimitry Andric }
3573c0981da4SDimitry Andric
3574c0981da4SDimitry Andric ModuleSP exe_module_sp = target->GetExecutableModule();
3575c0981da4SDimitry Andric
3576c0981da4SDimitry Andric if (exe_module_sp == nullptr) {
3577c0981da4SDimitry Andric SetError("No executable in target!");
3578c0981da4SDimitry Andric return nullptr;
3579c0981da4SDimitry Andric }
3580c0981da4SDimitry Andric
3581c0981da4SDimitry Andric return target;
3582c0981da4SDimitry Andric }
3583c0981da4SDimitry Andric
Launch(Window & window)3584c0981da4SDimitry Andric void Launch(Window &window) {
3585c0981da4SDimitry Andric ClearError();
3586c0981da4SDimitry Andric
3587c0981da4SDimitry Andric bool all_fields_are_valid = CheckFieldsValidity();
3588c0981da4SDimitry Andric if (!all_fields_are_valid)
3589c0981da4SDimitry Andric return;
3590c0981da4SDimitry Andric
3591c0981da4SDimitry Andric bool process_is_running = StopRunningProcess();
3592c0981da4SDimitry Andric if (process_is_running)
3593c0981da4SDimitry Andric return;
3594c0981da4SDimitry Andric
3595c0981da4SDimitry Andric Target *target = GetTarget();
3596c0981da4SDimitry Andric if (HasError())
3597c0981da4SDimitry Andric return;
3598c0981da4SDimitry Andric
3599c0981da4SDimitry Andric StreamString stream;
3600c0981da4SDimitry Andric ProcessLaunchInfo launch_info = GetLaunchInfo();
3601c0981da4SDimitry Andric Status status = target->Launch(launch_info, &stream);
3602c0981da4SDimitry Andric
3603c0981da4SDimitry Andric if (status.Fail()) {
3604c0981da4SDimitry Andric SetError(status.AsCString());
3605c0981da4SDimitry Andric return;
3606c0981da4SDimitry Andric }
3607c0981da4SDimitry Andric
3608c0981da4SDimitry Andric ProcessSP process_sp(target->GetProcessSP());
3609c0981da4SDimitry Andric if (!process_sp) {
3610c0981da4SDimitry Andric SetError("Launched successfully but target has no process!");
3611c0981da4SDimitry Andric return;
3612c0981da4SDimitry Andric }
3613c0981da4SDimitry Andric
3614c0981da4SDimitry Andric window.GetParent()->RemoveSubWindow(&window);
3615c0981da4SDimitry Andric }
3616c0981da4SDimitry Andric
3617c0981da4SDimitry Andric protected:
3618c0981da4SDimitry Andric Debugger &m_debugger;
3619c0981da4SDimitry Andric WindowSP m_main_window_sp;
3620c0981da4SDimitry Andric
3621c0981da4SDimitry Andric ArgumentsFieldDelegate *m_arguments_field;
3622c0981da4SDimitry Andric EnvironmentVariableListFieldDelegate *m_target_environment_field;
3623c0981da4SDimitry Andric DirectoryFieldDelegate *m_working_directory_field;
3624c0981da4SDimitry Andric
3625c0981da4SDimitry Andric BooleanFieldDelegate *m_show_advanced_field;
3626c0981da4SDimitry Andric
3627c0981da4SDimitry Andric BooleanFieldDelegate *m_stop_at_entry_field;
3628c0981da4SDimitry Andric BooleanFieldDelegate *m_detach_on_error_field;
3629c0981da4SDimitry Andric BooleanFieldDelegate *m_disable_aslr_field;
3630c0981da4SDimitry Andric ProcessPluginFieldDelegate *m_plugin_field;
3631c0981da4SDimitry Andric ArchFieldDelegate *m_arch_field;
3632c0981da4SDimitry Andric FileFieldDelegate *m_shell_field;
3633c0981da4SDimitry Andric BooleanFieldDelegate *m_expand_shell_arguments_field;
3634c0981da4SDimitry Andric BooleanFieldDelegate *m_disable_standard_io_field;
3635c0981da4SDimitry Andric FileFieldDelegate *m_standard_input_field;
3636c0981da4SDimitry Andric FileFieldDelegate *m_standard_output_field;
3637c0981da4SDimitry Andric FileFieldDelegate *m_standard_error_field;
3638c0981da4SDimitry Andric
3639c0981da4SDimitry Andric BooleanFieldDelegate *m_show_inherited_environment_field;
3640c0981da4SDimitry Andric EnvironmentVariableListFieldDelegate *m_inherited_environment_field;
3641c0981da4SDimitry Andric };
3642c0981da4SDimitry Andric
3643c0981da4SDimitry Andric ////////////
3644c0981da4SDimitry Andric // Searchers
3645c0981da4SDimitry Andric ////////////
3646c0981da4SDimitry Andric
3647c0981da4SDimitry Andric class SearcherDelegate {
3648c0981da4SDimitry Andric public:
3649145449b1SDimitry Andric SearcherDelegate() = default;
3650c0981da4SDimitry Andric
3651c0981da4SDimitry Andric virtual ~SearcherDelegate() = default;
3652c0981da4SDimitry Andric
3653c0981da4SDimitry Andric virtual int GetNumberOfMatches() = 0;
3654c0981da4SDimitry Andric
3655c0981da4SDimitry Andric // Get the string that will be displayed for the match at the input index.
3656c0981da4SDimitry Andric virtual const std::string &GetMatchTextAtIndex(int index) = 0;
3657c0981da4SDimitry Andric
3658c0981da4SDimitry Andric // Update the matches of the search. This is executed every time the text
3659c0981da4SDimitry Andric // field handles an event.
3660c0981da4SDimitry Andric virtual void UpdateMatches(const std::string &text) = 0;
3661c0981da4SDimitry Andric
3662c0981da4SDimitry Andric // Execute the user callback given the index of some match. This is executed
3663c0981da4SDimitry Andric // once the user selects a match.
3664c0981da4SDimitry Andric virtual void ExecuteCallback(int match_index) = 0;
3665c0981da4SDimitry Andric };
3666c0981da4SDimitry Andric
3667c0981da4SDimitry Andric typedef std::shared_ptr<SearcherDelegate> SearcherDelegateSP;
3668c0981da4SDimitry Andric
3669c0981da4SDimitry Andric class SearcherWindowDelegate : public WindowDelegate {
3670c0981da4SDimitry Andric public:
SearcherWindowDelegate(SearcherDelegateSP & delegate_sp)3671c0981da4SDimitry Andric SearcherWindowDelegate(SearcherDelegateSP &delegate_sp)
3672145449b1SDimitry Andric : m_delegate_sp(delegate_sp), m_text_field("Search", "", false) {
3673c0981da4SDimitry Andric ;
3674c0981da4SDimitry Andric }
3675c0981da4SDimitry Andric
3676c0981da4SDimitry Andric // A completion window is padded by one character from all sides. A text field
3677c0981da4SDimitry Andric // is first drawn for inputting the searcher request, then a list of matches
3678c0981da4SDimitry Andric // are displayed in a scrollable list.
3679c0981da4SDimitry Andric //
3680c0981da4SDimitry Andric // ___<Searcher Window Name>____________________________
3681c0981da4SDimitry Andric // | |
3682c0981da4SDimitry Andric // | __[Search]_______________________________________ |
3683c0981da4SDimitry Andric // | | | |
3684c0981da4SDimitry Andric // | |_______________________________________________| |
3685c0981da4SDimitry Andric // | - Match 1. |
3686c0981da4SDimitry Andric // | - Match 2. |
3687c0981da4SDimitry Andric // | - ... |
3688c0981da4SDimitry Andric // | |
3689c0981da4SDimitry Andric // |____________________________[Press Esc to Cancel]__|
3690c0981da4SDimitry Andric //
3691c0981da4SDimitry Andric
3692c0981da4SDimitry Andric // Get the index of the last visible match. Assuming at least one match
3693c0981da4SDimitry Andric // exists.
GetLastVisibleMatch(int height)3694c0981da4SDimitry Andric int GetLastVisibleMatch(int height) {
3695c0981da4SDimitry Andric int index = m_first_visible_match + height;
3696c0981da4SDimitry Andric return std::min(index, m_delegate_sp->GetNumberOfMatches()) - 1;
3697c0981da4SDimitry Andric }
3698c0981da4SDimitry Andric
GetNumberOfVisibleMatches(int height)3699c0981da4SDimitry Andric int GetNumberOfVisibleMatches(int height) {
3700c0981da4SDimitry Andric return GetLastVisibleMatch(height) - m_first_visible_match + 1;
3701c0981da4SDimitry Andric }
3702c0981da4SDimitry Andric
UpdateScrolling(Surface & surface)3703c0981da4SDimitry Andric void UpdateScrolling(Surface &surface) {
3704c0981da4SDimitry Andric if (m_selected_match < m_first_visible_match) {
3705c0981da4SDimitry Andric m_first_visible_match = m_selected_match;
3706c0981da4SDimitry Andric return;
3707c0981da4SDimitry Andric }
3708c0981da4SDimitry Andric
3709c0981da4SDimitry Andric int height = surface.GetHeight();
3710c0981da4SDimitry Andric int last_visible_match = GetLastVisibleMatch(height);
3711c0981da4SDimitry Andric if (m_selected_match > last_visible_match) {
3712c0981da4SDimitry Andric m_first_visible_match = m_selected_match - height + 1;
3713c0981da4SDimitry Andric }
3714c0981da4SDimitry Andric }
3715c0981da4SDimitry Andric
DrawMatches(Surface & surface)3716c0981da4SDimitry Andric void DrawMatches(Surface &surface) {
3717c0981da4SDimitry Andric if (m_delegate_sp->GetNumberOfMatches() == 0)
3718c0981da4SDimitry Andric return;
3719c0981da4SDimitry Andric
3720c0981da4SDimitry Andric UpdateScrolling(surface);
3721c0981da4SDimitry Andric
3722c0981da4SDimitry Andric int count = GetNumberOfVisibleMatches(surface.GetHeight());
3723c0981da4SDimitry Andric for (int i = 0; i < count; i++) {
3724c0981da4SDimitry Andric surface.MoveCursor(1, i);
3725c0981da4SDimitry Andric int current_match = m_first_visible_match + i;
3726c0981da4SDimitry Andric if (current_match == m_selected_match)
3727c0981da4SDimitry Andric surface.AttributeOn(A_REVERSE);
3728c0981da4SDimitry Andric surface.PutCString(
3729c0981da4SDimitry Andric m_delegate_sp->GetMatchTextAtIndex(current_match).c_str());
3730c0981da4SDimitry Andric if (current_match == m_selected_match)
3731c0981da4SDimitry Andric surface.AttributeOff(A_REVERSE);
3732c0981da4SDimitry Andric }
3733c0981da4SDimitry Andric }
3734c0981da4SDimitry Andric
DrawContent(Surface & surface)3735c0981da4SDimitry Andric void DrawContent(Surface &surface) {
3736c0981da4SDimitry Andric Rect content_bounds = surface.GetFrame();
3737c0981da4SDimitry Andric Rect text_field_bounds, matchs_bounds;
3738c0981da4SDimitry Andric content_bounds.HorizontalSplit(m_text_field.FieldDelegateGetHeight(),
3739c0981da4SDimitry Andric text_field_bounds, matchs_bounds);
3740c0981da4SDimitry Andric Surface text_field_surface = surface.SubSurface(text_field_bounds);
3741c0981da4SDimitry Andric Surface matches_surface = surface.SubSurface(matchs_bounds);
3742c0981da4SDimitry Andric
3743c0981da4SDimitry Andric m_text_field.FieldDelegateDraw(text_field_surface, true);
3744c0981da4SDimitry Andric DrawMatches(matches_surface);
3745c0981da4SDimitry Andric }
3746c0981da4SDimitry Andric
WindowDelegateDraw(Window & window,bool force)3747c0981da4SDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override {
3748c0981da4SDimitry Andric window.Erase();
3749c0981da4SDimitry Andric
3750c0981da4SDimitry Andric window.DrawTitleBox(window.GetName(), "Press Esc to Cancel");
3751c0981da4SDimitry Andric
3752c0981da4SDimitry Andric Rect content_bounds = window.GetFrame();
3753c0981da4SDimitry Andric content_bounds.Inset(2, 2);
3754c0981da4SDimitry Andric Surface content_surface = window.SubSurface(content_bounds);
3755c0981da4SDimitry Andric
3756c0981da4SDimitry Andric DrawContent(content_surface);
3757c0981da4SDimitry Andric return true;
3758c0981da4SDimitry Andric }
3759c0981da4SDimitry Andric
SelectNext()3760c0981da4SDimitry Andric void SelectNext() {
3761c0981da4SDimitry Andric if (m_selected_match != m_delegate_sp->GetNumberOfMatches() - 1)
3762c0981da4SDimitry Andric m_selected_match++;
3763c0981da4SDimitry Andric }
3764c0981da4SDimitry Andric
SelectPrevious()3765c0981da4SDimitry Andric void SelectPrevious() {
3766c0981da4SDimitry Andric if (m_selected_match != 0)
3767c0981da4SDimitry Andric m_selected_match--;
3768c0981da4SDimitry Andric }
3769c0981da4SDimitry Andric
ExecuteCallback(Window & window)3770c0981da4SDimitry Andric void ExecuteCallback(Window &window) {
3771c0981da4SDimitry Andric m_delegate_sp->ExecuteCallback(m_selected_match);
3772c0981da4SDimitry Andric window.GetParent()->RemoveSubWindow(&window);
3773c0981da4SDimitry Andric }
3774c0981da4SDimitry Andric
UpdateMatches()3775c0981da4SDimitry Andric void UpdateMatches() {
3776c0981da4SDimitry Andric m_delegate_sp->UpdateMatches(m_text_field.GetText());
3777c0981da4SDimitry Andric m_selected_match = 0;
3778c0981da4SDimitry Andric }
3779c0981da4SDimitry Andric
WindowDelegateHandleChar(Window & window,int key)3780c0981da4SDimitry Andric HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
3781c0981da4SDimitry Andric switch (key) {
3782c0981da4SDimitry Andric case '\r':
3783c0981da4SDimitry Andric case '\n':
3784c0981da4SDimitry Andric case KEY_ENTER:
3785c0981da4SDimitry Andric ExecuteCallback(window);
3786c0981da4SDimitry Andric return eKeyHandled;
3787c0981da4SDimitry Andric case '\t':
3788c0981da4SDimitry Andric case KEY_DOWN:
3789c0981da4SDimitry Andric SelectNext();
3790c0981da4SDimitry Andric return eKeyHandled;
3791c0981da4SDimitry Andric case KEY_SHIFT_TAB:
3792c0981da4SDimitry Andric case KEY_UP:
3793c0981da4SDimitry Andric SelectPrevious();
3794c0981da4SDimitry Andric return eKeyHandled;
3795c0981da4SDimitry Andric case KEY_ESCAPE:
3796c0981da4SDimitry Andric window.GetParent()->RemoveSubWindow(&window);
3797c0981da4SDimitry Andric return eKeyHandled;
3798c0981da4SDimitry Andric default:
3799c0981da4SDimitry Andric break;
3800c0981da4SDimitry Andric }
3801c0981da4SDimitry Andric
3802c0981da4SDimitry Andric if (m_text_field.FieldDelegateHandleChar(key) == eKeyHandled)
3803c0981da4SDimitry Andric UpdateMatches();
3804c0981da4SDimitry Andric
3805c0981da4SDimitry Andric return eKeyHandled;
3806c0981da4SDimitry Andric }
3807c0981da4SDimitry Andric
3808c0981da4SDimitry Andric protected:
3809c0981da4SDimitry Andric SearcherDelegateSP m_delegate_sp;
3810c0981da4SDimitry Andric TextFieldDelegate m_text_field;
3811c0981da4SDimitry Andric // The index of the currently selected match.
3812145449b1SDimitry Andric int m_selected_match = 0;
3813c0981da4SDimitry Andric // The index of the first visible match.
3814145449b1SDimitry Andric int m_first_visible_match = 0;
3815c0981da4SDimitry Andric };
3816c0981da4SDimitry Andric
3817c0981da4SDimitry Andric //////////////////////////////
3818c0981da4SDimitry Andric // Searcher Delegate Instances
3819c0981da4SDimitry Andric //////////////////////////////
3820c0981da4SDimitry Andric
3821c0981da4SDimitry Andric // This is a searcher delegate wrapper around CommandCompletions common
3822c0981da4SDimitry Andric // callbacks. The callbacks are only given the match string. The completion_mask
38237fa27ce4SDimitry Andric // can be a combination of lldb::CompletionType.
3824c0981da4SDimitry Andric class CommonCompletionSearcherDelegate : public SearcherDelegate {
3825c0981da4SDimitry Andric public:
3826c0981da4SDimitry Andric typedef std::function<void(const std::string &)> CallbackType;
3827c0981da4SDimitry Andric
CommonCompletionSearcherDelegate(Debugger & debugger,uint32_t completion_mask,CallbackType callback)3828c0981da4SDimitry Andric CommonCompletionSearcherDelegate(Debugger &debugger, uint32_t completion_mask,
3829c0981da4SDimitry Andric CallbackType callback)
3830c0981da4SDimitry Andric : m_debugger(debugger), m_completion_mask(completion_mask),
3831c0981da4SDimitry Andric m_callback(callback) {}
3832c0981da4SDimitry Andric
GetNumberOfMatches()3833c0981da4SDimitry Andric int GetNumberOfMatches() override { return m_matches.GetSize(); }
3834c0981da4SDimitry Andric
GetMatchTextAtIndex(int index)3835c0981da4SDimitry Andric const std::string &GetMatchTextAtIndex(int index) override {
3836c0981da4SDimitry Andric return m_matches[index];
3837c0981da4SDimitry Andric }
3838c0981da4SDimitry Andric
UpdateMatches(const std::string & text)3839c0981da4SDimitry Andric void UpdateMatches(const std::string &text) override {
3840c0981da4SDimitry Andric CompletionResult result;
3841c0981da4SDimitry Andric CompletionRequest request(text.c_str(), text.size(), result);
38427fa27ce4SDimitry Andric lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
3843c0981da4SDimitry Andric m_debugger.GetCommandInterpreter(), m_completion_mask, request,
3844c0981da4SDimitry Andric nullptr);
3845c0981da4SDimitry Andric result.GetMatches(m_matches);
3846c0981da4SDimitry Andric }
3847c0981da4SDimitry Andric
ExecuteCallback(int match_index)3848c0981da4SDimitry Andric void ExecuteCallback(int match_index) override {
3849c0981da4SDimitry Andric m_callback(m_matches[match_index]);
3850c0981da4SDimitry Andric }
3851c0981da4SDimitry Andric
3852c0981da4SDimitry Andric protected:
3853c0981da4SDimitry Andric Debugger &m_debugger;
38547fa27ce4SDimitry Andric // A compound mask from lldb::CompletionType.
3855c0981da4SDimitry Andric uint32_t m_completion_mask;
3856c0981da4SDimitry Andric // A callback to execute once the user selects a match. The match is passed to
3857c0981da4SDimitry Andric // the callback as a string.
3858c0981da4SDimitry Andric CallbackType m_callback;
3859c0981da4SDimitry Andric StringList m_matches;
3860c0981da4SDimitry Andric };
3861c0981da4SDimitry Andric
3862c0981da4SDimitry Andric ////////
3863c0981da4SDimitry Andric // Menus
3864c0981da4SDimitry Andric ////////
3865c0981da4SDimitry Andric
3866706b4fc4SDimitry Andric class MenuDelegate {
3867706b4fc4SDimitry Andric public:
3868706b4fc4SDimitry Andric virtual ~MenuDelegate() = default;
3869706b4fc4SDimitry Andric
3870706b4fc4SDimitry Andric virtual MenuActionResult MenuDelegateAction(Menu &menu) = 0;
3871706b4fc4SDimitry Andric };
3872706b4fc4SDimitry Andric
3873706b4fc4SDimitry Andric class Menu : public WindowDelegate {
3874706b4fc4SDimitry Andric public:
3875706b4fc4SDimitry Andric enum class Type { Invalid, Bar, Item, Separator };
3876706b4fc4SDimitry Andric
3877706b4fc4SDimitry Andric // Menubar or separator constructor
3878706b4fc4SDimitry Andric Menu(Type type);
3879706b4fc4SDimitry Andric
3880706b4fc4SDimitry Andric // Menuitem constructor
3881706b4fc4SDimitry Andric Menu(const char *name, const char *key_name, int key_value,
3882706b4fc4SDimitry Andric uint64_t identifier);
3883706b4fc4SDimitry Andric
3884706b4fc4SDimitry Andric ~Menu() override = default;
3885706b4fc4SDimitry Andric
GetDelegate() const3886706b4fc4SDimitry Andric const MenuDelegateSP &GetDelegate() const { return m_delegate_sp; }
3887706b4fc4SDimitry Andric
SetDelegate(const MenuDelegateSP & delegate_sp)3888706b4fc4SDimitry Andric void SetDelegate(const MenuDelegateSP &delegate_sp) {
3889706b4fc4SDimitry Andric m_delegate_sp = delegate_sp;
3890706b4fc4SDimitry Andric }
3891706b4fc4SDimitry Andric
3892706b4fc4SDimitry Andric void RecalculateNameLengths();
3893706b4fc4SDimitry Andric
3894706b4fc4SDimitry Andric void AddSubmenu(const MenuSP &menu_sp);
3895706b4fc4SDimitry Andric
3896706b4fc4SDimitry Andric int DrawAndRunMenu(Window &window);
3897706b4fc4SDimitry Andric
3898706b4fc4SDimitry Andric void DrawMenuTitle(Window &window, bool highlight);
3899706b4fc4SDimitry Andric
3900706b4fc4SDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override;
3901706b4fc4SDimitry Andric
3902706b4fc4SDimitry Andric HandleCharResult WindowDelegateHandleChar(Window &window, int key) override;
3903706b4fc4SDimitry Andric
ActionPrivate(Menu & menu)3904706b4fc4SDimitry Andric MenuActionResult ActionPrivate(Menu &menu) {
3905706b4fc4SDimitry Andric MenuActionResult result = MenuActionResult::NotHandled;
3906706b4fc4SDimitry Andric if (m_delegate_sp) {
3907706b4fc4SDimitry Andric result = m_delegate_sp->MenuDelegateAction(menu);
3908706b4fc4SDimitry Andric if (result != MenuActionResult::NotHandled)
3909706b4fc4SDimitry Andric return result;
3910706b4fc4SDimitry Andric } else if (m_parent) {
3911706b4fc4SDimitry Andric result = m_parent->ActionPrivate(menu);
3912706b4fc4SDimitry Andric if (result != MenuActionResult::NotHandled)
3913706b4fc4SDimitry Andric return result;
3914706b4fc4SDimitry Andric }
3915706b4fc4SDimitry Andric return m_canned_result;
3916706b4fc4SDimitry Andric }
3917706b4fc4SDimitry Andric
Action()3918706b4fc4SDimitry Andric MenuActionResult Action() {
3919706b4fc4SDimitry Andric // Call the recursive action so it can try to handle it with the menu
3920706b4fc4SDimitry Andric // delegate, and if not, try our parent menu
3921706b4fc4SDimitry Andric return ActionPrivate(*this);
3922706b4fc4SDimitry Andric }
3923706b4fc4SDimitry Andric
SetCannedResult(MenuActionResult result)3924706b4fc4SDimitry Andric void SetCannedResult(MenuActionResult result) { m_canned_result = result; }
3925706b4fc4SDimitry Andric
GetSubmenus()3926706b4fc4SDimitry Andric Menus &GetSubmenus() { return m_submenus; }
3927706b4fc4SDimitry Andric
GetSubmenus() const3928706b4fc4SDimitry Andric const Menus &GetSubmenus() const { return m_submenus; }
3929706b4fc4SDimitry Andric
GetSelectedSubmenuIndex() const3930706b4fc4SDimitry Andric int GetSelectedSubmenuIndex() const { return m_selected; }
3931706b4fc4SDimitry Andric
SetSelectedSubmenuIndex(int idx)3932706b4fc4SDimitry Andric void SetSelectedSubmenuIndex(int idx) { m_selected = idx; }
3933706b4fc4SDimitry Andric
GetType() const3934706b4fc4SDimitry Andric Type GetType() const { return m_type; }
3935706b4fc4SDimitry Andric
GetStartingColumn() const3936706b4fc4SDimitry Andric int GetStartingColumn() const { return m_start_col; }
3937706b4fc4SDimitry Andric
SetStartingColumn(int col)3938706b4fc4SDimitry Andric void SetStartingColumn(int col) { m_start_col = col; }
3939706b4fc4SDimitry Andric
GetKeyValue() const3940706b4fc4SDimitry Andric int GetKeyValue() const { return m_key_value; }
3941706b4fc4SDimitry Andric
GetName()3942706b4fc4SDimitry Andric std::string &GetName() { return m_name; }
3943706b4fc4SDimitry Andric
GetDrawWidth() const3944706b4fc4SDimitry Andric int GetDrawWidth() const {
3945706b4fc4SDimitry Andric return m_max_submenu_name_length + m_max_submenu_key_name_length + 8;
3946706b4fc4SDimitry Andric }
3947706b4fc4SDimitry Andric
GetIdentifier() const3948706b4fc4SDimitry Andric uint64_t GetIdentifier() const { return m_identifier; }
3949706b4fc4SDimitry Andric
SetIdentifier(uint64_t identifier)3950706b4fc4SDimitry Andric void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
3951706b4fc4SDimitry Andric
3952706b4fc4SDimitry Andric protected:
3953706b4fc4SDimitry Andric std::string m_name;
3954706b4fc4SDimitry Andric std::string m_key_name;
3955706b4fc4SDimitry Andric uint64_t m_identifier;
3956706b4fc4SDimitry Andric Type m_type;
3957706b4fc4SDimitry Andric int m_key_value;
3958706b4fc4SDimitry Andric int m_start_col;
3959706b4fc4SDimitry Andric int m_max_submenu_name_length;
3960706b4fc4SDimitry Andric int m_max_submenu_key_name_length;
3961706b4fc4SDimitry Andric int m_selected;
3962706b4fc4SDimitry Andric Menu *m_parent;
3963706b4fc4SDimitry Andric Menus m_submenus;
3964706b4fc4SDimitry Andric WindowSP m_menu_window_sp;
3965706b4fc4SDimitry Andric MenuActionResult m_canned_result;
3966706b4fc4SDimitry Andric MenuDelegateSP m_delegate_sp;
3967706b4fc4SDimitry Andric };
3968706b4fc4SDimitry Andric
3969706b4fc4SDimitry Andric // Menubar or separator constructor
Menu(Type type)3970706b4fc4SDimitry Andric Menu::Menu(Type type)
3971706b4fc4SDimitry Andric : m_name(), m_key_name(), m_identifier(0), m_type(type), m_key_value(0),
3972706b4fc4SDimitry Andric m_start_col(0), m_max_submenu_name_length(0),
3973706b4fc4SDimitry Andric m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr),
3974706b4fc4SDimitry Andric m_submenus(), m_canned_result(MenuActionResult::NotHandled),
3975706b4fc4SDimitry Andric m_delegate_sp() {}
3976706b4fc4SDimitry Andric
3977706b4fc4SDimitry Andric // Menuitem constructor
Menu(const char * name,const char * key_name,int key_value,uint64_t identifier)3978706b4fc4SDimitry Andric Menu::Menu(const char *name, const char *key_name, int key_value,
3979706b4fc4SDimitry Andric uint64_t identifier)
3980706b4fc4SDimitry Andric : m_name(), m_key_name(), m_identifier(identifier), m_type(Type::Invalid),
3981706b4fc4SDimitry Andric m_key_value(key_value), m_start_col(0), m_max_submenu_name_length(0),
3982706b4fc4SDimitry Andric m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr),
3983706b4fc4SDimitry Andric m_submenus(), m_canned_result(MenuActionResult::NotHandled),
3984706b4fc4SDimitry Andric m_delegate_sp() {
3985706b4fc4SDimitry Andric if (name && name[0]) {
3986706b4fc4SDimitry Andric m_name = name;
3987706b4fc4SDimitry Andric m_type = Type::Item;
3988706b4fc4SDimitry Andric if (key_name && key_name[0])
3989706b4fc4SDimitry Andric m_key_name = key_name;
3990706b4fc4SDimitry Andric } else {
3991706b4fc4SDimitry Andric m_type = Type::Separator;
3992706b4fc4SDimitry Andric }
3993706b4fc4SDimitry Andric }
3994706b4fc4SDimitry Andric
RecalculateNameLengths()3995706b4fc4SDimitry Andric void Menu::RecalculateNameLengths() {
3996706b4fc4SDimitry Andric m_max_submenu_name_length = 0;
3997706b4fc4SDimitry Andric m_max_submenu_key_name_length = 0;
3998706b4fc4SDimitry Andric Menus &submenus = GetSubmenus();
3999706b4fc4SDimitry Andric const size_t num_submenus = submenus.size();
4000706b4fc4SDimitry Andric for (size_t i = 0; i < num_submenus; ++i) {
4001706b4fc4SDimitry Andric Menu *submenu = submenus[i].get();
4002706b4fc4SDimitry Andric if (static_cast<size_t>(m_max_submenu_name_length) < submenu->m_name.size())
4003706b4fc4SDimitry Andric m_max_submenu_name_length = submenu->m_name.size();
4004706b4fc4SDimitry Andric if (static_cast<size_t>(m_max_submenu_key_name_length) <
4005706b4fc4SDimitry Andric submenu->m_key_name.size())
4006706b4fc4SDimitry Andric m_max_submenu_key_name_length = submenu->m_key_name.size();
4007706b4fc4SDimitry Andric }
4008706b4fc4SDimitry Andric }
4009706b4fc4SDimitry Andric
AddSubmenu(const MenuSP & menu_sp)4010706b4fc4SDimitry Andric void Menu::AddSubmenu(const MenuSP &menu_sp) {
4011706b4fc4SDimitry Andric menu_sp->m_parent = this;
4012706b4fc4SDimitry Andric if (static_cast<size_t>(m_max_submenu_name_length) < menu_sp->m_name.size())
4013706b4fc4SDimitry Andric m_max_submenu_name_length = menu_sp->m_name.size();
4014706b4fc4SDimitry Andric if (static_cast<size_t>(m_max_submenu_key_name_length) <
4015706b4fc4SDimitry Andric menu_sp->m_key_name.size())
4016706b4fc4SDimitry Andric m_max_submenu_key_name_length = menu_sp->m_key_name.size();
4017706b4fc4SDimitry Andric m_submenus.push_back(menu_sp);
4018706b4fc4SDimitry Andric }
4019706b4fc4SDimitry Andric
DrawMenuTitle(Window & window,bool highlight)4020706b4fc4SDimitry Andric void Menu::DrawMenuTitle(Window &window, bool highlight) {
4021706b4fc4SDimitry Andric if (m_type == Type::Separator) {
4022706b4fc4SDimitry Andric window.MoveCursor(0, window.GetCursorY());
4023706b4fc4SDimitry Andric window.PutChar(ACS_LTEE);
4024706b4fc4SDimitry Andric int width = window.GetWidth();
4025706b4fc4SDimitry Andric if (width > 2) {
4026706b4fc4SDimitry Andric width -= 2;
4027706b4fc4SDimitry Andric for (int i = 0; i < width; ++i)
4028706b4fc4SDimitry Andric window.PutChar(ACS_HLINE);
4029706b4fc4SDimitry Andric }
4030706b4fc4SDimitry Andric window.PutChar(ACS_RTEE);
4031706b4fc4SDimitry Andric } else {
4032706b4fc4SDimitry Andric const int shortcut_key = m_key_value;
4033706b4fc4SDimitry Andric bool underlined_shortcut = false;
4034b60736ecSDimitry Andric const attr_t highlight_attr = A_REVERSE;
4035706b4fc4SDimitry Andric if (highlight)
4036b60736ecSDimitry Andric window.AttributeOn(highlight_attr);
4037cfca06d7SDimitry Andric if (llvm::isPrint(shortcut_key)) {
4038706b4fc4SDimitry Andric size_t lower_pos = m_name.find(tolower(shortcut_key));
4039706b4fc4SDimitry Andric size_t upper_pos = m_name.find(toupper(shortcut_key));
4040706b4fc4SDimitry Andric const char *name = m_name.c_str();
4041706b4fc4SDimitry Andric size_t pos = std::min<size_t>(lower_pos, upper_pos);
4042706b4fc4SDimitry Andric if (pos != std::string::npos) {
4043706b4fc4SDimitry Andric underlined_shortcut = true;
4044706b4fc4SDimitry Andric if (pos > 0) {
4045706b4fc4SDimitry Andric window.PutCString(name, pos);
4046706b4fc4SDimitry Andric name += pos;
4047706b4fc4SDimitry Andric }
4048706b4fc4SDimitry Andric const attr_t shortcut_attr = A_UNDERLINE | A_BOLD;
4049706b4fc4SDimitry Andric window.AttributeOn(shortcut_attr);
4050706b4fc4SDimitry Andric window.PutChar(name[0]);
4051706b4fc4SDimitry Andric window.AttributeOff(shortcut_attr);
4052706b4fc4SDimitry Andric name++;
4053706b4fc4SDimitry Andric if (name[0])
4054706b4fc4SDimitry Andric window.PutCString(name);
4055706b4fc4SDimitry Andric }
4056706b4fc4SDimitry Andric }
4057706b4fc4SDimitry Andric
4058706b4fc4SDimitry Andric if (!underlined_shortcut) {
4059706b4fc4SDimitry Andric window.PutCString(m_name.c_str());
4060706b4fc4SDimitry Andric }
4061706b4fc4SDimitry Andric
4062706b4fc4SDimitry Andric if (highlight)
4063b60736ecSDimitry Andric window.AttributeOff(highlight_attr);
4064706b4fc4SDimitry Andric
4065706b4fc4SDimitry Andric if (m_key_name.empty()) {
4066cfca06d7SDimitry Andric if (!underlined_shortcut && llvm::isPrint(m_key_value)) {
4067b60736ecSDimitry Andric window.AttributeOn(COLOR_PAIR(MagentaOnWhite));
4068706b4fc4SDimitry Andric window.Printf(" (%c)", m_key_value);
4069b60736ecSDimitry Andric window.AttributeOff(COLOR_PAIR(MagentaOnWhite));
4070706b4fc4SDimitry Andric }
4071706b4fc4SDimitry Andric } else {
4072b60736ecSDimitry Andric window.AttributeOn(COLOR_PAIR(MagentaOnWhite));
4073706b4fc4SDimitry Andric window.Printf(" (%s)", m_key_name.c_str());
4074b60736ecSDimitry Andric window.AttributeOff(COLOR_PAIR(MagentaOnWhite));
4075706b4fc4SDimitry Andric }
4076706b4fc4SDimitry Andric }
4077706b4fc4SDimitry Andric }
4078706b4fc4SDimitry Andric
WindowDelegateDraw(Window & window,bool force)4079706b4fc4SDimitry Andric bool Menu::WindowDelegateDraw(Window &window, bool force) {
4080706b4fc4SDimitry Andric Menus &submenus = GetSubmenus();
4081706b4fc4SDimitry Andric const size_t num_submenus = submenus.size();
4082706b4fc4SDimitry Andric const int selected_idx = GetSelectedSubmenuIndex();
4083706b4fc4SDimitry Andric Menu::Type menu_type = GetType();
4084706b4fc4SDimitry Andric switch (menu_type) {
4085706b4fc4SDimitry Andric case Menu::Type::Bar: {
4086b60736ecSDimitry Andric window.SetBackground(BlackOnWhite);
4087706b4fc4SDimitry Andric window.MoveCursor(0, 0);
4088706b4fc4SDimitry Andric for (size_t i = 0; i < num_submenus; ++i) {
4089706b4fc4SDimitry Andric Menu *menu = submenus[i].get();
4090706b4fc4SDimitry Andric if (i > 0)
4091706b4fc4SDimitry Andric window.PutChar(' ');
4092706b4fc4SDimitry Andric menu->SetStartingColumn(window.GetCursorX());
4093706b4fc4SDimitry Andric window.PutCString("| ");
4094706b4fc4SDimitry Andric menu->DrawMenuTitle(window, false);
4095706b4fc4SDimitry Andric }
4096706b4fc4SDimitry Andric window.PutCString(" |");
4097706b4fc4SDimitry Andric } break;
4098706b4fc4SDimitry Andric
4099706b4fc4SDimitry Andric case Menu::Type::Item: {
4100706b4fc4SDimitry Andric int y = 1;
4101706b4fc4SDimitry Andric int x = 3;
4102706b4fc4SDimitry Andric // Draw the menu
4103706b4fc4SDimitry Andric int cursor_x = 0;
4104706b4fc4SDimitry Andric int cursor_y = 0;
4105706b4fc4SDimitry Andric window.Erase();
4106b60736ecSDimitry Andric window.SetBackground(BlackOnWhite);
4107706b4fc4SDimitry Andric window.Box();
4108706b4fc4SDimitry Andric for (size_t i = 0; i < num_submenus; ++i) {
4109706b4fc4SDimitry Andric const bool is_selected = (i == static_cast<size_t>(selected_idx));
4110706b4fc4SDimitry Andric window.MoveCursor(x, y + i);
4111706b4fc4SDimitry Andric if (is_selected) {
4112706b4fc4SDimitry Andric // Remember where we want the cursor to be
4113706b4fc4SDimitry Andric cursor_x = x - 1;
4114706b4fc4SDimitry Andric cursor_y = y + i;
4115706b4fc4SDimitry Andric }
4116706b4fc4SDimitry Andric submenus[i]->DrawMenuTitle(window, is_selected);
4117706b4fc4SDimitry Andric }
4118706b4fc4SDimitry Andric window.MoveCursor(cursor_x, cursor_y);
4119706b4fc4SDimitry Andric } break;
4120706b4fc4SDimitry Andric
4121706b4fc4SDimitry Andric default:
4122706b4fc4SDimitry Andric case Menu::Type::Separator:
4123706b4fc4SDimitry Andric break;
4124706b4fc4SDimitry Andric }
4125706b4fc4SDimitry Andric return true; // Drawing handled...
4126706b4fc4SDimitry Andric }
4127706b4fc4SDimitry Andric
WindowDelegateHandleChar(Window & window,int key)4128706b4fc4SDimitry Andric HandleCharResult Menu::WindowDelegateHandleChar(Window &window, int key) {
4129706b4fc4SDimitry Andric HandleCharResult result = eKeyNotHandled;
4130706b4fc4SDimitry Andric
4131706b4fc4SDimitry Andric Menus &submenus = GetSubmenus();
4132706b4fc4SDimitry Andric const size_t num_submenus = submenus.size();
4133706b4fc4SDimitry Andric const int selected_idx = GetSelectedSubmenuIndex();
4134706b4fc4SDimitry Andric Menu::Type menu_type = GetType();
4135706b4fc4SDimitry Andric if (menu_type == Menu::Type::Bar) {
4136706b4fc4SDimitry Andric MenuSP run_menu_sp;
4137706b4fc4SDimitry Andric switch (key) {
4138706b4fc4SDimitry Andric case KEY_DOWN:
4139706b4fc4SDimitry Andric case KEY_UP:
4140706b4fc4SDimitry Andric // Show last menu or first menu
4141706b4fc4SDimitry Andric if (selected_idx < static_cast<int>(num_submenus))
4142706b4fc4SDimitry Andric run_menu_sp = submenus[selected_idx];
4143706b4fc4SDimitry Andric else if (!submenus.empty())
4144706b4fc4SDimitry Andric run_menu_sp = submenus.front();
4145706b4fc4SDimitry Andric result = eKeyHandled;
4146706b4fc4SDimitry Andric break;
4147706b4fc4SDimitry Andric
4148706b4fc4SDimitry Andric case KEY_RIGHT:
4149706b4fc4SDimitry Andric ++m_selected;
4150706b4fc4SDimitry Andric if (m_selected >= static_cast<int>(num_submenus))
4151706b4fc4SDimitry Andric m_selected = 0;
4152706b4fc4SDimitry Andric if (m_selected < static_cast<int>(num_submenus))
4153706b4fc4SDimitry Andric run_menu_sp = submenus[m_selected];
4154706b4fc4SDimitry Andric else if (!submenus.empty())
4155706b4fc4SDimitry Andric run_menu_sp = submenus.front();
4156706b4fc4SDimitry Andric result = eKeyHandled;
4157706b4fc4SDimitry Andric break;
4158706b4fc4SDimitry Andric
4159706b4fc4SDimitry Andric case KEY_LEFT:
4160706b4fc4SDimitry Andric --m_selected;
4161706b4fc4SDimitry Andric if (m_selected < 0)
4162706b4fc4SDimitry Andric m_selected = num_submenus - 1;
4163706b4fc4SDimitry Andric if (m_selected < static_cast<int>(num_submenus))
4164706b4fc4SDimitry Andric run_menu_sp = submenus[m_selected];
4165706b4fc4SDimitry Andric else if (!submenus.empty())
4166706b4fc4SDimitry Andric run_menu_sp = submenus.front();
4167706b4fc4SDimitry Andric result = eKeyHandled;
4168706b4fc4SDimitry Andric break;
4169706b4fc4SDimitry Andric
4170706b4fc4SDimitry Andric default:
4171706b4fc4SDimitry Andric for (size_t i = 0; i < num_submenus; ++i) {
4172706b4fc4SDimitry Andric if (submenus[i]->GetKeyValue() == key) {
4173706b4fc4SDimitry Andric SetSelectedSubmenuIndex(i);
4174706b4fc4SDimitry Andric run_menu_sp = submenus[i];
4175706b4fc4SDimitry Andric result = eKeyHandled;
4176706b4fc4SDimitry Andric break;
4177706b4fc4SDimitry Andric }
4178706b4fc4SDimitry Andric }
4179706b4fc4SDimitry Andric break;
4180706b4fc4SDimitry Andric }
4181706b4fc4SDimitry Andric
4182706b4fc4SDimitry Andric if (run_menu_sp) {
4183706b4fc4SDimitry Andric // Run the action on this menu in case we need to populate the menu with
4184706b4fc4SDimitry Andric // dynamic content and also in case check marks, and any other menu
4185706b4fc4SDimitry Andric // decorations need to be calculated
4186706b4fc4SDimitry Andric if (run_menu_sp->Action() == MenuActionResult::Quit)
4187706b4fc4SDimitry Andric return eQuitApplication;
4188706b4fc4SDimitry Andric
4189706b4fc4SDimitry Andric Rect menu_bounds;
4190706b4fc4SDimitry Andric menu_bounds.origin.x = run_menu_sp->GetStartingColumn();
4191706b4fc4SDimitry Andric menu_bounds.origin.y = 1;
4192706b4fc4SDimitry Andric menu_bounds.size.width = run_menu_sp->GetDrawWidth();
4193706b4fc4SDimitry Andric menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2;
4194706b4fc4SDimitry Andric if (m_menu_window_sp)
4195706b4fc4SDimitry Andric window.GetParent()->RemoveSubWindow(m_menu_window_sp.get());
4196706b4fc4SDimitry Andric
4197706b4fc4SDimitry Andric m_menu_window_sp = window.GetParent()->CreateSubWindow(
4198706b4fc4SDimitry Andric run_menu_sp->GetName().c_str(), menu_bounds, true);
4199706b4fc4SDimitry Andric m_menu_window_sp->SetDelegate(run_menu_sp);
4200706b4fc4SDimitry Andric }
4201706b4fc4SDimitry Andric } else if (menu_type == Menu::Type::Item) {
4202706b4fc4SDimitry Andric switch (key) {
4203706b4fc4SDimitry Andric case KEY_DOWN:
4204706b4fc4SDimitry Andric if (m_submenus.size() > 1) {
4205706b4fc4SDimitry Andric const int start_select = m_selected;
4206706b4fc4SDimitry Andric while (++m_selected != start_select) {
4207706b4fc4SDimitry Andric if (static_cast<size_t>(m_selected) >= num_submenus)
4208706b4fc4SDimitry Andric m_selected = 0;
4209706b4fc4SDimitry Andric if (m_submenus[m_selected]->GetType() == Type::Separator)
4210706b4fc4SDimitry Andric continue;
4211706b4fc4SDimitry Andric else
4212706b4fc4SDimitry Andric break;
4213706b4fc4SDimitry Andric }
4214706b4fc4SDimitry Andric return eKeyHandled;
4215706b4fc4SDimitry Andric }
4216706b4fc4SDimitry Andric break;
4217706b4fc4SDimitry Andric
4218706b4fc4SDimitry Andric case KEY_UP:
4219706b4fc4SDimitry Andric if (m_submenus.size() > 1) {
4220706b4fc4SDimitry Andric const int start_select = m_selected;
4221706b4fc4SDimitry Andric while (--m_selected != start_select) {
4222706b4fc4SDimitry Andric if (m_selected < static_cast<int>(0))
4223706b4fc4SDimitry Andric m_selected = num_submenus - 1;
4224706b4fc4SDimitry Andric if (m_submenus[m_selected]->GetType() == Type::Separator)
4225706b4fc4SDimitry Andric continue;
4226706b4fc4SDimitry Andric else
4227706b4fc4SDimitry Andric break;
4228706b4fc4SDimitry Andric }
4229706b4fc4SDimitry Andric return eKeyHandled;
4230706b4fc4SDimitry Andric }
4231706b4fc4SDimitry Andric break;
4232706b4fc4SDimitry Andric
4233706b4fc4SDimitry Andric case KEY_RETURN:
4234706b4fc4SDimitry Andric if (static_cast<size_t>(selected_idx) < num_submenus) {
4235706b4fc4SDimitry Andric if (submenus[selected_idx]->Action() == MenuActionResult::Quit)
4236706b4fc4SDimitry Andric return eQuitApplication;
4237706b4fc4SDimitry Andric window.GetParent()->RemoveSubWindow(&window);
4238706b4fc4SDimitry Andric return eKeyHandled;
4239706b4fc4SDimitry Andric }
4240706b4fc4SDimitry Andric break;
4241706b4fc4SDimitry Andric
4242706b4fc4SDimitry Andric case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in
4243706b4fc4SDimitry Andric // case other chars are entered for escaped sequences
4244706b4fc4SDimitry Andric window.GetParent()->RemoveSubWindow(&window);
4245706b4fc4SDimitry Andric return eKeyHandled;
4246706b4fc4SDimitry Andric
4247706b4fc4SDimitry Andric default:
4248706b4fc4SDimitry Andric for (size_t i = 0; i < num_submenus; ++i) {
4249706b4fc4SDimitry Andric Menu *menu = submenus[i].get();
4250706b4fc4SDimitry Andric if (menu->GetKeyValue() == key) {
4251706b4fc4SDimitry Andric SetSelectedSubmenuIndex(i);
4252706b4fc4SDimitry Andric window.GetParent()->RemoveSubWindow(&window);
4253706b4fc4SDimitry Andric if (menu->Action() == MenuActionResult::Quit)
4254706b4fc4SDimitry Andric return eQuitApplication;
4255706b4fc4SDimitry Andric return eKeyHandled;
4256706b4fc4SDimitry Andric }
4257706b4fc4SDimitry Andric }
4258706b4fc4SDimitry Andric break;
4259706b4fc4SDimitry Andric }
4260706b4fc4SDimitry Andric } else if (menu_type == Menu::Type::Separator) {
4261706b4fc4SDimitry Andric }
4262706b4fc4SDimitry Andric return result;
4263706b4fc4SDimitry Andric }
4264706b4fc4SDimitry Andric
4265706b4fc4SDimitry Andric class Application {
4266706b4fc4SDimitry Andric public:
Application(FILE * in,FILE * out)4267145449b1SDimitry Andric Application(FILE *in, FILE *out) : m_window_sp(), m_in(in), m_out(out) {}
4268706b4fc4SDimitry Andric
~Application()4269706b4fc4SDimitry Andric ~Application() {
4270706b4fc4SDimitry Andric m_window_delegates.clear();
4271706b4fc4SDimitry Andric m_window_sp.reset();
4272706b4fc4SDimitry Andric if (m_screen) {
4273706b4fc4SDimitry Andric ::delscreen(m_screen);
4274706b4fc4SDimitry Andric m_screen = nullptr;
4275706b4fc4SDimitry Andric }
4276706b4fc4SDimitry Andric }
4277706b4fc4SDimitry Andric
Initialize()4278706b4fc4SDimitry Andric void Initialize() {
4279706b4fc4SDimitry Andric m_screen = ::newterm(nullptr, m_out, m_in);
4280706b4fc4SDimitry Andric ::start_color();
4281706b4fc4SDimitry Andric ::curs_set(0);
4282706b4fc4SDimitry Andric ::noecho();
4283706b4fc4SDimitry Andric ::keypad(stdscr, TRUE);
4284706b4fc4SDimitry Andric }
4285706b4fc4SDimitry Andric
Terminate()4286706b4fc4SDimitry Andric void Terminate() { ::endwin(); }
4287706b4fc4SDimitry Andric
Run(Debugger & debugger)4288706b4fc4SDimitry Andric void Run(Debugger &debugger) {
4289706b4fc4SDimitry Andric bool done = false;
4290706b4fc4SDimitry Andric int delay_in_tenths_of_a_second = 1;
4291706b4fc4SDimitry Andric
4292c0981da4SDimitry Andric // Alas the threading model in curses is a bit lame so we need to resort
4293c0981da4SDimitry Andric // to polling every 0.5 seconds. We could poll for stdin ourselves and
4294c0981da4SDimitry Andric // then pass the keys down but then we need to translate all of the escape
4295706b4fc4SDimitry Andric // sequences ourselves. So we resort to polling for input because we need
4296706b4fc4SDimitry Andric // to receive async process events while in this loop.
4297706b4fc4SDimitry Andric
4298c0981da4SDimitry Andric halfdelay(delay_in_tenths_of_a_second); // Poll using some number of
4299c0981da4SDimitry Andric // tenths of seconds seconds when
4300c0981da4SDimitry Andric // calling Window::GetChar()
4301706b4fc4SDimitry Andric
4302706b4fc4SDimitry Andric ListenerSP listener_sp(
4303706b4fc4SDimitry Andric Listener::MakeListener("lldb.IOHandler.curses.Application"));
4304706b4fc4SDimitry Andric ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass());
4305706b4fc4SDimitry Andric debugger.EnableForwardEvents(listener_sp);
4306706b4fc4SDimitry Andric
4307b60736ecSDimitry Andric m_update_screen = true;
4308706b4fc4SDimitry Andric #if defined(__APPLE__)
4309706b4fc4SDimitry Andric std::deque<int> escape_chars;
4310706b4fc4SDimitry Andric #endif
4311706b4fc4SDimitry Andric
4312706b4fc4SDimitry Andric while (!done) {
4313b60736ecSDimitry Andric if (m_update_screen) {
4314706b4fc4SDimitry Andric m_window_sp->Draw(false);
4315706b4fc4SDimitry Andric // All windows should be calling Window::DeferredRefresh() instead of
4316706b4fc4SDimitry Andric // Window::Refresh() so we can do a single update and avoid any screen
4317706b4fc4SDimitry Andric // blinking
4318706b4fc4SDimitry Andric update_panels();
4319706b4fc4SDimitry Andric
4320706b4fc4SDimitry Andric // Cursor hiding isn't working on MacOSX, so hide it in the top left
4321706b4fc4SDimitry Andric // corner
4322706b4fc4SDimitry Andric m_window_sp->MoveCursor(0, 0);
4323706b4fc4SDimitry Andric
4324706b4fc4SDimitry Andric doupdate();
4325b60736ecSDimitry Andric m_update_screen = false;
4326706b4fc4SDimitry Andric }
4327706b4fc4SDimitry Andric
4328706b4fc4SDimitry Andric #if defined(__APPLE__)
4329706b4fc4SDimitry Andric // Terminal.app doesn't map its function keys correctly, F1-F4 default
4330706b4fc4SDimitry Andric // to: \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if
4331706b4fc4SDimitry Andric // possible
4332706b4fc4SDimitry Andric int ch;
4333706b4fc4SDimitry Andric if (escape_chars.empty())
4334706b4fc4SDimitry Andric ch = m_window_sp->GetChar();
4335706b4fc4SDimitry Andric else {
4336706b4fc4SDimitry Andric ch = escape_chars.front();
4337706b4fc4SDimitry Andric escape_chars.pop_front();
4338706b4fc4SDimitry Andric }
4339706b4fc4SDimitry Andric if (ch == KEY_ESCAPE) {
4340706b4fc4SDimitry Andric int ch2 = m_window_sp->GetChar();
4341706b4fc4SDimitry Andric if (ch2 == 'O') {
4342706b4fc4SDimitry Andric int ch3 = m_window_sp->GetChar();
4343706b4fc4SDimitry Andric switch (ch3) {
4344706b4fc4SDimitry Andric case 'P':
4345706b4fc4SDimitry Andric ch = KEY_F(1);
4346706b4fc4SDimitry Andric break;
4347706b4fc4SDimitry Andric case 'Q':
4348706b4fc4SDimitry Andric ch = KEY_F(2);
4349706b4fc4SDimitry Andric break;
4350706b4fc4SDimitry Andric case 'R':
4351706b4fc4SDimitry Andric ch = KEY_F(3);
4352706b4fc4SDimitry Andric break;
4353706b4fc4SDimitry Andric case 'S':
4354706b4fc4SDimitry Andric ch = KEY_F(4);
4355706b4fc4SDimitry Andric break;
4356706b4fc4SDimitry Andric default:
4357706b4fc4SDimitry Andric escape_chars.push_back(ch2);
4358706b4fc4SDimitry Andric if (ch3 != -1)
4359706b4fc4SDimitry Andric escape_chars.push_back(ch3);
4360706b4fc4SDimitry Andric break;
4361706b4fc4SDimitry Andric }
4362706b4fc4SDimitry Andric } else if (ch2 != -1)
4363706b4fc4SDimitry Andric escape_chars.push_back(ch2);
4364706b4fc4SDimitry Andric }
4365706b4fc4SDimitry Andric #else
4366706b4fc4SDimitry Andric int ch = m_window_sp->GetChar();
4367706b4fc4SDimitry Andric
4368706b4fc4SDimitry Andric #endif
4369706b4fc4SDimitry Andric if (ch == -1) {
4370706b4fc4SDimitry Andric if (feof(m_in) || ferror(m_in)) {
4371706b4fc4SDimitry Andric done = true;
4372706b4fc4SDimitry Andric } else {
4373706b4fc4SDimitry Andric // Just a timeout from using halfdelay(), check for events
4374706b4fc4SDimitry Andric EventSP event_sp;
4375706b4fc4SDimitry Andric while (listener_sp->PeekAtNextEvent()) {
4376706b4fc4SDimitry Andric listener_sp->GetEvent(event_sp, std::chrono::seconds(0));
4377706b4fc4SDimitry Andric
4378706b4fc4SDimitry Andric if (event_sp) {
4379706b4fc4SDimitry Andric Broadcaster *broadcaster = event_sp->GetBroadcaster();
4380706b4fc4SDimitry Andric if (broadcaster) {
4381706b4fc4SDimitry Andric // uint32_t event_type = event_sp->GetType();
4382706b4fc4SDimitry Andric ConstString broadcaster_class(
4383706b4fc4SDimitry Andric broadcaster->GetBroadcasterClass());
4384706b4fc4SDimitry Andric if (broadcaster_class == broadcaster_class_process) {
4385b60736ecSDimitry Andric m_update_screen = true;
4386706b4fc4SDimitry Andric continue; // Don't get any key, just update our view
4387706b4fc4SDimitry Andric }
4388706b4fc4SDimitry Andric }
4389706b4fc4SDimitry Andric }
4390706b4fc4SDimitry Andric }
4391706b4fc4SDimitry Andric }
4392706b4fc4SDimitry Andric } else {
4393706b4fc4SDimitry Andric HandleCharResult key_result = m_window_sp->HandleChar(ch);
4394706b4fc4SDimitry Andric switch (key_result) {
4395706b4fc4SDimitry Andric case eKeyHandled:
4396b60736ecSDimitry Andric m_update_screen = true;
4397706b4fc4SDimitry Andric break;
4398706b4fc4SDimitry Andric case eKeyNotHandled:
4399b60736ecSDimitry Andric if (ch == 12) { // Ctrl+L, force full redraw
4400b60736ecSDimitry Andric redrawwin(m_window_sp->get());
4401b60736ecSDimitry Andric m_update_screen = true;
4402b60736ecSDimitry Andric }
4403706b4fc4SDimitry Andric break;
4404706b4fc4SDimitry Andric case eQuitApplication:
4405706b4fc4SDimitry Andric done = true;
4406706b4fc4SDimitry Andric break;
4407706b4fc4SDimitry Andric }
4408706b4fc4SDimitry Andric }
4409706b4fc4SDimitry Andric }
4410706b4fc4SDimitry Andric
4411706b4fc4SDimitry Andric debugger.CancelForwardEvents(listener_sp);
4412706b4fc4SDimitry Andric }
4413706b4fc4SDimitry Andric
GetMainWindow()4414706b4fc4SDimitry Andric WindowSP &GetMainWindow() {
4415706b4fc4SDimitry Andric if (!m_window_sp)
4416706b4fc4SDimitry Andric m_window_sp = std::make_shared<Window>("main", stdscr, false);
4417706b4fc4SDimitry Andric return m_window_sp;
4418706b4fc4SDimitry Andric }
4419706b4fc4SDimitry Andric
TerminalSizeChanged()4420b60736ecSDimitry Andric void TerminalSizeChanged() {
4421b60736ecSDimitry Andric ::endwin();
4422b60736ecSDimitry Andric ::refresh();
4423b60736ecSDimitry Andric Rect content_bounds = m_window_sp->GetFrame();
4424b60736ecSDimitry Andric m_window_sp->SetBounds(content_bounds);
4425b60736ecSDimitry Andric if (WindowSP menubar_window_sp = m_window_sp->FindSubWindow("Menubar"))
4426b60736ecSDimitry Andric menubar_window_sp->SetBounds(content_bounds.MakeMenuBar());
4427b60736ecSDimitry Andric if (WindowSP status_window_sp = m_window_sp->FindSubWindow("Status"))
4428b60736ecSDimitry Andric status_window_sp->SetBounds(content_bounds.MakeStatusBar());
4429b60736ecSDimitry Andric
4430b60736ecSDimitry Andric WindowSP source_window_sp = m_window_sp->FindSubWindow("Source");
4431b60736ecSDimitry Andric WindowSP variables_window_sp = m_window_sp->FindSubWindow("Variables");
4432b60736ecSDimitry Andric WindowSP registers_window_sp = m_window_sp->FindSubWindow("Registers");
4433b60736ecSDimitry Andric WindowSP threads_window_sp = m_window_sp->FindSubWindow("Threads");
4434b60736ecSDimitry Andric
4435b60736ecSDimitry Andric Rect threads_bounds;
4436b60736ecSDimitry Andric Rect source_variables_bounds;
4437b60736ecSDimitry Andric content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds,
4438b60736ecSDimitry Andric threads_bounds);
4439b60736ecSDimitry Andric if (threads_window_sp)
4440b60736ecSDimitry Andric threads_window_sp->SetBounds(threads_bounds);
4441b60736ecSDimitry Andric else
4442b60736ecSDimitry Andric source_variables_bounds = content_bounds;
4443b60736ecSDimitry Andric
4444b60736ecSDimitry Andric Rect source_bounds;
4445b60736ecSDimitry Andric Rect variables_registers_bounds;
4446b60736ecSDimitry Andric source_variables_bounds.HorizontalSplitPercentage(
4447b60736ecSDimitry Andric 0.70, source_bounds, variables_registers_bounds);
4448b60736ecSDimitry Andric if (variables_window_sp || registers_window_sp) {
4449b60736ecSDimitry Andric if (variables_window_sp && registers_window_sp) {
4450b60736ecSDimitry Andric Rect variables_bounds;
4451b60736ecSDimitry Andric Rect registers_bounds;
4452b60736ecSDimitry Andric variables_registers_bounds.VerticalSplitPercentage(
4453b60736ecSDimitry Andric 0.50, variables_bounds, registers_bounds);
4454b60736ecSDimitry Andric variables_window_sp->SetBounds(variables_bounds);
4455b60736ecSDimitry Andric registers_window_sp->SetBounds(registers_bounds);
4456b60736ecSDimitry Andric } else if (variables_window_sp) {
4457b60736ecSDimitry Andric variables_window_sp->SetBounds(variables_registers_bounds);
4458b60736ecSDimitry Andric } else {
4459b60736ecSDimitry Andric registers_window_sp->SetBounds(variables_registers_bounds);
4460b60736ecSDimitry Andric }
4461b60736ecSDimitry Andric } else {
4462b60736ecSDimitry Andric source_bounds = source_variables_bounds;
4463b60736ecSDimitry Andric }
4464b60736ecSDimitry Andric
4465b60736ecSDimitry Andric source_window_sp->SetBounds(source_bounds);
4466b60736ecSDimitry Andric
4467b60736ecSDimitry Andric touchwin(stdscr);
4468b60736ecSDimitry Andric redrawwin(m_window_sp->get());
4469b60736ecSDimitry Andric m_update_screen = true;
4470b60736ecSDimitry Andric }
4471b60736ecSDimitry Andric
4472706b4fc4SDimitry Andric protected:
4473706b4fc4SDimitry Andric WindowSP m_window_sp;
4474706b4fc4SDimitry Andric WindowDelegates m_window_delegates;
4475145449b1SDimitry Andric SCREEN *m_screen = nullptr;
4476706b4fc4SDimitry Andric FILE *m_in;
4477706b4fc4SDimitry Andric FILE *m_out;
4478b60736ecSDimitry Andric bool m_update_screen = false;
4479706b4fc4SDimitry Andric };
4480706b4fc4SDimitry Andric
4481706b4fc4SDimitry Andric } // namespace curses
4482706b4fc4SDimitry Andric
4483706b4fc4SDimitry Andric using namespace curses;
4484706b4fc4SDimitry Andric
4485706b4fc4SDimitry Andric struct Row {
4486344a3780SDimitry Andric ValueObjectUpdater value;
4487706b4fc4SDimitry Andric Row *parent;
4488706b4fc4SDimitry Andric // The process stop ID when the children were calculated.
4489b60736ecSDimitry Andric uint32_t children_stop_id = 0;
4490b60736ecSDimitry Andric int row_idx = 0;
4491b60736ecSDimitry Andric int x = 1;
4492b60736ecSDimitry Andric int y = 1;
4493706b4fc4SDimitry Andric bool might_have_children;
4494b60736ecSDimitry Andric bool expanded = false;
4495b60736ecSDimitry Andric bool calculated_children = false;
4496706b4fc4SDimitry Andric std::vector<Row> children;
4497706b4fc4SDimitry Andric
RowRow4498706b4fc4SDimitry Andric Row(const ValueObjectSP &v, Row *p)
4499344a3780SDimitry Andric : value(v), parent(p),
4500b60736ecSDimitry Andric might_have_children(v ? v->MightHaveChildren() : false) {}
4501706b4fc4SDimitry Andric
GetDepthRow4502706b4fc4SDimitry Andric size_t GetDepth() const {
4503706b4fc4SDimitry Andric if (parent)
4504706b4fc4SDimitry Andric return 1 + parent->GetDepth();
4505706b4fc4SDimitry Andric return 0;
4506706b4fc4SDimitry Andric }
4507706b4fc4SDimitry Andric
ExpandRow4508706b4fc4SDimitry Andric void Expand() { expanded = true; }
4509706b4fc4SDimitry Andric
GetChildrenRow4510706b4fc4SDimitry Andric std::vector<Row> &GetChildren() {
4511706b4fc4SDimitry Andric ProcessSP process_sp = value.GetProcessSP();
4512706b4fc4SDimitry Andric auto stop_id = process_sp->GetStopID();
4513706b4fc4SDimitry Andric if (process_sp && stop_id != children_stop_id) {
4514706b4fc4SDimitry Andric children_stop_id = stop_id;
4515706b4fc4SDimitry Andric calculated_children = false;
4516706b4fc4SDimitry Andric }
4517706b4fc4SDimitry Andric if (!calculated_children) {
4518706b4fc4SDimitry Andric children.clear();
4519706b4fc4SDimitry Andric calculated_children = true;
4520706b4fc4SDimitry Andric ValueObjectSP valobj = value.GetSP();
4521706b4fc4SDimitry Andric if (valobj) {
4522ac9a064cSDimitry Andric const uint32_t num_children = valobj->GetNumChildrenIgnoringErrors();
4523706b4fc4SDimitry Andric for (size_t i = 0; i < num_children; ++i) {
45247fa27ce4SDimitry Andric children.push_back(Row(valobj->GetChildAtIndex(i), this));
4525706b4fc4SDimitry Andric }
4526706b4fc4SDimitry Andric }
4527706b4fc4SDimitry Andric }
4528706b4fc4SDimitry Andric return children;
4529706b4fc4SDimitry Andric }
4530706b4fc4SDimitry Andric
UnexpandRow4531706b4fc4SDimitry Andric void Unexpand() {
4532706b4fc4SDimitry Andric expanded = false;
4533706b4fc4SDimitry Andric calculated_children = false;
4534706b4fc4SDimitry Andric children.clear();
4535706b4fc4SDimitry Andric }
4536706b4fc4SDimitry Andric
DrawTreeRow4537706b4fc4SDimitry Andric void DrawTree(Window &window) {
4538706b4fc4SDimitry Andric if (parent)
4539706b4fc4SDimitry Andric parent->DrawTreeForChild(window, this, 0);
4540706b4fc4SDimitry Andric
4541145449b1SDimitry Andric if (might_have_children &&
4542145449b1SDimitry Andric (!calculated_children || !GetChildren().empty())) {
4543706b4fc4SDimitry Andric // It we can get UTF8 characters to work we should try to use the
4544706b4fc4SDimitry Andric // "symbol" UTF8 string below
4545706b4fc4SDimitry Andric // const char *symbol = "";
4546706b4fc4SDimitry Andric // if (row.expanded)
4547706b4fc4SDimitry Andric // symbol = "\xe2\x96\xbd ";
4548706b4fc4SDimitry Andric // else
4549706b4fc4SDimitry Andric // symbol = "\xe2\x96\xb7 ";
4550706b4fc4SDimitry Andric // window.PutCString (symbol);
4551706b4fc4SDimitry Andric
4552706b4fc4SDimitry Andric // The ACS_DARROW and ACS_RARROW don't look very nice they are just a 'v'
4553706b4fc4SDimitry Andric // or '>' character...
4554706b4fc4SDimitry Andric // if (expanded)
4555706b4fc4SDimitry Andric // window.PutChar (ACS_DARROW);
4556706b4fc4SDimitry Andric // else
4557706b4fc4SDimitry Andric // window.PutChar (ACS_RARROW);
4558706b4fc4SDimitry Andric // Since we can't find any good looking right arrow/down arrow symbols,
4559706b4fc4SDimitry Andric // just use a diamond...
4560706b4fc4SDimitry Andric window.PutChar(ACS_DIAMOND);
4561706b4fc4SDimitry Andric window.PutChar(ACS_HLINE);
4562706b4fc4SDimitry Andric }
4563706b4fc4SDimitry Andric }
4564706b4fc4SDimitry Andric
DrawTreeForChildRow4565706b4fc4SDimitry Andric void DrawTreeForChild(Window &window, Row *child, uint32_t reverse_depth) {
4566706b4fc4SDimitry Andric if (parent)
4567706b4fc4SDimitry Andric parent->DrawTreeForChild(window, this, reverse_depth + 1);
4568706b4fc4SDimitry Andric
4569706b4fc4SDimitry Andric if (&GetChildren().back() == child) {
4570706b4fc4SDimitry Andric // Last child
4571706b4fc4SDimitry Andric if (reverse_depth == 0) {
4572706b4fc4SDimitry Andric window.PutChar(ACS_LLCORNER);
4573706b4fc4SDimitry Andric window.PutChar(ACS_HLINE);
4574706b4fc4SDimitry Andric } else {
4575706b4fc4SDimitry Andric window.PutChar(' ');
4576706b4fc4SDimitry Andric window.PutChar(' ');
4577706b4fc4SDimitry Andric }
4578706b4fc4SDimitry Andric } else {
4579706b4fc4SDimitry Andric if (reverse_depth == 0) {
4580706b4fc4SDimitry Andric window.PutChar(ACS_LTEE);
4581706b4fc4SDimitry Andric window.PutChar(ACS_HLINE);
4582706b4fc4SDimitry Andric } else {
4583706b4fc4SDimitry Andric window.PutChar(ACS_VLINE);
4584706b4fc4SDimitry Andric window.PutChar(' ');
4585706b4fc4SDimitry Andric }
4586706b4fc4SDimitry Andric }
4587706b4fc4SDimitry Andric }
4588706b4fc4SDimitry Andric };
4589706b4fc4SDimitry Andric
4590706b4fc4SDimitry Andric struct DisplayOptions {
4591706b4fc4SDimitry Andric bool show_types;
4592706b4fc4SDimitry Andric };
4593706b4fc4SDimitry Andric
4594706b4fc4SDimitry Andric class TreeItem;
4595706b4fc4SDimitry Andric
4596706b4fc4SDimitry Andric class TreeDelegate {
4597706b4fc4SDimitry Andric public:
4598706b4fc4SDimitry Andric TreeDelegate() = default;
4599706b4fc4SDimitry Andric virtual ~TreeDelegate() = default;
4600706b4fc4SDimitry Andric
4601706b4fc4SDimitry Andric virtual void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) = 0;
4602706b4fc4SDimitry Andric virtual void TreeDelegateGenerateChildren(TreeItem &item) = 0;
TreeDelegateUpdateSelection(TreeItem & root,int & selection_index,TreeItem * & selected_item)4603344a3780SDimitry Andric virtual void TreeDelegateUpdateSelection(TreeItem &root, int &selection_index,
460477fc4c14SDimitry Andric TreeItem *&selected_item) {}
4605c0981da4SDimitry Andric // This is invoked when a tree item is selected. If true is returned, the
4606c0981da4SDimitry Andric // views are updated.
4607c0981da4SDimitry Andric virtual bool TreeDelegateItemSelected(TreeItem &item) = 0;
TreeDelegateExpandRootByDefault()4608344a3780SDimitry Andric virtual bool TreeDelegateExpandRootByDefault() { return false; }
4609c0981da4SDimitry Andric // This is mostly useful for root tree delegates. If false is returned,
4610c0981da4SDimitry Andric // drawing will be skipped completely. This is needed, for instance, in
4611c0981da4SDimitry Andric // skipping drawing of the threads tree if there is no running process.
TreeDelegateShouldDraw()4612c0981da4SDimitry Andric virtual bool TreeDelegateShouldDraw() { return true; }
4613706b4fc4SDimitry Andric };
4614706b4fc4SDimitry Andric
4615706b4fc4SDimitry Andric typedef std::shared_ptr<TreeDelegate> TreeDelegateSP;
4616706b4fc4SDimitry Andric
4617b1c73532SDimitry Andric struct TreeItemData {
TreeItemDataTreeItemData4618b1c73532SDimitry Andric TreeItemData(TreeItem *parent, TreeDelegate &delegate,
4619b1c73532SDimitry Andric bool might_have_children, bool is_expanded)
4620b1c73532SDimitry Andric : m_parent(parent), m_delegate(&delegate),
4621b1c73532SDimitry Andric m_might_have_children(might_have_children), m_is_expanded(is_expanded) {
4622344a3780SDimitry Andric }
4623706b4fc4SDimitry Andric
4624b1c73532SDimitry Andric protected:
4625b1c73532SDimitry Andric TreeItem *m_parent;
4626b1c73532SDimitry Andric TreeDelegate *m_delegate;
4627b1c73532SDimitry Andric void *m_user_data = nullptr;
4628b1c73532SDimitry Andric uint64_t m_identifier = 0;
4629b1c73532SDimitry Andric std::string m_text;
4630b1c73532SDimitry Andric int m_row_idx = -1; // Zero based visible row index, -1 if not visible or for
4631b1c73532SDimitry Andric // the root item
4632b1c73532SDimitry Andric bool m_might_have_children;
4633b1c73532SDimitry Andric bool m_is_expanded = false;
4634b1c73532SDimitry Andric };
4635b1c73532SDimitry Andric
4636b1c73532SDimitry Andric class TreeItem : public TreeItemData {
4637b1c73532SDimitry Andric public:
TreeItem(TreeItem * parent,TreeDelegate & delegate,bool might_have_children)4638b1c73532SDimitry Andric TreeItem(TreeItem *parent, TreeDelegate &delegate, bool might_have_children)
4639b1c73532SDimitry Andric : TreeItemData(parent, delegate, might_have_children,
4640b1c73532SDimitry Andric parent == nullptr
4641b1c73532SDimitry Andric ? delegate.TreeDelegateExpandRootByDefault()
4642b1c73532SDimitry Andric : false),
4643b1c73532SDimitry Andric m_children() {}
4644b1c73532SDimitry Andric
4645b1c73532SDimitry Andric TreeItem(const TreeItem &) = delete;
4646b1c73532SDimitry Andric TreeItem &operator=(const TreeItem &rhs) = delete;
4647b1c73532SDimitry Andric
operator =(TreeItem && rhs)4648b1c73532SDimitry Andric TreeItem &operator=(TreeItem &&rhs) {
4649706b4fc4SDimitry Andric if (this != &rhs) {
4650b1c73532SDimitry Andric TreeItemData::operator=(std::move(rhs));
4651b1c73532SDimitry Andric AdoptChildren(rhs.m_children);
4652706b4fc4SDimitry Andric }
4653706b4fc4SDimitry Andric return *this;
4654706b4fc4SDimitry Andric }
4655706b4fc4SDimitry Andric
TreeItem(TreeItem && rhs)4656b1c73532SDimitry Andric TreeItem(TreeItem &&rhs) : TreeItemData(std::move(rhs)) {
4657b1c73532SDimitry Andric AdoptChildren(rhs.m_children);
4658b1c73532SDimitry Andric }
4659706b4fc4SDimitry Andric
GetDepth() const4660706b4fc4SDimitry Andric size_t GetDepth() const {
4661706b4fc4SDimitry Andric if (m_parent)
4662706b4fc4SDimitry Andric return 1 + m_parent->GetDepth();
4663706b4fc4SDimitry Andric return 0;
4664706b4fc4SDimitry Andric }
4665706b4fc4SDimitry Andric
GetRowIndex() const4666706b4fc4SDimitry Andric int GetRowIndex() const { return m_row_idx; }
4667706b4fc4SDimitry Andric
ClearChildren()4668706b4fc4SDimitry Andric void ClearChildren() { m_children.clear(); }
4669706b4fc4SDimitry Andric
Resize(size_t n,TreeDelegate & delegate,bool might_have_children)4670b1c73532SDimitry Andric void Resize(size_t n, TreeDelegate &delegate, bool might_have_children) {
4671b1c73532SDimitry Andric if (m_children.size() >= n) {
4672b1c73532SDimitry Andric m_children.erase(m_children.begin() + n, m_children.end());
4673b1c73532SDimitry Andric return;
4674b1c73532SDimitry Andric }
4675b1c73532SDimitry Andric m_children.reserve(n);
4676b1c73532SDimitry Andric std::generate_n(std::back_inserter(m_children), n - m_children.size(),
4677b1c73532SDimitry Andric [&, parent = this]() {
4678b1c73532SDimitry Andric return TreeItem(parent, delegate, might_have_children);
4679b1c73532SDimitry Andric });
4680b1c73532SDimitry Andric }
4681706b4fc4SDimitry Andric
operator [](size_t i)4682706b4fc4SDimitry Andric TreeItem &operator[](size_t i) { return m_children[i]; }
4683706b4fc4SDimitry Andric
SetRowIndex(int row_idx)4684706b4fc4SDimitry Andric void SetRowIndex(int row_idx) { m_row_idx = row_idx; }
4685706b4fc4SDimitry Andric
GetNumChildren()4686706b4fc4SDimitry Andric size_t GetNumChildren() {
4687b1c73532SDimitry Andric m_delegate->TreeDelegateGenerateChildren(*this);
4688706b4fc4SDimitry Andric return m_children.size();
4689706b4fc4SDimitry Andric }
4690706b4fc4SDimitry Andric
ItemWasSelected()4691b1c73532SDimitry Andric void ItemWasSelected() { m_delegate->TreeDelegateItemSelected(*this); }
4692706b4fc4SDimitry Andric
CalculateRowIndexes(int & row_idx)4693706b4fc4SDimitry Andric void CalculateRowIndexes(int &row_idx) {
4694706b4fc4SDimitry Andric SetRowIndex(row_idx);
4695706b4fc4SDimitry Andric ++row_idx;
4696706b4fc4SDimitry Andric
4697706b4fc4SDimitry Andric const bool expanded = IsExpanded();
4698706b4fc4SDimitry Andric
4699706b4fc4SDimitry Andric // The root item must calculate its children, or we must calculate the
4700706b4fc4SDimitry Andric // number of children if the item is expanded
4701706b4fc4SDimitry Andric if (m_parent == nullptr || expanded)
4702706b4fc4SDimitry Andric GetNumChildren();
4703706b4fc4SDimitry Andric
4704706b4fc4SDimitry Andric for (auto &item : m_children) {
4705706b4fc4SDimitry Andric if (expanded)
4706706b4fc4SDimitry Andric item.CalculateRowIndexes(row_idx);
4707706b4fc4SDimitry Andric else
4708706b4fc4SDimitry Andric item.SetRowIndex(-1);
4709706b4fc4SDimitry Andric }
4710706b4fc4SDimitry Andric }
4711706b4fc4SDimitry Andric
GetParent()4712706b4fc4SDimitry Andric TreeItem *GetParent() { return m_parent; }
4713706b4fc4SDimitry Andric
IsExpanded() const4714706b4fc4SDimitry Andric bool IsExpanded() const { return m_is_expanded; }
4715706b4fc4SDimitry Andric
Expand()4716706b4fc4SDimitry Andric void Expand() { m_is_expanded = true; }
4717706b4fc4SDimitry Andric
Unexpand()4718706b4fc4SDimitry Andric void Unexpand() { m_is_expanded = false; }
4719706b4fc4SDimitry Andric
Draw(Window & window,const int first_visible_row,const uint32_t selected_row_idx,int & row_idx,int & num_rows_left)4720706b4fc4SDimitry Andric bool Draw(Window &window, const int first_visible_row,
4721706b4fc4SDimitry Andric const uint32_t selected_row_idx, int &row_idx, int &num_rows_left) {
4722706b4fc4SDimitry Andric if (num_rows_left <= 0)
4723706b4fc4SDimitry Andric return false;
4724706b4fc4SDimitry Andric
4725706b4fc4SDimitry Andric if (m_row_idx >= first_visible_row) {
4726706b4fc4SDimitry Andric window.MoveCursor(2, row_idx + 1);
4727706b4fc4SDimitry Andric
4728706b4fc4SDimitry Andric if (m_parent)
4729706b4fc4SDimitry Andric m_parent->DrawTreeForChild(window, this, 0);
4730706b4fc4SDimitry Andric
4731706b4fc4SDimitry Andric if (m_might_have_children) {
4732706b4fc4SDimitry Andric // It we can get UTF8 characters to work we should try to use the
4733706b4fc4SDimitry Andric // "symbol" UTF8 string below
4734706b4fc4SDimitry Andric // const char *symbol = "";
4735706b4fc4SDimitry Andric // if (row.expanded)
4736706b4fc4SDimitry Andric // symbol = "\xe2\x96\xbd ";
4737706b4fc4SDimitry Andric // else
4738706b4fc4SDimitry Andric // symbol = "\xe2\x96\xb7 ";
4739706b4fc4SDimitry Andric // window.PutCString (symbol);
4740706b4fc4SDimitry Andric
4741706b4fc4SDimitry Andric // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
4742706b4fc4SDimitry Andric // 'v' or '>' character...
4743706b4fc4SDimitry Andric // if (expanded)
4744706b4fc4SDimitry Andric // window.PutChar (ACS_DARROW);
4745706b4fc4SDimitry Andric // else
4746706b4fc4SDimitry Andric // window.PutChar (ACS_RARROW);
4747706b4fc4SDimitry Andric // Since we can't find any good looking right arrow/down arrow symbols,
4748706b4fc4SDimitry Andric // just use a diamond...
4749706b4fc4SDimitry Andric window.PutChar(ACS_DIAMOND);
4750706b4fc4SDimitry Andric window.PutChar(ACS_HLINE);
4751706b4fc4SDimitry Andric }
4752706b4fc4SDimitry Andric bool highlight = (selected_row_idx == static_cast<size_t>(m_row_idx)) &&
4753706b4fc4SDimitry Andric window.IsActive();
4754706b4fc4SDimitry Andric
4755706b4fc4SDimitry Andric if (highlight)
4756706b4fc4SDimitry Andric window.AttributeOn(A_REVERSE);
4757706b4fc4SDimitry Andric
4758b1c73532SDimitry Andric m_delegate->TreeDelegateDrawTreeItem(*this, window);
4759706b4fc4SDimitry Andric
4760706b4fc4SDimitry Andric if (highlight)
4761706b4fc4SDimitry Andric window.AttributeOff(A_REVERSE);
4762706b4fc4SDimitry Andric ++row_idx;
4763706b4fc4SDimitry Andric --num_rows_left;
4764706b4fc4SDimitry Andric }
4765706b4fc4SDimitry Andric
4766706b4fc4SDimitry Andric if (num_rows_left <= 0)
4767706b4fc4SDimitry Andric return false; // We are done drawing...
4768706b4fc4SDimitry Andric
4769706b4fc4SDimitry Andric if (IsExpanded()) {
4770706b4fc4SDimitry Andric for (auto &item : m_children) {
4771706b4fc4SDimitry Andric // If we displayed all the rows and item.Draw() returns false we are
4772706b4fc4SDimitry Andric // done drawing and can exit this for loop
4773706b4fc4SDimitry Andric if (!item.Draw(window, first_visible_row, selected_row_idx, row_idx,
4774706b4fc4SDimitry Andric num_rows_left))
4775706b4fc4SDimitry Andric break;
4776706b4fc4SDimitry Andric }
4777706b4fc4SDimitry Andric }
4778706b4fc4SDimitry Andric return num_rows_left >= 0; // Return true if not done drawing yet
4779706b4fc4SDimitry Andric }
4780706b4fc4SDimitry Andric
DrawTreeForChild(Window & window,TreeItem * child,uint32_t reverse_depth)4781706b4fc4SDimitry Andric void DrawTreeForChild(Window &window, TreeItem *child,
4782706b4fc4SDimitry Andric uint32_t reverse_depth) {
4783706b4fc4SDimitry Andric if (m_parent)
4784706b4fc4SDimitry Andric m_parent->DrawTreeForChild(window, this, reverse_depth + 1);
4785706b4fc4SDimitry Andric
4786706b4fc4SDimitry Andric if (&m_children.back() == child) {
4787706b4fc4SDimitry Andric // Last child
4788706b4fc4SDimitry Andric if (reverse_depth == 0) {
4789706b4fc4SDimitry Andric window.PutChar(ACS_LLCORNER);
4790706b4fc4SDimitry Andric window.PutChar(ACS_HLINE);
4791706b4fc4SDimitry Andric } else {
4792706b4fc4SDimitry Andric window.PutChar(' ');
4793706b4fc4SDimitry Andric window.PutChar(' ');
4794706b4fc4SDimitry Andric }
4795706b4fc4SDimitry Andric } else {
4796706b4fc4SDimitry Andric if (reverse_depth == 0) {
4797706b4fc4SDimitry Andric window.PutChar(ACS_LTEE);
4798706b4fc4SDimitry Andric window.PutChar(ACS_HLINE);
4799706b4fc4SDimitry Andric } else {
4800706b4fc4SDimitry Andric window.PutChar(ACS_VLINE);
4801706b4fc4SDimitry Andric window.PutChar(' ');
4802706b4fc4SDimitry Andric }
4803706b4fc4SDimitry Andric }
4804706b4fc4SDimitry Andric }
4805706b4fc4SDimitry Andric
GetItemForRowIndex(uint32_t row_idx)4806706b4fc4SDimitry Andric TreeItem *GetItemForRowIndex(uint32_t row_idx) {
4807706b4fc4SDimitry Andric if (static_cast<uint32_t>(m_row_idx) == row_idx)
4808706b4fc4SDimitry Andric return this;
4809706b4fc4SDimitry Andric if (m_children.empty())
4810706b4fc4SDimitry Andric return nullptr;
4811706b4fc4SDimitry Andric if (IsExpanded()) {
4812706b4fc4SDimitry Andric for (auto &item : m_children) {
4813706b4fc4SDimitry Andric TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx);
4814706b4fc4SDimitry Andric if (selected_item_ptr)
4815706b4fc4SDimitry Andric return selected_item_ptr;
4816706b4fc4SDimitry Andric }
4817706b4fc4SDimitry Andric }
4818706b4fc4SDimitry Andric return nullptr;
4819706b4fc4SDimitry Andric }
4820706b4fc4SDimitry Andric
GetUserData() const4821706b4fc4SDimitry Andric void *GetUserData() const { return m_user_data; }
4822706b4fc4SDimitry Andric
SetUserData(void * user_data)4823706b4fc4SDimitry Andric void SetUserData(void *user_data) { m_user_data = user_data; }
4824706b4fc4SDimitry Andric
GetIdentifier() const4825706b4fc4SDimitry Andric uint64_t GetIdentifier() const { return m_identifier; }
4826706b4fc4SDimitry Andric
SetIdentifier(uint64_t identifier)4827706b4fc4SDimitry Andric void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
4828706b4fc4SDimitry Andric
GetText() const4829c0981da4SDimitry Andric const std::string &GetText() const { return m_text; }
4830c0981da4SDimitry Andric
SetText(const char * text)4831c0981da4SDimitry Andric void SetText(const char *text) {
4832c0981da4SDimitry Andric if (text == nullptr) {
4833c0981da4SDimitry Andric m_text.clear();
4834c0981da4SDimitry Andric return;
4835c0981da4SDimitry Andric }
4836c0981da4SDimitry Andric m_text = text;
4837c0981da4SDimitry Andric }
4838c0981da4SDimitry Andric
SetMightHaveChildren(bool b)4839706b4fc4SDimitry Andric void SetMightHaveChildren(bool b) { m_might_have_children = b; }
4840706b4fc4SDimitry Andric
4841706b4fc4SDimitry Andric protected:
AdoptChildren(std::vector<TreeItem> & children)4842b1c73532SDimitry Andric void AdoptChildren(std::vector<TreeItem> &children) {
4843b1c73532SDimitry Andric m_children = std::move(children);
4844b1c73532SDimitry Andric for (auto &child : m_children)
4845b1c73532SDimitry Andric child.m_parent = this;
4846b1c73532SDimitry Andric }
4847b1c73532SDimitry Andric
4848706b4fc4SDimitry Andric std::vector<TreeItem> m_children;
4849706b4fc4SDimitry Andric };
4850706b4fc4SDimitry Andric
4851706b4fc4SDimitry Andric class TreeWindowDelegate : public WindowDelegate {
4852706b4fc4SDimitry Andric public:
TreeWindowDelegate(Debugger & debugger,const TreeDelegateSP & delegate_sp)4853706b4fc4SDimitry Andric TreeWindowDelegate(Debugger &debugger, const TreeDelegateSP &delegate_sp)
4854706b4fc4SDimitry Andric : m_debugger(debugger), m_delegate_sp(delegate_sp),
4855145449b1SDimitry Andric m_root(nullptr, *delegate_sp, true) {}
4856706b4fc4SDimitry Andric
NumVisibleRows() const4857706b4fc4SDimitry Andric int NumVisibleRows() const { return m_max_y - m_min_y; }
4858706b4fc4SDimitry Andric
WindowDelegateDraw(Window & window,bool force)4859706b4fc4SDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override {
4860706b4fc4SDimitry Andric m_min_x = 2;
4861706b4fc4SDimitry Andric m_min_y = 1;
4862706b4fc4SDimitry Andric m_max_x = window.GetWidth() - 1;
4863706b4fc4SDimitry Andric m_max_y = window.GetHeight() - 1;
4864706b4fc4SDimitry Andric
4865706b4fc4SDimitry Andric window.Erase();
4866706b4fc4SDimitry Andric window.DrawTitleBox(window.GetName());
4867706b4fc4SDimitry Andric
4868c0981da4SDimitry Andric if (!m_delegate_sp->TreeDelegateShouldDraw()) {
4869c0981da4SDimitry Andric m_selected_item = nullptr;
4870c0981da4SDimitry Andric return true;
4871c0981da4SDimitry Andric }
4872c0981da4SDimitry Andric
4873706b4fc4SDimitry Andric const int num_visible_rows = NumVisibleRows();
4874706b4fc4SDimitry Andric m_num_rows = 0;
4875706b4fc4SDimitry Andric m_root.CalculateRowIndexes(m_num_rows);
4876344a3780SDimitry Andric m_delegate_sp->TreeDelegateUpdateSelection(m_root, m_selected_row_idx,
4877344a3780SDimitry Andric m_selected_item);
4878706b4fc4SDimitry Andric
4879706b4fc4SDimitry Andric // If we unexpanded while having something selected our total number of
4880706b4fc4SDimitry Andric // rows is less than the num visible rows, then make sure we show all the
4881706b4fc4SDimitry Andric // rows by setting the first visible row accordingly.
4882706b4fc4SDimitry Andric if (m_first_visible_row > 0 && m_num_rows < num_visible_rows)
4883706b4fc4SDimitry Andric m_first_visible_row = 0;
4884706b4fc4SDimitry Andric
4885706b4fc4SDimitry Andric // Make sure the selected row is always visible
4886706b4fc4SDimitry Andric if (m_selected_row_idx < m_first_visible_row)
4887706b4fc4SDimitry Andric m_first_visible_row = m_selected_row_idx;
4888706b4fc4SDimitry Andric else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
4889706b4fc4SDimitry Andric m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
4890706b4fc4SDimitry Andric
4891706b4fc4SDimitry Andric int row_idx = 0;
4892706b4fc4SDimitry Andric int num_rows_left = num_visible_rows;
4893706b4fc4SDimitry Andric m_root.Draw(window, m_first_visible_row, m_selected_row_idx, row_idx,
4894706b4fc4SDimitry Andric num_rows_left);
4895706b4fc4SDimitry Andric // Get the selected row
4896706b4fc4SDimitry Andric m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4897706b4fc4SDimitry Andric
4898706b4fc4SDimitry Andric return true; // Drawing handled
4899706b4fc4SDimitry Andric }
4900706b4fc4SDimitry Andric
WindowDelegateGetHelpText()4901706b4fc4SDimitry Andric const char *WindowDelegateGetHelpText() override {
4902706b4fc4SDimitry Andric return "Thread window keyboard shortcuts:";
4903706b4fc4SDimitry Andric }
4904706b4fc4SDimitry Andric
WindowDelegateGetKeyHelp()4905706b4fc4SDimitry Andric KeyHelp *WindowDelegateGetKeyHelp() override {
4906706b4fc4SDimitry Andric static curses::KeyHelp g_source_view_key_help[] = {
4907706b4fc4SDimitry Andric {KEY_UP, "Select previous item"},
4908706b4fc4SDimitry Andric {KEY_DOWN, "Select next item"},
4909706b4fc4SDimitry Andric {KEY_RIGHT, "Expand the selected item"},
4910706b4fc4SDimitry Andric {KEY_LEFT,
4911706b4fc4SDimitry Andric "Unexpand the selected item or select parent if not expanded"},
4912706b4fc4SDimitry Andric {KEY_PPAGE, "Page up"},
4913706b4fc4SDimitry Andric {KEY_NPAGE, "Page down"},
4914706b4fc4SDimitry Andric {'h', "Show help dialog"},
4915706b4fc4SDimitry Andric {' ', "Toggle item expansion"},
4916706b4fc4SDimitry Andric {',', "Page up"},
4917706b4fc4SDimitry Andric {'.', "Page down"},
4918706b4fc4SDimitry Andric {'\0', nullptr}};
4919706b4fc4SDimitry Andric return g_source_view_key_help;
4920706b4fc4SDimitry Andric }
4921706b4fc4SDimitry Andric
WindowDelegateHandleChar(Window & window,int c)4922706b4fc4SDimitry Andric HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
4923706b4fc4SDimitry Andric switch (c) {
4924706b4fc4SDimitry Andric case ',':
4925706b4fc4SDimitry Andric case KEY_PPAGE:
4926706b4fc4SDimitry Andric // Page up key
4927706b4fc4SDimitry Andric if (m_first_visible_row > 0) {
4928706b4fc4SDimitry Andric if (m_first_visible_row > m_max_y)
4929706b4fc4SDimitry Andric m_first_visible_row -= m_max_y;
4930706b4fc4SDimitry Andric else
4931706b4fc4SDimitry Andric m_first_visible_row = 0;
4932706b4fc4SDimitry Andric m_selected_row_idx = m_first_visible_row;
4933706b4fc4SDimitry Andric m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4934706b4fc4SDimitry Andric if (m_selected_item)
4935706b4fc4SDimitry Andric m_selected_item->ItemWasSelected();
4936706b4fc4SDimitry Andric }
4937706b4fc4SDimitry Andric return eKeyHandled;
4938706b4fc4SDimitry Andric
4939706b4fc4SDimitry Andric case '.':
4940706b4fc4SDimitry Andric case KEY_NPAGE:
4941706b4fc4SDimitry Andric // Page down key
4942706b4fc4SDimitry Andric if (m_num_rows > m_max_y) {
4943706b4fc4SDimitry Andric if (m_first_visible_row + m_max_y < m_num_rows) {
4944706b4fc4SDimitry Andric m_first_visible_row += m_max_y;
4945706b4fc4SDimitry Andric m_selected_row_idx = m_first_visible_row;
4946706b4fc4SDimitry Andric m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4947706b4fc4SDimitry Andric if (m_selected_item)
4948706b4fc4SDimitry Andric m_selected_item->ItemWasSelected();
4949706b4fc4SDimitry Andric }
4950706b4fc4SDimitry Andric }
4951706b4fc4SDimitry Andric return eKeyHandled;
4952706b4fc4SDimitry Andric
4953706b4fc4SDimitry Andric case KEY_UP:
4954706b4fc4SDimitry Andric if (m_selected_row_idx > 0) {
4955706b4fc4SDimitry Andric --m_selected_row_idx;
4956706b4fc4SDimitry Andric m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4957706b4fc4SDimitry Andric if (m_selected_item)
4958706b4fc4SDimitry Andric m_selected_item->ItemWasSelected();
4959706b4fc4SDimitry Andric }
4960706b4fc4SDimitry Andric return eKeyHandled;
4961706b4fc4SDimitry Andric
4962706b4fc4SDimitry Andric case KEY_DOWN:
4963706b4fc4SDimitry Andric if (m_selected_row_idx + 1 < m_num_rows) {
4964706b4fc4SDimitry Andric ++m_selected_row_idx;
4965706b4fc4SDimitry Andric m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4966706b4fc4SDimitry Andric if (m_selected_item)
4967706b4fc4SDimitry Andric m_selected_item->ItemWasSelected();
4968706b4fc4SDimitry Andric }
4969706b4fc4SDimitry Andric return eKeyHandled;
4970706b4fc4SDimitry Andric
4971706b4fc4SDimitry Andric case KEY_RIGHT:
4972706b4fc4SDimitry Andric if (m_selected_item) {
4973706b4fc4SDimitry Andric if (!m_selected_item->IsExpanded())
4974706b4fc4SDimitry Andric m_selected_item->Expand();
4975706b4fc4SDimitry Andric }
4976706b4fc4SDimitry Andric return eKeyHandled;
4977706b4fc4SDimitry Andric
4978706b4fc4SDimitry Andric case KEY_LEFT:
4979706b4fc4SDimitry Andric if (m_selected_item) {
4980706b4fc4SDimitry Andric if (m_selected_item->IsExpanded())
4981706b4fc4SDimitry Andric m_selected_item->Unexpand();
4982706b4fc4SDimitry Andric else if (m_selected_item->GetParent()) {
4983706b4fc4SDimitry Andric m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex();
4984706b4fc4SDimitry Andric m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4985706b4fc4SDimitry Andric if (m_selected_item)
4986706b4fc4SDimitry Andric m_selected_item->ItemWasSelected();
4987706b4fc4SDimitry Andric }
4988706b4fc4SDimitry Andric }
4989706b4fc4SDimitry Andric return eKeyHandled;
4990706b4fc4SDimitry Andric
4991706b4fc4SDimitry Andric case ' ':
4992706b4fc4SDimitry Andric // Toggle expansion state when SPACE is pressed
4993706b4fc4SDimitry Andric if (m_selected_item) {
4994706b4fc4SDimitry Andric if (m_selected_item->IsExpanded())
4995706b4fc4SDimitry Andric m_selected_item->Unexpand();
4996706b4fc4SDimitry Andric else
4997706b4fc4SDimitry Andric m_selected_item->Expand();
4998706b4fc4SDimitry Andric }
4999706b4fc4SDimitry Andric return eKeyHandled;
5000706b4fc4SDimitry Andric
5001706b4fc4SDimitry Andric case 'h':
5002706b4fc4SDimitry Andric window.CreateHelpSubwindow();
5003706b4fc4SDimitry Andric return eKeyHandled;
5004706b4fc4SDimitry Andric
5005706b4fc4SDimitry Andric default:
5006706b4fc4SDimitry Andric break;
5007706b4fc4SDimitry Andric }
5008706b4fc4SDimitry Andric return eKeyNotHandled;
5009706b4fc4SDimitry Andric }
5010706b4fc4SDimitry Andric
5011706b4fc4SDimitry Andric protected:
5012706b4fc4SDimitry Andric Debugger &m_debugger;
5013706b4fc4SDimitry Andric TreeDelegateSP m_delegate_sp;
5014706b4fc4SDimitry Andric TreeItem m_root;
5015145449b1SDimitry Andric TreeItem *m_selected_item = nullptr;
5016145449b1SDimitry Andric int m_num_rows = 0;
5017145449b1SDimitry Andric int m_selected_row_idx = 0;
5018145449b1SDimitry Andric int m_first_visible_row = 0;
5019145449b1SDimitry Andric int m_min_x = 0;
5020145449b1SDimitry Andric int m_min_y = 0;
5021145449b1SDimitry Andric int m_max_x = 0;
5022145449b1SDimitry Andric int m_max_y = 0;
5023706b4fc4SDimitry Andric };
5024706b4fc4SDimitry Andric
5025c0981da4SDimitry Andric // A tree delegate that just draws the text member of the tree item, it doesn't
5026c0981da4SDimitry Andric // have any children or actions.
5027c0981da4SDimitry Andric class TextTreeDelegate : public TreeDelegate {
5028c0981da4SDimitry Andric public:
TextTreeDelegate()5029c0981da4SDimitry Andric TextTreeDelegate() : TreeDelegate() {}
5030c0981da4SDimitry Andric
5031c0981da4SDimitry Andric ~TextTreeDelegate() override = default;
5032c0981da4SDimitry Andric
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5033c0981da4SDimitry Andric void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5034c0981da4SDimitry Andric window.PutCStringTruncated(1, item.GetText().c_str());
5035c0981da4SDimitry Andric }
5036c0981da4SDimitry Andric
TreeDelegateGenerateChildren(TreeItem & item)5037c0981da4SDimitry Andric void TreeDelegateGenerateChildren(TreeItem &item) override {}
5038c0981da4SDimitry Andric
TreeDelegateItemSelected(TreeItem & item)5039c0981da4SDimitry Andric bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5040c0981da4SDimitry Andric };
5041c0981da4SDimitry Andric
5042706b4fc4SDimitry Andric class FrameTreeDelegate : public TreeDelegate {
5043706b4fc4SDimitry Andric public:
FrameTreeDelegate()5044706b4fc4SDimitry Andric FrameTreeDelegate() : TreeDelegate() {
5045706b4fc4SDimitry Andric FormatEntity::Parse(
5046145449b1SDimitry Andric "#${frame.index}: {${function.name}${function.pc-offset}}}", m_format);
5047706b4fc4SDimitry Andric }
5048706b4fc4SDimitry Andric
5049706b4fc4SDimitry Andric ~FrameTreeDelegate() override = default;
5050706b4fc4SDimitry Andric
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5051706b4fc4SDimitry Andric void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5052706b4fc4SDimitry Andric Thread *thread = (Thread *)item.GetUserData();
5053706b4fc4SDimitry Andric if (thread) {
5054706b4fc4SDimitry Andric const uint64_t frame_idx = item.GetIdentifier();
5055706b4fc4SDimitry Andric StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_idx);
5056706b4fc4SDimitry Andric if (frame_sp) {
5057706b4fc4SDimitry Andric StreamString strm;
5058706b4fc4SDimitry Andric const SymbolContext &sc =
5059706b4fc4SDimitry Andric frame_sp->GetSymbolContext(eSymbolContextEverything);
5060706b4fc4SDimitry Andric ExecutionContext exe_ctx(frame_sp);
5061706b4fc4SDimitry Andric if (FormatEntity::Format(m_format, strm, &sc, &exe_ctx, nullptr,
5062706b4fc4SDimitry Andric nullptr, false, false)) {
5063706b4fc4SDimitry Andric int right_pad = 1;
5064b60736ecSDimitry Andric window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
5065706b4fc4SDimitry Andric }
5066706b4fc4SDimitry Andric }
5067706b4fc4SDimitry Andric }
5068706b4fc4SDimitry Andric }
5069706b4fc4SDimitry Andric
TreeDelegateGenerateChildren(TreeItem & item)5070706b4fc4SDimitry Andric void TreeDelegateGenerateChildren(TreeItem &item) override {
5071706b4fc4SDimitry Andric // No children for frames yet...
5072706b4fc4SDimitry Andric }
5073706b4fc4SDimitry Andric
TreeDelegateItemSelected(TreeItem & item)5074706b4fc4SDimitry Andric bool TreeDelegateItemSelected(TreeItem &item) override {
5075706b4fc4SDimitry Andric Thread *thread = (Thread *)item.GetUserData();
5076706b4fc4SDimitry Andric if (thread) {
5077706b4fc4SDimitry Andric thread->GetProcess()->GetThreadList().SetSelectedThreadByID(
5078706b4fc4SDimitry Andric thread->GetID());
5079706b4fc4SDimitry Andric const uint64_t frame_idx = item.GetIdentifier();
5080706b4fc4SDimitry Andric thread->SetSelectedFrameByIndex(frame_idx);
5081706b4fc4SDimitry Andric return true;
5082706b4fc4SDimitry Andric }
5083706b4fc4SDimitry Andric return false;
5084706b4fc4SDimitry Andric }
5085706b4fc4SDimitry Andric
5086706b4fc4SDimitry Andric protected:
5087706b4fc4SDimitry Andric FormatEntity::Entry m_format;
5088706b4fc4SDimitry Andric };
5089706b4fc4SDimitry Andric
5090706b4fc4SDimitry Andric class ThreadTreeDelegate : public TreeDelegate {
5091706b4fc4SDimitry Andric public:
ThreadTreeDelegate(Debugger & debugger)5092706b4fc4SDimitry Andric ThreadTreeDelegate(Debugger &debugger)
5093145449b1SDimitry Andric : TreeDelegate(), m_debugger(debugger) {
5094706b4fc4SDimitry Andric FormatEntity::Parse("thread #${thread.index}: tid = ${thread.id}{, stop "
5095706b4fc4SDimitry Andric "reason = ${thread.stop-reason}}",
5096706b4fc4SDimitry Andric m_format);
5097706b4fc4SDimitry Andric }
5098706b4fc4SDimitry Andric
5099706b4fc4SDimitry Andric ~ThreadTreeDelegate() override = default;
5100706b4fc4SDimitry Andric
GetProcess()5101706b4fc4SDimitry Andric ProcessSP GetProcess() {
5102706b4fc4SDimitry Andric return m_debugger.GetCommandInterpreter()
5103706b4fc4SDimitry Andric .GetExecutionContext()
5104706b4fc4SDimitry Andric .GetProcessSP();
5105706b4fc4SDimitry Andric }
5106706b4fc4SDimitry Andric
GetThread(const TreeItem & item)5107706b4fc4SDimitry Andric ThreadSP GetThread(const TreeItem &item) {
5108706b4fc4SDimitry Andric ProcessSP process_sp = GetProcess();
5109706b4fc4SDimitry Andric if (process_sp)
5110706b4fc4SDimitry Andric return process_sp->GetThreadList().FindThreadByID(item.GetIdentifier());
5111706b4fc4SDimitry Andric return ThreadSP();
5112706b4fc4SDimitry Andric }
5113706b4fc4SDimitry Andric
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5114706b4fc4SDimitry Andric void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5115706b4fc4SDimitry Andric ThreadSP thread_sp = GetThread(item);
5116706b4fc4SDimitry Andric if (thread_sp) {
5117706b4fc4SDimitry Andric StreamString strm;
5118706b4fc4SDimitry Andric ExecutionContext exe_ctx(thread_sp);
5119706b4fc4SDimitry Andric if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
5120706b4fc4SDimitry Andric nullptr, false, false)) {
5121706b4fc4SDimitry Andric int right_pad = 1;
5122b60736ecSDimitry Andric window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
5123706b4fc4SDimitry Andric }
5124706b4fc4SDimitry Andric }
5125706b4fc4SDimitry Andric }
5126706b4fc4SDimitry Andric
TreeDelegateGenerateChildren(TreeItem & item)5127706b4fc4SDimitry Andric void TreeDelegateGenerateChildren(TreeItem &item) override {
5128706b4fc4SDimitry Andric ProcessSP process_sp = GetProcess();
5129706b4fc4SDimitry Andric if (process_sp && process_sp->IsAlive()) {
5130706b4fc4SDimitry Andric StateType state = process_sp->GetState();
5131706b4fc4SDimitry Andric if (StateIsStoppedState(state, true)) {
5132706b4fc4SDimitry Andric ThreadSP thread_sp = GetThread(item);
5133706b4fc4SDimitry Andric if (thread_sp) {
5134706b4fc4SDimitry Andric if (m_stop_id == process_sp->GetStopID() &&
5135706b4fc4SDimitry Andric thread_sp->GetID() == m_tid)
5136706b4fc4SDimitry Andric return; // Children are already up to date
5137706b4fc4SDimitry Andric if (!m_frame_delegate_sp) {
5138706b4fc4SDimitry Andric // Always expand the thread item the first time we show it
5139706b4fc4SDimitry Andric m_frame_delegate_sp = std::make_shared<FrameTreeDelegate>();
5140706b4fc4SDimitry Andric }
5141706b4fc4SDimitry Andric
5142706b4fc4SDimitry Andric m_stop_id = process_sp->GetStopID();
5143706b4fc4SDimitry Andric m_tid = thread_sp->GetID();
5144706b4fc4SDimitry Andric
5145706b4fc4SDimitry Andric size_t num_frames = thread_sp->GetStackFrameCount();
5146b1c73532SDimitry Andric item.Resize(num_frames, *m_frame_delegate_sp, false);
5147706b4fc4SDimitry Andric for (size_t i = 0; i < num_frames; ++i) {
5148706b4fc4SDimitry Andric item[i].SetUserData(thread_sp.get());
5149706b4fc4SDimitry Andric item[i].SetIdentifier(i);
5150706b4fc4SDimitry Andric }
5151706b4fc4SDimitry Andric }
5152706b4fc4SDimitry Andric return;
5153706b4fc4SDimitry Andric }
5154706b4fc4SDimitry Andric }
5155706b4fc4SDimitry Andric item.ClearChildren();
5156706b4fc4SDimitry Andric }
5157706b4fc4SDimitry Andric
TreeDelegateItemSelected(TreeItem & item)5158706b4fc4SDimitry Andric bool TreeDelegateItemSelected(TreeItem &item) override {
5159706b4fc4SDimitry Andric ProcessSP process_sp = GetProcess();
5160706b4fc4SDimitry Andric if (process_sp && process_sp->IsAlive()) {
5161706b4fc4SDimitry Andric StateType state = process_sp->GetState();
5162706b4fc4SDimitry Andric if (StateIsStoppedState(state, true)) {
5163706b4fc4SDimitry Andric ThreadSP thread_sp = GetThread(item);
5164706b4fc4SDimitry Andric if (thread_sp) {
5165706b4fc4SDimitry Andric ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList();
5166706b4fc4SDimitry Andric std::lock_guard<std::recursive_mutex> guard(thread_list.GetMutex());
5167706b4fc4SDimitry Andric ThreadSP selected_thread_sp = thread_list.GetSelectedThread();
5168706b4fc4SDimitry Andric if (selected_thread_sp->GetID() != thread_sp->GetID()) {
5169706b4fc4SDimitry Andric thread_list.SetSelectedThreadByID(thread_sp->GetID());
5170706b4fc4SDimitry Andric return true;
5171706b4fc4SDimitry Andric }
5172706b4fc4SDimitry Andric }
5173706b4fc4SDimitry Andric }
5174706b4fc4SDimitry Andric }
5175706b4fc4SDimitry Andric return false;
5176706b4fc4SDimitry Andric }
5177706b4fc4SDimitry Andric
5178706b4fc4SDimitry Andric protected:
5179706b4fc4SDimitry Andric Debugger &m_debugger;
5180706b4fc4SDimitry Andric std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp;
5181145449b1SDimitry Andric lldb::user_id_t m_tid = LLDB_INVALID_THREAD_ID;
5182145449b1SDimitry Andric uint32_t m_stop_id = UINT32_MAX;
5183706b4fc4SDimitry Andric FormatEntity::Entry m_format;
5184706b4fc4SDimitry Andric };
5185706b4fc4SDimitry Andric
5186706b4fc4SDimitry Andric class ThreadsTreeDelegate : public TreeDelegate {
5187706b4fc4SDimitry Andric public:
ThreadsTreeDelegate(Debugger & debugger)5188706b4fc4SDimitry Andric ThreadsTreeDelegate(Debugger &debugger)
5189145449b1SDimitry Andric : TreeDelegate(), m_thread_delegate_sp(), m_debugger(debugger) {
5190706b4fc4SDimitry Andric FormatEntity::Parse("process ${process.id}{, name = ${process.name}}",
5191706b4fc4SDimitry Andric m_format);
5192706b4fc4SDimitry Andric }
5193706b4fc4SDimitry Andric
5194706b4fc4SDimitry Andric ~ThreadsTreeDelegate() override = default;
5195706b4fc4SDimitry Andric
GetProcess()5196706b4fc4SDimitry Andric ProcessSP GetProcess() {
5197706b4fc4SDimitry Andric return m_debugger.GetCommandInterpreter()
5198706b4fc4SDimitry Andric .GetExecutionContext()
5199706b4fc4SDimitry Andric .GetProcessSP();
5200706b4fc4SDimitry Andric }
5201706b4fc4SDimitry Andric
TreeDelegateShouldDraw()5202c0981da4SDimitry Andric bool TreeDelegateShouldDraw() override {
5203c0981da4SDimitry Andric ProcessSP process = GetProcess();
5204c0981da4SDimitry Andric if (!process)
5205c0981da4SDimitry Andric return false;
5206c0981da4SDimitry Andric
5207c0981da4SDimitry Andric if (StateIsRunningState(process->GetState()))
5208c0981da4SDimitry Andric return false;
5209c0981da4SDimitry Andric
5210c0981da4SDimitry Andric return true;
5211c0981da4SDimitry Andric }
5212c0981da4SDimitry Andric
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5213706b4fc4SDimitry Andric void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5214706b4fc4SDimitry Andric ProcessSP process_sp = GetProcess();
5215706b4fc4SDimitry Andric if (process_sp && process_sp->IsAlive()) {
5216706b4fc4SDimitry Andric StreamString strm;
5217706b4fc4SDimitry Andric ExecutionContext exe_ctx(process_sp);
5218706b4fc4SDimitry Andric if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
5219706b4fc4SDimitry Andric nullptr, false, false)) {
5220706b4fc4SDimitry Andric int right_pad = 1;
5221b60736ecSDimitry Andric window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
5222706b4fc4SDimitry Andric }
5223706b4fc4SDimitry Andric }
5224706b4fc4SDimitry Andric }
5225706b4fc4SDimitry Andric
TreeDelegateGenerateChildren(TreeItem & item)5226706b4fc4SDimitry Andric void TreeDelegateGenerateChildren(TreeItem &item) override {
5227706b4fc4SDimitry Andric ProcessSP process_sp = GetProcess();
5228344a3780SDimitry Andric m_update_selection = false;
5229706b4fc4SDimitry Andric if (process_sp && process_sp->IsAlive()) {
5230706b4fc4SDimitry Andric StateType state = process_sp->GetState();
5231706b4fc4SDimitry Andric if (StateIsStoppedState(state, true)) {
5232706b4fc4SDimitry Andric const uint32_t stop_id = process_sp->GetStopID();
5233706b4fc4SDimitry Andric if (m_stop_id == stop_id)
5234706b4fc4SDimitry Andric return; // Children are already up to date
5235706b4fc4SDimitry Andric
5236706b4fc4SDimitry Andric m_stop_id = stop_id;
5237344a3780SDimitry Andric m_update_selection = true;
5238706b4fc4SDimitry Andric
5239706b4fc4SDimitry Andric if (!m_thread_delegate_sp) {
5240706b4fc4SDimitry Andric // Always expand the thread item the first time we show it
5241706b4fc4SDimitry Andric // item.Expand();
5242706b4fc4SDimitry Andric m_thread_delegate_sp =
5243706b4fc4SDimitry Andric std::make_shared<ThreadTreeDelegate>(m_debugger);
5244706b4fc4SDimitry Andric }
5245706b4fc4SDimitry Andric
5246706b4fc4SDimitry Andric ThreadList &threads = process_sp->GetThreadList();
5247706b4fc4SDimitry Andric std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
5248344a3780SDimitry Andric ThreadSP selected_thread = threads.GetSelectedThread();
5249706b4fc4SDimitry Andric size_t num_threads = threads.GetSize();
5250b1c73532SDimitry Andric item.Resize(num_threads, *m_thread_delegate_sp, false);
5251706b4fc4SDimitry Andric for (size_t i = 0; i < num_threads; ++i) {
5252344a3780SDimitry Andric ThreadSP thread = threads.GetThreadAtIndex(i);
5253344a3780SDimitry Andric item[i].SetIdentifier(thread->GetID());
5254706b4fc4SDimitry Andric item[i].SetMightHaveChildren(true);
5255344a3780SDimitry Andric if (selected_thread->GetID() == thread->GetID())
5256344a3780SDimitry Andric item[i].Expand();
5257706b4fc4SDimitry Andric }
5258706b4fc4SDimitry Andric return;
5259706b4fc4SDimitry Andric }
5260706b4fc4SDimitry Andric }
5261706b4fc4SDimitry Andric item.ClearChildren();
5262706b4fc4SDimitry Andric }
5263706b4fc4SDimitry Andric
TreeDelegateUpdateSelection(TreeItem & root,int & selection_index,TreeItem * & selected_item)5264344a3780SDimitry Andric void TreeDelegateUpdateSelection(TreeItem &root, int &selection_index,
5265344a3780SDimitry Andric TreeItem *&selected_item) override {
5266344a3780SDimitry Andric if (!m_update_selection)
5267344a3780SDimitry Andric return;
5268344a3780SDimitry Andric
5269344a3780SDimitry Andric ProcessSP process_sp = GetProcess();
5270344a3780SDimitry Andric if (!(process_sp && process_sp->IsAlive()))
5271344a3780SDimitry Andric return;
5272344a3780SDimitry Andric
5273344a3780SDimitry Andric StateType state = process_sp->GetState();
5274344a3780SDimitry Andric if (!StateIsStoppedState(state, true))
5275344a3780SDimitry Andric return;
5276344a3780SDimitry Andric
5277344a3780SDimitry Andric ThreadList &threads = process_sp->GetThreadList();
5278344a3780SDimitry Andric std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
5279344a3780SDimitry Andric ThreadSP selected_thread = threads.GetSelectedThread();
5280344a3780SDimitry Andric size_t num_threads = threads.GetSize();
5281344a3780SDimitry Andric for (size_t i = 0; i < num_threads; ++i) {
5282344a3780SDimitry Andric ThreadSP thread = threads.GetThreadAtIndex(i);
5283344a3780SDimitry Andric if (selected_thread->GetID() == thread->GetID()) {
52847fa27ce4SDimitry Andric selected_item =
52857fa27ce4SDimitry Andric &root[i][thread->GetSelectedFrameIndex(SelectMostRelevantFrame)];
5286344a3780SDimitry Andric selection_index = selected_item->GetRowIndex();
5287344a3780SDimitry Andric return;
5288344a3780SDimitry Andric }
5289344a3780SDimitry Andric }
5290344a3780SDimitry Andric }
5291344a3780SDimitry Andric
TreeDelegateItemSelected(TreeItem & item)5292706b4fc4SDimitry Andric bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5293706b4fc4SDimitry Andric
TreeDelegateExpandRootByDefault()5294344a3780SDimitry Andric bool TreeDelegateExpandRootByDefault() override { return true; }
5295344a3780SDimitry Andric
5296706b4fc4SDimitry Andric protected:
5297706b4fc4SDimitry Andric std::shared_ptr<ThreadTreeDelegate> m_thread_delegate_sp;
5298706b4fc4SDimitry Andric Debugger &m_debugger;
5299145449b1SDimitry Andric uint32_t m_stop_id = UINT32_MAX;
5300145449b1SDimitry Andric bool m_update_selection = false;
5301706b4fc4SDimitry Andric FormatEntity::Entry m_format;
5302706b4fc4SDimitry Andric };
5303706b4fc4SDimitry Andric
5304c0981da4SDimitry Andric class BreakpointLocationTreeDelegate : public TreeDelegate {
5305c0981da4SDimitry Andric public:
BreakpointLocationTreeDelegate(Debugger & debugger)5306c0981da4SDimitry Andric BreakpointLocationTreeDelegate(Debugger &debugger)
5307c0981da4SDimitry Andric : TreeDelegate(), m_debugger(debugger) {}
5308c0981da4SDimitry Andric
5309c0981da4SDimitry Andric ~BreakpointLocationTreeDelegate() override = default;
5310c0981da4SDimitry Andric
GetProcess()5311c0981da4SDimitry Andric Process *GetProcess() {
5312c0981da4SDimitry Andric ExecutionContext exe_ctx(
5313c0981da4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext());
5314c0981da4SDimitry Andric return exe_ctx.GetProcessPtr();
5315c0981da4SDimitry Andric }
5316c0981da4SDimitry Andric
GetBreakpointLocation(const TreeItem & item)5317c0981da4SDimitry Andric BreakpointLocationSP GetBreakpointLocation(const TreeItem &item) {
5318c0981da4SDimitry Andric Breakpoint *breakpoint = (Breakpoint *)item.GetUserData();
5319c0981da4SDimitry Andric return breakpoint->GetLocationAtIndex(item.GetIdentifier());
5320c0981da4SDimitry Andric }
5321c0981da4SDimitry Andric
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5322c0981da4SDimitry Andric void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5323c0981da4SDimitry Andric BreakpointLocationSP breakpoint_location = GetBreakpointLocation(item);
5324c0981da4SDimitry Andric Process *process = GetProcess();
5325c0981da4SDimitry Andric StreamString stream;
5326c0981da4SDimitry Andric stream.Printf("%i.%i: ", breakpoint_location->GetBreakpoint().GetID(),
5327c0981da4SDimitry Andric breakpoint_location->GetID());
5328c0981da4SDimitry Andric Address address = breakpoint_location->GetAddress();
5329c0981da4SDimitry Andric address.Dump(&stream, process, Address::DumpStyleResolvedDescription,
5330c0981da4SDimitry Andric Address::DumpStyleInvalid);
5331c0981da4SDimitry Andric window.PutCStringTruncated(1, stream.GetString().str().c_str());
5332c0981da4SDimitry Andric }
5333c0981da4SDimitry Andric
ComputeDetailsList(BreakpointLocationSP breakpoint_location)5334c0981da4SDimitry Andric StringList ComputeDetailsList(BreakpointLocationSP breakpoint_location) {
5335c0981da4SDimitry Andric StringList details;
5336c0981da4SDimitry Andric
5337c0981da4SDimitry Andric Address address = breakpoint_location->GetAddress();
5338c0981da4SDimitry Andric SymbolContext symbol_context;
5339c0981da4SDimitry Andric address.CalculateSymbolContext(&symbol_context);
5340c0981da4SDimitry Andric
5341c0981da4SDimitry Andric if (symbol_context.module_sp) {
5342c0981da4SDimitry Andric StreamString module_stream;
5343c0981da4SDimitry Andric module_stream.PutCString("module = ");
5344c0981da4SDimitry Andric symbol_context.module_sp->GetFileSpec().Dump(
5345c0981da4SDimitry Andric module_stream.AsRawOstream());
5346c0981da4SDimitry Andric details.AppendString(module_stream.GetString());
5347c0981da4SDimitry Andric }
5348c0981da4SDimitry Andric
5349c0981da4SDimitry Andric if (symbol_context.comp_unit != nullptr) {
5350c0981da4SDimitry Andric StreamString compile_unit_stream;
5351c0981da4SDimitry Andric compile_unit_stream.PutCString("compile unit = ");
5352c0981da4SDimitry Andric symbol_context.comp_unit->GetPrimaryFile().GetFilename().Dump(
5353c0981da4SDimitry Andric &compile_unit_stream);
5354c0981da4SDimitry Andric details.AppendString(compile_unit_stream.GetString());
5355c0981da4SDimitry Andric
5356c0981da4SDimitry Andric if (symbol_context.function != nullptr) {
5357c0981da4SDimitry Andric StreamString function_stream;
5358c0981da4SDimitry Andric function_stream.PutCString("function = ");
5359c0981da4SDimitry Andric function_stream.PutCString(
5360c0981da4SDimitry Andric symbol_context.function->GetName().AsCString("<unknown>"));
5361c0981da4SDimitry Andric details.AppendString(function_stream.GetString());
5362c0981da4SDimitry Andric }
5363c0981da4SDimitry Andric
5364c0981da4SDimitry Andric if (symbol_context.line_entry.line > 0) {
5365c0981da4SDimitry Andric StreamString location_stream;
5366c0981da4SDimitry Andric location_stream.PutCString("location = ");
5367c0981da4SDimitry Andric symbol_context.line_entry.DumpStopContext(&location_stream, true);
5368c0981da4SDimitry Andric details.AppendString(location_stream.GetString());
5369c0981da4SDimitry Andric }
5370c0981da4SDimitry Andric
5371c0981da4SDimitry Andric } else {
5372c0981da4SDimitry Andric if (symbol_context.symbol) {
5373c0981da4SDimitry Andric StreamString symbol_stream;
5374c0981da4SDimitry Andric if (breakpoint_location->IsReExported())
5375c0981da4SDimitry Andric symbol_stream.PutCString("re-exported target = ");
5376c0981da4SDimitry Andric else
5377c0981da4SDimitry Andric symbol_stream.PutCString("symbol = ");
5378c0981da4SDimitry Andric symbol_stream.PutCString(
5379c0981da4SDimitry Andric symbol_context.symbol->GetName().AsCString("<unknown>"));
5380c0981da4SDimitry Andric details.AppendString(symbol_stream.GetString());
5381c0981da4SDimitry Andric }
5382c0981da4SDimitry Andric }
5383c0981da4SDimitry Andric
5384c0981da4SDimitry Andric Process *process = GetProcess();
5385c0981da4SDimitry Andric
5386c0981da4SDimitry Andric StreamString address_stream;
5387c0981da4SDimitry Andric address.Dump(&address_stream, process, Address::DumpStyleLoadAddress,
5388c0981da4SDimitry Andric Address::DumpStyleModuleWithFileAddress);
5389c0981da4SDimitry Andric details.AppendString(address_stream.GetString());
5390c0981da4SDimitry Andric
5391c0981da4SDimitry Andric BreakpointSiteSP breakpoint_site = breakpoint_location->GetBreakpointSite();
5392c0981da4SDimitry Andric if (breakpoint_location->IsIndirect() && breakpoint_site) {
5393c0981da4SDimitry Andric Address resolved_address;
5394c0981da4SDimitry Andric resolved_address.SetLoadAddress(breakpoint_site->GetLoadAddress(),
5395c0981da4SDimitry Andric &breakpoint_location->GetTarget());
5396c0981da4SDimitry Andric Symbol *resolved_symbol = resolved_address.CalculateSymbolContextSymbol();
5397c0981da4SDimitry Andric if (resolved_symbol) {
5398c0981da4SDimitry Andric StreamString indirect_target_stream;
5399c0981da4SDimitry Andric indirect_target_stream.PutCString("indirect target = ");
5400c0981da4SDimitry Andric indirect_target_stream.PutCString(
5401c0981da4SDimitry Andric resolved_symbol->GetName().GetCString());
5402c0981da4SDimitry Andric details.AppendString(indirect_target_stream.GetString());
5403c0981da4SDimitry Andric }
5404c0981da4SDimitry Andric }
5405c0981da4SDimitry Andric
5406c0981da4SDimitry Andric bool is_resolved = breakpoint_location->IsResolved();
5407c0981da4SDimitry Andric StreamString resolved_stream;
5408c0981da4SDimitry Andric resolved_stream.Printf("resolved = %s", is_resolved ? "true" : "false");
5409c0981da4SDimitry Andric details.AppendString(resolved_stream.GetString());
5410c0981da4SDimitry Andric
5411c0981da4SDimitry Andric bool is_hardware = is_resolved && breakpoint_site->IsHardware();
5412c0981da4SDimitry Andric StreamString hardware_stream;
5413c0981da4SDimitry Andric hardware_stream.Printf("hardware = %s", is_hardware ? "true" : "false");
5414c0981da4SDimitry Andric details.AppendString(hardware_stream.GetString());
5415c0981da4SDimitry Andric
5416c0981da4SDimitry Andric StreamString hit_count_stream;
5417c0981da4SDimitry Andric hit_count_stream.Printf("hit count = %-4u",
5418c0981da4SDimitry Andric breakpoint_location->GetHitCount());
5419c0981da4SDimitry Andric details.AppendString(hit_count_stream.GetString());
5420c0981da4SDimitry Andric
5421c0981da4SDimitry Andric return details;
5422c0981da4SDimitry Andric }
5423c0981da4SDimitry Andric
TreeDelegateGenerateChildren(TreeItem & item)5424c0981da4SDimitry Andric void TreeDelegateGenerateChildren(TreeItem &item) override {
5425c0981da4SDimitry Andric BreakpointLocationSP breakpoint_location = GetBreakpointLocation(item);
5426c0981da4SDimitry Andric StringList details = ComputeDetailsList(breakpoint_location);
5427c0981da4SDimitry Andric
5428c0981da4SDimitry Andric if (!m_string_delegate_sp)
5429c0981da4SDimitry Andric m_string_delegate_sp = std::make_shared<TextTreeDelegate>();
5430c0981da4SDimitry Andric
5431b1c73532SDimitry Andric item.Resize(details.GetSize(), *m_string_delegate_sp, false);
5432c0981da4SDimitry Andric for (size_t i = 0; i < details.GetSize(); i++) {
5433c0981da4SDimitry Andric item[i].SetText(details.GetStringAtIndex(i));
5434c0981da4SDimitry Andric }
5435c0981da4SDimitry Andric }
5436c0981da4SDimitry Andric
TreeDelegateItemSelected(TreeItem & item)5437c0981da4SDimitry Andric bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5438c0981da4SDimitry Andric
5439c0981da4SDimitry Andric protected:
5440c0981da4SDimitry Andric Debugger &m_debugger;
5441c0981da4SDimitry Andric std::shared_ptr<TextTreeDelegate> m_string_delegate_sp;
5442c0981da4SDimitry Andric };
5443c0981da4SDimitry Andric
5444c0981da4SDimitry Andric class BreakpointTreeDelegate : public TreeDelegate {
5445c0981da4SDimitry Andric public:
BreakpointTreeDelegate(Debugger & debugger)5446c0981da4SDimitry Andric BreakpointTreeDelegate(Debugger &debugger)
5447c0981da4SDimitry Andric : TreeDelegate(), m_debugger(debugger),
5448c0981da4SDimitry Andric m_breakpoint_location_delegate_sp() {}
5449c0981da4SDimitry Andric
5450c0981da4SDimitry Andric ~BreakpointTreeDelegate() override = default;
5451c0981da4SDimitry Andric
GetBreakpoint(const TreeItem & item)5452c0981da4SDimitry Andric BreakpointSP GetBreakpoint(const TreeItem &item) {
5453c0981da4SDimitry Andric TargetSP target = m_debugger.GetSelectedTarget();
5454c0981da4SDimitry Andric BreakpointList &breakpoints = target->GetBreakpointList(false);
5455c0981da4SDimitry Andric return breakpoints.GetBreakpointAtIndex(item.GetIdentifier());
5456c0981da4SDimitry Andric }
5457c0981da4SDimitry Andric
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5458c0981da4SDimitry Andric void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5459c0981da4SDimitry Andric BreakpointSP breakpoint = GetBreakpoint(item);
5460c0981da4SDimitry Andric StreamString stream;
5461c0981da4SDimitry Andric stream.Format("{0}: ", breakpoint->GetID());
5462c0981da4SDimitry Andric breakpoint->GetResolverDescription(&stream);
5463c0981da4SDimitry Andric breakpoint->GetFilterDescription(&stream);
5464c0981da4SDimitry Andric window.PutCStringTruncated(1, stream.GetString().str().c_str());
5465c0981da4SDimitry Andric }
5466c0981da4SDimitry Andric
TreeDelegateGenerateChildren(TreeItem & item)5467c0981da4SDimitry Andric void TreeDelegateGenerateChildren(TreeItem &item) override {
5468c0981da4SDimitry Andric BreakpointSP breakpoint = GetBreakpoint(item);
5469c0981da4SDimitry Andric
5470c0981da4SDimitry Andric if (!m_breakpoint_location_delegate_sp)
5471c0981da4SDimitry Andric m_breakpoint_location_delegate_sp =
5472c0981da4SDimitry Andric std::make_shared<BreakpointLocationTreeDelegate>(m_debugger);
5473c0981da4SDimitry Andric
5474b1c73532SDimitry Andric item.Resize(breakpoint->GetNumLocations(),
5475b1c73532SDimitry Andric *m_breakpoint_location_delegate_sp, true);
5476c0981da4SDimitry Andric for (size_t i = 0; i < breakpoint->GetNumLocations(); i++) {
5477c0981da4SDimitry Andric item[i].SetIdentifier(i);
5478c0981da4SDimitry Andric item[i].SetUserData(breakpoint.get());
5479c0981da4SDimitry Andric }
5480c0981da4SDimitry Andric }
5481c0981da4SDimitry Andric
TreeDelegateItemSelected(TreeItem & item)5482c0981da4SDimitry Andric bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5483c0981da4SDimitry Andric
5484c0981da4SDimitry Andric protected:
5485c0981da4SDimitry Andric Debugger &m_debugger;
5486c0981da4SDimitry Andric std::shared_ptr<BreakpointLocationTreeDelegate>
5487c0981da4SDimitry Andric m_breakpoint_location_delegate_sp;
5488c0981da4SDimitry Andric };
5489c0981da4SDimitry Andric
5490c0981da4SDimitry Andric class BreakpointsTreeDelegate : public TreeDelegate {
5491c0981da4SDimitry Andric public:
BreakpointsTreeDelegate(Debugger & debugger)5492c0981da4SDimitry Andric BreakpointsTreeDelegate(Debugger &debugger)
5493c0981da4SDimitry Andric : TreeDelegate(), m_debugger(debugger), m_breakpoint_delegate_sp() {}
5494c0981da4SDimitry Andric
5495c0981da4SDimitry Andric ~BreakpointsTreeDelegate() override = default;
5496c0981da4SDimitry Andric
TreeDelegateShouldDraw()5497c0981da4SDimitry Andric bool TreeDelegateShouldDraw() override {
5498c0981da4SDimitry Andric TargetSP target = m_debugger.GetSelectedTarget();
5499c0981da4SDimitry Andric if (!target)
5500c0981da4SDimitry Andric return false;
5501c0981da4SDimitry Andric
5502c0981da4SDimitry Andric return true;
5503c0981da4SDimitry Andric }
5504c0981da4SDimitry Andric
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5505c0981da4SDimitry Andric void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5506c0981da4SDimitry Andric window.PutCString("Breakpoints");
5507c0981da4SDimitry Andric }
5508c0981da4SDimitry Andric
TreeDelegateGenerateChildren(TreeItem & item)5509c0981da4SDimitry Andric void TreeDelegateGenerateChildren(TreeItem &item) override {
5510c0981da4SDimitry Andric TargetSP target = m_debugger.GetSelectedTarget();
5511c0981da4SDimitry Andric
5512c0981da4SDimitry Andric BreakpointList &breakpoints = target->GetBreakpointList(false);
5513c0981da4SDimitry Andric std::unique_lock<std::recursive_mutex> lock;
5514c0981da4SDimitry Andric breakpoints.GetListMutex(lock);
5515c0981da4SDimitry Andric
5516c0981da4SDimitry Andric if (!m_breakpoint_delegate_sp)
5517c0981da4SDimitry Andric m_breakpoint_delegate_sp =
5518c0981da4SDimitry Andric std::make_shared<BreakpointTreeDelegate>(m_debugger);
5519c0981da4SDimitry Andric
5520b1c73532SDimitry Andric item.Resize(breakpoints.GetSize(), *m_breakpoint_delegate_sp, true);
5521c0981da4SDimitry Andric for (size_t i = 0; i < breakpoints.GetSize(); i++) {
5522c0981da4SDimitry Andric item[i].SetIdentifier(i);
5523c0981da4SDimitry Andric }
5524c0981da4SDimitry Andric }
5525c0981da4SDimitry Andric
TreeDelegateItemSelected(TreeItem & item)5526c0981da4SDimitry Andric bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5527c0981da4SDimitry Andric
TreeDelegateExpandRootByDefault()5528c0981da4SDimitry Andric bool TreeDelegateExpandRootByDefault() override { return true; }
5529c0981da4SDimitry Andric
5530c0981da4SDimitry Andric protected:
5531c0981da4SDimitry Andric Debugger &m_debugger;
5532c0981da4SDimitry Andric std::shared_ptr<BreakpointTreeDelegate> m_breakpoint_delegate_sp;
5533c0981da4SDimitry Andric };
5534c0981da4SDimitry Andric
5535706b4fc4SDimitry Andric class ValueObjectListDelegate : public WindowDelegate {
5536706b4fc4SDimitry Andric public:
ValueObjectListDelegate()5537344a3780SDimitry Andric ValueObjectListDelegate() : m_rows() {}
5538706b4fc4SDimitry Andric
ValueObjectListDelegate(ValueObjectList & valobj_list)5539145449b1SDimitry Andric ValueObjectListDelegate(ValueObjectList &valobj_list) : m_rows() {
5540706b4fc4SDimitry Andric SetValues(valobj_list);
5541706b4fc4SDimitry Andric }
5542706b4fc4SDimitry Andric
5543706b4fc4SDimitry Andric ~ValueObjectListDelegate() override = default;
5544706b4fc4SDimitry Andric
SetValues(ValueObjectList & valobj_list)5545706b4fc4SDimitry Andric void SetValues(ValueObjectList &valobj_list) {
5546706b4fc4SDimitry Andric m_selected_row = nullptr;
5547706b4fc4SDimitry Andric m_selected_row_idx = 0;
5548706b4fc4SDimitry Andric m_first_visible_row = 0;
5549706b4fc4SDimitry Andric m_num_rows = 0;
5550706b4fc4SDimitry Andric m_rows.clear();
5551706b4fc4SDimitry Andric for (auto &valobj_sp : valobj_list.GetObjects())
5552706b4fc4SDimitry Andric m_rows.push_back(Row(valobj_sp, nullptr));
5553706b4fc4SDimitry Andric }
5554706b4fc4SDimitry Andric
WindowDelegateDraw(Window & window,bool force)5555706b4fc4SDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override {
5556706b4fc4SDimitry Andric m_num_rows = 0;
5557706b4fc4SDimitry Andric m_min_x = 2;
5558706b4fc4SDimitry Andric m_min_y = 1;
5559706b4fc4SDimitry Andric m_max_x = window.GetWidth() - 1;
5560706b4fc4SDimitry Andric m_max_y = window.GetHeight() - 1;
5561706b4fc4SDimitry Andric
5562706b4fc4SDimitry Andric window.Erase();
5563706b4fc4SDimitry Andric window.DrawTitleBox(window.GetName());
5564706b4fc4SDimitry Andric
5565706b4fc4SDimitry Andric const int num_visible_rows = NumVisibleRows();
5566706b4fc4SDimitry Andric const int num_rows = CalculateTotalNumberRows(m_rows);
5567706b4fc4SDimitry Andric
5568706b4fc4SDimitry Andric // If we unexpanded while having something selected our total number of
5569706b4fc4SDimitry Andric // rows is less than the num visible rows, then make sure we show all the
5570706b4fc4SDimitry Andric // rows by setting the first visible row accordingly.
5571706b4fc4SDimitry Andric if (m_first_visible_row > 0 && num_rows < num_visible_rows)
5572706b4fc4SDimitry Andric m_first_visible_row = 0;
5573706b4fc4SDimitry Andric
5574706b4fc4SDimitry Andric // Make sure the selected row is always visible
5575706b4fc4SDimitry Andric if (m_selected_row_idx < m_first_visible_row)
5576706b4fc4SDimitry Andric m_first_visible_row = m_selected_row_idx;
5577706b4fc4SDimitry Andric else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
5578706b4fc4SDimitry Andric m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
5579706b4fc4SDimitry Andric
5580706b4fc4SDimitry Andric DisplayRows(window, m_rows, g_options);
5581706b4fc4SDimitry Andric
5582706b4fc4SDimitry Andric // Get the selected row
5583706b4fc4SDimitry Andric m_selected_row = GetRowForRowIndex(m_selected_row_idx);
5584706b4fc4SDimitry Andric // Keep the cursor on the selected row so the highlight and the cursor are
5585706b4fc4SDimitry Andric // always on the same line
5586706b4fc4SDimitry Andric if (m_selected_row)
5587706b4fc4SDimitry Andric window.MoveCursor(m_selected_row->x, m_selected_row->y);
5588706b4fc4SDimitry Andric
5589706b4fc4SDimitry Andric return true; // Drawing handled
5590706b4fc4SDimitry Andric }
5591706b4fc4SDimitry Andric
WindowDelegateGetKeyHelp()5592706b4fc4SDimitry Andric KeyHelp *WindowDelegateGetKeyHelp() override {
5593706b4fc4SDimitry Andric static curses::KeyHelp g_source_view_key_help[] = {
5594706b4fc4SDimitry Andric {KEY_UP, "Select previous item"},
5595706b4fc4SDimitry Andric {KEY_DOWN, "Select next item"},
5596706b4fc4SDimitry Andric {KEY_RIGHT, "Expand selected item"},
5597706b4fc4SDimitry Andric {KEY_LEFT, "Unexpand selected item or select parent if not expanded"},
5598706b4fc4SDimitry Andric {KEY_PPAGE, "Page up"},
5599706b4fc4SDimitry Andric {KEY_NPAGE, "Page down"},
5600706b4fc4SDimitry Andric {'A', "Format as annotated address"},
5601706b4fc4SDimitry Andric {'b', "Format as binary"},
5602706b4fc4SDimitry Andric {'B', "Format as hex bytes with ASCII"},
5603706b4fc4SDimitry Andric {'c', "Format as character"},
5604706b4fc4SDimitry Andric {'d', "Format as a signed integer"},
5605706b4fc4SDimitry Andric {'D', "Format selected value using the default format for the type"},
5606706b4fc4SDimitry Andric {'f', "Format as float"},
5607706b4fc4SDimitry Andric {'h', "Show help dialog"},
5608706b4fc4SDimitry Andric {'i', "Format as instructions"},
5609706b4fc4SDimitry Andric {'o', "Format as octal"},
5610706b4fc4SDimitry Andric {'p', "Format as pointer"},
5611706b4fc4SDimitry Andric {'s', "Format as C string"},
5612706b4fc4SDimitry Andric {'t', "Toggle showing/hiding type names"},
5613706b4fc4SDimitry Andric {'u', "Format as an unsigned integer"},
5614706b4fc4SDimitry Andric {'x', "Format as hex"},
5615706b4fc4SDimitry Andric {'X', "Format as uppercase hex"},
5616706b4fc4SDimitry Andric {' ', "Toggle item expansion"},
5617706b4fc4SDimitry Andric {',', "Page up"},
5618706b4fc4SDimitry Andric {'.', "Page down"},
5619706b4fc4SDimitry Andric {'\0', nullptr}};
5620706b4fc4SDimitry Andric return g_source_view_key_help;
5621706b4fc4SDimitry Andric }
5622706b4fc4SDimitry Andric
WindowDelegateHandleChar(Window & window,int c)5623706b4fc4SDimitry Andric HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
5624706b4fc4SDimitry Andric switch (c) {
5625706b4fc4SDimitry Andric case 'x':
5626706b4fc4SDimitry Andric case 'X':
5627706b4fc4SDimitry Andric case 'o':
5628706b4fc4SDimitry Andric case 's':
5629706b4fc4SDimitry Andric case 'u':
5630706b4fc4SDimitry Andric case 'd':
5631706b4fc4SDimitry Andric case 'D':
5632706b4fc4SDimitry Andric case 'i':
5633706b4fc4SDimitry Andric case 'A':
5634706b4fc4SDimitry Andric case 'p':
5635706b4fc4SDimitry Andric case 'c':
5636706b4fc4SDimitry Andric case 'b':
5637706b4fc4SDimitry Andric case 'B':
5638706b4fc4SDimitry Andric case 'f':
5639706b4fc4SDimitry Andric // Change the format for the currently selected item
5640706b4fc4SDimitry Andric if (m_selected_row) {
5641706b4fc4SDimitry Andric auto valobj_sp = m_selected_row->value.GetSP();
5642706b4fc4SDimitry Andric if (valobj_sp)
5643706b4fc4SDimitry Andric valobj_sp->SetFormat(FormatForChar(c));
5644706b4fc4SDimitry Andric }
5645706b4fc4SDimitry Andric return eKeyHandled;
5646706b4fc4SDimitry Andric
5647706b4fc4SDimitry Andric case 't':
5648706b4fc4SDimitry Andric // Toggle showing type names
5649706b4fc4SDimitry Andric g_options.show_types = !g_options.show_types;
5650706b4fc4SDimitry Andric return eKeyHandled;
5651706b4fc4SDimitry Andric
5652706b4fc4SDimitry Andric case ',':
5653706b4fc4SDimitry Andric case KEY_PPAGE:
5654706b4fc4SDimitry Andric // Page up key
5655706b4fc4SDimitry Andric if (m_first_visible_row > 0) {
5656706b4fc4SDimitry Andric if (static_cast<int>(m_first_visible_row) > m_max_y)
5657706b4fc4SDimitry Andric m_first_visible_row -= m_max_y;
5658706b4fc4SDimitry Andric else
5659706b4fc4SDimitry Andric m_first_visible_row = 0;
5660706b4fc4SDimitry Andric m_selected_row_idx = m_first_visible_row;
5661706b4fc4SDimitry Andric }
5662706b4fc4SDimitry Andric return eKeyHandled;
5663706b4fc4SDimitry Andric
5664706b4fc4SDimitry Andric case '.':
5665706b4fc4SDimitry Andric case KEY_NPAGE:
5666706b4fc4SDimitry Andric // Page down key
5667706b4fc4SDimitry Andric if (m_num_rows > static_cast<size_t>(m_max_y)) {
5668706b4fc4SDimitry Andric if (m_first_visible_row + m_max_y < m_num_rows) {
5669706b4fc4SDimitry Andric m_first_visible_row += m_max_y;
5670706b4fc4SDimitry Andric m_selected_row_idx = m_first_visible_row;
5671706b4fc4SDimitry Andric }
5672706b4fc4SDimitry Andric }
5673706b4fc4SDimitry Andric return eKeyHandled;
5674706b4fc4SDimitry Andric
5675706b4fc4SDimitry Andric case KEY_UP:
5676706b4fc4SDimitry Andric if (m_selected_row_idx > 0)
5677706b4fc4SDimitry Andric --m_selected_row_idx;
5678706b4fc4SDimitry Andric return eKeyHandled;
5679706b4fc4SDimitry Andric
5680706b4fc4SDimitry Andric case KEY_DOWN:
5681706b4fc4SDimitry Andric if (m_selected_row_idx + 1 < m_num_rows)
5682706b4fc4SDimitry Andric ++m_selected_row_idx;
5683706b4fc4SDimitry Andric return eKeyHandled;
5684706b4fc4SDimitry Andric
5685706b4fc4SDimitry Andric case KEY_RIGHT:
5686706b4fc4SDimitry Andric if (m_selected_row) {
5687706b4fc4SDimitry Andric if (!m_selected_row->expanded)
5688706b4fc4SDimitry Andric m_selected_row->Expand();
5689706b4fc4SDimitry Andric }
5690706b4fc4SDimitry Andric return eKeyHandled;
5691706b4fc4SDimitry Andric
5692706b4fc4SDimitry Andric case KEY_LEFT:
5693706b4fc4SDimitry Andric if (m_selected_row) {
5694706b4fc4SDimitry Andric if (m_selected_row->expanded)
5695706b4fc4SDimitry Andric m_selected_row->Unexpand();
5696706b4fc4SDimitry Andric else if (m_selected_row->parent)
5697706b4fc4SDimitry Andric m_selected_row_idx = m_selected_row->parent->row_idx;
5698706b4fc4SDimitry Andric }
5699706b4fc4SDimitry Andric return eKeyHandled;
5700706b4fc4SDimitry Andric
5701706b4fc4SDimitry Andric case ' ':
5702706b4fc4SDimitry Andric // Toggle expansion state when SPACE is pressed
5703706b4fc4SDimitry Andric if (m_selected_row) {
5704706b4fc4SDimitry Andric if (m_selected_row->expanded)
5705706b4fc4SDimitry Andric m_selected_row->Unexpand();
5706706b4fc4SDimitry Andric else
5707706b4fc4SDimitry Andric m_selected_row->Expand();
5708706b4fc4SDimitry Andric }
5709706b4fc4SDimitry Andric return eKeyHandled;
5710706b4fc4SDimitry Andric
5711706b4fc4SDimitry Andric case 'h':
5712706b4fc4SDimitry Andric window.CreateHelpSubwindow();
5713706b4fc4SDimitry Andric return eKeyHandled;
5714706b4fc4SDimitry Andric
5715706b4fc4SDimitry Andric default:
5716706b4fc4SDimitry Andric break;
5717706b4fc4SDimitry Andric }
5718706b4fc4SDimitry Andric return eKeyNotHandled;
5719706b4fc4SDimitry Andric }
5720706b4fc4SDimitry Andric
5721706b4fc4SDimitry Andric protected:
5722706b4fc4SDimitry Andric std::vector<Row> m_rows;
5723344a3780SDimitry Andric Row *m_selected_row = nullptr;
5724344a3780SDimitry Andric uint32_t m_selected_row_idx = 0;
5725344a3780SDimitry Andric uint32_t m_first_visible_row = 0;
5726344a3780SDimitry Andric uint32_t m_num_rows = 0;
5727e3b55780SDimitry Andric int m_min_x = 0;
5728e3b55780SDimitry Andric int m_min_y = 0;
5729344a3780SDimitry Andric int m_max_x = 0;
5730344a3780SDimitry Andric int m_max_y = 0;
5731706b4fc4SDimitry Andric
FormatForChar(int c)5732706b4fc4SDimitry Andric static Format FormatForChar(int c) {
5733706b4fc4SDimitry Andric switch (c) {
5734706b4fc4SDimitry Andric case 'x':
5735706b4fc4SDimitry Andric return eFormatHex;
5736706b4fc4SDimitry Andric case 'X':
5737706b4fc4SDimitry Andric return eFormatHexUppercase;
5738706b4fc4SDimitry Andric case 'o':
5739706b4fc4SDimitry Andric return eFormatOctal;
5740706b4fc4SDimitry Andric case 's':
5741706b4fc4SDimitry Andric return eFormatCString;
5742706b4fc4SDimitry Andric case 'u':
5743706b4fc4SDimitry Andric return eFormatUnsigned;
5744706b4fc4SDimitry Andric case 'd':
5745706b4fc4SDimitry Andric return eFormatDecimal;
5746706b4fc4SDimitry Andric case 'D':
5747706b4fc4SDimitry Andric return eFormatDefault;
5748706b4fc4SDimitry Andric case 'i':
5749706b4fc4SDimitry Andric return eFormatInstruction;
5750706b4fc4SDimitry Andric case 'A':
5751706b4fc4SDimitry Andric return eFormatAddressInfo;
5752706b4fc4SDimitry Andric case 'p':
5753706b4fc4SDimitry Andric return eFormatPointer;
5754706b4fc4SDimitry Andric case 'c':
5755706b4fc4SDimitry Andric return eFormatChar;
5756706b4fc4SDimitry Andric case 'b':
5757706b4fc4SDimitry Andric return eFormatBinary;
5758706b4fc4SDimitry Andric case 'B':
5759706b4fc4SDimitry Andric return eFormatBytesWithASCII;
5760706b4fc4SDimitry Andric case 'f':
5761706b4fc4SDimitry Andric return eFormatFloat;
5762706b4fc4SDimitry Andric }
5763706b4fc4SDimitry Andric return eFormatDefault;
5764706b4fc4SDimitry Andric }
5765706b4fc4SDimitry Andric
DisplayRowObject(Window & window,Row & row,DisplayOptions & options,bool highlight,bool last_child)5766706b4fc4SDimitry Andric bool DisplayRowObject(Window &window, Row &row, DisplayOptions &options,
5767706b4fc4SDimitry Andric bool highlight, bool last_child) {
5768706b4fc4SDimitry Andric ValueObject *valobj = row.value.GetSP().get();
5769706b4fc4SDimitry Andric
5770706b4fc4SDimitry Andric if (valobj == nullptr)
5771706b4fc4SDimitry Andric return false;
5772706b4fc4SDimitry Andric
5773706b4fc4SDimitry Andric const char *type_name =
5774706b4fc4SDimitry Andric options.show_types ? valobj->GetTypeName().GetCString() : nullptr;
5775706b4fc4SDimitry Andric const char *name = valobj->GetName().GetCString();
5776706b4fc4SDimitry Andric const char *value = valobj->GetValueAsCString();
5777706b4fc4SDimitry Andric const char *summary = valobj->GetSummaryAsCString();
5778706b4fc4SDimitry Andric
5779706b4fc4SDimitry Andric window.MoveCursor(row.x, row.y);
5780706b4fc4SDimitry Andric
5781706b4fc4SDimitry Andric row.DrawTree(window);
5782706b4fc4SDimitry Andric
5783706b4fc4SDimitry Andric if (highlight)
5784706b4fc4SDimitry Andric window.AttributeOn(A_REVERSE);
5785706b4fc4SDimitry Andric
5786706b4fc4SDimitry Andric if (type_name && type_name[0])
5787b60736ecSDimitry Andric window.PrintfTruncated(1, "(%s) ", type_name);
5788706b4fc4SDimitry Andric
5789706b4fc4SDimitry Andric if (name && name[0])
5790b60736ecSDimitry Andric window.PutCStringTruncated(1, name);
5791706b4fc4SDimitry Andric
5792706b4fc4SDimitry Andric attr_t changd_attr = 0;
5793706b4fc4SDimitry Andric if (valobj->GetValueDidChange())
5794b60736ecSDimitry Andric changd_attr = COLOR_PAIR(RedOnBlack) | A_BOLD;
5795706b4fc4SDimitry Andric
5796706b4fc4SDimitry Andric if (value && value[0]) {
5797b60736ecSDimitry Andric window.PutCStringTruncated(1, " = ");
5798706b4fc4SDimitry Andric if (changd_attr)
5799706b4fc4SDimitry Andric window.AttributeOn(changd_attr);
5800b60736ecSDimitry Andric window.PutCStringTruncated(1, value);
5801706b4fc4SDimitry Andric if (changd_attr)
5802706b4fc4SDimitry Andric window.AttributeOff(changd_attr);
5803706b4fc4SDimitry Andric }
5804706b4fc4SDimitry Andric
5805706b4fc4SDimitry Andric if (summary && summary[0]) {
5806b60736ecSDimitry Andric window.PutCStringTruncated(1, " ");
5807706b4fc4SDimitry Andric if (changd_attr)
5808706b4fc4SDimitry Andric window.AttributeOn(changd_attr);
5809b60736ecSDimitry Andric window.PutCStringTruncated(1, summary);
5810706b4fc4SDimitry Andric if (changd_attr)
5811706b4fc4SDimitry Andric window.AttributeOff(changd_attr);
5812706b4fc4SDimitry Andric }
5813706b4fc4SDimitry Andric
5814706b4fc4SDimitry Andric if (highlight)
5815706b4fc4SDimitry Andric window.AttributeOff(A_REVERSE);
5816706b4fc4SDimitry Andric
5817706b4fc4SDimitry Andric return true;
5818706b4fc4SDimitry Andric }
5819706b4fc4SDimitry Andric
DisplayRows(Window & window,std::vector<Row> & rows,DisplayOptions & options)5820706b4fc4SDimitry Andric void DisplayRows(Window &window, std::vector<Row> &rows,
5821706b4fc4SDimitry Andric DisplayOptions &options) {
5822706b4fc4SDimitry Andric // > 0x25B7
5823706b4fc4SDimitry Andric // \/ 0x25BD
5824706b4fc4SDimitry Andric
5825706b4fc4SDimitry Andric bool window_is_active = window.IsActive();
5826706b4fc4SDimitry Andric for (auto &row : rows) {
5827706b4fc4SDimitry Andric const bool last_child = row.parent && &rows[rows.size() - 1] == &row;
5828706b4fc4SDimitry Andric // Save the row index in each Row structure
5829706b4fc4SDimitry Andric row.row_idx = m_num_rows;
5830706b4fc4SDimitry Andric if ((m_num_rows >= m_first_visible_row) &&
5831706b4fc4SDimitry Andric ((m_num_rows - m_first_visible_row) <
5832706b4fc4SDimitry Andric static_cast<size_t>(NumVisibleRows()))) {
5833706b4fc4SDimitry Andric row.x = m_min_x;
5834706b4fc4SDimitry Andric row.y = m_num_rows - m_first_visible_row + 1;
5835706b4fc4SDimitry Andric if (DisplayRowObject(window, row, options,
5836706b4fc4SDimitry Andric window_is_active &&
5837706b4fc4SDimitry Andric m_num_rows == m_selected_row_idx,
5838706b4fc4SDimitry Andric last_child)) {
5839706b4fc4SDimitry Andric ++m_num_rows;
5840706b4fc4SDimitry Andric } else {
5841706b4fc4SDimitry Andric row.x = 0;
5842706b4fc4SDimitry Andric row.y = 0;
5843706b4fc4SDimitry Andric }
5844706b4fc4SDimitry Andric } else {
5845706b4fc4SDimitry Andric row.x = 0;
5846706b4fc4SDimitry Andric row.y = 0;
5847706b4fc4SDimitry Andric ++m_num_rows;
5848706b4fc4SDimitry Andric }
5849706b4fc4SDimitry Andric
5850145449b1SDimitry Andric if (row.expanded) {
5851706b4fc4SDimitry Andric auto &children = row.GetChildren();
5852145449b1SDimitry Andric if (!children.empty()) {
5853706b4fc4SDimitry Andric DisplayRows(window, children, options);
5854706b4fc4SDimitry Andric }
5855706b4fc4SDimitry Andric }
5856706b4fc4SDimitry Andric }
5857145449b1SDimitry Andric }
5858706b4fc4SDimitry Andric
CalculateTotalNumberRows(std::vector<Row> & rows)5859706b4fc4SDimitry Andric int CalculateTotalNumberRows(std::vector<Row> &rows) {
5860706b4fc4SDimitry Andric int row_count = 0;
5861706b4fc4SDimitry Andric for (auto &row : rows) {
5862706b4fc4SDimitry Andric ++row_count;
5863706b4fc4SDimitry Andric if (row.expanded)
5864706b4fc4SDimitry Andric row_count += CalculateTotalNumberRows(row.GetChildren());
5865706b4fc4SDimitry Andric }
5866706b4fc4SDimitry Andric return row_count;
5867706b4fc4SDimitry Andric }
5868706b4fc4SDimitry Andric
GetRowForRowIndexImpl(std::vector<Row> & rows,size_t & row_index)5869706b4fc4SDimitry Andric static Row *GetRowForRowIndexImpl(std::vector<Row> &rows, size_t &row_index) {
5870706b4fc4SDimitry Andric for (auto &row : rows) {
5871706b4fc4SDimitry Andric if (row_index == 0)
5872706b4fc4SDimitry Andric return &row;
5873706b4fc4SDimitry Andric else {
5874706b4fc4SDimitry Andric --row_index;
5875145449b1SDimitry Andric if (row.expanded) {
5876706b4fc4SDimitry Andric auto &children = row.GetChildren();
5877145449b1SDimitry Andric if (!children.empty()) {
5878706b4fc4SDimitry Andric Row *result = GetRowForRowIndexImpl(children, row_index);
5879706b4fc4SDimitry Andric if (result)
5880706b4fc4SDimitry Andric return result;
5881706b4fc4SDimitry Andric }
5882706b4fc4SDimitry Andric }
5883706b4fc4SDimitry Andric }
5884145449b1SDimitry Andric }
5885706b4fc4SDimitry Andric return nullptr;
5886706b4fc4SDimitry Andric }
5887706b4fc4SDimitry Andric
GetRowForRowIndex(size_t row_index)5888706b4fc4SDimitry Andric Row *GetRowForRowIndex(size_t row_index) {
5889706b4fc4SDimitry Andric return GetRowForRowIndexImpl(m_rows, row_index);
5890706b4fc4SDimitry Andric }
5891706b4fc4SDimitry Andric
NumVisibleRows() const5892706b4fc4SDimitry Andric int NumVisibleRows() const { return m_max_y - m_min_y; }
5893706b4fc4SDimitry Andric
5894706b4fc4SDimitry Andric static DisplayOptions g_options;
5895706b4fc4SDimitry Andric };
5896706b4fc4SDimitry Andric
5897706b4fc4SDimitry Andric class FrameVariablesWindowDelegate : public ValueObjectListDelegate {
5898706b4fc4SDimitry Andric public:
FrameVariablesWindowDelegate(Debugger & debugger)5899706b4fc4SDimitry Andric FrameVariablesWindowDelegate(Debugger &debugger)
5900145449b1SDimitry Andric : ValueObjectListDelegate(), m_debugger(debugger) {}
5901706b4fc4SDimitry Andric
5902706b4fc4SDimitry Andric ~FrameVariablesWindowDelegate() override = default;
5903706b4fc4SDimitry Andric
WindowDelegateGetHelpText()5904706b4fc4SDimitry Andric const char *WindowDelegateGetHelpText() override {
5905706b4fc4SDimitry Andric return "Frame variable window keyboard shortcuts:";
5906706b4fc4SDimitry Andric }
5907706b4fc4SDimitry Andric
WindowDelegateDraw(Window & window,bool force)5908706b4fc4SDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override {
5909706b4fc4SDimitry Andric ExecutionContext exe_ctx(
5910706b4fc4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext());
5911706b4fc4SDimitry Andric Process *process = exe_ctx.GetProcessPtr();
5912706b4fc4SDimitry Andric Block *frame_block = nullptr;
5913706b4fc4SDimitry Andric StackFrame *frame = nullptr;
5914706b4fc4SDimitry Andric
5915706b4fc4SDimitry Andric if (process) {
5916706b4fc4SDimitry Andric StateType state = process->GetState();
5917706b4fc4SDimitry Andric if (StateIsStoppedState(state, true)) {
5918706b4fc4SDimitry Andric frame = exe_ctx.GetFramePtr();
5919706b4fc4SDimitry Andric if (frame)
5920706b4fc4SDimitry Andric frame_block = frame->GetFrameBlock();
5921706b4fc4SDimitry Andric } else if (StateIsRunningState(state)) {
5922706b4fc4SDimitry Andric return true; // Don't do any updating when we are running
5923706b4fc4SDimitry Andric }
5924706b4fc4SDimitry Andric }
5925706b4fc4SDimitry Andric
5926706b4fc4SDimitry Andric ValueObjectList local_values;
5927706b4fc4SDimitry Andric if (frame_block) {
5928706b4fc4SDimitry Andric // Only update the variables if they have changed
5929706b4fc4SDimitry Andric if (m_frame_block != frame_block) {
5930706b4fc4SDimitry Andric m_frame_block = frame_block;
5931706b4fc4SDimitry Andric
5932e3b55780SDimitry Andric VariableList *locals = frame->GetVariableList(true, nullptr);
5933706b4fc4SDimitry Andric if (locals) {
5934706b4fc4SDimitry Andric const DynamicValueType use_dynamic = eDynamicDontRunTarget;
5935706b4fc4SDimitry Andric for (const VariableSP &local_sp : *locals) {
5936706b4fc4SDimitry Andric ValueObjectSP value_sp =
5937706b4fc4SDimitry Andric frame->GetValueObjectForFrameVariable(local_sp, use_dynamic);
5938706b4fc4SDimitry Andric if (value_sp) {
5939706b4fc4SDimitry Andric ValueObjectSP synthetic_value_sp = value_sp->GetSyntheticValue();
5940706b4fc4SDimitry Andric if (synthetic_value_sp)
5941706b4fc4SDimitry Andric local_values.Append(synthetic_value_sp);
5942706b4fc4SDimitry Andric else
5943706b4fc4SDimitry Andric local_values.Append(value_sp);
5944706b4fc4SDimitry Andric }
5945706b4fc4SDimitry Andric }
5946706b4fc4SDimitry Andric // Update the values
5947706b4fc4SDimitry Andric SetValues(local_values);
5948706b4fc4SDimitry Andric }
5949706b4fc4SDimitry Andric }
5950706b4fc4SDimitry Andric } else {
5951706b4fc4SDimitry Andric m_frame_block = nullptr;
5952706b4fc4SDimitry Andric // Update the values with an empty list if there is no frame
5953706b4fc4SDimitry Andric SetValues(local_values);
5954706b4fc4SDimitry Andric }
5955706b4fc4SDimitry Andric
5956706b4fc4SDimitry Andric return ValueObjectListDelegate::WindowDelegateDraw(window, force);
5957706b4fc4SDimitry Andric }
5958706b4fc4SDimitry Andric
5959706b4fc4SDimitry Andric protected:
5960706b4fc4SDimitry Andric Debugger &m_debugger;
5961145449b1SDimitry Andric Block *m_frame_block = nullptr;
5962706b4fc4SDimitry Andric };
5963706b4fc4SDimitry Andric
5964706b4fc4SDimitry Andric class RegistersWindowDelegate : public ValueObjectListDelegate {
5965706b4fc4SDimitry Andric public:
RegistersWindowDelegate(Debugger & debugger)5966706b4fc4SDimitry Andric RegistersWindowDelegate(Debugger &debugger)
5967706b4fc4SDimitry Andric : ValueObjectListDelegate(), m_debugger(debugger) {}
5968706b4fc4SDimitry Andric
5969706b4fc4SDimitry Andric ~RegistersWindowDelegate() override = default;
5970706b4fc4SDimitry Andric
WindowDelegateGetHelpText()5971706b4fc4SDimitry Andric const char *WindowDelegateGetHelpText() override {
5972706b4fc4SDimitry Andric return "Register window keyboard shortcuts:";
5973706b4fc4SDimitry Andric }
5974706b4fc4SDimitry Andric
WindowDelegateDraw(Window & window,bool force)5975706b4fc4SDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override {
5976706b4fc4SDimitry Andric ExecutionContext exe_ctx(
5977706b4fc4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext());
5978706b4fc4SDimitry Andric StackFrame *frame = exe_ctx.GetFramePtr();
5979706b4fc4SDimitry Andric
5980706b4fc4SDimitry Andric ValueObjectList value_list;
5981706b4fc4SDimitry Andric if (frame) {
5982706b4fc4SDimitry Andric if (frame->GetStackID() != m_stack_id) {
5983706b4fc4SDimitry Andric m_stack_id = frame->GetStackID();
5984706b4fc4SDimitry Andric RegisterContextSP reg_ctx(frame->GetRegisterContext());
5985706b4fc4SDimitry Andric if (reg_ctx) {
5986706b4fc4SDimitry Andric const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
5987706b4fc4SDimitry Andric for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) {
5988706b4fc4SDimitry Andric value_list.Append(
5989706b4fc4SDimitry Andric ValueObjectRegisterSet::Create(frame, reg_ctx, set_idx));
5990706b4fc4SDimitry Andric }
5991706b4fc4SDimitry Andric }
5992706b4fc4SDimitry Andric SetValues(value_list);
5993706b4fc4SDimitry Andric }
5994706b4fc4SDimitry Andric } else {
5995706b4fc4SDimitry Andric Process *process = exe_ctx.GetProcessPtr();
5996706b4fc4SDimitry Andric if (process && process->IsAlive())
5997706b4fc4SDimitry Andric return true; // Don't do any updating if we are running
5998706b4fc4SDimitry Andric else {
5999706b4fc4SDimitry Andric // Update the values with an empty list if there is no process or the
6000706b4fc4SDimitry Andric // process isn't alive anymore
6001706b4fc4SDimitry Andric SetValues(value_list);
6002706b4fc4SDimitry Andric }
6003706b4fc4SDimitry Andric }
6004706b4fc4SDimitry Andric return ValueObjectListDelegate::WindowDelegateDraw(window, force);
6005706b4fc4SDimitry Andric }
6006706b4fc4SDimitry Andric
6007706b4fc4SDimitry Andric protected:
6008706b4fc4SDimitry Andric Debugger &m_debugger;
6009706b4fc4SDimitry Andric StackID m_stack_id;
6010706b4fc4SDimitry Andric };
6011706b4fc4SDimitry Andric
CursesKeyToCString(int ch)6012706b4fc4SDimitry Andric static const char *CursesKeyToCString(int ch) {
6013706b4fc4SDimitry Andric static char g_desc[32];
6014706b4fc4SDimitry Andric if (ch >= KEY_F0 && ch < KEY_F0 + 64) {
6015706b4fc4SDimitry Andric snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0);
6016706b4fc4SDimitry Andric return g_desc;
6017706b4fc4SDimitry Andric }
6018706b4fc4SDimitry Andric switch (ch) {
6019706b4fc4SDimitry Andric case KEY_DOWN:
6020706b4fc4SDimitry Andric return "down";
6021706b4fc4SDimitry Andric case KEY_UP:
6022706b4fc4SDimitry Andric return "up";
6023706b4fc4SDimitry Andric case KEY_LEFT:
6024706b4fc4SDimitry Andric return "left";
6025706b4fc4SDimitry Andric case KEY_RIGHT:
6026706b4fc4SDimitry Andric return "right";
6027706b4fc4SDimitry Andric case KEY_HOME:
6028706b4fc4SDimitry Andric return "home";
6029706b4fc4SDimitry Andric case KEY_BACKSPACE:
6030706b4fc4SDimitry Andric return "backspace";
6031706b4fc4SDimitry Andric case KEY_DL:
6032706b4fc4SDimitry Andric return "delete-line";
6033706b4fc4SDimitry Andric case KEY_IL:
6034706b4fc4SDimitry Andric return "insert-line";
6035706b4fc4SDimitry Andric case KEY_DC:
6036706b4fc4SDimitry Andric return "delete-char";
6037706b4fc4SDimitry Andric case KEY_IC:
6038706b4fc4SDimitry Andric return "insert-char";
6039706b4fc4SDimitry Andric case KEY_CLEAR:
6040706b4fc4SDimitry Andric return "clear";
6041706b4fc4SDimitry Andric case KEY_EOS:
6042706b4fc4SDimitry Andric return "clear-to-eos";
6043706b4fc4SDimitry Andric case KEY_EOL:
6044706b4fc4SDimitry Andric return "clear-to-eol";
6045706b4fc4SDimitry Andric case KEY_SF:
6046706b4fc4SDimitry Andric return "scroll-forward";
6047706b4fc4SDimitry Andric case KEY_SR:
6048706b4fc4SDimitry Andric return "scroll-backward";
6049706b4fc4SDimitry Andric case KEY_NPAGE:
6050706b4fc4SDimitry Andric return "page-down";
6051706b4fc4SDimitry Andric case KEY_PPAGE:
6052706b4fc4SDimitry Andric return "page-up";
6053706b4fc4SDimitry Andric case KEY_STAB:
6054706b4fc4SDimitry Andric return "set-tab";
6055706b4fc4SDimitry Andric case KEY_CTAB:
6056706b4fc4SDimitry Andric return "clear-tab";
6057706b4fc4SDimitry Andric case KEY_CATAB:
6058706b4fc4SDimitry Andric return "clear-all-tabs";
6059706b4fc4SDimitry Andric case KEY_ENTER:
6060706b4fc4SDimitry Andric return "enter";
6061706b4fc4SDimitry Andric case KEY_PRINT:
6062706b4fc4SDimitry Andric return "print";
6063706b4fc4SDimitry Andric case KEY_LL:
6064706b4fc4SDimitry Andric return "lower-left key";
6065706b4fc4SDimitry Andric case KEY_A1:
6066706b4fc4SDimitry Andric return "upper left of keypad";
6067706b4fc4SDimitry Andric case KEY_A3:
6068706b4fc4SDimitry Andric return "upper right of keypad";
6069706b4fc4SDimitry Andric case KEY_B2:
6070706b4fc4SDimitry Andric return "center of keypad";
6071706b4fc4SDimitry Andric case KEY_C1:
6072706b4fc4SDimitry Andric return "lower left of keypad";
6073706b4fc4SDimitry Andric case KEY_C3:
6074706b4fc4SDimitry Andric return "lower right of keypad";
6075706b4fc4SDimitry Andric case KEY_BTAB:
6076706b4fc4SDimitry Andric return "back-tab key";
6077706b4fc4SDimitry Andric case KEY_BEG:
6078706b4fc4SDimitry Andric return "begin key";
6079706b4fc4SDimitry Andric case KEY_CANCEL:
6080706b4fc4SDimitry Andric return "cancel key";
6081706b4fc4SDimitry Andric case KEY_CLOSE:
6082706b4fc4SDimitry Andric return "close key";
6083706b4fc4SDimitry Andric case KEY_COMMAND:
6084706b4fc4SDimitry Andric return "command key";
6085706b4fc4SDimitry Andric case KEY_COPY:
6086706b4fc4SDimitry Andric return "copy key";
6087706b4fc4SDimitry Andric case KEY_CREATE:
6088706b4fc4SDimitry Andric return "create key";
6089706b4fc4SDimitry Andric case KEY_END:
6090706b4fc4SDimitry Andric return "end key";
6091706b4fc4SDimitry Andric case KEY_EXIT:
6092706b4fc4SDimitry Andric return "exit key";
6093706b4fc4SDimitry Andric case KEY_FIND:
6094706b4fc4SDimitry Andric return "find key";
6095706b4fc4SDimitry Andric case KEY_HELP:
6096706b4fc4SDimitry Andric return "help key";
6097706b4fc4SDimitry Andric case KEY_MARK:
6098706b4fc4SDimitry Andric return "mark key";
6099706b4fc4SDimitry Andric case KEY_MESSAGE:
6100706b4fc4SDimitry Andric return "message key";
6101706b4fc4SDimitry Andric case KEY_MOVE:
6102706b4fc4SDimitry Andric return "move key";
6103706b4fc4SDimitry Andric case KEY_NEXT:
6104706b4fc4SDimitry Andric return "next key";
6105706b4fc4SDimitry Andric case KEY_OPEN:
6106706b4fc4SDimitry Andric return "open key";
6107706b4fc4SDimitry Andric case KEY_OPTIONS:
6108706b4fc4SDimitry Andric return "options key";
6109706b4fc4SDimitry Andric case KEY_PREVIOUS:
6110706b4fc4SDimitry Andric return "previous key";
6111706b4fc4SDimitry Andric case KEY_REDO:
6112706b4fc4SDimitry Andric return "redo key";
6113706b4fc4SDimitry Andric case KEY_REFERENCE:
6114706b4fc4SDimitry Andric return "reference key";
6115706b4fc4SDimitry Andric case KEY_REFRESH:
6116706b4fc4SDimitry Andric return "refresh key";
6117706b4fc4SDimitry Andric case KEY_REPLACE:
6118706b4fc4SDimitry Andric return "replace key";
6119706b4fc4SDimitry Andric case KEY_RESTART:
6120706b4fc4SDimitry Andric return "restart key";
6121706b4fc4SDimitry Andric case KEY_RESUME:
6122706b4fc4SDimitry Andric return "resume key";
6123706b4fc4SDimitry Andric case KEY_SAVE:
6124706b4fc4SDimitry Andric return "save key";
6125706b4fc4SDimitry Andric case KEY_SBEG:
6126706b4fc4SDimitry Andric return "shifted begin key";
6127706b4fc4SDimitry Andric case KEY_SCANCEL:
6128706b4fc4SDimitry Andric return "shifted cancel key";
6129706b4fc4SDimitry Andric case KEY_SCOMMAND:
6130706b4fc4SDimitry Andric return "shifted command key";
6131706b4fc4SDimitry Andric case KEY_SCOPY:
6132706b4fc4SDimitry Andric return "shifted copy key";
6133706b4fc4SDimitry Andric case KEY_SCREATE:
6134706b4fc4SDimitry Andric return "shifted create key";
6135706b4fc4SDimitry Andric case KEY_SDC:
6136706b4fc4SDimitry Andric return "shifted delete-character key";
6137706b4fc4SDimitry Andric case KEY_SDL:
6138706b4fc4SDimitry Andric return "shifted delete-line key";
6139706b4fc4SDimitry Andric case KEY_SELECT:
6140706b4fc4SDimitry Andric return "select key";
6141706b4fc4SDimitry Andric case KEY_SEND:
6142706b4fc4SDimitry Andric return "shifted end key";
6143706b4fc4SDimitry Andric case KEY_SEOL:
6144706b4fc4SDimitry Andric return "shifted clear-to-end-of-line key";
6145706b4fc4SDimitry Andric case KEY_SEXIT:
6146706b4fc4SDimitry Andric return "shifted exit key";
6147706b4fc4SDimitry Andric case KEY_SFIND:
6148706b4fc4SDimitry Andric return "shifted find key";
6149706b4fc4SDimitry Andric case KEY_SHELP:
6150706b4fc4SDimitry Andric return "shifted help key";
6151706b4fc4SDimitry Andric case KEY_SHOME:
6152706b4fc4SDimitry Andric return "shifted home key";
6153706b4fc4SDimitry Andric case KEY_SIC:
6154706b4fc4SDimitry Andric return "shifted insert-character key";
6155706b4fc4SDimitry Andric case KEY_SLEFT:
6156706b4fc4SDimitry Andric return "shifted left-arrow key";
6157706b4fc4SDimitry Andric case KEY_SMESSAGE:
6158706b4fc4SDimitry Andric return "shifted message key";
6159706b4fc4SDimitry Andric case KEY_SMOVE:
6160706b4fc4SDimitry Andric return "shifted move key";
6161706b4fc4SDimitry Andric case KEY_SNEXT:
6162706b4fc4SDimitry Andric return "shifted next key";
6163706b4fc4SDimitry Andric case KEY_SOPTIONS:
6164706b4fc4SDimitry Andric return "shifted options key";
6165706b4fc4SDimitry Andric case KEY_SPREVIOUS:
6166706b4fc4SDimitry Andric return "shifted previous key";
6167706b4fc4SDimitry Andric case KEY_SPRINT:
6168706b4fc4SDimitry Andric return "shifted print key";
6169706b4fc4SDimitry Andric case KEY_SREDO:
6170706b4fc4SDimitry Andric return "shifted redo key";
6171706b4fc4SDimitry Andric case KEY_SREPLACE:
6172706b4fc4SDimitry Andric return "shifted replace key";
6173706b4fc4SDimitry Andric case KEY_SRIGHT:
6174706b4fc4SDimitry Andric return "shifted right-arrow key";
6175706b4fc4SDimitry Andric case KEY_SRSUME:
6176706b4fc4SDimitry Andric return "shifted resume key";
6177706b4fc4SDimitry Andric case KEY_SSAVE:
6178706b4fc4SDimitry Andric return "shifted save key";
6179706b4fc4SDimitry Andric case KEY_SSUSPEND:
6180706b4fc4SDimitry Andric return "shifted suspend key";
6181706b4fc4SDimitry Andric case KEY_SUNDO:
6182706b4fc4SDimitry Andric return "shifted undo key";
6183706b4fc4SDimitry Andric case KEY_SUSPEND:
6184706b4fc4SDimitry Andric return "suspend key";
6185706b4fc4SDimitry Andric case KEY_UNDO:
6186706b4fc4SDimitry Andric return "undo key";
6187706b4fc4SDimitry Andric case KEY_MOUSE:
6188706b4fc4SDimitry Andric return "Mouse event has occurred";
6189706b4fc4SDimitry Andric case KEY_RESIZE:
6190706b4fc4SDimitry Andric return "Terminal resize event";
6191706b4fc4SDimitry Andric #ifdef KEY_EVENT
6192706b4fc4SDimitry Andric case KEY_EVENT:
6193706b4fc4SDimitry Andric return "We were interrupted by an event";
6194706b4fc4SDimitry Andric #endif
6195706b4fc4SDimitry Andric case KEY_RETURN:
6196706b4fc4SDimitry Andric return "return";
6197706b4fc4SDimitry Andric case ' ':
6198706b4fc4SDimitry Andric return "space";
6199706b4fc4SDimitry Andric case '\t':
6200706b4fc4SDimitry Andric return "tab";
6201706b4fc4SDimitry Andric case KEY_ESCAPE:
6202706b4fc4SDimitry Andric return "escape";
6203706b4fc4SDimitry Andric default:
6204cfca06d7SDimitry Andric if (llvm::isPrint(ch))
6205706b4fc4SDimitry Andric snprintf(g_desc, sizeof(g_desc), "%c", ch);
6206706b4fc4SDimitry Andric else
6207706b4fc4SDimitry Andric snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch);
6208706b4fc4SDimitry Andric return g_desc;
6209706b4fc4SDimitry Andric }
6210706b4fc4SDimitry Andric return nullptr;
6211706b4fc4SDimitry Andric }
6212706b4fc4SDimitry Andric
HelpDialogDelegate(const char * text,KeyHelp * key_help_array)6213706b4fc4SDimitry Andric HelpDialogDelegate::HelpDialogDelegate(const char *text,
6214706b4fc4SDimitry Andric KeyHelp *key_help_array)
6215145449b1SDimitry Andric : m_text() {
6216706b4fc4SDimitry Andric if (text && text[0]) {
6217706b4fc4SDimitry Andric m_text.SplitIntoLines(text);
6218706b4fc4SDimitry Andric m_text.AppendString("");
6219706b4fc4SDimitry Andric }
6220706b4fc4SDimitry Andric if (key_help_array) {
6221706b4fc4SDimitry Andric for (KeyHelp *key = key_help_array; key->ch; ++key) {
6222706b4fc4SDimitry Andric StreamString key_description;
6223706b4fc4SDimitry Andric key_description.Printf("%10s - %s", CursesKeyToCString(key->ch),
6224706b4fc4SDimitry Andric key->description);
6225706b4fc4SDimitry Andric m_text.AppendString(key_description.GetString());
6226706b4fc4SDimitry Andric }
6227706b4fc4SDimitry Andric }
6228706b4fc4SDimitry Andric }
6229706b4fc4SDimitry Andric
6230706b4fc4SDimitry Andric HelpDialogDelegate::~HelpDialogDelegate() = default;
6231706b4fc4SDimitry Andric
WindowDelegateDraw(Window & window,bool force)6232706b4fc4SDimitry Andric bool HelpDialogDelegate::WindowDelegateDraw(Window &window, bool force) {
6233706b4fc4SDimitry Andric window.Erase();
6234706b4fc4SDimitry Andric const int window_height = window.GetHeight();
6235706b4fc4SDimitry Andric int x = 2;
6236706b4fc4SDimitry Andric int y = 1;
6237706b4fc4SDimitry Andric const int min_y = y;
6238706b4fc4SDimitry Andric const int max_y = window_height - 1 - y;
6239706b4fc4SDimitry Andric const size_t num_visible_lines = max_y - min_y + 1;
6240706b4fc4SDimitry Andric const size_t num_lines = m_text.GetSize();
6241706b4fc4SDimitry Andric const char *bottom_message;
6242706b4fc4SDimitry Andric if (num_lines <= num_visible_lines)
6243706b4fc4SDimitry Andric bottom_message = "Press any key to exit";
6244706b4fc4SDimitry Andric else
6245706b4fc4SDimitry Andric bottom_message = "Use arrows to scroll, any other key to exit";
6246706b4fc4SDimitry Andric window.DrawTitleBox(window.GetName(), bottom_message);
6247706b4fc4SDimitry Andric while (y <= max_y) {
6248706b4fc4SDimitry Andric window.MoveCursor(x, y);
6249706b4fc4SDimitry Andric window.PutCStringTruncated(
6250b60736ecSDimitry Andric 1, m_text.GetStringAtIndex(m_first_visible_line + y - min_y));
6251706b4fc4SDimitry Andric ++y;
6252706b4fc4SDimitry Andric }
6253706b4fc4SDimitry Andric return true;
6254706b4fc4SDimitry Andric }
6255706b4fc4SDimitry Andric
WindowDelegateHandleChar(Window & window,int key)6256706b4fc4SDimitry Andric HandleCharResult HelpDialogDelegate::WindowDelegateHandleChar(Window &window,
6257706b4fc4SDimitry Andric int key) {
6258706b4fc4SDimitry Andric bool done = false;
6259706b4fc4SDimitry Andric const size_t num_lines = m_text.GetSize();
6260706b4fc4SDimitry Andric const size_t num_visible_lines = window.GetHeight() - 2;
6261706b4fc4SDimitry Andric
6262706b4fc4SDimitry Andric if (num_lines <= num_visible_lines) {
6263706b4fc4SDimitry Andric done = true;
6264706b4fc4SDimitry Andric // If we have all lines visible and don't need scrolling, then any key
6265706b4fc4SDimitry Andric // press will cause us to exit
6266706b4fc4SDimitry Andric } else {
6267706b4fc4SDimitry Andric switch (key) {
6268706b4fc4SDimitry Andric case KEY_UP:
6269706b4fc4SDimitry Andric if (m_first_visible_line > 0)
6270706b4fc4SDimitry Andric --m_first_visible_line;
6271706b4fc4SDimitry Andric break;
6272706b4fc4SDimitry Andric
6273706b4fc4SDimitry Andric case KEY_DOWN:
6274706b4fc4SDimitry Andric if (m_first_visible_line + num_visible_lines < num_lines)
6275706b4fc4SDimitry Andric ++m_first_visible_line;
6276706b4fc4SDimitry Andric break;
6277706b4fc4SDimitry Andric
6278706b4fc4SDimitry Andric case KEY_PPAGE:
6279706b4fc4SDimitry Andric case ',':
6280706b4fc4SDimitry Andric if (m_first_visible_line > 0) {
6281706b4fc4SDimitry Andric if (static_cast<size_t>(m_first_visible_line) >= num_visible_lines)
6282706b4fc4SDimitry Andric m_first_visible_line -= num_visible_lines;
6283706b4fc4SDimitry Andric else
6284706b4fc4SDimitry Andric m_first_visible_line = 0;
6285706b4fc4SDimitry Andric }
6286706b4fc4SDimitry Andric break;
6287706b4fc4SDimitry Andric
6288706b4fc4SDimitry Andric case KEY_NPAGE:
6289706b4fc4SDimitry Andric case '.':
6290706b4fc4SDimitry Andric if (m_first_visible_line + num_visible_lines < num_lines) {
6291706b4fc4SDimitry Andric m_first_visible_line += num_visible_lines;
6292706b4fc4SDimitry Andric if (static_cast<size_t>(m_first_visible_line) > num_lines)
6293706b4fc4SDimitry Andric m_first_visible_line = num_lines - num_visible_lines;
6294706b4fc4SDimitry Andric }
6295706b4fc4SDimitry Andric break;
6296706b4fc4SDimitry Andric
6297706b4fc4SDimitry Andric default:
6298706b4fc4SDimitry Andric done = true;
6299706b4fc4SDimitry Andric break;
6300706b4fc4SDimitry Andric }
6301706b4fc4SDimitry Andric }
6302706b4fc4SDimitry Andric if (done)
6303706b4fc4SDimitry Andric window.GetParent()->RemoveSubWindow(&window);
6304706b4fc4SDimitry Andric return eKeyHandled;
6305706b4fc4SDimitry Andric }
6306706b4fc4SDimitry Andric
6307706b4fc4SDimitry Andric class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
6308706b4fc4SDimitry Andric public:
6309706b4fc4SDimitry Andric enum {
6310706b4fc4SDimitry Andric eMenuID_LLDB = 1,
6311706b4fc4SDimitry Andric eMenuID_LLDBAbout,
6312706b4fc4SDimitry Andric eMenuID_LLDBExit,
6313706b4fc4SDimitry Andric
6314706b4fc4SDimitry Andric eMenuID_Target,
6315706b4fc4SDimitry Andric eMenuID_TargetCreate,
6316706b4fc4SDimitry Andric eMenuID_TargetDelete,
6317706b4fc4SDimitry Andric
6318706b4fc4SDimitry Andric eMenuID_Process,
6319706b4fc4SDimitry Andric eMenuID_ProcessAttach,
6320b60736ecSDimitry Andric eMenuID_ProcessDetachResume,
6321b60736ecSDimitry Andric eMenuID_ProcessDetachSuspended,
6322706b4fc4SDimitry Andric eMenuID_ProcessLaunch,
6323706b4fc4SDimitry Andric eMenuID_ProcessContinue,
6324706b4fc4SDimitry Andric eMenuID_ProcessHalt,
6325706b4fc4SDimitry Andric eMenuID_ProcessKill,
6326706b4fc4SDimitry Andric
6327706b4fc4SDimitry Andric eMenuID_Thread,
6328706b4fc4SDimitry Andric eMenuID_ThreadStepIn,
6329706b4fc4SDimitry Andric eMenuID_ThreadStepOver,
6330706b4fc4SDimitry Andric eMenuID_ThreadStepOut,
6331706b4fc4SDimitry Andric
6332706b4fc4SDimitry Andric eMenuID_View,
6333706b4fc4SDimitry Andric eMenuID_ViewBacktrace,
6334706b4fc4SDimitry Andric eMenuID_ViewRegisters,
6335706b4fc4SDimitry Andric eMenuID_ViewSource,
6336706b4fc4SDimitry Andric eMenuID_ViewVariables,
6337c0981da4SDimitry Andric eMenuID_ViewBreakpoints,
6338706b4fc4SDimitry Andric
6339706b4fc4SDimitry Andric eMenuID_Help,
6340706b4fc4SDimitry Andric eMenuID_HelpGUIHelp
6341706b4fc4SDimitry Andric };
6342706b4fc4SDimitry Andric
ApplicationDelegate(Application & app,Debugger & debugger)6343706b4fc4SDimitry Andric ApplicationDelegate(Application &app, Debugger &debugger)
6344706b4fc4SDimitry Andric : WindowDelegate(), MenuDelegate(), m_app(app), m_debugger(debugger) {}
6345706b4fc4SDimitry Andric
6346706b4fc4SDimitry Andric ~ApplicationDelegate() override = default;
6347706b4fc4SDimitry Andric
WindowDelegateDraw(Window & window,bool force)6348706b4fc4SDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override {
6349706b4fc4SDimitry Andric return false; // Drawing not handled, let standard window drawing happen
6350706b4fc4SDimitry Andric }
6351706b4fc4SDimitry Andric
WindowDelegateHandleChar(Window & window,int key)6352706b4fc4SDimitry Andric HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
6353706b4fc4SDimitry Andric switch (key) {
6354706b4fc4SDimitry Andric case '\t':
6355706b4fc4SDimitry Andric window.SelectNextWindowAsActive();
6356706b4fc4SDimitry Andric return eKeyHandled;
6357706b4fc4SDimitry Andric
6358344a3780SDimitry Andric case KEY_SHIFT_TAB:
6359b60736ecSDimitry Andric window.SelectPreviousWindowAsActive();
6360b60736ecSDimitry Andric return eKeyHandled;
6361b60736ecSDimitry Andric
6362706b4fc4SDimitry Andric case 'h':
6363706b4fc4SDimitry Andric window.CreateHelpSubwindow();
6364706b4fc4SDimitry Andric return eKeyHandled;
6365706b4fc4SDimitry Andric
6366706b4fc4SDimitry Andric case KEY_ESCAPE:
6367706b4fc4SDimitry Andric return eQuitApplication;
6368706b4fc4SDimitry Andric
6369706b4fc4SDimitry Andric default:
6370706b4fc4SDimitry Andric break;
6371706b4fc4SDimitry Andric }
6372706b4fc4SDimitry Andric return eKeyNotHandled;
6373706b4fc4SDimitry Andric }
6374706b4fc4SDimitry Andric
WindowDelegateGetHelpText()6375706b4fc4SDimitry Andric const char *WindowDelegateGetHelpText() override {
6376706b4fc4SDimitry Andric return "Welcome to the LLDB curses GUI.\n\n"
6377706b4fc4SDimitry Andric "Press the TAB key to change the selected view.\n"
6378706b4fc4SDimitry Andric "Each view has its own keyboard shortcuts, press 'h' to open a "
6379706b4fc4SDimitry Andric "dialog to display them.\n\n"
6380706b4fc4SDimitry Andric "Common key bindings for all views:";
6381706b4fc4SDimitry Andric }
6382706b4fc4SDimitry Andric
WindowDelegateGetKeyHelp()6383706b4fc4SDimitry Andric KeyHelp *WindowDelegateGetKeyHelp() override {
6384706b4fc4SDimitry Andric static curses::KeyHelp g_source_view_key_help[] = {
6385706b4fc4SDimitry Andric {'\t', "Select next view"},
6386b60736ecSDimitry Andric {KEY_BTAB, "Select previous view"},
6387706b4fc4SDimitry Andric {'h', "Show help dialog with view specific key bindings"},
6388706b4fc4SDimitry Andric {',', "Page up"},
6389706b4fc4SDimitry Andric {'.', "Page down"},
6390706b4fc4SDimitry Andric {KEY_UP, "Select previous"},
6391706b4fc4SDimitry Andric {KEY_DOWN, "Select next"},
6392706b4fc4SDimitry Andric {KEY_LEFT, "Unexpand or select parent"},
6393706b4fc4SDimitry Andric {KEY_RIGHT, "Expand"},
6394706b4fc4SDimitry Andric {KEY_PPAGE, "Page up"},
6395706b4fc4SDimitry Andric {KEY_NPAGE, "Page down"},
6396706b4fc4SDimitry Andric {'\0', nullptr}};
6397706b4fc4SDimitry Andric return g_source_view_key_help;
6398706b4fc4SDimitry Andric }
6399706b4fc4SDimitry Andric
MenuDelegateAction(Menu & menu)6400706b4fc4SDimitry Andric MenuActionResult MenuDelegateAction(Menu &menu) override {
6401706b4fc4SDimitry Andric switch (menu.GetIdentifier()) {
6402c0981da4SDimitry Andric case eMenuID_TargetCreate: {
6403c0981da4SDimitry Andric WindowSP main_window_sp = m_app.GetMainWindow();
6404c0981da4SDimitry Andric FormDelegateSP form_delegate_sp =
6405c0981da4SDimitry Andric FormDelegateSP(new TargetCreateFormDelegate(m_debugger));
6406c0981da4SDimitry Andric Rect bounds = main_window_sp->GetCenteredRect(80, 19);
6407c0981da4SDimitry Andric WindowSP form_window_sp = main_window_sp->CreateSubWindow(
6408c0981da4SDimitry Andric form_delegate_sp->GetName().c_str(), bounds, true);
6409c0981da4SDimitry Andric WindowDelegateSP window_delegate_sp =
6410c0981da4SDimitry Andric WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
6411c0981da4SDimitry Andric form_window_sp->SetDelegate(window_delegate_sp);
6412c0981da4SDimitry Andric return MenuActionResult::Handled;
6413c0981da4SDimitry Andric }
6414706b4fc4SDimitry Andric case eMenuID_ThreadStepIn: {
6415706b4fc4SDimitry Andric ExecutionContext exe_ctx =
6416706b4fc4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
6417706b4fc4SDimitry Andric if (exe_ctx.HasThreadScope()) {
6418706b4fc4SDimitry Andric Process *process = exe_ctx.GetProcessPtr();
6419706b4fc4SDimitry Andric if (process && process->IsAlive() &&
6420706b4fc4SDimitry Andric StateIsStoppedState(process->GetState(), true))
6421706b4fc4SDimitry Andric exe_ctx.GetThreadRef().StepIn(true);
6422706b4fc4SDimitry Andric }
6423706b4fc4SDimitry Andric }
6424706b4fc4SDimitry Andric return MenuActionResult::Handled;
6425706b4fc4SDimitry Andric
6426706b4fc4SDimitry Andric case eMenuID_ThreadStepOut: {
6427706b4fc4SDimitry Andric ExecutionContext exe_ctx =
6428706b4fc4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
6429706b4fc4SDimitry Andric if (exe_ctx.HasThreadScope()) {
6430706b4fc4SDimitry Andric Process *process = exe_ctx.GetProcessPtr();
6431706b4fc4SDimitry Andric if (process && process->IsAlive() &&
6432145449b1SDimitry Andric StateIsStoppedState(process->GetState(), true)) {
6433145449b1SDimitry Andric Thread *thread = exe_ctx.GetThreadPtr();
64347fa27ce4SDimitry Andric uint32_t frame_idx =
64357fa27ce4SDimitry Andric thread->GetSelectedFrameIndex(SelectMostRelevantFrame);
6436145449b1SDimitry Andric exe_ctx.GetThreadRef().StepOut(frame_idx);
6437145449b1SDimitry Andric }
6438706b4fc4SDimitry Andric }
6439706b4fc4SDimitry Andric }
6440706b4fc4SDimitry Andric return MenuActionResult::Handled;
6441706b4fc4SDimitry Andric
6442706b4fc4SDimitry Andric case eMenuID_ThreadStepOver: {
6443706b4fc4SDimitry Andric ExecutionContext exe_ctx =
6444706b4fc4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
6445706b4fc4SDimitry Andric if (exe_ctx.HasThreadScope()) {
6446706b4fc4SDimitry Andric Process *process = exe_ctx.GetProcessPtr();
6447706b4fc4SDimitry Andric if (process && process->IsAlive() &&
6448706b4fc4SDimitry Andric StateIsStoppedState(process->GetState(), true))
6449706b4fc4SDimitry Andric exe_ctx.GetThreadRef().StepOver(true);
6450706b4fc4SDimitry Andric }
6451706b4fc4SDimitry Andric }
6452706b4fc4SDimitry Andric return MenuActionResult::Handled;
6453706b4fc4SDimitry Andric
6454344a3780SDimitry Andric case eMenuID_ProcessAttach: {
6455344a3780SDimitry Andric WindowSP main_window_sp = m_app.GetMainWindow();
6456344a3780SDimitry Andric FormDelegateSP form_delegate_sp = FormDelegateSP(
6457344a3780SDimitry Andric new ProcessAttachFormDelegate(m_debugger, main_window_sp));
6458344a3780SDimitry Andric Rect bounds = main_window_sp->GetCenteredRect(80, 22);
6459344a3780SDimitry Andric WindowSP form_window_sp = main_window_sp->CreateSubWindow(
6460344a3780SDimitry Andric form_delegate_sp->GetName().c_str(), bounds, true);
6461344a3780SDimitry Andric WindowDelegateSP window_delegate_sp =
6462344a3780SDimitry Andric WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
6463344a3780SDimitry Andric form_window_sp->SetDelegate(window_delegate_sp);
6464344a3780SDimitry Andric return MenuActionResult::Handled;
6465344a3780SDimitry Andric }
6466c0981da4SDimitry Andric case eMenuID_ProcessLaunch: {
6467c0981da4SDimitry Andric WindowSP main_window_sp = m_app.GetMainWindow();
6468c0981da4SDimitry Andric FormDelegateSP form_delegate_sp = FormDelegateSP(
6469c0981da4SDimitry Andric new ProcessLaunchFormDelegate(m_debugger, main_window_sp));
6470c0981da4SDimitry Andric Rect bounds = main_window_sp->GetCenteredRect(80, 22);
6471c0981da4SDimitry Andric WindowSP form_window_sp = main_window_sp->CreateSubWindow(
6472c0981da4SDimitry Andric form_delegate_sp->GetName().c_str(), bounds, true);
6473c0981da4SDimitry Andric WindowDelegateSP window_delegate_sp =
6474c0981da4SDimitry Andric WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
6475c0981da4SDimitry Andric form_window_sp->SetDelegate(window_delegate_sp);
6476c0981da4SDimitry Andric return MenuActionResult::Handled;
6477c0981da4SDimitry Andric }
6478344a3780SDimitry Andric
6479706b4fc4SDimitry Andric case eMenuID_ProcessContinue: {
6480706b4fc4SDimitry Andric ExecutionContext exe_ctx =
6481706b4fc4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
6482706b4fc4SDimitry Andric if (exe_ctx.HasProcessScope()) {
6483706b4fc4SDimitry Andric Process *process = exe_ctx.GetProcessPtr();
6484706b4fc4SDimitry Andric if (process && process->IsAlive() &&
6485706b4fc4SDimitry Andric StateIsStoppedState(process->GetState(), true))
6486706b4fc4SDimitry Andric process->Resume();
6487706b4fc4SDimitry Andric }
6488706b4fc4SDimitry Andric }
6489706b4fc4SDimitry Andric return MenuActionResult::Handled;
6490706b4fc4SDimitry Andric
6491706b4fc4SDimitry Andric case eMenuID_ProcessKill: {
6492706b4fc4SDimitry Andric ExecutionContext exe_ctx =
6493706b4fc4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
6494706b4fc4SDimitry Andric if (exe_ctx.HasProcessScope()) {
6495706b4fc4SDimitry Andric Process *process = exe_ctx.GetProcessPtr();
6496706b4fc4SDimitry Andric if (process && process->IsAlive())
6497706b4fc4SDimitry Andric process->Destroy(false);
6498706b4fc4SDimitry Andric }
6499706b4fc4SDimitry Andric }
6500706b4fc4SDimitry Andric return MenuActionResult::Handled;
6501706b4fc4SDimitry Andric
6502706b4fc4SDimitry Andric case eMenuID_ProcessHalt: {
6503706b4fc4SDimitry Andric ExecutionContext exe_ctx =
6504706b4fc4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
6505706b4fc4SDimitry Andric if (exe_ctx.HasProcessScope()) {
6506706b4fc4SDimitry Andric Process *process = exe_ctx.GetProcessPtr();
6507706b4fc4SDimitry Andric if (process && process->IsAlive())
6508706b4fc4SDimitry Andric process->Halt();
6509706b4fc4SDimitry Andric }
6510706b4fc4SDimitry Andric }
6511706b4fc4SDimitry Andric return MenuActionResult::Handled;
6512706b4fc4SDimitry Andric
6513b60736ecSDimitry Andric case eMenuID_ProcessDetachResume:
6514b60736ecSDimitry Andric case eMenuID_ProcessDetachSuspended: {
6515706b4fc4SDimitry Andric ExecutionContext exe_ctx =
6516706b4fc4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
6517706b4fc4SDimitry Andric if (exe_ctx.HasProcessScope()) {
6518706b4fc4SDimitry Andric Process *process = exe_ctx.GetProcessPtr();
6519706b4fc4SDimitry Andric if (process && process->IsAlive())
6520b60736ecSDimitry Andric process->Detach(menu.GetIdentifier() ==
6521b60736ecSDimitry Andric eMenuID_ProcessDetachSuspended);
6522706b4fc4SDimitry Andric }
6523706b4fc4SDimitry Andric }
6524706b4fc4SDimitry Andric return MenuActionResult::Handled;
6525706b4fc4SDimitry Andric
6526706b4fc4SDimitry Andric case eMenuID_Process: {
6527706b4fc4SDimitry Andric // Populate the menu with all of the threads if the process is stopped
6528706b4fc4SDimitry Andric // when the Process menu gets selected and is about to display its
6529706b4fc4SDimitry Andric // submenu.
6530706b4fc4SDimitry Andric Menus &submenus = menu.GetSubmenus();
6531706b4fc4SDimitry Andric ExecutionContext exe_ctx =
6532706b4fc4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
6533706b4fc4SDimitry Andric Process *process = exe_ctx.GetProcessPtr();
6534706b4fc4SDimitry Andric if (process && process->IsAlive() &&
6535706b4fc4SDimitry Andric StateIsStoppedState(process->GetState(), true)) {
6536706b4fc4SDimitry Andric if (submenus.size() == 7)
6537706b4fc4SDimitry Andric menu.AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
6538706b4fc4SDimitry Andric else if (submenus.size() > 8)
6539706b4fc4SDimitry Andric submenus.erase(submenus.begin() + 8, submenus.end());
6540706b4fc4SDimitry Andric
6541706b4fc4SDimitry Andric ThreadList &threads = process->GetThreadList();
6542706b4fc4SDimitry Andric std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
6543706b4fc4SDimitry Andric size_t num_threads = threads.GetSize();
6544706b4fc4SDimitry Andric for (size_t i = 0; i < num_threads; ++i) {
6545706b4fc4SDimitry Andric ThreadSP thread_sp = threads.GetThreadAtIndex(i);
6546706b4fc4SDimitry Andric char menu_char = '\0';
6547706b4fc4SDimitry Andric if (i < 9)
6548706b4fc4SDimitry Andric menu_char = '1' + i;
6549706b4fc4SDimitry Andric StreamString thread_menu_title;
6550706b4fc4SDimitry Andric thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID());
6551706b4fc4SDimitry Andric const char *thread_name = thread_sp->GetName();
6552706b4fc4SDimitry Andric if (thread_name && thread_name[0])
6553706b4fc4SDimitry Andric thread_menu_title.Printf(" %s", thread_name);
6554706b4fc4SDimitry Andric else {
6555706b4fc4SDimitry Andric const char *queue_name = thread_sp->GetQueueName();
6556706b4fc4SDimitry Andric if (queue_name && queue_name[0])
6557706b4fc4SDimitry Andric thread_menu_title.Printf(" %s", queue_name);
6558706b4fc4SDimitry Andric }
6559706b4fc4SDimitry Andric menu.AddSubmenu(
6560706b4fc4SDimitry Andric MenuSP(new Menu(thread_menu_title.GetString().str().c_str(),
6561706b4fc4SDimitry Andric nullptr, menu_char, thread_sp->GetID())));
6562706b4fc4SDimitry Andric }
6563706b4fc4SDimitry Andric } else if (submenus.size() > 7) {
6564706b4fc4SDimitry Andric // Remove the separator and any other thread submenu items that were
6565706b4fc4SDimitry Andric // previously added
6566706b4fc4SDimitry Andric submenus.erase(submenus.begin() + 7, submenus.end());
6567706b4fc4SDimitry Andric }
6568c0981da4SDimitry Andric // Since we are adding and removing items we need to recalculate the
6569c0981da4SDimitry Andric // name lengths
6570706b4fc4SDimitry Andric menu.RecalculateNameLengths();
6571706b4fc4SDimitry Andric }
6572706b4fc4SDimitry Andric return MenuActionResult::Handled;
6573706b4fc4SDimitry Andric
6574706b4fc4SDimitry Andric case eMenuID_ViewVariables: {
6575706b4fc4SDimitry Andric WindowSP main_window_sp = m_app.GetMainWindow();
6576706b4fc4SDimitry Andric WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
6577706b4fc4SDimitry Andric WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
6578706b4fc4SDimitry Andric WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
6579706b4fc4SDimitry Andric const Rect source_bounds = source_window_sp->GetBounds();
6580706b4fc4SDimitry Andric
6581706b4fc4SDimitry Andric if (variables_window_sp) {
6582706b4fc4SDimitry Andric const Rect variables_bounds = variables_window_sp->GetBounds();
6583706b4fc4SDimitry Andric
6584706b4fc4SDimitry Andric main_window_sp->RemoveSubWindow(variables_window_sp.get());
6585706b4fc4SDimitry Andric
6586706b4fc4SDimitry Andric if (registers_window_sp) {
6587706b4fc4SDimitry Andric // We have a registers window, so give all the area back to the
6588706b4fc4SDimitry Andric // registers window
6589706b4fc4SDimitry Andric Rect registers_bounds = variables_bounds;
6590706b4fc4SDimitry Andric registers_bounds.size.width = source_bounds.size.width;
6591706b4fc4SDimitry Andric registers_window_sp->SetBounds(registers_bounds);
6592706b4fc4SDimitry Andric } else {
6593706b4fc4SDimitry Andric // We have no registers window showing so give the bottom area back
6594706b4fc4SDimitry Andric // to the source view
6595706b4fc4SDimitry Andric source_window_sp->Resize(source_bounds.size.width,
6596706b4fc4SDimitry Andric source_bounds.size.height +
6597706b4fc4SDimitry Andric variables_bounds.size.height);
6598706b4fc4SDimitry Andric }
6599706b4fc4SDimitry Andric } else {
6600706b4fc4SDimitry Andric Rect new_variables_rect;
6601706b4fc4SDimitry Andric if (registers_window_sp) {
6602706b4fc4SDimitry Andric // We have a registers window so split the area of the registers
6603706b4fc4SDimitry Andric // window into two columns where the left hand side will be the
6604706b4fc4SDimitry Andric // variables and the right hand side will be the registers
6605706b4fc4SDimitry Andric const Rect variables_bounds = registers_window_sp->GetBounds();
6606706b4fc4SDimitry Andric Rect new_registers_rect;
6607706b4fc4SDimitry Andric variables_bounds.VerticalSplitPercentage(0.50, new_variables_rect,
6608706b4fc4SDimitry Andric new_registers_rect);
6609706b4fc4SDimitry Andric registers_window_sp->SetBounds(new_registers_rect);
6610706b4fc4SDimitry Andric } else {
6611b60736ecSDimitry Andric // No registers window, grab the bottom part of the source window
6612706b4fc4SDimitry Andric Rect new_source_rect;
6613706b4fc4SDimitry Andric source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
6614706b4fc4SDimitry Andric new_variables_rect);
6615706b4fc4SDimitry Andric source_window_sp->SetBounds(new_source_rect);
6616706b4fc4SDimitry Andric }
6617706b4fc4SDimitry Andric WindowSP new_window_sp = main_window_sp->CreateSubWindow(
6618706b4fc4SDimitry Andric "Variables", new_variables_rect, false);
6619706b4fc4SDimitry Andric new_window_sp->SetDelegate(
6620706b4fc4SDimitry Andric WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
6621706b4fc4SDimitry Andric }
6622706b4fc4SDimitry Andric touchwin(stdscr);
6623706b4fc4SDimitry Andric }
6624706b4fc4SDimitry Andric return MenuActionResult::Handled;
6625706b4fc4SDimitry Andric
6626706b4fc4SDimitry Andric case eMenuID_ViewRegisters: {
6627706b4fc4SDimitry Andric WindowSP main_window_sp = m_app.GetMainWindow();
6628706b4fc4SDimitry Andric WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
6629706b4fc4SDimitry Andric WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
6630706b4fc4SDimitry Andric WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
6631706b4fc4SDimitry Andric const Rect source_bounds = source_window_sp->GetBounds();
6632706b4fc4SDimitry Andric
6633706b4fc4SDimitry Andric if (registers_window_sp) {
6634706b4fc4SDimitry Andric if (variables_window_sp) {
6635706b4fc4SDimitry Andric const Rect variables_bounds = variables_window_sp->GetBounds();
6636706b4fc4SDimitry Andric
6637706b4fc4SDimitry Andric // We have a variables window, so give all the area back to the
6638706b4fc4SDimitry Andric // variables window
6639706b4fc4SDimitry Andric variables_window_sp->Resize(variables_bounds.size.width +
6640706b4fc4SDimitry Andric registers_window_sp->GetWidth(),
6641706b4fc4SDimitry Andric variables_bounds.size.height);
6642706b4fc4SDimitry Andric } else {
6643706b4fc4SDimitry Andric // We have no variables window showing so give the bottom area back
6644706b4fc4SDimitry Andric // to the source view
6645706b4fc4SDimitry Andric source_window_sp->Resize(source_bounds.size.width,
6646706b4fc4SDimitry Andric source_bounds.size.height +
6647706b4fc4SDimitry Andric registers_window_sp->GetHeight());
6648706b4fc4SDimitry Andric }
6649706b4fc4SDimitry Andric main_window_sp->RemoveSubWindow(registers_window_sp.get());
6650706b4fc4SDimitry Andric } else {
6651706b4fc4SDimitry Andric Rect new_regs_rect;
6652706b4fc4SDimitry Andric if (variables_window_sp) {
6653706b4fc4SDimitry Andric // We have a variables window, split it into two columns where the
6654706b4fc4SDimitry Andric // left hand side will be the variables and the right hand side will
6655706b4fc4SDimitry Andric // be the registers
6656706b4fc4SDimitry Andric const Rect variables_bounds = variables_window_sp->GetBounds();
6657706b4fc4SDimitry Andric Rect new_vars_rect;
6658706b4fc4SDimitry Andric variables_bounds.VerticalSplitPercentage(0.50, new_vars_rect,
6659706b4fc4SDimitry Andric new_regs_rect);
6660706b4fc4SDimitry Andric variables_window_sp->SetBounds(new_vars_rect);
6661706b4fc4SDimitry Andric } else {
6662b60736ecSDimitry Andric // No variables window, grab the bottom part of the source window
6663706b4fc4SDimitry Andric Rect new_source_rect;
6664706b4fc4SDimitry Andric source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
6665706b4fc4SDimitry Andric new_regs_rect);
6666706b4fc4SDimitry Andric source_window_sp->SetBounds(new_source_rect);
6667706b4fc4SDimitry Andric }
6668706b4fc4SDimitry Andric WindowSP new_window_sp =
6669706b4fc4SDimitry Andric main_window_sp->CreateSubWindow("Registers", new_regs_rect, false);
6670706b4fc4SDimitry Andric new_window_sp->SetDelegate(
6671706b4fc4SDimitry Andric WindowDelegateSP(new RegistersWindowDelegate(m_debugger)));
6672706b4fc4SDimitry Andric }
6673706b4fc4SDimitry Andric touchwin(stdscr);
6674706b4fc4SDimitry Andric }
6675706b4fc4SDimitry Andric return MenuActionResult::Handled;
6676706b4fc4SDimitry Andric
6677c0981da4SDimitry Andric case eMenuID_ViewBreakpoints: {
6678c0981da4SDimitry Andric WindowSP main_window_sp = m_app.GetMainWindow();
6679c0981da4SDimitry Andric WindowSP threads_window_sp = main_window_sp->FindSubWindow("Threads");
6680c0981da4SDimitry Andric WindowSP breakpoints_window_sp =
6681c0981da4SDimitry Andric main_window_sp->FindSubWindow("Breakpoints");
6682c0981da4SDimitry Andric const Rect threads_bounds = threads_window_sp->GetBounds();
6683c0981da4SDimitry Andric
6684c0981da4SDimitry Andric // If a breakpoints window already exists, remove it and give the area
6685c0981da4SDimitry Andric // it used to occupy to the threads window. If it doesn't exist, split
6686c0981da4SDimitry Andric // the threads window horizontally into two windows where the top window
6687c0981da4SDimitry Andric // is the threads window and the bottom window is a newly added
6688c0981da4SDimitry Andric // breakpoints window.
6689c0981da4SDimitry Andric if (breakpoints_window_sp) {
6690c0981da4SDimitry Andric threads_window_sp->Resize(threads_bounds.size.width,
6691c0981da4SDimitry Andric threads_bounds.size.height +
6692c0981da4SDimitry Andric breakpoints_window_sp->GetHeight());
6693c0981da4SDimitry Andric main_window_sp->RemoveSubWindow(breakpoints_window_sp.get());
6694c0981da4SDimitry Andric } else {
6695c0981da4SDimitry Andric Rect new_threads_bounds, breakpoints_bounds;
6696c0981da4SDimitry Andric threads_bounds.HorizontalSplitPercentage(0.70, new_threads_bounds,
6697c0981da4SDimitry Andric breakpoints_bounds);
6698c0981da4SDimitry Andric threads_window_sp->SetBounds(new_threads_bounds);
6699c0981da4SDimitry Andric breakpoints_window_sp = main_window_sp->CreateSubWindow(
6700c0981da4SDimitry Andric "Breakpoints", breakpoints_bounds, false);
6701c0981da4SDimitry Andric TreeDelegateSP breakpoints_delegate_sp(
6702c0981da4SDimitry Andric new BreakpointsTreeDelegate(m_debugger));
6703c0981da4SDimitry Andric breakpoints_window_sp->SetDelegate(WindowDelegateSP(
6704c0981da4SDimitry Andric new TreeWindowDelegate(m_debugger, breakpoints_delegate_sp)));
6705c0981da4SDimitry Andric }
6706c0981da4SDimitry Andric touchwin(stdscr);
6707c0981da4SDimitry Andric return MenuActionResult::Handled;
6708c0981da4SDimitry Andric }
6709c0981da4SDimitry Andric
6710706b4fc4SDimitry Andric case eMenuID_HelpGUIHelp:
6711706b4fc4SDimitry Andric m_app.GetMainWindow()->CreateHelpSubwindow();
6712706b4fc4SDimitry Andric return MenuActionResult::Handled;
6713706b4fc4SDimitry Andric
6714706b4fc4SDimitry Andric default:
6715706b4fc4SDimitry Andric break;
6716706b4fc4SDimitry Andric }
6717706b4fc4SDimitry Andric
6718706b4fc4SDimitry Andric return MenuActionResult::NotHandled;
6719706b4fc4SDimitry Andric }
6720706b4fc4SDimitry Andric
6721706b4fc4SDimitry Andric protected:
6722706b4fc4SDimitry Andric Application &m_app;
6723706b4fc4SDimitry Andric Debugger &m_debugger;
6724706b4fc4SDimitry Andric };
6725706b4fc4SDimitry Andric
6726706b4fc4SDimitry Andric class StatusBarWindowDelegate : public WindowDelegate {
6727706b4fc4SDimitry Andric public:
StatusBarWindowDelegate(Debugger & debugger)6728706b4fc4SDimitry Andric StatusBarWindowDelegate(Debugger &debugger) : m_debugger(debugger) {
6729706b4fc4SDimitry Andric FormatEntity::Parse("Thread: ${thread.id%tid}", m_format);
6730706b4fc4SDimitry Andric }
6731706b4fc4SDimitry Andric
6732706b4fc4SDimitry Andric ~StatusBarWindowDelegate() override = default;
6733706b4fc4SDimitry Andric
WindowDelegateDraw(Window & window,bool force)6734706b4fc4SDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override {
6735706b4fc4SDimitry Andric ExecutionContext exe_ctx =
6736706b4fc4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
6737706b4fc4SDimitry Andric Process *process = exe_ctx.GetProcessPtr();
6738706b4fc4SDimitry Andric Thread *thread = exe_ctx.GetThreadPtr();
6739706b4fc4SDimitry Andric StackFrame *frame = exe_ctx.GetFramePtr();
6740706b4fc4SDimitry Andric window.Erase();
6741b60736ecSDimitry Andric window.SetBackground(BlackOnWhite);
6742706b4fc4SDimitry Andric window.MoveCursor(0, 0);
6743706b4fc4SDimitry Andric if (process) {
6744706b4fc4SDimitry Andric const StateType state = process->GetState();
6745706b4fc4SDimitry Andric window.Printf("Process: %5" PRIu64 " %10s", process->GetID(),
6746706b4fc4SDimitry Andric StateAsCString(state));
6747706b4fc4SDimitry Andric
6748706b4fc4SDimitry Andric if (StateIsStoppedState(state, true)) {
6749706b4fc4SDimitry Andric StreamString strm;
6750706b4fc4SDimitry Andric if (thread && FormatEntity::Format(m_format, strm, nullptr, &exe_ctx,
6751706b4fc4SDimitry Andric nullptr, nullptr, false, false)) {
6752706b4fc4SDimitry Andric window.MoveCursor(40, 0);
6753b60736ecSDimitry Andric window.PutCStringTruncated(1, strm.GetString().str().c_str());
6754706b4fc4SDimitry Andric }
6755706b4fc4SDimitry Andric
6756706b4fc4SDimitry Andric window.MoveCursor(60, 0);
6757706b4fc4SDimitry Andric if (frame)
6758706b4fc4SDimitry Andric window.Printf("Frame: %3u PC = 0x%16.16" PRIx64,
6759706b4fc4SDimitry Andric frame->GetFrameIndex(),
6760706b4fc4SDimitry Andric frame->GetFrameCodeAddress().GetOpcodeLoadAddress(
6761706b4fc4SDimitry Andric exe_ctx.GetTargetPtr()));
6762706b4fc4SDimitry Andric } else if (state == eStateExited) {
6763706b4fc4SDimitry Andric const char *exit_desc = process->GetExitDescription();
6764706b4fc4SDimitry Andric const int exit_status = process->GetExitStatus();
6765706b4fc4SDimitry Andric if (exit_desc && exit_desc[0])
6766706b4fc4SDimitry Andric window.Printf(" with status = %i (%s)", exit_status, exit_desc);
6767706b4fc4SDimitry Andric else
6768706b4fc4SDimitry Andric window.Printf(" with status = %i", exit_status);
6769706b4fc4SDimitry Andric }
6770706b4fc4SDimitry Andric }
6771706b4fc4SDimitry Andric return true;
6772706b4fc4SDimitry Andric }
6773706b4fc4SDimitry Andric
6774706b4fc4SDimitry Andric protected:
6775706b4fc4SDimitry Andric Debugger &m_debugger;
6776706b4fc4SDimitry Andric FormatEntity::Entry m_format;
6777706b4fc4SDimitry Andric };
6778706b4fc4SDimitry Andric
6779706b4fc4SDimitry Andric class SourceFileWindowDelegate : public WindowDelegate {
6780706b4fc4SDimitry Andric public:
SourceFileWindowDelegate(Debugger & debugger)6781706b4fc4SDimitry Andric SourceFileWindowDelegate(Debugger &debugger)
6782706b4fc4SDimitry Andric : WindowDelegate(), m_debugger(debugger), m_sc(), m_file_sp(),
6783145449b1SDimitry Andric m_disassembly_sp(), m_disassembly_range(), m_title() {}
6784706b4fc4SDimitry Andric
6785706b4fc4SDimitry Andric ~SourceFileWindowDelegate() override = default;
6786706b4fc4SDimitry Andric
Update(const SymbolContext & sc)6787706b4fc4SDimitry Andric void Update(const SymbolContext &sc) { m_sc = sc; }
6788706b4fc4SDimitry Andric
NumVisibleLines() const6789706b4fc4SDimitry Andric uint32_t NumVisibleLines() const { return m_max_y - m_min_y; }
6790706b4fc4SDimitry Andric
WindowDelegateGetHelpText()6791706b4fc4SDimitry Andric const char *WindowDelegateGetHelpText() override {
6792706b4fc4SDimitry Andric return "Source/Disassembly window keyboard shortcuts:";
6793706b4fc4SDimitry Andric }
6794706b4fc4SDimitry Andric
WindowDelegateGetKeyHelp()6795706b4fc4SDimitry Andric KeyHelp *WindowDelegateGetKeyHelp() override {
6796706b4fc4SDimitry Andric static curses::KeyHelp g_source_view_key_help[] = {
6797706b4fc4SDimitry Andric {KEY_RETURN, "Run to selected line with one shot breakpoint"},
6798706b4fc4SDimitry Andric {KEY_UP, "Select previous source line"},
6799706b4fc4SDimitry Andric {KEY_DOWN, "Select next source line"},
6800b60736ecSDimitry Andric {KEY_LEFT, "Scroll to the left"},
6801b60736ecSDimitry Andric {KEY_RIGHT, "Scroll to the right"},
6802706b4fc4SDimitry Andric {KEY_PPAGE, "Page up"},
6803706b4fc4SDimitry Andric {KEY_NPAGE, "Page down"},
6804706b4fc4SDimitry Andric {'b', "Set breakpoint on selected source/disassembly line"},
6805706b4fc4SDimitry Andric {'c', "Continue process"},
6806706b4fc4SDimitry Andric {'D', "Detach with process suspended"},
6807706b4fc4SDimitry Andric {'h', "Show help dialog"},
6808706b4fc4SDimitry Andric {'n', "Step over (source line)"},
6809706b4fc4SDimitry Andric {'N', "Step over (single instruction)"},
6810b60736ecSDimitry Andric {'f', "Step out (finish)"},
6811706b4fc4SDimitry Andric {'s', "Step in (source line)"},
6812706b4fc4SDimitry Andric {'S', "Step in (single instruction)"},
6813b60736ecSDimitry Andric {'u', "Frame up"},
6814b60736ecSDimitry Andric {'d', "Frame down"},
6815706b4fc4SDimitry Andric {',', "Page up"},
6816706b4fc4SDimitry Andric {'.', "Page down"},
6817706b4fc4SDimitry Andric {'\0', nullptr}};
6818706b4fc4SDimitry Andric return g_source_view_key_help;
6819706b4fc4SDimitry Andric }
6820706b4fc4SDimitry Andric
WindowDelegateDraw(Window & window,bool force)6821706b4fc4SDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override {
6822706b4fc4SDimitry Andric ExecutionContext exe_ctx =
6823706b4fc4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
6824706b4fc4SDimitry Andric Process *process = exe_ctx.GetProcessPtr();
6825706b4fc4SDimitry Andric Thread *thread = nullptr;
6826706b4fc4SDimitry Andric
6827706b4fc4SDimitry Andric bool update_location = false;
6828706b4fc4SDimitry Andric if (process) {
6829706b4fc4SDimitry Andric StateType state = process->GetState();
6830706b4fc4SDimitry Andric if (StateIsStoppedState(state, true)) {
6831706b4fc4SDimitry Andric // We are stopped, so it is ok to
6832706b4fc4SDimitry Andric update_location = true;
6833706b4fc4SDimitry Andric }
6834706b4fc4SDimitry Andric }
6835706b4fc4SDimitry Andric
6836706b4fc4SDimitry Andric m_min_x = 1;
6837706b4fc4SDimitry Andric m_min_y = 2;
6838706b4fc4SDimitry Andric m_max_x = window.GetMaxX() - 1;
6839706b4fc4SDimitry Andric m_max_y = window.GetMaxY() - 1;
6840706b4fc4SDimitry Andric
6841706b4fc4SDimitry Andric const uint32_t num_visible_lines = NumVisibleLines();
6842706b4fc4SDimitry Andric StackFrameSP frame_sp;
6843706b4fc4SDimitry Andric bool set_selected_line_to_pc = false;
6844706b4fc4SDimitry Andric
6845706b4fc4SDimitry Andric if (update_location) {
6846e3b55780SDimitry Andric const bool process_alive = process->IsAlive();
6847706b4fc4SDimitry Andric bool thread_changed = false;
6848706b4fc4SDimitry Andric if (process_alive) {
6849706b4fc4SDimitry Andric thread = exe_ctx.GetThreadPtr();
6850706b4fc4SDimitry Andric if (thread) {
68517fa27ce4SDimitry Andric frame_sp = thread->GetSelectedFrame(SelectMostRelevantFrame);
6852706b4fc4SDimitry Andric auto tid = thread->GetID();
6853706b4fc4SDimitry Andric thread_changed = tid != m_tid;
6854706b4fc4SDimitry Andric m_tid = tid;
6855706b4fc4SDimitry Andric } else {
6856706b4fc4SDimitry Andric if (m_tid != LLDB_INVALID_THREAD_ID) {
6857706b4fc4SDimitry Andric thread_changed = true;
6858706b4fc4SDimitry Andric m_tid = LLDB_INVALID_THREAD_ID;
6859706b4fc4SDimitry Andric }
6860706b4fc4SDimitry Andric }
6861706b4fc4SDimitry Andric }
6862706b4fc4SDimitry Andric const uint32_t stop_id = process ? process->GetStopID() : 0;
6863706b4fc4SDimitry Andric const bool stop_id_changed = stop_id != m_stop_id;
6864706b4fc4SDimitry Andric bool frame_changed = false;
6865706b4fc4SDimitry Andric m_stop_id = stop_id;
6866706b4fc4SDimitry Andric m_title.Clear();
6867706b4fc4SDimitry Andric if (frame_sp) {
6868706b4fc4SDimitry Andric m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
6869706b4fc4SDimitry Andric if (m_sc.module_sp) {
6870706b4fc4SDimitry Andric m_title.Printf(
6871706b4fc4SDimitry Andric "%s", m_sc.module_sp->GetFileSpec().GetFilename().GetCString());
6872706b4fc4SDimitry Andric ConstString func_name = m_sc.GetFunctionName();
6873706b4fc4SDimitry Andric if (func_name)
6874706b4fc4SDimitry Andric m_title.Printf("`%s", func_name.GetCString());
6875706b4fc4SDimitry Andric }
6876706b4fc4SDimitry Andric const uint32_t frame_idx = frame_sp->GetFrameIndex();
6877706b4fc4SDimitry Andric frame_changed = frame_idx != m_frame_idx;
6878706b4fc4SDimitry Andric m_frame_idx = frame_idx;
6879706b4fc4SDimitry Andric } else {
6880706b4fc4SDimitry Andric m_sc.Clear(true);
6881706b4fc4SDimitry Andric frame_changed = m_frame_idx != UINT32_MAX;
6882706b4fc4SDimitry Andric m_frame_idx = UINT32_MAX;
6883706b4fc4SDimitry Andric }
6884706b4fc4SDimitry Andric
6885706b4fc4SDimitry Andric const bool context_changed =
6886706b4fc4SDimitry Andric thread_changed || frame_changed || stop_id_changed;
6887706b4fc4SDimitry Andric
6888706b4fc4SDimitry Andric if (process_alive) {
6889706b4fc4SDimitry Andric if (m_sc.line_entry.IsValid()) {
6890706b4fc4SDimitry Andric m_pc_line = m_sc.line_entry.line;
6891706b4fc4SDimitry Andric if (m_pc_line != UINT32_MAX)
6892706b4fc4SDimitry Andric --m_pc_line; // Convert to zero based line number...
6893706b4fc4SDimitry Andric // Update the selected line if the stop ID changed...
6894706b4fc4SDimitry Andric if (context_changed)
6895706b4fc4SDimitry Andric m_selected_line = m_pc_line;
6896706b4fc4SDimitry Andric
6897ac9a064cSDimitry Andric if (m_file_sp &&
6898ac9a064cSDimitry Andric m_file_sp->GetFileSpec() == m_sc.line_entry.GetFile()) {
6899c0981da4SDimitry Andric // Same file, nothing to do, we should either have the lines or
6900c0981da4SDimitry Andric // not (source file missing)
6901706b4fc4SDimitry Andric if (m_selected_line >= static_cast<size_t>(m_first_visible_line)) {
6902706b4fc4SDimitry Andric if (m_selected_line >= m_first_visible_line + num_visible_lines)
6903706b4fc4SDimitry Andric m_first_visible_line = m_selected_line - 10;
6904706b4fc4SDimitry Andric } else {
6905706b4fc4SDimitry Andric if (m_selected_line > 10)
6906706b4fc4SDimitry Andric m_first_visible_line = m_selected_line - 10;
6907706b4fc4SDimitry Andric else
6908706b4fc4SDimitry Andric m_first_visible_line = 0;
6909706b4fc4SDimitry Andric }
6910706b4fc4SDimitry Andric } else {
6911706b4fc4SDimitry Andric // File changed, set selected line to the line with the PC
6912706b4fc4SDimitry Andric m_selected_line = m_pc_line;
6913ac9a064cSDimitry Andric m_file_sp = m_debugger.GetSourceManager().GetFile(
6914ac9a064cSDimitry Andric m_sc.line_entry.GetFile());
6915706b4fc4SDimitry Andric if (m_file_sp) {
6916706b4fc4SDimitry Andric const size_t num_lines = m_file_sp->GetNumLines();
6917706b4fc4SDimitry Andric m_line_width = 1;
6918706b4fc4SDimitry Andric for (size_t n = num_lines; n >= 10; n = n / 10)
6919706b4fc4SDimitry Andric ++m_line_width;
6920706b4fc4SDimitry Andric
6921706b4fc4SDimitry Andric if (num_lines < num_visible_lines ||
6922706b4fc4SDimitry Andric m_selected_line < num_visible_lines)
6923706b4fc4SDimitry Andric m_first_visible_line = 0;
6924706b4fc4SDimitry Andric else
6925706b4fc4SDimitry Andric m_first_visible_line = m_selected_line - 10;
6926706b4fc4SDimitry Andric }
6927706b4fc4SDimitry Andric }
6928706b4fc4SDimitry Andric } else {
6929706b4fc4SDimitry Andric m_file_sp.reset();
6930706b4fc4SDimitry Andric }
6931706b4fc4SDimitry Andric
6932706b4fc4SDimitry Andric if (!m_file_sp || m_file_sp->GetNumLines() == 0) {
6933706b4fc4SDimitry Andric // Show disassembly
6934706b4fc4SDimitry Andric bool prefer_file_cache = false;
6935706b4fc4SDimitry Andric if (m_sc.function) {
6936706b4fc4SDimitry Andric if (m_disassembly_scope != m_sc.function) {
6937706b4fc4SDimitry Andric m_disassembly_scope = m_sc.function;
6938706b4fc4SDimitry Andric m_disassembly_sp = m_sc.function->GetInstructions(
6939344a3780SDimitry Andric exe_ctx, nullptr, !prefer_file_cache);
6940706b4fc4SDimitry Andric if (m_disassembly_sp) {
6941706b4fc4SDimitry Andric set_selected_line_to_pc = true;
6942706b4fc4SDimitry Andric m_disassembly_range = m_sc.function->GetAddressRange();
6943706b4fc4SDimitry Andric } else {
6944706b4fc4SDimitry Andric m_disassembly_range.Clear();
6945706b4fc4SDimitry Andric }
6946706b4fc4SDimitry Andric } else {
6947706b4fc4SDimitry Andric set_selected_line_to_pc = context_changed;
6948706b4fc4SDimitry Andric }
6949706b4fc4SDimitry Andric } else if (m_sc.symbol) {
6950706b4fc4SDimitry Andric if (m_disassembly_scope != m_sc.symbol) {
6951706b4fc4SDimitry Andric m_disassembly_scope = m_sc.symbol;
6952706b4fc4SDimitry Andric m_disassembly_sp = m_sc.symbol->GetInstructions(
6953706b4fc4SDimitry Andric exe_ctx, nullptr, prefer_file_cache);
6954706b4fc4SDimitry Andric if (m_disassembly_sp) {
6955706b4fc4SDimitry Andric set_selected_line_to_pc = true;
6956706b4fc4SDimitry Andric m_disassembly_range.GetBaseAddress() =
6957706b4fc4SDimitry Andric m_sc.symbol->GetAddress();
6958706b4fc4SDimitry Andric m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize());
6959706b4fc4SDimitry Andric } else {
6960706b4fc4SDimitry Andric m_disassembly_range.Clear();
6961706b4fc4SDimitry Andric }
6962706b4fc4SDimitry Andric } else {
6963706b4fc4SDimitry Andric set_selected_line_to_pc = context_changed;
6964706b4fc4SDimitry Andric }
6965706b4fc4SDimitry Andric }
6966706b4fc4SDimitry Andric }
6967706b4fc4SDimitry Andric } else {
6968706b4fc4SDimitry Andric m_pc_line = UINT32_MAX;
6969706b4fc4SDimitry Andric }
6970706b4fc4SDimitry Andric }
6971706b4fc4SDimitry Andric
6972706b4fc4SDimitry Andric const int window_width = window.GetWidth();
6973706b4fc4SDimitry Andric window.Erase();
6974706b4fc4SDimitry Andric window.DrawTitleBox("Sources");
6975706b4fc4SDimitry Andric if (!m_title.GetString().empty()) {
6976706b4fc4SDimitry Andric window.AttributeOn(A_REVERSE);
6977706b4fc4SDimitry Andric window.MoveCursor(1, 1);
6978706b4fc4SDimitry Andric window.PutChar(' ');
6979b60736ecSDimitry Andric window.PutCStringTruncated(1, m_title.GetString().str().c_str());
6980706b4fc4SDimitry Andric int x = window.GetCursorX();
6981706b4fc4SDimitry Andric if (x < window_width - 1) {
6982706b4fc4SDimitry Andric window.Printf("%*s", window_width - x - 1, "");
6983706b4fc4SDimitry Andric }
6984706b4fc4SDimitry Andric window.AttributeOff(A_REVERSE);
6985706b4fc4SDimitry Andric }
6986706b4fc4SDimitry Andric
6987706b4fc4SDimitry Andric Target *target = exe_ctx.GetTargetPtr();
6988706b4fc4SDimitry Andric const size_t num_source_lines = GetNumSourceLines();
6989706b4fc4SDimitry Andric if (num_source_lines > 0) {
6990706b4fc4SDimitry Andric // Display source
6991706b4fc4SDimitry Andric BreakpointLines bp_lines;
6992706b4fc4SDimitry Andric if (target) {
6993706b4fc4SDimitry Andric BreakpointList &bp_list = target->GetBreakpointList();
6994706b4fc4SDimitry Andric const size_t num_bps = bp_list.GetSize();
6995706b4fc4SDimitry Andric for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
6996706b4fc4SDimitry Andric BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
6997706b4fc4SDimitry Andric const size_t num_bps_locs = bp_sp->GetNumLocations();
6998706b4fc4SDimitry Andric for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
6999706b4fc4SDimitry Andric BreakpointLocationSP bp_loc_sp =
7000706b4fc4SDimitry Andric bp_sp->GetLocationAtIndex(bp_loc_idx);
7001706b4fc4SDimitry Andric LineEntry bp_loc_line_entry;
7002706b4fc4SDimitry Andric if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry(
7003706b4fc4SDimitry Andric bp_loc_line_entry)) {
7004ac9a064cSDimitry Andric if (m_file_sp->GetFileSpec() == bp_loc_line_entry.GetFile()) {
7005706b4fc4SDimitry Andric bp_lines.insert(bp_loc_line_entry.line);
7006706b4fc4SDimitry Andric }
7007706b4fc4SDimitry Andric }
7008706b4fc4SDimitry Andric }
7009706b4fc4SDimitry Andric }
7010706b4fc4SDimitry Andric }
7011706b4fc4SDimitry Andric
7012706b4fc4SDimitry Andric for (size_t i = 0; i < num_visible_lines; ++i) {
7013706b4fc4SDimitry Andric const uint32_t curr_line = m_first_visible_line + i;
7014706b4fc4SDimitry Andric if (curr_line < num_source_lines) {
7015706b4fc4SDimitry Andric const int line_y = m_min_y + i;
7016706b4fc4SDimitry Andric window.MoveCursor(1, line_y);
7017706b4fc4SDimitry Andric const bool is_pc_line = curr_line == m_pc_line;
7018706b4fc4SDimitry Andric const bool line_is_selected = m_selected_line == curr_line;
7019145449b1SDimitry Andric // Highlight the line as the PC line first (done by passing
7020145449b1SDimitry Andric // argument to OutputColoredStringTruncated()), then if the selected
7021145449b1SDimitry Andric // line isn't the same as the PC line, highlight it differently.
7022706b4fc4SDimitry Andric attr_t highlight_attr = 0;
7023706b4fc4SDimitry Andric attr_t bp_attr = 0;
7024145449b1SDimitry Andric if (line_is_selected && !is_pc_line)
7025145449b1SDimitry Andric highlight_attr = A_REVERSE;
7026706b4fc4SDimitry Andric
7027706b4fc4SDimitry Andric if (bp_lines.find(curr_line + 1) != bp_lines.end())
7028b60736ecSDimitry Andric bp_attr = COLOR_PAIR(BlackOnWhite);
7029706b4fc4SDimitry Andric
7030706b4fc4SDimitry Andric if (bp_attr)
7031706b4fc4SDimitry Andric window.AttributeOn(bp_attr);
7032706b4fc4SDimitry Andric
7033706b4fc4SDimitry Andric window.Printf(" %*u ", m_line_width, curr_line + 1);
7034706b4fc4SDimitry Andric
7035706b4fc4SDimitry Andric if (bp_attr)
7036706b4fc4SDimitry Andric window.AttributeOff(bp_attr);
7037706b4fc4SDimitry Andric
7038706b4fc4SDimitry Andric window.PutChar(ACS_VLINE);
7039706b4fc4SDimitry Andric // Mark the line with the PC with a diamond
7040706b4fc4SDimitry Andric if (is_pc_line)
7041706b4fc4SDimitry Andric window.PutChar(ACS_DIAMOND);
7042706b4fc4SDimitry Andric else
7043706b4fc4SDimitry Andric window.PutChar(' ');
7044706b4fc4SDimitry Andric
7045706b4fc4SDimitry Andric if (highlight_attr)
7046706b4fc4SDimitry Andric window.AttributeOn(highlight_attr);
7047b60736ecSDimitry Andric
7048b60736ecSDimitry Andric StreamString lineStream;
7049145449b1SDimitry Andric
7050e3b55780SDimitry Andric std::optional<size_t> column;
7051145449b1SDimitry Andric if (is_pc_line && m_sc.line_entry.IsValid() && m_sc.line_entry.column)
7052145449b1SDimitry Andric column = m_sc.line_entry.column - 1;
7053145449b1SDimitry Andric m_file_sp->DisplaySourceLines(curr_line + 1, column, 0, 0,
7054145449b1SDimitry Andric &lineStream);
7055b60736ecSDimitry Andric StringRef line = lineStream.GetString();
7056312c0ed1SDimitry Andric if (line.ends_with("\n"))
7057b60736ecSDimitry Andric line = line.drop_back();
7058b60736ecSDimitry Andric bool wasWritten = window.OutputColoredStringTruncated(
7059145449b1SDimitry Andric 1, line, m_first_visible_column, is_pc_line);
7060145449b1SDimitry Andric if (!wasWritten && (line_is_selected || is_pc_line)) {
7061145449b1SDimitry Andric // Draw an empty space to show the selected/PC line if empty,
7062b60736ecSDimitry Andric // or draw '<' if nothing is visible because of scrolling too much
7063b60736ecSDimitry Andric // to the right.
7064b60736ecSDimitry Andric window.PutCStringTruncated(
7065b60736ecSDimitry Andric 1, line.empty() && m_first_visible_column == 0 ? " " : "<");
7066b60736ecSDimitry Andric }
7067706b4fc4SDimitry Andric
7068706b4fc4SDimitry Andric if (is_pc_line && frame_sp &&
7069706b4fc4SDimitry Andric frame_sp->GetConcreteFrameIndex() == 0) {
7070706b4fc4SDimitry Andric StopInfoSP stop_info_sp;
7071706b4fc4SDimitry Andric if (thread)
7072706b4fc4SDimitry Andric stop_info_sp = thread->GetStopInfo();
7073706b4fc4SDimitry Andric if (stop_info_sp) {
7074706b4fc4SDimitry Andric const char *stop_description = stop_info_sp->GetDescription();
7075706b4fc4SDimitry Andric if (stop_description && stop_description[0]) {
7076706b4fc4SDimitry Andric size_t stop_description_len = strlen(stop_description);
7077706b4fc4SDimitry Andric int desc_x = window_width - stop_description_len - 16;
7078b60736ecSDimitry Andric if (desc_x - window.GetCursorX() > 0)
7079706b4fc4SDimitry Andric window.Printf("%*s", desc_x - window.GetCursorX(), "");
7080b60736ecSDimitry Andric window.MoveCursor(window_width - stop_description_len - 16,
7081b60736ecSDimitry Andric line_y);
7082b60736ecSDimitry Andric const attr_t stop_reason_attr = COLOR_PAIR(WhiteOnBlue);
7083b60736ecSDimitry Andric window.AttributeOn(stop_reason_attr);
7084b60736ecSDimitry Andric window.PrintfTruncated(1, " <<< Thread %u: %s ",
7085b60736ecSDimitry Andric thread->GetIndexID(), stop_description);
7086b60736ecSDimitry Andric window.AttributeOff(stop_reason_attr);
7087706b4fc4SDimitry Andric }
7088706b4fc4SDimitry Andric } else {
7089706b4fc4SDimitry Andric window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
7090706b4fc4SDimitry Andric }
7091706b4fc4SDimitry Andric }
7092706b4fc4SDimitry Andric if (highlight_attr)
7093706b4fc4SDimitry Andric window.AttributeOff(highlight_attr);
7094706b4fc4SDimitry Andric } else {
7095706b4fc4SDimitry Andric break;
7096706b4fc4SDimitry Andric }
7097706b4fc4SDimitry Andric }
7098706b4fc4SDimitry Andric } else {
7099706b4fc4SDimitry Andric size_t num_disassembly_lines = GetNumDisassemblyLines();
7100706b4fc4SDimitry Andric if (num_disassembly_lines > 0) {
7101706b4fc4SDimitry Andric // Display disassembly
7102706b4fc4SDimitry Andric BreakpointAddrs bp_file_addrs;
7103706b4fc4SDimitry Andric Target *target = exe_ctx.GetTargetPtr();
7104706b4fc4SDimitry Andric if (target) {
7105706b4fc4SDimitry Andric BreakpointList &bp_list = target->GetBreakpointList();
7106706b4fc4SDimitry Andric const size_t num_bps = bp_list.GetSize();
7107706b4fc4SDimitry Andric for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
7108706b4fc4SDimitry Andric BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
7109706b4fc4SDimitry Andric const size_t num_bps_locs = bp_sp->GetNumLocations();
7110706b4fc4SDimitry Andric for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs;
7111706b4fc4SDimitry Andric ++bp_loc_idx) {
7112706b4fc4SDimitry Andric BreakpointLocationSP bp_loc_sp =
7113706b4fc4SDimitry Andric bp_sp->GetLocationAtIndex(bp_loc_idx);
7114706b4fc4SDimitry Andric LineEntry bp_loc_line_entry;
7115706b4fc4SDimitry Andric const lldb::addr_t file_addr =
7116706b4fc4SDimitry Andric bp_loc_sp->GetAddress().GetFileAddress();
7117706b4fc4SDimitry Andric if (file_addr != LLDB_INVALID_ADDRESS) {
7118706b4fc4SDimitry Andric if (m_disassembly_range.ContainsFileAddress(file_addr))
7119706b4fc4SDimitry Andric bp_file_addrs.insert(file_addr);
7120706b4fc4SDimitry Andric }
7121706b4fc4SDimitry Andric }
7122706b4fc4SDimitry Andric }
7123706b4fc4SDimitry Andric }
7124706b4fc4SDimitry Andric
7125706b4fc4SDimitry Andric const attr_t selected_highlight_attr = A_REVERSE;
7126b60736ecSDimitry Andric const attr_t pc_highlight_attr = COLOR_PAIR(WhiteOnBlue);
7127706b4fc4SDimitry Andric
7128706b4fc4SDimitry Andric StreamString strm;
7129706b4fc4SDimitry Andric
7130706b4fc4SDimitry Andric InstructionList &insts = m_disassembly_sp->GetInstructionList();
7131706b4fc4SDimitry Andric Address pc_address;
7132706b4fc4SDimitry Andric
7133706b4fc4SDimitry Andric if (frame_sp)
7134706b4fc4SDimitry Andric pc_address = frame_sp->GetFrameCodeAddress();
7135706b4fc4SDimitry Andric const uint32_t pc_idx =
7136706b4fc4SDimitry Andric pc_address.IsValid()
7137706b4fc4SDimitry Andric ? insts.GetIndexOfInstructionAtAddress(pc_address)
7138706b4fc4SDimitry Andric : UINT32_MAX;
7139706b4fc4SDimitry Andric if (set_selected_line_to_pc) {
7140706b4fc4SDimitry Andric m_selected_line = pc_idx;
7141706b4fc4SDimitry Andric }
7142706b4fc4SDimitry Andric
7143706b4fc4SDimitry Andric const uint32_t non_visible_pc_offset = (num_visible_lines / 5);
7144706b4fc4SDimitry Andric if (static_cast<size_t>(m_first_visible_line) >= num_disassembly_lines)
7145706b4fc4SDimitry Andric m_first_visible_line = 0;
7146706b4fc4SDimitry Andric
7147706b4fc4SDimitry Andric if (pc_idx < num_disassembly_lines) {
7148706b4fc4SDimitry Andric if (pc_idx < static_cast<uint32_t>(m_first_visible_line) ||
7149706b4fc4SDimitry Andric pc_idx >= m_first_visible_line + num_visible_lines)
7150706b4fc4SDimitry Andric m_first_visible_line = pc_idx - non_visible_pc_offset;
7151706b4fc4SDimitry Andric }
7152706b4fc4SDimitry Andric
7153706b4fc4SDimitry Andric for (size_t i = 0; i < num_visible_lines; ++i) {
7154706b4fc4SDimitry Andric const uint32_t inst_idx = m_first_visible_line + i;
7155706b4fc4SDimitry Andric Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get();
7156706b4fc4SDimitry Andric if (!inst)
7157706b4fc4SDimitry Andric break;
7158706b4fc4SDimitry Andric
7159706b4fc4SDimitry Andric const int line_y = m_min_y + i;
7160706b4fc4SDimitry Andric window.MoveCursor(1, line_y);
7161706b4fc4SDimitry Andric const bool is_pc_line = frame_sp && inst_idx == pc_idx;
7162706b4fc4SDimitry Andric const bool line_is_selected = m_selected_line == inst_idx;
7163c0981da4SDimitry Andric // Highlight the line as the PC line first, then if the selected
7164c0981da4SDimitry Andric // line isn't the same as the PC line, highlight it differently
7165706b4fc4SDimitry Andric attr_t highlight_attr = 0;
7166706b4fc4SDimitry Andric attr_t bp_attr = 0;
7167706b4fc4SDimitry Andric if (is_pc_line)
7168706b4fc4SDimitry Andric highlight_attr = pc_highlight_attr;
7169706b4fc4SDimitry Andric else if (line_is_selected)
7170706b4fc4SDimitry Andric highlight_attr = selected_highlight_attr;
7171706b4fc4SDimitry Andric
7172706b4fc4SDimitry Andric if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) !=
7173706b4fc4SDimitry Andric bp_file_addrs.end())
7174b60736ecSDimitry Andric bp_attr = COLOR_PAIR(BlackOnWhite);
7175706b4fc4SDimitry Andric
7176706b4fc4SDimitry Andric if (bp_attr)
7177706b4fc4SDimitry Andric window.AttributeOn(bp_attr);
7178706b4fc4SDimitry Andric
7179706b4fc4SDimitry Andric window.Printf(" 0x%16.16llx ",
7180706b4fc4SDimitry Andric static_cast<unsigned long long>(
7181706b4fc4SDimitry Andric inst->GetAddress().GetLoadAddress(target)));
7182706b4fc4SDimitry Andric
7183706b4fc4SDimitry Andric if (bp_attr)
7184706b4fc4SDimitry Andric window.AttributeOff(bp_attr);
7185706b4fc4SDimitry Andric
7186706b4fc4SDimitry Andric window.PutChar(ACS_VLINE);
7187706b4fc4SDimitry Andric // Mark the line with the PC with a diamond
7188706b4fc4SDimitry Andric if (is_pc_line)
7189706b4fc4SDimitry Andric window.PutChar(ACS_DIAMOND);
7190706b4fc4SDimitry Andric else
7191706b4fc4SDimitry Andric window.PutChar(' ');
7192706b4fc4SDimitry Andric
7193706b4fc4SDimitry Andric if (highlight_attr)
7194706b4fc4SDimitry Andric window.AttributeOn(highlight_attr);
7195706b4fc4SDimitry Andric
7196706b4fc4SDimitry Andric const char *mnemonic = inst->GetMnemonic(&exe_ctx);
7197706b4fc4SDimitry Andric const char *operands = inst->GetOperands(&exe_ctx);
7198706b4fc4SDimitry Andric const char *comment = inst->GetComment(&exe_ctx);
7199706b4fc4SDimitry Andric
7200706b4fc4SDimitry Andric if (mnemonic != nullptr && mnemonic[0] == '\0')
7201706b4fc4SDimitry Andric mnemonic = nullptr;
7202706b4fc4SDimitry Andric if (operands != nullptr && operands[0] == '\0')
7203706b4fc4SDimitry Andric operands = nullptr;
7204706b4fc4SDimitry Andric if (comment != nullptr && comment[0] == '\0')
7205706b4fc4SDimitry Andric comment = nullptr;
7206706b4fc4SDimitry Andric
7207706b4fc4SDimitry Andric strm.Clear();
7208706b4fc4SDimitry Andric
7209706b4fc4SDimitry Andric if (mnemonic != nullptr && operands != nullptr && comment != nullptr)
7210706b4fc4SDimitry Andric strm.Printf("%-8s %-25s ; %s", mnemonic, operands, comment);
7211706b4fc4SDimitry Andric else if (mnemonic != nullptr && operands != nullptr)
7212706b4fc4SDimitry Andric strm.Printf("%-8s %s", mnemonic, operands);
7213706b4fc4SDimitry Andric else if (mnemonic != nullptr)
7214706b4fc4SDimitry Andric strm.Printf("%s", mnemonic);
7215706b4fc4SDimitry Andric
7216706b4fc4SDimitry Andric int right_pad = 1;
7217b60736ecSDimitry Andric window.PutCStringTruncated(
7218b60736ecSDimitry Andric right_pad,
7219b60736ecSDimitry Andric strm.GetString().substr(m_first_visible_column).data());
7220706b4fc4SDimitry Andric
7221706b4fc4SDimitry Andric if (is_pc_line && frame_sp &&
7222706b4fc4SDimitry Andric frame_sp->GetConcreteFrameIndex() == 0) {
7223706b4fc4SDimitry Andric StopInfoSP stop_info_sp;
7224706b4fc4SDimitry Andric if (thread)
7225706b4fc4SDimitry Andric stop_info_sp = thread->GetStopInfo();
7226706b4fc4SDimitry Andric if (stop_info_sp) {
7227706b4fc4SDimitry Andric const char *stop_description = stop_info_sp->GetDescription();
7228706b4fc4SDimitry Andric if (stop_description && stop_description[0]) {
7229706b4fc4SDimitry Andric size_t stop_description_len = strlen(stop_description);
7230706b4fc4SDimitry Andric int desc_x = window_width - stop_description_len - 16;
7231b60736ecSDimitry Andric if (desc_x - window.GetCursorX() > 0)
7232706b4fc4SDimitry Andric window.Printf("%*s", desc_x - window.GetCursorX(), "");
7233b60736ecSDimitry Andric window.MoveCursor(window_width - stop_description_len - 15,
7234b60736ecSDimitry Andric line_y);
7235e3b55780SDimitry Andric if (thread)
7236b60736ecSDimitry Andric window.PrintfTruncated(1, "<<< Thread %u: %s ",
7237e3b55780SDimitry Andric thread->GetIndexID(),
7238e3b55780SDimitry Andric stop_description);
7239706b4fc4SDimitry Andric }
7240706b4fc4SDimitry Andric } else {
7241706b4fc4SDimitry Andric window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
7242706b4fc4SDimitry Andric }
7243706b4fc4SDimitry Andric }
7244706b4fc4SDimitry Andric if (highlight_attr)
7245706b4fc4SDimitry Andric window.AttributeOff(highlight_attr);
7246706b4fc4SDimitry Andric }
7247706b4fc4SDimitry Andric }
7248706b4fc4SDimitry Andric }
7249706b4fc4SDimitry Andric return true; // Drawing handled
7250706b4fc4SDimitry Andric }
7251706b4fc4SDimitry Andric
GetNumLines()7252706b4fc4SDimitry Andric size_t GetNumLines() {
7253706b4fc4SDimitry Andric size_t num_lines = GetNumSourceLines();
7254706b4fc4SDimitry Andric if (num_lines == 0)
7255706b4fc4SDimitry Andric num_lines = GetNumDisassemblyLines();
7256706b4fc4SDimitry Andric return num_lines;
7257706b4fc4SDimitry Andric }
7258706b4fc4SDimitry Andric
GetNumSourceLines() const7259706b4fc4SDimitry Andric size_t GetNumSourceLines() const {
7260706b4fc4SDimitry Andric if (m_file_sp)
7261706b4fc4SDimitry Andric return m_file_sp->GetNumLines();
7262706b4fc4SDimitry Andric return 0;
7263706b4fc4SDimitry Andric }
7264706b4fc4SDimitry Andric
GetNumDisassemblyLines() const7265706b4fc4SDimitry Andric size_t GetNumDisassemblyLines() const {
7266706b4fc4SDimitry Andric if (m_disassembly_sp)
7267706b4fc4SDimitry Andric return m_disassembly_sp->GetInstructionList().GetSize();
7268706b4fc4SDimitry Andric return 0;
7269706b4fc4SDimitry Andric }
7270706b4fc4SDimitry Andric
WindowDelegateHandleChar(Window & window,int c)7271706b4fc4SDimitry Andric HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
7272706b4fc4SDimitry Andric const uint32_t num_visible_lines = NumVisibleLines();
7273706b4fc4SDimitry Andric const size_t num_lines = GetNumLines();
7274706b4fc4SDimitry Andric
7275706b4fc4SDimitry Andric switch (c) {
7276706b4fc4SDimitry Andric case ',':
7277706b4fc4SDimitry Andric case KEY_PPAGE:
7278706b4fc4SDimitry Andric // Page up key
7279706b4fc4SDimitry Andric if (static_cast<uint32_t>(m_first_visible_line) > num_visible_lines)
7280706b4fc4SDimitry Andric m_first_visible_line -= num_visible_lines;
7281706b4fc4SDimitry Andric else
7282706b4fc4SDimitry Andric m_first_visible_line = 0;
7283706b4fc4SDimitry Andric m_selected_line = m_first_visible_line;
7284706b4fc4SDimitry Andric return eKeyHandled;
7285706b4fc4SDimitry Andric
7286706b4fc4SDimitry Andric case '.':
7287706b4fc4SDimitry Andric case KEY_NPAGE:
7288706b4fc4SDimitry Andric // Page down key
7289706b4fc4SDimitry Andric {
7290706b4fc4SDimitry Andric if (m_first_visible_line + num_visible_lines < num_lines)
7291706b4fc4SDimitry Andric m_first_visible_line += num_visible_lines;
7292706b4fc4SDimitry Andric else if (num_lines < num_visible_lines)
7293706b4fc4SDimitry Andric m_first_visible_line = 0;
7294706b4fc4SDimitry Andric else
7295706b4fc4SDimitry Andric m_first_visible_line = num_lines - num_visible_lines;
7296706b4fc4SDimitry Andric m_selected_line = m_first_visible_line;
7297706b4fc4SDimitry Andric }
7298706b4fc4SDimitry Andric return eKeyHandled;
7299706b4fc4SDimitry Andric
7300706b4fc4SDimitry Andric case KEY_UP:
7301706b4fc4SDimitry Andric if (m_selected_line > 0) {
7302706b4fc4SDimitry Andric m_selected_line--;
7303706b4fc4SDimitry Andric if (static_cast<size_t>(m_first_visible_line) > m_selected_line)
7304706b4fc4SDimitry Andric m_first_visible_line = m_selected_line;
7305706b4fc4SDimitry Andric }
7306706b4fc4SDimitry Andric return eKeyHandled;
7307706b4fc4SDimitry Andric
7308706b4fc4SDimitry Andric case KEY_DOWN:
7309706b4fc4SDimitry Andric if (m_selected_line + 1 < num_lines) {
7310706b4fc4SDimitry Andric m_selected_line++;
7311706b4fc4SDimitry Andric if (m_first_visible_line + num_visible_lines < m_selected_line)
7312706b4fc4SDimitry Andric m_first_visible_line++;
7313706b4fc4SDimitry Andric }
7314706b4fc4SDimitry Andric return eKeyHandled;
7315706b4fc4SDimitry Andric
7316b60736ecSDimitry Andric case KEY_LEFT:
7317b60736ecSDimitry Andric if (m_first_visible_column > 0)
7318b60736ecSDimitry Andric --m_first_visible_column;
7319b60736ecSDimitry Andric return eKeyHandled;
7320b60736ecSDimitry Andric
7321b60736ecSDimitry Andric case KEY_RIGHT:
7322b60736ecSDimitry Andric ++m_first_visible_column;
7323b60736ecSDimitry Andric return eKeyHandled;
7324b60736ecSDimitry Andric
7325706b4fc4SDimitry Andric case '\r':
7326706b4fc4SDimitry Andric case '\n':
7327706b4fc4SDimitry Andric case KEY_ENTER:
7328706b4fc4SDimitry Andric // Set a breakpoint and run to the line using a one shot breakpoint
7329706b4fc4SDimitry Andric if (GetNumSourceLines() > 0) {
7330706b4fc4SDimitry Andric ExecutionContext exe_ctx =
7331706b4fc4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
7332706b4fc4SDimitry Andric if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive()) {
7333706b4fc4SDimitry Andric BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
7334706b4fc4SDimitry Andric nullptr, // Don't limit the breakpoint to certain modules
7335706b4fc4SDimitry Andric m_file_sp->GetFileSpec(), // Source file
7336706b4fc4SDimitry Andric m_selected_line +
7337706b4fc4SDimitry Andric 1, // Source line number (m_selected_line is zero based)
7338706b4fc4SDimitry Andric 0, // Unspecified column.
7339706b4fc4SDimitry Andric 0, // No offset
7340706b4fc4SDimitry Andric eLazyBoolCalculate, // Check inlines using global setting
7341706b4fc4SDimitry Andric eLazyBoolCalculate, // Skip prologue using global setting,
7342706b4fc4SDimitry Andric false, // internal
7343706b4fc4SDimitry Andric false, // request_hardware
7344706b4fc4SDimitry Andric eLazyBoolCalculate); // move_to_nearest_code
7345706b4fc4SDimitry Andric // Make breakpoint one shot
7346344a3780SDimitry Andric bp_sp->GetOptions().SetOneShot(true);
7347706b4fc4SDimitry Andric exe_ctx.GetProcessRef().Resume();
7348706b4fc4SDimitry Andric }
7349706b4fc4SDimitry Andric } else if (m_selected_line < GetNumDisassemblyLines()) {
7350706b4fc4SDimitry Andric const Instruction *inst = m_disassembly_sp->GetInstructionList()
7351706b4fc4SDimitry Andric .GetInstructionAtIndex(m_selected_line)
7352706b4fc4SDimitry Andric .get();
7353706b4fc4SDimitry Andric ExecutionContext exe_ctx =
7354706b4fc4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
7355706b4fc4SDimitry Andric if (exe_ctx.HasTargetScope()) {
7356706b4fc4SDimitry Andric Address addr = inst->GetAddress();
7357706b4fc4SDimitry Andric BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
7358706b4fc4SDimitry Andric addr, // lldb_private::Address
7359706b4fc4SDimitry Andric false, // internal
7360706b4fc4SDimitry Andric false); // request_hardware
7361706b4fc4SDimitry Andric // Make breakpoint one shot
7362344a3780SDimitry Andric bp_sp->GetOptions().SetOneShot(true);
7363706b4fc4SDimitry Andric exe_ctx.GetProcessRef().Resume();
7364706b4fc4SDimitry Andric }
7365706b4fc4SDimitry Andric }
7366706b4fc4SDimitry Andric return eKeyHandled;
7367706b4fc4SDimitry Andric
7368706b4fc4SDimitry Andric case 'b': // 'b' == toggle breakpoint on currently selected line
7369b60736ecSDimitry Andric ToggleBreakpointOnSelectedLine();
7370706b4fc4SDimitry Andric return eKeyHandled;
7371706b4fc4SDimitry Andric
7372706b4fc4SDimitry Andric case 'D': // 'D' == detach and keep stopped
7373706b4fc4SDimitry Andric {
7374706b4fc4SDimitry Andric ExecutionContext exe_ctx =
7375706b4fc4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
7376706b4fc4SDimitry Andric if (exe_ctx.HasProcessScope())
7377b60736ecSDimitry Andric exe_ctx.GetProcessRef().Detach(true);
7378706b4fc4SDimitry Andric }
7379706b4fc4SDimitry Andric return eKeyHandled;
7380706b4fc4SDimitry Andric
7381706b4fc4SDimitry Andric case 'c':
7382706b4fc4SDimitry Andric // 'c' == continue
7383706b4fc4SDimitry Andric {
7384706b4fc4SDimitry Andric ExecutionContext exe_ctx =
7385706b4fc4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
7386706b4fc4SDimitry Andric if (exe_ctx.HasProcessScope())
7387706b4fc4SDimitry Andric exe_ctx.GetProcessRef().Resume();
7388706b4fc4SDimitry Andric }
7389706b4fc4SDimitry Andric return eKeyHandled;
7390706b4fc4SDimitry Andric
7391b60736ecSDimitry Andric case 'f':
7392b60736ecSDimitry Andric // 'f' == step out (finish)
7393706b4fc4SDimitry Andric {
7394706b4fc4SDimitry Andric ExecutionContext exe_ctx =
7395706b4fc4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
7396706b4fc4SDimitry Andric if (exe_ctx.HasThreadScope() &&
7397706b4fc4SDimitry Andric StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
7398145449b1SDimitry Andric Thread *thread = exe_ctx.GetThreadPtr();
73997fa27ce4SDimitry Andric uint32_t frame_idx =
74007fa27ce4SDimitry Andric thread->GetSelectedFrameIndex(SelectMostRelevantFrame);
7401145449b1SDimitry Andric exe_ctx.GetThreadRef().StepOut(frame_idx);
7402706b4fc4SDimitry Andric }
7403706b4fc4SDimitry Andric }
7404706b4fc4SDimitry Andric return eKeyHandled;
7405706b4fc4SDimitry Andric
7406706b4fc4SDimitry Andric case 'n': // 'n' == step over
7407706b4fc4SDimitry Andric case 'N': // 'N' == step over instruction
7408706b4fc4SDimitry Andric {
7409706b4fc4SDimitry Andric ExecutionContext exe_ctx =
7410706b4fc4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
7411706b4fc4SDimitry Andric if (exe_ctx.HasThreadScope() &&
7412706b4fc4SDimitry Andric StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
7413706b4fc4SDimitry Andric bool source_step = (c == 'n');
7414706b4fc4SDimitry Andric exe_ctx.GetThreadRef().StepOver(source_step);
7415706b4fc4SDimitry Andric }
7416706b4fc4SDimitry Andric }
7417706b4fc4SDimitry Andric return eKeyHandled;
7418706b4fc4SDimitry Andric
7419706b4fc4SDimitry Andric case 's': // 's' == step into
7420706b4fc4SDimitry Andric case 'S': // 'S' == step into instruction
7421706b4fc4SDimitry Andric {
7422706b4fc4SDimitry Andric ExecutionContext exe_ctx =
7423706b4fc4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
7424706b4fc4SDimitry Andric if (exe_ctx.HasThreadScope() &&
7425706b4fc4SDimitry Andric StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
7426706b4fc4SDimitry Andric bool source_step = (c == 's');
7427706b4fc4SDimitry Andric exe_ctx.GetThreadRef().StepIn(source_step);
7428706b4fc4SDimitry Andric }
7429706b4fc4SDimitry Andric }
7430706b4fc4SDimitry Andric return eKeyHandled;
7431706b4fc4SDimitry Andric
7432b60736ecSDimitry Andric case 'u': // 'u' == frame up
7433b60736ecSDimitry Andric case 'd': // 'd' == frame down
7434b60736ecSDimitry Andric {
7435b60736ecSDimitry Andric ExecutionContext exe_ctx =
7436b60736ecSDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
7437b60736ecSDimitry Andric if (exe_ctx.HasThreadScope()) {
7438b60736ecSDimitry Andric Thread *thread = exe_ctx.GetThreadPtr();
74397fa27ce4SDimitry Andric uint32_t frame_idx =
74407fa27ce4SDimitry Andric thread->GetSelectedFrameIndex(SelectMostRelevantFrame);
7441b60736ecSDimitry Andric if (frame_idx == UINT32_MAX)
7442b60736ecSDimitry Andric frame_idx = 0;
7443b60736ecSDimitry Andric if (c == 'u' && frame_idx + 1 < thread->GetStackFrameCount())
7444b60736ecSDimitry Andric ++frame_idx;
7445b60736ecSDimitry Andric else if (c == 'd' && frame_idx > 0)
7446b60736ecSDimitry Andric --frame_idx;
7447b60736ecSDimitry Andric if (thread->SetSelectedFrameByIndex(frame_idx, true))
74487fa27ce4SDimitry Andric exe_ctx.SetFrameSP(thread->GetSelectedFrame(SelectMostRelevantFrame));
7449b60736ecSDimitry Andric }
7450b60736ecSDimitry Andric }
7451b60736ecSDimitry Andric return eKeyHandled;
7452b60736ecSDimitry Andric
7453706b4fc4SDimitry Andric case 'h':
7454706b4fc4SDimitry Andric window.CreateHelpSubwindow();
7455706b4fc4SDimitry Andric return eKeyHandled;
7456706b4fc4SDimitry Andric
7457706b4fc4SDimitry Andric default:
7458706b4fc4SDimitry Andric break;
7459706b4fc4SDimitry Andric }
7460706b4fc4SDimitry Andric return eKeyNotHandled;
7461706b4fc4SDimitry Andric }
7462706b4fc4SDimitry Andric
ToggleBreakpointOnSelectedLine()7463b60736ecSDimitry Andric void ToggleBreakpointOnSelectedLine() {
7464b60736ecSDimitry Andric ExecutionContext exe_ctx =
7465b60736ecSDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
7466b60736ecSDimitry Andric if (!exe_ctx.HasTargetScope())
7467b60736ecSDimitry Andric return;
7468b60736ecSDimitry Andric if (GetNumSourceLines() > 0) {
7469b60736ecSDimitry Andric // Source file breakpoint.
7470b60736ecSDimitry Andric BreakpointList &bp_list = exe_ctx.GetTargetRef().GetBreakpointList();
7471b60736ecSDimitry Andric const size_t num_bps = bp_list.GetSize();
7472b60736ecSDimitry Andric for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
7473b60736ecSDimitry Andric BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
7474b60736ecSDimitry Andric const size_t num_bps_locs = bp_sp->GetNumLocations();
7475b60736ecSDimitry Andric for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
7476b60736ecSDimitry Andric BreakpointLocationSP bp_loc_sp =
7477b60736ecSDimitry Andric bp_sp->GetLocationAtIndex(bp_loc_idx);
7478b60736ecSDimitry Andric LineEntry bp_loc_line_entry;
7479b60736ecSDimitry Andric if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry(
7480b60736ecSDimitry Andric bp_loc_line_entry)) {
7481ac9a064cSDimitry Andric if (m_file_sp->GetFileSpec() == bp_loc_line_entry.GetFile() &&
7482b60736ecSDimitry Andric m_selected_line + 1 == bp_loc_line_entry.line) {
7483b60736ecSDimitry Andric bool removed =
7484b60736ecSDimitry Andric exe_ctx.GetTargetRef().RemoveBreakpointByID(bp_sp->GetID());
7485b60736ecSDimitry Andric assert(removed);
7486b60736ecSDimitry Andric UNUSED_IF_ASSERT_DISABLED(removed);
7487b60736ecSDimitry Andric return; // Existing breakpoint removed.
7488b60736ecSDimitry Andric }
7489b60736ecSDimitry Andric }
7490b60736ecSDimitry Andric }
7491b60736ecSDimitry Andric }
7492b60736ecSDimitry Andric // No breakpoint found on the location, add it.
7493b60736ecSDimitry Andric BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
7494b60736ecSDimitry Andric nullptr, // Don't limit the breakpoint to certain modules
7495b60736ecSDimitry Andric m_file_sp->GetFileSpec(), // Source file
7496b60736ecSDimitry Andric m_selected_line +
7497b60736ecSDimitry Andric 1, // Source line number (m_selected_line is zero based)
7498b60736ecSDimitry Andric 0, // No column specified.
7499b60736ecSDimitry Andric 0, // No offset
7500b60736ecSDimitry Andric eLazyBoolCalculate, // Check inlines using global setting
7501b60736ecSDimitry Andric eLazyBoolCalculate, // Skip prologue using global setting,
7502b60736ecSDimitry Andric false, // internal
7503b60736ecSDimitry Andric false, // request_hardware
7504b60736ecSDimitry Andric eLazyBoolCalculate); // move_to_nearest_code
7505b60736ecSDimitry Andric } else {
7506b60736ecSDimitry Andric // Disassembly breakpoint.
7507b60736ecSDimitry Andric assert(GetNumDisassemblyLines() > 0);
7508b60736ecSDimitry Andric assert(m_selected_line < GetNumDisassemblyLines());
7509b60736ecSDimitry Andric const Instruction *inst = m_disassembly_sp->GetInstructionList()
7510b60736ecSDimitry Andric .GetInstructionAtIndex(m_selected_line)
7511b60736ecSDimitry Andric .get();
7512b60736ecSDimitry Andric Address addr = inst->GetAddress();
7513b60736ecSDimitry Andric // Try to find it.
7514b60736ecSDimitry Andric BreakpointList &bp_list = exe_ctx.GetTargetRef().GetBreakpointList();
7515b60736ecSDimitry Andric const size_t num_bps = bp_list.GetSize();
7516b60736ecSDimitry Andric for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
7517b60736ecSDimitry Andric BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
7518b60736ecSDimitry Andric const size_t num_bps_locs = bp_sp->GetNumLocations();
7519b60736ecSDimitry Andric for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
7520b60736ecSDimitry Andric BreakpointLocationSP bp_loc_sp =
7521b60736ecSDimitry Andric bp_sp->GetLocationAtIndex(bp_loc_idx);
7522b60736ecSDimitry Andric LineEntry bp_loc_line_entry;
7523b60736ecSDimitry Andric const lldb::addr_t file_addr =
7524b60736ecSDimitry Andric bp_loc_sp->GetAddress().GetFileAddress();
7525b60736ecSDimitry Andric if (file_addr == addr.GetFileAddress()) {
7526b60736ecSDimitry Andric bool removed =
7527b60736ecSDimitry Andric exe_ctx.GetTargetRef().RemoveBreakpointByID(bp_sp->GetID());
7528b60736ecSDimitry Andric assert(removed);
7529b60736ecSDimitry Andric UNUSED_IF_ASSERT_DISABLED(removed);
7530b60736ecSDimitry Andric return; // Existing breakpoint removed.
7531b60736ecSDimitry Andric }
7532b60736ecSDimitry Andric }
7533b60736ecSDimitry Andric }
7534b60736ecSDimitry Andric // No breakpoint found on the address, add it.
7535b60736ecSDimitry Andric BreakpointSP bp_sp =
7536b60736ecSDimitry Andric exe_ctx.GetTargetRef().CreateBreakpoint(addr, // lldb_private::Address
7537b60736ecSDimitry Andric false, // internal
7538b60736ecSDimitry Andric false); // request_hardware
7539b60736ecSDimitry Andric }
7540b60736ecSDimitry Andric }
7541b60736ecSDimitry Andric
7542706b4fc4SDimitry Andric protected:
7543706b4fc4SDimitry Andric typedef std::set<uint32_t> BreakpointLines;
7544706b4fc4SDimitry Andric typedef std::set<lldb::addr_t> BreakpointAddrs;
7545706b4fc4SDimitry Andric
7546706b4fc4SDimitry Andric Debugger &m_debugger;
7547706b4fc4SDimitry Andric SymbolContext m_sc;
7548706b4fc4SDimitry Andric SourceManager::FileSP m_file_sp;
7549145449b1SDimitry Andric SymbolContextScope *m_disassembly_scope = nullptr;
7550706b4fc4SDimitry Andric lldb::DisassemblerSP m_disassembly_sp;
7551706b4fc4SDimitry Andric AddressRange m_disassembly_range;
7552706b4fc4SDimitry Andric StreamString m_title;
7553145449b1SDimitry Andric lldb::user_id_t m_tid = LLDB_INVALID_THREAD_ID;
7554145449b1SDimitry Andric int m_line_width = 4;
7555145449b1SDimitry Andric uint32_t m_selected_line = 0; // The selected line
7556145449b1SDimitry Andric uint32_t m_pc_line = 0; // The line with the PC
7557145449b1SDimitry Andric uint32_t m_stop_id = 0;
7558145449b1SDimitry Andric uint32_t m_frame_idx = UINT32_MAX;
7559145449b1SDimitry Andric int m_first_visible_line = 0;
7560145449b1SDimitry Andric int m_first_visible_column = 0;
7561145449b1SDimitry Andric int m_min_x = 0;
7562145449b1SDimitry Andric int m_min_y = 0;
7563145449b1SDimitry Andric int m_max_x = 0;
7564145449b1SDimitry Andric int m_max_y = 0;
7565706b4fc4SDimitry Andric };
7566706b4fc4SDimitry Andric
7567706b4fc4SDimitry Andric DisplayOptions ValueObjectListDelegate::g_options = {true};
7568706b4fc4SDimitry Andric
IOHandlerCursesGUI(Debugger & debugger)7569706b4fc4SDimitry Andric IOHandlerCursesGUI::IOHandlerCursesGUI(Debugger &debugger)
7570706b4fc4SDimitry Andric : IOHandler(debugger, IOHandler::Type::Curses) {}
7571706b4fc4SDimitry Andric
Activate()7572706b4fc4SDimitry Andric void IOHandlerCursesGUI::Activate() {
7573706b4fc4SDimitry Andric IOHandler::Activate();
7574706b4fc4SDimitry Andric if (!m_app_ap) {
7575cfca06d7SDimitry Andric m_app_ap = std::make_unique<Application>(GetInputFILE(), GetOutputFILE());
7576706b4fc4SDimitry Andric
7577706b4fc4SDimitry Andric // This is both a window and a menu delegate
7578706b4fc4SDimitry Andric std::shared_ptr<ApplicationDelegate> app_delegate_sp(
7579706b4fc4SDimitry Andric new ApplicationDelegate(*m_app_ap, m_debugger));
7580706b4fc4SDimitry Andric
7581706b4fc4SDimitry Andric MenuDelegateSP app_menu_delegate_sp =
7582706b4fc4SDimitry Andric std::static_pointer_cast<MenuDelegate>(app_delegate_sp);
7583706b4fc4SDimitry Andric MenuSP lldb_menu_sp(
7584706b4fc4SDimitry Andric new Menu("LLDB", "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB));
7585706b4fc4SDimitry Andric MenuSP exit_menuitem_sp(
7586706b4fc4SDimitry Andric new Menu("Exit", nullptr, 'x', ApplicationDelegate::eMenuID_LLDBExit));
7587706b4fc4SDimitry Andric exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit);
7588706b4fc4SDimitry Andric lldb_menu_sp->AddSubmenu(MenuSP(new Menu(
7589706b4fc4SDimitry Andric "About LLDB", nullptr, 'a', ApplicationDelegate::eMenuID_LLDBAbout)));
7590706b4fc4SDimitry Andric lldb_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
7591706b4fc4SDimitry Andric lldb_menu_sp->AddSubmenu(exit_menuitem_sp);
7592706b4fc4SDimitry Andric
7593706b4fc4SDimitry Andric MenuSP target_menu_sp(new Menu("Target", "F2", KEY_F(2),
7594706b4fc4SDimitry Andric ApplicationDelegate::eMenuID_Target));
7595706b4fc4SDimitry Andric target_menu_sp->AddSubmenu(MenuSP(new Menu(
7596706b4fc4SDimitry Andric "Create", nullptr, 'c', ApplicationDelegate::eMenuID_TargetCreate)));
7597706b4fc4SDimitry Andric target_menu_sp->AddSubmenu(MenuSP(new Menu(
7598706b4fc4SDimitry Andric "Delete", nullptr, 'd', ApplicationDelegate::eMenuID_TargetDelete)));
7599706b4fc4SDimitry Andric
7600706b4fc4SDimitry Andric MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3),
7601706b4fc4SDimitry Andric ApplicationDelegate::eMenuID_Process));
7602706b4fc4SDimitry Andric process_menu_sp->AddSubmenu(MenuSP(new Menu(
7603706b4fc4SDimitry Andric "Attach", nullptr, 'a', ApplicationDelegate::eMenuID_ProcessAttach)));
7604b60736ecSDimitry Andric process_menu_sp->AddSubmenu(
7605b60736ecSDimitry Andric MenuSP(new Menu("Detach and resume", nullptr, 'd',
7606b60736ecSDimitry Andric ApplicationDelegate::eMenuID_ProcessDetachResume)));
7607b60736ecSDimitry Andric process_menu_sp->AddSubmenu(
7608b60736ecSDimitry Andric MenuSP(new Menu("Detach suspended", nullptr, 's',
7609b60736ecSDimitry Andric ApplicationDelegate::eMenuID_ProcessDetachSuspended)));
7610706b4fc4SDimitry Andric process_menu_sp->AddSubmenu(MenuSP(new Menu(
7611706b4fc4SDimitry Andric "Launch", nullptr, 'l', ApplicationDelegate::eMenuID_ProcessLaunch)));
7612706b4fc4SDimitry Andric process_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
7613706b4fc4SDimitry Andric process_menu_sp->AddSubmenu(
7614706b4fc4SDimitry Andric MenuSP(new Menu("Continue", nullptr, 'c',
7615706b4fc4SDimitry Andric ApplicationDelegate::eMenuID_ProcessContinue)));
7616706b4fc4SDimitry Andric process_menu_sp->AddSubmenu(MenuSP(new Menu(
7617706b4fc4SDimitry Andric "Halt", nullptr, 'h', ApplicationDelegate::eMenuID_ProcessHalt)));
7618706b4fc4SDimitry Andric process_menu_sp->AddSubmenu(MenuSP(new Menu(
7619706b4fc4SDimitry Andric "Kill", nullptr, 'k', ApplicationDelegate::eMenuID_ProcessKill)));
7620706b4fc4SDimitry Andric
7621706b4fc4SDimitry Andric MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4),
7622706b4fc4SDimitry Andric ApplicationDelegate::eMenuID_Thread));
7623706b4fc4SDimitry Andric thread_menu_sp->AddSubmenu(MenuSP(new Menu(
7624706b4fc4SDimitry Andric "Step In", nullptr, 'i', ApplicationDelegate::eMenuID_ThreadStepIn)));
7625706b4fc4SDimitry Andric thread_menu_sp->AddSubmenu(
7626706b4fc4SDimitry Andric MenuSP(new Menu("Step Over", nullptr, 'v',
7627706b4fc4SDimitry Andric ApplicationDelegate::eMenuID_ThreadStepOver)));
7628706b4fc4SDimitry Andric thread_menu_sp->AddSubmenu(MenuSP(new Menu(
7629706b4fc4SDimitry Andric "Step Out", nullptr, 'o', ApplicationDelegate::eMenuID_ThreadStepOut)));
7630706b4fc4SDimitry Andric
7631706b4fc4SDimitry Andric MenuSP view_menu_sp(
7632706b4fc4SDimitry Andric new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View));
7633706b4fc4SDimitry Andric view_menu_sp->AddSubmenu(
7634c0981da4SDimitry Andric MenuSP(new Menu("Backtrace", nullptr, 't',
7635706b4fc4SDimitry Andric ApplicationDelegate::eMenuID_ViewBacktrace)));
7636706b4fc4SDimitry Andric view_menu_sp->AddSubmenu(
7637706b4fc4SDimitry Andric MenuSP(new Menu("Registers", nullptr, 'r',
7638706b4fc4SDimitry Andric ApplicationDelegate::eMenuID_ViewRegisters)));
7639706b4fc4SDimitry Andric view_menu_sp->AddSubmenu(MenuSP(new Menu(
7640706b4fc4SDimitry Andric "Source", nullptr, 's', ApplicationDelegate::eMenuID_ViewSource)));
7641706b4fc4SDimitry Andric view_menu_sp->AddSubmenu(
7642706b4fc4SDimitry Andric MenuSP(new Menu("Variables", nullptr, 'v',
7643706b4fc4SDimitry Andric ApplicationDelegate::eMenuID_ViewVariables)));
7644c0981da4SDimitry Andric view_menu_sp->AddSubmenu(
7645c0981da4SDimitry Andric MenuSP(new Menu("Breakpoints", nullptr, 'b',
7646c0981da4SDimitry Andric ApplicationDelegate::eMenuID_ViewBreakpoints)));
7647706b4fc4SDimitry Andric
7648706b4fc4SDimitry Andric MenuSP help_menu_sp(
7649706b4fc4SDimitry Andric new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help));
7650706b4fc4SDimitry Andric help_menu_sp->AddSubmenu(MenuSP(new Menu(
7651706b4fc4SDimitry Andric "GUI Help", nullptr, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp)));
7652706b4fc4SDimitry Andric
7653706b4fc4SDimitry Andric m_app_ap->Initialize();
7654706b4fc4SDimitry Andric WindowSP &main_window_sp = m_app_ap->GetMainWindow();
7655706b4fc4SDimitry Andric
7656706b4fc4SDimitry Andric MenuSP menubar_sp(new Menu(Menu::Type::Bar));
7657706b4fc4SDimitry Andric menubar_sp->AddSubmenu(lldb_menu_sp);
7658706b4fc4SDimitry Andric menubar_sp->AddSubmenu(target_menu_sp);
7659706b4fc4SDimitry Andric menubar_sp->AddSubmenu(process_menu_sp);
7660706b4fc4SDimitry Andric menubar_sp->AddSubmenu(thread_menu_sp);
7661706b4fc4SDimitry Andric menubar_sp->AddSubmenu(view_menu_sp);
7662706b4fc4SDimitry Andric menubar_sp->AddSubmenu(help_menu_sp);
7663706b4fc4SDimitry Andric menubar_sp->SetDelegate(app_menu_delegate_sp);
7664706b4fc4SDimitry Andric
7665706b4fc4SDimitry Andric Rect content_bounds = main_window_sp->GetFrame();
7666706b4fc4SDimitry Andric Rect menubar_bounds = content_bounds.MakeMenuBar();
7667706b4fc4SDimitry Andric Rect status_bounds = content_bounds.MakeStatusBar();
7668706b4fc4SDimitry Andric Rect source_bounds;
7669706b4fc4SDimitry Andric Rect variables_bounds;
7670706b4fc4SDimitry Andric Rect threads_bounds;
7671706b4fc4SDimitry Andric Rect source_variables_bounds;
7672706b4fc4SDimitry Andric content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds,
7673706b4fc4SDimitry Andric threads_bounds);
7674706b4fc4SDimitry Andric source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds,
7675706b4fc4SDimitry Andric variables_bounds);
7676706b4fc4SDimitry Andric
7677706b4fc4SDimitry Andric WindowSP menubar_window_sp =
7678706b4fc4SDimitry Andric main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false);
7679706b4fc4SDimitry Andric // Let the menubar get keys if the active window doesn't handle the keys
7680706b4fc4SDimitry Andric // that are typed so it can respond to menubar key presses.
7681706b4fc4SDimitry Andric menubar_window_sp->SetCanBeActive(
7682706b4fc4SDimitry Andric false); // Don't let the menubar become the active window
7683706b4fc4SDimitry Andric menubar_window_sp->SetDelegate(menubar_sp);
7684706b4fc4SDimitry Andric
7685706b4fc4SDimitry Andric WindowSP source_window_sp(
7686706b4fc4SDimitry Andric main_window_sp->CreateSubWindow("Source", source_bounds, true));
7687706b4fc4SDimitry Andric WindowSP variables_window_sp(
7688706b4fc4SDimitry Andric main_window_sp->CreateSubWindow("Variables", variables_bounds, false));
7689706b4fc4SDimitry Andric WindowSP threads_window_sp(
7690706b4fc4SDimitry Andric main_window_sp->CreateSubWindow("Threads", threads_bounds, false));
7691706b4fc4SDimitry Andric WindowSP status_window_sp(
7692706b4fc4SDimitry Andric main_window_sp->CreateSubWindow("Status", status_bounds, false));
7693706b4fc4SDimitry Andric status_window_sp->SetCanBeActive(
7694706b4fc4SDimitry Andric false); // Don't let the status bar become the active window
7695706b4fc4SDimitry Andric main_window_sp->SetDelegate(
7696706b4fc4SDimitry Andric std::static_pointer_cast<WindowDelegate>(app_delegate_sp));
7697706b4fc4SDimitry Andric source_window_sp->SetDelegate(
7698706b4fc4SDimitry Andric WindowDelegateSP(new SourceFileWindowDelegate(m_debugger)));
7699706b4fc4SDimitry Andric variables_window_sp->SetDelegate(
7700706b4fc4SDimitry Andric WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
7701706b4fc4SDimitry Andric TreeDelegateSP thread_delegate_sp(new ThreadsTreeDelegate(m_debugger));
7702706b4fc4SDimitry Andric threads_window_sp->SetDelegate(WindowDelegateSP(
7703706b4fc4SDimitry Andric new TreeWindowDelegate(m_debugger, thread_delegate_sp)));
7704706b4fc4SDimitry Andric status_window_sp->SetDelegate(
7705706b4fc4SDimitry Andric WindowDelegateSP(new StatusBarWindowDelegate(m_debugger)));
7706706b4fc4SDimitry Andric
7707b60736ecSDimitry Andric // All colors with black background.
7708b60736ecSDimitry Andric init_pair(1, COLOR_BLACK, COLOR_BLACK);
7709b60736ecSDimitry Andric init_pair(2, COLOR_RED, COLOR_BLACK);
7710b60736ecSDimitry Andric init_pair(3, COLOR_GREEN, COLOR_BLACK);
7711b60736ecSDimitry Andric init_pair(4, COLOR_YELLOW, COLOR_BLACK);
7712b60736ecSDimitry Andric init_pair(5, COLOR_BLUE, COLOR_BLACK);
7713b60736ecSDimitry Andric init_pair(6, COLOR_MAGENTA, COLOR_BLACK);
7714b60736ecSDimitry Andric init_pair(7, COLOR_CYAN, COLOR_BLACK);
7715b60736ecSDimitry Andric init_pair(8, COLOR_WHITE, COLOR_BLACK);
7716b60736ecSDimitry Andric // All colors with blue background.
7717b60736ecSDimitry Andric init_pair(9, COLOR_BLACK, COLOR_BLUE);
7718b60736ecSDimitry Andric init_pair(10, COLOR_RED, COLOR_BLUE);
7719b60736ecSDimitry Andric init_pair(11, COLOR_GREEN, COLOR_BLUE);
7720b60736ecSDimitry Andric init_pair(12, COLOR_YELLOW, COLOR_BLUE);
7721b60736ecSDimitry Andric init_pair(13, COLOR_BLUE, COLOR_BLUE);
7722b60736ecSDimitry Andric init_pair(14, COLOR_MAGENTA, COLOR_BLUE);
7723b60736ecSDimitry Andric init_pair(15, COLOR_CYAN, COLOR_BLUE);
7724b60736ecSDimitry Andric init_pair(16, COLOR_WHITE, COLOR_BLUE);
7725b60736ecSDimitry Andric // These must match the order in the color indexes enum.
7726b60736ecSDimitry Andric init_pair(17, COLOR_BLACK, COLOR_WHITE);
7727b60736ecSDimitry Andric init_pair(18, COLOR_MAGENTA, COLOR_WHITE);
7728b60736ecSDimitry Andric static_assert(LastColorPairIndex == 18, "Color indexes do not match.");
7729344a3780SDimitry Andric
7730344a3780SDimitry Andric define_key("\033[Z", KEY_SHIFT_TAB);
7731c0981da4SDimitry Andric define_key("\033\015", KEY_ALT_ENTER);
7732706b4fc4SDimitry Andric }
7733706b4fc4SDimitry Andric }
7734706b4fc4SDimitry Andric
Deactivate()7735706b4fc4SDimitry Andric void IOHandlerCursesGUI::Deactivate() { m_app_ap->Terminate(); }
7736706b4fc4SDimitry Andric
Run()7737706b4fc4SDimitry Andric void IOHandlerCursesGUI::Run() {
7738706b4fc4SDimitry Andric m_app_ap->Run(m_debugger);
7739706b4fc4SDimitry Andric SetIsDone(true);
7740706b4fc4SDimitry Andric }
7741706b4fc4SDimitry Andric
7742706b4fc4SDimitry Andric IOHandlerCursesGUI::~IOHandlerCursesGUI() = default;
7743706b4fc4SDimitry Andric
Cancel()7744706b4fc4SDimitry Andric void IOHandlerCursesGUI::Cancel() {}
7745706b4fc4SDimitry Andric
Interrupt()7746145449b1SDimitry Andric bool IOHandlerCursesGUI::Interrupt() {
7747145449b1SDimitry Andric return m_debugger.GetCommandInterpreter().IOHandlerInterrupt(*this);
7748145449b1SDimitry Andric }
7749706b4fc4SDimitry Andric
GotEOF()7750706b4fc4SDimitry Andric void IOHandlerCursesGUI::GotEOF() {}
7751706b4fc4SDimitry Andric
TerminalSizeChanged()7752b60736ecSDimitry Andric void IOHandlerCursesGUI::TerminalSizeChanged() {
7753b60736ecSDimitry Andric m_app_ap->TerminalSizeChanged();
7754b60736ecSDimitry Andric }
7755b60736ecSDimitry Andric
7756706b4fc4SDimitry Andric #endif // LLDB_ENABLE_CURSES
7757