1*7caa17caSRaphael Isemann //===-- IOHandlerCursesGUI.cpp ----------------------------------*- C++ -*-===//
2*7caa17caSRaphael Isemann //
3*7caa17caSRaphael Isemann // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*7caa17caSRaphael Isemann // See https://llvm.org/LICENSE.txt for license information.
5*7caa17caSRaphael Isemann // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*7caa17caSRaphael Isemann //
7*7caa17caSRaphael Isemann //===----------------------------------------------------------------------===//
8*7caa17caSRaphael Isemann 
9*7caa17caSRaphael Isemann #include "lldb/Core/IOHandlerCursesGUI.h"
10*7caa17caSRaphael Isemann 
11*7caa17caSRaphael Isemann #ifndef LLDB_DISABLE_CURSES
12*7caa17caSRaphael Isemann #include <curses.h>
13*7caa17caSRaphael Isemann #include <panel.h>
14*7caa17caSRaphael Isemann #endif
15*7caa17caSRaphael Isemann 
16*7caa17caSRaphael Isemann #if defined(__APPLE__)
17*7caa17caSRaphael Isemann #include <deque>
18*7caa17caSRaphael Isemann #endif
19*7caa17caSRaphael Isemann #include <string>
20*7caa17caSRaphael Isemann 
21*7caa17caSRaphael Isemann #include "lldb/Core/Debugger.h"
22*7caa17caSRaphael Isemann #include "lldb/Core/StreamFile.h"
23*7caa17caSRaphael Isemann #include "lldb/Host/File.h"
24*7caa17caSRaphael Isemann #include "lldb/Utility/Predicate.h"
25*7caa17caSRaphael Isemann #include "lldb/Utility/Status.h"
26*7caa17caSRaphael Isemann #include "lldb/Utility/StreamString.h"
27*7caa17caSRaphael Isemann #include "lldb/Utility/StringList.h"
28*7caa17caSRaphael Isemann #include "lldb/lldb-forward.h"
29*7caa17caSRaphael Isemann 
30*7caa17caSRaphael Isemann #include "lldb/Interpreter/CommandCompletions.h"
31*7caa17caSRaphael Isemann #include "lldb/Interpreter/CommandInterpreter.h"
32*7caa17caSRaphael Isemann 
33*7caa17caSRaphael Isemann #ifndef LLDB_DISABLE_CURSES
34*7caa17caSRaphael Isemann #include "lldb/Breakpoint/BreakpointLocation.h"
35*7caa17caSRaphael Isemann #include "lldb/Core/Module.h"
36*7caa17caSRaphael Isemann #include "lldb/Core/ValueObject.h"
37*7caa17caSRaphael Isemann #include "lldb/Core/ValueObjectRegister.h"
38*7caa17caSRaphael Isemann #include "lldb/Symbol/Block.h"
39*7caa17caSRaphael Isemann #include "lldb/Symbol/Function.h"
40*7caa17caSRaphael Isemann #include "lldb/Symbol/Symbol.h"
41*7caa17caSRaphael Isemann #include "lldb/Symbol/VariableList.h"
42*7caa17caSRaphael Isemann #include "lldb/Target/Process.h"
43*7caa17caSRaphael Isemann #include "lldb/Target/RegisterContext.h"
44*7caa17caSRaphael Isemann #include "lldb/Target/StackFrame.h"
45*7caa17caSRaphael Isemann #include "lldb/Target/StopInfo.h"
46*7caa17caSRaphael Isemann #include "lldb/Target/Target.h"
47*7caa17caSRaphael Isemann #include "lldb/Target/Thread.h"
48*7caa17caSRaphael Isemann #include "lldb/Utility/State.h"
49*7caa17caSRaphael Isemann #endif
50*7caa17caSRaphael Isemann 
51*7caa17caSRaphael Isemann #include "llvm/ADT/StringRef.h"
52*7caa17caSRaphael Isemann 
53*7caa17caSRaphael Isemann #ifdef _WIN32
54*7caa17caSRaphael Isemann #include "lldb/Host/windows/windows.h"
55*7caa17caSRaphael Isemann #endif
56*7caa17caSRaphael Isemann 
57*7caa17caSRaphael Isemann #include <memory>
58*7caa17caSRaphael Isemann #include <mutex>
59*7caa17caSRaphael Isemann 
60*7caa17caSRaphael Isemann #include <assert.h>
61*7caa17caSRaphael Isemann #include <ctype.h>
62*7caa17caSRaphael Isemann #include <errno.h>
63*7caa17caSRaphael Isemann #include <locale.h>
64*7caa17caSRaphael Isemann #include <stdint.h>
65*7caa17caSRaphael Isemann #include <stdio.h>
66*7caa17caSRaphael Isemann #include <string.h>
67*7caa17caSRaphael Isemann #include <type_traits>
68*7caa17caSRaphael Isemann 
69*7caa17caSRaphael Isemann using namespace lldb;
70*7caa17caSRaphael Isemann using namespace lldb_private;
71*7caa17caSRaphael Isemann using llvm::None;
72*7caa17caSRaphael Isemann using llvm::Optional;
73*7caa17caSRaphael Isemann using llvm::StringRef;
74*7caa17caSRaphael Isemann 
75*7caa17caSRaphael Isemann // we may want curses to be disabled for some builds for instance, windows
76*7caa17caSRaphael Isemann #ifndef LLDB_DISABLE_CURSES
77*7caa17caSRaphael Isemann 
78*7caa17caSRaphael Isemann #define KEY_RETURN 10
79*7caa17caSRaphael Isemann #define KEY_ESCAPE 27
80*7caa17caSRaphael Isemann 
81*7caa17caSRaphael Isemann namespace curses {
82*7caa17caSRaphael Isemann class Menu;
83*7caa17caSRaphael Isemann class MenuDelegate;
84*7caa17caSRaphael Isemann class Window;
85*7caa17caSRaphael Isemann class WindowDelegate;
86*7caa17caSRaphael Isemann typedef std::shared_ptr<Menu> MenuSP;
87*7caa17caSRaphael Isemann typedef std::shared_ptr<MenuDelegate> MenuDelegateSP;
88*7caa17caSRaphael Isemann typedef std::shared_ptr<Window> WindowSP;
89*7caa17caSRaphael Isemann typedef std::shared_ptr<WindowDelegate> WindowDelegateSP;
90*7caa17caSRaphael Isemann typedef std::vector<MenuSP> Menus;
91*7caa17caSRaphael Isemann typedef std::vector<WindowSP> Windows;
92*7caa17caSRaphael Isemann typedef std::vector<WindowDelegateSP> WindowDelegates;
93*7caa17caSRaphael Isemann 
94*7caa17caSRaphael Isemann #if 0
95*7caa17caSRaphael Isemann type summary add -s "x=${var.x}, y=${var.y}" curses::Point
96*7caa17caSRaphael Isemann type summary add -s "w=${var.width}, h=${var.height}" curses::Size
97*7caa17caSRaphael Isemann type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect
98*7caa17caSRaphael Isemann #endif
99*7caa17caSRaphael Isemann 
100*7caa17caSRaphael Isemann struct Point {
101*7caa17caSRaphael Isemann   int x;
102*7caa17caSRaphael Isemann   int y;
103*7caa17caSRaphael Isemann 
104*7caa17caSRaphael Isemann   Point(int _x = 0, int _y = 0) : x(_x), y(_y) {}
105*7caa17caSRaphael Isemann 
106*7caa17caSRaphael Isemann   void Clear() {
107*7caa17caSRaphael Isemann     x = 0;
108*7caa17caSRaphael Isemann     y = 0;
109*7caa17caSRaphael Isemann   }
110*7caa17caSRaphael Isemann 
111*7caa17caSRaphael Isemann   Point &operator+=(const Point &rhs) {
112*7caa17caSRaphael Isemann     x += rhs.x;
113*7caa17caSRaphael Isemann     y += rhs.y;
114*7caa17caSRaphael Isemann     return *this;
115*7caa17caSRaphael Isemann   }
116*7caa17caSRaphael Isemann 
117*7caa17caSRaphael Isemann   void Dump() { printf("(x=%i, y=%i)\n", x, y); }
118*7caa17caSRaphael Isemann };
119*7caa17caSRaphael Isemann 
120*7caa17caSRaphael Isemann bool operator==(const Point &lhs, const Point &rhs) {
121*7caa17caSRaphael Isemann   return lhs.x == rhs.x && lhs.y == rhs.y;
122*7caa17caSRaphael Isemann }
123*7caa17caSRaphael Isemann 
124*7caa17caSRaphael Isemann bool operator!=(const Point &lhs, const Point &rhs) {
125*7caa17caSRaphael Isemann   return lhs.x != rhs.x || lhs.y != rhs.y;
126*7caa17caSRaphael Isemann }
127*7caa17caSRaphael Isemann 
128*7caa17caSRaphael Isemann struct Size {
129*7caa17caSRaphael Isemann   int width;
130*7caa17caSRaphael Isemann   int height;
131*7caa17caSRaphael Isemann   Size(int w = 0, int h = 0) : width(w), height(h) {}
132*7caa17caSRaphael Isemann 
133*7caa17caSRaphael Isemann   void Clear() {
134*7caa17caSRaphael Isemann     width = 0;
135*7caa17caSRaphael Isemann     height = 0;
136*7caa17caSRaphael Isemann   }
137*7caa17caSRaphael Isemann 
138*7caa17caSRaphael Isemann   void Dump() { printf("(w=%i, h=%i)\n", width, height); }
139*7caa17caSRaphael Isemann };
140*7caa17caSRaphael Isemann 
141*7caa17caSRaphael Isemann bool operator==(const Size &lhs, const Size &rhs) {
142*7caa17caSRaphael Isemann   return lhs.width == rhs.width && lhs.height == rhs.height;
143*7caa17caSRaphael Isemann }
144*7caa17caSRaphael Isemann 
145*7caa17caSRaphael Isemann bool operator!=(const Size &lhs, const Size &rhs) {
146*7caa17caSRaphael Isemann   return lhs.width != rhs.width || lhs.height != rhs.height;
147*7caa17caSRaphael Isemann }
148*7caa17caSRaphael Isemann 
149*7caa17caSRaphael Isemann struct Rect {
150*7caa17caSRaphael Isemann   Point origin;
151*7caa17caSRaphael Isemann   Size size;
152*7caa17caSRaphael Isemann 
153*7caa17caSRaphael Isemann   Rect() : origin(), size() {}
154*7caa17caSRaphael Isemann 
155*7caa17caSRaphael Isemann   Rect(const Point &p, const Size &s) : origin(p), size(s) {}
156*7caa17caSRaphael Isemann 
157*7caa17caSRaphael Isemann   void Clear() {
158*7caa17caSRaphael Isemann     origin.Clear();
159*7caa17caSRaphael Isemann     size.Clear();
160*7caa17caSRaphael Isemann   }
161*7caa17caSRaphael Isemann 
162*7caa17caSRaphael Isemann   void Dump() {
163*7caa17caSRaphael Isemann     printf("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width,
164*7caa17caSRaphael Isemann            size.height);
165*7caa17caSRaphael Isemann   }
166*7caa17caSRaphael Isemann 
167*7caa17caSRaphael Isemann   void Inset(int w, int h) {
168*7caa17caSRaphael Isemann     if (size.width > w * 2)
169*7caa17caSRaphael Isemann       size.width -= w * 2;
170*7caa17caSRaphael Isemann     origin.x += w;
171*7caa17caSRaphael Isemann 
172*7caa17caSRaphael Isemann     if (size.height > h * 2)
173*7caa17caSRaphael Isemann       size.height -= h * 2;
174*7caa17caSRaphael Isemann     origin.y += h;
175*7caa17caSRaphael Isemann   }
176*7caa17caSRaphael Isemann 
177*7caa17caSRaphael Isemann   // Return a status bar rectangle which is the last line of this rectangle.
178*7caa17caSRaphael Isemann   // This rectangle will be modified to not include the status bar area.
179*7caa17caSRaphael Isemann   Rect MakeStatusBar() {
180*7caa17caSRaphael Isemann     Rect status_bar;
181*7caa17caSRaphael Isemann     if (size.height > 1) {
182*7caa17caSRaphael Isemann       status_bar.origin.x = origin.x;
183*7caa17caSRaphael Isemann       status_bar.origin.y = size.height;
184*7caa17caSRaphael Isemann       status_bar.size.width = size.width;
185*7caa17caSRaphael Isemann       status_bar.size.height = 1;
186*7caa17caSRaphael Isemann       --size.height;
187*7caa17caSRaphael Isemann     }
188*7caa17caSRaphael Isemann     return status_bar;
189*7caa17caSRaphael Isemann   }
190*7caa17caSRaphael Isemann 
191*7caa17caSRaphael Isemann   // Return a menubar rectangle which is the first line of this rectangle. This
192*7caa17caSRaphael Isemann   // rectangle will be modified to not include the menubar area.
193*7caa17caSRaphael Isemann   Rect MakeMenuBar() {
194*7caa17caSRaphael Isemann     Rect menubar;
195*7caa17caSRaphael Isemann     if (size.height > 1) {
196*7caa17caSRaphael Isemann       menubar.origin.x = origin.x;
197*7caa17caSRaphael Isemann       menubar.origin.y = origin.y;
198*7caa17caSRaphael Isemann       menubar.size.width = size.width;
199*7caa17caSRaphael Isemann       menubar.size.height = 1;
200*7caa17caSRaphael Isemann       ++origin.y;
201*7caa17caSRaphael Isemann       --size.height;
202*7caa17caSRaphael Isemann     }
203*7caa17caSRaphael Isemann     return menubar;
204*7caa17caSRaphael Isemann   }
205*7caa17caSRaphael Isemann 
206*7caa17caSRaphael Isemann   void HorizontalSplitPercentage(float top_percentage, Rect &top,
207*7caa17caSRaphael Isemann                                  Rect &bottom) const {
208*7caa17caSRaphael Isemann     float top_height = top_percentage * size.height;
209*7caa17caSRaphael Isemann     HorizontalSplit(top_height, top, bottom);
210*7caa17caSRaphael Isemann   }
211*7caa17caSRaphael Isemann 
212*7caa17caSRaphael Isemann   void HorizontalSplit(int top_height, Rect &top, Rect &bottom) const {
213*7caa17caSRaphael Isemann     top = *this;
214*7caa17caSRaphael Isemann     if (top_height < size.height) {
215*7caa17caSRaphael Isemann       top.size.height = top_height;
216*7caa17caSRaphael Isemann       bottom.origin.x = origin.x;
217*7caa17caSRaphael Isemann       bottom.origin.y = origin.y + top.size.height;
218*7caa17caSRaphael Isemann       bottom.size.width = size.width;
219*7caa17caSRaphael Isemann       bottom.size.height = size.height - top.size.height;
220*7caa17caSRaphael Isemann     } else {
221*7caa17caSRaphael Isemann       bottom.Clear();
222*7caa17caSRaphael Isemann     }
223*7caa17caSRaphael Isemann   }
224*7caa17caSRaphael Isemann 
225*7caa17caSRaphael Isemann   void VerticalSplitPercentage(float left_percentage, Rect &left,
226*7caa17caSRaphael Isemann                                Rect &right) const {
227*7caa17caSRaphael Isemann     float left_width = left_percentage * size.width;
228*7caa17caSRaphael Isemann     VerticalSplit(left_width, left, right);
229*7caa17caSRaphael Isemann   }
230*7caa17caSRaphael Isemann 
231*7caa17caSRaphael Isemann   void VerticalSplit(int left_width, Rect &left, Rect &right) const {
232*7caa17caSRaphael Isemann     left = *this;
233*7caa17caSRaphael Isemann     if (left_width < size.width) {
234*7caa17caSRaphael Isemann       left.size.width = left_width;
235*7caa17caSRaphael Isemann       right.origin.x = origin.x + left.size.width;
236*7caa17caSRaphael Isemann       right.origin.y = origin.y;
237*7caa17caSRaphael Isemann       right.size.width = size.width - left.size.width;
238*7caa17caSRaphael Isemann       right.size.height = size.height;
239*7caa17caSRaphael Isemann     } else {
240*7caa17caSRaphael Isemann       right.Clear();
241*7caa17caSRaphael Isemann     }
242*7caa17caSRaphael Isemann   }
243*7caa17caSRaphael Isemann };
244*7caa17caSRaphael Isemann 
245*7caa17caSRaphael Isemann bool operator==(const Rect &lhs, const Rect &rhs) {
246*7caa17caSRaphael Isemann   return lhs.origin == rhs.origin && lhs.size == rhs.size;
247*7caa17caSRaphael Isemann }
248*7caa17caSRaphael Isemann 
249*7caa17caSRaphael Isemann bool operator!=(const Rect &lhs, const Rect &rhs) {
250*7caa17caSRaphael Isemann   return lhs.origin != rhs.origin || lhs.size != rhs.size;
251*7caa17caSRaphael Isemann }
252*7caa17caSRaphael Isemann 
253*7caa17caSRaphael Isemann enum HandleCharResult {
254*7caa17caSRaphael Isemann   eKeyNotHandled = 0,
255*7caa17caSRaphael Isemann   eKeyHandled = 1,
256*7caa17caSRaphael Isemann   eQuitApplication = 2
257*7caa17caSRaphael Isemann };
258*7caa17caSRaphael Isemann 
259*7caa17caSRaphael Isemann enum class MenuActionResult {
260*7caa17caSRaphael Isemann   Handled,
261*7caa17caSRaphael Isemann   NotHandled,
262*7caa17caSRaphael Isemann   Quit // Exit all menus and quit
263*7caa17caSRaphael Isemann };
264*7caa17caSRaphael Isemann 
265*7caa17caSRaphael Isemann struct KeyHelp {
266*7caa17caSRaphael Isemann   int ch;
267*7caa17caSRaphael Isemann   const char *description;
268*7caa17caSRaphael Isemann };
269*7caa17caSRaphael Isemann 
270*7caa17caSRaphael Isemann class WindowDelegate {
271*7caa17caSRaphael Isemann public:
272*7caa17caSRaphael Isemann   virtual ~WindowDelegate() = default;
273*7caa17caSRaphael Isemann 
274*7caa17caSRaphael Isemann   virtual bool WindowDelegateDraw(Window &window, bool force) {
275*7caa17caSRaphael Isemann     return false; // Drawing not handled
276*7caa17caSRaphael Isemann   }
277*7caa17caSRaphael Isemann 
278*7caa17caSRaphael Isemann   virtual HandleCharResult WindowDelegateHandleChar(Window &window, int key) {
279*7caa17caSRaphael Isemann     return eKeyNotHandled;
280*7caa17caSRaphael Isemann   }
281*7caa17caSRaphael Isemann 
282*7caa17caSRaphael Isemann   virtual const char *WindowDelegateGetHelpText() { return nullptr; }
283*7caa17caSRaphael Isemann 
284*7caa17caSRaphael Isemann   virtual KeyHelp *WindowDelegateGetKeyHelp() { return nullptr; }
285*7caa17caSRaphael Isemann };
286*7caa17caSRaphael Isemann 
287*7caa17caSRaphael Isemann class HelpDialogDelegate : public WindowDelegate {
288*7caa17caSRaphael Isemann public:
289*7caa17caSRaphael Isemann   HelpDialogDelegate(const char *text, KeyHelp *key_help_array);
290*7caa17caSRaphael Isemann 
291*7caa17caSRaphael Isemann   ~HelpDialogDelegate() override;
292*7caa17caSRaphael Isemann 
293*7caa17caSRaphael Isemann   bool WindowDelegateDraw(Window &window, bool force) override;
294*7caa17caSRaphael Isemann 
295*7caa17caSRaphael Isemann   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override;
296*7caa17caSRaphael Isemann 
297*7caa17caSRaphael Isemann   size_t GetNumLines() const { return m_text.GetSize(); }
298*7caa17caSRaphael Isemann 
299*7caa17caSRaphael Isemann   size_t GetMaxLineLength() const { return m_text.GetMaxStringLength(); }
300*7caa17caSRaphael Isemann 
301*7caa17caSRaphael Isemann protected:
302*7caa17caSRaphael Isemann   StringList m_text;
303*7caa17caSRaphael Isemann   int m_first_visible_line;
304*7caa17caSRaphael Isemann };
305*7caa17caSRaphael Isemann 
306*7caa17caSRaphael Isemann class Window {
307*7caa17caSRaphael Isemann public:
308*7caa17caSRaphael Isemann   Window(const char *name)
309*7caa17caSRaphael Isemann       : m_name(name), m_window(nullptr), m_panel(nullptr), m_parent(nullptr),
310*7caa17caSRaphael Isemann         m_subwindows(), m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX),
311*7caa17caSRaphael Isemann         m_prev_active_window_idx(UINT32_MAX), m_delete(false),
312*7caa17caSRaphael Isemann         m_needs_update(true), m_can_activate(true), m_is_subwin(false) {}
313*7caa17caSRaphael Isemann 
314*7caa17caSRaphael Isemann   Window(const char *name, WINDOW *w, bool del = true)
315*7caa17caSRaphael Isemann       : m_name(name), m_window(nullptr), m_panel(nullptr), m_parent(nullptr),
316*7caa17caSRaphael Isemann         m_subwindows(), m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX),
317*7caa17caSRaphael Isemann         m_prev_active_window_idx(UINT32_MAX), m_delete(del),
318*7caa17caSRaphael Isemann         m_needs_update(true), m_can_activate(true), m_is_subwin(false) {
319*7caa17caSRaphael Isemann     if (w)
320*7caa17caSRaphael Isemann       Reset(w);
321*7caa17caSRaphael Isemann   }
322*7caa17caSRaphael Isemann 
323*7caa17caSRaphael Isemann   Window(const char *name, const Rect &bounds)
324*7caa17caSRaphael Isemann       : m_name(name), m_window(nullptr), m_parent(nullptr), m_subwindows(),
325*7caa17caSRaphael Isemann         m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX),
326*7caa17caSRaphael Isemann         m_prev_active_window_idx(UINT32_MAX), m_delete(true),
327*7caa17caSRaphael Isemann         m_needs_update(true), m_can_activate(true), m_is_subwin(false) {
328*7caa17caSRaphael Isemann     Reset(::newwin(bounds.size.height, bounds.size.width, bounds.origin.y,
329*7caa17caSRaphael Isemann                    bounds.origin.y));
330*7caa17caSRaphael Isemann   }
331*7caa17caSRaphael Isemann 
332*7caa17caSRaphael Isemann   virtual ~Window() {
333*7caa17caSRaphael Isemann     RemoveSubWindows();
334*7caa17caSRaphael Isemann     Reset();
335*7caa17caSRaphael Isemann   }
336*7caa17caSRaphael Isemann 
337*7caa17caSRaphael Isemann   void Reset(WINDOW *w = nullptr, bool del = true) {
338*7caa17caSRaphael Isemann     if (m_window == w)
339*7caa17caSRaphael Isemann       return;
340*7caa17caSRaphael Isemann 
341*7caa17caSRaphael Isemann     if (m_panel) {
342*7caa17caSRaphael Isemann       ::del_panel(m_panel);
343*7caa17caSRaphael Isemann       m_panel = nullptr;
344*7caa17caSRaphael Isemann     }
345*7caa17caSRaphael Isemann     if (m_window && m_delete) {
346*7caa17caSRaphael Isemann       ::delwin(m_window);
347*7caa17caSRaphael Isemann       m_window = nullptr;
348*7caa17caSRaphael Isemann       m_delete = false;
349*7caa17caSRaphael Isemann     }
350*7caa17caSRaphael Isemann     if (w) {
351*7caa17caSRaphael Isemann       m_window = w;
352*7caa17caSRaphael Isemann       m_panel = ::new_panel(m_window);
353*7caa17caSRaphael Isemann       m_delete = del;
354*7caa17caSRaphael Isemann     }
355*7caa17caSRaphael Isemann   }
356*7caa17caSRaphael Isemann 
357*7caa17caSRaphael Isemann   void AttributeOn(attr_t attr) { ::wattron(m_window, attr); }
358*7caa17caSRaphael Isemann   void AttributeOff(attr_t attr) { ::wattroff(m_window, attr); }
359*7caa17caSRaphael Isemann   void Box(chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) {
360*7caa17caSRaphael Isemann     ::box(m_window, v_char, h_char);
361*7caa17caSRaphael Isemann   }
362*7caa17caSRaphael Isemann   void Clear() { ::wclear(m_window); }
363*7caa17caSRaphael Isemann   void Erase() { ::werase(m_window); }
364*7caa17caSRaphael Isemann   Rect GetBounds() {
365*7caa17caSRaphael Isemann     return Rect(GetParentOrigin(), GetSize());
366*7caa17caSRaphael Isemann   } // Get the rectangle in our parent window
367*7caa17caSRaphael Isemann   int GetChar() { return ::wgetch(m_window); }
368*7caa17caSRaphael Isemann   int GetCursorX() { return getcurx(m_window); }
369*7caa17caSRaphael Isemann   int GetCursorY() { return getcury(m_window); }
370*7caa17caSRaphael Isemann   Rect GetFrame() {
371*7caa17caSRaphael Isemann     return Rect(Point(), GetSize());
372*7caa17caSRaphael Isemann   } // Get our rectangle in our own coordinate system
373*7caa17caSRaphael Isemann   Point GetParentOrigin() { return Point(GetParentX(), GetParentY()); }
374*7caa17caSRaphael Isemann   Size GetSize() { return Size(GetWidth(), GetHeight()); }
375*7caa17caSRaphael Isemann   int GetParentX() { return getparx(m_window); }
376*7caa17caSRaphael Isemann   int GetParentY() { return getpary(m_window); }
377*7caa17caSRaphael Isemann   int GetMaxX() { return getmaxx(m_window); }
378*7caa17caSRaphael Isemann   int GetMaxY() { return getmaxy(m_window); }
379*7caa17caSRaphael Isemann   int GetWidth() { return GetMaxX(); }
380*7caa17caSRaphael Isemann   int GetHeight() { return GetMaxY(); }
381*7caa17caSRaphael Isemann   void MoveCursor(int x, int y) { ::wmove(m_window, y, x); }
382*7caa17caSRaphael Isemann   void MoveWindow(int x, int y) { MoveWindow(Point(x, y)); }
383*7caa17caSRaphael Isemann   void Resize(int w, int h) { ::wresize(m_window, h, w); }
384*7caa17caSRaphael Isemann   void Resize(const Size &size) {
385*7caa17caSRaphael Isemann     ::wresize(m_window, size.height, size.width);
386*7caa17caSRaphael Isemann   }
387*7caa17caSRaphael Isemann   void PutChar(int ch) { ::waddch(m_window, ch); }
388*7caa17caSRaphael Isemann   void PutCString(const char *s, int len = -1) { ::waddnstr(m_window, s, len); }
389*7caa17caSRaphael Isemann   void SetBackground(int color_pair_idx) {
390*7caa17caSRaphael Isemann     ::wbkgd(m_window, COLOR_PAIR(color_pair_idx));
391*7caa17caSRaphael Isemann   }
392*7caa17caSRaphael Isemann 
393*7caa17caSRaphael Isemann   void PutCStringTruncated(const char *s, int right_pad) {
394*7caa17caSRaphael Isemann     int bytes_left = GetWidth() - GetCursorX();
395*7caa17caSRaphael Isemann     if (bytes_left > right_pad) {
396*7caa17caSRaphael Isemann       bytes_left -= right_pad;
397*7caa17caSRaphael Isemann       ::waddnstr(m_window, s, bytes_left);
398*7caa17caSRaphael Isemann     }
399*7caa17caSRaphael Isemann   }
400*7caa17caSRaphael Isemann 
401*7caa17caSRaphael Isemann   void MoveWindow(const Point &origin) {
402*7caa17caSRaphael Isemann     const bool moving_window = origin != GetParentOrigin();
403*7caa17caSRaphael Isemann     if (m_is_subwin && moving_window) {
404*7caa17caSRaphael Isemann       // Can't move subwindows, must delete and re-create
405*7caa17caSRaphael Isemann       Size size = GetSize();
406*7caa17caSRaphael Isemann       Reset(::subwin(m_parent->m_window, size.height, size.width, origin.y,
407*7caa17caSRaphael Isemann                      origin.x),
408*7caa17caSRaphael Isemann             true);
409*7caa17caSRaphael Isemann     } else {
410*7caa17caSRaphael Isemann       ::mvwin(m_window, origin.y, origin.x);
411*7caa17caSRaphael Isemann     }
412*7caa17caSRaphael Isemann   }
413*7caa17caSRaphael Isemann 
414*7caa17caSRaphael Isemann   void SetBounds(const Rect &bounds) {
415*7caa17caSRaphael Isemann     const bool moving_window = bounds.origin != GetParentOrigin();
416*7caa17caSRaphael Isemann     if (m_is_subwin && moving_window) {
417*7caa17caSRaphael Isemann       // Can't move subwindows, must delete and re-create
418*7caa17caSRaphael Isemann       Reset(::subwin(m_parent->m_window, bounds.size.height, bounds.size.width,
419*7caa17caSRaphael Isemann                      bounds.origin.y, bounds.origin.x),
420*7caa17caSRaphael Isemann             true);
421*7caa17caSRaphael Isemann     } else {
422*7caa17caSRaphael Isemann       if (moving_window)
423*7caa17caSRaphael Isemann         MoveWindow(bounds.origin);
424*7caa17caSRaphael Isemann       Resize(bounds.size);
425*7caa17caSRaphael Isemann     }
426*7caa17caSRaphael Isemann   }
427*7caa17caSRaphael Isemann 
428*7caa17caSRaphael Isemann   void Printf(const char *format, ...) __attribute__((format(printf, 2, 3))) {
429*7caa17caSRaphael Isemann     va_list args;
430*7caa17caSRaphael Isemann     va_start(args, format);
431*7caa17caSRaphael Isemann     vwprintw(m_window, format, args);
432*7caa17caSRaphael Isemann     va_end(args);
433*7caa17caSRaphael Isemann   }
434*7caa17caSRaphael Isemann 
435*7caa17caSRaphael Isemann   void Touch() {
436*7caa17caSRaphael Isemann     ::touchwin(m_window);
437*7caa17caSRaphael Isemann     if (m_parent)
438*7caa17caSRaphael Isemann       m_parent->Touch();
439*7caa17caSRaphael Isemann   }
440*7caa17caSRaphael Isemann 
441*7caa17caSRaphael Isemann   WindowSP CreateSubWindow(const char *name, const Rect &bounds,
442*7caa17caSRaphael Isemann                            bool make_active) {
443*7caa17caSRaphael Isemann     auto get_window = [this, &bounds]() {
444*7caa17caSRaphael Isemann       return m_window
445*7caa17caSRaphael Isemann                  ? ::subwin(m_window, bounds.size.height, bounds.size.width,
446*7caa17caSRaphael Isemann                             bounds.origin.y, bounds.origin.x)
447*7caa17caSRaphael Isemann                  : ::newwin(bounds.size.height, bounds.size.width,
448*7caa17caSRaphael Isemann                             bounds.origin.y, bounds.origin.x);
449*7caa17caSRaphael Isemann     };
450*7caa17caSRaphael Isemann     WindowSP subwindow_sp = std::make_shared<Window>(name, get_window(), true);
451*7caa17caSRaphael Isemann     subwindow_sp->m_is_subwin = subwindow_sp.operator bool();
452*7caa17caSRaphael Isemann     subwindow_sp->m_parent = this;
453*7caa17caSRaphael Isemann     if (make_active) {
454*7caa17caSRaphael Isemann       m_prev_active_window_idx = m_curr_active_window_idx;
455*7caa17caSRaphael Isemann       m_curr_active_window_idx = m_subwindows.size();
456*7caa17caSRaphael Isemann     }
457*7caa17caSRaphael Isemann     m_subwindows.push_back(subwindow_sp);
458*7caa17caSRaphael Isemann     ::top_panel(subwindow_sp->m_panel);
459*7caa17caSRaphael Isemann     m_needs_update = true;
460*7caa17caSRaphael Isemann     return subwindow_sp;
461*7caa17caSRaphael Isemann   }
462*7caa17caSRaphael Isemann 
463*7caa17caSRaphael Isemann   bool RemoveSubWindow(Window *window) {
464*7caa17caSRaphael Isemann     Windows::iterator pos, end = m_subwindows.end();
465*7caa17caSRaphael Isemann     size_t i = 0;
466*7caa17caSRaphael Isemann     for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) {
467*7caa17caSRaphael Isemann       if ((*pos).get() == window) {
468*7caa17caSRaphael Isemann         if (m_prev_active_window_idx == i)
469*7caa17caSRaphael Isemann           m_prev_active_window_idx = UINT32_MAX;
470*7caa17caSRaphael Isemann         else if (m_prev_active_window_idx != UINT32_MAX &&
471*7caa17caSRaphael Isemann                  m_prev_active_window_idx > i)
472*7caa17caSRaphael Isemann           --m_prev_active_window_idx;
473*7caa17caSRaphael Isemann 
474*7caa17caSRaphael Isemann         if (m_curr_active_window_idx == i)
475*7caa17caSRaphael Isemann           m_curr_active_window_idx = UINT32_MAX;
476*7caa17caSRaphael Isemann         else if (m_curr_active_window_idx != UINT32_MAX &&
477*7caa17caSRaphael Isemann                  m_curr_active_window_idx > i)
478*7caa17caSRaphael Isemann           --m_curr_active_window_idx;
479*7caa17caSRaphael Isemann         window->Erase();
480*7caa17caSRaphael Isemann         m_subwindows.erase(pos);
481*7caa17caSRaphael Isemann         m_needs_update = true;
482*7caa17caSRaphael Isemann         if (m_parent)
483*7caa17caSRaphael Isemann           m_parent->Touch();
484*7caa17caSRaphael Isemann         else
485*7caa17caSRaphael Isemann           ::touchwin(stdscr);
486*7caa17caSRaphael Isemann         return true;
487*7caa17caSRaphael Isemann       }
488*7caa17caSRaphael Isemann     }
489*7caa17caSRaphael Isemann     return false;
490*7caa17caSRaphael Isemann   }
491*7caa17caSRaphael Isemann 
492*7caa17caSRaphael Isemann   WindowSP FindSubWindow(const char *name) {
493*7caa17caSRaphael Isemann     Windows::iterator pos, end = m_subwindows.end();
494*7caa17caSRaphael Isemann     size_t i = 0;
495*7caa17caSRaphael Isemann     for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) {
496*7caa17caSRaphael Isemann       if ((*pos)->m_name == name)
497*7caa17caSRaphael Isemann         return *pos;
498*7caa17caSRaphael Isemann     }
499*7caa17caSRaphael Isemann     return WindowSP();
500*7caa17caSRaphael Isemann   }
501*7caa17caSRaphael Isemann 
502*7caa17caSRaphael Isemann   void RemoveSubWindows() {
503*7caa17caSRaphael Isemann     m_curr_active_window_idx = UINT32_MAX;
504*7caa17caSRaphael Isemann     m_prev_active_window_idx = UINT32_MAX;
505*7caa17caSRaphael Isemann     for (Windows::iterator pos = m_subwindows.begin();
506*7caa17caSRaphael Isemann          pos != m_subwindows.end(); pos = m_subwindows.erase(pos)) {
507*7caa17caSRaphael Isemann       (*pos)->Erase();
508*7caa17caSRaphael Isemann     }
509*7caa17caSRaphael Isemann     if (m_parent)
510*7caa17caSRaphael Isemann       m_parent->Touch();
511*7caa17caSRaphael Isemann     else
512*7caa17caSRaphael Isemann       ::touchwin(stdscr);
513*7caa17caSRaphael Isemann   }
514*7caa17caSRaphael Isemann 
515*7caa17caSRaphael Isemann   WINDOW *get() { return m_window; }
516*7caa17caSRaphael Isemann 
517*7caa17caSRaphael Isemann   operator WINDOW *() { return m_window; }
518*7caa17caSRaphael Isemann 
519*7caa17caSRaphael Isemann   // Window drawing utilities
520*7caa17caSRaphael Isemann   void DrawTitleBox(const char *title, const char *bottom_message = nullptr) {
521*7caa17caSRaphael Isemann     attr_t attr = 0;
522*7caa17caSRaphael Isemann     if (IsActive())
523*7caa17caSRaphael Isemann       attr = A_BOLD | COLOR_PAIR(2);
524*7caa17caSRaphael Isemann     else
525*7caa17caSRaphael Isemann       attr = 0;
526*7caa17caSRaphael Isemann     if (attr)
527*7caa17caSRaphael Isemann       AttributeOn(attr);
528*7caa17caSRaphael Isemann 
529*7caa17caSRaphael Isemann     Box();
530*7caa17caSRaphael Isemann     MoveCursor(3, 0);
531*7caa17caSRaphael Isemann 
532*7caa17caSRaphael Isemann     if (title && title[0]) {
533*7caa17caSRaphael Isemann       PutChar('<');
534*7caa17caSRaphael Isemann       PutCString(title);
535*7caa17caSRaphael Isemann       PutChar('>');
536*7caa17caSRaphael Isemann     }
537*7caa17caSRaphael Isemann 
538*7caa17caSRaphael Isemann     if (bottom_message && bottom_message[0]) {
539*7caa17caSRaphael Isemann       int bottom_message_length = strlen(bottom_message);
540*7caa17caSRaphael Isemann       int x = GetWidth() - 3 - (bottom_message_length + 2);
541*7caa17caSRaphael Isemann 
542*7caa17caSRaphael Isemann       if (x > 0) {
543*7caa17caSRaphael Isemann         MoveCursor(x, GetHeight() - 1);
544*7caa17caSRaphael Isemann         PutChar('[');
545*7caa17caSRaphael Isemann         PutCString(bottom_message);
546*7caa17caSRaphael Isemann         PutChar(']');
547*7caa17caSRaphael Isemann       } else {
548*7caa17caSRaphael Isemann         MoveCursor(1, GetHeight() - 1);
549*7caa17caSRaphael Isemann         PutChar('[');
550*7caa17caSRaphael Isemann         PutCStringTruncated(bottom_message, 1);
551*7caa17caSRaphael Isemann       }
552*7caa17caSRaphael Isemann     }
553*7caa17caSRaphael Isemann     if (attr)
554*7caa17caSRaphael Isemann       AttributeOff(attr);
555*7caa17caSRaphael Isemann   }
556*7caa17caSRaphael Isemann 
557*7caa17caSRaphael Isemann   virtual void Draw(bool force) {
558*7caa17caSRaphael Isemann     if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw(*this, force))
559*7caa17caSRaphael Isemann       return;
560*7caa17caSRaphael Isemann 
561*7caa17caSRaphael Isemann     for (auto &subwindow_sp : m_subwindows)
562*7caa17caSRaphael Isemann       subwindow_sp->Draw(force);
563*7caa17caSRaphael Isemann   }
564*7caa17caSRaphael Isemann 
565*7caa17caSRaphael Isemann   bool CreateHelpSubwindow() {
566*7caa17caSRaphael Isemann     if (m_delegate_sp) {
567*7caa17caSRaphael Isemann       const char *text = m_delegate_sp->WindowDelegateGetHelpText();
568*7caa17caSRaphael Isemann       KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp();
569*7caa17caSRaphael Isemann       if ((text && text[0]) || key_help) {
570*7caa17caSRaphael Isemann         std::unique_ptr<HelpDialogDelegate> help_delegate_up(
571*7caa17caSRaphael Isemann             new HelpDialogDelegate(text, key_help));
572*7caa17caSRaphael Isemann         const size_t num_lines = help_delegate_up->GetNumLines();
573*7caa17caSRaphael Isemann         const size_t max_length = help_delegate_up->GetMaxLineLength();
574*7caa17caSRaphael Isemann         Rect bounds = GetBounds();
575*7caa17caSRaphael Isemann         bounds.Inset(1, 1);
576*7caa17caSRaphael Isemann         if (max_length + 4 < static_cast<size_t>(bounds.size.width)) {
577*7caa17caSRaphael Isemann           bounds.origin.x += (bounds.size.width - max_length + 4) / 2;
578*7caa17caSRaphael Isemann           bounds.size.width = max_length + 4;
579*7caa17caSRaphael Isemann         } else {
580*7caa17caSRaphael Isemann           if (bounds.size.width > 100) {
581*7caa17caSRaphael Isemann             const int inset_w = bounds.size.width / 4;
582*7caa17caSRaphael Isemann             bounds.origin.x += inset_w;
583*7caa17caSRaphael Isemann             bounds.size.width -= 2 * inset_w;
584*7caa17caSRaphael Isemann           }
585*7caa17caSRaphael Isemann         }
586*7caa17caSRaphael Isemann 
587*7caa17caSRaphael Isemann         if (num_lines + 2 < static_cast<size_t>(bounds.size.height)) {
588*7caa17caSRaphael Isemann           bounds.origin.y += (bounds.size.height - num_lines + 2) / 2;
589*7caa17caSRaphael Isemann           bounds.size.height = num_lines + 2;
590*7caa17caSRaphael Isemann         } else {
591*7caa17caSRaphael Isemann           if (bounds.size.height > 100) {
592*7caa17caSRaphael Isemann             const int inset_h = bounds.size.height / 4;
593*7caa17caSRaphael Isemann             bounds.origin.y += inset_h;
594*7caa17caSRaphael Isemann             bounds.size.height -= 2 * inset_h;
595*7caa17caSRaphael Isemann           }
596*7caa17caSRaphael Isemann         }
597*7caa17caSRaphael Isemann         WindowSP help_window_sp;
598*7caa17caSRaphael Isemann         Window *parent_window = GetParent();
599*7caa17caSRaphael Isemann         if (parent_window)
600*7caa17caSRaphael Isemann           help_window_sp = parent_window->CreateSubWindow("Help", bounds, true);
601*7caa17caSRaphael Isemann         else
602*7caa17caSRaphael Isemann           help_window_sp = CreateSubWindow("Help", bounds, true);
603*7caa17caSRaphael Isemann         help_window_sp->SetDelegate(
604*7caa17caSRaphael Isemann             WindowDelegateSP(help_delegate_up.release()));
605*7caa17caSRaphael Isemann         return true;
606*7caa17caSRaphael Isemann       }
607*7caa17caSRaphael Isemann     }
608*7caa17caSRaphael Isemann     return false;
609*7caa17caSRaphael Isemann   }
610*7caa17caSRaphael Isemann 
611*7caa17caSRaphael Isemann   virtual HandleCharResult HandleChar(int key) {
612*7caa17caSRaphael Isemann     // Always check the active window first
613*7caa17caSRaphael Isemann     HandleCharResult result = eKeyNotHandled;
614*7caa17caSRaphael Isemann     WindowSP active_window_sp = GetActiveWindow();
615*7caa17caSRaphael Isemann     if (active_window_sp) {
616*7caa17caSRaphael Isemann       result = active_window_sp->HandleChar(key);
617*7caa17caSRaphael Isemann       if (result != eKeyNotHandled)
618*7caa17caSRaphael Isemann         return result;
619*7caa17caSRaphael Isemann     }
620*7caa17caSRaphael Isemann 
621*7caa17caSRaphael Isemann     if (m_delegate_sp) {
622*7caa17caSRaphael Isemann       result = m_delegate_sp->WindowDelegateHandleChar(*this, key);
623*7caa17caSRaphael Isemann       if (result != eKeyNotHandled)
624*7caa17caSRaphael Isemann         return result;
625*7caa17caSRaphael Isemann     }
626*7caa17caSRaphael Isemann 
627*7caa17caSRaphael Isemann     // Then check for any windows that want any keys that weren't handled. This
628*7caa17caSRaphael Isemann     // is typically only for a menubar. Make a copy of the subwindows in case
629*7caa17caSRaphael Isemann     // any HandleChar() functions muck with the subwindows. If we don't do
630*7caa17caSRaphael Isemann     // this, we can crash when iterating over the subwindows.
631*7caa17caSRaphael Isemann     Windows subwindows(m_subwindows);
632*7caa17caSRaphael Isemann     for (auto subwindow_sp : subwindows) {
633*7caa17caSRaphael Isemann       if (!subwindow_sp->m_can_activate) {
634*7caa17caSRaphael Isemann         HandleCharResult result = subwindow_sp->HandleChar(key);
635*7caa17caSRaphael Isemann         if (result != eKeyNotHandled)
636*7caa17caSRaphael Isemann           return result;
637*7caa17caSRaphael Isemann       }
638*7caa17caSRaphael Isemann     }
639*7caa17caSRaphael Isemann 
640*7caa17caSRaphael Isemann     return eKeyNotHandled;
641*7caa17caSRaphael Isemann   }
642*7caa17caSRaphael Isemann 
643*7caa17caSRaphael Isemann   WindowSP GetActiveWindow() {
644*7caa17caSRaphael Isemann     if (!m_subwindows.empty()) {
645*7caa17caSRaphael Isemann       if (m_curr_active_window_idx >= m_subwindows.size()) {
646*7caa17caSRaphael Isemann         if (m_prev_active_window_idx < m_subwindows.size()) {
647*7caa17caSRaphael Isemann           m_curr_active_window_idx = m_prev_active_window_idx;
648*7caa17caSRaphael Isemann           m_prev_active_window_idx = UINT32_MAX;
649*7caa17caSRaphael Isemann         } else if (IsActive()) {
650*7caa17caSRaphael Isemann           m_prev_active_window_idx = UINT32_MAX;
651*7caa17caSRaphael Isemann           m_curr_active_window_idx = UINT32_MAX;
652*7caa17caSRaphael Isemann 
653*7caa17caSRaphael Isemann           // Find first window that wants to be active if this window is active
654*7caa17caSRaphael Isemann           const size_t num_subwindows = m_subwindows.size();
655*7caa17caSRaphael Isemann           for (size_t i = 0; i < num_subwindows; ++i) {
656*7caa17caSRaphael Isemann             if (m_subwindows[i]->GetCanBeActive()) {
657*7caa17caSRaphael Isemann               m_curr_active_window_idx = i;
658*7caa17caSRaphael Isemann               break;
659*7caa17caSRaphael Isemann             }
660*7caa17caSRaphael Isemann           }
661*7caa17caSRaphael Isemann         }
662*7caa17caSRaphael Isemann       }
663*7caa17caSRaphael Isemann 
664*7caa17caSRaphael Isemann       if (m_curr_active_window_idx < m_subwindows.size())
665*7caa17caSRaphael Isemann         return m_subwindows[m_curr_active_window_idx];
666*7caa17caSRaphael Isemann     }
667*7caa17caSRaphael Isemann     return WindowSP();
668*7caa17caSRaphael Isemann   }
669*7caa17caSRaphael Isemann 
670*7caa17caSRaphael Isemann   bool GetCanBeActive() const { return m_can_activate; }
671*7caa17caSRaphael Isemann 
672*7caa17caSRaphael Isemann   void SetCanBeActive(bool b) { m_can_activate = b; }
673*7caa17caSRaphael Isemann 
674*7caa17caSRaphael Isemann   void SetDelegate(const WindowDelegateSP &delegate_sp) {
675*7caa17caSRaphael Isemann     m_delegate_sp = delegate_sp;
676*7caa17caSRaphael Isemann   }
677*7caa17caSRaphael Isemann 
678*7caa17caSRaphael Isemann   Window *GetParent() const { return m_parent; }
679*7caa17caSRaphael Isemann 
680*7caa17caSRaphael Isemann   bool IsActive() const {
681*7caa17caSRaphael Isemann     if (m_parent)
682*7caa17caSRaphael Isemann       return m_parent->GetActiveWindow().get() == this;
683*7caa17caSRaphael Isemann     else
684*7caa17caSRaphael Isemann       return true; // Top level window is always active
685*7caa17caSRaphael Isemann   }
686*7caa17caSRaphael Isemann 
687*7caa17caSRaphael Isemann   void SelectNextWindowAsActive() {
688*7caa17caSRaphael Isemann     // Move active focus to next window
689*7caa17caSRaphael Isemann     const size_t num_subwindows = m_subwindows.size();
690*7caa17caSRaphael Isemann     if (m_curr_active_window_idx == UINT32_MAX) {
691*7caa17caSRaphael Isemann       uint32_t idx = 0;
692*7caa17caSRaphael Isemann       for (auto subwindow_sp : m_subwindows) {
693*7caa17caSRaphael Isemann         if (subwindow_sp->GetCanBeActive()) {
694*7caa17caSRaphael Isemann           m_curr_active_window_idx = idx;
695*7caa17caSRaphael Isemann           break;
696*7caa17caSRaphael Isemann         }
697*7caa17caSRaphael Isemann         ++idx;
698*7caa17caSRaphael Isemann       }
699*7caa17caSRaphael Isemann     } else if (m_curr_active_window_idx + 1 < num_subwindows) {
700*7caa17caSRaphael Isemann       bool handled = false;
701*7caa17caSRaphael Isemann       m_prev_active_window_idx = m_curr_active_window_idx;
702*7caa17caSRaphael Isemann       for (size_t idx = m_curr_active_window_idx + 1; idx < num_subwindows;
703*7caa17caSRaphael Isemann            ++idx) {
704*7caa17caSRaphael Isemann         if (m_subwindows[idx]->GetCanBeActive()) {
705*7caa17caSRaphael Isemann           m_curr_active_window_idx = idx;
706*7caa17caSRaphael Isemann           handled = true;
707*7caa17caSRaphael Isemann           break;
708*7caa17caSRaphael Isemann         }
709*7caa17caSRaphael Isemann       }
710*7caa17caSRaphael Isemann       if (!handled) {
711*7caa17caSRaphael Isemann         for (size_t idx = 0; idx <= m_prev_active_window_idx; ++idx) {
712*7caa17caSRaphael Isemann           if (m_subwindows[idx]->GetCanBeActive()) {
713*7caa17caSRaphael Isemann             m_curr_active_window_idx = idx;
714*7caa17caSRaphael Isemann             break;
715*7caa17caSRaphael Isemann           }
716*7caa17caSRaphael Isemann         }
717*7caa17caSRaphael Isemann       }
718*7caa17caSRaphael Isemann     } else {
719*7caa17caSRaphael Isemann       m_prev_active_window_idx = m_curr_active_window_idx;
720*7caa17caSRaphael Isemann       for (size_t idx = 0; idx < num_subwindows; ++idx) {
721*7caa17caSRaphael Isemann         if (m_subwindows[idx]->GetCanBeActive()) {
722*7caa17caSRaphael Isemann           m_curr_active_window_idx = idx;
723*7caa17caSRaphael Isemann           break;
724*7caa17caSRaphael Isemann         }
725*7caa17caSRaphael Isemann       }
726*7caa17caSRaphael Isemann     }
727*7caa17caSRaphael Isemann   }
728*7caa17caSRaphael Isemann 
729*7caa17caSRaphael Isemann   const char *GetName() const { return m_name.c_str(); }
730*7caa17caSRaphael Isemann 
731*7caa17caSRaphael Isemann protected:
732*7caa17caSRaphael Isemann   std::string m_name;
733*7caa17caSRaphael Isemann   WINDOW *m_window;
734*7caa17caSRaphael Isemann   PANEL *m_panel;
735*7caa17caSRaphael Isemann   Window *m_parent;
736*7caa17caSRaphael Isemann   Windows m_subwindows;
737*7caa17caSRaphael Isemann   WindowDelegateSP m_delegate_sp;
738*7caa17caSRaphael Isemann   uint32_t m_curr_active_window_idx;
739*7caa17caSRaphael Isemann   uint32_t m_prev_active_window_idx;
740*7caa17caSRaphael Isemann   bool m_delete;
741*7caa17caSRaphael Isemann   bool m_needs_update;
742*7caa17caSRaphael Isemann   bool m_can_activate;
743*7caa17caSRaphael Isemann   bool m_is_subwin;
744*7caa17caSRaphael Isemann 
745*7caa17caSRaphael Isemann private:
746*7caa17caSRaphael Isemann   DISALLOW_COPY_AND_ASSIGN(Window);
747*7caa17caSRaphael Isemann };
748*7caa17caSRaphael Isemann 
749*7caa17caSRaphael Isemann class MenuDelegate {
750*7caa17caSRaphael Isemann public:
751*7caa17caSRaphael Isemann   virtual ~MenuDelegate() = default;
752*7caa17caSRaphael Isemann 
753*7caa17caSRaphael Isemann   virtual MenuActionResult MenuDelegateAction(Menu &menu) = 0;
754*7caa17caSRaphael Isemann };
755*7caa17caSRaphael Isemann 
756*7caa17caSRaphael Isemann class Menu : public WindowDelegate {
757*7caa17caSRaphael Isemann public:
758*7caa17caSRaphael Isemann   enum class Type { Invalid, Bar, Item, Separator };
759*7caa17caSRaphael Isemann 
760*7caa17caSRaphael Isemann   // Menubar or separator constructor
761*7caa17caSRaphael Isemann   Menu(Type type);
762*7caa17caSRaphael Isemann 
763*7caa17caSRaphael Isemann   // Menuitem constructor
764*7caa17caSRaphael Isemann   Menu(const char *name, const char *key_name, int key_value,
765*7caa17caSRaphael Isemann        uint64_t identifier);
766*7caa17caSRaphael Isemann 
767*7caa17caSRaphael Isemann   ~Menu() override = default;
768*7caa17caSRaphael Isemann 
769*7caa17caSRaphael Isemann   const MenuDelegateSP &GetDelegate() const { return m_delegate_sp; }
770*7caa17caSRaphael Isemann 
771*7caa17caSRaphael Isemann   void SetDelegate(const MenuDelegateSP &delegate_sp) {
772*7caa17caSRaphael Isemann     m_delegate_sp = delegate_sp;
773*7caa17caSRaphael Isemann   }
774*7caa17caSRaphael Isemann 
775*7caa17caSRaphael Isemann   void RecalculateNameLengths();
776*7caa17caSRaphael Isemann 
777*7caa17caSRaphael Isemann   void AddSubmenu(const MenuSP &menu_sp);
778*7caa17caSRaphael Isemann 
779*7caa17caSRaphael Isemann   int DrawAndRunMenu(Window &window);
780*7caa17caSRaphael Isemann 
781*7caa17caSRaphael Isemann   void DrawMenuTitle(Window &window, bool highlight);
782*7caa17caSRaphael Isemann 
783*7caa17caSRaphael Isemann   bool WindowDelegateDraw(Window &window, bool force) override;
784*7caa17caSRaphael Isemann 
785*7caa17caSRaphael Isemann   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override;
786*7caa17caSRaphael Isemann 
787*7caa17caSRaphael Isemann   MenuActionResult ActionPrivate(Menu &menu) {
788*7caa17caSRaphael Isemann     MenuActionResult result = MenuActionResult::NotHandled;
789*7caa17caSRaphael Isemann     if (m_delegate_sp) {
790*7caa17caSRaphael Isemann       result = m_delegate_sp->MenuDelegateAction(menu);
791*7caa17caSRaphael Isemann       if (result != MenuActionResult::NotHandled)
792*7caa17caSRaphael Isemann         return result;
793*7caa17caSRaphael Isemann     } else if (m_parent) {
794*7caa17caSRaphael Isemann       result = m_parent->ActionPrivate(menu);
795*7caa17caSRaphael Isemann       if (result != MenuActionResult::NotHandled)
796*7caa17caSRaphael Isemann         return result;
797*7caa17caSRaphael Isemann     }
798*7caa17caSRaphael Isemann     return m_canned_result;
799*7caa17caSRaphael Isemann   }
800*7caa17caSRaphael Isemann 
801*7caa17caSRaphael Isemann   MenuActionResult Action() {
802*7caa17caSRaphael Isemann     // Call the recursive action so it can try to handle it with the menu
803*7caa17caSRaphael Isemann     // delegate, and if not, try our parent menu
804*7caa17caSRaphael Isemann     return ActionPrivate(*this);
805*7caa17caSRaphael Isemann   }
806*7caa17caSRaphael Isemann 
807*7caa17caSRaphael Isemann   void SetCannedResult(MenuActionResult result) { m_canned_result = result; }
808*7caa17caSRaphael Isemann 
809*7caa17caSRaphael Isemann   Menus &GetSubmenus() { return m_submenus; }
810*7caa17caSRaphael Isemann 
811*7caa17caSRaphael Isemann   const Menus &GetSubmenus() const { return m_submenus; }
812*7caa17caSRaphael Isemann 
813*7caa17caSRaphael Isemann   int GetSelectedSubmenuIndex() const { return m_selected; }
814*7caa17caSRaphael Isemann 
815*7caa17caSRaphael Isemann   void SetSelectedSubmenuIndex(int idx) { m_selected = idx; }
816*7caa17caSRaphael Isemann 
817*7caa17caSRaphael Isemann   Type GetType() const { return m_type; }
818*7caa17caSRaphael Isemann 
819*7caa17caSRaphael Isemann   int GetStartingColumn() const { return m_start_col; }
820*7caa17caSRaphael Isemann 
821*7caa17caSRaphael Isemann   void SetStartingColumn(int col) { m_start_col = col; }
822*7caa17caSRaphael Isemann 
823*7caa17caSRaphael Isemann   int GetKeyValue() const { return m_key_value; }
824*7caa17caSRaphael Isemann 
825*7caa17caSRaphael Isemann   std::string &GetName() { return m_name; }
826*7caa17caSRaphael Isemann 
827*7caa17caSRaphael Isemann   int GetDrawWidth() const {
828*7caa17caSRaphael Isemann     return m_max_submenu_name_length + m_max_submenu_key_name_length + 8;
829*7caa17caSRaphael Isemann   }
830*7caa17caSRaphael Isemann 
831*7caa17caSRaphael Isemann   uint64_t GetIdentifier() const { return m_identifier; }
832*7caa17caSRaphael Isemann 
833*7caa17caSRaphael Isemann   void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
834*7caa17caSRaphael Isemann 
835*7caa17caSRaphael Isemann protected:
836*7caa17caSRaphael Isemann   std::string m_name;
837*7caa17caSRaphael Isemann   std::string m_key_name;
838*7caa17caSRaphael Isemann   uint64_t m_identifier;
839*7caa17caSRaphael Isemann   Type m_type;
840*7caa17caSRaphael Isemann   int m_key_value;
841*7caa17caSRaphael Isemann   int m_start_col;
842*7caa17caSRaphael Isemann   int m_max_submenu_name_length;
843*7caa17caSRaphael Isemann   int m_max_submenu_key_name_length;
844*7caa17caSRaphael Isemann   int m_selected;
845*7caa17caSRaphael Isemann   Menu *m_parent;
846*7caa17caSRaphael Isemann   Menus m_submenus;
847*7caa17caSRaphael Isemann   WindowSP m_menu_window_sp;
848*7caa17caSRaphael Isemann   MenuActionResult m_canned_result;
849*7caa17caSRaphael Isemann   MenuDelegateSP m_delegate_sp;
850*7caa17caSRaphael Isemann };
851*7caa17caSRaphael Isemann 
852*7caa17caSRaphael Isemann // Menubar or separator constructor
853*7caa17caSRaphael Isemann Menu::Menu(Type type)
854*7caa17caSRaphael Isemann     : m_name(), m_key_name(), m_identifier(0), m_type(type), m_key_value(0),
855*7caa17caSRaphael Isemann       m_start_col(0), m_max_submenu_name_length(0),
856*7caa17caSRaphael Isemann       m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr),
857*7caa17caSRaphael Isemann       m_submenus(), m_canned_result(MenuActionResult::NotHandled),
858*7caa17caSRaphael Isemann       m_delegate_sp() {}
859*7caa17caSRaphael Isemann 
860*7caa17caSRaphael Isemann // Menuitem constructor
861*7caa17caSRaphael Isemann Menu::Menu(const char *name, const char *key_name, int key_value,
862*7caa17caSRaphael Isemann            uint64_t identifier)
863*7caa17caSRaphael Isemann     : m_name(), m_key_name(), m_identifier(identifier), m_type(Type::Invalid),
864*7caa17caSRaphael Isemann       m_key_value(key_value), m_start_col(0), m_max_submenu_name_length(0),
865*7caa17caSRaphael Isemann       m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr),
866*7caa17caSRaphael Isemann       m_submenus(), m_canned_result(MenuActionResult::NotHandled),
867*7caa17caSRaphael Isemann       m_delegate_sp() {
868*7caa17caSRaphael Isemann   if (name && name[0]) {
869*7caa17caSRaphael Isemann     m_name = name;
870*7caa17caSRaphael Isemann     m_type = Type::Item;
871*7caa17caSRaphael Isemann     if (key_name && key_name[0])
872*7caa17caSRaphael Isemann       m_key_name = key_name;
873*7caa17caSRaphael Isemann   } else {
874*7caa17caSRaphael Isemann     m_type = Type::Separator;
875*7caa17caSRaphael Isemann   }
876*7caa17caSRaphael Isemann }
877*7caa17caSRaphael Isemann 
878*7caa17caSRaphael Isemann void Menu::RecalculateNameLengths() {
879*7caa17caSRaphael Isemann   m_max_submenu_name_length = 0;
880*7caa17caSRaphael Isemann   m_max_submenu_key_name_length = 0;
881*7caa17caSRaphael Isemann   Menus &submenus = GetSubmenus();
882*7caa17caSRaphael Isemann   const size_t num_submenus = submenus.size();
883*7caa17caSRaphael Isemann   for (size_t i = 0; i < num_submenus; ++i) {
884*7caa17caSRaphael Isemann     Menu *submenu = submenus[i].get();
885*7caa17caSRaphael Isemann     if (static_cast<size_t>(m_max_submenu_name_length) < submenu->m_name.size())
886*7caa17caSRaphael Isemann       m_max_submenu_name_length = submenu->m_name.size();
887*7caa17caSRaphael Isemann     if (static_cast<size_t>(m_max_submenu_key_name_length) <
888*7caa17caSRaphael Isemann         submenu->m_key_name.size())
889*7caa17caSRaphael Isemann       m_max_submenu_key_name_length = submenu->m_key_name.size();
890*7caa17caSRaphael Isemann   }
891*7caa17caSRaphael Isemann }
892*7caa17caSRaphael Isemann 
893*7caa17caSRaphael Isemann void Menu::AddSubmenu(const MenuSP &menu_sp) {
894*7caa17caSRaphael Isemann   menu_sp->m_parent = this;
895*7caa17caSRaphael Isemann   if (static_cast<size_t>(m_max_submenu_name_length) < menu_sp->m_name.size())
896*7caa17caSRaphael Isemann     m_max_submenu_name_length = menu_sp->m_name.size();
897*7caa17caSRaphael Isemann   if (static_cast<size_t>(m_max_submenu_key_name_length) <
898*7caa17caSRaphael Isemann       menu_sp->m_key_name.size())
899*7caa17caSRaphael Isemann     m_max_submenu_key_name_length = menu_sp->m_key_name.size();
900*7caa17caSRaphael Isemann   m_submenus.push_back(menu_sp);
901*7caa17caSRaphael Isemann }
902*7caa17caSRaphael Isemann 
903*7caa17caSRaphael Isemann void Menu::DrawMenuTitle(Window &window, bool highlight) {
904*7caa17caSRaphael Isemann   if (m_type == Type::Separator) {
905*7caa17caSRaphael Isemann     window.MoveCursor(0, window.GetCursorY());
906*7caa17caSRaphael Isemann     window.PutChar(ACS_LTEE);
907*7caa17caSRaphael Isemann     int width = window.GetWidth();
908*7caa17caSRaphael Isemann     if (width > 2) {
909*7caa17caSRaphael Isemann       width -= 2;
910*7caa17caSRaphael Isemann       for (int i = 0; i < width; ++i)
911*7caa17caSRaphael Isemann         window.PutChar(ACS_HLINE);
912*7caa17caSRaphael Isemann     }
913*7caa17caSRaphael Isemann     window.PutChar(ACS_RTEE);
914*7caa17caSRaphael Isemann   } else {
915*7caa17caSRaphael Isemann     const int shortcut_key = m_key_value;
916*7caa17caSRaphael Isemann     bool underlined_shortcut = false;
917*7caa17caSRaphael Isemann     const attr_t hilgight_attr = A_REVERSE;
918*7caa17caSRaphael Isemann     if (highlight)
919*7caa17caSRaphael Isemann       window.AttributeOn(hilgight_attr);
920*7caa17caSRaphael Isemann     if (isprint(shortcut_key)) {
921*7caa17caSRaphael Isemann       size_t lower_pos = m_name.find(tolower(shortcut_key));
922*7caa17caSRaphael Isemann       size_t upper_pos = m_name.find(toupper(shortcut_key));
923*7caa17caSRaphael Isemann       const char *name = m_name.c_str();
924*7caa17caSRaphael Isemann       size_t pos = std::min<size_t>(lower_pos, upper_pos);
925*7caa17caSRaphael Isemann       if (pos != std::string::npos) {
926*7caa17caSRaphael Isemann         underlined_shortcut = true;
927*7caa17caSRaphael Isemann         if (pos > 0) {
928*7caa17caSRaphael Isemann           window.PutCString(name, pos);
929*7caa17caSRaphael Isemann           name += pos;
930*7caa17caSRaphael Isemann         }
931*7caa17caSRaphael Isemann         const attr_t shortcut_attr = A_UNDERLINE | A_BOLD;
932*7caa17caSRaphael Isemann         window.AttributeOn(shortcut_attr);
933*7caa17caSRaphael Isemann         window.PutChar(name[0]);
934*7caa17caSRaphael Isemann         window.AttributeOff(shortcut_attr);
935*7caa17caSRaphael Isemann         name++;
936*7caa17caSRaphael Isemann         if (name[0])
937*7caa17caSRaphael Isemann           window.PutCString(name);
938*7caa17caSRaphael Isemann       }
939*7caa17caSRaphael Isemann     }
940*7caa17caSRaphael Isemann 
941*7caa17caSRaphael Isemann     if (!underlined_shortcut) {
942*7caa17caSRaphael Isemann       window.PutCString(m_name.c_str());
943*7caa17caSRaphael Isemann     }
944*7caa17caSRaphael Isemann 
945*7caa17caSRaphael Isemann     if (highlight)
946*7caa17caSRaphael Isemann       window.AttributeOff(hilgight_attr);
947*7caa17caSRaphael Isemann 
948*7caa17caSRaphael Isemann     if (m_key_name.empty()) {
949*7caa17caSRaphael Isemann       if (!underlined_shortcut && isprint(m_key_value)) {
950*7caa17caSRaphael Isemann         window.AttributeOn(COLOR_PAIR(3));
951*7caa17caSRaphael Isemann         window.Printf(" (%c)", m_key_value);
952*7caa17caSRaphael Isemann         window.AttributeOff(COLOR_PAIR(3));
953*7caa17caSRaphael Isemann       }
954*7caa17caSRaphael Isemann     } else {
955*7caa17caSRaphael Isemann       window.AttributeOn(COLOR_PAIR(3));
956*7caa17caSRaphael Isemann       window.Printf(" (%s)", m_key_name.c_str());
957*7caa17caSRaphael Isemann       window.AttributeOff(COLOR_PAIR(3));
958*7caa17caSRaphael Isemann     }
959*7caa17caSRaphael Isemann   }
960*7caa17caSRaphael Isemann }
961*7caa17caSRaphael Isemann 
962*7caa17caSRaphael Isemann bool Menu::WindowDelegateDraw(Window &window, bool force) {
963*7caa17caSRaphael Isemann   Menus &submenus = GetSubmenus();
964*7caa17caSRaphael Isemann   const size_t num_submenus = submenus.size();
965*7caa17caSRaphael Isemann   const int selected_idx = GetSelectedSubmenuIndex();
966*7caa17caSRaphael Isemann   Menu::Type menu_type = GetType();
967*7caa17caSRaphael Isemann   switch (menu_type) {
968*7caa17caSRaphael Isemann   case Menu::Type::Bar: {
969*7caa17caSRaphael Isemann     window.SetBackground(2);
970*7caa17caSRaphael Isemann     window.MoveCursor(0, 0);
971*7caa17caSRaphael Isemann     for (size_t i = 0; i < num_submenus; ++i) {
972*7caa17caSRaphael Isemann       Menu *menu = submenus[i].get();
973*7caa17caSRaphael Isemann       if (i > 0)
974*7caa17caSRaphael Isemann         window.PutChar(' ');
975*7caa17caSRaphael Isemann       menu->SetStartingColumn(window.GetCursorX());
976*7caa17caSRaphael Isemann       window.PutCString("| ");
977*7caa17caSRaphael Isemann       menu->DrawMenuTitle(window, false);
978*7caa17caSRaphael Isemann     }
979*7caa17caSRaphael Isemann     window.PutCString(" |");
980*7caa17caSRaphael Isemann   } break;
981*7caa17caSRaphael Isemann 
982*7caa17caSRaphael Isemann   case Menu::Type::Item: {
983*7caa17caSRaphael Isemann     int y = 1;
984*7caa17caSRaphael Isemann     int x = 3;
985*7caa17caSRaphael Isemann     // Draw the menu
986*7caa17caSRaphael Isemann     int cursor_x = 0;
987*7caa17caSRaphael Isemann     int cursor_y = 0;
988*7caa17caSRaphael Isemann     window.Erase();
989*7caa17caSRaphael Isemann     window.SetBackground(2);
990*7caa17caSRaphael Isemann     window.Box();
991*7caa17caSRaphael Isemann     for (size_t i = 0; i < num_submenus; ++i) {
992*7caa17caSRaphael Isemann       const bool is_selected = (i == static_cast<size_t>(selected_idx));
993*7caa17caSRaphael Isemann       window.MoveCursor(x, y + i);
994*7caa17caSRaphael Isemann       if (is_selected) {
995*7caa17caSRaphael Isemann         // Remember where we want the cursor to be
996*7caa17caSRaphael Isemann         cursor_x = x - 1;
997*7caa17caSRaphael Isemann         cursor_y = y + i;
998*7caa17caSRaphael Isemann       }
999*7caa17caSRaphael Isemann       submenus[i]->DrawMenuTitle(window, is_selected);
1000*7caa17caSRaphael Isemann     }
1001*7caa17caSRaphael Isemann     window.MoveCursor(cursor_x, cursor_y);
1002*7caa17caSRaphael Isemann   } break;
1003*7caa17caSRaphael Isemann 
1004*7caa17caSRaphael Isemann   default:
1005*7caa17caSRaphael Isemann   case Menu::Type::Separator:
1006*7caa17caSRaphael Isemann     break;
1007*7caa17caSRaphael Isemann   }
1008*7caa17caSRaphael Isemann   return true; // Drawing handled...
1009*7caa17caSRaphael Isemann }
1010*7caa17caSRaphael Isemann 
1011*7caa17caSRaphael Isemann HandleCharResult Menu::WindowDelegateHandleChar(Window &window, int key) {
1012*7caa17caSRaphael Isemann   HandleCharResult result = eKeyNotHandled;
1013*7caa17caSRaphael Isemann 
1014*7caa17caSRaphael Isemann   Menus &submenus = GetSubmenus();
1015*7caa17caSRaphael Isemann   const size_t num_submenus = submenus.size();
1016*7caa17caSRaphael Isemann   const int selected_idx = GetSelectedSubmenuIndex();
1017*7caa17caSRaphael Isemann   Menu::Type menu_type = GetType();
1018*7caa17caSRaphael Isemann   if (menu_type == Menu::Type::Bar) {
1019*7caa17caSRaphael Isemann     MenuSP run_menu_sp;
1020*7caa17caSRaphael Isemann     switch (key) {
1021*7caa17caSRaphael Isemann     case KEY_DOWN:
1022*7caa17caSRaphael Isemann     case KEY_UP:
1023*7caa17caSRaphael Isemann       // Show last menu or first menu
1024*7caa17caSRaphael Isemann       if (selected_idx < static_cast<int>(num_submenus))
1025*7caa17caSRaphael Isemann         run_menu_sp = submenus[selected_idx];
1026*7caa17caSRaphael Isemann       else if (!submenus.empty())
1027*7caa17caSRaphael Isemann         run_menu_sp = submenus.front();
1028*7caa17caSRaphael Isemann       result = eKeyHandled;
1029*7caa17caSRaphael Isemann       break;
1030*7caa17caSRaphael Isemann 
1031*7caa17caSRaphael Isemann     case KEY_RIGHT:
1032*7caa17caSRaphael Isemann       ++m_selected;
1033*7caa17caSRaphael Isemann       if (m_selected >= static_cast<int>(num_submenus))
1034*7caa17caSRaphael Isemann         m_selected = 0;
1035*7caa17caSRaphael Isemann       if (m_selected < static_cast<int>(num_submenus))
1036*7caa17caSRaphael Isemann         run_menu_sp = submenus[m_selected];
1037*7caa17caSRaphael Isemann       else if (!submenus.empty())
1038*7caa17caSRaphael Isemann         run_menu_sp = submenus.front();
1039*7caa17caSRaphael Isemann       result = eKeyHandled;
1040*7caa17caSRaphael Isemann       break;
1041*7caa17caSRaphael Isemann 
1042*7caa17caSRaphael Isemann     case KEY_LEFT:
1043*7caa17caSRaphael Isemann       --m_selected;
1044*7caa17caSRaphael Isemann       if (m_selected < 0)
1045*7caa17caSRaphael Isemann         m_selected = num_submenus - 1;
1046*7caa17caSRaphael Isemann       if (m_selected < static_cast<int>(num_submenus))
1047*7caa17caSRaphael Isemann         run_menu_sp = submenus[m_selected];
1048*7caa17caSRaphael Isemann       else if (!submenus.empty())
1049*7caa17caSRaphael Isemann         run_menu_sp = submenus.front();
1050*7caa17caSRaphael Isemann       result = eKeyHandled;
1051*7caa17caSRaphael Isemann       break;
1052*7caa17caSRaphael Isemann 
1053*7caa17caSRaphael Isemann     default:
1054*7caa17caSRaphael Isemann       for (size_t i = 0; i < num_submenus; ++i) {
1055*7caa17caSRaphael Isemann         if (submenus[i]->GetKeyValue() == key) {
1056*7caa17caSRaphael Isemann           SetSelectedSubmenuIndex(i);
1057*7caa17caSRaphael Isemann           run_menu_sp = submenus[i];
1058*7caa17caSRaphael Isemann           result = eKeyHandled;
1059*7caa17caSRaphael Isemann           break;
1060*7caa17caSRaphael Isemann         }
1061*7caa17caSRaphael Isemann       }
1062*7caa17caSRaphael Isemann       break;
1063*7caa17caSRaphael Isemann     }
1064*7caa17caSRaphael Isemann 
1065*7caa17caSRaphael Isemann     if (run_menu_sp) {
1066*7caa17caSRaphael Isemann       // Run the action on this menu in case we need to populate the menu with
1067*7caa17caSRaphael Isemann       // dynamic content and also in case check marks, and any other menu
1068*7caa17caSRaphael Isemann       // decorations need to be calculated
1069*7caa17caSRaphael Isemann       if (run_menu_sp->Action() == MenuActionResult::Quit)
1070*7caa17caSRaphael Isemann         return eQuitApplication;
1071*7caa17caSRaphael Isemann 
1072*7caa17caSRaphael Isemann       Rect menu_bounds;
1073*7caa17caSRaphael Isemann       menu_bounds.origin.x = run_menu_sp->GetStartingColumn();
1074*7caa17caSRaphael Isemann       menu_bounds.origin.y = 1;
1075*7caa17caSRaphael Isemann       menu_bounds.size.width = run_menu_sp->GetDrawWidth();
1076*7caa17caSRaphael Isemann       menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2;
1077*7caa17caSRaphael Isemann       if (m_menu_window_sp)
1078*7caa17caSRaphael Isemann         window.GetParent()->RemoveSubWindow(m_menu_window_sp.get());
1079*7caa17caSRaphael Isemann 
1080*7caa17caSRaphael Isemann       m_menu_window_sp = window.GetParent()->CreateSubWindow(
1081*7caa17caSRaphael Isemann           run_menu_sp->GetName().c_str(), menu_bounds, true);
1082*7caa17caSRaphael Isemann       m_menu_window_sp->SetDelegate(run_menu_sp);
1083*7caa17caSRaphael Isemann     }
1084*7caa17caSRaphael Isemann   } else if (menu_type == Menu::Type::Item) {
1085*7caa17caSRaphael Isemann     switch (key) {
1086*7caa17caSRaphael Isemann     case KEY_DOWN:
1087*7caa17caSRaphael Isemann       if (m_submenus.size() > 1) {
1088*7caa17caSRaphael Isemann         const int start_select = m_selected;
1089*7caa17caSRaphael Isemann         while (++m_selected != start_select) {
1090*7caa17caSRaphael Isemann           if (static_cast<size_t>(m_selected) >= num_submenus)
1091*7caa17caSRaphael Isemann             m_selected = 0;
1092*7caa17caSRaphael Isemann           if (m_submenus[m_selected]->GetType() == Type::Separator)
1093*7caa17caSRaphael Isemann             continue;
1094*7caa17caSRaphael Isemann           else
1095*7caa17caSRaphael Isemann             break;
1096*7caa17caSRaphael Isemann         }
1097*7caa17caSRaphael Isemann         return eKeyHandled;
1098*7caa17caSRaphael Isemann       }
1099*7caa17caSRaphael Isemann       break;
1100*7caa17caSRaphael Isemann 
1101*7caa17caSRaphael Isemann     case KEY_UP:
1102*7caa17caSRaphael Isemann       if (m_submenus.size() > 1) {
1103*7caa17caSRaphael Isemann         const int start_select = m_selected;
1104*7caa17caSRaphael Isemann         while (--m_selected != start_select) {
1105*7caa17caSRaphael Isemann           if (m_selected < static_cast<int>(0))
1106*7caa17caSRaphael Isemann             m_selected = num_submenus - 1;
1107*7caa17caSRaphael Isemann           if (m_submenus[m_selected]->GetType() == Type::Separator)
1108*7caa17caSRaphael Isemann             continue;
1109*7caa17caSRaphael Isemann           else
1110*7caa17caSRaphael Isemann             break;
1111*7caa17caSRaphael Isemann         }
1112*7caa17caSRaphael Isemann         return eKeyHandled;
1113*7caa17caSRaphael Isemann       }
1114*7caa17caSRaphael Isemann       break;
1115*7caa17caSRaphael Isemann 
1116*7caa17caSRaphael Isemann     case KEY_RETURN:
1117*7caa17caSRaphael Isemann       if (static_cast<size_t>(selected_idx) < num_submenus) {
1118*7caa17caSRaphael Isemann         if (submenus[selected_idx]->Action() == MenuActionResult::Quit)
1119*7caa17caSRaphael Isemann           return eQuitApplication;
1120*7caa17caSRaphael Isemann         window.GetParent()->RemoveSubWindow(&window);
1121*7caa17caSRaphael Isemann         return eKeyHandled;
1122*7caa17caSRaphael Isemann       }
1123*7caa17caSRaphael Isemann       break;
1124*7caa17caSRaphael Isemann 
1125*7caa17caSRaphael Isemann     case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in
1126*7caa17caSRaphael Isemann                      // case other chars are entered for escaped sequences
1127*7caa17caSRaphael Isemann       window.GetParent()->RemoveSubWindow(&window);
1128*7caa17caSRaphael Isemann       return eKeyHandled;
1129*7caa17caSRaphael Isemann 
1130*7caa17caSRaphael Isemann     default:
1131*7caa17caSRaphael Isemann       for (size_t i = 0; i < num_submenus; ++i) {
1132*7caa17caSRaphael Isemann         Menu *menu = submenus[i].get();
1133*7caa17caSRaphael Isemann         if (menu->GetKeyValue() == key) {
1134*7caa17caSRaphael Isemann           SetSelectedSubmenuIndex(i);
1135*7caa17caSRaphael Isemann           window.GetParent()->RemoveSubWindow(&window);
1136*7caa17caSRaphael Isemann           if (menu->Action() == MenuActionResult::Quit)
1137*7caa17caSRaphael Isemann             return eQuitApplication;
1138*7caa17caSRaphael Isemann           return eKeyHandled;
1139*7caa17caSRaphael Isemann         }
1140*7caa17caSRaphael Isemann       }
1141*7caa17caSRaphael Isemann       break;
1142*7caa17caSRaphael Isemann     }
1143*7caa17caSRaphael Isemann   } else if (menu_type == Menu::Type::Separator) {
1144*7caa17caSRaphael Isemann   }
1145*7caa17caSRaphael Isemann   return result;
1146*7caa17caSRaphael Isemann }
1147*7caa17caSRaphael Isemann 
1148*7caa17caSRaphael Isemann class Application {
1149*7caa17caSRaphael Isemann public:
1150*7caa17caSRaphael Isemann   Application(FILE *in, FILE *out)
1151*7caa17caSRaphael Isemann       : m_window_sp(), m_screen(nullptr), m_in(in), m_out(out) {}
1152*7caa17caSRaphael Isemann 
1153*7caa17caSRaphael Isemann   ~Application() {
1154*7caa17caSRaphael Isemann     m_window_delegates.clear();
1155*7caa17caSRaphael Isemann     m_window_sp.reset();
1156*7caa17caSRaphael Isemann     if (m_screen) {
1157*7caa17caSRaphael Isemann       ::delscreen(m_screen);
1158*7caa17caSRaphael Isemann       m_screen = nullptr;
1159*7caa17caSRaphael Isemann     }
1160*7caa17caSRaphael Isemann   }
1161*7caa17caSRaphael Isemann 
1162*7caa17caSRaphael Isemann   void Initialize() {
1163*7caa17caSRaphael Isemann     ::setlocale(LC_ALL, "");
1164*7caa17caSRaphael Isemann     ::setlocale(LC_CTYPE, "");
1165*7caa17caSRaphael Isemann     m_screen = ::newterm(nullptr, m_out, m_in);
1166*7caa17caSRaphael Isemann     ::start_color();
1167*7caa17caSRaphael Isemann     ::curs_set(0);
1168*7caa17caSRaphael Isemann     ::noecho();
1169*7caa17caSRaphael Isemann     ::keypad(stdscr, TRUE);
1170*7caa17caSRaphael Isemann   }
1171*7caa17caSRaphael Isemann 
1172*7caa17caSRaphael Isemann   void Terminate() { ::endwin(); }
1173*7caa17caSRaphael Isemann 
1174*7caa17caSRaphael Isemann   void Run(Debugger &debugger) {
1175*7caa17caSRaphael Isemann     bool done = false;
1176*7caa17caSRaphael Isemann     int delay_in_tenths_of_a_second = 1;
1177*7caa17caSRaphael Isemann 
1178*7caa17caSRaphael Isemann     // Alas the threading model in curses is a bit lame so we need to resort to
1179*7caa17caSRaphael Isemann     // polling every 0.5 seconds. We could poll for stdin ourselves and then
1180*7caa17caSRaphael Isemann     // pass the keys down but then we need to translate all of the escape
1181*7caa17caSRaphael Isemann     // sequences ourselves. So we resort to polling for input because we need
1182*7caa17caSRaphael Isemann     // to receive async process events while in this loop.
1183*7caa17caSRaphael Isemann 
1184*7caa17caSRaphael Isemann     halfdelay(delay_in_tenths_of_a_second); // Poll using some number of tenths
1185*7caa17caSRaphael Isemann                                             // of seconds seconds when calling
1186*7caa17caSRaphael Isemann                                             // Window::GetChar()
1187*7caa17caSRaphael Isemann 
1188*7caa17caSRaphael Isemann     ListenerSP listener_sp(
1189*7caa17caSRaphael Isemann         Listener::MakeListener("lldb.IOHandler.curses.Application"));
1190*7caa17caSRaphael Isemann     ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass());
1191*7caa17caSRaphael Isemann     ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass());
1192*7caa17caSRaphael Isemann     ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass());
1193*7caa17caSRaphael Isemann     debugger.EnableForwardEvents(listener_sp);
1194*7caa17caSRaphael Isemann 
1195*7caa17caSRaphael Isemann     bool update = true;
1196*7caa17caSRaphael Isemann #if defined(__APPLE__)
1197*7caa17caSRaphael Isemann     std::deque<int> escape_chars;
1198*7caa17caSRaphael Isemann #endif
1199*7caa17caSRaphael Isemann 
1200*7caa17caSRaphael Isemann     while (!done) {
1201*7caa17caSRaphael Isemann       if (update) {
1202*7caa17caSRaphael Isemann         m_window_sp->Draw(false);
1203*7caa17caSRaphael Isemann         // All windows should be calling Window::DeferredRefresh() instead of
1204*7caa17caSRaphael Isemann         // Window::Refresh() so we can do a single update and avoid any screen
1205*7caa17caSRaphael Isemann         // blinking
1206*7caa17caSRaphael Isemann         update_panels();
1207*7caa17caSRaphael Isemann 
1208*7caa17caSRaphael Isemann         // Cursor hiding isn't working on MacOSX, so hide it in the top left
1209*7caa17caSRaphael Isemann         // corner
1210*7caa17caSRaphael Isemann         m_window_sp->MoveCursor(0, 0);
1211*7caa17caSRaphael Isemann 
1212*7caa17caSRaphael Isemann         doupdate();
1213*7caa17caSRaphael Isemann         update = false;
1214*7caa17caSRaphael Isemann       }
1215*7caa17caSRaphael Isemann 
1216*7caa17caSRaphael Isemann #if defined(__APPLE__)
1217*7caa17caSRaphael Isemann       // Terminal.app doesn't map its function keys correctly, F1-F4 default
1218*7caa17caSRaphael Isemann       // to: \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if
1219*7caa17caSRaphael Isemann       // possible
1220*7caa17caSRaphael Isemann       int ch;
1221*7caa17caSRaphael Isemann       if (escape_chars.empty())
1222*7caa17caSRaphael Isemann         ch = m_window_sp->GetChar();
1223*7caa17caSRaphael Isemann       else {
1224*7caa17caSRaphael Isemann         ch = escape_chars.front();
1225*7caa17caSRaphael Isemann         escape_chars.pop_front();
1226*7caa17caSRaphael Isemann       }
1227*7caa17caSRaphael Isemann       if (ch == KEY_ESCAPE) {
1228*7caa17caSRaphael Isemann         int ch2 = m_window_sp->GetChar();
1229*7caa17caSRaphael Isemann         if (ch2 == 'O') {
1230*7caa17caSRaphael Isemann           int ch3 = m_window_sp->GetChar();
1231*7caa17caSRaphael Isemann           switch (ch3) {
1232*7caa17caSRaphael Isemann           case 'P':
1233*7caa17caSRaphael Isemann             ch = KEY_F(1);
1234*7caa17caSRaphael Isemann             break;
1235*7caa17caSRaphael Isemann           case 'Q':
1236*7caa17caSRaphael Isemann             ch = KEY_F(2);
1237*7caa17caSRaphael Isemann             break;
1238*7caa17caSRaphael Isemann           case 'R':
1239*7caa17caSRaphael Isemann             ch = KEY_F(3);
1240*7caa17caSRaphael Isemann             break;
1241*7caa17caSRaphael Isemann           case 'S':
1242*7caa17caSRaphael Isemann             ch = KEY_F(4);
1243*7caa17caSRaphael Isemann             break;
1244*7caa17caSRaphael Isemann           default:
1245*7caa17caSRaphael Isemann             escape_chars.push_back(ch2);
1246*7caa17caSRaphael Isemann             if (ch3 != -1)
1247*7caa17caSRaphael Isemann               escape_chars.push_back(ch3);
1248*7caa17caSRaphael Isemann             break;
1249*7caa17caSRaphael Isemann           }
1250*7caa17caSRaphael Isemann         } else if (ch2 != -1)
1251*7caa17caSRaphael Isemann           escape_chars.push_back(ch2);
1252*7caa17caSRaphael Isemann       }
1253*7caa17caSRaphael Isemann #else
1254*7caa17caSRaphael Isemann       int ch = m_window_sp->GetChar();
1255*7caa17caSRaphael Isemann 
1256*7caa17caSRaphael Isemann #endif
1257*7caa17caSRaphael Isemann       if (ch == -1) {
1258*7caa17caSRaphael Isemann         if (feof(m_in) || ferror(m_in)) {
1259*7caa17caSRaphael Isemann           done = true;
1260*7caa17caSRaphael Isemann         } else {
1261*7caa17caSRaphael Isemann           // Just a timeout from using halfdelay(), check for events
1262*7caa17caSRaphael Isemann           EventSP event_sp;
1263*7caa17caSRaphael Isemann           while (listener_sp->PeekAtNextEvent()) {
1264*7caa17caSRaphael Isemann             listener_sp->GetEvent(event_sp, std::chrono::seconds(0));
1265*7caa17caSRaphael Isemann 
1266*7caa17caSRaphael Isemann             if (event_sp) {
1267*7caa17caSRaphael Isemann               Broadcaster *broadcaster = event_sp->GetBroadcaster();
1268*7caa17caSRaphael Isemann               if (broadcaster) {
1269*7caa17caSRaphael Isemann                 // uint32_t event_type = event_sp->GetType();
1270*7caa17caSRaphael Isemann                 ConstString broadcaster_class(
1271*7caa17caSRaphael Isemann                     broadcaster->GetBroadcasterClass());
1272*7caa17caSRaphael Isemann                 if (broadcaster_class == broadcaster_class_process) {
1273*7caa17caSRaphael Isemann                   debugger.GetCommandInterpreter().UpdateExecutionContext(
1274*7caa17caSRaphael Isemann                       nullptr);
1275*7caa17caSRaphael Isemann                   update = true;
1276*7caa17caSRaphael Isemann                   continue; // Don't get any key, just update our view
1277*7caa17caSRaphael Isemann                 }
1278*7caa17caSRaphael Isemann               }
1279*7caa17caSRaphael Isemann             }
1280*7caa17caSRaphael Isemann           }
1281*7caa17caSRaphael Isemann         }
1282*7caa17caSRaphael Isemann       } else {
1283*7caa17caSRaphael Isemann         HandleCharResult key_result = m_window_sp->HandleChar(ch);
1284*7caa17caSRaphael Isemann         switch (key_result) {
1285*7caa17caSRaphael Isemann         case eKeyHandled:
1286*7caa17caSRaphael Isemann           debugger.GetCommandInterpreter().UpdateExecutionContext(nullptr);
1287*7caa17caSRaphael Isemann           update = true;
1288*7caa17caSRaphael Isemann           break;
1289*7caa17caSRaphael Isemann         case eKeyNotHandled:
1290*7caa17caSRaphael Isemann           break;
1291*7caa17caSRaphael Isemann         case eQuitApplication:
1292*7caa17caSRaphael Isemann           done = true;
1293*7caa17caSRaphael Isemann           break;
1294*7caa17caSRaphael Isemann         }
1295*7caa17caSRaphael Isemann       }
1296*7caa17caSRaphael Isemann     }
1297*7caa17caSRaphael Isemann 
1298*7caa17caSRaphael Isemann     debugger.CancelForwardEvents(listener_sp);
1299*7caa17caSRaphael Isemann   }
1300*7caa17caSRaphael Isemann 
1301*7caa17caSRaphael Isemann   WindowSP &GetMainWindow() {
1302*7caa17caSRaphael Isemann     if (!m_window_sp)
1303*7caa17caSRaphael Isemann       m_window_sp = std::make_shared<Window>("main", stdscr, false);
1304*7caa17caSRaphael Isemann     return m_window_sp;
1305*7caa17caSRaphael Isemann   }
1306*7caa17caSRaphael Isemann 
1307*7caa17caSRaphael Isemann protected:
1308*7caa17caSRaphael Isemann   WindowSP m_window_sp;
1309*7caa17caSRaphael Isemann   WindowDelegates m_window_delegates;
1310*7caa17caSRaphael Isemann   SCREEN *m_screen;
1311*7caa17caSRaphael Isemann   FILE *m_in;
1312*7caa17caSRaphael Isemann   FILE *m_out;
1313*7caa17caSRaphael Isemann };
1314*7caa17caSRaphael Isemann 
1315*7caa17caSRaphael Isemann } // namespace curses
1316*7caa17caSRaphael Isemann 
1317*7caa17caSRaphael Isemann using namespace curses;
1318*7caa17caSRaphael Isemann 
1319*7caa17caSRaphael Isemann struct Row {
1320*7caa17caSRaphael Isemann   ValueObjectManager value;
1321*7caa17caSRaphael Isemann   Row *parent;
1322*7caa17caSRaphael Isemann   // The process stop ID when the children were calculated.
1323*7caa17caSRaphael Isemann   uint32_t children_stop_id;
1324*7caa17caSRaphael Isemann   int row_idx;
1325*7caa17caSRaphael Isemann   int x;
1326*7caa17caSRaphael Isemann   int y;
1327*7caa17caSRaphael Isemann   bool might_have_children;
1328*7caa17caSRaphael Isemann   bool expanded;
1329*7caa17caSRaphael Isemann   bool calculated_children;
1330*7caa17caSRaphael Isemann   std::vector<Row> children;
1331*7caa17caSRaphael Isemann 
1332*7caa17caSRaphael Isemann   Row(const ValueObjectSP &v, Row *p)
1333*7caa17caSRaphael Isemann       : value(v, lldb::eDynamicDontRunTarget, true), parent(p), row_idx(0),
1334*7caa17caSRaphael Isemann         x(1), y(1), might_have_children(v ? v->MightHaveChildren() : false),
1335*7caa17caSRaphael Isemann         expanded(false), calculated_children(false), children() {}
1336*7caa17caSRaphael Isemann 
1337*7caa17caSRaphael Isemann   size_t GetDepth() const {
1338*7caa17caSRaphael Isemann     if (parent)
1339*7caa17caSRaphael Isemann       return 1 + parent->GetDepth();
1340*7caa17caSRaphael Isemann     return 0;
1341*7caa17caSRaphael Isemann   }
1342*7caa17caSRaphael Isemann 
1343*7caa17caSRaphael Isemann   void Expand() { expanded = true; }
1344*7caa17caSRaphael Isemann 
1345*7caa17caSRaphael Isemann   std::vector<Row> &GetChildren() {
1346*7caa17caSRaphael Isemann     ProcessSP process_sp = value.GetProcessSP();
1347*7caa17caSRaphael Isemann     auto stop_id = process_sp->GetStopID();
1348*7caa17caSRaphael Isemann     if (process_sp && stop_id != children_stop_id) {
1349*7caa17caSRaphael Isemann       children_stop_id = stop_id;
1350*7caa17caSRaphael Isemann       calculated_children = false;
1351*7caa17caSRaphael Isemann     }
1352*7caa17caSRaphael Isemann     if (!calculated_children) {
1353*7caa17caSRaphael Isemann       children.clear();
1354*7caa17caSRaphael Isemann       calculated_children = true;
1355*7caa17caSRaphael Isemann       ValueObjectSP valobj = value.GetSP();
1356*7caa17caSRaphael Isemann       if (valobj) {
1357*7caa17caSRaphael Isemann         const size_t num_children = valobj->GetNumChildren();
1358*7caa17caSRaphael Isemann         for (size_t i = 0; i < num_children; ++i) {
1359*7caa17caSRaphael Isemann           children.push_back(Row(valobj->GetChildAtIndex(i, true), this));
1360*7caa17caSRaphael Isemann         }
1361*7caa17caSRaphael Isemann       }
1362*7caa17caSRaphael Isemann     }
1363*7caa17caSRaphael Isemann     return children;
1364*7caa17caSRaphael Isemann   }
1365*7caa17caSRaphael Isemann 
1366*7caa17caSRaphael Isemann   void Unexpand() {
1367*7caa17caSRaphael Isemann     expanded = false;
1368*7caa17caSRaphael Isemann     calculated_children = false;
1369*7caa17caSRaphael Isemann     children.clear();
1370*7caa17caSRaphael Isemann   }
1371*7caa17caSRaphael Isemann 
1372*7caa17caSRaphael Isemann   void DrawTree(Window &window) {
1373*7caa17caSRaphael Isemann     if (parent)
1374*7caa17caSRaphael Isemann       parent->DrawTreeForChild(window, this, 0);
1375*7caa17caSRaphael Isemann 
1376*7caa17caSRaphael Isemann     if (might_have_children) {
1377*7caa17caSRaphael Isemann       // It we can get UTF8 characters to work we should try to use the
1378*7caa17caSRaphael Isemann       // "symbol" UTF8 string below
1379*7caa17caSRaphael Isemann       //            const char *symbol = "";
1380*7caa17caSRaphael Isemann       //            if (row.expanded)
1381*7caa17caSRaphael Isemann       //                symbol = "\xe2\x96\xbd ";
1382*7caa17caSRaphael Isemann       //            else
1383*7caa17caSRaphael Isemann       //                symbol = "\xe2\x96\xb7 ";
1384*7caa17caSRaphael Isemann       //            window.PutCString (symbol);
1385*7caa17caSRaphael Isemann 
1386*7caa17caSRaphael Isemann       // The ACS_DARROW and ACS_RARROW don't look very nice they are just a 'v'
1387*7caa17caSRaphael Isemann       // or '>' character...
1388*7caa17caSRaphael Isemann       //            if (expanded)
1389*7caa17caSRaphael Isemann       //                window.PutChar (ACS_DARROW);
1390*7caa17caSRaphael Isemann       //            else
1391*7caa17caSRaphael Isemann       //                window.PutChar (ACS_RARROW);
1392*7caa17caSRaphael Isemann       // Since we can't find any good looking right arrow/down arrow symbols,
1393*7caa17caSRaphael Isemann       // just use a diamond...
1394*7caa17caSRaphael Isemann       window.PutChar(ACS_DIAMOND);
1395*7caa17caSRaphael Isemann       window.PutChar(ACS_HLINE);
1396*7caa17caSRaphael Isemann     }
1397*7caa17caSRaphael Isemann   }
1398*7caa17caSRaphael Isemann 
1399*7caa17caSRaphael Isemann   void DrawTreeForChild(Window &window, Row *child, uint32_t reverse_depth) {
1400*7caa17caSRaphael Isemann     if (parent)
1401*7caa17caSRaphael Isemann       parent->DrawTreeForChild(window, this, reverse_depth + 1);
1402*7caa17caSRaphael Isemann 
1403*7caa17caSRaphael Isemann     if (&GetChildren().back() == child) {
1404*7caa17caSRaphael Isemann       // Last child
1405*7caa17caSRaphael Isemann       if (reverse_depth == 0) {
1406*7caa17caSRaphael Isemann         window.PutChar(ACS_LLCORNER);
1407*7caa17caSRaphael Isemann         window.PutChar(ACS_HLINE);
1408*7caa17caSRaphael Isemann       } else {
1409*7caa17caSRaphael Isemann         window.PutChar(' ');
1410*7caa17caSRaphael Isemann         window.PutChar(' ');
1411*7caa17caSRaphael Isemann       }
1412*7caa17caSRaphael Isemann     } else {
1413*7caa17caSRaphael Isemann       if (reverse_depth == 0) {
1414*7caa17caSRaphael Isemann         window.PutChar(ACS_LTEE);
1415*7caa17caSRaphael Isemann         window.PutChar(ACS_HLINE);
1416*7caa17caSRaphael Isemann       } else {
1417*7caa17caSRaphael Isemann         window.PutChar(ACS_VLINE);
1418*7caa17caSRaphael Isemann         window.PutChar(' ');
1419*7caa17caSRaphael Isemann       }
1420*7caa17caSRaphael Isemann     }
1421*7caa17caSRaphael Isemann   }
1422*7caa17caSRaphael Isemann };
1423*7caa17caSRaphael Isemann 
1424*7caa17caSRaphael Isemann struct DisplayOptions {
1425*7caa17caSRaphael Isemann   bool show_types;
1426*7caa17caSRaphael Isemann };
1427*7caa17caSRaphael Isemann 
1428*7caa17caSRaphael Isemann class TreeItem;
1429*7caa17caSRaphael Isemann 
1430*7caa17caSRaphael Isemann class TreeDelegate {
1431*7caa17caSRaphael Isemann public:
1432*7caa17caSRaphael Isemann   TreeDelegate() = default;
1433*7caa17caSRaphael Isemann   virtual ~TreeDelegate() = default;
1434*7caa17caSRaphael Isemann 
1435*7caa17caSRaphael Isemann   virtual void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) = 0;
1436*7caa17caSRaphael Isemann   virtual void TreeDelegateGenerateChildren(TreeItem &item) = 0;
1437*7caa17caSRaphael Isemann   virtual bool TreeDelegateItemSelected(
1438*7caa17caSRaphael Isemann       TreeItem &item) = 0; // Return true if we need to update views
1439*7caa17caSRaphael Isemann };
1440*7caa17caSRaphael Isemann 
1441*7caa17caSRaphael Isemann typedef std::shared_ptr<TreeDelegate> TreeDelegateSP;
1442*7caa17caSRaphael Isemann 
1443*7caa17caSRaphael Isemann class TreeItem {
1444*7caa17caSRaphael Isemann public:
1445*7caa17caSRaphael Isemann   TreeItem(TreeItem *parent, TreeDelegate &delegate, bool might_have_children)
1446*7caa17caSRaphael Isemann       : m_parent(parent), m_delegate(delegate), m_user_data(nullptr),
1447*7caa17caSRaphael Isemann         m_identifier(0), m_row_idx(-1), m_children(),
1448*7caa17caSRaphael Isemann         m_might_have_children(might_have_children), m_is_expanded(false) {}
1449*7caa17caSRaphael Isemann 
1450*7caa17caSRaphael Isemann   TreeItem &operator=(const TreeItem &rhs) {
1451*7caa17caSRaphael Isemann     if (this != &rhs) {
1452*7caa17caSRaphael Isemann       m_parent = rhs.m_parent;
1453*7caa17caSRaphael Isemann       m_delegate = rhs.m_delegate;
1454*7caa17caSRaphael Isemann       m_user_data = rhs.m_user_data;
1455*7caa17caSRaphael Isemann       m_identifier = rhs.m_identifier;
1456*7caa17caSRaphael Isemann       m_row_idx = rhs.m_row_idx;
1457*7caa17caSRaphael Isemann       m_children = rhs.m_children;
1458*7caa17caSRaphael Isemann       m_might_have_children = rhs.m_might_have_children;
1459*7caa17caSRaphael Isemann       m_is_expanded = rhs.m_is_expanded;
1460*7caa17caSRaphael Isemann     }
1461*7caa17caSRaphael Isemann     return *this;
1462*7caa17caSRaphael Isemann   }
1463*7caa17caSRaphael Isemann 
1464*7caa17caSRaphael Isemann   size_t GetDepth() const {
1465*7caa17caSRaphael Isemann     if (m_parent)
1466*7caa17caSRaphael Isemann       return 1 + m_parent->GetDepth();
1467*7caa17caSRaphael Isemann     return 0;
1468*7caa17caSRaphael Isemann   }
1469*7caa17caSRaphael Isemann 
1470*7caa17caSRaphael Isemann   int GetRowIndex() const { return m_row_idx; }
1471*7caa17caSRaphael Isemann 
1472*7caa17caSRaphael Isemann   void ClearChildren() { m_children.clear(); }
1473*7caa17caSRaphael Isemann 
1474*7caa17caSRaphael Isemann   void Resize(size_t n, const TreeItem &t) { m_children.resize(n, t); }
1475*7caa17caSRaphael Isemann 
1476*7caa17caSRaphael Isemann   TreeItem &operator[](size_t i) { return m_children[i]; }
1477*7caa17caSRaphael Isemann 
1478*7caa17caSRaphael Isemann   void SetRowIndex(int row_idx) { m_row_idx = row_idx; }
1479*7caa17caSRaphael Isemann 
1480*7caa17caSRaphael Isemann   size_t GetNumChildren() {
1481*7caa17caSRaphael Isemann     m_delegate.TreeDelegateGenerateChildren(*this);
1482*7caa17caSRaphael Isemann     return m_children.size();
1483*7caa17caSRaphael Isemann   }
1484*7caa17caSRaphael Isemann 
1485*7caa17caSRaphael Isemann   void ItemWasSelected() { m_delegate.TreeDelegateItemSelected(*this); }
1486*7caa17caSRaphael Isemann 
1487*7caa17caSRaphael Isemann   void CalculateRowIndexes(int &row_idx) {
1488*7caa17caSRaphael Isemann     SetRowIndex(row_idx);
1489*7caa17caSRaphael Isemann     ++row_idx;
1490*7caa17caSRaphael Isemann 
1491*7caa17caSRaphael Isemann     const bool expanded = IsExpanded();
1492*7caa17caSRaphael Isemann 
1493*7caa17caSRaphael Isemann     // The root item must calculate its children, or we must calculate the
1494*7caa17caSRaphael Isemann     // number of children if the item is expanded
1495*7caa17caSRaphael Isemann     if (m_parent == nullptr || expanded)
1496*7caa17caSRaphael Isemann       GetNumChildren();
1497*7caa17caSRaphael Isemann 
1498*7caa17caSRaphael Isemann     for (auto &item : m_children) {
1499*7caa17caSRaphael Isemann       if (expanded)
1500*7caa17caSRaphael Isemann         item.CalculateRowIndexes(row_idx);
1501*7caa17caSRaphael Isemann       else
1502*7caa17caSRaphael Isemann         item.SetRowIndex(-1);
1503*7caa17caSRaphael Isemann     }
1504*7caa17caSRaphael Isemann   }
1505*7caa17caSRaphael Isemann 
1506*7caa17caSRaphael Isemann   TreeItem *GetParent() { return m_parent; }
1507*7caa17caSRaphael Isemann 
1508*7caa17caSRaphael Isemann   bool IsExpanded() const { return m_is_expanded; }
1509*7caa17caSRaphael Isemann 
1510*7caa17caSRaphael Isemann   void Expand() { m_is_expanded = true; }
1511*7caa17caSRaphael Isemann 
1512*7caa17caSRaphael Isemann   void Unexpand() { m_is_expanded = false; }
1513*7caa17caSRaphael Isemann 
1514*7caa17caSRaphael Isemann   bool Draw(Window &window, const int first_visible_row,
1515*7caa17caSRaphael Isemann             const uint32_t selected_row_idx, int &row_idx, int &num_rows_left) {
1516*7caa17caSRaphael Isemann     if (num_rows_left <= 0)
1517*7caa17caSRaphael Isemann       return false;
1518*7caa17caSRaphael Isemann 
1519*7caa17caSRaphael Isemann     if (m_row_idx >= first_visible_row) {
1520*7caa17caSRaphael Isemann       window.MoveCursor(2, row_idx + 1);
1521*7caa17caSRaphael Isemann 
1522*7caa17caSRaphael Isemann       if (m_parent)
1523*7caa17caSRaphael Isemann         m_parent->DrawTreeForChild(window, this, 0);
1524*7caa17caSRaphael Isemann 
1525*7caa17caSRaphael Isemann       if (m_might_have_children) {
1526*7caa17caSRaphael Isemann         // It we can get UTF8 characters to work we should try to use the
1527*7caa17caSRaphael Isemann         // "symbol" UTF8 string below
1528*7caa17caSRaphael Isemann         //            const char *symbol = "";
1529*7caa17caSRaphael Isemann         //            if (row.expanded)
1530*7caa17caSRaphael Isemann         //                symbol = "\xe2\x96\xbd ";
1531*7caa17caSRaphael Isemann         //            else
1532*7caa17caSRaphael Isemann         //                symbol = "\xe2\x96\xb7 ";
1533*7caa17caSRaphael Isemann         //            window.PutCString (symbol);
1534*7caa17caSRaphael Isemann 
1535*7caa17caSRaphael Isemann         // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
1536*7caa17caSRaphael Isemann         // 'v' or '>' character...
1537*7caa17caSRaphael Isemann         //            if (expanded)
1538*7caa17caSRaphael Isemann         //                window.PutChar (ACS_DARROW);
1539*7caa17caSRaphael Isemann         //            else
1540*7caa17caSRaphael Isemann         //                window.PutChar (ACS_RARROW);
1541*7caa17caSRaphael Isemann         // Since we can't find any good looking right arrow/down arrow symbols,
1542*7caa17caSRaphael Isemann         // just use a diamond...
1543*7caa17caSRaphael Isemann         window.PutChar(ACS_DIAMOND);
1544*7caa17caSRaphael Isemann         window.PutChar(ACS_HLINE);
1545*7caa17caSRaphael Isemann       }
1546*7caa17caSRaphael Isemann       bool highlight = (selected_row_idx == static_cast<size_t>(m_row_idx)) &&
1547*7caa17caSRaphael Isemann                        window.IsActive();
1548*7caa17caSRaphael Isemann 
1549*7caa17caSRaphael Isemann       if (highlight)
1550*7caa17caSRaphael Isemann         window.AttributeOn(A_REVERSE);
1551*7caa17caSRaphael Isemann 
1552*7caa17caSRaphael Isemann       m_delegate.TreeDelegateDrawTreeItem(*this, window);
1553*7caa17caSRaphael Isemann 
1554*7caa17caSRaphael Isemann       if (highlight)
1555*7caa17caSRaphael Isemann         window.AttributeOff(A_REVERSE);
1556*7caa17caSRaphael Isemann       ++row_idx;
1557*7caa17caSRaphael Isemann       --num_rows_left;
1558*7caa17caSRaphael Isemann     }
1559*7caa17caSRaphael Isemann 
1560*7caa17caSRaphael Isemann     if (num_rows_left <= 0)
1561*7caa17caSRaphael Isemann       return false; // We are done drawing...
1562*7caa17caSRaphael Isemann 
1563*7caa17caSRaphael Isemann     if (IsExpanded()) {
1564*7caa17caSRaphael Isemann       for (auto &item : m_children) {
1565*7caa17caSRaphael Isemann         // If we displayed all the rows and item.Draw() returns false we are
1566*7caa17caSRaphael Isemann         // done drawing and can exit this for loop
1567*7caa17caSRaphael Isemann         if (!item.Draw(window, first_visible_row, selected_row_idx, row_idx,
1568*7caa17caSRaphael Isemann                        num_rows_left))
1569*7caa17caSRaphael Isemann           break;
1570*7caa17caSRaphael Isemann       }
1571*7caa17caSRaphael Isemann     }
1572*7caa17caSRaphael Isemann     return num_rows_left >= 0; // Return true if not done drawing yet
1573*7caa17caSRaphael Isemann   }
1574*7caa17caSRaphael Isemann 
1575*7caa17caSRaphael Isemann   void DrawTreeForChild(Window &window, TreeItem *child,
1576*7caa17caSRaphael Isemann                         uint32_t reverse_depth) {
1577*7caa17caSRaphael Isemann     if (m_parent)
1578*7caa17caSRaphael Isemann       m_parent->DrawTreeForChild(window, this, reverse_depth + 1);
1579*7caa17caSRaphael Isemann 
1580*7caa17caSRaphael Isemann     if (&m_children.back() == child) {
1581*7caa17caSRaphael Isemann       // Last child
1582*7caa17caSRaphael Isemann       if (reverse_depth == 0) {
1583*7caa17caSRaphael Isemann         window.PutChar(ACS_LLCORNER);
1584*7caa17caSRaphael Isemann         window.PutChar(ACS_HLINE);
1585*7caa17caSRaphael Isemann       } else {
1586*7caa17caSRaphael Isemann         window.PutChar(' ');
1587*7caa17caSRaphael Isemann         window.PutChar(' ');
1588*7caa17caSRaphael Isemann       }
1589*7caa17caSRaphael Isemann     } else {
1590*7caa17caSRaphael Isemann       if (reverse_depth == 0) {
1591*7caa17caSRaphael Isemann         window.PutChar(ACS_LTEE);
1592*7caa17caSRaphael Isemann         window.PutChar(ACS_HLINE);
1593*7caa17caSRaphael Isemann       } else {
1594*7caa17caSRaphael Isemann         window.PutChar(ACS_VLINE);
1595*7caa17caSRaphael Isemann         window.PutChar(' ');
1596*7caa17caSRaphael Isemann       }
1597*7caa17caSRaphael Isemann     }
1598*7caa17caSRaphael Isemann   }
1599*7caa17caSRaphael Isemann 
1600*7caa17caSRaphael Isemann   TreeItem *GetItemForRowIndex(uint32_t row_idx) {
1601*7caa17caSRaphael Isemann     if (static_cast<uint32_t>(m_row_idx) == row_idx)
1602*7caa17caSRaphael Isemann       return this;
1603*7caa17caSRaphael Isemann     if (m_children.empty())
1604*7caa17caSRaphael Isemann       return nullptr;
1605*7caa17caSRaphael Isemann     if (IsExpanded()) {
1606*7caa17caSRaphael Isemann       for (auto &item : m_children) {
1607*7caa17caSRaphael Isemann         TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx);
1608*7caa17caSRaphael Isemann         if (selected_item_ptr)
1609*7caa17caSRaphael Isemann           return selected_item_ptr;
1610*7caa17caSRaphael Isemann       }
1611*7caa17caSRaphael Isemann     }
1612*7caa17caSRaphael Isemann     return nullptr;
1613*7caa17caSRaphael Isemann   }
1614*7caa17caSRaphael Isemann 
1615*7caa17caSRaphael Isemann   void *GetUserData() const { return m_user_data; }
1616*7caa17caSRaphael Isemann 
1617*7caa17caSRaphael Isemann   void SetUserData(void *user_data) { m_user_data = user_data; }
1618*7caa17caSRaphael Isemann 
1619*7caa17caSRaphael Isemann   uint64_t GetIdentifier() const { return m_identifier; }
1620*7caa17caSRaphael Isemann 
1621*7caa17caSRaphael Isemann   void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
1622*7caa17caSRaphael Isemann 
1623*7caa17caSRaphael Isemann   void SetMightHaveChildren(bool b) { m_might_have_children = b; }
1624*7caa17caSRaphael Isemann 
1625*7caa17caSRaphael Isemann protected:
1626*7caa17caSRaphael Isemann   TreeItem *m_parent;
1627*7caa17caSRaphael Isemann   TreeDelegate &m_delegate;
1628*7caa17caSRaphael Isemann   void *m_user_data;
1629*7caa17caSRaphael Isemann   uint64_t m_identifier;
1630*7caa17caSRaphael Isemann   int m_row_idx; // Zero based visible row index, -1 if not visible or for the
1631*7caa17caSRaphael Isemann                  // root item
1632*7caa17caSRaphael Isemann   std::vector<TreeItem> m_children;
1633*7caa17caSRaphael Isemann   bool m_might_have_children;
1634*7caa17caSRaphael Isemann   bool m_is_expanded;
1635*7caa17caSRaphael Isemann };
1636*7caa17caSRaphael Isemann 
1637*7caa17caSRaphael Isemann class TreeWindowDelegate : public WindowDelegate {
1638*7caa17caSRaphael Isemann public:
1639*7caa17caSRaphael Isemann   TreeWindowDelegate(Debugger &debugger, const TreeDelegateSP &delegate_sp)
1640*7caa17caSRaphael Isemann       : m_debugger(debugger), m_delegate_sp(delegate_sp),
1641*7caa17caSRaphael Isemann         m_root(nullptr, *delegate_sp, true), m_selected_item(nullptr),
1642*7caa17caSRaphael Isemann         m_num_rows(0), m_selected_row_idx(0), m_first_visible_row(0),
1643*7caa17caSRaphael Isemann         m_min_x(0), m_min_y(0), m_max_x(0), m_max_y(0) {}
1644*7caa17caSRaphael Isemann 
1645*7caa17caSRaphael Isemann   int NumVisibleRows() const { return m_max_y - m_min_y; }
1646*7caa17caSRaphael Isemann 
1647*7caa17caSRaphael Isemann   bool WindowDelegateDraw(Window &window, bool force) override {
1648*7caa17caSRaphael Isemann     ExecutionContext exe_ctx(
1649*7caa17caSRaphael Isemann         m_debugger.GetCommandInterpreter().GetExecutionContext());
1650*7caa17caSRaphael Isemann     Process *process = exe_ctx.GetProcessPtr();
1651*7caa17caSRaphael Isemann 
1652*7caa17caSRaphael Isemann     bool display_content = false;
1653*7caa17caSRaphael Isemann     if (process) {
1654*7caa17caSRaphael Isemann       StateType state = process->GetState();
1655*7caa17caSRaphael Isemann       if (StateIsStoppedState(state, true)) {
1656*7caa17caSRaphael Isemann         // We are stopped, so it is ok to
1657*7caa17caSRaphael Isemann         display_content = true;
1658*7caa17caSRaphael Isemann       } else if (StateIsRunningState(state)) {
1659*7caa17caSRaphael Isemann         return true; // Don't do any updating when we are running
1660*7caa17caSRaphael Isemann       }
1661*7caa17caSRaphael Isemann     }
1662*7caa17caSRaphael Isemann 
1663*7caa17caSRaphael Isemann     m_min_x = 2;
1664*7caa17caSRaphael Isemann     m_min_y = 1;
1665*7caa17caSRaphael Isemann     m_max_x = window.GetWidth() - 1;
1666*7caa17caSRaphael Isemann     m_max_y = window.GetHeight() - 1;
1667*7caa17caSRaphael Isemann 
1668*7caa17caSRaphael Isemann     window.Erase();
1669*7caa17caSRaphael Isemann     window.DrawTitleBox(window.GetName());
1670*7caa17caSRaphael Isemann 
1671*7caa17caSRaphael Isemann     if (display_content) {
1672*7caa17caSRaphael Isemann       const int num_visible_rows = NumVisibleRows();
1673*7caa17caSRaphael Isemann       m_num_rows = 0;
1674*7caa17caSRaphael Isemann       m_root.CalculateRowIndexes(m_num_rows);
1675*7caa17caSRaphael Isemann 
1676*7caa17caSRaphael Isemann       // If we unexpanded while having something selected our total number of
1677*7caa17caSRaphael Isemann       // rows is less than the num visible rows, then make sure we show all the
1678*7caa17caSRaphael Isemann       // rows by setting the first visible row accordingly.
1679*7caa17caSRaphael Isemann       if (m_first_visible_row > 0 && m_num_rows < num_visible_rows)
1680*7caa17caSRaphael Isemann         m_first_visible_row = 0;
1681*7caa17caSRaphael Isemann 
1682*7caa17caSRaphael Isemann       // Make sure the selected row is always visible
1683*7caa17caSRaphael Isemann       if (m_selected_row_idx < m_first_visible_row)
1684*7caa17caSRaphael Isemann         m_first_visible_row = m_selected_row_idx;
1685*7caa17caSRaphael Isemann       else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
1686*7caa17caSRaphael Isemann         m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
1687*7caa17caSRaphael Isemann 
1688*7caa17caSRaphael Isemann       int row_idx = 0;
1689*7caa17caSRaphael Isemann       int num_rows_left = num_visible_rows;
1690*7caa17caSRaphael Isemann       m_root.Draw(window, m_first_visible_row, m_selected_row_idx, row_idx,
1691*7caa17caSRaphael Isemann                   num_rows_left);
1692*7caa17caSRaphael Isemann       // Get the selected row
1693*7caa17caSRaphael Isemann       m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
1694*7caa17caSRaphael Isemann     } else {
1695*7caa17caSRaphael Isemann       m_selected_item = nullptr;
1696*7caa17caSRaphael Isemann     }
1697*7caa17caSRaphael Isemann 
1698*7caa17caSRaphael Isemann     return true; // Drawing handled
1699*7caa17caSRaphael Isemann   }
1700*7caa17caSRaphael Isemann 
1701*7caa17caSRaphael Isemann   const char *WindowDelegateGetHelpText() override {
1702*7caa17caSRaphael Isemann     return "Thread window keyboard shortcuts:";
1703*7caa17caSRaphael Isemann   }
1704*7caa17caSRaphael Isemann 
1705*7caa17caSRaphael Isemann   KeyHelp *WindowDelegateGetKeyHelp() override {
1706*7caa17caSRaphael Isemann     static curses::KeyHelp g_source_view_key_help[] = {
1707*7caa17caSRaphael Isemann         {KEY_UP, "Select previous item"},
1708*7caa17caSRaphael Isemann         {KEY_DOWN, "Select next item"},
1709*7caa17caSRaphael Isemann         {KEY_RIGHT, "Expand the selected item"},
1710*7caa17caSRaphael Isemann         {KEY_LEFT,
1711*7caa17caSRaphael Isemann          "Unexpand the selected item or select parent if not expanded"},
1712*7caa17caSRaphael Isemann         {KEY_PPAGE, "Page up"},
1713*7caa17caSRaphael Isemann         {KEY_NPAGE, "Page down"},
1714*7caa17caSRaphael Isemann         {'h', "Show help dialog"},
1715*7caa17caSRaphael Isemann         {' ', "Toggle item expansion"},
1716*7caa17caSRaphael Isemann         {',', "Page up"},
1717*7caa17caSRaphael Isemann         {'.', "Page down"},
1718*7caa17caSRaphael Isemann         {'\0', nullptr}};
1719*7caa17caSRaphael Isemann     return g_source_view_key_help;
1720*7caa17caSRaphael Isemann   }
1721*7caa17caSRaphael Isemann 
1722*7caa17caSRaphael Isemann   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
1723*7caa17caSRaphael Isemann     switch (c) {
1724*7caa17caSRaphael Isemann     case ',':
1725*7caa17caSRaphael Isemann     case KEY_PPAGE:
1726*7caa17caSRaphael Isemann       // Page up key
1727*7caa17caSRaphael Isemann       if (m_first_visible_row > 0) {
1728*7caa17caSRaphael Isemann         if (m_first_visible_row > m_max_y)
1729*7caa17caSRaphael Isemann           m_first_visible_row -= m_max_y;
1730*7caa17caSRaphael Isemann         else
1731*7caa17caSRaphael Isemann           m_first_visible_row = 0;
1732*7caa17caSRaphael Isemann         m_selected_row_idx = m_first_visible_row;
1733*7caa17caSRaphael Isemann         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
1734*7caa17caSRaphael Isemann         if (m_selected_item)
1735*7caa17caSRaphael Isemann           m_selected_item->ItemWasSelected();
1736*7caa17caSRaphael Isemann       }
1737*7caa17caSRaphael Isemann       return eKeyHandled;
1738*7caa17caSRaphael Isemann 
1739*7caa17caSRaphael Isemann     case '.':
1740*7caa17caSRaphael Isemann     case KEY_NPAGE:
1741*7caa17caSRaphael Isemann       // Page down key
1742*7caa17caSRaphael Isemann       if (m_num_rows > m_max_y) {
1743*7caa17caSRaphael Isemann         if (m_first_visible_row + m_max_y < m_num_rows) {
1744*7caa17caSRaphael Isemann           m_first_visible_row += m_max_y;
1745*7caa17caSRaphael Isemann           m_selected_row_idx = m_first_visible_row;
1746*7caa17caSRaphael Isemann           m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
1747*7caa17caSRaphael Isemann           if (m_selected_item)
1748*7caa17caSRaphael Isemann             m_selected_item->ItemWasSelected();
1749*7caa17caSRaphael Isemann         }
1750*7caa17caSRaphael Isemann       }
1751*7caa17caSRaphael Isemann       return eKeyHandled;
1752*7caa17caSRaphael Isemann 
1753*7caa17caSRaphael Isemann     case KEY_UP:
1754*7caa17caSRaphael Isemann       if (m_selected_row_idx > 0) {
1755*7caa17caSRaphael Isemann         --m_selected_row_idx;
1756*7caa17caSRaphael Isemann         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
1757*7caa17caSRaphael Isemann         if (m_selected_item)
1758*7caa17caSRaphael Isemann           m_selected_item->ItemWasSelected();
1759*7caa17caSRaphael Isemann       }
1760*7caa17caSRaphael Isemann       return eKeyHandled;
1761*7caa17caSRaphael Isemann 
1762*7caa17caSRaphael Isemann     case KEY_DOWN:
1763*7caa17caSRaphael Isemann       if (m_selected_row_idx + 1 < m_num_rows) {
1764*7caa17caSRaphael Isemann         ++m_selected_row_idx;
1765*7caa17caSRaphael Isemann         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
1766*7caa17caSRaphael Isemann         if (m_selected_item)
1767*7caa17caSRaphael Isemann           m_selected_item->ItemWasSelected();
1768*7caa17caSRaphael Isemann       }
1769*7caa17caSRaphael Isemann       return eKeyHandled;
1770*7caa17caSRaphael Isemann 
1771*7caa17caSRaphael Isemann     case KEY_RIGHT:
1772*7caa17caSRaphael Isemann       if (m_selected_item) {
1773*7caa17caSRaphael Isemann         if (!m_selected_item->IsExpanded())
1774*7caa17caSRaphael Isemann           m_selected_item->Expand();
1775*7caa17caSRaphael Isemann       }
1776*7caa17caSRaphael Isemann       return eKeyHandled;
1777*7caa17caSRaphael Isemann 
1778*7caa17caSRaphael Isemann     case KEY_LEFT:
1779*7caa17caSRaphael Isemann       if (m_selected_item) {
1780*7caa17caSRaphael Isemann         if (m_selected_item->IsExpanded())
1781*7caa17caSRaphael Isemann           m_selected_item->Unexpand();
1782*7caa17caSRaphael Isemann         else if (m_selected_item->GetParent()) {
1783*7caa17caSRaphael Isemann           m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex();
1784*7caa17caSRaphael Isemann           m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
1785*7caa17caSRaphael Isemann           if (m_selected_item)
1786*7caa17caSRaphael Isemann             m_selected_item->ItemWasSelected();
1787*7caa17caSRaphael Isemann         }
1788*7caa17caSRaphael Isemann       }
1789*7caa17caSRaphael Isemann       return eKeyHandled;
1790*7caa17caSRaphael Isemann 
1791*7caa17caSRaphael Isemann     case ' ':
1792*7caa17caSRaphael Isemann       // Toggle expansion state when SPACE is pressed
1793*7caa17caSRaphael Isemann       if (m_selected_item) {
1794*7caa17caSRaphael Isemann         if (m_selected_item->IsExpanded())
1795*7caa17caSRaphael Isemann           m_selected_item->Unexpand();
1796*7caa17caSRaphael Isemann         else
1797*7caa17caSRaphael Isemann           m_selected_item->Expand();
1798*7caa17caSRaphael Isemann       }
1799*7caa17caSRaphael Isemann       return eKeyHandled;
1800*7caa17caSRaphael Isemann 
1801*7caa17caSRaphael Isemann     case 'h':
1802*7caa17caSRaphael Isemann       window.CreateHelpSubwindow();
1803*7caa17caSRaphael Isemann       return eKeyHandled;
1804*7caa17caSRaphael Isemann 
1805*7caa17caSRaphael Isemann     default:
1806*7caa17caSRaphael Isemann       break;
1807*7caa17caSRaphael Isemann     }
1808*7caa17caSRaphael Isemann     return eKeyNotHandled;
1809*7caa17caSRaphael Isemann   }
1810*7caa17caSRaphael Isemann 
1811*7caa17caSRaphael Isemann protected:
1812*7caa17caSRaphael Isemann   Debugger &m_debugger;
1813*7caa17caSRaphael Isemann   TreeDelegateSP m_delegate_sp;
1814*7caa17caSRaphael Isemann   TreeItem m_root;
1815*7caa17caSRaphael Isemann   TreeItem *m_selected_item;
1816*7caa17caSRaphael Isemann   int m_num_rows;
1817*7caa17caSRaphael Isemann   int m_selected_row_idx;
1818*7caa17caSRaphael Isemann   int m_first_visible_row;
1819*7caa17caSRaphael Isemann   int m_min_x;
1820*7caa17caSRaphael Isemann   int m_min_y;
1821*7caa17caSRaphael Isemann   int m_max_x;
1822*7caa17caSRaphael Isemann   int m_max_y;
1823*7caa17caSRaphael Isemann };
1824*7caa17caSRaphael Isemann 
1825*7caa17caSRaphael Isemann class FrameTreeDelegate : public TreeDelegate {
1826*7caa17caSRaphael Isemann public:
1827*7caa17caSRaphael Isemann   FrameTreeDelegate() : TreeDelegate() {
1828*7caa17caSRaphael Isemann     FormatEntity::Parse(
1829*7caa17caSRaphael Isemann         "frame #${frame.index}: {${function.name}${function.pc-offset}}}",
1830*7caa17caSRaphael Isemann         m_format);
1831*7caa17caSRaphael Isemann   }
1832*7caa17caSRaphael Isemann 
1833*7caa17caSRaphael Isemann   ~FrameTreeDelegate() override = default;
1834*7caa17caSRaphael Isemann 
1835*7caa17caSRaphael Isemann   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
1836*7caa17caSRaphael Isemann     Thread *thread = (Thread *)item.GetUserData();
1837*7caa17caSRaphael Isemann     if (thread) {
1838*7caa17caSRaphael Isemann       const uint64_t frame_idx = item.GetIdentifier();
1839*7caa17caSRaphael Isemann       StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_idx);
1840*7caa17caSRaphael Isemann       if (frame_sp) {
1841*7caa17caSRaphael Isemann         StreamString strm;
1842*7caa17caSRaphael Isemann         const SymbolContext &sc =
1843*7caa17caSRaphael Isemann             frame_sp->GetSymbolContext(eSymbolContextEverything);
1844*7caa17caSRaphael Isemann         ExecutionContext exe_ctx(frame_sp);
1845*7caa17caSRaphael Isemann         if (FormatEntity::Format(m_format, strm, &sc, &exe_ctx, nullptr,
1846*7caa17caSRaphael Isemann                                  nullptr, false, false)) {
1847*7caa17caSRaphael Isemann           int right_pad = 1;
1848*7caa17caSRaphael Isemann           window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad);
1849*7caa17caSRaphael Isemann         }
1850*7caa17caSRaphael Isemann       }
1851*7caa17caSRaphael Isemann     }
1852*7caa17caSRaphael Isemann   }
1853*7caa17caSRaphael Isemann 
1854*7caa17caSRaphael Isemann   void TreeDelegateGenerateChildren(TreeItem &item) override {
1855*7caa17caSRaphael Isemann     // No children for frames yet...
1856*7caa17caSRaphael Isemann   }
1857*7caa17caSRaphael Isemann 
1858*7caa17caSRaphael Isemann   bool TreeDelegateItemSelected(TreeItem &item) override {
1859*7caa17caSRaphael Isemann     Thread *thread = (Thread *)item.GetUserData();
1860*7caa17caSRaphael Isemann     if (thread) {
1861*7caa17caSRaphael Isemann       thread->GetProcess()->GetThreadList().SetSelectedThreadByID(
1862*7caa17caSRaphael Isemann           thread->GetID());
1863*7caa17caSRaphael Isemann       const uint64_t frame_idx = item.GetIdentifier();
1864*7caa17caSRaphael Isemann       thread->SetSelectedFrameByIndex(frame_idx);
1865*7caa17caSRaphael Isemann       return true;
1866*7caa17caSRaphael Isemann     }
1867*7caa17caSRaphael Isemann     return false;
1868*7caa17caSRaphael Isemann   }
1869*7caa17caSRaphael Isemann 
1870*7caa17caSRaphael Isemann protected:
1871*7caa17caSRaphael Isemann   FormatEntity::Entry m_format;
1872*7caa17caSRaphael Isemann };
1873*7caa17caSRaphael Isemann 
1874*7caa17caSRaphael Isemann class ThreadTreeDelegate : public TreeDelegate {
1875*7caa17caSRaphael Isemann public:
1876*7caa17caSRaphael Isemann   ThreadTreeDelegate(Debugger &debugger)
1877*7caa17caSRaphael Isemann       : TreeDelegate(), m_debugger(debugger), m_tid(LLDB_INVALID_THREAD_ID),
1878*7caa17caSRaphael Isemann         m_stop_id(UINT32_MAX) {
1879*7caa17caSRaphael Isemann     FormatEntity::Parse("thread #${thread.index}: tid = ${thread.id}{, stop "
1880*7caa17caSRaphael Isemann                         "reason = ${thread.stop-reason}}",
1881*7caa17caSRaphael Isemann                         m_format);
1882*7caa17caSRaphael Isemann   }
1883*7caa17caSRaphael Isemann 
1884*7caa17caSRaphael Isemann   ~ThreadTreeDelegate() override = default;
1885*7caa17caSRaphael Isemann 
1886*7caa17caSRaphael Isemann   ProcessSP GetProcess() {
1887*7caa17caSRaphael Isemann     return m_debugger.GetCommandInterpreter()
1888*7caa17caSRaphael Isemann         .GetExecutionContext()
1889*7caa17caSRaphael Isemann         .GetProcessSP();
1890*7caa17caSRaphael Isemann   }
1891*7caa17caSRaphael Isemann 
1892*7caa17caSRaphael Isemann   ThreadSP GetThread(const TreeItem &item) {
1893*7caa17caSRaphael Isemann     ProcessSP process_sp = GetProcess();
1894*7caa17caSRaphael Isemann     if (process_sp)
1895*7caa17caSRaphael Isemann       return process_sp->GetThreadList().FindThreadByID(item.GetIdentifier());
1896*7caa17caSRaphael Isemann     return ThreadSP();
1897*7caa17caSRaphael Isemann   }
1898*7caa17caSRaphael Isemann 
1899*7caa17caSRaphael Isemann   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
1900*7caa17caSRaphael Isemann     ThreadSP thread_sp = GetThread(item);
1901*7caa17caSRaphael Isemann     if (thread_sp) {
1902*7caa17caSRaphael Isemann       StreamString strm;
1903*7caa17caSRaphael Isemann       ExecutionContext exe_ctx(thread_sp);
1904*7caa17caSRaphael Isemann       if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
1905*7caa17caSRaphael Isemann                                nullptr, false, false)) {
1906*7caa17caSRaphael Isemann         int right_pad = 1;
1907*7caa17caSRaphael Isemann         window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad);
1908*7caa17caSRaphael Isemann       }
1909*7caa17caSRaphael Isemann     }
1910*7caa17caSRaphael Isemann   }
1911*7caa17caSRaphael Isemann 
1912*7caa17caSRaphael Isemann   void TreeDelegateGenerateChildren(TreeItem &item) override {
1913*7caa17caSRaphael Isemann     ProcessSP process_sp = GetProcess();
1914*7caa17caSRaphael Isemann     if (process_sp && process_sp->IsAlive()) {
1915*7caa17caSRaphael Isemann       StateType state = process_sp->GetState();
1916*7caa17caSRaphael Isemann       if (StateIsStoppedState(state, true)) {
1917*7caa17caSRaphael Isemann         ThreadSP thread_sp = GetThread(item);
1918*7caa17caSRaphael Isemann         if (thread_sp) {
1919*7caa17caSRaphael Isemann           if (m_stop_id == process_sp->GetStopID() &&
1920*7caa17caSRaphael Isemann               thread_sp->GetID() == m_tid)
1921*7caa17caSRaphael Isemann             return; // Children are already up to date
1922*7caa17caSRaphael Isemann           if (!m_frame_delegate_sp) {
1923*7caa17caSRaphael Isemann             // Always expand the thread item the first time we show it
1924*7caa17caSRaphael Isemann             m_frame_delegate_sp = std::make_shared<FrameTreeDelegate>();
1925*7caa17caSRaphael Isemann           }
1926*7caa17caSRaphael Isemann 
1927*7caa17caSRaphael Isemann           m_stop_id = process_sp->GetStopID();
1928*7caa17caSRaphael Isemann           m_tid = thread_sp->GetID();
1929*7caa17caSRaphael Isemann 
1930*7caa17caSRaphael Isemann           TreeItem t(&item, *m_frame_delegate_sp, false);
1931*7caa17caSRaphael Isemann           size_t num_frames = thread_sp->GetStackFrameCount();
1932*7caa17caSRaphael Isemann           item.Resize(num_frames, t);
1933*7caa17caSRaphael Isemann           for (size_t i = 0; i < num_frames; ++i) {
1934*7caa17caSRaphael Isemann             item[i].SetUserData(thread_sp.get());
1935*7caa17caSRaphael Isemann             item[i].SetIdentifier(i);
1936*7caa17caSRaphael Isemann           }
1937*7caa17caSRaphael Isemann         }
1938*7caa17caSRaphael Isemann         return;
1939*7caa17caSRaphael Isemann       }
1940*7caa17caSRaphael Isemann     }
1941*7caa17caSRaphael Isemann     item.ClearChildren();
1942*7caa17caSRaphael Isemann   }
1943*7caa17caSRaphael Isemann 
1944*7caa17caSRaphael Isemann   bool TreeDelegateItemSelected(TreeItem &item) override {
1945*7caa17caSRaphael Isemann     ProcessSP process_sp = GetProcess();
1946*7caa17caSRaphael Isemann     if (process_sp && process_sp->IsAlive()) {
1947*7caa17caSRaphael Isemann       StateType state = process_sp->GetState();
1948*7caa17caSRaphael Isemann       if (StateIsStoppedState(state, true)) {
1949*7caa17caSRaphael Isemann         ThreadSP thread_sp = GetThread(item);
1950*7caa17caSRaphael Isemann         if (thread_sp) {
1951*7caa17caSRaphael Isemann           ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList();
1952*7caa17caSRaphael Isemann           std::lock_guard<std::recursive_mutex> guard(thread_list.GetMutex());
1953*7caa17caSRaphael Isemann           ThreadSP selected_thread_sp = thread_list.GetSelectedThread();
1954*7caa17caSRaphael Isemann           if (selected_thread_sp->GetID() != thread_sp->GetID()) {
1955*7caa17caSRaphael Isemann             thread_list.SetSelectedThreadByID(thread_sp->GetID());
1956*7caa17caSRaphael Isemann             return true;
1957*7caa17caSRaphael Isemann           }
1958*7caa17caSRaphael Isemann         }
1959*7caa17caSRaphael Isemann       }
1960*7caa17caSRaphael Isemann     }
1961*7caa17caSRaphael Isemann     return false;
1962*7caa17caSRaphael Isemann   }
1963*7caa17caSRaphael Isemann 
1964*7caa17caSRaphael Isemann protected:
1965*7caa17caSRaphael Isemann   Debugger &m_debugger;
1966*7caa17caSRaphael Isemann   std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp;
1967*7caa17caSRaphael Isemann   lldb::user_id_t m_tid;
1968*7caa17caSRaphael Isemann   uint32_t m_stop_id;
1969*7caa17caSRaphael Isemann   FormatEntity::Entry m_format;
1970*7caa17caSRaphael Isemann };
1971*7caa17caSRaphael Isemann 
1972*7caa17caSRaphael Isemann class ThreadsTreeDelegate : public TreeDelegate {
1973*7caa17caSRaphael Isemann public:
1974*7caa17caSRaphael Isemann   ThreadsTreeDelegate(Debugger &debugger)
1975*7caa17caSRaphael Isemann       : TreeDelegate(), m_thread_delegate_sp(), m_debugger(debugger),
1976*7caa17caSRaphael Isemann         m_stop_id(UINT32_MAX) {
1977*7caa17caSRaphael Isemann     FormatEntity::Parse("process ${process.id}{, name = ${process.name}}",
1978*7caa17caSRaphael Isemann                         m_format);
1979*7caa17caSRaphael Isemann   }
1980*7caa17caSRaphael Isemann 
1981*7caa17caSRaphael Isemann   ~ThreadsTreeDelegate() override = default;
1982*7caa17caSRaphael Isemann 
1983*7caa17caSRaphael Isemann   ProcessSP GetProcess() {
1984*7caa17caSRaphael Isemann     return m_debugger.GetCommandInterpreter()
1985*7caa17caSRaphael Isemann         .GetExecutionContext()
1986*7caa17caSRaphael Isemann         .GetProcessSP();
1987*7caa17caSRaphael Isemann   }
1988*7caa17caSRaphael Isemann 
1989*7caa17caSRaphael Isemann   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
1990*7caa17caSRaphael Isemann     ProcessSP process_sp = GetProcess();
1991*7caa17caSRaphael Isemann     if (process_sp && process_sp->IsAlive()) {
1992*7caa17caSRaphael Isemann       StreamString strm;
1993*7caa17caSRaphael Isemann       ExecutionContext exe_ctx(process_sp);
1994*7caa17caSRaphael Isemann       if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
1995*7caa17caSRaphael Isemann                                nullptr, false, false)) {
1996*7caa17caSRaphael Isemann         int right_pad = 1;
1997*7caa17caSRaphael Isemann         window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad);
1998*7caa17caSRaphael Isemann       }
1999*7caa17caSRaphael Isemann     }
2000*7caa17caSRaphael Isemann   }
2001*7caa17caSRaphael Isemann 
2002*7caa17caSRaphael Isemann   void TreeDelegateGenerateChildren(TreeItem &item) override {
2003*7caa17caSRaphael Isemann     ProcessSP process_sp = GetProcess();
2004*7caa17caSRaphael Isemann     if (process_sp && process_sp->IsAlive()) {
2005*7caa17caSRaphael Isemann       StateType state = process_sp->GetState();
2006*7caa17caSRaphael Isemann       if (StateIsStoppedState(state, true)) {
2007*7caa17caSRaphael Isemann         const uint32_t stop_id = process_sp->GetStopID();
2008*7caa17caSRaphael Isemann         if (m_stop_id == stop_id)
2009*7caa17caSRaphael Isemann           return; // Children are already up to date
2010*7caa17caSRaphael Isemann 
2011*7caa17caSRaphael Isemann         m_stop_id = stop_id;
2012*7caa17caSRaphael Isemann 
2013*7caa17caSRaphael Isemann         if (!m_thread_delegate_sp) {
2014*7caa17caSRaphael Isemann           // Always expand the thread item the first time we show it
2015*7caa17caSRaphael Isemann           // item.Expand();
2016*7caa17caSRaphael Isemann           m_thread_delegate_sp =
2017*7caa17caSRaphael Isemann               std::make_shared<ThreadTreeDelegate>(m_debugger);
2018*7caa17caSRaphael Isemann         }
2019*7caa17caSRaphael Isemann 
2020*7caa17caSRaphael Isemann         TreeItem t(&item, *m_thread_delegate_sp, false);
2021*7caa17caSRaphael Isemann         ThreadList &threads = process_sp->GetThreadList();
2022*7caa17caSRaphael Isemann         std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
2023*7caa17caSRaphael Isemann         size_t num_threads = threads.GetSize();
2024*7caa17caSRaphael Isemann         item.Resize(num_threads, t);
2025*7caa17caSRaphael Isemann         for (size_t i = 0; i < num_threads; ++i) {
2026*7caa17caSRaphael Isemann           item[i].SetIdentifier(threads.GetThreadAtIndex(i)->GetID());
2027*7caa17caSRaphael Isemann           item[i].SetMightHaveChildren(true);
2028*7caa17caSRaphael Isemann         }
2029*7caa17caSRaphael Isemann         return;
2030*7caa17caSRaphael Isemann       }
2031*7caa17caSRaphael Isemann     }
2032*7caa17caSRaphael Isemann     item.ClearChildren();
2033*7caa17caSRaphael Isemann   }
2034*7caa17caSRaphael Isemann 
2035*7caa17caSRaphael Isemann   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
2036*7caa17caSRaphael Isemann 
2037*7caa17caSRaphael Isemann protected:
2038*7caa17caSRaphael Isemann   std::shared_ptr<ThreadTreeDelegate> m_thread_delegate_sp;
2039*7caa17caSRaphael Isemann   Debugger &m_debugger;
2040*7caa17caSRaphael Isemann   uint32_t m_stop_id;
2041*7caa17caSRaphael Isemann   FormatEntity::Entry m_format;
2042*7caa17caSRaphael Isemann };
2043*7caa17caSRaphael Isemann 
2044*7caa17caSRaphael Isemann class ValueObjectListDelegate : public WindowDelegate {
2045*7caa17caSRaphael Isemann public:
2046*7caa17caSRaphael Isemann   ValueObjectListDelegate()
2047*7caa17caSRaphael Isemann       : m_rows(), m_selected_row(nullptr), m_selected_row_idx(0),
2048*7caa17caSRaphael Isemann         m_first_visible_row(0), m_num_rows(0), m_max_x(0), m_max_y(0) {}
2049*7caa17caSRaphael Isemann 
2050*7caa17caSRaphael Isemann   ValueObjectListDelegate(ValueObjectList &valobj_list)
2051*7caa17caSRaphael Isemann       : m_rows(), m_selected_row(nullptr), m_selected_row_idx(0),
2052*7caa17caSRaphael Isemann         m_first_visible_row(0), m_num_rows(0), m_max_x(0), m_max_y(0) {
2053*7caa17caSRaphael Isemann     SetValues(valobj_list);
2054*7caa17caSRaphael Isemann   }
2055*7caa17caSRaphael Isemann 
2056*7caa17caSRaphael Isemann   ~ValueObjectListDelegate() override = default;
2057*7caa17caSRaphael Isemann 
2058*7caa17caSRaphael Isemann   void SetValues(ValueObjectList &valobj_list) {
2059*7caa17caSRaphael Isemann     m_selected_row = nullptr;
2060*7caa17caSRaphael Isemann     m_selected_row_idx = 0;
2061*7caa17caSRaphael Isemann     m_first_visible_row = 0;
2062*7caa17caSRaphael Isemann     m_num_rows = 0;
2063*7caa17caSRaphael Isemann     m_rows.clear();
2064*7caa17caSRaphael Isemann     for (auto &valobj_sp : valobj_list.GetObjects())
2065*7caa17caSRaphael Isemann       m_rows.push_back(Row(valobj_sp, nullptr));
2066*7caa17caSRaphael Isemann   }
2067*7caa17caSRaphael Isemann 
2068*7caa17caSRaphael Isemann   bool WindowDelegateDraw(Window &window, bool force) override {
2069*7caa17caSRaphael Isemann     m_num_rows = 0;
2070*7caa17caSRaphael Isemann     m_min_x = 2;
2071*7caa17caSRaphael Isemann     m_min_y = 1;
2072*7caa17caSRaphael Isemann     m_max_x = window.GetWidth() - 1;
2073*7caa17caSRaphael Isemann     m_max_y = window.GetHeight() - 1;
2074*7caa17caSRaphael Isemann 
2075*7caa17caSRaphael Isemann     window.Erase();
2076*7caa17caSRaphael Isemann     window.DrawTitleBox(window.GetName());
2077*7caa17caSRaphael Isemann 
2078*7caa17caSRaphael Isemann     const int num_visible_rows = NumVisibleRows();
2079*7caa17caSRaphael Isemann     const int num_rows = CalculateTotalNumberRows(m_rows);
2080*7caa17caSRaphael Isemann 
2081*7caa17caSRaphael Isemann     // If we unexpanded while having something selected our total number of
2082*7caa17caSRaphael Isemann     // rows is less than the num visible rows, then make sure we show all the
2083*7caa17caSRaphael Isemann     // rows by setting the first visible row accordingly.
2084*7caa17caSRaphael Isemann     if (m_first_visible_row > 0 && num_rows < num_visible_rows)
2085*7caa17caSRaphael Isemann       m_first_visible_row = 0;
2086*7caa17caSRaphael Isemann 
2087*7caa17caSRaphael Isemann     // Make sure the selected row is always visible
2088*7caa17caSRaphael Isemann     if (m_selected_row_idx < m_first_visible_row)
2089*7caa17caSRaphael Isemann       m_first_visible_row = m_selected_row_idx;
2090*7caa17caSRaphael Isemann     else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
2091*7caa17caSRaphael Isemann       m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
2092*7caa17caSRaphael Isemann 
2093*7caa17caSRaphael Isemann     DisplayRows(window, m_rows, g_options);
2094*7caa17caSRaphael Isemann 
2095*7caa17caSRaphael Isemann     // Get the selected row
2096*7caa17caSRaphael Isemann     m_selected_row = GetRowForRowIndex(m_selected_row_idx);
2097*7caa17caSRaphael Isemann     // Keep the cursor on the selected row so the highlight and the cursor are
2098*7caa17caSRaphael Isemann     // always on the same line
2099*7caa17caSRaphael Isemann     if (m_selected_row)
2100*7caa17caSRaphael Isemann       window.MoveCursor(m_selected_row->x, m_selected_row->y);
2101*7caa17caSRaphael Isemann 
2102*7caa17caSRaphael Isemann     return true; // Drawing handled
2103*7caa17caSRaphael Isemann   }
2104*7caa17caSRaphael Isemann 
2105*7caa17caSRaphael Isemann   KeyHelp *WindowDelegateGetKeyHelp() override {
2106*7caa17caSRaphael Isemann     static curses::KeyHelp g_source_view_key_help[] = {
2107*7caa17caSRaphael Isemann         {KEY_UP, "Select previous item"},
2108*7caa17caSRaphael Isemann         {KEY_DOWN, "Select next item"},
2109*7caa17caSRaphael Isemann         {KEY_RIGHT, "Expand selected item"},
2110*7caa17caSRaphael Isemann         {KEY_LEFT, "Unexpand selected item or select parent if not expanded"},
2111*7caa17caSRaphael Isemann         {KEY_PPAGE, "Page up"},
2112*7caa17caSRaphael Isemann         {KEY_NPAGE, "Page down"},
2113*7caa17caSRaphael Isemann         {'A', "Format as annotated address"},
2114*7caa17caSRaphael Isemann         {'b', "Format as binary"},
2115*7caa17caSRaphael Isemann         {'B', "Format as hex bytes with ASCII"},
2116*7caa17caSRaphael Isemann         {'c', "Format as character"},
2117*7caa17caSRaphael Isemann         {'d', "Format as a signed integer"},
2118*7caa17caSRaphael Isemann         {'D', "Format selected value using the default format for the type"},
2119*7caa17caSRaphael Isemann         {'f', "Format as float"},
2120*7caa17caSRaphael Isemann         {'h', "Show help dialog"},
2121*7caa17caSRaphael Isemann         {'i', "Format as instructions"},
2122*7caa17caSRaphael Isemann         {'o', "Format as octal"},
2123*7caa17caSRaphael Isemann         {'p', "Format as pointer"},
2124*7caa17caSRaphael Isemann         {'s', "Format as C string"},
2125*7caa17caSRaphael Isemann         {'t', "Toggle showing/hiding type names"},
2126*7caa17caSRaphael Isemann         {'u', "Format as an unsigned integer"},
2127*7caa17caSRaphael Isemann         {'x', "Format as hex"},
2128*7caa17caSRaphael Isemann         {'X', "Format as uppercase hex"},
2129*7caa17caSRaphael Isemann         {' ', "Toggle item expansion"},
2130*7caa17caSRaphael Isemann         {',', "Page up"},
2131*7caa17caSRaphael Isemann         {'.', "Page down"},
2132*7caa17caSRaphael Isemann         {'\0', nullptr}};
2133*7caa17caSRaphael Isemann     return g_source_view_key_help;
2134*7caa17caSRaphael Isemann   }
2135*7caa17caSRaphael Isemann 
2136*7caa17caSRaphael Isemann   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
2137*7caa17caSRaphael Isemann     switch (c) {
2138*7caa17caSRaphael Isemann     case 'x':
2139*7caa17caSRaphael Isemann     case 'X':
2140*7caa17caSRaphael Isemann     case 'o':
2141*7caa17caSRaphael Isemann     case 's':
2142*7caa17caSRaphael Isemann     case 'u':
2143*7caa17caSRaphael Isemann     case 'd':
2144*7caa17caSRaphael Isemann     case 'D':
2145*7caa17caSRaphael Isemann     case 'i':
2146*7caa17caSRaphael Isemann     case 'A':
2147*7caa17caSRaphael Isemann     case 'p':
2148*7caa17caSRaphael Isemann     case 'c':
2149*7caa17caSRaphael Isemann     case 'b':
2150*7caa17caSRaphael Isemann     case 'B':
2151*7caa17caSRaphael Isemann     case 'f':
2152*7caa17caSRaphael Isemann       // Change the format for the currently selected item
2153*7caa17caSRaphael Isemann       if (m_selected_row) {
2154*7caa17caSRaphael Isemann         auto valobj_sp = m_selected_row->value.GetSP();
2155*7caa17caSRaphael Isemann         if (valobj_sp)
2156*7caa17caSRaphael Isemann           valobj_sp->SetFormat(FormatForChar(c));
2157*7caa17caSRaphael Isemann       }
2158*7caa17caSRaphael Isemann       return eKeyHandled;
2159*7caa17caSRaphael Isemann 
2160*7caa17caSRaphael Isemann     case 't':
2161*7caa17caSRaphael Isemann       // Toggle showing type names
2162*7caa17caSRaphael Isemann       g_options.show_types = !g_options.show_types;
2163*7caa17caSRaphael Isemann       return eKeyHandled;
2164*7caa17caSRaphael Isemann 
2165*7caa17caSRaphael Isemann     case ',':
2166*7caa17caSRaphael Isemann     case KEY_PPAGE:
2167*7caa17caSRaphael Isemann       // Page up key
2168*7caa17caSRaphael Isemann       if (m_first_visible_row > 0) {
2169*7caa17caSRaphael Isemann         if (static_cast<int>(m_first_visible_row) > m_max_y)
2170*7caa17caSRaphael Isemann           m_first_visible_row -= m_max_y;
2171*7caa17caSRaphael Isemann         else
2172*7caa17caSRaphael Isemann           m_first_visible_row = 0;
2173*7caa17caSRaphael Isemann         m_selected_row_idx = m_first_visible_row;
2174*7caa17caSRaphael Isemann       }
2175*7caa17caSRaphael Isemann       return eKeyHandled;
2176*7caa17caSRaphael Isemann 
2177*7caa17caSRaphael Isemann     case '.':
2178*7caa17caSRaphael Isemann     case KEY_NPAGE:
2179*7caa17caSRaphael Isemann       // Page down key
2180*7caa17caSRaphael Isemann       if (m_num_rows > static_cast<size_t>(m_max_y)) {
2181*7caa17caSRaphael Isemann         if (m_first_visible_row + m_max_y < m_num_rows) {
2182*7caa17caSRaphael Isemann           m_first_visible_row += m_max_y;
2183*7caa17caSRaphael Isemann           m_selected_row_idx = m_first_visible_row;
2184*7caa17caSRaphael Isemann         }
2185*7caa17caSRaphael Isemann       }
2186*7caa17caSRaphael Isemann       return eKeyHandled;
2187*7caa17caSRaphael Isemann 
2188*7caa17caSRaphael Isemann     case KEY_UP:
2189*7caa17caSRaphael Isemann       if (m_selected_row_idx > 0)
2190*7caa17caSRaphael Isemann         --m_selected_row_idx;
2191*7caa17caSRaphael Isemann       return eKeyHandled;
2192*7caa17caSRaphael Isemann 
2193*7caa17caSRaphael Isemann     case KEY_DOWN:
2194*7caa17caSRaphael Isemann       if (m_selected_row_idx + 1 < m_num_rows)
2195*7caa17caSRaphael Isemann         ++m_selected_row_idx;
2196*7caa17caSRaphael Isemann       return eKeyHandled;
2197*7caa17caSRaphael Isemann 
2198*7caa17caSRaphael Isemann     case KEY_RIGHT:
2199*7caa17caSRaphael Isemann       if (m_selected_row) {
2200*7caa17caSRaphael Isemann         if (!m_selected_row->expanded)
2201*7caa17caSRaphael Isemann           m_selected_row->Expand();
2202*7caa17caSRaphael Isemann       }
2203*7caa17caSRaphael Isemann       return eKeyHandled;
2204*7caa17caSRaphael Isemann 
2205*7caa17caSRaphael Isemann     case KEY_LEFT:
2206*7caa17caSRaphael Isemann       if (m_selected_row) {
2207*7caa17caSRaphael Isemann         if (m_selected_row->expanded)
2208*7caa17caSRaphael Isemann           m_selected_row->Unexpand();
2209*7caa17caSRaphael Isemann         else if (m_selected_row->parent)
2210*7caa17caSRaphael Isemann           m_selected_row_idx = m_selected_row->parent->row_idx;
2211*7caa17caSRaphael Isemann       }
2212*7caa17caSRaphael Isemann       return eKeyHandled;
2213*7caa17caSRaphael Isemann 
2214*7caa17caSRaphael Isemann     case ' ':
2215*7caa17caSRaphael Isemann       // Toggle expansion state when SPACE is pressed
2216*7caa17caSRaphael Isemann       if (m_selected_row) {
2217*7caa17caSRaphael Isemann         if (m_selected_row->expanded)
2218*7caa17caSRaphael Isemann           m_selected_row->Unexpand();
2219*7caa17caSRaphael Isemann         else
2220*7caa17caSRaphael Isemann           m_selected_row->Expand();
2221*7caa17caSRaphael Isemann       }
2222*7caa17caSRaphael Isemann       return eKeyHandled;
2223*7caa17caSRaphael Isemann 
2224*7caa17caSRaphael Isemann     case 'h':
2225*7caa17caSRaphael Isemann       window.CreateHelpSubwindow();
2226*7caa17caSRaphael Isemann       return eKeyHandled;
2227*7caa17caSRaphael Isemann 
2228*7caa17caSRaphael Isemann     default:
2229*7caa17caSRaphael Isemann       break;
2230*7caa17caSRaphael Isemann     }
2231*7caa17caSRaphael Isemann     return eKeyNotHandled;
2232*7caa17caSRaphael Isemann   }
2233*7caa17caSRaphael Isemann 
2234*7caa17caSRaphael Isemann protected:
2235*7caa17caSRaphael Isemann   std::vector<Row> m_rows;
2236*7caa17caSRaphael Isemann   Row *m_selected_row;
2237*7caa17caSRaphael Isemann   uint32_t m_selected_row_idx;
2238*7caa17caSRaphael Isemann   uint32_t m_first_visible_row;
2239*7caa17caSRaphael Isemann   uint32_t m_num_rows;
2240*7caa17caSRaphael Isemann   int m_min_x;
2241*7caa17caSRaphael Isemann   int m_min_y;
2242*7caa17caSRaphael Isemann   int m_max_x;
2243*7caa17caSRaphael Isemann   int m_max_y;
2244*7caa17caSRaphael Isemann 
2245*7caa17caSRaphael Isemann   static Format FormatForChar(int c) {
2246*7caa17caSRaphael Isemann     switch (c) {
2247*7caa17caSRaphael Isemann     case 'x':
2248*7caa17caSRaphael Isemann       return eFormatHex;
2249*7caa17caSRaphael Isemann     case 'X':
2250*7caa17caSRaphael Isemann       return eFormatHexUppercase;
2251*7caa17caSRaphael Isemann     case 'o':
2252*7caa17caSRaphael Isemann       return eFormatOctal;
2253*7caa17caSRaphael Isemann     case 's':
2254*7caa17caSRaphael Isemann       return eFormatCString;
2255*7caa17caSRaphael Isemann     case 'u':
2256*7caa17caSRaphael Isemann       return eFormatUnsigned;
2257*7caa17caSRaphael Isemann     case 'd':
2258*7caa17caSRaphael Isemann       return eFormatDecimal;
2259*7caa17caSRaphael Isemann     case 'D':
2260*7caa17caSRaphael Isemann       return eFormatDefault;
2261*7caa17caSRaphael Isemann     case 'i':
2262*7caa17caSRaphael Isemann       return eFormatInstruction;
2263*7caa17caSRaphael Isemann     case 'A':
2264*7caa17caSRaphael Isemann       return eFormatAddressInfo;
2265*7caa17caSRaphael Isemann     case 'p':
2266*7caa17caSRaphael Isemann       return eFormatPointer;
2267*7caa17caSRaphael Isemann     case 'c':
2268*7caa17caSRaphael Isemann       return eFormatChar;
2269*7caa17caSRaphael Isemann     case 'b':
2270*7caa17caSRaphael Isemann       return eFormatBinary;
2271*7caa17caSRaphael Isemann     case 'B':
2272*7caa17caSRaphael Isemann       return eFormatBytesWithASCII;
2273*7caa17caSRaphael Isemann     case 'f':
2274*7caa17caSRaphael Isemann       return eFormatFloat;
2275*7caa17caSRaphael Isemann     }
2276*7caa17caSRaphael Isemann     return eFormatDefault;
2277*7caa17caSRaphael Isemann   }
2278*7caa17caSRaphael Isemann 
2279*7caa17caSRaphael Isemann   bool DisplayRowObject(Window &window, Row &row, DisplayOptions &options,
2280*7caa17caSRaphael Isemann                         bool highlight, bool last_child) {
2281*7caa17caSRaphael Isemann     ValueObject *valobj = row.value.GetSP().get();
2282*7caa17caSRaphael Isemann 
2283*7caa17caSRaphael Isemann     if (valobj == nullptr)
2284*7caa17caSRaphael Isemann       return false;
2285*7caa17caSRaphael Isemann 
2286*7caa17caSRaphael Isemann     const char *type_name =
2287*7caa17caSRaphael Isemann         options.show_types ? valobj->GetTypeName().GetCString() : nullptr;
2288*7caa17caSRaphael Isemann     const char *name = valobj->GetName().GetCString();
2289*7caa17caSRaphael Isemann     const char *value = valobj->GetValueAsCString();
2290*7caa17caSRaphael Isemann     const char *summary = valobj->GetSummaryAsCString();
2291*7caa17caSRaphael Isemann 
2292*7caa17caSRaphael Isemann     window.MoveCursor(row.x, row.y);
2293*7caa17caSRaphael Isemann 
2294*7caa17caSRaphael Isemann     row.DrawTree(window);
2295*7caa17caSRaphael Isemann 
2296*7caa17caSRaphael Isemann     if (highlight)
2297*7caa17caSRaphael Isemann       window.AttributeOn(A_REVERSE);
2298*7caa17caSRaphael Isemann 
2299*7caa17caSRaphael Isemann     if (type_name && type_name[0])
2300*7caa17caSRaphael Isemann       window.Printf("(%s) ", type_name);
2301*7caa17caSRaphael Isemann 
2302*7caa17caSRaphael Isemann     if (name && name[0])
2303*7caa17caSRaphael Isemann       window.PutCString(name);
2304*7caa17caSRaphael Isemann 
2305*7caa17caSRaphael Isemann     attr_t changd_attr = 0;
2306*7caa17caSRaphael Isemann     if (valobj->GetValueDidChange())
2307*7caa17caSRaphael Isemann       changd_attr = COLOR_PAIR(5) | A_BOLD;
2308*7caa17caSRaphael Isemann 
2309*7caa17caSRaphael Isemann     if (value && value[0]) {
2310*7caa17caSRaphael Isemann       window.PutCString(" = ");
2311*7caa17caSRaphael Isemann       if (changd_attr)
2312*7caa17caSRaphael Isemann         window.AttributeOn(changd_attr);
2313*7caa17caSRaphael Isemann       window.PutCString(value);
2314*7caa17caSRaphael Isemann       if (changd_attr)
2315*7caa17caSRaphael Isemann         window.AttributeOff(changd_attr);
2316*7caa17caSRaphael Isemann     }
2317*7caa17caSRaphael Isemann 
2318*7caa17caSRaphael Isemann     if (summary && summary[0]) {
2319*7caa17caSRaphael Isemann       window.PutChar(' ');
2320*7caa17caSRaphael Isemann       if (changd_attr)
2321*7caa17caSRaphael Isemann         window.AttributeOn(changd_attr);
2322*7caa17caSRaphael Isemann       window.PutCString(summary);
2323*7caa17caSRaphael Isemann       if (changd_attr)
2324*7caa17caSRaphael Isemann         window.AttributeOff(changd_attr);
2325*7caa17caSRaphael Isemann     }
2326*7caa17caSRaphael Isemann 
2327*7caa17caSRaphael Isemann     if (highlight)
2328*7caa17caSRaphael Isemann       window.AttributeOff(A_REVERSE);
2329*7caa17caSRaphael Isemann 
2330*7caa17caSRaphael Isemann     return true;
2331*7caa17caSRaphael Isemann   }
2332*7caa17caSRaphael Isemann 
2333*7caa17caSRaphael Isemann   void DisplayRows(Window &window, std::vector<Row> &rows,
2334*7caa17caSRaphael Isemann                    DisplayOptions &options) {
2335*7caa17caSRaphael Isemann     // >   0x25B7
2336*7caa17caSRaphael Isemann     // \/  0x25BD
2337*7caa17caSRaphael Isemann 
2338*7caa17caSRaphael Isemann     bool window_is_active = window.IsActive();
2339*7caa17caSRaphael Isemann     for (auto &row : rows) {
2340*7caa17caSRaphael Isemann       const bool last_child = row.parent && &rows[rows.size() - 1] == &row;
2341*7caa17caSRaphael Isemann       // Save the row index in each Row structure
2342*7caa17caSRaphael Isemann       row.row_idx = m_num_rows;
2343*7caa17caSRaphael Isemann       if ((m_num_rows >= m_first_visible_row) &&
2344*7caa17caSRaphael Isemann           ((m_num_rows - m_first_visible_row) <
2345*7caa17caSRaphael Isemann            static_cast<size_t>(NumVisibleRows()))) {
2346*7caa17caSRaphael Isemann         row.x = m_min_x;
2347*7caa17caSRaphael Isemann         row.y = m_num_rows - m_first_visible_row + 1;
2348*7caa17caSRaphael Isemann         if (DisplayRowObject(window, row, options,
2349*7caa17caSRaphael Isemann                              window_is_active &&
2350*7caa17caSRaphael Isemann                                  m_num_rows == m_selected_row_idx,
2351*7caa17caSRaphael Isemann                              last_child)) {
2352*7caa17caSRaphael Isemann           ++m_num_rows;
2353*7caa17caSRaphael Isemann         } else {
2354*7caa17caSRaphael Isemann           row.x = 0;
2355*7caa17caSRaphael Isemann           row.y = 0;
2356*7caa17caSRaphael Isemann         }
2357*7caa17caSRaphael Isemann       } else {
2358*7caa17caSRaphael Isemann         row.x = 0;
2359*7caa17caSRaphael Isemann         row.y = 0;
2360*7caa17caSRaphael Isemann         ++m_num_rows;
2361*7caa17caSRaphael Isemann       }
2362*7caa17caSRaphael Isemann 
2363*7caa17caSRaphael Isemann       auto &children = row.GetChildren();
2364*7caa17caSRaphael Isemann       if (row.expanded && !children.empty()) {
2365*7caa17caSRaphael Isemann         DisplayRows(window, children, options);
2366*7caa17caSRaphael Isemann       }
2367*7caa17caSRaphael Isemann     }
2368*7caa17caSRaphael Isemann   }
2369*7caa17caSRaphael Isemann 
2370*7caa17caSRaphael Isemann   int CalculateTotalNumberRows(std::vector<Row> &rows) {
2371*7caa17caSRaphael Isemann     int row_count = 0;
2372*7caa17caSRaphael Isemann     for (auto &row : rows) {
2373*7caa17caSRaphael Isemann       ++row_count;
2374*7caa17caSRaphael Isemann       if (row.expanded)
2375*7caa17caSRaphael Isemann         row_count += CalculateTotalNumberRows(row.GetChildren());
2376*7caa17caSRaphael Isemann     }
2377*7caa17caSRaphael Isemann     return row_count;
2378*7caa17caSRaphael Isemann   }
2379*7caa17caSRaphael Isemann 
2380*7caa17caSRaphael Isemann   static Row *GetRowForRowIndexImpl(std::vector<Row> &rows, size_t &row_index) {
2381*7caa17caSRaphael Isemann     for (auto &row : rows) {
2382*7caa17caSRaphael Isemann       if (row_index == 0)
2383*7caa17caSRaphael Isemann         return &row;
2384*7caa17caSRaphael Isemann       else {
2385*7caa17caSRaphael Isemann         --row_index;
2386*7caa17caSRaphael Isemann         auto &children = row.GetChildren();
2387*7caa17caSRaphael Isemann         if (row.expanded && !children.empty()) {
2388*7caa17caSRaphael Isemann           Row *result = GetRowForRowIndexImpl(children, row_index);
2389*7caa17caSRaphael Isemann           if (result)
2390*7caa17caSRaphael Isemann             return result;
2391*7caa17caSRaphael Isemann         }
2392*7caa17caSRaphael Isemann       }
2393*7caa17caSRaphael Isemann     }
2394*7caa17caSRaphael Isemann     return nullptr;
2395*7caa17caSRaphael Isemann   }
2396*7caa17caSRaphael Isemann 
2397*7caa17caSRaphael Isemann   Row *GetRowForRowIndex(size_t row_index) {
2398*7caa17caSRaphael Isemann     return GetRowForRowIndexImpl(m_rows, row_index);
2399*7caa17caSRaphael Isemann   }
2400*7caa17caSRaphael Isemann 
2401*7caa17caSRaphael Isemann   int NumVisibleRows() const { return m_max_y - m_min_y; }
2402*7caa17caSRaphael Isemann 
2403*7caa17caSRaphael Isemann   static DisplayOptions g_options;
2404*7caa17caSRaphael Isemann };
2405*7caa17caSRaphael Isemann 
2406*7caa17caSRaphael Isemann class FrameVariablesWindowDelegate : public ValueObjectListDelegate {
2407*7caa17caSRaphael Isemann public:
2408*7caa17caSRaphael Isemann   FrameVariablesWindowDelegate(Debugger &debugger)
2409*7caa17caSRaphael Isemann       : ValueObjectListDelegate(), m_debugger(debugger),
2410*7caa17caSRaphael Isemann         m_frame_block(nullptr) {}
2411*7caa17caSRaphael Isemann 
2412*7caa17caSRaphael Isemann   ~FrameVariablesWindowDelegate() override = default;
2413*7caa17caSRaphael Isemann 
2414*7caa17caSRaphael Isemann   const char *WindowDelegateGetHelpText() override {
2415*7caa17caSRaphael Isemann     return "Frame variable window keyboard shortcuts:";
2416*7caa17caSRaphael Isemann   }
2417*7caa17caSRaphael Isemann 
2418*7caa17caSRaphael Isemann   bool WindowDelegateDraw(Window &window, bool force) override {
2419*7caa17caSRaphael Isemann     ExecutionContext exe_ctx(
2420*7caa17caSRaphael Isemann         m_debugger.GetCommandInterpreter().GetExecutionContext());
2421*7caa17caSRaphael Isemann     Process *process = exe_ctx.GetProcessPtr();
2422*7caa17caSRaphael Isemann     Block *frame_block = nullptr;
2423*7caa17caSRaphael Isemann     StackFrame *frame = nullptr;
2424*7caa17caSRaphael Isemann 
2425*7caa17caSRaphael Isemann     if (process) {
2426*7caa17caSRaphael Isemann       StateType state = process->GetState();
2427*7caa17caSRaphael Isemann       if (StateIsStoppedState(state, true)) {
2428*7caa17caSRaphael Isemann         frame = exe_ctx.GetFramePtr();
2429*7caa17caSRaphael Isemann         if (frame)
2430*7caa17caSRaphael Isemann           frame_block = frame->GetFrameBlock();
2431*7caa17caSRaphael Isemann       } else if (StateIsRunningState(state)) {
2432*7caa17caSRaphael Isemann         return true; // Don't do any updating when we are running
2433*7caa17caSRaphael Isemann       }
2434*7caa17caSRaphael Isemann     }
2435*7caa17caSRaphael Isemann 
2436*7caa17caSRaphael Isemann     ValueObjectList local_values;
2437*7caa17caSRaphael Isemann     if (frame_block) {
2438*7caa17caSRaphael Isemann       // Only update the variables if they have changed
2439*7caa17caSRaphael Isemann       if (m_frame_block != frame_block) {
2440*7caa17caSRaphael Isemann         m_frame_block = frame_block;
2441*7caa17caSRaphael Isemann 
2442*7caa17caSRaphael Isemann         VariableList *locals = frame->GetVariableList(true);
2443*7caa17caSRaphael Isemann         if (locals) {
2444*7caa17caSRaphael Isemann           const DynamicValueType use_dynamic = eDynamicDontRunTarget;
2445*7caa17caSRaphael Isemann           for (const VariableSP &local_sp : *locals) {
2446*7caa17caSRaphael Isemann             ValueObjectSP value_sp =
2447*7caa17caSRaphael Isemann                 frame->GetValueObjectForFrameVariable(local_sp, use_dynamic);
2448*7caa17caSRaphael Isemann             if (value_sp) {
2449*7caa17caSRaphael Isemann               ValueObjectSP synthetic_value_sp = value_sp->GetSyntheticValue();
2450*7caa17caSRaphael Isemann               if (synthetic_value_sp)
2451*7caa17caSRaphael Isemann                 local_values.Append(synthetic_value_sp);
2452*7caa17caSRaphael Isemann               else
2453*7caa17caSRaphael Isemann                 local_values.Append(value_sp);
2454*7caa17caSRaphael Isemann             }
2455*7caa17caSRaphael Isemann           }
2456*7caa17caSRaphael Isemann           // Update the values
2457*7caa17caSRaphael Isemann           SetValues(local_values);
2458*7caa17caSRaphael Isemann         }
2459*7caa17caSRaphael Isemann       }
2460*7caa17caSRaphael Isemann     } else {
2461*7caa17caSRaphael Isemann       m_frame_block = nullptr;
2462*7caa17caSRaphael Isemann       // Update the values with an empty list if there is no frame
2463*7caa17caSRaphael Isemann       SetValues(local_values);
2464*7caa17caSRaphael Isemann     }
2465*7caa17caSRaphael Isemann 
2466*7caa17caSRaphael Isemann     return ValueObjectListDelegate::WindowDelegateDraw(window, force);
2467*7caa17caSRaphael Isemann   }
2468*7caa17caSRaphael Isemann 
2469*7caa17caSRaphael Isemann protected:
2470*7caa17caSRaphael Isemann   Debugger &m_debugger;
2471*7caa17caSRaphael Isemann   Block *m_frame_block;
2472*7caa17caSRaphael Isemann };
2473*7caa17caSRaphael Isemann 
2474*7caa17caSRaphael Isemann class RegistersWindowDelegate : public ValueObjectListDelegate {
2475*7caa17caSRaphael Isemann public:
2476*7caa17caSRaphael Isemann   RegistersWindowDelegate(Debugger &debugger)
2477*7caa17caSRaphael Isemann       : ValueObjectListDelegate(), m_debugger(debugger) {}
2478*7caa17caSRaphael Isemann 
2479*7caa17caSRaphael Isemann   ~RegistersWindowDelegate() override = default;
2480*7caa17caSRaphael Isemann 
2481*7caa17caSRaphael Isemann   const char *WindowDelegateGetHelpText() override {
2482*7caa17caSRaphael Isemann     return "Register window keyboard shortcuts:";
2483*7caa17caSRaphael Isemann   }
2484*7caa17caSRaphael Isemann 
2485*7caa17caSRaphael Isemann   bool WindowDelegateDraw(Window &window, bool force) override {
2486*7caa17caSRaphael Isemann     ExecutionContext exe_ctx(
2487*7caa17caSRaphael Isemann         m_debugger.GetCommandInterpreter().GetExecutionContext());
2488*7caa17caSRaphael Isemann     StackFrame *frame = exe_ctx.GetFramePtr();
2489*7caa17caSRaphael Isemann 
2490*7caa17caSRaphael Isemann     ValueObjectList value_list;
2491*7caa17caSRaphael Isemann     if (frame) {
2492*7caa17caSRaphael Isemann       if (frame->GetStackID() != m_stack_id) {
2493*7caa17caSRaphael Isemann         m_stack_id = frame->GetStackID();
2494*7caa17caSRaphael Isemann         RegisterContextSP reg_ctx(frame->GetRegisterContext());
2495*7caa17caSRaphael Isemann         if (reg_ctx) {
2496*7caa17caSRaphael Isemann           const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
2497*7caa17caSRaphael Isemann           for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) {
2498*7caa17caSRaphael Isemann             value_list.Append(
2499*7caa17caSRaphael Isemann                 ValueObjectRegisterSet::Create(frame, reg_ctx, set_idx));
2500*7caa17caSRaphael Isemann           }
2501*7caa17caSRaphael Isemann         }
2502*7caa17caSRaphael Isemann         SetValues(value_list);
2503*7caa17caSRaphael Isemann       }
2504*7caa17caSRaphael Isemann     } else {
2505*7caa17caSRaphael Isemann       Process *process = exe_ctx.GetProcessPtr();
2506*7caa17caSRaphael Isemann       if (process && process->IsAlive())
2507*7caa17caSRaphael Isemann         return true; // Don't do any updating if we are running
2508*7caa17caSRaphael Isemann       else {
2509*7caa17caSRaphael Isemann         // Update the values with an empty list if there is no process or the
2510*7caa17caSRaphael Isemann         // process isn't alive anymore
2511*7caa17caSRaphael Isemann         SetValues(value_list);
2512*7caa17caSRaphael Isemann       }
2513*7caa17caSRaphael Isemann     }
2514*7caa17caSRaphael Isemann     return ValueObjectListDelegate::WindowDelegateDraw(window, force);
2515*7caa17caSRaphael Isemann   }
2516*7caa17caSRaphael Isemann 
2517*7caa17caSRaphael Isemann protected:
2518*7caa17caSRaphael Isemann   Debugger &m_debugger;
2519*7caa17caSRaphael Isemann   StackID m_stack_id;
2520*7caa17caSRaphael Isemann };
2521*7caa17caSRaphael Isemann 
2522*7caa17caSRaphael Isemann static const char *CursesKeyToCString(int ch) {
2523*7caa17caSRaphael Isemann   static char g_desc[32];
2524*7caa17caSRaphael Isemann   if (ch >= KEY_F0 && ch < KEY_F0 + 64) {
2525*7caa17caSRaphael Isemann     snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0);
2526*7caa17caSRaphael Isemann     return g_desc;
2527*7caa17caSRaphael Isemann   }
2528*7caa17caSRaphael Isemann   switch (ch) {
2529*7caa17caSRaphael Isemann   case KEY_DOWN:
2530*7caa17caSRaphael Isemann     return "down";
2531*7caa17caSRaphael Isemann   case KEY_UP:
2532*7caa17caSRaphael Isemann     return "up";
2533*7caa17caSRaphael Isemann   case KEY_LEFT:
2534*7caa17caSRaphael Isemann     return "left";
2535*7caa17caSRaphael Isemann   case KEY_RIGHT:
2536*7caa17caSRaphael Isemann     return "right";
2537*7caa17caSRaphael Isemann   case KEY_HOME:
2538*7caa17caSRaphael Isemann     return "home";
2539*7caa17caSRaphael Isemann   case KEY_BACKSPACE:
2540*7caa17caSRaphael Isemann     return "backspace";
2541*7caa17caSRaphael Isemann   case KEY_DL:
2542*7caa17caSRaphael Isemann     return "delete-line";
2543*7caa17caSRaphael Isemann   case KEY_IL:
2544*7caa17caSRaphael Isemann     return "insert-line";
2545*7caa17caSRaphael Isemann   case KEY_DC:
2546*7caa17caSRaphael Isemann     return "delete-char";
2547*7caa17caSRaphael Isemann   case KEY_IC:
2548*7caa17caSRaphael Isemann     return "insert-char";
2549*7caa17caSRaphael Isemann   case KEY_CLEAR:
2550*7caa17caSRaphael Isemann     return "clear";
2551*7caa17caSRaphael Isemann   case KEY_EOS:
2552*7caa17caSRaphael Isemann     return "clear-to-eos";
2553*7caa17caSRaphael Isemann   case KEY_EOL:
2554*7caa17caSRaphael Isemann     return "clear-to-eol";
2555*7caa17caSRaphael Isemann   case KEY_SF:
2556*7caa17caSRaphael Isemann     return "scroll-forward";
2557*7caa17caSRaphael Isemann   case KEY_SR:
2558*7caa17caSRaphael Isemann     return "scroll-backward";
2559*7caa17caSRaphael Isemann   case KEY_NPAGE:
2560*7caa17caSRaphael Isemann     return "page-down";
2561*7caa17caSRaphael Isemann   case KEY_PPAGE:
2562*7caa17caSRaphael Isemann     return "page-up";
2563*7caa17caSRaphael Isemann   case KEY_STAB:
2564*7caa17caSRaphael Isemann     return "set-tab";
2565*7caa17caSRaphael Isemann   case KEY_CTAB:
2566*7caa17caSRaphael Isemann     return "clear-tab";
2567*7caa17caSRaphael Isemann   case KEY_CATAB:
2568*7caa17caSRaphael Isemann     return "clear-all-tabs";
2569*7caa17caSRaphael Isemann   case KEY_ENTER:
2570*7caa17caSRaphael Isemann     return "enter";
2571*7caa17caSRaphael Isemann   case KEY_PRINT:
2572*7caa17caSRaphael Isemann     return "print";
2573*7caa17caSRaphael Isemann   case KEY_LL:
2574*7caa17caSRaphael Isemann     return "lower-left key";
2575*7caa17caSRaphael Isemann   case KEY_A1:
2576*7caa17caSRaphael Isemann     return "upper left of keypad";
2577*7caa17caSRaphael Isemann   case KEY_A3:
2578*7caa17caSRaphael Isemann     return "upper right of keypad";
2579*7caa17caSRaphael Isemann   case KEY_B2:
2580*7caa17caSRaphael Isemann     return "center of keypad";
2581*7caa17caSRaphael Isemann   case KEY_C1:
2582*7caa17caSRaphael Isemann     return "lower left of keypad";
2583*7caa17caSRaphael Isemann   case KEY_C3:
2584*7caa17caSRaphael Isemann     return "lower right of keypad";
2585*7caa17caSRaphael Isemann   case KEY_BTAB:
2586*7caa17caSRaphael Isemann     return "back-tab key";
2587*7caa17caSRaphael Isemann   case KEY_BEG:
2588*7caa17caSRaphael Isemann     return "begin key";
2589*7caa17caSRaphael Isemann   case KEY_CANCEL:
2590*7caa17caSRaphael Isemann     return "cancel key";
2591*7caa17caSRaphael Isemann   case KEY_CLOSE:
2592*7caa17caSRaphael Isemann     return "close key";
2593*7caa17caSRaphael Isemann   case KEY_COMMAND:
2594*7caa17caSRaphael Isemann     return "command key";
2595*7caa17caSRaphael Isemann   case KEY_COPY:
2596*7caa17caSRaphael Isemann     return "copy key";
2597*7caa17caSRaphael Isemann   case KEY_CREATE:
2598*7caa17caSRaphael Isemann     return "create key";
2599*7caa17caSRaphael Isemann   case KEY_END:
2600*7caa17caSRaphael Isemann     return "end key";
2601*7caa17caSRaphael Isemann   case KEY_EXIT:
2602*7caa17caSRaphael Isemann     return "exit key";
2603*7caa17caSRaphael Isemann   case KEY_FIND:
2604*7caa17caSRaphael Isemann     return "find key";
2605*7caa17caSRaphael Isemann   case KEY_HELP:
2606*7caa17caSRaphael Isemann     return "help key";
2607*7caa17caSRaphael Isemann   case KEY_MARK:
2608*7caa17caSRaphael Isemann     return "mark key";
2609*7caa17caSRaphael Isemann   case KEY_MESSAGE:
2610*7caa17caSRaphael Isemann     return "message key";
2611*7caa17caSRaphael Isemann   case KEY_MOVE:
2612*7caa17caSRaphael Isemann     return "move key";
2613*7caa17caSRaphael Isemann   case KEY_NEXT:
2614*7caa17caSRaphael Isemann     return "next key";
2615*7caa17caSRaphael Isemann   case KEY_OPEN:
2616*7caa17caSRaphael Isemann     return "open key";
2617*7caa17caSRaphael Isemann   case KEY_OPTIONS:
2618*7caa17caSRaphael Isemann     return "options key";
2619*7caa17caSRaphael Isemann   case KEY_PREVIOUS:
2620*7caa17caSRaphael Isemann     return "previous key";
2621*7caa17caSRaphael Isemann   case KEY_REDO:
2622*7caa17caSRaphael Isemann     return "redo key";
2623*7caa17caSRaphael Isemann   case KEY_REFERENCE:
2624*7caa17caSRaphael Isemann     return "reference key";
2625*7caa17caSRaphael Isemann   case KEY_REFRESH:
2626*7caa17caSRaphael Isemann     return "refresh key";
2627*7caa17caSRaphael Isemann   case KEY_REPLACE:
2628*7caa17caSRaphael Isemann     return "replace key";
2629*7caa17caSRaphael Isemann   case KEY_RESTART:
2630*7caa17caSRaphael Isemann     return "restart key";
2631*7caa17caSRaphael Isemann   case KEY_RESUME:
2632*7caa17caSRaphael Isemann     return "resume key";
2633*7caa17caSRaphael Isemann   case KEY_SAVE:
2634*7caa17caSRaphael Isemann     return "save key";
2635*7caa17caSRaphael Isemann   case KEY_SBEG:
2636*7caa17caSRaphael Isemann     return "shifted begin key";
2637*7caa17caSRaphael Isemann   case KEY_SCANCEL:
2638*7caa17caSRaphael Isemann     return "shifted cancel key";
2639*7caa17caSRaphael Isemann   case KEY_SCOMMAND:
2640*7caa17caSRaphael Isemann     return "shifted command key";
2641*7caa17caSRaphael Isemann   case KEY_SCOPY:
2642*7caa17caSRaphael Isemann     return "shifted copy key";
2643*7caa17caSRaphael Isemann   case KEY_SCREATE:
2644*7caa17caSRaphael Isemann     return "shifted create key";
2645*7caa17caSRaphael Isemann   case KEY_SDC:
2646*7caa17caSRaphael Isemann     return "shifted delete-character key";
2647*7caa17caSRaphael Isemann   case KEY_SDL:
2648*7caa17caSRaphael Isemann     return "shifted delete-line key";
2649*7caa17caSRaphael Isemann   case KEY_SELECT:
2650*7caa17caSRaphael Isemann     return "select key";
2651*7caa17caSRaphael Isemann   case KEY_SEND:
2652*7caa17caSRaphael Isemann     return "shifted end key";
2653*7caa17caSRaphael Isemann   case KEY_SEOL:
2654*7caa17caSRaphael Isemann     return "shifted clear-to-end-of-line key";
2655*7caa17caSRaphael Isemann   case KEY_SEXIT:
2656*7caa17caSRaphael Isemann     return "shifted exit key";
2657*7caa17caSRaphael Isemann   case KEY_SFIND:
2658*7caa17caSRaphael Isemann     return "shifted find key";
2659*7caa17caSRaphael Isemann   case KEY_SHELP:
2660*7caa17caSRaphael Isemann     return "shifted help key";
2661*7caa17caSRaphael Isemann   case KEY_SHOME:
2662*7caa17caSRaphael Isemann     return "shifted home key";
2663*7caa17caSRaphael Isemann   case KEY_SIC:
2664*7caa17caSRaphael Isemann     return "shifted insert-character key";
2665*7caa17caSRaphael Isemann   case KEY_SLEFT:
2666*7caa17caSRaphael Isemann     return "shifted left-arrow key";
2667*7caa17caSRaphael Isemann   case KEY_SMESSAGE:
2668*7caa17caSRaphael Isemann     return "shifted message key";
2669*7caa17caSRaphael Isemann   case KEY_SMOVE:
2670*7caa17caSRaphael Isemann     return "shifted move key";
2671*7caa17caSRaphael Isemann   case KEY_SNEXT:
2672*7caa17caSRaphael Isemann     return "shifted next key";
2673*7caa17caSRaphael Isemann   case KEY_SOPTIONS:
2674*7caa17caSRaphael Isemann     return "shifted options key";
2675*7caa17caSRaphael Isemann   case KEY_SPREVIOUS:
2676*7caa17caSRaphael Isemann     return "shifted previous key";
2677*7caa17caSRaphael Isemann   case KEY_SPRINT:
2678*7caa17caSRaphael Isemann     return "shifted print key";
2679*7caa17caSRaphael Isemann   case KEY_SREDO:
2680*7caa17caSRaphael Isemann     return "shifted redo key";
2681*7caa17caSRaphael Isemann   case KEY_SREPLACE:
2682*7caa17caSRaphael Isemann     return "shifted replace key";
2683*7caa17caSRaphael Isemann   case KEY_SRIGHT:
2684*7caa17caSRaphael Isemann     return "shifted right-arrow key";
2685*7caa17caSRaphael Isemann   case KEY_SRSUME:
2686*7caa17caSRaphael Isemann     return "shifted resume key";
2687*7caa17caSRaphael Isemann   case KEY_SSAVE:
2688*7caa17caSRaphael Isemann     return "shifted save key";
2689*7caa17caSRaphael Isemann   case KEY_SSUSPEND:
2690*7caa17caSRaphael Isemann     return "shifted suspend key";
2691*7caa17caSRaphael Isemann   case KEY_SUNDO:
2692*7caa17caSRaphael Isemann     return "shifted undo key";
2693*7caa17caSRaphael Isemann   case KEY_SUSPEND:
2694*7caa17caSRaphael Isemann     return "suspend key";
2695*7caa17caSRaphael Isemann   case KEY_UNDO:
2696*7caa17caSRaphael Isemann     return "undo key";
2697*7caa17caSRaphael Isemann   case KEY_MOUSE:
2698*7caa17caSRaphael Isemann     return "Mouse event has occurred";
2699*7caa17caSRaphael Isemann   case KEY_RESIZE:
2700*7caa17caSRaphael Isemann     return "Terminal resize event";
2701*7caa17caSRaphael Isemann #ifdef KEY_EVENT
2702*7caa17caSRaphael Isemann   case KEY_EVENT:
2703*7caa17caSRaphael Isemann     return "We were interrupted by an event";
2704*7caa17caSRaphael Isemann #endif
2705*7caa17caSRaphael Isemann   case KEY_RETURN:
2706*7caa17caSRaphael Isemann     return "return";
2707*7caa17caSRaphael Isemann   case ' ':
2708*7caa17caSRaphael Isemann     return "space";
2709*7caa17caSRaphael Isemann   case '\t':
2710*7caa17caSRaphael Isemann     return "tab";
2711*7caa17caSRaphael Isemann   case KEY_ESCAPE:
2712*7caa17caSRaphael Isemann     return "escape";
2713*7caa17caSRaphael Isemann   default:
2714*7caa17caSRaphael Isemann     if (isprint(ch))
2715*7caa17caSRaphael Isemann       snprintf(g_desc, sizeof(g_desc), "%c", ch);
2716*7caa17caSRaphael Isemann     else
2717*7caa17caSRaphael Isemann       snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch);
2718*7caa17caSRaphael Isemann     return g_desc;
2719*7caa17caSRaphael Isemann   }
2720*7caa17caSRaphael Isemann   return nullptr;
2721*7caa17caSRaphael Isemann }
2722*7caa17caSRaphael Isemann 
2723*7caa17caSRaphael Isemann HelpDialogDelegate::HelpDialogDelegate(const char *text,
2724*7caa17caSRaphael Isemann                                        KeyHelp *key_help_array)
2725*7caa17caSRaphael Isemann     : m_text(), m_first_visible_line(0) {
2726*7caa17caSRaphael Isemann   if (text && text[0]) {
2727*7caa17caSRaphael Isemann     m_text.SplitIntoLines(text);
2728*7caa17caSRaphael Isemann     m_text.AppendString("");
2729*7caa17caSRaphael Isemann   }
2730*7caa17caSRaphael Isemann   if (key_help_array) {
2731*7caa17caSRaphael Isemann     for (KeyHelp *key = key_help_array; key->ch; ++key) {
2732*7caa17caSRaphael Isemann       StreamString key_description;
2733*7caa17caSRaphael Isemann       key_description.Printf("%10s - %s", CursesKeyToCString(key->ch),
2734*7caa17caSRaphael Isemann                              key->description);
2735*7caa17caSRaphael Isemann       m_text.AppendString(key_description.GetString());
2736*7caa17caSRaphael Isemann     }
2737*7caa17caSRaphael Isemann   }
2738*7caa17caSRaphael Isemann }
2739*7caa17caSRaphael Isemann 
2740*7caa17caSRaphael Isemann HelpDialogDelegate::~HelpDialogDelegate() = default;
2741*7caa17caSRaphael Isemann 
2742*7caa17caSRaphael Isemann bool HelpDialogDelegate::WindowDelegateDraw(Window &window, bool force) {
2743*7caa17caSRaphael Isemann   window.Erase();
2744*7caa17caSRaphael Isemann   const int window_height = window.GetHeight();
2745*7caa17caSRaphael Isemann   int x = 2;
2746*7caa17caSRaphael Isemann   int y = 1;
2747*7caa17caSRaphael Isemann   const int min_y = y;
2748*7caa17caSRaphael Isemann   const int max_y = window_height - 1 - y;
2749*7caa17caSRaphael Isemann   const size_t num_visible_lines = max_y - min_y + 1;
2750*7caa17caSRaphael Isemann   const size_t num_lines = m_text.GetSize();
2751*7caa17caSRaphael Isemann   const char *bottom_message;
2752*7caa17caSRaphael Isemann   if (num_lines <= num_visible_lines)
2753*7caa17caSRaphael Isemann     bottom_message = "Press any key to exit";
2754*7caa17caSRaphael Isemann   else
2755*7caa17caSRaphael Isemann     bottom_message = "Use arrows to scroll, any other key to exit";
2756*7caa17caSRaphael Isemann   window.DrawTitleBox(window.GetName(), bottom_message);
2757*7caa17caSRaphael Isemann   while (y <= max_y) {
2758*7caa17caSRaphael Isemann     window.MoveCursor(x, y);
2759*7caa17caSRaphael Isemann     window.PutCStringTruncated(
2760*7caa17caSRaphael Isemann         m_text.GetStringAtIndex(m_first_visible_line + y - min_y), 1);
2761*7caa17caSRaphael Isemann     ++y;
2762*7caa17caSRaphael Isemann   }
2763*7caa17caSRaphael Isemann   return true;
2764*7caa17caSRaphael Isemann }
2765*7caa17caSRaphael Isemann 
2766*7caa17caSRaphael Isemann HandleCharResult HelpDialogDelegate::WindowDelegateHandleChar(Window &window,
2767*7caa17caSRaphael Isemann                                                               int key) {
2768*7caa17caSRaphael Isemann   bool done = false;
2769*7caa17caSRaphael Isemann   const size_t num_lines = m_text.GetSize();
2770*7caa17caSRaphael Isemann   const size_t num_visible_lines = window.GetHeight() - 2;
2771*7caa17caSRaphael Isemann 
2772*7caa17caSRaphael Isemann   if (num_lines <= num_visible_lines) {
2773*7caa17caSRaphael Isemann     done = true;
2774*7caa17caSRaphael Isemann     // If we have all lines visible and don't need scrolling, then any key
2775*7caa17caSRaphael Isemann     // press will cause us to exit
2776*7caa17caSRaphael Isemann   } else {
2777*7caa17caSRaphael Isemann     switch (key) {
2778*7caa17caSRaphael Isemann     case KEY_UP:
2779*7caa17caSRaphael Isemann       if (m_first_visible_line > 0)
2780*7caa17caSRaphael Isemann         --m_first_visible_line;
2781*7caa17caSRaphael Isemann       break;
2782*7caa17caSRaphael Isemann 
2783*7caa17caSRaphael Isemann     case KEY_DOWN:
2784*7caa17caSRaphael Isemann       if (m_first_visible_line + num_visible_lines < num_lines)
2785*7caa17caSRaphael Isemann         ++m_first_visible_line;
2786*7caa17caSRaphael Isemann       break;
2787*7caa17caSRaphael Isemann 
2788*7caa17caSRaphael Isemann     case KEY_PPAGE:
2789*7caa17caSRaphael Isemann     case ',':
2790*7caa17caSRaphael Isemann       if (m_first_visible_line > 0) {
2791*7caa17caSRaphael Isemann         if (static_cast<size_t>(m_first_visible_line) >= num_visible_lines)
2792*7caa17caSRaphael Isemann           m_first_visible_line -= num_visible_lines;
2793*7caa17caSRaphael Isemann         else
2794*7caa17caSRaphael Isemann           m_first_visible_line = 0;
2795*7caa17caSRaphael Isemann       }
2796*7caa17caSRaphael Isemann       break;
2797*7caa17caSRaphael Isemann 
2798*7caa17caSRaphael Isemann     case KEY_NPAGE:
2799*7caa17caSRaphael Isemann     case '.':
2800*7caa17caSRaphael Isemann       if (m_first_visible_line + num_visible_lines < num_lines) {
2801*7caa17caSRaphael Isemann         m_first_visible_line += num_visible_lines;
2802*7caa17caSRaphael Isemann         if (static_cast<size_t>(m_first_visible_line) > num_lines)
2803*7caa17caSRaphael Isemann           m_first_visible_line = num_lines - num_visible_lines;
2804*7caa17caSRaphael Isemann       }
2805*7caa17caSRaphael Isemann       break;
2806*7caa17caSRaphael Isemann 
2807*7caa17caSRaphael Isemann     default:
2808*7caa17caSRaphael Isemann       done = true;
2809*7caa17caSRaphael Isemann       break;
2810*7caa17caSRaphael Isemann     }
2811*7caa17caSRaphael Isemann   }
2812*7caa17caSRaphael Isemann   if (done)
2813*7caa17caSRaphael Isemann     window.GetParent()->RemoveSubWindow(&window);
2814*7caa17caSRaphael Isemann   return eKeyHandled;
2815*7caa17caSRaphael Isemann }
2816*7caa17caSRaphael Isemann 
2817*7caa17caSRaphael Isemann class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
2818*7caa17caSRaphael Isemann public:
2819*7caa17caSRaphael Isemann   enum {
2820*7caa17caSRaphael Isemann     eMenuID_LLDB = 1,
2821*7caa17caSRaphael Isemann     eMenuID_LLDBAbout,
2822*7caa17caSRaphael Isemann     eMenuID_LLDBExit,
2823*7caa17caSRaphael Isemann 
2824*7caa17caSRaphael Isemann     eMenuID_Target,
2825*7caa17caSRaphael Isemann     eMenuID_TargetCreate,
2826*7caa17caSRaphael Isemann     eMenuID_TargetDelete,
2827*7caa17caSRaphael Isemann 
2828*7caa17caSRaphael Isemann     eMenuID_Process,
2829*7caa17caSRaphael Isemann     eMenuID_ProcessAttach,
2830*7caa17caSRaphael Isemann     eMenuID_ProcessDetach,
2831*7caa17caSRaphael Isemann     eMenuID_ProcessLaunch,
2832*7caa17caSRaphael Isemann     eMenuID_ProcessContinue,
2833*7caa17caSRaphael Isemann     eMenuID_ProcessHalt,
2834*7caa17caSRaphael Isemann     eMenuID_ProcessKill,
2835*7caa17caSRaphael Isemann 
2836*7caa17caSRaphael Isemann     eMenuID_Thread,
2837*7caa17caSRaphael Isemann     eMenuID_ThreadStepIn,
2838*7caa17caSRaphael Isemann     eMenuID_ThreadStepOver,
2839*7caa17caSRaphael Isemann     eMenuID_ThreadStepOut,
2840*7caa17caSRaphael Isemann 
2841*7caa17caSRaphael Isemann     eMenuID_View,
2842*7caa17caSRaphael Isemann     eMenuID_ViewBacktrace,
2843*7caa17caSRaphael Isemann     eMenuID_ViewRegisters,
2844*7caa17caSRaphael Isemann     eMenuID_ViewSource,
2845*7caa17caSRaphael Isemann     eMenuID_ViewVariables,
2846*7caa17caSRaphael Isemann 
2847*7caa17caSRaphael Isemann     eMenuID_Help,
2848*7caa17caSRaphael Isemann     eMenuID_HelpGUIHelp
2849*7caa17caSRaphael Isemann   };
2850*7caa17caSRaphael Isemann 
2851*7caa17caSRaphael Isemann   ApplicationDelegate(Application &app, Debugger &debugger)
2852*7caa17caSRaphael Isemann       : WindowDelegate(), MenuDelegate(), m_app(app), m_debugger(debugger) {}
2853*7caa17caSRaphael Isemann 
2854*7caa17caSRaphael Isemann   ~ApplicationDelegate() override = default;
2855*7caa17caSRaphael Isemann 
2856*7caa17caSRaphael Isemann   bool WindowDelegateDraw(Window &window, bool force) override {
2857*7caa17caSRaphael Isemann     return false; // Drawing not handled, let standard window drawing happen
2858*7caa17caSRaphael Isemann   }
2859*7caa17caSRaphael Isemann 
2860*7caa17caSRaphael Isemann   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
2861*7caa17caSRaphael Isemann     switch (key) {
2862*7caa17caSRaphael Isemann     case '\t':
2863*7caa17caSRaphael Isemann       window.SelectNextWindowAsActive();
2864*7caa17caSRaphael Isemann       return eKeyHandled;
2865*7caa17caSRaphael Isemann 
2866*7caa17caSRaphael Isemann     case 'h':
2867*7caa17caSRaphael Isemann       window.CreateHelpSubwindow();
2868*7caa17caSRaphael Isemann       return eKeyHandled;
2869*7caa17caSRaphael Isemann 
2870*7caa17caSRaphael Isemann     case KEY_ESCAPE:
2871*7caa17caSRaphael Isemann       return eQuitApplication;
2872*7caa17caSRaphael Isemann 
2873*7caa17caSRaphael Isemann     default:
2874*7caa17caSRaphael Isemann       break;
2875*7caa17caSRaphael Isemann     }
2876*7caa17caSRaphael Isemann     return eKeyNotHandled;
2877*7caa17caSRaphael Isemann   }
2878*7caa17caSRaphael Isemann 
2879*7caa17caSRaphael Isemann   const char *WindowDelegateGetHelpText() override {
2880*7caa17caSRaphael Isemann     return "Welcome to the LLDB curses GUI.\n\n"
2881*7caa17caSRaphael Isemann            "Press the TAB key to change the selected view.\n"
2882*7caa17caSRaphael Isemann            "Each view has its own keyboard shortcuts, press 'h' to open a "
2883*7caa17caSRaphael Isemann            "dialog to display them.\n\n"
2884*7caa17caSRaphael Isemann            "Common key bindings for all views:";
2885*7caa17caSRaphael Isemann   }
2886*7caa17caSRaphael Isemann 
2887*7caa17caSRaphael Isemann   KeyHelp *WindowDelegateGetKeyHelp() override {
2888*7caa17caSRaphael Isemann     static curses::KeyHelp g_source_view_key_help[] = {
2889*7caa17caSRaphael Isemann         {'\t', "Select next view"},
2890*7caa17caSRaphael Isemann         {'h', "Show help dialog with view specific key bindings"},
2891*7caa17caSRaphael Isemann         {',', "Page up"},
2892*7caa17caSRaphael Isemann         {'.', "Page down"},
2893*7caa17caSRaphael Isemann         {KEY_UP, "Select previous"},
2894*7caa17caSRaphael Isemann         {KEY_DOWN, "Select next"},
2895*7caa17caSRaphael Isemann         {KEY_LEFT, "Unexpand or select parent"},
2896*7caa17caSRaphael Isemann         {KEY_RIGHT, "Expand"},
2897*7caa17caSRaphael Isemann         {KEY_PPAGE, "Page up"},
2898*7caa17caSRaphael Isemann         {KEY_NPAGE, "Page down"},
2899*7caa17caSRaphael Isemann         {'\0', nullptr}};
2900*7caa17caSRaphael Isemann     return g_source_view_key_help;
2901*7caa17caSRaphael Isemann   }
2902*7caa17caSRaphael Isemann 
2903*7caa17caSRaphael Isemann   MenuActionResult MenuDelegateAction(Menu &menu) override {
2904*7caa17caSRaphael Isemann     switch (menu.GetIdentifier()) {
2905*7caa17caSRaphael Isemann     case eMenuID_ThreadStepIn: {
2906*7caa17caSRaphael Isemann       ExecutionContext exe_ctx =
2907*7caa17caSRaphael Isemann           m_debugger.GetCommandInterpreter().GetExecutionContext();
2908*7caa17caSRaphael Isemann       if (exe_ctx.HasThreadScope()) {
2909*7caa17caSRaphael Isemann         Process *process = exe_ctx.GetProcessPtr();
2910*7caa17caSRaphael Isemann         if (process && process->IsAlive() &&
2911*7caa17caSRaphael Isemann             StateIsStoppedState(process->GetState(), true))
2912*7caa17caSRaphael Isemann           exe_ctx.GetThreadRef().StepIn(true);
2913*7caa17caSRaphael Isemann       }
2914*7caa17caSRaphael Isemann     }
2915*7caa17caSRaphael Isemann       return MenuActionResult::Handled;
2916*7caa17caSRaphael Isemann 
2917*7caa17caSRaphael Isemann     case eMenuID_ThreadStepOut: {
2918*7caa17caSRaphael Isemann       ExecutionContext exe_ctx =
2919*7caa17caSRaphael Isemann           m_debugger.GetCommandInterpreter().GetExecutionContext();
2920*7caa17caSRaphael Isemann       if (exe_ctx.HasThreadScope()) {
2921*7caa17caSRaphael Isemann         Process *process = exe_ctx.GetProcessPtr();
2922*7caa17caSRaphael Isemann         if (process && process->IsAlive() &&
2923*7caa17caSRaphael Isemann             StateIsStoppedState(process->GetState(), true))
2924*7caa17caSRaphael Isemann           exe_ctx.GetThreadRef().StepOut();
2925*7caa17caSRaphael Isemann       }
2926*7caa17caSRaphael Isemann     }
2927*7caa17caSRaphael Isemann       return MenuActionResult::Handled;
2928*7caa17caSRaphael Isemann 
2929*7caa17caSRaphael Isemann     case eMenuID_ThreadStepOver: {
2930*7caa17caSRaphael Isemann       ExecutionContext exe_ctx =
2931*7caa17caSRaphael Isemann           m_debugger.GetCommandInterpreter().GetExecutionContext();
2932*7caa17caSRaphael Isemann       if (exe_ctx.HasThreadScope()) {
2933*7caa17caSRaphael Isemann         Process *process = exe_ctx.GetProcessPtr();
2934*7caa17caSRaphael Isemann         if (process && process->IsAlive() &&
2935*7caa17caSRaphael Isemann             StateIsStoppedState(process->GetState(), true))
2936*7caa17caSRaphael Isemann           exe_ctx.GetThreadRef().StepOver(true);
2937*7caa17caSRaphael Isemann       }
2938*7caa17caSRaphael Isemann     }
2939*7caa17caSRaphael Isemann       return MenuActionResult::Handled;
2940*7caa17caSRaphael Isemann 
2941*7caa17caSRaphael Isemann     case eMenuID_ProcessContinue: {
2942*7caa17caSRaphael Isemann       ExecutionContext exe_ctx =
2943*7caa17caSRaphael Isemann           m_debugger.GetCommandInterpreter().GetExecutionContext();
2944*7caa17caSRaphael Isemann       if (exe_ctx.HasProcessScope()) {
2945*7caa17caSRaphael Isemann         Process *process = exe_ctx.GetProcessPtr();
2946*7caa17caSRaphael Isemann         if (process && process->IsAlive() &&
2947*7caa17caSRaphael Isemann             StateIsStoppedState(process->GetState(), true))
2948*7caa17caSRaphael Isemann           process->Resume();
2949*7caa17caSRaphael Isemann       }
2950*7caa17caSRaphael Isemann     }
2951*7caa17caSRaphael Isemann       return MenuActionResult::Handled;
2952*7caa17caSRaphael Isemann 
2953*7caa17caSRaphael Isemann     case eMenuID_ProcessKill: {
2954*7caa17caSRaphael Isemann       ExecutionContext exe_ctx =
2955*7caa17caSRaphael Isemann           m_debugger.GetCommandInterpreter().GetExecutionContext();
2956*7caa17caSRaphael Isemann       if (exe_ctx.HasProcessScope()) {
2957*7caa17caSRaphael Isemann         Process *process = exe_ctx.GetProcessPtr();
2958*7caa17caSRaphael Isemann         if (process && process->IsAlive())
2959*7caa17caSRaphael Isemann           process->Destroy(false);
2960*7caa17caSRaphael Isemann       }
2961*7caa17caSRaphael Isemann     }
2962*7caa17caSRaphael Isemann       return MenuActionResult::Handled;
2963*7caa17caSRaphael Isemann 
2964*7caa17caSRaphael Isemann     case eMenuID_ProcessHalt: {
2965*7caa17caSRaphael Isemann       ExecutionContext exe_ctx =
2966*7caa17caSRaphael Isemann           m_debugger.GetCommandInterpreter().GetExecutionContext();
2967*7caa17caSRaphael Isemann       if (exe_ctx.HasProcessScope()) {
2968*7caa17caSRaphael Isemann         Process *process = exe_ctx.GetProcessPtr();
2969*7caa17caSRaphael Isemann         if (process && process->IsAlive())
2970*7caa17caSRaphael Isemann           process->Halt();
2971*7caa17caSRaphael Isemann       }
2972*7caa17caSRaphael Isemann     }
2973*7caa17caSRaphael Isemann       return MenuActionResult::Handled;
2974*7caa17caSRaphael Isemann 
2975*7caa17caSRaphael Isemann     case eMenuID_ProcessDetach: {
2976*7caa17caSRaphael Isemann       ExecutionContext exe_ctx =
2977*7caa17caSRaphael Isemann           m_debugger.GetCommandInterpreter().GetExecutionContext();
2978*7caa17caSRaphael Isemann       if (exe_ctx.HasProcessScope()) {
2979*7caa17caSRaphael Isemann         Process *process = exe_ctx.GetProcessPtr();
2980*7caa17caSRaphael Isemann         if (process && process->IsAlive())
2981*7caa17caSRaphael Isemann           process->Detach(false);
2982*7caa17caSRaphael Isemann       }
2983*7caa17caSRaphael Isemann     }
2984*7caa17caSRaphael Isemann       return MenuActionResult::Handled;
2985*7caa17caSRaphael Isemann 
2986*7caa17caSRaphael Isemann     case eMenuID_Process: {
2987*7caa17caSRaphael Isemann       // Populate the menu with all of the threads if the process is stopped
2988*7caa17caSRaphael Isemann       // when the Process menu gets selected and is about to display its
2989*7caa17caSRaphael Isemann       // submenu.
2990*7caa17caSRaphael Isemann       Menus &submenus = menu.GetSubmenus();
2991*7caa17caSRaphael Isemann       ExecutionContext exe_ctx =
2992*7caa17caSRaphael Isemann           m_debugger.GetCommandInterpreter().GetExecutionContext();
2993*7caa17caSRaphael Isemann       Process *process = exe_ctx.GetProcessPtr();
2994*7caa17caSRaphael Isemann       if (process && process->IsAlive() &&
2995*7caa17caSRaphael Isemann           StateIsStoppedState(process->GetState(), true)) {
2996*7caa17caSRaphael Isemann         if (submenus.size() == 7)
2997*7caa17caSRaphael Isemann           menu.AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
2998*7caa17caSRaphael Isemann         else if (submenus.size() > 8)
2999*7caa17caSRaphael Isemann           submenus.erase(submenus.begin() + 8, submenus.end());
3000*7caa17caSRaphael Isemann 
3001*7caa17caSRaphael Isemann         ThreadList &threads = process->GetThreadList();
3002*7caa17caSRaphael Isemann         std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
3003*7caa17caSRaphael Isemann         size_t num_threads = threads.GetSize();
3004*7caa17caSRaphael Isemann         for (size_t i = 0; i < num_threads; ++i) {
3005*7caa17caSRaphael Isemann           ThreadSP thread_sp = threads.GetThreadAtIndex(i);
3006*7caa17caSRaphael Isemann           char menu_char = '\0';
3007*7caa17caSRaphael Isemann           if (i < 9)
3008*7caa17caSRaphael Isemann             menu_char = '1' + i;
3009*7caa17caSRaphael Isemann           StreamString thread_menu_title;
3010*7caa17caSRaphael Isemann           thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID());
3011*7caa17caSRaphael Isemann           const char *thread_name = thread_sp->GetName();
3012*7caa17caSRaphael Isemann           if (thread_name && thread_name[0])
3013*7caa17caSRaphael Isemann             thread_menu_title.Printf(" %s", thread_name);
3014*7caa17caSRaphael Isemann           else {
3015*7caa17caSRaphael Isemann             const char *queue_name = thread_sp->GetQueueName();
3016*7caa17caSRaphael Isemann             if (queue_name && queue_name[0])
3017*7caa17caSRaphael Isemann               thread_menu_title.Printf(" %s", queue_name);
3018*7caa17caSRaphael Isemann           }
3019*7caa17caSRaphael Isemann           menu.AddSubmenu(
3020*7caa17caSRaphael Isemann               MenuSP(new Menu(thread_menu_title.GetString().str().c_str(),
3021*7caa17caSRaphael Isemann                               nullptr, menu_char, thread_sp->GetID())));
3022*7caa17caSRaphael Isemann         }
3023*7caa17caSRaphael Isemann       } else if (submenus.size() > 7) {
3024*7caa17caSRaphael Isemann         // Remove the separator and any other thread submenu items that were
3025*7caa17caSRaphael Isemann         // previously added
3026*7caa17caSRaphael Isemann         submenus.erase(submenus.begin() + 7, submenus.end());
3027*7caa17caSRaphael Isemann       }
3028*7caa17caSRaphael Isemann       // Since we are adding and removing items we need to recalculate the name
3029*7caa17caSRaphael Isemann       // lengths
3030*7caa17caSRaphael Isemann       menu.RecalculateNameLengths();
3031*7caa17caSRaphael Isemann     }
3032*7caa17caSRaphael Isemann       return MenuActionResult::Handled;
3033*7caa17caSRaphael Isemann 
3034*7caa17caSRaphael Isemann     case eMenuID_ViewVariables: {
3035*7caa17caSRaphael Isemann       WindowSP main_window_sp = m_app.GetMainWindow();
3036*7caa17caSRaphael Isemann       WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
3037*7caa17caSRaphael Isemann       WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
3038*7caa17caSRaphael Isemann       WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
3039*7caa17caSRaphael Isemann       const Rect source_bounds = source_window_sp->GetBounds();
3040*7caa17caSRaphael Isemann 
3041*7caa17caSRaphael Isemann       if (variables_window_sp) {
3042*7caa17caSRaphael Isemann         const Rect variables_bounds = variables_window_sp->GetBounds();
3043*7caa17caSRaphael Isemann 
3044*7caa17caSRaphael Isemann         main_window_sp->RemoveSubWindow(variables_window_sp.get());
3045*7caa17caSRaphael Isemann 
3046*7caa17caSRaphael Isemann         if (registers_window_sp) {
3047*7caa17caSRaphael Isemann           // We have a registers window, so give all the area back to the
3048*7caa17caSRaphael Isemann           // registers window
3049*7caa17caSRaphael Isemann           Rect registers_bounds = variables_bounds;
3050*7caa17caSRaphael Isemann           registers_bounds.size.width = source_bounds.size.width;
3051*7caa17caSRaphael Isemann           registers_window_sp->SetBounds(registers_bounds);
3052*7caa17caSRaphael Isemann         } else {
3053*7caa17caSRaphael Isemann           // We have no registers window showing so give the bottom area back
3054*7caa17caSRaphael Isemann           // to the source view
3055*7caa17caSRaphael Isemann           source_window_sp->Resize(source_bounds.size.width,
3056*7caa17caSRaphael Isemann                                    source_bounds.size.height +
3057*7caa17caSRaphael Isemann                                        variables_bounds.size.height);
3058*7caa17caSRaphael Isemann         }
3059*7caa17caSRaphael Isemann       } else {
3060*7caa17caSRaphael Isemann         Rect new_variables_rect;
3061*7caa17caSRaphael Isemann         if (registers_window_sp) {
3062*7caa17caSRaphael Isemann           // We have a registers window so split the area of the registers
3063*7caa17caSRaphael Isemann           // window into two columns where the left hand side will be the
3064*7caa17caSRaphael Isemann           // variables and the right hand side will be the registers
3065*7caa17caSRaphael Isemann           const Rect variables_bounds = registers_window_sp->GetBounds();
3066*7caa17caSRaphael Isemann           Rect new_registers_rect;
3067*7caa17caSRaphael Isemann           variables_bounds.VerticalSplitPercentage(0.50, new_variables_rect,
3068*7caa17caSRaphael Isemann                                                    new_registers_rect);
3069*7caa17caSRaphael Isemann           registers_window_sp->SetBounds(new_registers_rect);
3070*7caa17caSRaphael Isemann         } else {
3071*7caa17caSRaphael Isemann           // No variables window, grab the bottom part of the source window
3072*7caa17caSRaphael Isemann           Rect new_source_rect;
3073*7caa17caSRaphael Isemann           source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
3074*7caa17caSRaphael Isemann                                                   new_variables_rect);
3075*7caa17caSRaphael Isemann           source_window_sp->SetBounds(new_source_rect);
3076*7caa17caSRaphael Isemann         }
3077*7caa17caSRaphael Isemann         WindowSP new_window_sp = main_window_sp->CreateSubWindow(
3078*7caa17caSRaphael Isemann             "Variables", new_variables_rect, false);
3079*7caa17caSRaphael Isemann         new_window_sp->SetDelegate(
3080*7caa17caSRaphael Isemann             WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
3081*7caa17caSRaphael Isemann       }
3082*7caa17caSRaphael Isemann       touchwin(stdscr);
3083*7caa17caSRaphael Isemann     }
3084*7caa17caSRaphael Isemann       return MenuActionResult::Handled;
3085*7caa17caSRaphael Isemann 
3086*7caa17caSRaphael Isemann     case eMenuID_ViewRegisters: {
3087*7caa17caSRaphael Isemann       WindowSP main_window_sp = m_app.GetMainWindow();
3088*7caa17caSRaphael Isemann       WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
3089*7caa17caSRaphael Isemann       WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
3090*7caa17caSRaphael Isemann       WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
3091*7caa17caSRaphael Isemann       const Rect source_bounds = source_window_sp->GetBounds();
3092*7caa17caSRaphael Isemann 
3093*7caa17caSRaphael Isemann       if (registers_window_sp) {
3094*7caa17caSRaphael Isemann         if (variables_window_sp) {
3095*7caa17caSRaphael Isemann           const Rect variables_bounds = variables_window_sp->GetBounds();
3096*7caa17caSRaphael Isemann 
3097*7caa17caSRaphael Isemann           // We have a variables window, so give all the area back to the
3098*7caa17caSRaphael Isemann           // variables window
3099*7caa17caSRaphael Isemann           variables_window_sp->Resize(variables_bounds.size.width +
3100*7caa17caSRaphael Isemann                                           registers_window_sp->GetWidth(),
3101*7caa17caSRaphael Isemann                                       variables_bounds.size.height);
3102*7caa17caSRaphael Isemann         } else {
3103*7caa17caSRaphael Isemann           // We have no variables window showing so give the bottom area back
3104*7caa17caSRaphael Isemann           // to the source view
3105*7caa17caSRaphael Isemann           source_window_sp->Resize(source_bounds.size.width,
3106*7caa17caSRaphael Isemann                                    source_bounds.size.height +
3107*7caa17caSRaphael Isemann                                        registers_window_sp->GetHeight());
3108*7caa17caSRaphael Isemann         }
3109*7caa17caSRaphael Isemann         main_window_sp->RemoveSubWindow(registers_window_sp.get());
3110*7caa17caSRaphael Isemann       } else {
3111*7caa17caSRaphael Isemann         Rect new_regs_rect;
3112*7caa17caSRaphael Isemann         if (variables_window_sp) {
3113*7caa17caSRaphael Isemann           // We have a variables window, split it into two columns where the
3114*7caa17caSRaphael Isemann           // left hand side will be the variables and the right hand side will
3115*7caa17caSRaphael Isemann           // be the registers
3116*7caa17caSRaphael Isemann           const Rect variables_bounds = variables_window_sp->GetBounds();
3117*7caa17caSRaphael Isemann           Rect new_vars_rect;
3118*7caa17caSRaphael Isemann           variables_bounds.VerticalSplitPercentage(0.50, new_vars_rect,
3119*7caa17caSRaphael Isemann                                                    new_regs_rect);
3120*7caa17caSRaphael Isemann           variables_window_sp->SetBounds(new_vars_rect);
3121*7caa17caSRaphael Isemann         } else {
3122*7caa17caSRaphael Isemann           // No registers window, grab the bottom part of the source window
3123*7caa17caSRaphael Isemann           Rect new_source_rect;
3124*7caa17caSRaphael Isemann           source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
3125*7caa17caSRaphael Isemann                                                   new_regs_rect);
3126*7caa17caSRaphael Isemann           source_window_sp->SetBounds(new_source_rect);
3127*7caa17caSRaphael Isemann         }
3128*7caa17caSRaphael Isemann         WindowSP new_window_sp =
3129*7caa17caSRaphael Isemann             main_window_sp->CreateSubWindow("Registers", new_regs_rect, false);
3130*7caa17caSRaphael Isemann         new_window_sp->SetDelegate(
3131*7caa17caSRaphael Isemann             WindowDelegateSP(new RegistersWindowDelegate(m_debugger)));
3132*7caa17caSRaphael Isemann       }
3133*7caa17caSRaphael Isemann       touchwin(stdscr);
3134*7caa17caSRaphael Isemann     }
3135*7caa17caSRaphael Isemann       return MenuActionResult::Handled;
3136*7caa17caSRaphael Isemann 
3137*7caa17caSRaphael Isemann     case eMenuID_HelpGUIHelp:
3138*7caa17caSRaphael Isemann       m_app.GetMainWindow()->CreateHelpSubwindow();
3139*7caa17caSRaphael Isemann       return MenuActionResult::Handled;
3140*7caa17caSRaphael Isemann 
3141*7caa17caSRaphael Isemann     default:
3142*7caa17caSRaphael Isemann       break;
3143*7caa17caSRaphael Isemann     }
3144*7caa17caSRaphael Isemann 
3145*7caa17caSRaphael Isemann     return MenuActionResult::NotHandled;
3146*7caa17caSRaphael Isemann   }
3147*7caa17caSRaphael Isemann 
3148*7caa17caSRaphael Isemann protected:
3149*7caa17caSRaphael Isemann   Application &m_app;
3150*7caa17caSRaphael Isemann   Debugger &m_debugger;
3151*7caa17caSRaphael Isemann };
3152*7caa17caSRaphael Isemann 
3153*7caa17caSRaphael Isemann class StatusBarWindowDelegate : public WindowDelegate {
3154*7caa17caSRaphael Isemann public:
3155*7caa17caSRaphael Isemann   StatusBarWindowDelegate(Debugger &debugger) : m_debugger(debugger) {
3156*7caa17caSRaphael Isemann     FormatEntity::Parse("Thread: ${thread.id%tid}", m_format);
3157*7caa17caSRaphael Isemann   }
3158*7caa17caSRaphael Isemann 
3159*7caa17caSRaphael Isemann   ~StatusBarWindowDelegate() override = default;
3160*7caa17caSRaphael Isemann 
3161*7caa17caSRaphael Isemann   bool WindowDelegateDraw(Window &window, bool force) override {
3162*7caa17caSRaphael Isemann     ExecutionContext exe_ctx =
3163*7caa17caSRaphael Isemann         m_debugger.GetCommandInterpreter().GetExecutionContext();
3164*7caa17caSRaphael Isemann     Process *process = exe_ctx.GetProcessPtr();
3165*7caa17caSRaphael Isemann     Thread *thread = exe_ctx.GetThreadPtr();
3166*7caa17caSRaphael Isemann     StackFrame *frame = exe_ctx.GetFramePtr();
3167*7caa17caSRaphael Isemann     window.Erase();
3168*7caa17caSRaphael Isemann     window.SetBackground(2);
3169*7caa17caSRaphael Isemann     window.MoveCursor(0, 0);
3170*7caa17caSRaphael Isemann     if (process) {
3171*7caa17caSRaphael Isemann       const StateType state = process->GetState();
3172*7caa17caSRaphael Isemann       window.Printf("Process: %5" PRIu64 " %10s", process->GetID(),
3173*7caa17caSRaphael Isemann                     StateAsCString(state));
3174*7caa17caSRaphael Isemann 
3175*7caa17caSRaphael Isemann       if (StateIsStoppedState(state, true)) {
3176*7caa17caSRaphael Isemann         StreamString strm;
3177*7caa17caSRaphael Isemann         if (thread && FormatEntity::Format(m_format, strm, nullptr, &exe_ctx,
3178*7caa17caSRaphael Isemann                                            nullptr, nullptr, false, false)) {
3179*7caa17caSRaphael Isemann           window.MoveCursor(40, 0);
3180*7caa17caSRaphael Isemann           window.PutCStringTruncated(strm.GetString().str().c_str(), 1);
3181*7caa17caSRaphael Isemann         }
3182*7caa17caSRaphael Isemann 
3183*7caa17caSRaphael Isemann         window.MoveCursor(60, 0);
3184*7caa17caSRaphael Isemann         if (frame)
3185*7caa17caSRaphael Isemann           window.Printf("Frame: %3u  PC = 0x%16.16" PRIx64,
3186*7caa17caSRaphael Isemann                         frame->GetFrameIndex(),
3187*7caa17caSRaphael Isemann                         frame->GetFrameCodeAddress().GetOpcodeLoadAddress(
3188*7caa17caSRaphael Isemann                             exe_ctx.GetTargetPtr()));
3189*7caa17caSRaphael Isemann       } else if (state == eStateExited) {
3190*7caa17caSRaphael Isemann         const char *exit_desc = process->GetExitDescription();
3191*7caa17caSRaphael Isemann         const int exit_status = process->GetExitStatus();
3192*7caa17caSRaphael Isemann         if (exit_desc && exit_desc[0])
3193*7caa17caSRaphael Isemann           window.Printf(" with status = %i (%s)", exit_status, exit_desc);
3194*7caa17caSRaphael Isemann         else
3195*7caa17caSRaphael Isemann           window.Printf(" with status = %i", exit_status);
3196*7caa17caSRaphael Isemann       }
3197*7caa17caSRaphael Isemann     }
3198*7caa17caSRaphael Isemann     return true;
3199*7caa17caSRaphael Isemann   }
3200*7caa17caSRaphael Isemann 
3201*7caa17caSRaphael Isemann protected:
3202*7caa17caSRaphael Isemann   Debugger &m_debugger;
3203*7caa17caSRaphael Isemann   FormatEntity::Entry m_format;
3204*7caa17caSRaphael Isemann };
3205*7caa17caSRaphael Isemann 
3206*7caa17caSRaphael Isemann class SourceFileWindowDelegate : public WindowDelegate {
3207*7caa17caSRaphael Isemann public:
3208*7caa17caSRaphael Isemann   SourceFileWindowDelegate(Debugger &debugger)
3209*7caa17caSRaphael Isemann       : WindowDelegate(), m_debugger(debugger), m_sc(), m_file_sp(),
3210*7caa17caSRaphael Isemann         m_disassembly_scope(nullptr), m_disassembly_sp(), m_disassembly_range(),
3211*7caa17caSRaphael Isemann         m_title(), m_line_width(4), m_selected_line(0), m_pc_line(0),
3212*7caa17caSRaphael Isemann         m_stop_id(0), m_frame_idx(UINT32_MAX), m_first_visible_line(0),
3213*7caa17caSRaphael Isemann         m_min_x(0), m_min_y(0), m_max_x(0), m_max_y(0) {}
3214*7caa17caSRaphael Isemann 
3215*7caa17caSRaphael Isemann   ~SourceFileWindowDelegate() override = default;
3216*7caa17caSRaphael Isemann 
3217*7caa17caSRaphael Isemann   void Update(const SymbolContext &sc) { m_sc = sc; }
3218*7caa17caSRaphael Isemann 
3219*7caa17caSRaphael Isemann   uint32_t NumVisibleLines() const { return m_max_y - m_min_y; }
3220*7caa17caSRaphael Isemann 
3221*7caa17caSRaphael Isemann   const char *WindowDelegateGetHelpText() override {
3222*7caa17caSRaphael Isemann     return "Source/Disassembly window keyboard shortcuts:";
3223*7caa17caSRaphael Isemann   }
3224*7caa17caSRaphael Isemann 
3225*7caa17caSRaphael Isemann   KeyHelp *WindowDelegateGetKeyHelp() override {
3226*7caa17caSRaphael Isemann     static curses::KeyHelp g_source_view_key_help[] = {
3227*7caa17caSRaphael Isemann         {KEY_RETURN, "Run to selected line with one shot breakpoint"},
3228*7caa17caSRaphael Isemann         {KEY_UP, "Select previous source line"},
3229*7caa17caSRaphael Isemann         {KEY_DOWN, "Select next source line"},
3230*7caa17caSRaphael Isemann         {KEY_PPAGE, "Page up"},
3231*7caa17caSRaphael Isemann         {KEY_NPAGE, "Page down"},
3232*7caa17caSRaphael Isemann         {'b', "Set breakpoint on selected source/disassembly line"},
3233*7caa17caSRaphael Isemann         {'c', "Continue process"},
3234*7caa17caSRaphael Isemann         {'d', "Detach and resume process"},
3235*7caa17caSRaphael Isemann         {'D', "Detach with process suspended"},
3236*7caa17caSRaphael Isemann         {'h', "Show help dialog"},
3237*7caa17caSRaphael Isemann         {'k', "Kill process"},
3238*7caa17caSRaphael Isemann         {'n', "Step over (source line)"},
3239*7caa17caSRaphael Isemann         {'N', "Step over (single instruction)"},
3240*7caa17caSRaphael Isemann         {'o', "Step out"},
3241*7caa17caSRaphael Isemann         {'s', "Step in (source line)"},
3242*7caa17caSRaphael Isemann         {'S', "Step in (single instruction)"},
3243*7caa17caSRaphael Isemann         {',', "Page up"},
3244*7caa17caSRaphael Isemann         {'.', "Page down"},
3245*7caa17caSRaphael Isemann         {'\0', nullptr}};
3246*7caa17caSRaphael Isemann     return g_source_view_key_help;
3247*7caa17caSRaphael Isemann   }
3248*7caa17caSRaphael Isemann 
3249*7caa17caSRaphael Isemann   bool WindowDelegateDraw(Window &window, bool force) override {
3250*7caa17caSRaphael Isemann     ExecutionContext exe_ctx =
3251*7caa17caSRaphael Isemann         m_debugger.GetCommandInterpreter().GetExecutionContext();
3252*7caa17caSRaphael Isemann     Process *process = exe_ctx.GetProcessPtr();
3253*7caa17caSRaphael Isemann     Thread *thread = nullptr;
3254*7caa17caSRaphael Isemann 
3255*7caa17caSRaphael Isemann     bool update_location = false;
3256*7caa17caSRaphael Isemann     if (process) {
3257*7caa17caSRaphael Isemann       StateType state = process->GetState();
3258*7caa17caSRaphael Isemann       if (StateIsStoppedState(state, true)) {
3259*7caa17caSRaphael Isemann         // We are stopped, so it is ok to
3260*7caa17caSRaphael Isemann         update_location = true;
3261*7caa17caSRaphael Isemann       }
3262*7caa17caSRaphael Isemann     }
3263*7caa17caSRaphael Isemann 
3264*7caa17caSRaphael Isemann     m_min_x = 1;
3265*7caa17caSRaphael Isemann     m_min_y = 2;
3266*7caa17caSRaphael Isemann     m_max_x = window.GetMaxX() - 1;
3267*7caa17caSRaphael Isemann     m_max_y = window.GetMaxY() - 1;
3268*7caa17caSRaphael Isemann 
3269*7caa17caSRaphael Isemann     const uint32_t num_visible_lines = NumVisibleLines();
3270*7caa17caSRaphael Isemann     StackFrameSP frame_sp;
3271*7caa17caSRaphael Isemann     bool set_selected_line_to_pc = false;
3272*7caa17caSRaphael Isemann 
3273*7caa17caSRaphael Isemann     if (update_location) {
3274*7caa17caSRaphael Isemann       const bool process_alive = process ? process->IsAlive() : false;
3275*7caa17caSRaphael Isemann       bool thread_changed = false;
3276*7caa17caSRaphael Isemann       if (process_alive) {
3277*7caa17caSRaphael Isemann         thread = exe_ctx.GetThreadPtr();
3278*7caa17caSRaphael Isemann         if (thread) {
3279*7caa17caSRaphael Isemann           frame_sp = thread->GetSelectedFrame();
3280*7caa17caSRaphael Isemann           auto tid = thread->GetID();
3281*7caa17caSRaphael Isemann           thread_changed = tid != m_tid;
3282*7caa17caSRaphael Isemann           m_tid = tid;
3283*7caa17caSRaphael Isemann         } else {
3284*7caa17caSRaphael Isemann           if (m_tid != LLDB_INVALID_THREAD_ID) {
3285*7caa17caSRaphael Isemann             thread_changed = true;
3286*7caa17caSRaphael Isemann             m_tid = LLDB_INVALID_THREAD_ID;
3287*7caa17caSRaphael Isemann           }
3288*7caa17caSRaphael Isemann         }
3289*7caa17caSRaphael Isemann       }
3290*7caa17caSRaphael Isemann       const uint32_t stop_id = process ? process->GetStopID() : 0;
3291*7caa17caSRaphael Isemann       const bool stop_id_changed = stop_id != m_stop_id;
3292*7caa17caSRaphael Isemann       bool frame_changed = false;
3293*7caa17caSRaphael Isemann       m_stop_id = stop_id;
3294*7caa17caSRaphael Isemann       m_title.Clear();
3295*7caa17caSRaphael Isemann       if (frame_sp) {
3296*7caa17caSRaphael Isemann         m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
3297*7caa17caSRaphael Isemann         if (m_sc.module_sp) {
3298*7caa17caSRaphael Isemann           m_title.Printf(
3299*7caa17caSRaphael Isemann               "%s", m_sc.module_sp->GetFileSpec().GetFilename().GetCString());
3300*7caa17caSRaphael Isemann           ConstString func_name = m_sc.GetFunctionName();
3301*7caa17caSRaphael Isemann           if (func_name)
3302*7caa17caSRaphael Isemann             m_title.Printf("`%s", func_name.GetCString());
3303*7caa17caSRaphael Isemann         }
3304*7caa17caSRaphael Isemann         const uint32_t frame_idx = frame_sp->GetFrameIndex();
3305*7caa17caSRaphael Isemann         frame_changed = frame_idx != m_frame_idx;
3306*7caa17caSRaphael Isemann         m_frame_idx = frame_idx;
3307*7caa17caSRaphael Isemann       } else {
3308*7caa17caSRaphael Isemann         m_sc.Clear(true);
3309*7caa17caSRaphael Isemann         frame_changed = m_frame_idx != UINT32_MAX;
3310*7caa17caSRaphael Isemann         m_frame_idx = UINT32_MAX;
3311*7caa17caSRaphael Isemann       }
3312*7caa17caSRaphael Isemann 
3313*7caa17caSRaphael Isemann       const bool context_changed =
3314*7caa17caSRaphael Isemann           thread_changed || frame_changed || stop_id_changed;
3315*7caa17caSRaphael Isemann 
3316*7caa17caSRaphael Isemann       if (process_alive) {
3317*7caa17caSRaphael Isemann         if (m_sc.line_entry.IsValid()) {
3318*7caa17caSRaphael Isemann           m_pc_line = m_sc.line_entry.line;
3319*7caa17caSRaphael Isemann           if (m_pc_line != UINT32_MAX)
3320*7caa17caSRaphael Isemann             --m_pc_line; // Convert to zero based line number...
3321*7caa17caSRaphael Isemann           // Update the selected line if the stop ID changed...
3322*7caa17caSRaphael Isemann           if (context_changed)
3323*7caa17caSRaphael Isemann             m_selected_line = m_pc_line;
3324*7caa17caSRaphael Isemann 
3325*7caa17caSRaphael Isemann           if (m_file_sp && m_file_sp->FileSpecMatches(m_sc.line_entry.file)) {
3326*7caa17caSRaphael Isemann             // Same file, nothing to do, we should either have the lines or not
3327*7caa17caSRaphael Isemann             // (source file missing)
3328*7caa17caSRaphael Isemann             if (m_selected_line >= static_cast<size_t>(m_first_visible_line)) {
3329*7caa17caSRaphael Isemann               if (m_selected_line >= m_first_visible_line + num_visible_lines)
3330*7caa17caSRaphael Isemann                 m_first_visible_line = m_selected_line - 10;
3331*7caa17caSRaphael Isemann             } else {
3332*7caa17caSRaphael Isemann               if (m_selected_line > 10)
3333*7caa17caSRaphael Isemann                 m_first_visible_line = m_selected_line - 10;
3334*7caa17caSRaphael Isemann               else
3335*7caa17caSRaphael Isemann                 m_first_visible_line = 0;
3336*7caa17caSRaphael Isemann             }
3337*7caa17caSRaphael Isemann           } else {
3338*7caa17caSRaphael Isemann             // File changed, set selected line to the line with the PC
3339*7caa17caSRaphael Isemann             m_selected_line = m_pc_line;
3340*7caa17caSRaphael Isemann             m_file_sp =
3341*7caa17caSRaphael Isemann                 m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file);
3342*7caa17caSRaphael Isemann             if (m_file_sp) {
3343*7caa17caSRaphael Isemann               const size_t num_lines = m_file_sp->GetNumLines();
3344*7caa17caSRaphael Isemann               m_line_width = 1;
3345*7caa17caSRaphael Isemann               for (size_t n = num_lines; n >= 10; n = n / 10)
3346*7caa17caSRaphael Isemann                 ++m_line_width;
3347*7caa17caSRaphael Isemann 
3348*7caa17caSRaphael Isemann               if (num_lines < num_visible_lines ||
3349*7caa17caSRaphael Isemann                   m_selected_line < num_visible_lines)
3350*7caa17caSRaphael Isemann                 m_first_visible_line = 0;
3351*7caa17caSRaphael Isemann               else
3352*7caa17caSRaphael Isemann                 m_first_visible_line = m_selected_line - 10;
3353*7caa17caSRaphael Isemann             }
3354*7caa17caSRaphael Isemann           }
3355*7caa17caSRaphael Isemann         } else {
3356*7caa17caSRaphael Isemann           m_file_sp.reset();
3357*7caa17caSRaphael Isemann         }
3358*7caa17caSRaphael Isemann 
3359*7caa17caSRaphael Isemann         if (!m_file_sp || m_file_sp->GetNumLines() == 0) {
3360*7caa17caSRaphael Isemann           // Show disassembly
3361*7caa17caSRaphael Isemann           bool prefer_file_cache = false;
3362*7caa17caSRaphael Isemann           if (m_sc.function) {
3363*7caa17caSRaphael Isemann             if (m_disassembly_scope != m_sc.function) {
3364*7caa17caSRaphael Isemann               m_disassembly_scope = m_sc.function;
3365*7caa17caSRaphael Isemann               m_disassembly_sp = m_sc.function->GetInstructions(
3366*7caa17caSRaphael Isemann                   exe_ctx, nullptr, prefer_file_cache);
3367*7caa17caSRaphael Isemann               if (m_disassembly_sp) {
3368*7caa17caSRaphael Isemann                 set_selected_line_to_pc = true;
3369*7caa17caSRaphael Isemann                 m_disassembly_range = m_sc.function->GetAddressRange();
3370*7caa17caSRaphael Isemann               } else {
3371*7caa17caSRaphael Isemann                 m_disassembly_range.Clear();
3372*7caa17caSRaphael Isemann               }
3373*7caa17caSRaphael Isemann             } else {
3374*7caa17caSRaphael Isemann               set_selected_line_to_pc = context_changed;
3375*7caa17caSRaphael Isemann             }
3376*7caa17caSRaphael Isemann           } else if (m_sc.symbol) {
3377*7caa17caSRaphael Isemann             if (m_disassembly_scope != m_sc.symbol) {
3378*7caa17caSRaphael Isemann               m_disassembly_scope = m_sc.symbol;
3379*7caa17caSRaphael Isemann               m_disassembly_sp = m_sc.symbol->GetInstructions(
3380*7caa17caSRaphael Isemann                   exe_ctx, nullptr, prefer_file_cache);
3381*7caa17caSRaphael Isemann               if (m_disassembly_sp) {
3382*7caa17caSRaphael Isemann                 set_selected_line_to_pc = true;
3383*7caa17caSRaphael Isemann                 m_disassembly_range.GetBaseAddress() =
3384*7caa17caSRaphael Isemann                     m_sc.symbol->GetAddress();
3385*7caa17caSRaphael Isemann                 m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize());
3386*7caa17caSRaphael Isemann               } else {
3387*7caa17caSRaphael Isemann                 m_disassembly_range.Clear();
3388*7caa17caSRaphael Isemann               }
3389*7caa17caSRaphael Isemann             } else {
3390*7caa17caSRaphael Isemann               set_selected_line_to_pc = context_changed;
3391*7caa17caSRaphael Isemann             }
3392*7caa17caSRaphael Isemann           }
3393*7caa17caSRaphael Isemann         }
3394*7caa17caSRaphael Isemann       } else {
3395*7caa17caSRaphael Isemann         m_pc_line = UINT32_MAX;
3396*7caa17caSRaphael Isemann       }
3397*7caa17caSRaphael Isemann     }
3398*7caa17caSRaphael Isemann 
3399*7caa17caSRaphael Isemann     const int window_width = window.GetWidth();
3400*7caa17caSRaphael Isemann     window.Erase();
3401*7caa17caSRaphael Isemann     window.DrawTitleBox("Sources");
3402*7caa17caSRaphael Isemann     if (!m_title.GetString().empty()) {
3403*7caa17caSRaphael Isemann       window.AttributeOn(A_REVERSE);
3404*7caa17caSRaphael Isemann       window.MoveCursor(1, 1);
3405*7caa17caSRaphael Isemann       window.PutChar(' ');
3406*7caa17caSRaphael Isemann       window.PutCStringTruncated(m_title.GetString().str().c_str(), 1);
3407*7caa17caSRaphael Isemann       int x = window.GetCursorX();
3408*7caa17caSRaphael Isemann       if (x < window_width - 1) {
3409*7caa17caSRaphael Isemann         window.Printf("%*s", window_width - x - 1, "");
3410*7caa17caSRaphael Isemann       }
3411*7caa17caSRaphael Isemann       window.AttributeOff(A_REVERSE);
3412*7caa17caSRaphael Isemann     }
3413*7caa17caSRaphael Isemann 
3414*7caa17caSRaphael Isemann     Target *target = exe_ctx.GetTargetPtr();
3415*7caa17caSRaphael Isemann     const size_t num_source_lines = GetNumSourceLines();
3416*7caa17caSRaphael Isemann     if (num_source_lines > 0) {
3417*7caa17caSRaphael Isemann       // Display source
3418*7caa17caSRaphael Isemann       BreakpointLines bp_lines;
3419*7caa17caSRaphael Isemann       if (target) {
3420*7caa17caSRaphael Isemann         BreakpointList &bp_list = target->GetBreakpointList();
3421*7caa17caSRaphael Isemann         const size_t num_bps = bp_list.GetSize();
3422*7caa17caSRaphael Isemann         for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
3423*7caa17caSRaphael Isemann           BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
3424*7caa17caSRaphael Isemann           const size_t num_bps_locs = bp_sp->GetNumLocations();
3425*7caa17caSRaphael Isemann           for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
3426*7caa17caSRaphael Isemann             BreakpointLocationSP bp_loc_sp =
3427*7caa17caSRaphael Isemann                 bp_sp->GetLocationAtIndex(bp_loc_idx);
3428*7caa17caSRaphael Isemann             LineEntry bp_loc_line_entry;
3429*7caa17caSRaphael Isemann             if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry(
3430*7caa17caSRaphael Isemann                     bp_loc_line_entry)) {
3431*7caa17caSRaphael Isemann               if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file) {
3432*7caa17caSRaphael Isemann                 bp_lines.insert(bp_loc_line_entry.line);
3433*7caa17caSRaphael Isemann               }
3434*7caa17caSRaphael Isemann             }
3435*7caa17caSRaphael Isemann           }
3436*7caa17caSRaphael Isemann         }
3437*7caa17caSRaphael Isemann       }
3438*7caa17caSRaphael Isemann 
3439*7caa17caSRaphael Isemann       const attr_t selected_highlight_attr = A_REVERSE;
3440*7caa17caSRaphael Isemann       const attr_t pc_highlight_attr = COLOR_PAIR(1);
3441*7caa17caSRaphael Isemann 
3442*7caa17caSRaphael Isemann       for (size_t i = 0; i < num_visible_lines; ++i) {
3443*7caa17caSRaphael Isemann         const uint32_t curr_line = m_first_visible_line + i;
3444*7caa17caSRaphael Isemann         if (curr_line < num_source_lines) {
3445*7caa17caSRaphael Isemann           const int line_y = m_min_y + i;
3446*7caa17caSRaphael Isemann           window.MoveCursor(1, line_y);
3447*7caa17caSRaphael Isemann           const bool is_pc_line = curr_line == m_pc_line;
3448*7caa17caSRaphael Isemann           const bool line_is_selected = m_selected_line == curr_line;
3449*7caa17caSRaphael Isemann           // Highlight the line as the PC line first, then if the selected line
3450*7caa17caSRaphael Isemann           // isn't the same as the PC line, highlight it differently
3451*7caa17caSRaphael Isemann           attr_t highlight_attr = 0;
3452*7caa17caSRaphael Isemann           attr_t bp_attr = 0;
3453*7caa17caSRaphael Isemann           if (is_pc_line)
3454*7caa17caSRaphael Isemann             highlight_attr = pc_highlight_attr;
3455*7caa17caSRaphael Isemann           else if (line_is_selected)
3456*7caa17caSRaphael Isemann             highlight_attr = selected_highlight_attr;
3457*7caa17caSRaphael Isemann 
3458*7caa17caSRaphael Isemann           if (bp_lines.find(curr_line + 1) != bp_lines.end())
3459*7caa17caSRaphael Isemann             bp_attr = COLOR_PAIR(2);
3460*7caa17caSRaphael Isemann 
3461*7caa17caSRaphael Isemann           if (bp_attr)
3462*7caa17caSRaphael Isemann             window.AttributeOn(bp_attr);
3463*7caa17caSRaphael Isemann 
3464*7caa17caSRaphael Isemann           window.Printf(" %*u ", m_line_width, curr_line + 1);
3465*7caa17caSRaphael Isemann 
3466*7caa17caSRaphael Isemann           if (bp_attr)
3467*7caa17caSRaphael Isemann             window.AttributeOff(bp_attr);
3468*7caa17caSRaphael Isemann 
3469*7caa17caSRaphael Isemann           window.PutChar(ACS_VLINE);
3470*7caa17caSRaphael Isemann           // Mark the line with the PC with a diamond
3471*7caa17caSRaphael Isemann           if (is_pc_line)
3472*7caa17caSRaphael Isemann             window.PutChar(ACS_DIAMOND);
3473*7caa17caSRaphael Isemann           else
3474*7caa17caSRaphael Isemann             window.PutChar(' ');
3475*7caa17caSRaphael Isemann 
3476*7caa17caSRaphael Isemann           if (highlight_attr)
3477*7caa17caSRaphael Isemann             window.AttributeOn(highlight_attr);
3478*7caa17caSRaphael Isemann           const uint32_t line_len =
3479*7caa17caSRaphael Isemann               m_file_sp->GetLineLength(curr_line + 1, false);
3480*7caa17caSRaphael Isemann           if (line_len > 0)
3481*7caa17caSRaphael Isemann             window.PutCString(m_file_sp->PeekLineData(curr_line + 1), line_len);
3482*7caa17caSRaphael Isemann 
3483*7caa17caSRaphael Isemann           if (is_pc_line && frame_sp &&
3484*7caa17caSRaphael Isemann               frame_sp->GetConcreteFrameIndex() == 0) {
3485*7caa17caSRaphael Isemann             StopInfoSP stop_info_sp;
3486*7caa17caSRaphael Isemann             if (thread)
3487*7caa17caSRaphael Isemann               stop_info_sp = thread->GetStopInfo();
3488*7caa17caSRaphael Isemann             if (stop_info_sp) {
3489*7caa17caSRaphael Isemann               const char *stop_description = stop_info_sp->GetDescription();
3490*7caa17caSRaphael Isemann               if (stop_description && stop_description[0]) {
3491*7caa17caSRaphael Isemann                 size_t stop_description_len = strlen(stop_description);
3492*7caa17caSRaphael Isemann                 int desc_x = window_width - stop_description_len - 16;
3493*7caa17caSRaphael Isemann                 window.Printf("%*s", desc_x - window.GetCursorX(), "");
3494*7caa17caSRaphael Isemann                 // window.MoveCursor(window_width - stop_description_len - 15,
3495*7caa17caSRaphael Isemann                 // line_y);
3496*7caa17caSRaphael Isemann                 window.Printf("<<< Thread %u: %s ", thread->GetIndexID(),
3497*7caa17caSRaphael Isemann                               stop_description);
3498*7caa17caSRaphael Isemann               }
3499*7caa17caSRaphael Isemann             } else {
3500*7caa17caSRaphael Isemann               window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
3501*7caa17caSRaphael Isemann             }
3502*7caa17caSRaphael Isemann           }
3503*7caa17caSRaphael Isemann           if (highlight_attr)
3504*7caa17caSRaphael Isemann             window.AttributeOff(highlight_attr);
3505*7caa17caSRaphael Isemann         } else {
3506*7caa17caSRaphael Isemann           break;
3507*7caa17caSRaphael Isemann         }
3508*7caa17caSRaphael Isemann       }
3509*7caa17caSRaphael Isemann     } else {
3510*7caa17caSRaphael Isemann       size_t num_disassembly_lines = GetNumDisassemblyLines();
3511*7caa17caSRaphael Isemann       if (num_disassembly_lines > 0) {
3512*7caa17caSRaphael Isemann         // Display disassembly
3513*7caa17caSRaphael Isemann         BreakpointAddrs bp_file_addrs;
3514*7caa17caSRaphael Isemann         Target *target = exe_ctx.GetTargetPtr();
3515*7caa17caSRaphael Isemann         if (target) {
3516*7caa17caSRaphael Isemann           BreakpointList &bp_list = target->GetBreakpointList();
3517*7caa17caSRaphael Isemann           const size_t num_bps = bp_list.GetSize();
3518*7caa17caSRaphael Isemann           for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
3519*7caa17caSRaphael Isemann             BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
3520*7caa17caSRaphael Isemann             const size_t num_bps_locs = bp_sp->GetNumLocations();
3521*7caa17caSRaphael Isemann             for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs;
3522*7caa17caSRaphael Isemann                  ++bp_loc_idx) {
3523*7caa17caSRaphael Isemann               BreakpointLocationSP bp_loc_sp =
3524*7caa17caSRaphael Isemann                   bp_sp->GetLocationAtIndex(bp_loc_idx);
3525*7caa17caSRaphael Isemann               LineEntry bp_loc_line_entry;
3526*7caa17caSRaphael Isemann               const lldb::addr_t file_addr =
3527*7caa17caSRaphael Isemann                   bp_loc_sp->GetAddress().GetFileAddress();
3528*7caa17caSRaphael Isemann               if (file_addr != LLDB_INVALID_ADDRESS) {
3529*7caa17caSRaphael Isemann                 if (m_disassembly_range.ContainsFileAddress(file_addr))
3530*7caa17caSRaphael Isemann                   bp_file_addrs.insert(file_addr);
3531*7caa17caSRaphael Isemann               }
3532*7caa17caSRaphael Isemann             }
3533*7caa17caSRaphael Isemann           }
3534*7caa17caSRaphael Isemann         }
3535*7caa17caSRaphael Isemann 
3536*7caa17caSRaphael Isemann         const attr_t selected_highlight_attr = A_REVERSE;
3537*7caa17caSRaphael Isemann         const attr_t pc_highlight_attr = COLOR_PAIR(1);
3538*7caa17caSRaphael Isemann 
3539*7caa17caSRaphael Isemann         StreamString strm;
3540*7caa17caSRaphael Isemann 
3541*7caa17caSRaphael Isemann         InstructionList &insts = m_disassembly_sp->GetInstructionList();
3542*7caa17caSRaphael Isemann         Address pc_address;
3543*7caa17caSRaphael Isemann 
3544*7caa17caSRaphael Isemann         if (frame_sp)
3545*7caa17caSRaphael Isemann           pc_address = frame_sp->GetFrameCodeAddress();
3546*7caa17caSRaphael Isemann         const uint32_t pc_idx =
3547*7caa17caSRaphael Isemann             pc_address.IsValid()
3548*7caa17caSRaphael Isemann                 ? insts.GetIndexOfInstructionAtAddress(pc_address)
3549*7caa17caSRaphael Isemann                 : UINT32_MAX;
3550*7caa17caSRaphael Isemann         if (set_selected_line_to_pc) {
3551*7caa17caSRaphael Isemann           m_selected_line = pc_idx;
3552*7caa17caSRaphael Isemann         }
3553*7caa17caSRaphael Isemann 
3554*7caa17caSRaphael Isemann         const uint32_t non_visible_pc_offset = (num_visible_lines / 5);
3555*7caa17caSRaphael Isemann         if (static_cast<size_t>(m_first_visible_line) >= num_disassembly_lines)
3556*7caa17caSRaphael Isemann           m_first_visible_line = 0;
3557*7caa17caSRaphael Isemann 
3558*7caa17caSRaphael Isemann         if (pc_idx < num_disassembly_lines) {
3559*7caa17caSRaphael Isemann           if (pc_idx < static_cast<uint32_t>(m_first_visible_line) ||
3560*7caa17caSRaphael Isemann               pc_idx >= m_first_visible_line + num_visible_lines)
3561*7caa17caSRaphael Isemann             m_first_visible_line = pc_idx - non_visible_pc_offset;
3562*7caa17caSRaphael Isemann         }
3563*7caa17caSRaphael Isemann 
3564*7caa17caSRaphael Isemann         for (size_t i = 0; i < num_visible_lines; ++i) {
3565*7caa17caSRaphael Isemann           const uint32_t inst_idx = m_first_visible_line + i;
3566*7caa17caSRaphael Isemann           Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get();
3567*7caa17caSRaphael Isemann           if (!inst)
3568*7caa17caSRaphael Isemann             break;
3569*7caa17caSRaphael Isemann 
3570*7caa17caSRaphael Isemann           const int line_y = m_min_y + i;
3571*7caa17caSRaphael Isemann           window.MoveCursor(1, line_y);
3572*7caa17caSRaphael Isemann           const bool is_pc_line = frame_sp && inst_idx == pc_idx;
3573*7caa17caSRaphael Isemann           const bool line_is_selected = m_selected_line == inst_idx;
3574*7caa17caSRaphael Isemann           // Highlight the line as the PC line first, then if the selected line
3575*7caa17caSRaphael Isemann           // isn't the same as the PC line, highlight it differently
3576*7caa17caSRaphael Isemann           attr_t highlight_attr = 0;
3577*7caa17caSRaphael Isemann           attr_t bp_attr = 0;
3578*7caa17caSRaphael Isemann           if (is_pc_line)
3579*7caa17caSRaphael Isemann             highlight_attr = pc_highlight_attr;
3580*7caa17caSRaphael Isemann           else if (line_is_selected)
3581*7caa17caSRaphael Isemann             highlight_attr = selected_highlight_attr;
3582*7caa17caSRaphael Isemann 
3583*7caa17caSRaphael Isemann           if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) !=
3584*7caa17caSRaphael Isemann               bp_file_addrs.end())
3585*7caa17caSRaphael Isemann             bp_attr = COLOR_PAIR(2);
3586*7caa17caSRaphael Isemann 
3587*7caa17caSRaphael Isemann           if (bp_attr)
3588*7caa17caSRaphael Isemann             window.AttributeOn(bp_attr);
3589*7caa17caSRaphael Isemann 
3590*7caa17caSRaphael Isemann           window.Printf(" 0x%16.16llx ",
3591*7caa17caSRaphael Isemann                         static_cast<unsigned long long>(
3592*7caa17caSRaphael Isemann                             inst->GetAddress().GetLoadAddress(target)));
3593*7caa17caSRaphael Isemann 
3594*7caa17caSRaphael Isemann           if (bp_attr)
3595*7caa17caSRaphael Isemann             window.AttributeOff(bp_attr);
3596*7caa17caSRaphael Isemann 
3597*7caa17caSRaphael Isemann           window.PutChar(ACS_VLINE);
3598*7caa17caSRaphael Isemann           // Mark the line with the PC with a diamond
3599*7caa17caSRaphael Isemann           if (is_pc_line)
3600*7caa17caSRaphael Isemann             window.PutChar(ACS_DIAMOND);
3601*7caa17caSRaphael Isemann           else
3602*7caa17caSRaphael Isemann             window.PutChar(' ');
3603*7caa17caSRaphael Isemann 
3604*7caa17caSRaphael Isemann           if (highlight_attr)
3605*7caa17caSRaphael Isemann             window.AttributeOn(highlight_attr);
3606*7caa17caSRaphael Isemann 
3607*7caa17caSRaphael Isemann           const char *mnemonic = inst->GetMnemonic(&exe_ctx);
3608*7caa17caSRaphael Isemann           const char *operands = inst->GetOperands(&exe_ctx);
3609*7caa17caSRaphael Isemann           const char *comment = inst->GetComment(&exe_ctx);
3610*7caa17caSRaphael Isemann 
3611*7caa17caSRaphael Isemann           if (mnemonic != nullptr && mnemonic[0] == '\0')
3612*7caa17caSRaphael Isemann             mnemonic = nullptr;
3613*7caa17caSRaphael Isemann           if (operands != nullptr && operands[0] == '\0')
3614*7caa17caSRaphael Isemann             operands = nullptr;
3615*7caa17caSRaphael Isemann           if (comment != nullptr && comment[0] == '\0')
3616*7caa17caSRaphael Isemann             comment = nullptr;
3617*7caa17caSRaphael Isemann 
3618*7caa17caSRaphael Isemann           strm.Clear();
3619*7caa17caSRaphael Isemann 
3620*7caa17caSRaphael Isemann           if (mnemonic != nullptr && operands != nullptr && comment != nullptr)
3621*7caa17caSRaphael Isemann             strm.Printf("%-8s %-25s ; %s", mnemonic, operands, comment);
3622*7caa17caSRaphael Isemann           else if (mnemonic != nullptr && operands != nullptr)
3623*7caa17caSRaphael Isemann             strm.Printf("%-8s %s", mnemonic, operands);
3624*7caa17caSRaphael Isemann           else if (mnemonic != nullptr)
3625*7caa17caSRaphael Isemann             strm.Printf("%s", mnemonic);
3626*7caa17caSRaphael Isemann 
3627*7caa17caSRaphael Isemann           int right_pad = 1;
3628*7caa17caSRaphael Isemann           window.PutCStringTruncated(strm.GetData(), right_pad);
3629*7caa17caSRaphael Isemann 
3630*7caa17caSRaphael Isemann           if (is_pc_line && frame_sp &&
3631*7caa17caSRaphael Isemann               frame_sp->GetConcreteFrameIndex() == 0) {
3632*7caa17caSRaphael Isemann             StopInfoSP stop_info_sp;
3633*7caa17caSRaphael Isemann             if (thread)
3634*7caa17caSRaphael Isemann               stop_info_sp = thread->GetStopInfo();
3635*7caa17caSRaphael Isemann             if (stop_info_sp) {
3636*7caa17caSRaphael Isemann               const char *stop_description = stop_info_sp->GetDescription();
3637*7caa17caSRaphael Isemann               if (stop_description && stop_description[0]) {
3638*7caa17caSRaphael Isemann                 size_t stop_description_len = strlen(stop_description);
3639*7caa17caSRaphael Isemann                 int desc_x = window_width - stop_description_len - 16;
3640*7caa17caSRaphael Isemann                 window.Printf("%*s", desc_x - window.GetCursorX(), "");
3641*7caa17caSRaphael Isemann                 // window.MoveCursor(window_width - stop_description_len - 15,
3642*7caa17caSRaphael Isemann                 // line_y);
3643*7caa17caSRaphael Isemann                 window.Printf("<<< Thread %u: %s ", thread->GetIndexID(),
3644*7caa17caSRaphael Isemann                               stop_description);
3645*7caa17caSRaphael Isemann               }
3646*7caa17caSRaphael Isemann             } else {
3647*7caa17caSRaphael Isemann               window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
3648*7caa17caSRaphael Isemann             }
3649*7caa17caSRaphael Isemann           }
3650*7caa17caSRaphael Isemann           if (highlight_attr)
3651*7caa17caSRaphael Isemann             window.AttributeOff(highlight_attr);
3652*7caa17caSRaphael Isemann         }
3653*7caa17caSRaphael Isemann       }
3654*7caa17caSRaphael Isemann     }
3655*7caa17caSRaphael Isemann     return true; // Drawing handled
3656*7caa17caSRaphael Isemann   }
3657*7caa17caSRaphael Isemann 
3658*7caa17caSRaphael Isemann   size_t GetNumLines() {
3659*7caa17caSRaphael Isemann     size_t num_lines = GetNumSourceLines();
3660*7caa17caSRaphael Isemann     if (num_lines == 0)
3661*7caa17caSRaphael Isemann       num_lines = GetNumDisassemblyLines();
3662*7caa17caSRaphael Isemann     return num_lines;
3663*7caa17caSRaphael Isemann   }
3664*7caa17caSRaphael Isemann 
3665*7caa17caSRaphael Isemann   size_t GetNumSourceLines() const {
3666*7caa17caSRaphael Isemann     if (m_file_sp)
3667*7caa17caSRaphael Isemann       return m_file_sp->GetNumLines();
3668*7caa17caSRaphael Isemann     return 0;
3669*7caa17caSRaphael Isemann   }
3670*7caa17caSRaphael Isemann 
3671*7caa17caSRaphael Isemann   size_t GetNumDisassemblyLines() const {
3672*7caa17caSRaphael Isemann     if (m_disassembly_sp)
3673*7caa17caSRaphael Isemann       return m_disassembly_sp->GetInstructionList().GetSize();
3674*7caa17caSRaphael Isemann     return 0;
3675*7caa17caSRaphael Isemann   }
3676*7caa17caSRaphael Isemann 
3677*7caa17caSRaphael Isemann   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
3678*7caa17caSRaphael Isemann     const uint32_t num_visible_lines = NumVisibleLines();
3679*7caa17caSRaphael Isemann     const size_t num_lines = GetNumLines();
3680*7caa17caSRaphael Isemann 
3681*7caa17caSRaphael Isemann     switch (c) {
3682*7caa17caSRaphael Isemann     case ',':
3683*7caa17caSRaphael Isemann     case KEY_PPAGE:
3684*7caa17caSRaphael Isemann       // Page up key
3685*7caa17caSRaphael Isemann       if (static_cast<uint32_t>(m_first_visible_line) > num_visible_lines)
3686*7caa17caSRaphael Isemann         m_first_visible_line -= num_visible_lines;
3687*7caa17caSRaphael Isemann       else
3688*7caa17caSRaphael Isemann         m_first_visible_line = 0;
3689*7caa17caSRaphael Isemann       m_selected_line = m_first_visible_line;
3690*7caa17caSRaphael Isemann       return eKeyHandled;
3691*7caa17caSRaphael Isemann 
3692*7caa17caSRaphael Isemann     case '.':
3693*7caa17caSRaphael Isemann     case KEY_NPAGE:
3694*7caa17caSRaphael Isemann       // Page down key
3695*7caa17caSRaphael Isemann       {
3696*7caa17caSRaphael Isemann         if (m_first_visible_line + num_visible_lines < num_lines)
3697*7caa17caSRaphael Isemann           m_first_visible_line += num_visible_lines;
3698*7caa17caSRaphael Isemann         else if (num_lines < num_visible_lines)
3699*7caa17caSRaphael Isemann           m_first_visible_line = 0;
3700*7caa17caSRaphael Isemann         else
3701*7caa17caSRaphael Isemann           m_first_visible_line = num_lines - num_visible_lines;
3702*7caa17caSRaphael Isemann         m_selected_line = m_first_visible_line;
3703*7caa17caSRaphael Isemann       }
3704*7caa17caSRaphael Isemann       return eKeyHandled;
3705*7caa17caSRaphael Isemann 
3706*7caa17caSRaphael Isemann     case KEY_UP:
3707*7caa17caSRaphael Isemann       if (m_selected_line > 0) {
3708*7caa17caSRaphael Isemann         m_selected_line--;
3709*7caa17caSRaphael Isemann         if (static_cast<size_t>(m_first_visible_line) > m_selected_line)
3710*7caa17caSRaphael Isemann           m_first_visible_line = m_selected_line;
3711*7caa17caSRaphael Isemann       }
3712*7caa17caSRaphael Isemann       return eKeyHandled;
3713*7caa17caSRaphael Isemann 
3714*7caa17caSRaphael Isemann     case KEY_DOWN:
3715*7caa17caSRaphael Isemann       if (m_selected_line + 1 < num_lines) {
3716*7caa17caSRaphael Isemann         m_selected_line++;
3717*7caa17caSRaphael Isemann         if (m_first_visible_line + num_visible_lines < m_selected_line)
3718*7caa17caSRaphael Isemann           m_first_visible_line++;
3719*7caa17caSRaphael Isemann       }
3720*7caa17caSRaphael Isemann       return eKeyHandled;
3721*7caa17caSRaphael Isemann 
3722*7caa17caSRaphael Isemann     case '\r':
3723*7caa17caSRaphael Isemann     case '\n':
3724*7caa17caSRaphael Isemann     case KEY_ENTER:
3725*7caa17caSRaphael Isemann       // Set a breakpoint and run to the line using a one shot breakpoint
3726*7caa17caSRaphael Isemann       if (GetNumSourceLines() > 0) {
3727*7caa17caSRaphael Isemann         ExecutionContext exe_ctx =
3728*7caa17caSRaphael Isemann             m_debugger.GetCommandInterpreter().GetExecutionContext();
3729*7caa17caSRaphael Isemann         if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive()) {
3730*7caa17caSRaphael Isemann           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
3731*7caa17caSRaphael Isemann               nullptr, // Don't limit the breakpoint to certain modules
3732*7caa17caSRaphael Isemann               m_file_sp->GetFileSpec(), // Source file
3733*7caa17caSRaphael Isemann               m_selected_line +
3734*7caa17caSRaphael Isemann                   1, // Source line number (m_selected_line is zero based)
3735*7caa17caSRaphael Isemann               0,     // Unspecified column.
3736*7caa17caSRaphael Isemann               0,     // No offset
3737*7caa17caSRaphael Isemann               eLazyBoolCalculate,  // Check inlines using global setting
3738*7caa17caSRaphael Isemann               eLazyBoolCalculate,  // Skip prologue using global setting,
3739*7caa17caSRaphael Isemann               false,               // internal
3740*7caa17caSRaphael Isemann               false,               // request_hardware
3741*7caa17caSRaphael Isemann               eLazyBoolCalculate); // move_to_nearest_code
3742*7caa17caSRaphael Isemann           // Make breakpoint one shot
3743*7caa17caSRaphael Isemann           bp_sp->GetOptions()->SetOneShot(true);
3744*7caa17caSRaphael Isemann           exe_ctx.GetProcessRef().Resume();
3745*7caa17caSRaphael Isemann         }
3746*7caa17caSRaphael Isemann       } else if (m_selected_line < GetNumDisassemblyLines()) {
3747*7caa17caSRaphael Isemann         const Instruction *inst = m_disassembly_sp->GetInstructionList()
3748*7caa17caSRaphael Isemann                                       .GetInstructionAtIndex(m_selected_line)
3749*7caa17caSRaphael Isemann                                       .get();
3750*7caa17caSRaphael Isemann         ExecutionContext exe_ctx =
3751*7caa17caSRaphael Isemann             m_debugger.GetCommandInterpreter().GetExecutionContext();
3752*7caa17caSRaphael Isemann         if (exe_ctx.HasTargetScope()) {
3753*7caa17caSRaphael Isemann           Address addr = inst->GetAddress();
3754*7caa17caSRaphael Isemann           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
3755*7caa17caSRaphael Isemann               addr,   // lldb_private::Address
3756*7caa17caSRaphael Isemann               false,  // internal
3757*7caa17caSRaphael Isemann               false); // request_hardware
3758*7caa17caSRaphael Isemann           // Make breakpoint one shot
3759*7caa17caSRaphael Isemann           bp_sp->GetOptions()->SetOneShot(true);
3760*7caa17caSRaphael Isemann           exe_ctx.GetProcessRef().Resume();
3761*7caa17caSRaphael Isemann         }
3762*7caa17caSRaphael Isemann       }
3763*7caa17caSRaphael Isemann       return eKeyHandled;
3764*7caa17caSRaphael Isemann 
3765*7caa17caSRaphael Isemann     case 'b': // 'b' == toggle breakpoint on currently selected line
3766*7caa17caSRaphael Isemann       if (m_selected_line < GetNumSourceLines()) {
3767*7caa17caSRaphael Isemann         ExecutionContext exe_ctx =
3768*7caa17caSRaphael Isemann             m_debugger.GetCommandInterpreter().GetExecutionContext();
3769*7caa17caSRaphael Isemann         if (exe_ctx.HasTargetScope()) {
3770*7caa17caSRaphael Isemann           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
3771*7caa17caSRaphael Isemann               nullptr, // Don't limit the breakpoint to certain modules
3772*7caa17caSRaphael Isemann               m_file_sp->GetFileSpec(), // Source file
3773*7caa17caSRaphael Isemann               m_selected_line +
3774*7caa17caSRaphael Isemann                   1, // Source line number (m_selected_line is zero based)
3775*7caa17caSRaphael Isemann               0,     // No column specified.
3776*7caa17caSRaphael Isemann               0,     // No offset
3777*7caa17caSRaphael Isemann               eLazyBoolCalculate,  // Check inlines using global setting
3778*7caa17caSRaphael Isemann               eLazyBoolCalculate,  // Skip prologue using global setting,
3779*7caa17caSRaphael Isemann               false,               // internal
3780*7caa17caSRaphael Isemann               false,               // request_hardware
3781*7caa17caSRaphael Isemann               eLazyBoolCalculate); // move_to_nearest_code
3782*7caa17caSRaphael Isemann         }
3783*7caa17caSRaphael Isemann       } else if (m_selected_line < GetNumDisassemblyLines()) {
3784*7caa17caSRaphael Isemann         const Instruction *inst = m_disassembly_sp->GetInstructionList()
3785*7caa17caSRaphael Isemann                                       .GetInstructionAtIndex(m_selected_line)
3786*7caa17caSRaphael Isemann                                       .get();
3787*7caa17caSRaphael Isemann         ExecutionContext exe_ctx =
3788*7caa17caSRaphael Isemann             m_debugger.GetCommandInterpreter().GetExecutionContext();
3789*7caa17caSRaphael Isemann         if (exe_ctx.HasTargetScope()) {
3790*7caa17caSRaphael Isemann           Address addr = inst->GetAddress();
3791*7caa17caSRaphael Isemann           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
3792*7caa17caSRaphael Isemann               addr,   // lldb_private::Address
3793*7caa17caSRaphael Isemann               false,  // internal
3794*7caa17caSRaphael Isemann               false); // request_hardware
3795*7caa17caSRaphael Isemann         }
3796*7caa17caSRaphael Isemann       }
3797*7caa17caSRaphael Isemann       return eKeyHandled;
3798*7caa17caSRaphael Isemann 
3799*7caa17caSRaphael Isemann     case 'd': // 'd' == detach and let run
3800*7caa17caSRaphael Isemann     case 'D': // 'D' == detach and keep stopped
3801*7caa17caSRaphael Isemann     {
3802*7caa17caSRaphael Isemann       ExecutionContext exe_ctx =
3803*7caa17caSRaphael Isemann           m_debugger.GetCommandInterpreter().GetExecutionContext();
3804*7caa17caSRaphael Isemann       if (exe_ctx.HasProcessScope())
3805*7caa17caSRaphael Isemann         exe_ctx.GetProcessRef().Detach(c == 'D');
3806*7caa17caSRaphael Isemann     }
3807*7caa17caSRaphael Isemann       return eKeyHandled;
3808*7caa17caSRaphael Isemann 
3809*7caa17caSRaphael Isemann     case 'k':
3810*7caa17caSRaphael Isemann       // 'k' == kill
3811*7caa17caSRaphael Isemann       {
3812*7caa17caSRaphael Isemann         ExecutionContext exe_ctx =
3813*7caa17caSRaphael Isemann             m_debugger.GetCommandInterpreter().GetExecutionContext();
3814*7caa17caSRaphael Isemann         if (exe_ctx.HasProcessScope())
3815*7caa17caSRaphael Isemann           exe_ctx.GetProcessRef().Destroy(false);
3816*7caa17caSRaphael Isemann       }
3817*7caa17caSRaphael Isemann       return eKeyHandled;
3818*7caa17caSRaphael Isemann 
3819*7caa17caSRaphael Isemann     case 'c':
3820*7caa17caSRaphael Isemann       // 'c' == continue
3821*7caa17caSRaphael Isemann       {
3822*7caa17caSRaphael Isemann         ExecutionContext exe_ctx =
3823*7caa17caSRaphael Isemann             m_debugger.GetCommandInterpreter().GetExecutionContext();
3824*7caa17caSRaphael Isemann         if (exe_ctx.HasProcessScope())
3825*7caa17caSRaphael Isemann           exe_ctx.GetProcessRef().Resume();
3826*7caa17caSRaphael Isemann       }
3827*7caa17caSRaphael Isemann       return eKeyHandled;
3828*7caa17caSRaphael Isemann 
3829*7caa17caSRaphael Isemann     case 'o':
3830*7caa17caSRaphael Isemann       // 'o' == step out
3831*7caa17caSRaphael Isemann       {
3832*7caa17caSRaphael Isemann         ExecutionContext exe_ctx =
3833*7caa17caSRaphael Isemann             m_debugger.GetCommandInterpreter().GetExecutionContext();
3834*7caa17caSRaphael Isemann         if (exe_ctx.HasThreadScope() &&
3835*7caa17caSRaphael Isemann             StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
3836*7caa17caSRaphael Isemann           exe_ctx.GetThreadRef().StepOut();
3837*7caa17caSRaphael Isemann         }
3838*7caa17caSRaphael Isemann       }
3839*7caa17caSRaphael Isemann       return eKeyHandled;
3840*7caa17caSRaphael Isemann 
3841*7caa17caSRaphael Isemann     case 'n': // 'n' == step over
3842*7caa17caSRaphael Isemann     case 'N': // 'N' == step over instruction
3843*7caa17caSRaphael Isemann     {
3844*7caa17caSRaphael Isemann       ExecutionContext exe_ctx =
3845*7caa17caSRaphael Isemann           m_debugger.GetCommandInterpreter().GetExecutionContext();
3846*7caa17caSRaphael Isemann       if (exe_ctx.HasThreadScope() &&
3847*7caa17caSRaphael Isemann           StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
3848*7caa17caSRaphael Isemann         bool source_step = (c == 'n');
3849*7caa17caSRaphael Isemann         exe_ctx.GetThreadRef().StepOver(source_step);
3850*7caa17caSRaphael Isemann       }
3851*7caa17caSRaphael Isemann     }
3852*7caa17caSRaphael Isemann       return eKeyHandled;
3853*7caa17caSRaphael Isemann 
3854*7caa17caSRaphael Isemann     case 's': // 's' == step into
3855*7caa17caSRaphael Isemann     case 'S': // 'S' == step into instruction
3856*7caa17caSRaphael Isemann     {
3857*7caa17caSRaphael Isemann       ExecutionContext exe_ctx =
3858*7caa17caSRaphael Isemann           m_debugger.GetCommandInterpreter().GetExecutionContext();
3859*7caa17caSRaphael Isemann       if (exe_ctx.HasThreadScope() &&
3860*7caa17caSRaphael Isemann           StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
3861*7caa17caSRaphael Isemann         bool source_step = (c == 's');
3862*7caa17caSRaphael Isemann         exe_ctx.GetThreadRef().StepIn(source_step);
3863*7caa17caSRaphael Isemann       }
3864*7caa17caSRaphael Isemann     }
3865*7caa17caSRaphael Isemann       return eKeyHandled;
3866*7caa17caSRaphael Isemann 
3867*7caa17caSRaphael Isemann     case 'h':
3868*7caa17caSRaphael Isemann       window.CreateHelpSubwindow();
3869*7caa17caSRaphael Isemann       return eKeyHandled;
3870*7caa17caSRaphael Isemann 
3871*7caa17caSRaphael Isemann     default:
3872*7caa17caSRaphael Isemann       break;
3873*7caa17caSRaphael Isemann     }
3874*7caa17caSRaphael Isemann     return eKeyNotHandled;
3875*7caa17caSRaphael Isemann   }
3876*7caa17caSRaphael Isemann 
3877*7caa17caSRaphael Isemann protected:
3878*7caa17caSRaphael Isemann   typedef std::set<uint32_t> BreakpointLines;
3879*7caa17caSRaphael Isemann   typedef std::set<lldb::addr_t> BreakpointAddrs;
3880*7caa17caSRaphael Isemann 
3881*7caa17caSRaphael Isemann   Debugger &m_debugger;
3882*7caa17caSRaphael Isemann   SymbolContext m_sc;
3883*7caa17caSRaphael Isemann   SourceManager::FileSP m_file_sp;
3884*7caa17caSRaphael Isemann   SymbolContextScope *m_disassembly_scope;
3885*7caa17caSRaphael Isemann   lldb::DisassemblerSP m_disassembly_sp;
3886*7caa17caSRaphael Isemann   AddressRange m_disassembly_range;
3887*7caa17caSRaphael Isemann   StreamString m_title;
3888*7caa17caSRaphael Isemann   lldb::user_id_t m_tid;
3889*7caa17caSRaphael Isemann   int m_line_width;
3890*7caa17caSRaphael Isemann   uint32_t m_selected_line; // The selected line
3891*7caa17caSRaphael Isemann   uint32_t m_pc_line;       // The line with the PC
3892*7caa17caSRaphael Isemann   uint32_t m_stop_id;
3893*7caa17caSRaphael Isemann   uint32_t m_frame_idx;
3894*7caa17caSRaphael Isemann   int m_first_visible_line;
3895*7caa17caSRaphael Isemann   int m_min_x;
3896*7caa17caSRaphael Isemann   int m_min_y;
3897*7caa17caSRaphael Isemann   int m_max_x;
3898*7caa17caSRaphael Isemann   int m_max_y;
3899*7caa17caSRaphael Isemann };
3900*7caa17caSRaphael Isemann 
3901*7caa17caSRaphael Isemann DisplayOptions ValueObjectListDelegate::g_options = {true};
3902*7caa17caSRaphael Isemann 
3903*7caa17caSRaphael Isemann IOHandlerCursesGUI::IOHandlerCursesGUI(Debugger &debugger)
3904*7caa17caSRaphael Isemann     : IOHandler(debugger, IOHandler::Type::Curses) {}
3905*7caa17caSRaphael Isemann 
3906*7caa17caSRaphael Isemann void IOHandlerCursesGUI::Activate() {
3907*7caa17caSRaphael Isemann   IOHandler::Activate();
3908*7caa17caSRaphael Isemann   if (!m_app_ap) {
3909*7caa17caSRaphael Isemann     m_app_ap.reset(new Application(GetInputFILE(), GetOutputFILE()));
3910*7caa17caSRaphael Isemann 
3911*7caa17caSRaphael Isemann     // This is both a window and a menu delegate
3912*7caa17caSRaphael Isemann     std::shared_ptr<ApplicationDelegate> app_delegate_sp(
3913*7caa17caSRaphael Isemann         new ApplicationDelegate(*m_app_ap, m_debugger));
3914*7caa17caSRaphael Isemann 
3915*7caa17caSRaphael Isemann     MenuDelegateSP app_menu_delegate_sp =
3916*7caa17caSRaphael Isemann         std::static_pointer_cast<MenuDelegate>(app_delegate_sp);
3917*7caa17caSRaphael Isemann     MenuSP lldb_menu_sp(
3918*7caa17caSRaphael Isemann         new Menu("LLDB", "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB));
3919*7caa17caSRaphael Isemann     MenuSP exit_menuitem_sp(
3920*7caa17caSRaphael Isemann         new Menu("Exit", nullptr, 'x', ApplicationDelegate::eMenuID_LLDBExit));
3921*7caa17caSRaphael Isemann     exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit);
3922*7caa17caSRaphael Isemann     lldb_menu_sp->AddSubmenu(MenuSP(new Menu(
3923*7caa17caSRaphael Isemann         "About LLDB", nullptr, 'a', ApplicationDelegate::eMenuID_LLDBAbout)));
3924*7caa17caSRaphael Isemann     lldb_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
3925*7caa17caSRaphael Isemann     lldb_menu_sp->AddSubmenu(exit_menuitem_sp);
3926*7caa17caSRaphael Isemann 
3927*7caa17caSRaphael Isemann     MenuSP target_menu_sp(new Menu("Target", "F2", KEY_F(2),
3928*7caa17caSRaphael Isemann                                    ApplicationDelegate::eMenuID_Target));
3929*7caa17caSRaphael Isemann     target_menu_sp->AddSubmenu(MenuSP(new Menu(
3930*7caa17caSRaphael Isemann         "Create", nullptr, 'c', ApplicationDelegate::eMenuID_TargetCreate)));
3931*7caa17caSRaphael Isemann     target_menu_sp->AddSubmenu(MenuSP(new Menu(
3932*7caa17caSRaphael Isemann         "Delete", nullptr, 'd', ApplicationDelegate::eMenuID_TargetDelete)));
3933*7caa17caSRaphael Isemann 
3934*7caa17caSRaphael Isemann     MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3),
3935*7caa17caSRaphael Isemann                                     ApplicationDelegate::eMenuID_Process));
3936*7caa17caSRaphael Isemann     process_menu_sp->AddSubmenu(MenuSP(new Menu(
3937*7caa17caSRaphael Isemann         "Attach", nullptr, 'a', ApplicationDelegate::eMenuID_ProcessAttach)));
3938*7caa17caSRaphael Isemann     process_menu_sp->AddSubmenu(MenuSP(new Menu(
3939*7caa17caSRaphael Isemann         "Detach", nullptr, 'd', ApplicationDelegate::eMenuID_ProcessDetach)));
3940*7caa17caSRaphael Isemann     process_menu_sp->AddSubmenu(MenuSP(new Menu(
3941*7caa17caSRaphael Isemann         "Launch", nullptr, 'l', ApplicationDelegate::eMenuID_ProcessLaunch)));
3942*7caa17caSRaphael Isemann     process_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
3943*7caa17caSRaphael Isemann     process_menu_sp->AddSubmenu(
3944*7caa17caSRaphael Isemann         MenuSP(new Menu("Continue", nullptr, 'c',
3945*7caa17caSRaphael Isemann                         ApplicationDelegate::eMenuID_ProcessContinue)));
3946*7caa17caSRaphael Isemann     process_menu_sp->AddSubmenu(MenuSP(new Menu(
3947*7caa17caSRaphael Isemann         "Halt", nullptr, 'h', ApplicationDelegate::eMenuID_ProcessHalt)));
3948*7caa17caSRaphael Isemann     process_menu_sp->AddSubmenu(MenuSP(new Menu(
3949*7caa17caSRaphael Isemann         "Kill", nullptr, 'k', ApplicationDelegate::eMenuID_ProcessKill)));
3950*7caa17caSRaphael Isemann 
3951*7caa17caSRaphael Isemann     MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4),
3952*7caa17caSRaphael Isemann                                    ApplicationDelegate::eMenuID_Thread));
3953*7caa17caSRaphael Isemann     thread_menu_sp->AddSubmenu(MenuSP(new Menu(
3954*7caa17caSRaphael Isemann         "Step In", nullptr, 'i', ApplicationDelegate::eMenuID_ThreadStepIn)));
3955*7caa17caSRaphael Isemann     thread_menu_sp->AddSubmenu(
3956*7caa17caSRaphael Isemann         MenuSP(new Menu("Step Over", nullptr, 'v',
3957*7caa17caSRaphael Isemann                         ApplicationDelegate::eMenuID_ThreadStepOver)));
3958*7caa17caSRaphael Isemann     thread_menu_sp->AddSubmenu(MenuSP(new Menu(
3959*7caa17caSRaphael Isemann         "Step Out", nullptr, 'o', ApplicationDelegate::eMenuID_ThreadStepOut)));
3960*7caa17caSRaphael Isemann 
3961*7caa17caSRaphael Isemann     MenuSP view_menu_sp(
3962*7caa17caSRaphael Isemann         new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View));
3963*7caa17caSRaphael Isemann     view_menu_sp->AddSubmenu(
3964*7caa17caSRaphael Isemann         MenuSP(new Menu("Backtrace", nullptr, 'b',
3965*7caa17caSRaphael Isemann                         ApplicationDelegate::eMenuID_ViewBacktrace)));
3966*7caa17caSRaphael Isemann     view_menu_sp->AddSubmenu(
3967*7caa17caSRaphael Isemann         MenuSP(new Menu("Registers", nullptr, 'r',
3968*7caa17caSRaphael Isemann                         ApplicationDelegate::eMenuID_ViewRegisters)));
3969*7caa17caSRaphael Isemann     view_menu_sp->AddSubmenu(MenuSP(new Menu(
3970*7caa17caSRaphael Isemann         "Source", nullptr, 's', ApplicationDelegate::eMenuID_ViewSource)));
3971*7caa17caSRaphael Isemann     view_menu_sp->AddSubmenu(
3972*7caa17caSRaphael Isemann         MenuSP(new Menu("Variables", nullptr, 'v',
3973*7caa17caSRaphael Isemann                         ApplicationDelegate::eMenuID_ViewVariables)));
3974*7caa17caSRaphael Isemann 
3975*7caa17caSRaphael Isemann     MenuSP help_menu_sp(
3976*7caa17caSRaphael Isemann         new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help));
3977*7caa17caSRaphael Isemann     help_menu_sp->AddSubmenu(MenuSP(new Menu(
3978*7caa17caSRaphael Isemann         "GUI Help", nullptr, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp)));
3979*7caa17caSRaphael Isemann 
3980*7caa17caSRaphael Isemann     m_app_ap->Initialize();
3981*7caa17caSRaphael Isemann     WindowSP &main_window_sp = m_app_ap->GetMainWindow();
3982*7caa17caSRaphael Isemann 
3983*7caa17caSRaphael Isemann     MenuSP menubar_sp(new Menu(Menu::Type::Bar));
3984*7caa17caSRaphael Isemann     menubar_sp->AddSubmenu(lldb_menu_sp);
3985*7caa17caSRaphael Isemann     menubar_sp->AddSubmenu(target_menu_sp);
3986*7caa17caSRaphael Isemann     menubar_sp->AddSubmenu(process_menu_sp);
3987*7caa17caSRaphael Isemann     menubar_sp->AddSubmenu(thread_menu_sp);
3988*7caa17caSRaphael Isemann     menubar_sp->AddSubmenu(view_menu_sp);
3989*7caa17caSRaphael Isemann     menubar_sp->AddSubmenu(help_menu_sp);
3990*7caa17caSRaphael Isemann     menubar_sp->SetDelegate(app_menu_delegate_sp);
3991*7caa17caSRaphael Isemann 
3992*7caa17caSRaphael Isemann     Rect content_bounds = main_window_sp->GetFrame();
3993*7caa17caSRaphael Isemann     Rect menubar_bounds = content_bounds.MakeMenuBar();
3994*7caa17caSRaphael Isemann     Rect status_bounds = content_bounds.MakeStatusBar();
3995*7caa17caSRaphael Isemann     Rect source_bounds;
3996*7caa17caSRaphael Isemann     Rect variables_bounds;
3997*7caa17caSRaphael Isemann     Rect threads_bounds;
3998*7caa17caSRaphael Isemann     Rect source_variables_bounds;
3999*7caa17caSRaphael Isemann     content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds,
4000*7caa17caSRaphael Isemann                                            threads_bounds);
4001*7caa17caSRaphael Isemann     source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds,
4002*7caa17caSRaphael Isemann                                                       variables_bounds);
4003*7caa17caSRaphael Isemann 
4004*7caa17caSRaphael Isemann     WindowSP menubar_window_sp =
4005*7caa17caSRaphael Isemann         main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false);
4006*7caa17caSRaphael Isemann     // Let the menubar get keys if the active window doesn't handle the keys
4007*7caa17caSRaphael Isemann     // that are typed so it can respond to menubar key presses.
4008*7caa17caSRaphael Isemann     menubar_window_sp->SetCanBeActive(
4009*7caa17caSRaphael Isemann         false); // Don't let the menubar become the active window
4010*7caa17caSRaphael Isemann     menubar_window_sp->SetDelegate(menubar_sp);
4011*7caa17caSRaphael Isemann 
4012*7caa17caSRaphael Isemann     WindowSP source_window_sp(
4013*7caa17caSRaphael Isemann         main_window_sp->CreateSubWindow("Source", source_bounds, true));
4014*7caa17caSRaphael Isemann     WindowSP variables_window_sp(
4015*7caa17caSRaphael Isemann         main_window_sp->CreateSubWindow("Variables", variables_bounds, false));
4016*7caa17caSRaphael Isemann     WindowSP threads_window_sp(
4017*7caa17caSRaphael Isemann         main_window_sp->CreateSubWindow("Threads", threads_bounds, false));
4018*7caa17caSRaphael Isemann     WindowSP status_window_sp(
4019*7caa17caSRaphael Isemann         main_window_sp->CreateSubWindow("Status", status_bounds, false));
4020*7caa17caSRaphael Isemann     status_window_sp->SetCanBeActive(
4021*7caa17caSRaphael Isemann         false); // Don't let the status bar become the active window
4022*7caa17caSRaphael Isemann     main_window_sp->SetDelegate(
4023*7caa17caSRaphael Isemann         std::static_pointer_cast<WindowDelegate>(app_delegate_sp));
4024*7caa17caSRaphael Isemann     source_window_sp->SetDelegate(
4025*7caa17caSRaphael Isemann         WindowDelegateSP(new SourceFileWindowDelegate(m_debugger)));
4026*7caa17caSRaphael Isemann     variables_window_sp->SetDelegate(
4027*7caa17caSRaphael Isemann         WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
4028*7caa17caSRaphael Isemann     TreeDelegateSP thread_delegate_sp(new ThreadsTreeDelegate(m_debugger));
4029*7caa17caSRaphael Isemann     threads_window_sp->SetDelegate(WindowDelegateSP(
4030*7caa17caSRaphael Isemann         new TreeWindowDelegate(m_debugger, thread_delegate_sp)));
4031*7caa17caSRaphael Isemann     status_window_sp->SetDelegate(
4032*7caa17caSRaphael Isemann         WindowDelegateSP(new StatusBarWindowDelegate(m_debugger)));
4033*7caa17caSRaphael Isemann 
4034*7caa17caSRaphael Isemann     // Show the main help window once the first time the curses GUI is launched
4035*7caa17caSRaphael Isemann     static bool g_showed_help = false;
4036*7caa17caSRaphael Isemann     if (!g_showed_help) {
4037*7caa17caSRaphael Isemann       g_showed_help = true;
4038*7caa17caSRaphael Isemann       main_window_sp->CreateHelpSubwindow();
4039*7caa17caSRaphael Isemann     }
4040*7caa17caSRaphael Isemann 
4041*7caa17caSRaphael Isemann     init_pair(1, COLOR_WHITE, COLOR_BLUE);
4042*7caa17caSRaphael Isemann     init_pair(2, COLOR_BLACK, COLOR_WHITE);
4043*7caa17caSRaphael Isemann     init_pair(3, COLOR_MAGENTA, COLOR_WHITE);
4044*7caa17caSRaphael Isemann     init_pair(4, COLOR_MAGENTA, COLOR_BLACK);
4045*7caa17caSRaphael Isemann     init_pair(5, COLOR_RED, COLOR_BLACK);
4046*7caa17caSRaphael Isemann   }
4047*7caa17caSRaphael Isemann }
4048*7caa17caSRaphael Isemann 
4049*7caa17caSRaphael Isemann void IOHandlerCursesGUI::Deactivate() { m_app_ap->Terminate(); }
4050*7caa17caSRaphael Isemann 
4051*7caa17caSRaphael Isemann void IOHandlerCursesGUI::Run() {
4052*7caa17caSRaphael Isemann   m_app_ap->Run(m_debugger);
4053*7caa17caSRaphael Isemann   SetIsDone(true);
4054*7caa17caSRaphael Isemann }
4055*7caa17caSRaphael Isemann 
4056*7caa17caSRaphael Isemann IOHandlerCursesGUI::~IOHandlerCursesGUI() = default;
4057*7caa17caSRaphael Isemann 
4058*7caa17caSRaphael Isemann void IOHandlerCursesGUI::Cancel() {}
4059*7caa17caSRaphael Isemann 
4060*7caa17caSRaphael Isemann bool IOHandlerCursesGUI::Interrupt() { return false; }
4061*7caa17caSRaphael Isemann 
4062*7caa17caSRaphael Isemann void IOHandlerCursesGUI::GotEOF() {}
4063*7caa17caSRaphael Isemann 
4064*7caa17caSRaphael Isemann #endif // LLDB_DISABLE_CURSES
4065