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