15ffd83dbSDimitry Andric //===-- IOHandlerCursesGUI.cpp --------------------------------------------===// 2480093f4SDimitry Andric // 3480093f4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4480093f4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5480093f4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6480093f4SDimitry Andric // 7480093f4SDimitry Andric //===----------------------------------------------------------------------===// 8480093f4SDimitry Andric 9480093f4SDimitry Andric #include "lldb/Core/IOHandlerCursesGUI.h" 10480093f4SDimitry Andric #include "lldb/Host/Config.h" 11480093f4SDimitry Andric 12480093f4SDimitry Andric #if LLDB_ENABLE_CURSES 13e8d8bef9SDimitry Andric #if CURSES_HAVE_NCURSES_CURSES_H 14e8d8bef9SDimitry Andric #include <ncurses/curses.h> 15e8d8bef9SDimitry Andric #include <ncurses/panel.h> 16e8d8bef9SDimitry Andric #else 17480093f4SDimitry Andric #include <curses.h> 18480093f4SDimitry Andric #include <panel.h> 19480093f4SDimitry Andric #endif 20e8d8bef9SDimitry Andric #endif 21480093f4SDimitry Andric 22480093f4SDimitry Andric #if defined(__APPLE__) 23480093f4SDimitry Andric #include <deque> 24480093f4SDimitry Andric #endif 25480093f4SDimitry Andric #include <string> 26480093f4SDimitry Andric 27480093f4SDimitry Andric #include "lldb/Core/Debugger.h" 28480093f4SDimitry Andric #include "lldb/Core/StreamFile.h" 29fe6060f1SDimitry Andric #include "lldb/Core/ValueObjectUpdater.h" 30480093f4SDimitry Andric #include "lldb/Host/File.h" 3181ad6265SDimitry Andric #include "lldb/Utility/AnsiTerminal.h" 32480093f4SDimitry Andric #include "lldb/Utility/Predicate.h" 33480093f4SDimitry Andric #include "lldb/Utility/Status.h" 34480093f4SDimitry Andric #include "lldb/Utility/StreamString.h" 35480093f4SDimitry Andric #include "lldb/Utility/StringList.h" 36480093f4SDimitry Andric #include "lldb/lldb-forward.h" 37480093f4SDimitry Andric 38480093f4SDimitry Andric #include "lldb/Interpreter/CommandCompletions.h" 39480093f4SDimitry Andric #include "lldb/Interpreter/CommandInterpreter.h" 40349cc55cSDimitry Andric #include "lldb/Interpreter/OptionGroupPlatform.h" 41480093f4SDimitry Andric 42480093f4SDimitry Andric #if LLDB_ENABLE_CURSES 43480093f4SDimitry Andric #include "lldb/Breakpoint/BreakpointLocation.h" 44480093f4SDimitry Andric #include "lldb/Core/Module.h" 45fe6060f1SDimitry Andric #include "lldb/Core/PluginManager.h" 46480093f4SDimitry Andric #include "lldb/Core/ValueObject.h" 47480093f4SDimitry Andric #include "lldb/Core/ValueObjectRegister.h" 48480093f4SDimitry Andric #include "lldb/Symbol/Block.h" 49349cc55cSDimitry Andric #include "lldb/Symbol/CompileUnit.h" 50480093f4SDimitry Andric #include "lldb/Symbol/Function.h" 51480093f4SDimitry Andric #include "lldb/Symbol/Symbol.h" 52480093f4SDimitry Andric #include "lldb/Symbol/VariableList.h" 53480093f4SDimitry Andric #include "lldb/Target/Process.h" 54480093f4SDimitry Andric #include "lldb/Target/RegisterContext.h" 55480093f4SDimitry Andric #include "lldb/Target/StackFrame.h" 56480093f4SDimitry Andric #include "lldb/Target/StopInfo.h" 57480093f4SDimitry Andric #include "lldb/Target/Target.h" 58480093f4SDimitry Andric #include "lldb/Target/Thread.h" 59480093f4SDimitry Andric #include "lldb/Utility/State.h" 60480093f4SDimitry Andric #endif 61480093f4SDimitry Andric 62480093f4SDimitry Andric #include "llvm/ADT/StringRef.h" 63480093f4SDimitry Andric 64480093f4SDimitry Andric #ifdef _WIN32 65480093f4SDimitry Andric #include "lldb/Host/windows/windows.h" 66480093f4SDimitry Andric #endif 67480093f4SDimitry Andric 68480093f4SDimitry Andric #include <memory> 69480093f4SDimitry Andric #include <mutex> 70480093f4SDimitry Andric 71fe6060f1SDimitry Andric #include <cassert> 72fe6060f1SDimitry Andric #include <cctype> 73fe6060f1SDimitry Andric #include <cerrno> 74fe6060f1SDimitry Andric #include <cstdint> 75fe6060f1SDimitry Andric #include <cstdio> 76fe6060f1SDimitry Andric #include <cstring> 77fe6060f1SDimitry Andric #include <functional> 78bdd1243dSDimitry Andric #include <optional> 79480093f4SDimitry Andric #include <type_traits> 80480093f4SDimitry Andric 81480093f4SDimitry Andric using namespace lldb; 82480093f4SDimitry Andric using namespace lldb_private; 83480093f4SDimitry Andric using llvm::StringRef; 84480093f4SDimitry Andric 85480093f4SDimitry Andric // we may want curses to be disabled for some builds for instance, windows 86480093f4SDimitry Andric #if LLDB_ENABLE_CURSES 87480093f4SDimitry Andric 88349cc55cSDimitry Andric #define KEY_CTRL_A 1 89349cc55cSDimitry Andric #define KEY_CTRL_E 5 90349cc55cSDimitry Andric #define KEY_CTRL_K 11 91480093f4SDimitry Andric #define KEY_RETURN 10 92480093f4SDimitry Andric #define KEY_ESCAPE 27 93349cc55cSDimitry Andric #define KEY_DELETE 127 94480093f4SDimitry Andric 95fe6060f1SDimitry Andric #define KEY_SHIFT_TAB (KEY_MAX + 1) 96349cc55cSDimitry Andric #define KEY_ALT_ENTER (KEY_MAX + 2) 97fe6060f1SDimitry Andric 98480093f4SDimitry Andric namespace curses { 99480093f4SDimitry Andric class Menu; 100480093f4SDimitry Andric class MenuDelegate; 101480093f4SDimitry Andric class Window; 102480093f4SDimitry Andric class WindowDelegate; 103480093f4SDimitry Andric typedef std::shared_ptr<Menu> MenuSP; 104480093f4SDimitry Andric typedef std::shared_ptr<MenuDelegate> MenuDelegateSP; 105480093f4SDimitry Andric typedef std::shared_ptr<Window> WindowSP; 106480093f4SDimitry Andric typedef std::shared_ptr<WindowDelegate> WindowDelegateSP; 107480093f4SDimitry Andric typedef std::vector<MenuSP> Menus; 108480093f4SDimitry Andric typedef std::vector<WindowSP> Windows; 109480093f4SDimitry Andric typedef std::vector<WindowDelegateSP> WindowDelegates; 110480093f4SDimitry Andric 111480093f4SDimitry Andric #if 0 112480093f4SDimitry Andric type summary add -s "x=${var.x}, y=${var.y}" curses::Point 113480093f4SDimitry Andric type summary add -s "w=${var.width}, h=${var.height}" curses::Size 114480093f4SDimitry Andric type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect 115480093f4SDimitry Andric #endif 116480093f4SDimitry Andric 117480093f4SDimitry Andric struct Point { 118480093f4SDimitry Andric int x; 119480093f4SDimitry Andric int y; 120480093f4SDimitry Andric 121480093f4SDimitry Andric Point(int _x = 0, int _y = 0) : x(_x), y(_y) {} 122480093f4SDimitry Andric 123480093f4SDimitry Andric void Clear() { 124480093f4SDimitry Andric x = 0; 125480093f4SDimitry Andric y = 0; 126480093f4SDimitry Andric } 127480093f4SDimitry Andric 128480093f4SDimitry Andric Point &operator+=(const Point &rhs) { 129480093f4SDimitry Andric x += rhs.x; 130480093f4SDimitry Andric y += rhs.y; 131480093f4SDimitry Andric return *this; 132480093f4SDimitry Andric } 133480093f4SDimitry Andric 134480093f4SDimitry Andric void Dump() { printf("(x=%i, y=%i)\n", x, y); } 135480093f4SDimitry Andric }; 136480093f4SDimitry Andric 137480093f4SDimitry Andric bool operator==(const Point &lhs, const Point &rhs) { 138480093f4SDimitry Andric return lhs.x == rhs.x && lhs.y == rhs.y; 139480093f4SDimitry Andric } 140480093f4SDimitry Andric 141480093f4SDimitry Andric bool operator!=(const Point &lhs, const Point &rhs) { 142480093f4SDimitry Andric return lhs.x != rhs.x || lhs.y != rhs.y; 143480093f4SDimitry Andric } 144480093f4SDimitry Andric 145480093f4SDimitry Andric struct Size { 146480093f4SDimitry Andric int width; 147480093f4SDimitry Andric int height; 148480093f4SDimitry Andric Size(int w = 0, int h = 0) : width(w), height(h) {} 149480093f4SDimitry Andric 150480093f4SDimitry Andric void Clear() { 151480093f4SDimitry Andric width = 0; 152480093f4SDimitry Andric height = 0; 153480093f4SDimitry Andric } 154480093f4SDimitry Andric 155480093f4SDimitry Andric void Dump() { printf("(w=%i, h=%i)\n", width, height); } 156480093f4SDimitry Andric }; 157480093f4SDimitry Andric 158480093f4SDimitry Andric bool operator==(const Size &lhs, const Size &rhs) { 159480093f4SDimitry Andric return lhs.width == rhs.width && lhs.height == rhs.height; 160480093f4SDimitry Andric } 161480093f4SDimitry Andric 162480093f4SDimitry Andric bool operator!=(const Size &lhs, const Size &rhs) { 163480093f4SDimitry Andric return lhs.width != rhs.width || lhs.height != rhs.height; 164480093f4SDimitry Andric } 165480093f4SDimitry Andric 166480093f4SDimitry Andric struct Rect { 167480093f4SDimitry Andric Point origin; 168480093f4SDimitry Andric Size size; 169480093f4SDimitry Andric 170480093f4SDimitry Andric Rect() : origin(), size() {} 171480093f4SDimitry Andric 172480093f4SDimitry Andric Rect(const Point &p, const Size &s) : origin(p), size(s) {} 173480093f4SDimitry Andric 174480093f4SDimitry Andric void Clear() { 175480093f4SDimitry Andric origin.Clear(); 176480093f4SDimitry Andric size.Clear(); 177480093f4SDimitry Andric } 178480093f4SDimitry Andric 179480093f4SDimitry Andric void Dump() { 180480093f4SDimitry Andric printf("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width, 181480093f4SDimitry Andric size.height); 182480093f4SDimitry Andric } 183480093f4SDimitry Andric 184480093f4SDimitry Andric void Inset(int w, int h) { 185480093f4SDimitry Andric if (size.width > w * 2) 186480093f4SDimitry Andric size.width -= w * 2; 187480093f4SDimitry Andric origin.x += w; 188480093f4SDimitry Andric 189480093f4SDimitry Andric if (size.height > h * 2) 190480093f4SDimitry Andric size.height -= h * 2; 191480093f4SDimitry Andric origin.y += h; 192480093f4SDimitry Andric } 193480093f4SDimitry Andric 194480093f4SDimitry Andric // Return a status bar rectangle which is the last line of this rectangle. 195480093f4SDimitry Andric // This rectangle will be modified to not include the status bar area. 196480093f4SDimitry Andric Rect MakeStatusBar() { 197480093f4SDimitry Andric Rect status_bar; 198480093f4SDimitry Andric if (size.height > 1) { 199480093f4SDimitry Andric status_bar.origin.x = origin.x; 200480093f4SDimitry Andric status_bar.origin.y = size.height; 201480093f4SDimitry Andric status_bar.size.width = size.width; 202480093f4SDimitry Andric status_bar.size.height = 1; 203480093f4SDimitry Andric --size.height; 204480093f4SDimitry Andric } 205480093f4SDimitry Andric return status_bar; 206480093f4SDimitry Andric } 207480093f4SDimitry Andric 208480093f4SDimitry Andric // Return a menubar rectangle which is the first line of this rectangle. This 209480093f4SDimitry Andric // rectangle will be modified to not include the menubar area. 210480093f4SDimitry Andric Rect MakeMenuBar() { 211480093f4SDimitry Andric Rect menubar; 212480093f4SDimitry Andric if (size.height > 1) { 213480093f4SDimitry Andric menubar.origin.x = origin.x; 214480093f4SDimitry Andric menubar.origin.y = origin.y; 215480093f4SDimitry Andric menubar.size.width = size.width; 216480093f4SDimitry Andric menubar.size.height = 1; 217480093f4SDimitry Andric ++origin.y; 218480093f4SDimitry Andric --size.height; 219480093f4SDimitry Andric } 220480093f4SDimitry Andric return menubar; 221480093f4SDimitry Andric } 222480093f4SDimitry Andric 223480093f4SDimitry Andric void HorizontalSplitPercentage(float top_percentage, Rect &top, 224480093f4SDimitry Andric Rect &bottom) const { 225480093f4SDimitry Andric float top_height = top_percentage * size.height; 226480093f4SDimitry Andric HorizontalSplit(top_height, top, bottom); 227480093f4SDimitry Andric } 228480093f4SDimitry Andric 229480093f4SDimitry Andric void HorizontalSplit(int top_height, Rect &top, Rect &bottom) const { 230480093f4SDimitry Andric top = *this; 231480093f4SDimitry Andric if (top_height < size.height) { 232480093f4SDimitry Andric top.size.height = top_height; 233480093f4SDimitry Andric bottom.origin.x = origin.x; 234480093f4SDimitry Andric bottom.origin.y = origin.y + top.size.height; 235480093f4SDimitry Andric bottom.size.width = size.width; 236480093f4SDimitry Andric bottom.size.height = size.height - top.size.height; 237480093f4SDimitry Andric } else { 238480093f4SDimitry Andric bottom.Clear(); 239480093f4SDimitry Andric } 240480093f4SDimitry Andric } 241480093f4SDimitry Andric 242480093f4SDimitry Andric void VerticalSplitPercentage(float left_percentage, Rect &left, 243480093f4SDimitry Andric Rect &right) const { 244480093f4SDimitry Andric float left_width = left_percentage * size.width; 245480093f4SDimitry Andric VerticalSplit(left_width, left, right); 246480093f4SDimitry Andric } 247480093f4SDimitry Andric 248480093f4SDimitry Andric void VerticalSplit(int left_width, Rect &left, Rect &right) const { 249480093f4SDimitry Andric left = *this; 250480093f4SDimitry Andric if (left_width < size.width) { 251480093f4SDimitry Andric left.size.width = left_width; 252480093f4SDimitry Andric right.origin.x = origin.x + left.size.width; 253480093f4SDimitry Andric right.origin.y = origin.y; 254480093f4SDimitry Andric right.size.width = size.width - left.size.width; 255480093f4SDimitry Andric right.size.height = size.height; 256480093f4SDimitry Andric } else { 257480093f4SDimitry Andric right.Clear(); 258480093f4SDimitry Andric } 259480093f4SDimitry Andric } 260480093f4SDimitry Andric }; 261480093f4SDimitry Andric 262480093f4SDimitry Andric bool operator==(const Rect &lhs, const Rect &rhs) { 263480093f4SDimitry Andric return lhs.origin == rhs.origin && lhs.size == rhs.size; 264480093f4SDimitry Andric } 265480093f4SDimitry Andric 266480093f4SDimitry Andric bool operator!=(const Rect &lhs, const Rect &rhs) { 267480093f4SDimitry Andric return lhs.origin != rhs.origin || lhs.size != rhs.size; 268480093f4SDimitry Andric } 269480093f4SDimitry Andric 270480093f4SDimitry Andric enum HandleCharResult { 271480093f4SDimitry Andric eKeyNotHandled = 0, 272480093f4SDimitry Andric eKeyHandled = 1, 273480093f4SDimitry Andric eQuitApplication = 2 274480093f4SDimitry Andric }; 275480093f4SDimitry Andric 276480093f4SDimitry Andric enum class MenuActionResult { 277480093f4SDimitry Andric Handled, 278480093f4SDimitry Andric NotHandled, 279480093f4SDimitry Andric Quit // Exit all menus and quit 280480093f4SDimitry Andric }; 281480093f4SDimitry Andric 282480093f4SDimitry Andric struct KeyHelp { 283480093f4SDimitry Andric int ch; 284480093f4SDimitry Andric const char *description; 285480093f4SDimitry Andric }; 286480093f4SDimitry Andric 287e8d8bef9SDimitry Andric // COLOR_PAIR index names 288e8d8bef9SDimitry Andric enum { 289e8d8bef9SDimitry Andric // First 16 colors are 8 black background and 8 blue background colors, 290e8d8bef9SDimitry Andric // needed by OutputColoredStringTruncated(). 291e8d8bef9SDimitry Andric BlackOnBlack = 1, 292e8d8bef9SDimitry Andric RedOnBlack, 293e8d8bef9SDimitry Andric GreenOnBlack, 294e8d8bef9SDimitry Andric YellowOnBlack, 295e8d8bef9SDimitry Andric BlueOnBlack, 296e8d8bef9SDimitry Andric MagentaOnBlack, 297e8d8bef9SDimitry Andric CyanOnBlack, 298e8d8bef9SDimitry Andric WhiteOnBlack, 299e8d8bef9SDimitry Andric BlackOnBlue, 300e8d8bef9SDimitry Andric RedOnBlue, 301e8d8bef9SDimitry Andric GreenOnBlue, 302e8d8bef9SDimitry Andric YellowOnBlue, 303e8d8bef9SDimitry Andric BlueOnBlue, 304e8d8bef9SDimitry Andric MagentaOnBlue, 305e8d8bef9SDimitry Andric CyanOnBlue, 306e8d8bef9SDimitry Andric WhiteOnBlue, 307e8d8bef9SDimitry Andric // Other colors, as needed. 308e8d8bef9SDimitry Andric BlackOnWhite, 309e8d8bef9SDimitry Andric MagentaOnWhite, 310e8d8bef9SDimitry Andric LastColorPairIndex = MagentaOnWhite 311e8d8bef9SDimitry Andric }; 312e8d8bef9SDimitry Andric 313480093f4SDimitry Andric class WindowDelegate { 314480093f4SDimitry Andric public: 315480093f4SDimitry Andric virtual ~WindowDelegate() = default; 316480093f4SDimitry Andric 317480093f4SDimitry Andric virtual bool WindowDelegateDraw(Window &window, bool force) { 318480093f4SDimitry Andric return false; // Drawing not handled 319480093f4SDimitry Andric } 320480093f4SDimitry Andric 321480093f4SDimitry Andric virtual HandleCharResult WindowDelegateHandleChar(Window &window, int key) { 322480093f4SDimitry Andric return eKeyNotHandled; 323480093f4SDimitry Andric } 324480093f4SDimitry Andric 325480093f4SDimitry Andric virtual const char *WindowDelegateGetHelpText() { return nullptr; } 326480093f4SDimitry Andric 327480093f4SDimitry Andric virtual KeyHelp *WindowDelegateGetKeyHelp() { return nullptr; } 328480093f4SDimitry Andric }; 329480093f4SDimitry Andric 330480093f4SDimitry Andric class HelpDialogDelegate : public WindowDelegate { 331480093f4SDimitry Andric public: 332480093f4SDimitry Andric HelpDialogDelegate(const char *text, KeyHelp *key_help_array); 333480093f4SDimitry Andric 334480093f4SDimitry Andric ~HelpDialogDelegate() override; 335480093f4SDimitry Andric 336480093f4SDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override; 337480093f4SDimitry Andric 338480093f4SDimitry Andric HandleCharResult WindowDelegateHandleChar(Window &window, int key) override; 339480093f4SDimitry Andric 340480093f4SDimitry Andric size_t GetNumLines() const { return m_text.GetSize(); } 341480093f4SDimitry Andric 342480093f4SDimitry Andric size_t GetMaxLineLength() const { return m_text.GetMaxStringLength(); } 343480093f4SDimitry Andric 344480093f4SDimitry Andric protected: 345480093f4SDimitry Andric StringList m_text; 34681ad6265SDimitry Andric int m_first_visible_line = 0; 347480093f4SDimitry Andric }; 348480093f4SDimitry Andric 349fe6060f1SDimitry Andric // A surface is an abstraction for something than can be drawn on. The surface 350fe6060f1SDimitry Andric // have a width, a height, a cursor position, and a multitude of drawing 351fe6060f1SDimitry Andric // operations. This type should be sub-classed to get an actually useful ncurses 352349cc55cSDimitry Andric // object, such as a Window or a Pad. 353fe6060f1SDimitry Andric class Surface { 354480093f4SDimitry Andric public: 355349cc55cSDimitry Andric enum class Type { Window, Pad }; 356349cc55cSDimitry Andric 35781ad6265SDimitry Andric Surface(Surface::Type type) : m_type(type) {} 358480093f4SDimitry Andric 359fe6060f1SDimitry Andric WINDOW *get() { return m_window; } 360fe6060f1SDimitry Andric 361fe6060f1SDimitry Andric operator WINDOW *() { return m_window; } 362fe6060f1SDimitry Andric 363349cc55cSDimitry Andric Surface SubSurface(Rect bounds) { 364349cc55cSDimitry Andric Surface subSurface(m_type); 365349cc55cSDimitry Andric if (m_type == Type::Pad) 366349cc55cSDimitry Andric subSurface.m_window = 367349cc55cSDimitry Andric ::subpad(m_window, bounds.size.height, bounds.size.width, 368349cc55cSDimitry Andric bounds.origin.y, bounds.origin.x); 369349cc55cSDimitry Andric else 370349cc55cSDimitry Andric subSurface.m_window = 371349cc55cSDimitry Andric ::derwin(m_window, bounds.size.height, bounds.size.width, 372349cc55cSDimitry Andric bounds.origin.y, bounds.origin.x); 373349cc55cSDimitry Andric return subSurface; 374349cc55cSDimitry Andric } 375349cc55cSDimitry Andric 376fe6060f1SDimitry Andric // Copy a region of the surface to another surface. 377fe6060f1SDimitry Andric void CopyToSurface(Surface &target, Point source_origin, Point target_origin, 378fe6060f1SDimitry Andric Size size) { 379fe6060f1SDimitry Andric ::copywin(m_window, target.get(), source_origin.y, source_origin.x, 380fe6060f1SDimitry Andric target_origin.y, target_origin.x, 381fe6060f1SDimitry Andric target_origin.y + size.height - 1, 382fe6060f1SDimitry Andric target_origin.x + size.width - 1, false); 383480093f4SDimitry Andric } 384480093f4SDimitry Andric 385fe6060f1SDimitry Andric int GetCursorX() const { return getcurx(m_window); } 386fe6060f1SDimitry Andric int GetCursorY() const { return getcury(m_window); } 387fe6060f1SDimitry Andric void MoveCursor(int x, int y) { ::wmove(m_window, y, x); } 388480093f4SDimitry Andric 389480093f4SDimitry Andric void AttributeOn(attr_t attr) { ::wattron(m_window, attr); } 390480093f4SDimitry Andric void AttributeOff(attr_t attr) { ::wattroff(m_window, attr); } 391fe6060f1SDimitry Andric 392e8d8bef9SDimitry Andric int GetMaxX() const { return getmaxx(m_window); } 393e8d8bef9SDimitry Andric int GetMaxY() const { return getmaxy(m_window); } 394e8d8bef9SDimitry Andric int GetWidth() const { return GetMaxX(); } 395e8d8bef9SDimitry Andric int GetHeight() const { return GetMaxY(); } 396fe6060f1SDimitry Andric Size GetSize() const { return Size(GetWidth(), GetHeight()); } 397fe6060f1SDimitry Andric // Get a zero origin rectangle width the surface size. 398fe6060f1SDimitry Andric Rect GetFrame() const { return Rect(Point(), GetSize()); } 399fe6060f1SDimitry Andric 400fe6060f1SDimitry Andric void Clear() { ::wclear(m_window); } 401fe6060f1SDimitry Andric void Erase() { ::werase(m_window); } 402fe6060f1SDimitry Andric 403480093f4SDimitry Andric void SetBackground(int color_pair_idx) { 404480093f4SDimitry Andric ::wbkgd(m_window, COLOR_PAIR(color_pair_idx)); 405480093f4SDimitry Andric } 406480093f4SDimitry Andric 407fe6060f1SDimitry Andric void PutChar(int ch) { ::waddch(m_window, ch); } 408fe6060f1SDimitry Andric void PutCString(const char *s, int len = -1) { ::waddnstr(m_window, s, len); } 409fe6060f1SDimitry Andric 410e8d8bef9SDimitry Andric void PutCStringTruncated(int right_pad, const char *s, int len = -1) { 411480093f4SDimitry Andric int bytes_left = GetWidth() - GetCursorX(); 412480093f4SDimitry Andric if (bytes_left > right_pad) { 413480093f4SDimitry Andric bytes_left -= right_pad; 414e8d8bef9SDimitry Andric ::waddnstr(m_window, s, len < 0 ? bytes_left : std::min(bytes_left, len)); 415480093f4SDimitry Andric } 416480093f4SDimitry Andric } 417480093f4SDimitry Andric 418480093f4SDimitry Andric void Printf(const char *format, ...) __attribute__((format(printf, 2, 3))) { 419480093f4SDimitry Andric va_list args; 420480093f4SDimitry Andric va_start(args, format); 421fe6060f1SDimitry Andric vw_printw(m_window, format, args); 422480093f4SDimitry Andric va_end(args); 423480093f4SDimitry Andric } 424480093f4SDimitry Andric 425e8d8bef9SDimitry Andric void PrintfTruncated(int right_pad, const char *format, ...) 426e8d8bef9SDimitry Andric __attribute__((format(printf, 3, 4))) { 427e8d8bef9SDimitry Andric va_list args; 428e8d8bef9SDimitry Andric va_start(args, format); 429e8d8bef9SDimitry Andric StreamString strm; 430e8d8bef9SDimitry Andric strm.PrintfVarArg(format, args); 431e8d8bef9SDimitry Andric va_end(args); 432e8d8bef9SDimitry Andric PutCStringTruncated(right_pad, strm.GetData()); 433e8d8bef9SDimitry Andric } 434e8d8bef9SDimitry Andric 435fe6060f1SDimitry Andric void VerticalLine(int n, chtype v_char = ACS_VLINE) { 436fe6060f1SDimitry Andric ::wvline(m_window, v_char, n); 437fe6060f1SDimitry Andric } 438fe6060f1SDimitry Andric void HorizontalLine(int n, chtype h_char = ACS_HLINE) { 439fe6060f1SDimitry Andric ::whline(m_window, h_char, n); 440fe6060f1SDimitry Andric } 441fe6060f1SDimitry Andric void Box(chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) { 442fe6060f1SDimitry Andric ::box(m_window, v_char, h_char); 443fe6060f1SDimitry Andric } 444fe6060f1SDimitry Andric 445fe6060f1SDimitry Andric void TitledBox(const char *title, chtype v_char = ACS_VLINE, 446fe6060f1SDimitry Andric chtype h_char = ACS_HLINE) { 447fe6060f1SDimitry Andric Box(v_char, h_char); 448fe6060f1SDimitry Andric int title_offset = 2; 449fe6060f1SDimitry Andric MoveCursor(title_offset, 0); 450fe6060f1SDimitry Andric PutChar('['); 451fe6060f1SDimitry Andric PutCString(title, GetWidth() - title_offset); 452fe6060f1SDimitry Andric PutChar(']'); 453fe6060f1SDimitry Andric } 454fe6060f1SDimitry Andric 455fe6060f1SDimitry Andric void Box(const Rect &bounds, chtype v_char = ACS_VLINE, 456fe6060f1SDimitry Andric chtype h_char = ACS_HLINE) { 457fe6060f1SDimitry Andric MoveCursor(bounds.origin.x, bounds.origin.y); 458fe6060f1SDimitry Andric VerticalLine(bounds.size.height); 459fe6060f1SDimitry Andric HorizontalLine(bounds.size.width); 460fe6060f1SDimitry Andric PutChar(ACS_ULCORNER); 461fe6060f1SDimitry Andric 462fe6060f1SDimitry Andric MoveCursor(bounds.origin.x + bounds.size.width - 1, bounds.origin.y); 463fe6060f1SDimitry Andric VerticalLine(bounds.size.height); 464fe6060f1SDimitry Andric PutChar(ACS_URCORNER); 465fe6060f1SDimitry Andric 466fe6060f1SDimitry Andric MoveCursor(bounds.origin.x, bounds.origin.y + bounds.size.height - 1); 467fe6060f1SDimitry Andric HorizontalLine(bounds.size.width); 468fe6060f1SDimitry Andric PutChar(ACS_LLCORNER); 469fe6060f1SDimitry Andric 470fe6060f1SDimitry Andric MoveCursor(bounds.origin.x + bounds.size.width - 1, 471fe6060f1SDimitry Andric bounds.origin.y + bounds.size.height - 1); 472fe6060f1SDimitry Andric PutChar(ACS_LRCORNER); 473fe6060f1SDimitry Andric } 474fe6060f1SDimitry Andric 475fe6060f1SDimitry Andric void TitledBox(const Rect &bounds, const char *title, 476fe6060f1SDimitry Andric chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) { 477fe6060f1SDimitry Andric Box(bounds, v_char, h_char); 478fe6060f1SDimitry Andric int title_offset = 2; 479fe6060f1SDimitry Andric MoveCursor(bounds.origin.x + title_offset, bounds.origin.y); 480fe6060f1SDimitry Andric PutChar('['); 481fe6060f1SDimitry Andric PutCString(title, bounds.size.width - title_offset); 482fe6060f1SDimitry Andric PutChar(']'); 483e8d8bef9SDimitry Andric } 484e8d8bef9SDimitry Andric 485e8d8bef9SDimitry Andric // Curses doesn't allow direct output of color escape sequences, but that's 486e8d8bef9SDimitry Andric // how we get source lines from the Highligher class. Read the line and 487e8d8bef9SDimitry Andric // convert color escape sequences to curses color attributes. Use 488e8d8bef9SDimitry Andric // first_skip_count to skip leading visible characters. Returns false if all 489e8d8bef9SDimitry Andric // visible characters were skipped due to first_skip_count. 490e8d8bef9SDimitry Andric bool OutputColoredStringTruncated(int right_pad, StringRef string, 491e8d8bef9SDimitry Andric size_t skip_first_count, 492e8d8bef9SDimitry Andric bool use_blue_background) { 493e8d8bef9SDimitry Andric attr_t saved_attr; 494e8d8bef9SDimitry Andric short saved_pair; 495e8d8bef9SDimitry Andric bool result = false; 496e8d8bef9SDimitry Andric wattr_get(m_window, &saved_attr, &saved_pair, nullptr); 497e8d8bef9SDimitry Andric if (use_blue_background) 498e8d8bef9SDimitry Andric ::wattron(m_window, COLOR_PAIR(WhiteOnBlue)); 499e8d8bef9SDimitry Andric while (!string.empty()) { 50081ad6265SDimitry Andric size_t esc_pos = string.find(ANSI_ESC_START); 501e8d8bef9SDimitry Andric if (esc_pos == StringRef::npos) { 502e8d8bef9SDimitry Andric string = string.substr(skip_first_count); 503e8d8bef9SDimitry Andric if (!string.empty()) { 504e8d8bef9SDimitry Andric PutCStringTruncated(right_pad, string.data(), string.size()); 505e8d8bef9SDimitry Andric result = true; 506e8d8bef9SDimitry Andric } 507e8d8bef9SDimitry Andric break; 508e8d8bef9SDimitry Andric } 509e8d8bef9SDimitry Andric if (esc_pos > 0) { 510e8d8bef9SDimitry Andric if (skip_first_count > 0) { 511e8d8bef9SDimitry Andric int skip = std::min(esc_pos, skip_first_count); 512e8d8bef9SDimitry Andric string = string.substr(skip); 513e8d8bef9SDimitry Andric skip_first_count -= skip; 514e8d8bef9SDimitry Andric esc_pos -= skip; 515e8d8bef9SDimitry Andric } 516e8d8bef9SDimitry Andric if (esc_pos > 0) { 517e8d8bef9SDimitry Andric PutCStringTruncated(right_pad, string.data(), esc_pos); 518e8d8bef9SDimitry Andric result = true; 519e8d8bef9SDimitry Andric string = string.drop_front(esc_pos); 520e8d8bef9SDimitry Andric } 521e8d8bef9SDimitry Andric } 52281ad6265SDimitry Andric bool consumed = string.consume_front(ANSI_ESC_START); 523e8d8bef9SDimitry Andric assert(consumed); 524e8d8bef9SDimitry Andric UNUSED_IF_ASSERT_DISABLED(consumed); 525e8d8bef9SDimitry Andric // This is written to match our Highlighter classes, which seem to 526e8d8bef9SDimitry Andric // generate only foreground color escape sequences. If necessary, this 527e8d8bef9SDimitry Andric // will need to be extended. 52881ad6265SDimitry Andric // Only 8 basic foreground colors, underline and reset, our Highlighter 52981ad6265SDimitry Andric // doesn't use anything else. 530e8d8bef9SDimitry Andric int value; 531e8d8bef9SDimitry Andric if (!!string.consumeInteger(10, value) || // Returns false on success. 53281ad6265SDimitry Andric !(value == 0 || value == ANSI_CTRL_UNDERLINE || 53381ad6265SDimitry Andric (value >= ANSI_FG_COLOR_BLACK && value <= ANSI_FG_COLOR_WHITE))) { 534e8d8bef9SDimitry Andric llvm::errs() << "No valid color code in color escape sequence.\n"; 535e8d8bef9SDimitry Andric continue; 536e8d8bef9SDimitry Andric } 53781ad6265SDimitry Andric if (!string.consume_front(ANSI_ESC_END)) { 53881ad6265SDimitry Andric llvm::errs() << "Missing '" << ANSI_ESC_END 53981ad6265SDimitry Andric << "' in color escape sequence.\n"; 540e8d8bef9SDimitry Andric continue; 541e8d8bef9SDimitry Andric } 542e8d8bef9SDimitry Andric if (value == 0) { // Reset. 543e8d8bef9SDimitry Andric wattr_set(m_window, saved_attr, saved_pair, nullptr); 544e8d8bef9SDimitry Andric if (use_blue_background) 545e8d8bef9SDimitry Andric ::wattron(m_window, COLOR_PAIR(WhiteOnBlue)); 54681ad6265SDimitry Andric } else if (value == ANSI_CTRL_UNDERLINE) { 54781ad6265SDimitry Andric ::wattron(m_window, A_UNDERLINE); 548e8d8bef9SDimitry Andric } else { 549e8d8bef9SDimitry Andric // Mapped directly to first 16 color pairs (black/blue background). 55081ad6265SDimitry Andric ::wattron(m_window, COLOR_PAIR(value - ANSI_FG_COLOR_BLACK + 1 + 55181ad6265SDimitry Andric (use_blue_background ? 8 : 0))); 552e8d8bef9SDimitry Andric } 553e8d8bef9SDimitry Andric } 554e8d8bef9SDimitry Andric wattr_set(m_window, saved_attr, saved_pair, nullptr); 555e8d8bef9SDimitry Andric return result; 556e8d8bef9SDimitry Andric } 557e8d8bef9SDimitry Andric 558fe6060f1SDimitry Andric protected: 559349cc55cSDimitry Andric Type m_type; 56081ad6265SDimitry Andric WINDOW *m_window = nullptr; 561fe6060f1SDimitry Andric }; 562fe6060f1SDimitry Andric 563fe6060f1SDimitry Andric class Pad : public Surface { 564fe6060f1SDimitry Andric public: 565349cc55cSDimitry Andric Pad(Size size) : Surface(Surface::Type::Pad) { 566349cc55cSDimitry Andric m_window = ::newpad(size.height, size.width); 567349cc55cSDimitry Andric } 568fe6060f1SDimitry Andric 569fe6060f1SDimitry Andric ~Pad() { ::delwin(m_window); } 570fe6060f1SDimitry Andric }; 571fe6060f1SDimitry Andric 572fe6060f1SDimitry Andric class Window : public Surface { 573fe6060f1SDimitry Andric public: 574fe6060f1SDimitry Andric Window(const char *name) 575349cc55cSDimitry Andric : Surface(Surface::Type::Window), m_name(name), m_panel(nullptr), 576349cc55cSDimitry Andric m_parent(nullptr), m_subwindows(), m_delegate_sp(), 577349cc55cSDimitry Andric m_curr_active_window_idx(UINT32_MAX), 578fe6060f1SDimitry Andric m_prev_active_window_idx(UINT32_MAX), m_delete(false), 579fe6060f1SDimitry Andric m_needs_update(true), m_can_activate(true), m_is_subwin(false) {} 580fe6060f1SDimitry Andric 581fe6060f1SDimitry Andric Window(const char *name, WINDOW *w, bool del = true) 582349cc55cSDimitry Andric : Surface(Surface::Type::Window), m_name(name), m_panel(nullptr), 583349cc55cSDimitry Andric m_parent(nullptr), m_subwindows(), m_delegate_sp(), 584349cc55cSDimitry Andric m_curr_active_window_idx(UINT32_MAX), 585fe6060f1SDimitry Andric m_prev_active_window_idx(UINT32_MAX), m_delete(del), 586fe6060f1SDimitry Andric m_needs_update(true), m_can_activate(true), m_is_subwin(false) { 587fe6060f1SDimitry Andric if (w) 588fe6060f1SDimitry Andric Reset(w); 589fe6060f1SDimitry Andric } 590fe6060f1SDimitry Andric 591fe6060f1SDimitry Andric Window(const char *name, const Rect &bounds) 592bdd1243dSDimitry Andric : Surface(Surface::Type::Window), m_name(name), m_panel(nullptr), 593bdd1243dSDimitry Andric m_parent(nullptr), m_subwindows(), m_delegate_sp(), 594bdd1243dSDimitry Andric m_curr_active_window_idx(UINT32_MAX), 595bdd1243dSDimitry Andric m_prev_active_window_idx(UINT32_MAX), m_delete(false), 596fe6060f1SDimitry Andric m_needs_update(true), m_can_activate(true), m_is_subwin(false) { 597fe6060f1SDimitry Andric Reset(::newwin(bounds.size.height, bounds.size.width, bounds.origin.y, 598fe6060f1SDimitry Andric bounds.origin.y)); 599fe6060f1SDimitry Andric } 600fe6060f1SDimitry Andric 601fe6060f1SDimitry Andric virtual ~Window() { 602fe6060f1SDimitry Andric RemoveSubWindows(); 603fe6060f1SDimitry Andric Reset(); 604fe6060f1SDimitry Andric } 605fe6060f1SDimitry Andric 606fe6060f1SDimitry Andric void Reset(WINDOW *w = nullptr, bool del = true) { 607fe6060f1SDimitry Andric if (m_window == w) 608fe6060f1SDimitry Andric return; 609fe6060f1SDimitry Andric 610fe6060f1SDimitry Andric if (m_panel) { 611fe6060f1SDimitry Andric ::del_panel(m_panel); 612fe6060f1SDimitry Andric m_panel = nullptr; 613fe6060f1SDimitry Andric } 614fe6060f1SDimitry Andric if (m_window && m_delete) { 615fe6060f1SDimitry Andric ::delwin(m_window); 616fe6060f1SDimitry Andric m_window = nullptr; 617fe6060f1SDimitry Andric m_delete = false; 618fe6060f1SDimitry Andric } 619fe6060f1SDimitry Andric if (w) { 620fe6060f1SDimitry Andric m_window = w; 621fe6060f1SDimitry Andric m_panel = ::new_panel(m_window); 622fe6060f1SDimitry Andric m_delete = del; 623fe6060f1SDimitry Andric } 624fe6060f1SDimitry Andric } 625fe6060f1SDimitry Andric 626fe6060f1SDimitry Andric // Get the rectangle in our parent window 627fe6060f1SDimitry Andric Rect GetBounds() const { return Rect(GetParentOrigin(), GetSize()); } 628fe6060f1SDimitry Andric 629fe6060f1SDimitry Andric Rect GetCenteredRect(int width, int height) { 630fe6060f1SDimitry Andric Size size = GetSize(); 631fe6060f1SDimitry Andric width = std::min(size.width, width); 632fe6060f1SDimitry Andric height = std::min(size.height, height); 633fe6060f1SDimitry Andric int x = (size.width - width) / 2; 634fe6060f1SDimitry Andric int y = (size.height - height) / 2; 635fe6060f1SDimitry Andric return Rect(Point(x, y), Size(width, height)); 636fe6060f1SDimitry Andric } 637fe6060f1SDimitry Andric 638fe6060f1SDimitry Andric int GetChar() { return ::wgetch(m_window); } 639fe6060f1SDimitry Andric Point GetParentOrigin() const { return Point(GetParentX(), GetParentY()); } 640fe6060f1SDimitry Andric int GetParentX() const { return getparx(m_window); } 641fe6060f1SDimitry Andric int GetParentY() const { return getpary(m_window); } 642fe6060f1SDimitry Andric void MoveWindow(int x, int y) { MoveWindow(Point(x, y)); } 643fe6060f1SDimitry Andric void Resize(int w, int h) { ::wresize(m_window, h, w); } 644fe6060f1SDimitry Andric void Resize(const Size &size) { 645fe6060f1SDimitry Andric ::wresize(m_window, size.height, size.width); 646fe6060f1SDimitry Andric } 647fe6060f1SDimitry Andric void MoveWindow(const Point &origin) { 648fe6060f1SDimitry Andric const bool moving_window = origin != GetParentOrigin(); 649fe6060f1SDimitry Andric if (m_is_subwin && moving_window) { 650fe6060f1SDimitry Andric // Can't move subwindows, must delete and re-create 651fe6060f1SDimitry Andric Size size = GetSize(); 652fe6060f1SDimitry Andric Reset(::subwin(m_parent->m_window, size.height, size.width, origin.y, 653fe6060f1SDimitry Andric origin.x), 654fe6060f1SDimitry Andric true); 655fe6060f1SDimitry Andric } else { 656fe6060f1SDimitry Andric ::mvwin(m_window, origin.y, origin.x); 657fe6060f1SDimitry Andric } 658fe6060f1SDimitry Andric } 659fe6060f1SDimitry Andric 660fe6060f1SDimitry Andric void SetBounds(const Rect &bounds) { 661fe6060f1SDimitry Andric const bool moving_window = bounds.origin != GetParentOrigin(); 662fe6060f1SDimitry Andric if (m_is_subwin && moving_window) { 663fe6060f1SDimitry Andric // Can't move subwindows, must delete and re-create 664fe6060f1SDimitry Andric Reset(::subwin(m_parent->m_window, bounds.size.height, bounds.size.width, 665fe6060f1SDimitry Andric bounds.origin.y, bounds.origin.x), 666fe6060f1SDimitry Andric true); 667fe6060f1SDimitry Andric } else { 668fe6060f1SDimitry Andric if (moving_window) 669fe6060f1SDimitry Andric MoveWindow(bounds.origin); 670fe6060f1SDimitry Andric Resize(bounds.size); 671fe6060f1SDimitry Andric } 672fe6060f1SDimitry Andric } 673fe6060f1SDimitry Andric 674480093f4SDimitry Andric void Touch() { 675480093f4SDimitry Andric ::touchwin(m_window); 676480093f4SDimitry Andric if (m_parent) 677480093f4SDimitry Andric m_parent->Touch(); 678480093f4SDimitry Andric } 679480093f4SDimitry Andric 680480093f4SDimitry Andric WindowSP CreateSubWindow(const char *name, const Rect &bounds, 681480093f4SDimitry Andric bool make_active) { 682480093f4SDimitry Andric auto get_window = [this, &bounds]() { 683480093f4SDimitry Andric return m_window 684480093f4SDimitry Andric ? ::subwin(m_window, bounds.size.height, bounds.size.width, 685480093f4SDimitry Andric bounds.origin.y, bounds.origin.x) 686480093f4SDimitry Andric : ::newwin(bounds.size.height, bounds.size.width, 687480093f4SDimitry Andric bounds.origin.y, bounds.origin.x); 688480093f4SDimitry Andric }; 689480093f4SDimitry Andric WindowSP subwindow_sp = std::make_shared<Window>(name, get_window(), true); 690480093f4SDimitry Andric subwindow_sp->m_is_subwin = subwindow_sp.operator bool(); 691480093f4SDimitry Andric subwindow_sp->m_parent = this; 692480093f4SDimitry Andric if (make_active) { 693480093f4SDimitry Andric m_prev_active_window_idx = m_curr_active_window_idx; 694480093f4SDimitry Andric m_curr_active_window_idx = m_subwindows.size(); 695480093f4SDimitry Andric } 696480093f4SDimitry Andric m_subwindows.push_back(subwindow_sp); 697480093f4SDimitry Andric ::top_panel(subwindow_sp->m_panel); 698480093f4SDimitry Andric m_needs_update = true; 699480093f4SDimitry Andric return subwindow_sp; 700480093f4SDimitry Andric } 701480093f4SDimitry Andric 702480093f4SDimitry Andric bool RemoveSubWindow(Window *window) { 703480093f4SDimitry Andric Windows::iterator pos, end = m_subwindows.end(); 704480093f4SDimitry Andric size_t i = 0; 705480093f4SDimitry Andric for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) { 706480093f4SDimitry Andric if ((*pos).get() == window) { 707480093f4SDimitry Andric if (m_prev_active_window_idx == i) 708480093f4SDimitry Andric m_prev_active_window_idx = UINT32_MAX; 709480093f4SDimitry Andric else if (m_prev_active_window_idx != UINT32_MAX && 710480093f4SDimitry Andric m_prev_active_window_idx > i) 711480093f4SDimitry Andric --m_prev_active_window_idx; 712480093f4SDimitry Andric 713480093f4SDimitry Andric if (m_curr_active_window_idx == i) 714480093f4SDimitry Andric m_curr_active_window_idx = UINT32_MAX; 715480093f4SDimitry Andric else if (m_curr_active_window_idx != UINT32_MAX && 716480093f4SDimitry Andric m_curr_active_window_idx > i) 717480093f4SDimitry Andric --m_curr_active_window_idx; 718480093f4SDimitry Andric window->Erase(); 719480093f4SDimitry Andric m_subwindows.erase(pos); 720480093f4SDimitry Andric m_needs_update = true; 721480093f4SDimitry Andric if (m_parent) 722480093f4SDimitry Andric m_parent->Touch(); 723480093f4SDimitry Andric else 724480093f4SDimitry Andric ::touchwin(stdscr); 725480093f4SDimitry Andric return true; 726480093f4SDimitry Andric } 727480093f4SDimitry Andric } 728480093f4SDimitry Andric return false; 729480093f4SDimitry Andric } 730480093f4SDimitry Andric 731480093f4SDimitry Andric WindowSP FindSubWindow(const char *name) { 732480093f4SDimitry Andric Windows::iterator pos, end = m_subwindows.end(); 733480093f4SDimitry Andric size_t i = 0; 734480093f4SDimitry Andric for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) { 735480093f4SDimitry Andric if ((*pos)->m_name == name) 736480093f4SDimitry Andric return *pos; 737480093f4SDimitry Andric } 738480093f4SDimitry Andric return WindowSP(); 739480093f4SDimitry Andric } 740480093f4SDimitry Andric 741480093f4SDimitry Andric void RemoveSubWindows() { 742480093f4SDimitry Andric m_curr_active_window_idx = UINT32_MAX; 743480093f4SDimitry Andric m_prev_active_window_idx = UINT32_MAX; 744480093f4SDimitry Andric for (Windows::iterator pos = m_subwindows.begin(); 745480093f4SDimitry Andric pos != m_subwindows.end(); pos = m_subwindows.erase(pos)) { 746480093f4SDimitry Andric (*pos)->Erase(); 747480093f4SDimitry Andric } 748480093f4SDimitry Andric if (m_parent) 749480093f4SDimitry Andric m_parent->Touch(); 750480093f4SDimitry Andric else 751480093f4SDimitry Andric ::touchwin(stdscr); 752480093f4SDimitry Andric } 753480093f4SDimitry Andric 754480093f4SDimitry Andric // Window drawing utilities 755480093f4SDimitry Andric void DrawTitleBox(const char *title, const char *bottom_message = nullptr) { 756480093f4SDimitry Andric attr_t attr = 0; 757480093f4SDimitry Andric if (IsActive()) 758e8d8bef9SDimitry Andric attr = A_BOLD | COLOR_PAIR(BlackOnWhite); 759480093f4SDimitry Andric else 760480093f4SDimitry Andric attr = 0; 761480093f4SDimitry Andric if (attr) 762480093f4SDimitry Andric AttributeOn(attr); 763480093f4SDimitry Andric 764480093f4SDimitry Andric Box(); 765480093f4SDimitry Andric MoveCursor(3, 0); 766480093f4SDimitry Andric 767480093f4SDimitry Andric if (title && title[0]) { 768480093f4SDimitry Andric PutChar('<'); 769480093f4SDimitry Andric PutCString(title); 770480093f4SDimitry Andric PutChar('>'); 771480093f4SDimitry Andric } 772480093f4SDimitry Andric 773480093f4SDimitry Andric if (bottom_message && bottom_message[0]) { 774480093f4SDimitry Andric int bottom_message_length = strlen(bottom_message); 775480093f4SDimitry Andric int x = GetWidth() - 3 - (bottom_message_length + 2); 776480093f4SDimitry Andric 777480093f4SDimitry Andric if (x > 0) { 778480093f4SDimitry Andric MoveCursor(x, GetHeight() - 1); 779480093f4SDimitry Andric PutChar('['); 780480093f4SDimitry Andric PutCString(bottom_message); 781480093f4SDimitry Andric PutChar(']'); 782480093f4SDimitry Andric } else { 783480093f4SDimitry Andric MoveCursor(1, GetHeight() - 1); 784480093f4SDimitry Andric PutChar('['); 785e8d8bef9SDimitry Andric PutCStringTruncated(1, bottom_message); 786480093f4SDimitry Andric } 787480093f4SDimitry Andric } 788480093f4SDimitry Andric if (attr) 789480093f4SDimitry Andric AttributeOff(attr); 790480093f4SDimitry Andric } 791480093f4SDimitry Andric 792480093f4SDimitry Andric virtual void Draw(bool force) { 793480093f4SDimitry Andric if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw(*this, force)) 794480093f4SDimitry Andric return; 795480093f4SDimitry Andric 796480093f4SDimitry Andric for (auto &subwindow_sp : m_subwindows) 797480093f4SDimitry Andric subwindow_sp->Draw(force); 798480093f4SDimitry Andric } 799480093f4SDimitry Andric 800480093f4SDimitry Andric bool CreateHelpSubwindow() { 801480093f4SDimitry Andric if (m_delegate_sp) { 802480093f4SDimitry Andric const char *text = m_delegate_sp->WindowDelegateGetHelpText(); 803480093f4SDimitry Andric KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp(); 804480093f4SDimitry Andric if ((text && text[0]) || key_help) { 805480093f4SDimitry Andric std::unique_ptr<HelpDialogDelegate> help_delegate_up( 806480093f4SDimitry Andric new HelpDialogDelegate(text, key_help)); 807480093f4SDimitry Andric const size_t num_lines = help_delegate_up->GetNumLines(); 808480093f4SDimitry Andric const size_t max_length = help_delegate_up->GetMaxLineLength(); 809480093f4SDimitry Andric Rect bounds = GetBounds(); 810480093f4SDimitry Andric bounds.Inset(1, 1); 811480093f4SDimitry Andric if (max_length + 4 < static_cast<size_t>(bounds.size.width)) { 812480093f4SDimitry Andric bounds.origin.x += (bounds.size.width - max_length + 4) / 2; 813480093f4SDimitry Andric bounds.size.width = max_length + 4; 814480093f4SDimitry Andric } else { 815480093f4SDimitry Andric if (bounds.size.width > 100) { 816480093f4SDimitry Andric const int inset_w = bounds.size.width / 4; 817480093f4SDimitry Andric bounds.origin.x += inset_w; 818480093f4SDimitry Andric bounds.size.width -= 2 * inset_w; 819480093f4SDimitry Andric } 820480093f4SDimitry Andric } 821480093f4SDimitry Andric 822480093f4SDimitry Andric if (num_lines + 2 < static_cast<size_t>(bounds.size.height)) { 823480093f4SDimitry Andric bounds.origin.y += (bounds.size.height - num_lines + 2) / 2; 824480093f4SDimitry Andric bounds.size.height = num_lines + 2; 825480093f4SDimitry Andric } else { 826480093f4SDimitry Andric if (bounds.size.height > 100) { 827480093f4SDimitry Andric const int inset_h = bounds.size.height / 4; 828480093f4SDimitry Andric bounds.origin.y += inset_h; 829480093f4SDimitry Andric bounds.size.height -= 2 * inset_h; 830480093f4SDimitry Andric } 831480093f4SDimitry Andric } 832480093f4SDimitry Andric WindowSP help_window_sp; 833480093f4SDimitry Andric Window *parent_window = GetParent(); 834480093f4SDimitry Andric if (parent_window) 835480093f4SDimitry Andric help_window_sp = parent_window->CreateSubWindow("Help", bounds, true); 836480093f4SDimitry Andric else 837480093f4SDimitry Andric help_window_sp = CreateSubWindow("Help", bounds, true); 838480093f4SDimitry Andric help_window_sp->SetDelegate( 839480093f4SDimitry Andric WindowDelegateSP(help_delegate_up.release())); 840480093f4SDimitry Andric return true; 841480093f4SDimitry Andric } 842480093f4SDimitry Andric } 843480093f4SDimitry Andric return false; 844480093f4SDimitry Andric } 845480093f4SDimitry Andric 846480093f4SDimitry Andric virtual HandleCharResult HandleChar(int key) { 847480093f4SDimitry Andric // Always check the active window first 848480093f4SDimitry Andric HandleCharResult result = eKeyNotHandled; 849480093f4SDimitry Andric WindowSP active_window_sp = GetActiveWindow(); 850480093f4SDimitry Andric if (active_window_sp) { 851480093f4SDimitry Andric result = active_window_sp->HandleChar(key); 852480093f4SDimitry Andric if (result != eKeyNotHandled) 853480093f4SDimitry Andric return result; 854480093f4SDimitry Andric } 855480093f4SDimitry Andric 856480093f4SDimitry Andric if (m_delegate_sp) { 857480093f4SDimitry Andric result = m_delegate_sp->WindowDelegateHandleChar(*this, key); 858480093f4SDimitry Andric if (result != eKeyNotHandled) 859480093f4SDimitry Andric return result; 860480093f4SDimitry Andric } 861480093f4SDimitry Andric 862480093f4SDimitry Andric // Then check for any windows that want any keys that weren't handled. This 863480093f4SDimitry Andric // is typically only for a menubar. Make a copy of the subwindows in case 864480093f4SDimitry Andric // any HandleChar() functions muck with the subwindows. If we don't do 865480093f4SDimitry Andric // this, we can crash when iterating over the subwindows. 866480093f4SDimitry Andric Windows subwindows(m_subwindows); 867480093f4SDimitry Andric for (auto subwindow_sp : subwindows) { 868480093f4SDimitry Andric if (!subwindow_sp->m_can_activate) { 869480093f4SDimitry Andric HandleCharResult result = subwindow_sp->HandleChar(key); 870480093f4SDimitry Andric if (result != eKeyNotHandled) 871480093f4SDimitry Andric return result; 872480093f4SDimitry Andric } 873480093f4SDimitry Andric } 874480093f4SDimitry Andric 875480093f4SDimitry Andric return eKeyNotHandled; 876480093f4SDimitry Andric } 877480093f4SDimitry Andric 878480093f4SDimitry Andric WindowSP GetActiveWindow() { 879480093f4SDimitry Andric if (!m_subwindows.empty()) { 880480093f4SDimitry Andric if (m_curr_active_window_idx >= m_subwindows.size()) { 881480093f4SDimitry Andric if (m_prev_active_window_idx < m_subwindows.size()) { 882480093f4SDimitry Andric m_curr_active_window_idx = m_prev_active_window_idx; 883480093f4SDimitry Andric m_prev_active_window_idx = UINT32_MAX; 884480093f4SDimitry Andric } else if (IsActive()) { 885480093f4SDimitry Andric m_prev_active_window_idx = UINT32_MAX; 886480093f4SDimitry Andric m_curr_active_window_idx = UINT32_MAX; 887480093f4SDimitry Andric 888480093f4SDimitry Andric // Find first window that wants to be active if this window is active 889480093f4SDimitry Andric const size_t num_subwindows = m_subwindows.size(); 890480093f4SDimitry Andric for (size_t i = 0; i < num_subwindows; ++i) { 891480093f4SDimitry Andric if (m_subwindows[i]->GetCanBeActive()) { 892480093f4SDimitry Andric m_curr_active_window_idx = i; 893480093f4SDimitry Andric break; 894480093f4SDimitry Andric } 895480093f4SDimitry Andric } 896480093f4SDimitry Andric } 897480093f4SDimitry Andric } 898480093f4SDimitry Andric 899480093f4SDimitry Andric if (m_curr_active_window_idx < m_subwindows.size()) 900480093f4SDimitry Andric return m_subwindows[m_curr_active_window_idx]; 901480093f4SDimitry Andric } 902480093f4SDimitry Andric return WindowSP(); 903480093f4SDimitry Andric } 904480093f4SDimitry Andric 905480093f4SDimitry Andric bool GetCanBeActive() const { return m_can_activate; } 906480093f4SDimitry Andric 907480093f4SDimitry Andric void SetCanBeActive(bool b) { m_can_activate = b; } 908480093f4SDimitry Andric 909480093f4SDimitry Andric void SetDelegate(const WindowDelegateSP &delegate_sp) { 910480093f4SDimitry Andric m_delegate_sp = delegate_sp; 911480093f4SDimitry Andric } 912480093f4SDimitry Andric 913480093f4SDimitry Andric Window *GetParent() const { return m_parent; } 914480093f4SDimitry Andric 915480093f4SDimitry Andric bool IsActive() const { 916480093f4SDimitry Andric if (m_parent) 917480093f4SDimitry Andric return m_parent->GetActiveWindow().get() == this; 918480093f4SDimitry Andric else 919480093f4SDimitry Andric return true; // Top level window is always active 920480093f4SDimitry Andric } 921480093f4SDimitry Andric 922480093f4SDimitry Andric void SelectNextWindowAsActive() { 923480093f4SDimitry Andric // Move active focus to next window 924e8d8bef9SDimitry Andric const int num_subwindows = m_subwindows.size(); 925e8d8bef9SDimitry Andric int start_idx = 0; 926e8d8bef9SDimitry Andric if (m_curr_active_window_idx != UINT32_MAX) { 927480093f4SDimitry Andric m_prev_active_window_idx = m_curr_active_window_idx; 928e8d8bef9SDimitry Andric start_idx = m_curr_active_window_idx + 1; 929e8d8bef9SDimitry Andric } 930e8d8bef9SDimitry Andric for (int idx = start_idx; idx < num_subwindows; ++idx) { 931480093f4SDimitry Andric if (m_subwindows[idx]->GetCanBeActive()) { 932480093f4SDimitry Andric m_curr_active_window_idx = idx; 933e8d8bef9SDimitry Andric return; 934480093f4SDimitry Andric } 935480093f4SDimitry Andric } 936e8d8bef9SDimitry Andric for (int idx = 0; idx < start_idx; ++idx) { 937480093f4SDimitry Andric if (m_subwindows[idx]->GetCanBeActive()) { 938480093f4SDimitry Andric m_curr_active_window_idx = idx; 939480093f4SDimitry Andric break; 940480093f4SDimitry Andric } 941480093f4SDimitry Andric } 942480093f4SDimitry Andric } 943e8d8bef9SDimitry Andric 944e8d8bef9SDimitry Andric void SelectPreviousWindowAsActive() { 945e8d8bef9SDimitry Andric // Move active focus to previous window 946e8d8bef9SDimitry Andric const int num_subwindows = m_subwindows.size(); 947e8d8bef9SDimitry Andric int start_idx = num_subwindows - 1; 948e8d8bef9SDimitry Andric if (m_curr_active_window_idx != UINT32_MAX) { 949480093f4SDimitry Andric m_prev_active_window_idx = m_curr_active_window_idx; 950e8d8bef9SDimitry Andric start_idx = m_curr_active_window_idx - 1; 951e8d8bef9SDimitry Andric } 952e8d8bef9SDimitry Andric for (int idx = start_idx; idx >= 0; --idx) { 953e8d8bef9SDimitry Andric if (m_subwindows[idx]->GetCanBeActive()) { 954e8d8bef9SDimitry Andric m_curr_active_window_idx = idx; 955e8d8bef9SDimitry Andric return; 956e8d8bef9SDimitry Andric } 957e8d8bef9SDimitry Andric } 958e8d8bef9SDimitry Andric for (int idx = num_subwindows - 1; idx > start_idx; --idx) { 959480093f4SDimitry Andric if (m_subwindows[idx]->GetCanBeActive()) { 960480093f4SDimitry Andric m_curr_active_window_idx = idx; 961480093f4SDimitry Andric break; 962480093f4SDimitry Andric } 963480093f4SDimitry Andric } 964480093f4SDimitry Andric } 965480093f4SDimitry Andric 966480093f4SDimitry Andric const char *GetName() const { return m_name.c_str(); } 967480093f4SDimitry Andric 968480093f4SDimitry Andric protected: 969480093f4SDimitry Andric std::string m_name; 970480093f4SDimitry Andric PANEL *m_panel; 971480093f4SDimitry Andric Window *m_parent; 972480093f4SDimitry Andric Windows m_subwindows; 973480093f4SDimitry Andric WindowDelegateSP m_delegate_sp; 974480093f4SDimitry Andric uint32_t m_curr_active_window_idx; 975480093f4SDimitry Andric uint32_t m_prev_active_window_idx; 976480093f4SDimitry Andric bool m_delete; 977480093f4SDimitry Andric bool m_needs_update; 978480093f4SDimitry Andric bool m_can_activate; 979480093f4SDimitry Andric bool m_is_subwin; 980480093f4SDimitry Andric 981480093f4SDimitry Andric private: 9825ffd83dbSDimitry Andric Window(const Window &) = delete; 9835ffd83dbSDimitry Andric const Window &operator=(const Window &) = delete; 984480093f4SDimitry Andric }; 985480093f4SDimitry Andric 986fe6060f1SDimitry Andric ///////// 987fe6060f1SDimitry Andric // Forms 988fe6060f1SDimitry Andric ///////// 989fe6060f1SDimitry Andric 990fe6060f1SDimitry Andric // A scroll context defines a vertical region that needs to be visible in a 991fe6060f1SDimitry Andric // scrolling area. The region is defined by the index of the start and end lines 992fe6060f1SDimitry Andric // of the region. The start and end lines may be equal, in which case, the 993fe6060f1SDimitry Andric // region is a single line. 994fe6060f1SDimitry Andric struct ScrollContext { 995fe6060f1SDimitry Andric int start; 996fe6060f1SDimitry Andric int end; 997fe6060f1SDimitry Andric 998fe6060f1SDimitry Andric ScrollContext(int line) : start(line), end(line) {} 999fe6060f1SDimitry Andric ScrollContext(int _start, int _end) : start(_start), end(_end) {} 1000fe6060f1SDimitry Andric 1001fe6060f1SDimitry Andric void Offset(int offset) { 1002fe6060f1SDimitry Andric start += offset; 1003fe6060f1SDimitry Andric end += offset; 1004fe6060f1SDimitry Andric } 1005fe6060f1SDimitry Andric }; 1006fe6060f1SDimitry Andric 1007fe6060f1SDimitry Andric class FieldDelegate { 1008fe6060f1SDimitry Andric public: 1009fe6060f1SDimitry Andric virtual ~FieldDelegate() = default; 1010fe6060f1SDimitry Andric 1011fe6060f1SDimitry Andric // Returns the number of lines needed to draw the field. The draw method will 1012fe6060f1SDimitry Andric // be given a surface that have exactly this number of lines. 1013fe6060f1SDimitry Andric virtual int FieldDelegateGetHeight() = 0; 1014fe6060f1SDimitry Andric 1015fe6060f1SDimitry Andric // Returns the scroll context in the local coordinates of the field. By 1016fe6060f1SDimitry Andric // default, the scroll context spans the whole field. Bigger fields with 1017fe6060f1SDimitry Andric // internal navigation should override this method to provide a finer context. 1018fe6060f1SDimitry Andric // Typical override methods would first get the scroll context of the internal 1019fe6060f1SDimitry Andric // element then add the offset of the element in the field. 1020fe6060f1SDimitry Andric virtual ScrollContext FieldDelegateGetScrollContext() { 1021fe6060f1SDimitry Andric return ScrollContext(0, FieldDelegateGetHeight() - 1); 1022fe6060f1SDimitry Andric } 1023fe6060f1SDimitry Andric 1024fe6060f1SDimitry Andric // Draw the field in the given subpad surface. The surface have a height that 1025fe6060f1SDimitry Andric // is equal to the height returned by FieldDelegateGetHeight(). If the field 1026fe6060f1SDimitry Andric // is selected in the form window, then is_selected will be true. 1027349cc55cSDimitry Andric virtual void FieldDelegateDraw(Surface &surface, bool is_selected) = 0; 1028fe6060f1SDimitry Andric 1029fe6060f1SDimitry Andric // Handle the key that wasn't handled by the form window or a container field. 1030fe6060f1SDimitry Andric virtual HandleCharResult FieldDelegateHandleChar(int key) { 1031fe6060f1SDimitry Andric return eKeyNotHandled; 1032fe6060f1SDimitry Andric } 1033fe6060f1SDimitry Andric 1034fe6060f1SDimitry Andric // This is executed once the user exists the field, that is, once the user 1035fe6060f1SDimitry Andric // navigates to the next or the previous field. This is particularly useful to 1036fe6060f1SDimitry Andric // do in-field validation and error setting. Fields with internal navigation 1037fe6060f1SDimitry Andric // should call this method on their fields. 10380eae32dcSDimitry Andric virtual void FieldDelegateExitCallback() {} 1039fe6060f1SDimitry Andric 1040fe6060f1SDimitry Andric // Fields may have internal navigation, for instance, a List Field have 1041fe6060f1SDimitry Andric // multiple internal elements, which needs to be navigated. To allow for this 1042fe6060f1SDimitry Andric // mechanism, the window shouldn't handle the navigation keys all the time, 1043fe6060f1SDimitry Andric // and instead call the key handing method of the selected field. It should 1044fe6060f1SDimitry Andric // only handle the navigation keys when the field contains a single element or 1045fe6060f1SDimitry Andric // have the last or first element selected depending on if the user is 1046fe6060f1SDimitry Andric // navigating forward or backward. Additionally, once a field is selected in 1047fe6060f1SDimitry Andric // the forward or backward direction, its first or last internal element 1048fe6060f1SDimitry Andric // should be selected. The following methods implements those mechanisms. 1049fe6060f1SDimitry Andric 1050fe6060f1SDimitry Andric // Returns true if the first element in the field is selected or if the field 1051fe6060f1SDimitry Andric // contains a single element. 1052fe6060f1SDimitry Andric virtual bool FieldDelegateOnFirstOrOnlyElement() { return true; } 1053fe6060f1SDimitry Andric 1054fe6060f1SDimitry Andric // Returns true if the last element in the field is selected or if the field 1055fe6060f1SDimitry Andric // contains a single element. 1056fe6060f1SDimitry Andric virtual bool FieldDelegateOnLastOrOnlyElement() { return true; } 1057fe6060f1SDimitry Andric 1058fe6060f1SDimitry Andric // Select the first element in the field if multiple elements exists. 10590eae32dcSDimitry Andric virtual void FieldDelegateSelectFirstElement() {} 1060fe6060f1SDimitry Andric 1061fe6060f1SDimitry Andric // Select the last element in the field if multiple elements exists. 10620eae32dcSDimitry Andric virtual void FieldDelegateSelectLastElement() {} 1063fe6060f1SDimitry Andric 1064fe6060f1SDimitry Andric // Returns true if the field has an error, false otherwise. 1065fe6060f1SDimitry Andric virtual bool FieldDelegateHasError() { return false; } 1066fe6060f1SDimitry Andric 1067fe6060f1SDimitry Andric bool FieldDelegateIsVisible() { return m_is_visible; } 1068fe6060f1SDimitry Andric 1069fe6060f1SDimitry Andric void FieldDelegateHide() { m_is_visible = false; } 1070fe6060f1SDimitry Andric 1071fe6060f1SDimitry Andric void FieldDelegateShow() { m_is_visible = true; } 1072fe6060f1SDimitry Andric 1073fe6060f1SDimitry Andric protected: 1074fe6060f1SDimitry Andric bool m_is_visible = true; 1075fe6060f1SDimitry Andric }; 1076fe6060f1SDimitry Andric 1077fe6060f1SDimitry Andric typedef std::unique_ptr<FieldDelegate> FieldDelegateUP; 1078fe6060f1SDimitry Andric 1079fe6060f1SDimitry Andric class TextFieldDelegate : public FieldDelegate { 1080fe6060f1SDimitry Andric public: 1081fe6060f1SDimitry Andric TextFieldDelegate(const char *label, const char *content, bool required) 108281ad6265SDimitry Andric : m_label(label), m_required(required) { 1083fe6060f1SDimitry Andric if (content) 1084fe6060f1SDimitry Andric m_content = content; 1085fe6060f1SDimitry Andric } 1086fe6060f1SDimitry Andric 1087fe6060f1SDimitry Andric // Text fields are drawn as titled boxes of a single line, with a possible 1088fe6060f1SDimitry Andric // error messages at the end. 1089fe6060f1SDimitry Andric // 1090fe6060f1SDimitry Andric // __[Label]___________ 1091fe6060f1SDimitry Andric // | | 1092fe6060f1SDimitry Andric // |__________________| 1093fe6060f1SDimitry Andric // - Error message if it exists. 1094fe6060f1SDimitry Andric 1095fe6060f1SDimitry Andric // The text field has a height of 3 lines. 2 lines for borders and 1 line for 1096fe6060f1SDimitry Andric // the content. 1097fe6060f1SDimitry Andric int GetFieldHeight() { return 3; } 1098fe6060f1SDimitry Andric 1099fe6060f1SDimitry Andric // The text field has a full height of 3 or 4 lines. 3 lines for the actual 1100fe6060f1SDimitry Andric // field and an optional line for an error if it exists. 1101fe6060f1SDimitry Andric int FieldDelegateGetHeight() override { 1102fe6060f1SDimitry Andric int height = GetFieldHeight(); 1103fe6060f1SDimitry Andric if (FieldDelegateHasError()) 1104fe6060f1SDimitry Andric height++; 1105fe6060f1SDimitry Andric return height; 1106fe6060f1SDimitry Andric } 1107fe6060f1SDimitry Andric 1108fe6060f1SDimitry Andric // Get the cursor X position in the surface coordinate. 1109fe6060f1SDimitry Andric int GetCursorXPosition() { return m_cursor_position - m_first_visibile_char; } 1110fe6060f1SDimitry Andric 1111fe6060f1SDimitry Andric int GetContentLength() { return m_content.length(); } 1112fe6060f1SDimitry Andric 1113349cc55cSDimitry Andric void DrawContent(Surface &surface, bool is_selected) { 1114349cc55cSDimitry Andric UpdateScrolling(surface.GetWidth()); 1115349cc55cSDimitry Andric 1116fe6060f1SDimitry Andric surface.MoveCursor(0, 0); 1117fe6060f1SDimitry Andric const char *text = m_content.c_str() + m_first_visibile_char; 1118fe6060f1SDimitry Andric surface.PutCString(text, surface.GetWidth()); 1119fe6060f1SDimitry Andric 1120fe6060f1SDimitry Andric // Highlight the cursor. 1121fe6060f1SDimitry Andric surface.MoveCursor(GetCursorXPosition(), 0); 1122fe6060f1SDimitry Andric if (is_selected) 1123fe6060f1SDimitry Andric surface.AttributeOn(A_REVERSE); 1124fe6060f1SDimitry Andric if (m_cursor_position == GetContentLength()) 1125fe6060f1SDimitry Andric // Cursor is past the last character. Highlight an empty space. 1126fe6060f1SDimitry Andric surface.PutChar(' '); 1127fe6060f1SDimitry Andric else 1128fe6060f1SDimitry Andric surface.PutChar(m_content[m_cursor_position]); 1129fe6060f1SDimitry Andric if (is_selected) 1130fe6060f1SDimitry Andric surface.AttributeOff(A_REVERSE); 1131fe6060f1SDimitry Andric } 1132fe6060f1SDimitry Andric 1133349cc55cSDimitry Andric void DrawField(Surface &surface, bool is_selected) { 1134fe6060f1SDimitry Andric surface.TitledBox(m_label.c_str()); 1135fe6060f1SDimitry Andric 1136fe6060f1SDimitry Andric Rect content_bounds = surface.GetFrame(); 1137fe6060f1SDimitry Andric content_bounds.Inset(1, 1); 1138349cc55cSDimitry Andric Surface content_surface = surface.SubSurface(content_bounds); 1139fe6060f1SDimitry Andric 1140fe6060f1SDimitry Andric DrawContent(content_surface, is_selected); 1141fe6060f1SDimitry Andric } 1142fe6060f1SDimitry Andric 1143349cc55cSDimitry Andric void DrawError(Surface &surface) { 1144fe6060f1SDimitry Andric if (!FieldDelegateHasError()) 1145fe6060f1SDimitry Andric return; 1146fe6060f1SDimitry Andric surface.MoveCursor(0, 0); 1147fe6060f1SDimitry Andric surface.AttributeOn(COLOR_PAIR(RedOnBlack)); 1148fe6060f1SDimitry Andric surface.PutChar(ACS_DIAMOND); 1149fe6060f1SDimitry Andric surface.PutChar(' '); 1150fe6060f1SDimitry Andric surface.PutCStringTruncated(1, GetError().c_str()); 1151fe6060f1SDimitry Andric surface.AttributeOff(COLOR_PAIR(RedOnBlack)); 1152fe6060f1SDimitry Andric } 1153fe6060f1SDimitry Andric 1154349cc55cSDimitry Andric void FieldDelegateDraw(Surface &surface, bool is_selected) override { 1155fe6060f1SDimitry Andric Rect frame = surface.GetFrame(); 1156fe6060f1SDimitry Andric Rect field_bounds, error_bounds; 1157fe6060f1SDimitry Andric frame.HorizontalSplit(GetFieldHeight(), field_bounds, error_bounds); 1158349cc55cSDimitry Andric Surface field_surface = surface.SubSurface(field_bounds); 1159349cc55cSDimitry Andric Surface error_surface = surface.SubSurface(error_bounds); 1160fe6060f1SDimitry Andric 1161fe6060f1SDimitry Andric DrawField(field_surface, is_selected); 1162fe6060f1SDimitry Andric DrawError(error_surface); 1163fe6060f1SDimitry Andric } 1164fe6060f1SDimitry Andric 1165349cc55cSDimitry Andric // Get the position of the last visible character. 1166349cc55cSDimitry Andric int GetLastVisibleCharPosition(int width) { 1167349cc55cSDimitry Andric int position = m_first_visibile_char + width - 1; 1168349cc55cSDimitry Andric return std::min(position, GetContentLength()); 1169349cc55cSDimitry Andric } 1170349cc55cSDimitry Andric 1171349cc55cSDimitry Andric void UpdateScrolling(int width) { 1172349cc55cSDimitry Andric if (m_cursor_position < m_first_visibile_char) { 1173349cc55cSDimitry Andric m_first_visibile_char = m_cursor_position; 1174349cc55cSDimitry Andric return; 1175349cc55cSDimitry Andric } 1176349cc55cSDimitry Andric 1177349cc55cSDimitry Andric if (m_cursor_position > GetLastVisibleCharPosition(width)) 1178349cc55cSDimitry Andric m_first_visibile_char = m_cursor_position - (width - 1); 1179349cc55cSDimitry Andric } 1180349cc55cSDimitry Andric 1181fe6060f1SDimitry Andric // The cursor is allowed to move one character past the string. 1182fe6060f1SDimitry Andric // m_cursor_position is in range [0, GetContentLength()]. 1183fe6060f1SDimitry Andric void MoveCursorRight() { 1184fe6060f1SDimitry Andric if (m_cursor_position < GetContentLength()) 1185fe6060f1SDimitry Andric m_cursor_position++; 1186fe6060f1SDimitry Andric } 1187fe6060f1SDimitry Andric 1188fe6060f1SDimitry Andric void MoveCursorLeft() { 1189fe6060f1SDimitry Andric if (m_cursor_position > 0) 1190fe6060f1SDimitry Andric m_cursor_position--; 1191fe6060f1SDimitry Andric } 1192fe6060f1SDimitry Andric 1193349cc55cSDimitry Andric void MoveCursorToStart() { m_cursor_position = 0; } 1194349cc55cSDimitry Andric 1195349cc55cSDimitry Andric void MoveCursorToEnd() { m_cursor_position = GetContentLength(); } 1196fe6060f1SDimitry Andric 1197fe6060f1SDimitry Andric void ScrollLeft() { 1198fe6060f1SDimitry Andric if (m_first_visibile_char > 0) 1199fe6060f1SDimitry Andric m_first_visibile_char--; 1200fe6060f1SDimitry Andric } 1201fe6060f1SDimitry Andric 1202349cc55cSDimitry Andric // Insert a character at the current cursor position and advance the cursor 1203349cc55cSDimitry Andric // position. 1204fe6060f1SDimitry Andric void InsertChar(char character) { 1205fe6060f1SDimitry Andric m_content.insert(m_cursor_position, 1, character); 1206fe6060f1SDimitry Andric m_cursor_position++; 1207349cc55cSDimitry Andric ClearError(); 1208fe6060f1SDimitry Andric } 1209fe6060f1SDimitry Andric 1210fe6060f1SDimitry Andric // Remove the character before the cursor position, retreat the cursor 1211349cc55cSDimitry Andric // position, and scroll left. 1212349cc55cSDimitry Andric void RemovePreviousChar() { 1213fe6060f1SDimitry Andric if (m_cursor_position == 0) 1214fe6060f1SDimitry Andric return; 1215fe6060f1SDimitry Andric 1216fe6060f1SDimitry Andric m_content.erase(m_cursor_position - 1, 1); 1217fe6060f1SDimitry Andric m_cursor_position--; 1218fe6060f1SDimitry Andric ScrollLeft(); 1219349cc55cSDimitry Andric ClearError(); 1220349cc55cSDimitry Andric } 1221349cc55cSDimitry Andric 1222349cc55cSDimitry Andric // Remove the character after the cursor position. 1223349cc55cSDimitry Andric void RemoveNextChar() { 1224349cc55cSDimitry Andric if (m_cursor_position == GetContentLength()) 1225349cc55cSDimitry Andric return; 1226349cc55cSDimitry Andric 1227349cc55cSDimitry Andric m_content.erase(m_cursor_position, 1); 1228349cc55cSDimitry Andric ClearError(); 1229349cc55cSDimitry Andric } 1230349cc55cSDimitry Andric 1231349cc55cSDimitry Andric // Clear characters from the current cursor position to the end. 1232349cc55cSDimitry Andric void ClearToEnd() { 1233349cc55cSDimitry Andric m_content.erase(m_cursor_position); 1234349cc55cSDimitry Andric ClearError(); 1235349cc55cSDimitry Andric } 1236349cc55cSDimitry Andric 1237349cc55cSDimitry Andric void Clear() { 1238349cc55cSDimitry Andric m_content.clear(); 1239349cc55cSDimitry Andric m_cursor_position = 0; 1240349cc55cSDimitry Andric ClearError(); 1241fe6060f1SDimitry Andric } 1242fe6060f1SDimitry Andric 1243fe6060f1SDimitry Andric // True if the key represents a char that can be inserted in the field 1244fe6060f1SDimitry Andric // content, false otherwise. 1245349cc55cSDimitry Andric virtual bool IsAcceptableChar(int key) { 1246349cc55cSDimitry Andric // The behavior of isprint is undefined when the value is not representable 1247349cc55cSDimitry Andric // as an unsigned char. So explicitly check for non-ascii key codes. 1248349cc55cSDimitry Andric if (key > 127) 1249349cc55cSDimitry Andric return false; 1250349cc55cSDimitry Andric return isprint(key); 1251349cc55cSDimitry Andric } 1252fe6060f1SDimitry Andric 1253fe6060f1SDimitry Andric HandleCharResult FieldDelegateHandleChar(int key) override { 1254fe6060f1SDimitry Andric if (IsAcceptableChar(key)) { 1255fe6060f1SDimitry Andric ClearError(); 1256fe6060f1SDimitry Andric InsertChar((char)key); 1257fe6060f1SDimitry Andric return eKeyHandled; 1258fe6060f1SDimitry Andric } 1259fe6060f1SDimitry Andric 1260fe6060f1SDimitry Andric switch (key) { 1261349cc55cSDimitry Andric case KEY_HOME: 1262349cc55cSDimitry Andric case KEY_CTRL_A: 1263349cc55cSDimitry Andric MoveCursorToStart(); 1264349cc55cSDimitry Andric return eKeyHandled; 1265349cc55cSDimitry Andric case KEY_END: 1266349cc55cSDimitry Andric case KEY_CTRL_E: 1267349cc55cSDimitry Andric MoveCursorToEnd(); 1268349cc55cSDimitry Andric return eKeyHandled; 1269fe6060f1SDimitry Andric case KEY_RIGHT: 1270349cc55cSDimitry Andric case KEY_SF: 1271fe6060f1SDimitry Andric MoveCursorRight(); 1272fe6060f1SDimitry Andric return eKeyHandled; 1273fe6060f1SDimitry Andric case KEY_LEFT: 1274349cc55cSDimitry Andric case KEY_SR: 1275fe6060f1SDimitry Andric MoveCursorLeft(); 1276fe6060f1SDimitry Andric return eKeyHandled; 1277fe6060f1SDimitry Andric case KEY_BACKSPACE: 1278349cc55cSDimitry Andric case KEY_DELETE: 1279349cc55cSDimitry Andric RemovePreviousChar(); 1280349cc55cSDimitry Andric return eKeyHandled; 1281349cc55cSDimitry Andric case KEY_DC: 1282349cc55cSDimitry Andric RemoveNextChar(); 1283349cc55cSDimitry Andric return eKeyHandled; 1284349cc55cSDimitry Andric case KEY_EOL: 1285349cc55cSDimitry Andric case KEY_CTRL_K: 1286349cc55cSDimitry Andric ClearToEnd(); 1287349cc55cSDimitry Andric return eKeyHandled; 1288349cc55cSDimitry Andric case KEY_DL: 1289349cc55cSDimitry Andric case KEY_CLEAR: 1290349cc55cSDimitry Andric Clear(); 1291fe6060f1SDimitry Andric return eKeyHandled; 1292fe6060f1SDimitry Andric default: 1293fe6060f1SDimitry Andric break; 1294fe6060f1SDimitry Andric } 1295fe6060f1SDimitry Andric return eKeyNotHandled; 1296fe6060f1SDimitry Andric } 1297fe6060f1SDimitry Andric 1298fe6060f1SDimitry Andric bool FieldDelegateHasError() override { return !m_error.empty(); } 1299fe6060f1SDimitry Andric 1300fe6060f1SDimitry Andric void FieldDelegateExitCallback() override { 1301fe6060f1SDimitry Andric if (!IsSpecified() && m_required) 1302fe6060f1SDimitry Andric SetError("This field is required!"); 1303fe6060f1SDimitry Andric } 1304fe6060f1SDimitry Andric 1305fe6060f1SDimitry Andric bool IsSpecified() { return !m_content.empty(); } 1306fe6060f1SDimitry Andric 1307fe6060f1SDimitry Andric void ClearError() { m_error.clear(); } 1308fe6060f1SDimitry Andric 1309fe6060f1SDimitry Andric const std::string &GetError() { return m_error; } 1310fe6060f1SDimitry Andric 1311fe6060f1SDimitry Andric void SetError(const char *error) { m_error = error; } 1312fe6060f1SDimitry Andric 1313fe6060f1SDimitry Andric const std::string &GetText() { return m_content; } 1314fe6060f1SDimitry Andric 1315349cc55cSDimitry Andric void SetText(const char *text) { 1316349cc55cSDimitry Andric if (text == nullptr) { 1317349cc55cSDimitry Andric m_content.clear(); 1318349cc55cSDimitry Andric return; 1319349cc55cSDimitry Andric } 1320349cc55cSDimitry Andric m_content = text; 1321349cc55cSDimitry Andric } 1322349cc55cSDimitry Andric 1323fe6060f1SDimitry Andric protected: 1324fe6060f1SDimitry Andric std::string m_label; 1325fe6060f1SDimitry Andric bool m_required; 1326fe6060f1SDimitry Andric // The position of the top left corner character of the border. 1327fe6060f1SDimitry Andric std::string m_content; 1328fe6060f1SDimitry Andric // The cursor position in the content string itself. Can be in the range 1329fe6060f1SDimitry Andric // [0, GetContentLength()]. 133081ad6265SDimitry Andric int m_cursor_position = 0; 1331fe6060f1SDimitry Andric // The index of the first visible character in the content. 133281ad6265SDimitry Andric int m_first_visibile_char = 0; 1333fe6060f1SDimitry Andric // Optional error message. If empty, field is considered to have no error. 1334fe6060f1SDimitry Andric std::string m_error; 1335fe6060f1SDimitry Andric }; 1336fe6060f1SDimitry Andric 1337fe6060f1SDimitry Andric class IntegerFieldDelegate : public TextFieldDelegate { 1338fe6060f1SDimitry Andric public: 1339fe6060f1SDimitry Andric IntegerFieldDelegate(const char *label, int content, bool required) 1340fe6060f1SDimitry Andric : TextFieldDelegate(label, std::to_string(content).c_str(), required) {} 1341fe6060f1SDimitry Andric 1342fe6060f1SDimitry Andric // Only accept digits. 1343fe6060f1SDimitry Andric bool IsAcceptableChar(int key) override { return isdigit(key); } 1344fe6060f1SDimitry Andric 1345fe6060f1SDimitry Andric // Returns the integer content of the field. 1346fe6060f1SDimitry Andric int GetInteger() { return std::stoi(m_content); } 1347fe6060f1SDimitry Andric }; 1348fe6060f1SDimitry Andric 1349fe6060f1SDimitry Andric class FileFieldDelegate : public TextFieldDelegate { 1350fe6060f1SDimitry Andric public: 1351fe6060f1SDimitry Andric FileFieldDelegate(const char *label, const char *content, bool need_to_exist, 1352fe6060f1SDimitry Andric bool required) 1353fe6060f1SDimitry Andric : TextFieldDelegate(label, content, required), 1354fe6060f1SDimitry Andric m_need_to_exist(need_to_exist) {} 1355fe6060f1SDimitry Andric 1356fe6060f1SDimitry Andric void FieldDelegateExitCallback() override { 1357fe6060f1SDimitry Andric TextFieldDelegate::FieldDelegateExitCallback(); 1358fe6060f1SDimitry Andric if (!IsSpecified()) 1359fe6060f1SDimitry Andric return; 1360fe6060f1SDimitry Andric 1361fe6060f1SDimitry Andric if (!m_need_to_exist) 1362fe6060f1SDimitry Andric return; 1363fe6060f1SDimitry Andric 1364fe6060f1SDimitry Andric FileSpec file = GetResolvedFileSpec(); 1365fe6060f1SDimitry Andric if (!FileSystem::Instance().Exists(file)) { 1366fe6060f1SDimitry Andric SetError("File doesn't exist!"); 1367fe6060f1SDimitry Andric return; 1368fe6060f1SDimitry Andric } 1369fe6060f1SDimitry Andric if (FileSystem::Instance().IsDirectory(file)) { 1370fe6060f1SDimitry Andric SetError("Not a file!"); 1371fe6060f1SDimitry Andric return; 1372fe6060f1SDimitry Andric } 1373fe6060f1SDimitry Andric } 1374fe6060f1SDimitry Andric 1375fe6060f1SDimitry Andric FileSpec GetFileSpec() { 1376fe6060f1SDimitry Andric FileSpec file_spec(GetPath()); 1377fe6060f1SDimitry Andric return file_spec; 1378fe6060f1SDimitry Andric } 1379fe6060f1SDimitry Andric 1380fe6060f1SDimitry Andric FileSpec GetResolvedFileSpec() { 1381fe6060f1SDimitry Andric FileSpec file_spec(GetPath()); 1382fe6060f1SDimitry Andric FileSystem::Instance().Resolve(file_spec); 1383fe6060f1SDimitry Andric return file_spec; 1384fe6060f1SDimitry Andric } 1385fe6060f1SDimitry Andric 1386fe6060f1SDimitry Andric const std::string &GetPath() { return m_content; } 1387fe6060f1SDimitry Andric 1388fe6060f1SDimitry Andric protected: 1389fe6060f1SDimitry Andric bool m_need_to_exist; 1390fe6060f1SDimitry Andric }; 1391fe6060f1SDimitry Andric 1392fe6060f1SDimitry Andric class DirectoryFieldDelegate : public TextFieldDelegate { 1393fe6060f1SDimitry Andric public: 1394fe6060f1SDimitry Andric DirectoryFieldDelegate(const char *label, const char *content, 1395fe6060f1SDimitry Andric bool need_to_exist, bool required) 1396fe6060f1SDimitry Andric : TextFieldDelegate(label, content, required), 1397fe6060f1SDimitry Andric m_need_to_exist(need_to_exist) {} 1398fe6060f1SDimitry Andric 1399fe6060f1SDimitry Andric void FieldDelegateExitCallback() override { 1400fe6060f1SDimitry Andric TextFieldDelegate::FieldDelegateExitCallback(); 1401fe6060f1SDimitry Andric if (!IsSpecified()) 1402fe6060f1SDimitry Andric return; 1403fe6060f1SDimitry Andric 1404fe6060f1SDimitry Andric if (!m_need_to_exist) 1405fe6060f1SDimitry Andric return; 1406fe6060f1SDimitry Andric 1407fe6060f1SDimitry Andric FileSpec file = GetResolvedFileSpec(); 1408fe6060f1SDimitry Andric if (!FileSystem::Instance().Exists(file)) { 1409fe6060f1SDimitry Andric SetError("Directory doesn't exist!"); 1410fe6060f1SDimitry Andric return; 1411fe6060f1SDimitry Andric } 1412fe6060f1SDimitry Andric if (!FileSystem::Instance().IsDirectory(file)) { 1413fe6060f1SDimitry Andric SetError("Not a directory!"); 1414fe6060f1SDimitry Andric return; 1415fe6060f1SDimitry Andric } 1416fe6060f1SDimitry Andric } 1417fe6060f1SDimitry Andric 1418fe6060f1SDimitry Andric FileSpec GetFileSpec() { 1419fe6060f1SDimitry Andric FileSpec file_spec(GetPath()); 1420fe6060f1SDimitry Andric return file_spec; 1421fe6060f1SDimitry Andric } 1422fe6060f1SDimitry Andric 1423fe6060f1SDimitry Andric FileSpec GetResolvedFileSpec() { 1424fe6060f1SDimitry Andric FileSpec file_spec(GetPath()); 1425fe6060f1SDimitry Andric FileSystem::Instance().Resolve(file_spec); 1426fe6060f1SDimitry Andric return file_spec; 1427fe6060f1SDimitry Andric } 1428fe6060f1SDimitry Andric 1429fe6060f1SDimitry Andric const std::string &GetPath() { return m_content; } 1430fe6060f1SDimitry Andric 1431fe6060f1SDimitry Andric protected: 1432fe6060f1SDimitry Andric bool m_need_to_exist; 1433fe6060f1SDimitry Andric }; 1434fe6060f1SDimitry Andric 1435fe6060f1SDimitry Andric class ArchFieldDelegate : public TextFieldDelegate { 1436fe6060f1SDimitry Andric public: 1437fe6060f1SDimitry Andric ArchFieldDelegate(const char *label, const char *content, bool required) 1438fe6060f1SDimitry Andric : TextFieldDelegate(label, content, required) {} 1439fe6060f1SDimitry Andric 1440fe6060f1SDimitry Andric void FieldDelegateExitCallback() override { 1441fe6060f1SDimitry Andric TextFieldDelegate::FieldDelegateExitCallback(); 1442fe6060f1SDimitry Andric if (!IsSpecified()) 1443fe6060f1SDimitry Andric return; 1444fe6060f1SDimitry Andric 1445fe6060f1SDimitry Andric if (!GetArchSpec().IsValid()) 1446fe6060f1SDimitry Andric SetError("Not a valid arch!"); 1447fe6060f1SDimitry Andric } 1448fe6060f1SDimitry Andric 1449fe6060f1SDimitry Andric const std::string &GetArchString() { return m_content; } 1450fe6060f1SDimitry Andric 1451fe6060f1SDimitry Andric ArchSpec GetArchSpec() { return ArchSpec(GetArchString()); } 1452fe6060f1SDimitry Andric }; 1453fe6060f1SDimitry Andric 1454fe6060f1SDimitry Andric class BooleanFieldDelegate : public FieldDelegate { 1455fe6060f1SDimitry Andric public: 1456fe6060f1SDimitry Andric BooleanFieldDelegate(const char *label, bool content) 1457fe6060f1SDimitry Andric : m_label(label), m_content(content) {} 1458fe6060f1SDimitry Andric 1459fe6060f1SDimitry Andric // Boolean fields are drawn as checkboxes. 1460fe6060f1SDimitry Andric // 1461fe6060f1SDimitry Andric // [X] Label or [ ] Label 1462fe6060f1SDimitry Andric 1463fe6060f1SDimitry Andric // Boolean fields are have a single line. 1464fe6060f1SDimitry Andric int FieldDelegateGetHeight() override { return 1; } 1465fe6060f1SDimitry Andric 1466349cc55cSDimitry Andric void FieldDelegateDraw(Surface &surface, bool is_selected) override { 1467fe6060f1SDimitry Andric surface.MoveCursor(0, 0); 1468fe6060f1SDimitry Andric surface.PutChar('['); 1469fe6060f1SDimitry Andric if (is_selected) 1470fe6060f1SDimitry Andric surface.AttributeOn(A_REVERSE); 1471fe6060f1SDimitry Andric surface.PutChar(m_content ? ACS_DIAMOND : ' '); 1472fe6060f1SDimitry Andric if (is_selected) 1473fe6060f1SDimitry Andric surface.AttributeOff(A_REVERSE); 1474fe6060f1SDimitry Andric surface.PutChar(']'); 1475fe6060f1SDimitry Andric surface.PutChar(' '); 1476fe6060f1SDimitry Andric surface.PutCString(m_label.c_str()); 1477fe6060f1SDimitry Andric } 1478fe6060f1SDimitry Andric 1479fe6060f1SDimitry Andric void ToggleContent() { m_content = !m_content; } 1480fe6060f1SDimitry Andric 1481fe6060f1SDimitry Andric void SetContentToTrue() { m_content = true; } 1482fe6060f1SDimitry Andric 1483fe6060f1SDimitry Andric void SetContentToFalse() { m_content = false; } 1484fe6060f1SDimitry Andric 1485fe6060f1SDimitry Andric HandleCharResult FieldDelegateHandleChar(int key) override { 1486fe6060f1SDimitry Andric switch (key) { 1487fe6060f1SDimitry Andric case 't': 1488fe6060f1SDimitry Andric case '1': 1489fe6060f1SDimitry Andric SetContentToTrue(); 1490fe6060f1SDimitry Andric return eKeyHandled; 1491fe6060f1SDimitry Andric case 'f': 1492fe6060f1SDimitry Andric case '0': 1493fe6060f1SDimitry Andric SetContentToFalse(); 1494fe6060f1SDimitry Andric return eKeyHandled; 1495fe6060f1SDimitry Andric case ' ': 1496fe6060f1SDimitry Andric case '\r': 1497fe6060f1SDimitry Andric case '\n': 1498fe6060f1SDimitry Andric case KEY_ENTER: 1499fe6060f1SDimitry Andric ToggleContent(); 1500fe6060f1SDimitry Andric return eKeyHandled; 1501fe6060f1SDimitry Andric default: 1502fe6060f1SDimitry Andric break; 1503fe6060f1SDimitry Andric } 1504fe6060f1SDimitry Andric return eKeyNotHandled; 1505fe6060f1SDimitry Andric } 1506fe6060f1SDimitry Andric 1507fe6060f1SDimitry Andric // Returns the boolean content of the field. 1508fe6060f1SDimitry Andric bool GetBoolean() { return m_content; } 1509fe6060f1SDimitry Andric 1510fe6060f1SDimitry Andric protected: 1511fe6060f1SDimitry Andric std::string m_label; 1512fe6060f1SDimitry Andric bool m_content; 1513fe6060f1SDimitry Andric }; 1514fe6060f1SDimitry Andric 1515fe6060f1SDimitry Andric class ChoicesFieldDelegate : public FieldDelegate { 1516fe6060f1SDimitry Andric public: 1517fe6060f1SDimitry Andric ChoicesFieldDelegate(const char *label, int number_of_visible_choices, 1518fe6060f1SDimitry Andric std::vector<std::string> choices) 1519fe6060f1SDimitry Andric : m_label(label), m_number_of_visible_choices(number_of_visible_choices), 152081ad6265SDimitry Andric m_choices(choices) {} 1521fe6060f1SDimitry Andric 1522fe6060f1SDimitry Andric // Choices fields are drawn as titles boxses of a number of visible choices. 1523fe6060f1SDimitry Andric // The rest of the choices become visible as the user scroll. The selected 1524fe6060f1SDimitry Andric // choice is denoted by a diamond as the first character. 1525fe6060f1SDimitry Andric // 1526fe6060f1SDimitry Andric // __[Label]___________ 1527fe6060f1SDimitry Andric // |-Choice 1 | 1528fe6060f1SDimitry Andric // | Choice 2 | 1529fe6060f1SDimitry Andric // | Choice 3 | 1530fe6060f1SDimitry Andric // |__________________| 1531fe6060f1SDimitry Andric 1532fe6060f1SDimitry Andric // Choices field have two border characters plus the number of visible 1533fe6060f1SDimitry Andric // choices. 1534fe6060f1SDimitry Andric int FieldDelegateGetHeight() override { 1535fe6060f1SDimitry Andric return m_number_of_visible_choices + 2; 1536fe6060f1SDimitry Andric } 1537fe6060f1SDimitry Andric 1538fe6060f1SDimitry Andric int GetNumberOfChoices() { return m_choices.size(); } 1539fe6060f1SDimitry Andric 1540fe6060f1SDimitry Andric // Get the index of the last visible choice. 1541fe6060f1SDimitry Andric int GetLastVisibleChoice() { 1542fe6060f1SDimitry Andric int index = m_first_visibile_choice + m_number_of_visible_choices; 1543fe6060f1SDimitry Andric return std::min(index, GetNumberOfChoices()) - 1; 1544fe6060f1SDimitry Andric } 1545fe6060f1SDimitry Andric 1546349cc55cSDimitry Andric void DrawContent(Surface &surface, bool is_selected) { 1547fe6060f1SDimitry Andric int choices_to_draw = GetLastVisibleChoice() - m_first_visibile_choice + 1; 1548fe6060f1SDimitry Andric for (int i = 0; i < choices_to_draw; i++) { 1549fe6060f1SDimitry Andric surface.MoveCursor(0, i); 1550fe6060f1SDimitry Andric int current_choice = m_first_visibile_choice + i; 1551fe6060f1SDimitry Andric const char *text = m_choices[current_choice].c_str(); 1552fe6060f1SDimitry Andric bool highlight = is_selected && current_choice == m_choice; 1553fe6060f1SDimitry Andric if (highlight) 1554fe6060f1SDimitry Andric surface.AttributeOn(A_REVERSE); 1555fe6060f1SDimitry Andric surface.PutChar(current_choice == m_choice ? ACS_DIAMOND : ' '); 1556fe6060f1SDimitry Andric surface.PutCString(text); 1557fe6060f1SDimitry Andric if (highlight) 1558fe6060f1SDimitry Andric surface.AttributeOff(A_REVERSE); 1559fe6060f1SDimitry Andric } 1560fe6060f1SDimitry Andric } 1561fe6060f1SDimitry Andric 1562349cc55cSDimitry Andric void FieldDelegateDraw(Surface &surface, bool is_selected) override { 1563fe6060f1SDimitry Andric UpdateScrolling(); 1564fe6060f1SDimitry Andric 1565fe6060f1SDimitry Andric surface.TitledBox(m_label.c_str()); 1566fe6060f1SDimitry Andric 1567fe6060f1SDimitry Andric Rect content_bounds = surface.GetFrame(); 1568fe6060f1SDimitry Andric content_bounds.Inset(1, 1); 1569349cc55cSDimitry Andric Surface content_surface = surface.SubSurface(content_bounds); 1570fe6060f1SDimitry Andric 1571fe6060f1SDimitry Andric DrawContent(content_surface, is_selected); 1572fe6060f1SDimitry Andric } 1573fe6060f1SDimitry Andric 1574fe6060f1SDimitry Andric void SelectPrevious() { 1575fe6060f1SDimitry Andric if (m_choice > 0) 1576fe6060f1SDimitry Andric m_choice--; 1577fe6060f1SDimitry Andric } 1578fe6060f1SDimitry Andric 1579fe6060f1SDimitry Andric void SelectNext() { 1580fe6060f1SDimitry Andric if (m_choice < GetNumberOfChoices() - 1) 1581fe6060f1SDimitry Andric m_choice++; 1582fe6060f1SDimitry Andric } 1583fe6060f1SDimitry Andric 1584fe6060f1SDimitry Andric void UpdateScrolling() { 1585fe6060f1SDimitry Andric if (m_choice > GetLastVisibleChoice()) { 1586fe6060f1SDimitry Andric m_first_visibile_choice = m_choice - (m_number_of_visible_choices - 1); 1587fe6060f1SDimitry Andric return; 1588fe6060f1SDimitry Andric } 1589fe6060f1SDimitry Andric 1590fe6060f1SDimitry Andric if (m_choice < m_first_visibile_choice) 1591fe6060f1SDimitry Andric m_first_visibile_choice = m_choice; 1592fe6060f1SDimitry Andric } 1593fe6060f1SDimitry Andric 1594fe6060f1SDimitry Andric HandleCharResult FieldDelegateHandleChar(int key) override { 1595fe6060f1SDimitry Andric switch (key) { 1596fe6060f1SDimitry Andric case KEY_UP: 1597fe6060f1SDimitry Andric SelectPrevious(); 1598fe6060f1SDimitry Andric return eKeyHandled; 1599fe6060f1SDimitry Andric case KEY_DOWN: 1600fe6060f1SDimitry Andric SelectNext(); 1601fe6060f1SDimitry Andric return eKeyHandled; 1602fe6060f1SDimitry Andric default: 1603fe6060f1SDimitry Andric break; 1604fe6060f1SDimitry Andric } 1605fe6060f1SDimitry Andric return eKeyNotHandled; 1606fe6060f1SDimitry Andric } 1607fe6060f1SDimitry Andric 1608fe6060f1SDimitry Andric // Returns the content of the choice as a string. 1609fe6060f1SDimitry Andric std::string GetChoiceContent() { return m_choices[m_choice]; } 1610fe6060f1SDimitry Andric 1611fe6060f1SDimitry Andric // Returns the index of the choice. 1612fe6060f1SDimitry Andric int GetChoice() { return m_choice; } 1613fe6060f1SDimitry Andric 161481ad6265SDimitry Andric void SetChoice(llvm::StringRef choice) { 1615fe6060f1SDimitry Andric for (int i = 0; i < GetNumberOfChoices(); i++) { 1616fe6060f1SDimitry Andric if (choice == m_choices[i]) { 1617fe6060f1SDimitry Andric m_choice = i; 1618fe6060f1SDimitry Andric return; 1619fe6060f1SDimitry Andric } 1620fe6060f1SDimitry Andric } 1621fe6060f1SDimitry Andric } 1622fe6060f1SDimitry Andric 1623fe6060f1SDimitry Andric protected: 1624fe6060f1SDimitry Andric std::string m_label; 1625fe6060f1SDimitry Andric int m_number_of_visible_choices; 1626fe6060f1SDimitry Andric std::vector<std::string> m_choices; 1627fe6060f1SDimitry Andric // The index of the selected choice. 162881ad6265SDimitry Andric int m_choice = 0; 1629fe6060f1SDimitry Andric // The index of the first visible choice in the field. 163081ad6265SDimitry Andric int m_first_visibile_choice = 0; 1631fe6060f1SDimitry Andric }; 1632fe6060f1SDimitry Andric 1633fe6060f1SDimitry Andric class PlatformPluginFieldDelegate : public ChoicesFieldDelegate { 1634fe6060f1SDimitry Andric public: 1635fe6060f1SDimitry Andric PlatformPluginFieldDelegate(Debugger &debugger) 1636fe6060f1SDimitry Andric : ChoicesFieldDelegate("Platform Plugin", 3, GetPossiblePluginNames()) { 1637fe6060f1SDimitry Andric PlatformSP platform_sp = debugger.GetPlatformList().GetSelectedPlatform(); 1638fe6060f1SDimitry Andric if (platform_sp) 163981ad6265SDimitry Andric SetChoice(platform_sp->GetPluginName()); 1640fe6060f1SDimitry Andric } 1641fe6060f1SDimitry Andric 1642fe6060f1SDimitry Andric std::vector<std::string> GetPossiblePluginNames() { 1643fe6060f1SDimitry Andric std::vector<std::string> names; 1644fe6060f1SDimitry Andric size_t i = 0; 1645349cc55cSDimitry Andric for (llvm::StringRef name = 1646349cc55cSDimitry Andric PluginManager::GetPlatformPluginNameAtIndex(i++); 1647349cc55cSDimitry Andric !name.empty(); name = PluginManager::GetProcessPluginNameAtIndex(i++)) 1648349cc55cSDimitry Andric names.push_back(name.str()); 1649fe6060f1SDimitry Andric return names; 1650fe6060f1SDimitry Andric } 1651fe6060f1SDimitry Andric 1652fe6060f1SDimitry Andric std::string GetPluginName() { 1653fe6060f1SDimitry Andric std::string plugin_name = GetChoiceContent(); 1654fe6060f1SDimitry Andric return plugin_name; 1655fe6060f1SDimitry Andric } 1656fe6060f1SDimitry Andric }; 1657fe6060f1SDimitry Andric 1658fe6060f1SDimitry Andric class ProcessPluginFieldDelegate : public ChoicesFieldDelegate { 1659fe6060f1SDimitry Andric public: 1660fe6060f1SDimitry Andric ProcessPluginFieldDelegate() 1661fe6060f1SDimitry Andric : ChoicesFieldDelegate("Process Plugin", 3, GetPossiblePluginNames()) {} 1662fe6060f1SDimitry Andric 1663fe6060f1SDimitry Andric std::vector<std::string> GetPossiblePluginNames() { 1664fe6060f1SDimitry Andric std::vector<std::string> names; 1665fe6060f1SDimitry Andric names.push_back("<default>"); 1666fe6060f1SDimitry Andric 1667fe6060f1SDimitry Andric size_t i = 0; 1668349cc55cSDimitry Andric for (llvm::StringRef name = PluginManager::GetProcessPluginNameAtIndex(i++); 1669349cc55cSDimitry Andric !name.empty(); name = PluginManager::GetProcessPluginNameAtIndex(i++)) 1670349cc55cSDimitry Andric names.push_back(name.str()); 1671fe6060f1SDimitry Andric return names; 1672fe6060f1SDimitry Andric } 1673fe6060f1SDimitry Andric 1674fe6060f1SDimitry Andric std::string GetPluginName() { 1675fe6060f1SDimitry Andric std::string plugin_name = GetChoiceContent(); 1676fe6060f1SDimitry Andric if (plugin_name == "<default>") 1677fe6060f1SDimitry Andric return ""; 1678fe6060f1SDimitry Andric return plugin_name; 1679fe6060f1SDimitry Andric } 1680fe6060f1SDimitry Andric }; 1681fe6060f1SDimitry Andric 1682349cc55cSDimitry Andric class LazyBooleanFieldDelegate : public ChoicesFieldDelegate { 1683349cc55cSDimitry Andric public: 1684349cc55cSDimitry Andric LazyBooleanFieldDelegate(const char *label, const char *calculate_label) 1685349cc55cSDimitry Andric : ChoicesFieldDelegate(label, 3, GetPossibleOptions(calculate_label)) {} 1686349cc55cSDimitry Andric 1687349cc55cSDimitry Andric static constexpr const char *kNo = "No"; 1688349cc55cSDimitry Andric static constexpr const char *kYes = "Yes"; 1689349cc55cSDimitry Andric 1690349cc55cSDimitry Andric std::vector<std::string> GetPossibleOptions(const char *calculate_label) { 1691349cc55cSDimitry Andric std::vector<std::string> options; 1692349cc55cSDimitry Andric options.push_back(calculate_label); 1693349cc55cSDimitry Andric options.push_back(kYes); 1694349cc55cSDimitry Andric options.push_back(kNo); 1695349cc55cSDimitry Andric return options; 1696349cc55cSDimitry Andric } 1697349cc55cSDimitry Andric 1698349cc55cSDimitry Andric LazyBool GetLazyBoolean() { 1699349cc55cSDimitry Andric std::string choice = GetChoiceContent(); 1700349cc55cSDimitry Andric if (choice == kNo) 1701349cc55cSDimitry Andric return eLazyBoolNo; 1702349cc55cSDimitry Andric else if (choice == kYes) 1703349cc55cSDimitry Andric return eLazyBoolYes; 1704349cc55cSDimitry Andric else 1705349cc55cSDimitry Andric return eLazyBoolCalculate; 1706349cc55cSDimitry Andric } 1707349cc55cSDimitry Andric }; 1708349cc55cSDimitry Andric 1709fe6060f1SDimitry Andric template <class T> class ListFieldDelegate : public FieldDelegate { 1710fe6060f1SDimitry Andric public: 1711fe6060f1SDimitry Andric ListFieldDelegate(const char *label, T default_field) 171281ad6265SDimitry Andric : m_label(label), m_default_field(default_field), 1713fe6060f1SDimitry Andric m_selection_type(SelectionType::NewButton) {} 1714fe6060f1SDimitry Andric 1715fe6060f1SDimitry Andric // Signify which element is selected. If a field or a remove button is 1716fe6060f1SDimitry Andric // selected, then m_selection_index signifies the particular field that 1717fe6060f1SDimitry Andric // is selected or the field that the remove button belongs to. 1718fe6060f1SDimitry Andric enum class SelectionType { Field, RemoveButton, NewButton }; 1719fe6060f1SDimitry Andric 1720fe6060f1SDimitry Andric // A List field is drawn as a titled box of a number of other fields of the 1721fe6060f1SDimitry Andric // same type. Each field has a Remove button next to it that removes the 1722fe6060f1SDimitry Andric // corresponding field. Finally, the last line contains a New button to add a 1723fe6060f1SDimitry Andric // new field. 1724fe6060f1SDimitry Andric // 1725fe6060f1SDimitry Andric // __[Label]___________ 1726fe6060f1SDimitry Andric // | Field 0 [Remove] | 1727fe6060f1SDimitry Andric // | Field 1 [Remove] | 1728fe6060f1SDimitry Andric // | Field 2 [Remove] | 1729fe6060f1SDimitry Andric // | [New] | 1730fe6060f1SDimitry Andric // |__________________| 1731fe6060f1SDimitry Andric 1732fe6060f1SDimitry Andric // List fields have two lines for border characters, 1 line for the New 1733fe6060f1SDimitry Andric // button, and the total height of the available fields. 1734fe6060f1SDimitry Andric int FieldDelegateGetHeight() override { 1735fe6060f1SDimitry Andric // 2 border characters. 1736fe6060f1SDimitry Andric int height = 2; 1737fe6060f1SDimitry Andric // Total height of the fields. 1738fe6060f1SDimitry Andric for (int i = 0; i < GetNumberOfFields(); i++) { 1739fe6060f1SDimitry Andric height += m_fields[i].FieldDelegateGetHeight(); 1740fe6060f1SDimitry Andric } 1741fe6060f1SDimitry Andric // A line for the New button. 1742fe6060f1SDimitry Andric height++; 1743fe6060f1SDimitry Andric return height; 1744fe6060f1SDimitry Andric } 1745fe6060f1SDimitry Andric 1746fe6060f1SDimitry Andric ScrollContext FieldDelegateGetScrollContext() override { 1747fe6060f1SDimitry Andric int height = FieldDelegateGetHeight(); 1748fe6060f1SDimitry Andric if (m_selection_type == SelectionType::NewButton) 1749fe6060f1SDimitry Andric return ScrollContext(height - 2, height - 1); 1750fe6060f1SDimitry Andric 1751fe6060f1SDimitry Andric FieldDelegate &field = m_fields[m_selection_index]; 1752fe6060f1SDimitry Andric ScrollContext context = field.FieldDelegateGetScrollContext(); 1753fe6060f1SDimitry Andric 1754fe6060f1SDimitry Andric // Start at 1 because of the top border. 1755fe6060f1SDimitry Andric int offset = 1; 1756fe6060f1SDimitry Andric for (int i = 0; i < m_selection_index; i++) { 1757fe6060f1SDimitry Andric offset += m_fields[i].FieldDelegateGetHeight(); 1758fe6060f1SDimitry Andric } 1759fe6060f1SDimitry Andric context.Offset(offset); 1760fe6060f1SDimitry Andric 1761fe6060f1SDimitry Andric // If the scroll context is touching the top border, include it in the 1762fe6060f1SDimitry Andric // context to show the label. 1763fe6060f1SDimitry Andric if (context.start == 1) 1764fe6060f1SDimitry Andric context.start--; 1765fe6060f1SDimitry Andric 1766fe6060f1SDimitry Andric // If the scroll context is touching the new button, include it as well as 1767fe6060f1SDimitry Andric // the bottom border in the context. 1768fe6060f1SDimitry Andric if (context.end == height - 3) 1769fe6060f1SDimitry Andric context.end += 2; 1770fe6060f1SDimitry Andric 1771fe6060f1SDimitry Andric return context; 1772fe6060f1SDimitry Andric } 1773fe6060f1SDimitry Andric 1774349cc55cSDimitry Andric void DrawRemoveButton(Surface &surface, int highlight) { 1775fe6060f1SDimitry Andric surface.MoveCursor(1, surface.GetHeight() / 2); 1776fe6060f1SDimitry Andric if (highlight) 1777fe6060f1SDimitry Andric surface.AttributeOn(A_REVERSE); 1778fe6060f1SDimitry Andric surface.PutCString("[Remove]"); 1779fe6060f1SDimitry Andric if (highlight) 1780fe6060f1SDimitry Andric surface.AttributeOff(A_REVERSE); 1781fe6060f1SDimitry Andric } 1782fe6060f1SDimitry Andric 1783349cc55cSDimitry Andric void DrawFields(Surface &surface, bool is_selected) { 1784fe6060f1SDimitry Andric int line = 0; 1785fe6060f1SDimitry Andric int width = surface.GetWidth(); 1786fe6060f1SDimitry Andric for (int i = 0; i < GetNumberOfFields(); i++) { 1787fe6060f1SDimitry Andric int height = m_fields[i].FieldDelegateGetHeight(); 1788fe6060f1SDimitry Andric Rect bounds = Rect(Point(0, line), Size(width, height)); 1789fe6060f1SDimitry Andric Rect field_bounds, remove_button_bounds; 1790fe6060f1SDimitry Andric bounds.VerticalSplit(bounds.size.width - sizeof(" [Remove]"), 1791fe6060f1SDimitry Andric field_bounds, remove_button_bounds); 1792349cc55cSDimitry Andric Surface field_surface = surface.SubSurface(field_bounds); 1793349cc55cSDimitry Andric Surface remove_button_surface = surface.SubSurface(remove_button_bounds); 1794fe6060f1SDimitry Andric 1795fe6060f1SDimitry Andric bool is_element_selected = m_selection_index == i && is_selected; 1796fe6060f1SDimitry Andric bool is_field_selected = 1797fe6060f1SDimitry Andric is_element_selected && m_selection_type == SelectionType::Field; 1798fe6060f1SDimitry Andric bool is_remove_button_selected = 1799fe6060f1SDimitry Andric is_element_selected && 1800fe6060f1SDimitry Andric m_selection_type == SelectionType::RemoveButton; 1801fe6060f1SDimitry Andric m_fields[i].FieldDelegateDraw(field_surface, is_field_selected); 1802fe6060f1SDimitry Andric DrawRemoveButton(remove_button_surface, is_remove_button_selected); 1803fe6060f1SDimitry Andric 1804fe6060f1SDimitry Andric line += height; 1805fe6060f1SDimitry Andric } 1806fe6060f1SDimitry Andric } 1807fe6060f1SDimitry Andric 1808349cc55cSDimitry Andric void DrawNewButton(Surface &surface, bool is_selected) { 1809fe6060f1SDimitry Andric const char *button_text = "[New]"; 1810fe6060f1SDimitry Andric int x = (surface.GetWidth() - sizeof(button_text) - 1) / 2; 1811fe6060f1SDimitry Andric surface.MoveCursor(x, 0); 1812fe6060f1SDimitry Andric bool highlight = 1813fe6060f1SDimitry Andric is_selected && m_selection_type == SelectionType::NewButton; 1814fe6060f1SDimitry Andric if (highlight) 1815fe6060f1SDimitry Andric surface.AttributeOn(A_REVERSE); 1816fe6060f1SDimitry Andric surface.PutCString(button_text); 1817fe6060f1SDimitry Andric if (highlight) 1818fe6060f1SDimitry Andric surface.AttributeOff(A_REVERSE); 1819fe6060f1SDimitry Andric } 1820fe6060f1SDimitry Andric 1821349cc55cSDimitry Andric void FieldDelegateDraw(Surface &surface, bool is_selected) override { 1822fe6060f1SDimitry Andric surface.TitledBox(m_label.c_str()); 1823fe6060f1SDimitry Andric 1824fe6060f1SDimitry Andric Rect content_bounds = surface.GetFrame(); 1825fe6060f1SDimitry Andric content_bounds.Inset(1, 1); 1826fe6060f1SDimitry Andric Rect fields_bounds, new_button_bounds; 1827fe6060f1SDimitry Andric content_bounds.HorizontalSplit(content_bounds.size.height - 1, 1828fe6060f1SDimitry Andric fields_bounds, new_button_bounds); 1829349cc55cSDimitry Andric Surface fields_surface = surface.SubSurface(fields_bounds); 1830349cc55cSDimitry Andric Surface new_button_surface = surface.SubSurface(new_button_bounds); 1831fe6060f1SDimitry Andric 1832fe6060f1SDimitry Andric DrawFields(fields_surface, is_selected); 1833fe6060f1SDimitry Andric DrawNewButton(new_button_surface, is_selected); 1834fe6060f1SDimitry Andric } 1835fe6060f1SDimitry Andric 1836fe6060f1SDimitry Andric void AddNewField() { 1837fe6060f1SDimitry Andric m_fields.push_back(m_default_field); 1838fe6060f1SDimitry Andric m_selection_index = GetNumberOfFields() - 1; 1839fe6060f1SDimitry Andric m_selection_type = SelectionType::Field; 1840fe6060f1SDimitry Andric FieldDelegate &field = m_fields[m_selection_index]; 1841fe6060f1SDimitry Andric field.FieldDelegateSelectFirstElement(); 1842fe6060f1SDimitry Andric } 1843fe6060f1SDimitry Andric 1844fe6060f1SDimitry Andric void RemoveField() { 1845fe6060f1SDimitry Andric m_fields.erase(m_fields.begin() + m_selection_index); 1846fe6060f1SDimitry Andric if (m_selection_index != 0) 1847fe6060f1SDimitry Andric m_selection_index--; 1848fe6060f1SDimitry Andric 1849fe6060f1SDimitry Andric if (GetNumberOfFields() > 0) { 1850fe6060f1SDimitry Andric m_selection_type = SelectionType::Field; 1851fe6060f1SDimitry Andric FieldDelegate &field = m_fields[m_selection_index]; 1852fe6060f1SDimitry Andric field.FieldDelegateSelectFirstElement(); 1853fe6060f1SDimitry Andric } else 1854fe6060f1SDimitry Andric m_selection_type = SelectionType::NewButton; 1855fe6060f1SDimitry Andric } 1856fe6060f1SDimitry Andric 1857fe6060f1SDimitry Andric HandleCharResult SelectNext(int key) { 1858fe6060f1SDimitry Andric if (m_selection_type == SelectionType::NewButton) 1859fe6060f1SDimitry Andric return eKeyNotHandled; 1860fe6060f1SDimitry Andric 1861fe6060f1SDimitry Andric if (m_selection_type == SelectionType::RemoveButton) { 1862fe6060f1SDimitry Andric if (m_selection_index == GetNumberOfFields() - 1) { 1863fe6060f1SDimitry Andric m_selection_type = SelectionType::NewButton; 1864fe6060f1SDimitry Andric return eKeyHandled; 1865fe6060f1SDimitry Andric } 1866fe6060f1SDimitry Andric m_selection_index++; 1867fe6060f1SDimitry Andric m_selection_type = SelectionType::Field; 1868fe6060f1SDimitry Andric FieldDelegate &next_field = m_fields[m_selection_index]; 1869fe6060f1SDimitry Andric next_field.FieldDelegateSelectFirstElement(); 1870fe6060f1SDimitry Andric return eKeyHandled; 1871fe6060f1SDimitry Andric } 1872fe6060f1SDimitry Andric 1873fe6060f1SDimitry Andric FieldDelegate &field = m_fields[m_selection_index]; 1874fe6060f1SDimitry Andric if (!field.FieldDelegateOnLastOrOnlyElement()) { 1875fe6060f1SDimitry Andric return field.FieldDelegateHandleChar(key); 1876fe6060f1SDimitry Andric } 1877fe6060f1SDimitry Andric 1878fe6060f1SDimitry Andric field.FieldDelegateExitCallback(); 1879fe6060f1SDimitry Andric 1880fe6060f1SDimitry Andric m_selection_type = SelectionType::RemoveButton; 1881fe6060f1SDimitry Andric return eKeyHandled; 1882fe6060f1SDimitry Andric } 1883fe6060f1SDimitry Andric 1884fe6060f1SDimitry Andric HandleCharResult SelectPrevious(int key) { 1885fe6060f1SDimitry Andric if (FieldDelegateOnFirstOrOnlyElement()) 1886fe6060f1SDimitry Andric return eKeyNotHandled; 1887fe6060f1SDimitry Andric 1888fe6060f1SDimitry Andric if (m_selection_type == SelectionType::RemoveButton) { 1889fe6060f1SDimitry Andric m_selection_type = SelectionType::Field; 1890fe6060f1SDimitry Andric FieldDelegate &field = m_fields[m_selection_index]; 1891fe6060f1SDimitry Andric field.FieldDelegateSelectLastElement(); 1892fe6060f1SDimitry Andric return eKeyHandled; 1893fe6060f1SDimitry Andric } 1894fe6060f1SDimitry Andric 1895fe6060f1SDimitry Andric if (m_selection_type == SelectionType::NewButton) { 1896fe6060f1SDimitry Andric m_selection_type = SelectionType::RemoveButton; 1897fe6060f1SDimitry Andric m_selection_index = GetNumberOfFields() - 1; 1898fe6060f1SDimitry Andric return eKeyHandled; 1899fe6060f1SDimitry Andric } 1900fe6060f1SDimitry Andric 1901fe6060f1SDimitry Andric FieldDelegate &field = m_fields[m_selection_index]; 1902fe6060f1SDimitry Andric if (!field.FieldDelegateOnFirstOrOnlyElement()) { 1903fe6060f1SDimitry Andric return field.FieldDelegateHandleChar(key); 1904fe6060f1SDimitry Andric } 1905fe6060f1SDimitry Andric 1906fe6060f1SDimitry Andric field.FieldDelegateExitCallback(); 1907fe6060f1SDimitry Andric 1908fe6060f1SDimitry Andric m_selection_type = SelectionType::RemoveButton; 1909fe6060f1SDimitry Andric m_selection_index--; 1910fe6060f1SDimitry Andric return eKeyHandled; 1911fe6060f1SDimitry Andric } 1912fe6060f1SDimitry Andric 1913349cc55cSDimitry Andric // If the last element of the field is selected and it didn't handle the key. 1914349cc55cSDimitry Andric // Select the next field or new button if the selected field is the last one. 1915349cc55cSDimitry Andric HandleCharResult SelectNextInList(int key) { 1916349cc55cSDimitry Andric assert(m_selection_type == SelectionType::Field); 1917349cc55cSDimitry Andric 1918349cc55cSDimitry Andric FieldDelegate &field = m_fields[m_selection_index]; 1919349cc55cSDimitry Andric if (field.FieldDelegateHandleChar(key) == eKeyHandled) 1920349cc55cSDimitry Andric return eKeyHandled; 1921349cc55cSDimitry Andric 1922349cc55cSDimitry Andric if (!field.FieldDelegateOnLastOrOnlyElement()) 1923349cc55cSDimitry Andric return eKeyNotHandled; 1924349cc55cSDimitry Andric 1925349cc55cSDimitry Andric field.FieldDelegateExitCallback(); 1926349cc55cSDimitry Andric 1927349cc55cSDimitry Andric if (m_selection_index == GetNumberOfFields() - 1) { 1928349cc55cSDimitry Andric m_selection_type = SelectionType::NewButton; 1929349cc55cSDimitry Andric return eKeyHandled; 1930349cc55cSDimitry Andric } 1931349cc55cSDimitry Andric 1932349cc55cSDimitry Andric m_selection_index++; 1933349cc55cSDimitry Andric FieldDelegate &next_field = m_fields[m_selection_index]; 1934349cc55cSDimitry Andric next_field.FieldDelegateSelectFirstElement(); 1935349cc55cSDimitry Andric return eKeyHandled; 1936349cc55cSDimitry Andric } 1937349cc55cSDimitry Andric 1938fe6060f1SDimitry Andric HandleCharResult FieldDelegateHandleChar(int key) override { 1939fe6060f1SDimitry Andric switch (key) { 1940fe6060f1SDimitry Andric case '\r': 1941fe6060f1SDimitry Andric case '\n': 1942fe6060f1SDimitry Andric case KEY_ENTER: 1943fe6060f1SDimitry Andric switch (m_selection_type) { 1944fe6060f1SDimitry Andric case SelectionType::NewButton: 1945fe6060f1SDimitry Andric AddNewField(); 1946fe6060f1SDimitry Andric return eKeyHandled; 1947fe6060f1SDimitry Andric case SelectionType::RemoveButton: 1948fe6060f1SDimitry Andric RemoveField(); 1949fe6060f1SDimitry Andric return eKeyHandled; 1950349cc55cSDimitry Andric case SelectionType::Field: 1951349cc55cSDimitry Andric return SelectNextInList(key); 1952fe6060f1SDimitry Andric } 1953fe6060f1SDimitry Andric break; 1954fe6060f1SDimitry Andric case '\t': 1955349cc55cSDimitry Andric return SelectNext(key); 1956fe6060f1SDimitry Andric case KEY_SHIFT_TAB: 1957349cc55cSDimitry Andric return SelectPrevious(key); 1958fe6060f1SDimitry Andric default: 1959fe6060f1SDimitry Andric break; 1960fe6060f1SDimitry Andric } 1961fe6060f1SDimitry Andric 1962fe6060f1SDimitry Andric // If the key wasn't handled and one of the fields is selected, pass the key 1963fe6060f1SDimitry Andric // to that field. 1964fe6060f1SDimitry Andric if (m_selection_type == SelectionType::Field) { 1965fe6060f1SDimitry Andric return m_fields[m_selection_index].FieldDelegateHandleChar(key); 1966fe6060f1SDimitry Andric } 1967fe6060f1SDimitry Andric 1968fe6060f1SDimitry Andric return eKeyNotHandled; 1969fe6060f1SDimitry Andric } 1970fe6060f1SDimitry Andric 1971fe6060f1SDimitry Andric bool FieldDelegateOnLastOrOnlyElement() override { 1972fe6060f1SDimitry Andric if (m_selection_type == SelectionType::NewButton) { 1973fe6060f1SDimitry Andric return true; 1974fe6060f1SDimitry Andric } 1975fe6060f1SDimitry Andric return false; 1976fe6060f1SDimitry Andric } 1977fe6060f1SDimitry Andric 1978fe6060f1SDimitry Andric bool FieldDelegateOnFirstOrOnlyElement() override { 1979fe6060f1SDimitry Andric if (m_selection_type == SelectionType::NewButton && 1980fe6060f1SDimitry Andric GetNumberOfFields() == 0) 1981fe6060f1SDimitry Andric return true; 1982fe6060f1SDimitry Andric 1983fe6060f1SDimitry Andric if (m_selection_type == SelectionType::Field && m_selection_index == 0) { 1984fe6060f1SDimitry Andric FieldDelegate &field = m_fields[m_selection_index]; 1985fe6060f1SDimitry Andric return field.FieldDelegateOnFirstOrOnlyElement(); 1986fe6060f1SDimitry Andric } 1987fe6060f1SDimitry Andric 1988fe6060f1SDimitry Andric return false; 1989fe6060f1SDimitry Andric } 1990fe6060f1SDimitry Andric 1991fe6060f1SDimitry Andric void FieldDelegateSelectFirstElement() override { 1992fe6060f1SDimitry Andric if (GetNumberOfFields() == 0) { 1993fe6060f1SDimitry Andric m_selection_type = SelectionType::NewButton; 1994fe6060f1SDimitry Andric return; 1995fe6060f1SDimitry Andric } 1996fe6060f1SDimitry Andric 1997fe6060f1SDimitry Andric m_selection_type = SelectionType::Field; 1998fe6060f1SDimitry Andric m_selection_index = 0; 1999fe6060f1SDimitry Andric } 2000fe6060f1SDimitry Andric 2001fe6060f1SDimitry Andric void FieldDelegateSelectLastElement() override { 2002fe6060f1SDimitry Andric m_selection_type = SelectionType::NewButton; 2003fe6060f1SDimitry Andric } 2004fe6060f1SDimitry Andric 2005fe6060f1SDimitry Andric int GetNumberOfFields() { return m_fields.size(); } 2006fe6060f1SDimitry Andric 2007fe6060f1SDimitry Andric // Returns the form delegate at the current index. 2008fe6060f1SDimitry Andric T &GetField(int index) { return m_fields[index]; } 2009fe6060f1SDimitry Andric 2010fe6060f1SDimitry Andric protected: 2011fe6060f1SDimitry Andric std::string m_label; 2012fe6060f1SDimitry Andric // The default field delegate instance from which new field delegates will be 2013fe6060f1SDimitry Andric // created though a copy. 2014fe6060f1SDimitry Andric T m_default_field; 2015fe6060f1SDimitry Andric std::vector<T> m_fields; 201681ad6265SDimitry Andric int m_selection_index = 0; 2017fe6060f1SDimitry Andric // See SelectionType class enum. 2018fe6060f1SDimitry Andric SelectionType m_selection_type; 2019fe6060f1SDimitry Andric }; 2020fe6060f1SDimitry Andric 2021349cc55cSDimitry Andric class ArgumentsFieldDelegate : public ListFieldDelegate<TextFieldDelegate> { 2022349cc55cSDimitry Andric public: 2023349cc55cSDimitry Andric ArgumentsFieldDelegate() 2024349cc55cSDimitry Andric : ListFieldDelegate("Arguments", 2025349cc55cSDimitry Andric TextFieldDelegate("Argument", "", false)) {} 2026349cc55cSDimitry Andric 2027349cc55cSDimitry Andric Args GetArguments() { 2028349cc55cSDimitry Andric Args arguments; 2029349cc55cSDimitry Andric for (int i = 0; i < GetNumberOfFields(); i++) { 2030349cc55cSDimitry Andric arguments.AppendArgument(GetField(i).GetText()); 2031349cc55cSDimitry Andric } 2032349cc55cSDimitry Andric return arguments; 2033349cc55cSDimitry Andric } 2034349cc55cSDimitry Andric 2035349cc55cSDimitry Andric void AddArguments(const Args &arguments) { 2036349cc55cSDimitry Andric for (size_t i = 0; i < arguments.GetArgumentCount(); i++) { 2037349cc55cSDimitry Andric AddNewField(); 2038349cc55cSDimitry Andric TextFieldDelegate &field = GetField(GetNumberOfFields() - 1); 2039349cc55cSDimitry Andric field.SetText(arguments.GetArgumentAtIndex(i)); 2040349cc55cSDimitry Andric } 2041349cc55cSDimitry Andric } 2042349cc55cSDimitry Andric }; 2043349cc55cSDimitry Andric 2044349cc55cSDimitry Andric template <class KeyFieldDelegateType, class ValueFieldDelegateType> 2045349cc55cSDimitry Andric class MappingFieldDelegate : public FieldDelegate { 2046349cc55cSDimitry Andric public: 2047349cc55cSDimitry Andric MappingFieldDelegate(KeyFieldDelegateType key_field, 2048349cc55cSDimitry Andric ValueFieldDelegateType value_field) 2049349cc55cSDimitry Andric : m_key_field(key_field), m_value_field(value_field), 2050349cc55cSDimitry Andric m_selection_type(SelectionType::Key) {} 2051349cc55cSDimitry Andric 2052349cc55cSDimitry Andric // Signify which element is selected. The key field or its value field. 2053349cc55cSDimitry Andric enum class SelectionType { Key, Value }; 2054349cc55cSDimitry Andric 2055349cc55cSDimitry Andric // A mapping field is drawn as two text fields with a right arrow in between. 2056349cc55cSDimitry Andric // The first field stores the key of the mapping and the second stores the 2057349cc55cSDimitry Andric // value if the mapping. 2058349cc55cSDimitry Andric // 2059349cc55cSDimitry Andric // __[Key]_____________ __[Value]___________ 2060349cc55cSDimitry Andric // | | > | | 2061349cc55cSDimitry Andric // |__________________| |__________________| 2062349cc55cSDimitry Andric // - Error message if it exists. 2063349cc55cSDimitry Andric 2064349cc55cSDimitry Andric // The mapping field has a height that is equal to the maximum height between 2065349cc55cSDimitry Andric // the key and value fields. 2066349cc55cSDimitry Andric int FieldDelegateGetHeight() override { 2067349cc55cSDimitry Andric return std::max(m_key_field.FieldDelegateGetHeight(), 2068349cc55cSDimitry Andric m_value_field.FieldDelegateGetHeight()); 2069349cc55cSDimitry Andric } 2070349cc55cSDimitry Andric 2071349cc55cSDimitry Andric void DrawArrow(Surface &surface) { 2072349cc55cSDimitry Andric surface.MoveCursor(0, 1); 2073349cc55cSDimitry Andric surface.PutChar(ACS_RARROW); 2074349cc55cSDimitry Andric } 2075349cc55cSDimitry Andric 2076349cc55cSDimitry Andric void FieldDelegateDraw(Surface &surface, bool is_selected) override { 2077349cc55cSDimitry Andric Rect bounds = surface.GetFrame(); 2078349cc55cSDimitry Andric Rect key_field_bounds, arrow_and_value_field_bounds; 2079349cc55cSDimitry Andric bounds.VerticalSplit(bounds.size.width / 2, key_field_bounds, 2080349cc55cSDimitry Andric arrow_and_value_field_bounds); 2081349cc55cSDimitry Andric Rect arrow_bounds, value_field_bounds; 2082349cc55cSDimitry Andric arrow_and_value_field_bounds.VerticalSplit(1, arrow_bounds, 2083349cc55cSDimitry Andric value_field_bounds); 2084349cc55cSDimitry Andric 2085349cc55cSDimitry Andric Surface key_field_surface = surface.SubSurface(key_field_bounds); 2086349cc55cSDimitry Andric Surface arrow_surface = surface.SubSurface(arrow_bounds); 2087349cc55cSDimitry Andric Surface value_field_surface = surface.SubSurface(value_field_bounds); 2088349cc55cSDimitry Andric 2089349cc55cSDimitry Andric bool key_is_selected = 2090349cc55cSDimitry Andric m_selection_type == SelectionType::Key && is_selected; 2091349cc55cSDimitry Andric m_key_field.FieldDelegateDraw(key_field_surface, key_is_selected); 2092349cc55cSDimitry Andric DrawArrow(arrow_surface); 2093349cc55cSDimitry Andric bool value_is_selected = 2094349cc55cSDimitry Andric m_selection_type == SelectionType::Value && is_selected; 2095349cc55cSDimitry Andric m_value_field.FieldDelegateDraw(value_field_surface, value_is_selected); 2096349cc55cSDimitry Andric } 2097349cc55cSDimitry Andric 2098349cc55cSDimitry Andric HandleCharResult SelectNext(int key) { 2099349cc55cSDimitry Andric if (FieldDelegateOnLastOrOnlyElement()) 2100349cc55cSDimitry Andric return eKeyNotHandled; 2101349cc55cSDimitry Andric 2102349cc55cSDimitry Andric if (!m_key_field.FieldDelegateOnLastOrOnlyElement()) { 2103349cc55cSDimitry Andric return m_key_field.FieldDelegateHandleChar(key); 2104349cc55cSDimitry Andric } 2105349cc55cSDimitry Andric 2106349cc55cSDimitry Andric m_key_field.FieldDelegateExitCallback(); 2107349cc55cSDimitry Andric m_selection_type = SelectionType::Value; 2108349cc55cSDimitry Andric m_value_field.FieldDelegateSelectFirstElement(); 2109349cc55cSDimitry Andric return eKeyHandled; 2110349cc55cSDimitry Andric } 2111349cc55cSDimitry Andric 2112349cc55cSDimitry Andric HandleCharResult SelectPrevious(int key) { 2113349cc55cSDimitry Andric if (FieldDelegateOnFirstOrOnlyElement()) 2114349cc55cSDimitry Andric return eKeyNotHandled; 2115349cc55cSDimitry Andric 2116349cc55cSDimitry Andric if (!m_value_field.FieldDelegateOnFirstOrOnlyElement()) { 2117349cc55cSDimitry Andric return m_value_field.FieldDelegateHandleChar(key); 2118349cc55cSDimitry Andric } 2119349cc55cSDimitry Andric 2120349cc55cSDimitry Andric m_value_field.FieldDelegateExitCallback(); 2121349cc55cSDimitry Andric m_selection_type = SelectionType::Key; 2122349cc55cSDimitry Andric m_key_field.FieldDelegateSelectLastElement(); 2123349cc55cSDimitry Andric return eKeyHandled; 2124349cc55cSDimitry Andric } 2125349cc55cSDimitry Andric 2126349cc55cSDimitry Andric // If the value field is selected, pass the key to it. If the key field is 2127349cc55cSDimitry Andric // selected, its last element is selected, and it didn't handle the key, then 2128349cc55cSDimitry Andric // select its corresponding value field. 2129349cc55cSDimitry Andric HandleCharResult SelectNextField(int key) { 2130349cc55cSDimitry Andric if (m_selection_type == SelectionType::Value) { 2131349cc55cSDimitry Andric return m_value_field.FieldDelegateHandleChar(key); 2132349cc55cSDimitry Andric } 2133349cc55cSDimitry Andric 2134349cc55cSDimitry Andric if (m_key_field.FieldDelegateHandleChar(key) == eKeyHandled) 2135349cc55cSDimitry Andric return eKeyHandled; 2136349cc55cSDimitry Andric 2137349cc55cSDimitry Andric if (!m_key_field.FieldDelegateOnLastOrOnlyElement()) 2138349cc55cSDimitry Andric return eKeyNotHandled; 2139349cc55cSDimitry Andric 2140349cc55cSDimitry Andric m_key_field.FieldDelegateExitCallback(); 2141349cc55cSDimitry Andric m_selection_type = SelectionType::Value; 2142349cc55cSDimitry Andric m_value_field.FieldDelegateSelectFirstElement(); 2143349cc55cSDimitry Andric return eKeyHandled; 2144349cc55cSDimitry Andric } 2145349cc55cSDimitry Andric 2146349cc55cSDimitry Andric HandleCharResult FieldDelegateHandleChar(int key) override { 2147349cc55cSDimitry Andric switch (key) { 2148349cc55cSDimitry Andric case KEY_RETURN: 2149349cc55cSDimitry Andric return SelectNextField(key); 2150349cc55cSDimitry Andric case '\t': 2151349cc55cSDimitry Andric return SelectNext(key); 2152349cc55cSDimitry Andric case KEY_SHIFT_TAB: 2153349cc55cSDimitry Andric return SelectPrevious(key); 2154349cc55cSDimitry Andric default: 2155349cc55cSDimitry Andric break; 2156349cc55cSDimitry Andric } 2157349cc55cSDimitry Andric 2158349cc55cSDimitry Andric // If the key wasn't handled, pass the key to the selected field. 2159349cc55cSDimitry Andric if (m_selection_type == SelectionType::Key) 2160349cc55cSDimitry Andric return m_key_field.FieldDelegateHandleChar(key); 2161349cc55cSDimitry Andric else 2162349cc55cSDimitry Andric return m_value_field.FieldDelegateHandleChar(key); 2163349cc55cSDimitry Andric 2164349cc55cSDimitry Andric return eKeyNotHandled; 2165349cc55cSDimitry Andric } 2166349cc55cSDimitry Andric 2167349cc55cSDimitry Andric bool FieldDelegateOnFirstOrOnlyElement() override { 2168349cc55cSDimitry Andric return m_selection_type == SelectionType::Key; 2169349cc55cSDimitry Andric } 2170349cc55cSDimitry Andric 2171349cc55cSDimitry Andric bool FieldDelegateOnLastOrOnlyElement() override { 2172349cc55cSDimitry Andric return m_selection_type == SelectionType::Value; 2173349cc55cSDimitry Andric } 2174349cc55cSDimitry Andric 2175349cc55cSDimitry Andric void FieldDelegateSelectFirstElement() override { 2176349cc55cSDimitry Andric m_selection_type = SelectionType::Key; 2177349cc55cSDimitry Andric } 2178349cc55cSDimitry Andric 2179349cc55cSDimitry Andric void FieldDelegateSelectLastElement() override { 2180349cc55cSDimitry Andric m_selection_type = SelectionType::Value; 2181349cc55cSDimitry Andric } 2182349cc55cSDimitry Andric 2183349cc55cSDimitry Andric bool FieldDelegateHasError() override { 2184349cc55cSDimitry Andric return m_key_field.FieldDelegateHasError() || 2185349cc55cSDimitry Andric m_value_field.FieldDelegateHasError(); 2186349cc55cSDimitry Andric } 2187349cc55cSDimitry Andric 2188349cc55cSDimitry Andric KeyFieldDelegateType &GetKeyField() { return m_key_field; } 2189349cc55cSDimitry Andric 2190349cc55cSDimitry Andric ValueFieldDelegateType &GetValueField() { return m_value_field; } 2191349cc55cSDimitry Andric 2192349cc55cSDimitry Andric protected: 2193349cc55cSDimitry Andric KeyFieldDelegateType m_key_field; 2194349cc55cSDimitry Andric ValueFieldDelegateType m_value_field; 2195349cc55cSDimitry Andric // See SelectionType class enum. 2196349cc55cSDimitry Andric SelectionType m_selection_type; 2197349cc55cSDimitry Andric }; 2198349cc55cSDimitry Andric 2199349cc55cSDimitry Andric class EnvironmentVariableNameFieldDelegate : public TextFieldDelegate { 2200349cc55cSDimitry Andric public: 2201349cc55cSDimitry Andric EnvironmentVariableNameFieldDelegate(const char *content) 2202349cc55cSDimitry Andric : TextFieldDelegate("Name", content, true) {} 2203349cc55cSDimitry Andric 2204349cc55cSDimitry Andric // Environment variable names can't contain an equal sign. 2205349cc55cSDimitry Andric bool IsAcceptableChar(int key) override { 2206349cc55cSDimitry Andric return TextFieldDelegate::IsAcceptableChar(key) && key != '='; 2207349cc55cSDimitry Andric } 2208349cc55cSDimitry Andric 2209349cc55cSDimitry Andric const std::string &GetName() { return m_content; } 2210349cc55cSDimitry Andric }; 2211349cc55cSDimitry Andric 2212349cc55cSDimitry Andric class EnvironmentVariableFieldDelegate 2213349cc55cSDimitry Andric : public MappingFieldDelegate<EnvironmentVariableNameFieldDelegate, 2214349cc55cSDimitry Andric TextFieldDelegate> { 2215349cc55cSDimitry Andric public: 2216349cc55cSDimitry Andric EnvironmentVariableFieldDelegate() 2217349cc55cSDimitry Andric : MappingFieldDelegate( 2218349cc55cSDimitry Andric EnvironmentVariableNameFieldDelegate(""), 2219349cc55cSDimitry Andric TextFieldDelegate("Value", "", /*required=*/false)) {} 2220349cc55cSDimitry Andric 2221349cc55cSDimitry Andric const std::string &GetName() { return GetKeyField().GetName(); } 2222349cc55cSDimitry Andric 2223349cc55cSDimitry Andric const std::string &GetValue() { return GetValueField().GetText(); } 2224349cc55cSDimitry Andric 2225349cc55cSDimitry Andric void SetName(const char *name) { return GetKeyField().SetText(name); } 2226349cc55cSDimitry Andric 2227349cc55cSDimitry Andric void SetValue(const char *value) { return GetValueField().SetText(value); } 2228349cc55cSDimitry Andric }; 2229349cc55cSDimitry Andric 2230349cc55cSDimitry Andric class EnvironmentVariableListFieldDelegate 2231349cc55cSDimitry Andric : public ListFieldDelegate<EnvironmentVariableFieldDelegate> { 2232349cc55cSDimitry Andric public: 2233349cc55cSDimitry Andric EnvironmentVariableListFieldDelegate(const char *label) 2234349cc55cSDimitry Andric : ListFieldDelegate(label, EnvironmentVariableFieldDelegate()) {} 2235349cc55cSDimitry Andric 2236349cc55cSDimitry Andric Environment GetEnvironment() { 2237349cc55cSDimitry Andric Environment environment; 2238349cc55cSDimitry Andric for (int i = 0; i < GetNumberOfFields(); i++) { 2239349cc55cSDimitry Andric environment.insert( 2240349cc55cSDimitry Andric std::make_pair(GetField(i).GetName(), GetField(i).GetValue())); 2241349cc55cSDimitry Andric } 2242349cc55cSDimitry Andric return environment; 2243349cc55cSDimitry Andric } 2244349cc55cSDimitry Andric 2245349cc55cSDimitry Andric void AddEnvironmentVariables(const Environment &environment) { 2246349cc55cSDimitry Andric for (auto &variable : environment) { 2247349cc55cSDimitry Andric AddNewField(); 2248349cc55cSDimitry Andric EnvironmentVariableFieldDelegate &field = 2249349cc55cSDimitry Andric GetField(GetNumberOfFields() - 1); 2250349cc55cSDimitry Andric field.SetName(variable.getKey().str().c_str()); 2251349cc55cSDimitry Andric field.SetValue(variable.getValue().c_str()); 2252349cc55cSDimitry Andric } 2253349cc55cSDimitry Andric } 2254349cc55cSDimitry Andric }; 2255349cc55cSDimitry Andric 2256fe6060f1SDimitry Andric class FormAction { 2257fe6060f1SDimitry Andric public: 2258fe6060f1SDimitry Andric FormAction(const char *label, std::function<void(Window &)> action) 2259fe6060f1SDimitry Andric : m_action(action) { 2260fe6060f1SDimitry Andric if (label) 2261fe6060f1SDimitry Andric m_label = label; 2262fe6060f1SDimitry Andric } 2263fe6060f1SDimitry Andric 2264fe6060f1SDimitry Andric // Draw a centered [Label]. 2265349cc55cSDimitry Andric void Draw(Surface &surface, bool is_selected) { 2266fe6060f1SDimitry Andric int x = (surface.GetWidth() - m_label.length()) / 2; 2267fe6060f1SDimitry Andric surface.MoveCursor(x, 0); 2268fe6060f1SDimitry Andric if (is_selected) 2269fe6060f1SDimitry Andric surface.AttributeOn(A_REVERSE); 2270fe6060f1SDimitry Andric surface.PutChar('['); 2271fe6060f1SDimitry Andric surface.PutCString(m_label.c_str()); 2272fe6060f1SDimitry Andric surface.PutChar(']'); 2273fe6060f1SDimitry Andric if (is_selected) 2274fe6060f1SDimitry Andric surface.AttributeOff(A_REVERSE); 2275fe6060f1SDimitry Andric } 2276fe6060f1SDimitry Andric 2277fe6060f1SDimitry Andric void Execute(Window &window) { m_action(window); } 2278fe6060f1SDimitry Andric 2279fe6060f1SDimitry Andric const std::string &GetLabel() { return m_label; } 2280fe6060f1SDimitry Andric 2281fe6060f1SDimitry Andric protected: 2282fe6060f1SDimitry Andric std::string m_label; 2283fe6060f1SDimitry Andric std::function<void(Window &)> m_action; 2284fe6060f1SDimitry Andric }; 2285fe6060f1SDimitry Andric 2286fe6060f1SDimitry Andric class FormDelegate { 2287fe6060f1SDimitry Andric public: 228881ad6265SDimitry Andric FormDelegate() = default; 2289fe6060f1SDimitry Andric 2290fe6060f1SDimitry Andric virtual ~FormDelegate() = default; 2291fe6060f1SDimitry Andric 2292fe6060f1SDimitry Andric virtual std::string GetName() = 0; 2293fe6060f1SDimitry Andric 22940eae32dcSDimitry Andric virtual void UpdateFieldsVisibility() {} 2295fe6060f1SDimitry Andric 2296fe6060f1SDimitry Andric FieldDelegate *GetField(uint32_t field_index) { 2297fe6060f1SDimitry Andric if (field_index < m_fields.size()) 2298fe6060f1SDimitry Andric return m_fields[field_index].get(); 2299fe6060f1SDimitry Andric return nullptr; 2300fe6060f1SDimitry Andric } 2301fe6060f1SDimitry Andric 2302fe6060f1SDimitry Andric FormAction &GetAction(int action_index) { return m_actions[action_index]; } 2303fe6060f1SDimitry Andric 2304fe6060f1SDimitry Andric int GetNumberOfFields() { return m_fields.size(); } 2305fe6060f1SDimitry Andric 2306fe6060f1SDimitry Andric int GetNumberOfActions() { return m_actions.size(); } 2307fe6060f1SDimitry Andric 2308fe6060f1SDimitry Andric bool HasError() { return !m_error.empty(); } 2309fe6060f1SDimitry Andric 2310fe6060f1SDimitry Andric void ClearError() { m_error.clear(); } 2311fe6060f1SDimitry Andric 2312fe6060f1SDimitry Andric const std::string &GetError() { return m_error; } 2313fe6060f1SDimitry Andric 2314fe6060f1SDimitry Andric void SetError(const char *error) { m_error = error; } 2315fe6060f1SDimitry Andric 2316fe6060f1SDimitry Andric // If all fields are valid, true is returned. Otherwise, an error message is 2317fe6060f1SDimitry Andric // set and false is returned. This method is usually called at the start of an 2318fe6060f1SDimitry Andric // action that requires valid fields. 2319fe6060f1SDimitry Andric bool CheckFieldsValidity() { 2320fe6060f1SDimitry Andric for (int i = 0; i < GetNumberOfFields(); i++) { 2321349cc55cSDimitry Andric GetField(i)->FieldDelegateExitCallback(); 2322fe6060f1SDimitry Andric if (GetField(i)->FieldDelegateHasError()) { 2323fe6060f1SDimitry Andric SetError("Some fields are invalid!"); 2324fe6060f1SDimitry Andric return false; 2325fe6060f1SDimitry Andric } 2326fe6060f1SDimitry Andric } 2327fe6060f1SDimitry Andric return true; 2328fe6060f1SDimitry Andric } 2329fe6060f1SDimitry Andric 2330fe6060f1SDimitry Andric // Factory methods to create and add fields of specific types. 2331fe6060f1SDimitry Andric 2332fe6060f1SDimitry Andric TextFieldDelegate *AddTextField(const char *label, const char *content, 2333fe6060f1SDimitry Andric bool required) { 2334fe6060f1SDimitry Andric TextFieldDelegate *delegate = 2335fe6060f1SDimitry Andric new TextFieldDelegate(label, content, required); 2336fe6060f1SDimitry Andric m_fields.push_back(FieldDelegateUP(delegate)); 2337fe6060f1SDimitry Andric return delegate; 2338fe6060f1SDimitry Andric } 2339fe6060f1SDimitry Andric 2340fe6060f1SDimitry Andric FileFieldDelegate *AddFileField(const char *label, const char *content, 2341fe6060f1SDimitry Andric bool need_to_exist, bool required) { 2342fe6060f1SDimitry Andric FileFieldDelegate *delegate = 2343fe6060f1SDimitry Andric new FileFieldDelegate(label, content, need_to_exist, required); 2344fe6060f1SDimitry Andric m_fields.push_back(FieldDelegateUP(delegate)); 2345fe6060f1SDimitry Andric return delegate; 2346fe6060f1SDimitry Andric } 2347fe6060f1SDimitry Andric 2348fe6060f1SDimitry Andric DirectoryFieldDelegate *AddDirectoryField(const char *label, 2349fe6060f1SDimitry Andric const char *content, 2350fe6060f1SDimitry Andric bool need_to_exist, bool required) { 2351fe6060f1SDimitry Andric DirectoryFieldDelegate *delegate = 2352fe6060f1SDimitry Andric new DirectoryFieldDelegate(label, content, need_to_exist, required); 2353fe6060f1SDimitry Andric m_fields.push_back(FieldDelegateUP(delegate)); 2354fe6060f1SDimitry Andric return delegate; 2355fe6060f1SDimitry Andric } 2356fe6060f1SDimitry Andric 2357fe6060f1SDimitry Andric ArchFieldDelegate *AddArchField(const char *label, const char *content, 2358fe6060f1SDimitry Andric bool required) { 2359fe6060f1SDimitry Andric ArchFieldDelegate *delegate = 2360fe6060f1SDimitry Andric new ArchFieldDelegate(label, content, required); 2361fe6060f1SDimitry Andric m_fields.push_back(FieldDelegateUP(delegate)); 2362fe6060f1SDimitry Andric return delegate; 2363fe6060f1SDimitry Andric } 2364fe6060f1SDimitry Andric 2365fe6060f1SDimitry Andric IntegerFieldDelegate *AddIntegerField(const char *label, int content, 2366fe6060f1SDimitry Andric bool required) { 2367fe6060f1SDimitry Andric IntegerFieldDelegate *delegate = 2368fe6060f1SDimitry Andric new IntegerFieldDelegate(label, content, required); 2369fe6060f1SDimitry Andric m_fields.push_back(FieldDelegateUP(delegate)); 2370fe6060f1SDimitry Andric return delegate; 2371fe6060f1SDimitry Andric } 2372fe6060f1SDimitry Andric 2373fe6060f1SDimitry Andric BooleanFieldDelegate *AddBooleanField(const char *label, bool content) { 2374fe6060f1SDimitry Andric BooleanFieldDelegate *delegate = new BooleanFieldDelegate(label, content); 2375fe6060f1SDimitry Andric m_fields.push_back(FieldDelegateUP(delegate)); 2376fe6060f1SDimitry Andric return delegate; 2377fe6060f1SDimitry Andric } 2378fe6060f1SDimitry Andric 2379349cc55cSDimitry Andric LazyBooleanFieldDelegate *AddLazyBooleanField(const char *label, 2380349cc55cSDimitry Andric const char *calculate_label) { 2381349cc55cSDimitry Andric LazyBooleanFieldDelegate *delegate = 2382349cc55cSDimitry Andric new LazyBooleanFieldDelegate(label, calculate_label); 2383349cc55cSDimitry Andric m_fields.push_back(FieldDelegateUP(delegate)); 2384349cc55cSDimitry Andric return delegate; 2385349cc55cSDimitry Andric } 2386349cc55cSDimitry Andric 2387fe6060f1SDimitry Andric ChoicesFieldDelegate *AddChoicesField(const char *label, int height, 2388fe6060f1SDimitry Andric std::vector<std::string> choices) { 2389fe6060f1SDimitry Andric ChoicesFieldDelegate *delegate = 2390fe6060f1SDimitry Andric new ChoicesFieldDelegate(label, height, choices); 2391fe6060f1SDimitry Andric m_fields.push_back(FieldDelegateUP(delegate)); 2392fe6060f1SDimitry Andric return delegate; 2393fe6060f1SDimitry Andric } 2394fe6060f1SDimitry Andric 2395fe6060f1SDimitry Andric PlatformPluginFieldDelegate *AddPlatformPluginField(Debugger &debugger) { 2396fe6060f1SDimitry Andric PlatformPluginFieldDelegate *delegate = 2397fe6060f1SDimitry Andric new PlatformPluginFieldDelegate(debugger); 2398fe6060f1SDimitry Andric m_fields.push_back(FieldDelegateUP(delegate)); 2399fe6060f1SDimitry Andric return delegate; 2400fe6060f1SDimitry Andric } 2401fe6060f1SDimitry Andric 2402fe6060f1SDimitry Andric ProcessPluginFieldDelegate *AddProcessPluginField() { 2403fe6060f1SDimitry Andric ProcessPluginFieldDelegate *delegate = new ProcessPluginFieldDelegate(); 2404fe6060f1SDimitry Andric m_fields.push_back(FieldDelegateUP(delegate)); 2405fe6060f1SDimitry Andric return delegate; 2406fe6060f1SDimitry Andric } 2407fe6060f1SDimitry Andric 2408fe6060f1SDimitry Andric template <class T> 2409fe6060f1SDimitry Andric ListFieldDelegate<T> *AddListField(const char *label, T default_field) { 2410fe6060f1SDimitry Andric ListFieldDelegate<T> *delegate = 2411fe6060f1SDimitry Andric new ListFieldDelegate<T>(label, default_field); 2412fe6060f1SDimitry Andric m_fields.push_back(FieldDelegateUP(delegate)); 2413fe6060f1SDimitry Andric return delegate; 2414fe6060f1SDimitry Andric } 2415fe6060f1SDimitry Andric 2416349cc55cSDimitry Andric ArgumentsFieldDelegate *AddArgumentsField() { 2417349cc55cSDimitry Andric ArgumentsFieldDelegate *delegate = new ArgumentsFieldDelegate(); 2418349cc55cSDimitry Andric m_fields.push_back(FieldDelegateUP(delegate)); 2419349cc55cSDimitry Andric return delegate; 2420349cc55cSDimitry Andric } 2421349cc55cSDimitry Andric 2422349cc55cSDimitry Andric template <class K, class V> 2423349cc55cSDimitry Andric MappingFieldDelegate<K, V> *AddMappingField(K key_field, V value_field) { 2424349cc55cSDimitry Andric MappingFieldDelegate<K, V> *delegate = 2425349cc55cSDimitry Andric new MappingFieldDelegate<K, V>(key_field, value_field); 2426349cc55cSDimitry Andric m_fields.push_back(FieldDelegateUP(delegate)); 2427349cc55cSDimitry Andric return delegate; 2428349cc55cSDimitry Andric } 2429349cc55cSDimitry Andric 2430349cc55cSDimitry Andric EnvironmentVariableNameFieldDelegate * 2431349cc55cSDimitry Andric AddEnvironmentVariableNameField(const char *content) { 2432349cc55cSDimitry Andric EnvironmentVariableNameFieldDelegate *delegate = 2433349cc55cSDimitry Andric new EnvironmentVariableNameFieldDelegate(content); 2434349cc55cSDimitry Andric m_fields.push_back(FieldDelegateUP(delegate)); 2435349cc55cSDimitry Andric return delegate; 2436349cc55cSDimitry Andric } 2437349cc55cSDimitry Andric 2438349cc55cSDimitry Andric EnvironmentVariableFieldDelegate *AddEnvironmentVariableField() { 2439349cc55cSDimitry Andric EnvironmentVariableFieldDelegate *delegate = 2440349cc55cSDimitry Andric new EnvironmentVariableFieldDelegate(); 2441349cc55cSDimitry Andric m_fields.push_back(FieldDelegateUP(delegate)); 2442349cc55cSDimitry Andric return delegate; 2443349cc55cSDimitry Andric } 2444349cc55cSDimitry Andric 2445349cc55cSDimitry Andric EnvironmentVariableListFieldDelegate * 2446349cc55cSDimitry Andric AddEnvironmentVariableListField(const char *label) { 2447349cc55cSDimitry Andric EnvironmentVariableListFieldDelegate *delegate = 2448349cc55cSDimitry Andric new EnvironmentVariableListFieldDelegate(label); 2449349cc55cSDimitry Andric m_fields.push_back(FieldDelegateUP(delegate)); 2450349cc55cSDimitry Andric return delegate; 2451349cc55cSDimitry Andric } 2452349cc55cSDimitry Andric 2453fe6060f1SDimitry Andric // Factory methods for adding actions. 2454fe6060f1SDimitry Andric 2455fe6060f1SDimitry Andric void AddAction(const char *label, std::function<void(Window &)> action) { 2456fe6060f1SDimitry Andric m_actions.push_back(FormAction(label, action)); 2457fe6060f1SDimitry Andric } 2458fe6060f1SDimitry Andric 2459fe6060f1SDimitry Andric protected: 2460fe6060f1SDimitry Andric std::vector<FieldDelegateUP> m_fields; 2461fe6060f1SDimitry Andric std::vector<FormAction> m_actions; 2462fe6060f1SDimitry Andric // Optional error message. If empty, form is considered to have no error. 2463fe6060f1SDimitry Andric std::string m_error; 2464fe6060f1SDimitry Andric }; 2465fe6060f1SDimitry Andric 2466fe6060f1SDimitry Andric typedef std::shared_ptr<FormDelegate> FormDelegateSP; 2467fe6060f1SDimitry Andric 2468fe6060f1SDimitry Andric class FormWindowDelegate : public WindowDelegate { 2469fe6060f1SDimitry Andric public: 247081ad6265SDimitry Andric FormWindowDelegate(FormDelegateSP &delegate_sp) : m_delegate_sp(delegate_sp) { 2471fe6060f1SDimitry Andric assert(m_delegate_sp->GetNumberOfActions() > 0); 2472fe6060f1SDimitry Andric if (m_delegate_sp->GetNumberOfFields() > 0) 2473fe6060f1SDimitry Andric m_selection_type = SelectionType::Field; 2474fe6060f1SDimitry Andric else 2475fe6060f1SDimitry Andric m_selection_type = SelectionType::Action; 2476fe6060f1SDimitry Andric } 2477fe6060f1SDimitry Andric 2478fe6060f1SDimitry Andric // Signify which element is selected. If a field or an action is selected, 2479fe6060f1SDimitry Andric // then m_selection_index signifies the particular field or action that is 2480fe6060f1SDimitry Andric // selected. 2481fe6060f1SDimitry Andric enum class SelectionType { Field, Action }; 2482fe6060f1SDimitry Andric 2483fe6060f1SDimitry Andric // A form window is padded by one character from all sides. First, if an error 2484fe6060f1SDimitry Andric // message exists, it is drawn followed by a separator. Then one or more 2485fe6060f1SDimitry Andric // fields are drawn. Finally, all available actions are drawn on a single 2486fe6060f1SDimitry Andric // line. 2487fe6060f1SDimitry Andric // 2488fe6060f1SDimitry Andric // ___<Form Name>_________________________________________________ 2489fe6060f1SDimitry Andric // | | 2490fe6060f1SDimitry Andric // | - Error message if it exists. | 2491fe6060f1SDimitry Andric // |-------------------------------------------------------------| 2492fe6060f1SDimitry Andric // | Form elements here. | 2493fe6060f1SDimitry Andric // | Form actions here. | 2494fe6060f1SDimitry Andric // | | 2495fe6060f1SDimitry Andric // |______________________________________[Press Esc to cancel]__| 2496fe6060f1SDimitry Andric // 2497fe6060f1SDimitry Andric 2498fe6060f1SDimitry Andric // One line for the error and another for the horizontal line. 2499fe6060f1SDimitry Andric int GetErrorHeight() { 2500fe6060f1SDimitry Andric if (m_delegate_sp->HasError()) 2501fe6060f1SDimitry Andric return 2; 2502fe6060f1SDimitry Andric return 0; 2503fe6060f1SDimitry Andric } 2504fe6060f1SDimitry Andric 2505fe6060f1SDimitry Andric // Actions span a single line. 2506fe6060f1SDimitry Andric int GetActionsHeight() { 2507fe6060f1SDimitry Andric if (m_delegate_sp->GetNumberOfActions() > 0) 2508fe6060f1SDimitry Andric return 1; 2509fe6060f1SDimitry Andric return 0; 2510fe6060f1SDimitry Andric } 2511fe6060f1SDimitry Andric 2512fe6060f1SDimitry Andric // Get the total number of needed lines to draw the contents. 2513fe6060f1SDimitry Andric int GetContentHeight() { 2514fe6060f1SDimitry Andric int height = 0; 2515fe6060f1SDimitry Andric height += GetErrorHeight(); 2516fe6060f1SDimitry Andric for (int i = 0; i < m_delegate_sp->GetNumberOfFields(); i++) { 2517fe6060f1SDimitry Andric if (!m_delegate_sp->GetField(i)->FieldDelegateIsVisible()) 2518fe6060f1SDimitry Andric continue; 2519fe6060f1SDimitry Andric height += m_delegate_sp->GetField(i)->FieldDelegateGetHeight(); 2520fe6060f1SDimitry Andric } 2521fe6060f1SDimitry Andric height += GetActionsHeight(); 2522fe6060f1SDimitry Andric return height; 2523fe6060f1SDimitry Andric } 2524fe6060f1SDimitry Andric 2525fe6060f1SDimitry Andric ScrollContext GetScrollContext() { 2526fe6060f1SDimitry Andric if (m_selection_type == SelectionType::Action) 2527fe6060f1SDimitry Andric return ScrollContext(GetContentHeight() - 1); 2528fe6060f1SDimitry Andric 2529fe6060f1SDimitry Andric FieldDelegate *field = m_delegate_sp->GetField(m_selection_index); 2530fe6060f1SDimitry Andric ScrollContext context = field->FieldDelegateGetScrollContext(); 2531fe6060f1SDimitry Andric 2532fe6060f1SDimitry Andric int offset = GetErrorHeight(); 2533fe6060f1SDimitry Andric for (int i = 0; i < m_selection_index; i++) { 2534fe6060f1SDimitry Andric if (!m_delegate_sp->GetField(i)->FieldDelegateIsVisible()) 2535fe6060f1SDimitry Andric continue; 2536fe6060f1SDimitry Andric offset += m_delegate_sp->GetField(i)->FieldDelegateGetHeight(); 2537fe6060f1SDimitry Andric } 2538fe6060f1SDimitry Andric context.Offset(offset); 2539fe6060f1SDimitry Andric 2540fe6060f1SDimitry Andric // If the context is touching the error, include the error in the context as 2541fe6060f1SDimitry Andric // well. 2542fe6060f1SDimitry Andric if (context.start == GetErrorHeight()) 2543fe6060f1SDimitry Andric context.start = 0; 2544fe6060f1SDimitry Andric 2545fe6060f1SDimitry Andric return context; 2546fe6060f1SDimitry Andric } 2547fe6060f1SDimitry Andric 2548349cc55cSDimitry Andric void UpdateScrolling(Surface &surface) { 2549fe6060f1SDimitry Andric ScrollContext context = GetScrollContext(); 2550fe6060f1SDimitry Andric int content_height = GetContentHeight(); 2551fe6060f1SDimitry Andric int surface_height = surface.GetHeight(); 2552fe6060f1SDimitry Andric int visible_height = std::min(content_height, surface_height); 2553fe6060f1SDimitry Andric int last_visible_line = m_first_visible_line + visible_height - 1; 2554fe6060f1SDimitry Andric 2555fe6060f1SDimitry Andric // If the last visible line is bigger than the content, then it is invalid 2556fe6060f1SDimitry Andric // and needs to be set to the last line in the content. This can happen when 2557fe6060f1SDimitry Andric // a field has shrunk in height. 2558fe6060f1SDimitry Andric if (last_visible_line > content_height - 1) { 2559fe6060f1SDimitry Andric m_first_visible_line = content_height - visible_height; 2560fe6060f1SDimitry Andric } 2561fe6060f1SDimitry Andric 2562fe6060f1SDimitry Andric if (context.start < m_first_visible_line) { 2563fe6060f1SDimitry Andric m_first_visible_line = context.start; 2564fe6060f1SDimitry Andric return; 2565fe6060f1SDimitry Andric } 2566fe6060f1SDimitry Andric 2567fe6060f1SDimitry Andric if (context.end > last_visible_line) { 2568fe6060f1SDimitry Andric m_first_visible_line = context.end - visible_height + 1; 2569fe6060f1SDimitry Andric } 2570fe6060f1SDimitry Andric } 2571fe6060f1SDimitry Andric 2572349cc55cSDimitry Andric void DrawError(Surface &surface) { 2573fe6060f1SDimitry Andric if (!m_delegate_sp->HasError()) 2574fe6060f1SDimitry Andric return; 2575fe6060f1SDimitry Andric surface.MoveCursor(0, 0); 2576fe6060f1SDimitry Andric surface.AttributeOn(COLOR_PAIR(RedOnBlack)); 2577fe6060f1SDimitry Andric surface.PutChar(ACS_DIAMOND); 2578fe6060f1SDimitry Andric surface.PutChar(' '); 2579fe6060f1SDimitry Andric surface.PutCStringTruncated(1, m_delegate_sp->GetError().c_str()); 2580fe6060f1SDimitry Andric surface.AttributeOff(COLOR_PAIR(RedOnBlack)); 2581fe6060f1SDimitry Andric 2582fe6060f1SDimitry Andric surface.MoveCursor(0, 1); 2583fe6060f1SDimitry Andric surface.HorizontalLine(surface.GetWidth()); 2584fe6060f1SDimitry Andric } 2585fe6060f1SDimitry Andric 2586349cc55cSDimitry Andric void DrawFields(Surface &surface) { 2587fe6060f1SDimitry Andric int line = 0; 2588fe6060f1SDimitry Andric int width = surface.GetWidth(); 2589fe6060f1SDimitry Andric bool a_field_is_selected = m_selection_type == SelectionType::Field; 2590fe6060f1SDimitry Andric for (int i = 0; i < m_delegate_sp->GetNumberOfFields(); i++) { 2591fe6060f1SDimitry Andric FieldDelegate *field = m_delegate_sp->GetField(i); 2592fe6060f1SDimitry Andric if (!field->FieldDelegateIsVisible()) 2593fe6060f1SDimitry Andric continue; 2594fe6060f1SDimitry Andric bool is_field_selected = a_field_is_selected && m_selection_index == i; 2595fe6060f1SDimitry Andric int height = field->FieldDelegateGetHeight(); 2596fe6060f1SDimitry Andric Rect bounds = Rect(Point(0, line), Size(width, height)); 2597349cc55cSDimitry Andric Surface field_surface = surface.SubSurface(bounds); 2598fe6060f1SDimitry Andric field->FieldDelegateDraw(field_surface, is_field_selected); 2599fe6060f1SDimitry Andric line += height; 2600fe6060f1SDimitry Andric } 2601fe6060f1SDimitry Andric } 2602fe6060f1SDimitry Andric 2603349cc55cSDimitry Andric void DrawActions(Surface &surface) { 2604fe6060f1SDimitry Andric int number_of_actions = m_delegate_sp->GetNumberOfActions(); 2605fe6060f1SDimitry Andric int width = surface.GetWidth() / number_of_actions; 2606fe6060f1SDimitry Andric bool an_action_is_selected = m_selection_type == SelectionType::Action; 2607fe6060f1SDimitry Andric int x = 0; 2608fe6060f1SDimitry Andric for (int i = 0; i < number_of_actions; i++) { 2609fe6060f1SDimitry Andric bool is_action_selected = an_action_is_selected && m_selection_index == i; 2610fe6060f1SDimitry Andric FormAction &action = m_delegate_sp->GetAction(i); 2611fe6060f1SDimitry Andric Rect bounds = Rect(Point(x, 0), Size(width, 1)); 2612349cc55cSDimitry Andric Surface action_surface = surface.SubSurface(bounds); 2613fe6060f1SDimitry Andric action.Draw(action_surface, is_action_selected); 2614fe6060f1SDimitry Andric x += width; 2615fe6060f1SDimitry Andric } 2616fe6060f1SDimitry Andric } 2617fe6060f1SDimitry Andric 2618349cc55cSDimitry Andric void DrawElements(Surface &surface) { 2619fe6060f1SDimitry Andric Rect frame = surface.GetFrame(); 2620fe6060f1SDimitry Andric Rect fields_bounds, actions_bounds; 2621fe6060f1SDimitry Andric frame.HorizontalSplit(surface.GetHeight() - GetActionsHeight(), 2622fe6060f1SDimitry Andric fields_bounds, actions_bounds); 2623349cc55cSDimitry Andric Surface fields_surface = surface.SubSurface(fields_bounds); 2624349cc55cSDimitry Andric Surface actions_surface = surface.SubSurface(actions_bounds); 2625fe6060f1SDimitry Andric 2626fe6060f1SDimitry Andric DrawFields(fields_surface); 2627fe6060f1SDimitry Andric DrawActions(actions_surface); 2628fe6060f1SDimitry Andric } 2629fe6060f1SDimitry Andric 2630fe6060f1SDimitry Andric // Contents are first drawn on a pad. Then a subset of that pad is copied to 2631fe6060f1SDimitry Andric // the derived window starting at the first visible line. This essentially 2632fe6060f1SDimitry Andric // provides scrolling functionality. 2633349cc55cSDimitry Andric void DrawContent(Surface &surface) { 2634fe6060f1SDimitry Andric UpdateScrolling(surface); 2635fe6060f1SDimitry Andric 2636fe6060f1SDimitry Andric int width = surface.GetWidth(); 2637fe6060f1SDimitry Andric int height = GetContentHeight(); 2638fe6060f1SDimitry Andric Pad pad = Pad(Size(width, height)); 2639fe6060f1SDimitry Andric 2640fe6060f1SDimitry Andric Rect frame = pad.GetFrame(); 2641fe6060f1SDimitry Andric Rect error_bounds, elements_bounds; 2642fe6060f1SDimitry Andric frame.HorizontalSplit(GetErrorHeight(), error_bounds, elements_bounds); 2643349cc55cSDimitry Andric Surface error_surface = pad.SubSurface(error_bounds); 2644349cc55cSDimitry Andric Surface elements_surface = pad.SubSurface(elements_bounds); 2645fe6060f1SDimitry Andric 2646fe6060f1SDimitry Andric DrawError(error_surface); 2647fe6060f1SDimitry Andric DrawElements(elements_surface); 2648fe6060f1SDimitry Andric 2649fe6060f1SDimitry Andric int copy_height = std::min(surface.GetHeight(), pad.GetHeight()); 2650fe6060f1SDimitry Andric pad.CopyToSurface(surface, Point(0, m_first_visible_line), Point(), 2651fe6060f1SDimitry Andric Size(width, copy_height)); 2652fe6060f1SDimitry Andric } 2653fe6060f1SDimitry Andric 2654349cc55cSDimitry Andric void DrawSubmitHint(Surface &surface, bool is_active) { 2655349cc55cSDimitry Andric surface.MoveCursor(2, surface.GetHeight() - 1); 2656349cc55cSDimitry Andric if (is_active) 2657349cc55cSDimitry Andric surface.AttributeOn(A_BOLD | COLOR_PAIR(BlackOnWhite)); 2658349cc55cSDimitry Andric surface.Printf("[Press Alt+Enter to %s]", 2659349cc55cSDimitry Andric m_delegate_sp->GetAction(0).GetLabel().c_str()); 2660349cc55cSDimitry Andric if (is_active) 2661349cc55cSDimitry Andric surface.AttributeOff(A_BOLD | COLOR_PAIR(BlackOnWhite)); 2662349cc55cSDimitry Andric } 2663349cc55cSDimitry Andric 2664fe6060f1SDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override { 2665fe6060f1SDimitry Andric m_delegate_sp->UpdateFieldsVisibility(); 2666fe6060f1SDimitry Andric 2667fe6060f1SDimitry Andric window.Erase(); 2668fe6060f1SDimitry Andric 2669fe6060f1SDimitry Andric window.DrawTitleBox(m_delegate_sp->GetName().c_str(), 2670349cc55cSDimitry Andric "Press Esc to Cancel"); 2671349cc55cSDimitry Andric DrawSubmitHint(window, window.IsActive()); 2672fe6060f1SDimitry Andric 2673fe6060f1SDimitry Andric Rect content_bounds = window.GetFrame(); 2674fe6060f1SDimitry Andric content_bounds.Inset(2, 2); 2675349cc55cSDimitry Andric Surface content_surface = window.SubSurface(content_bounds); 2676fe6060f1SDimitry Andric 2677fe6060f1SDimitry Andric DrawContent(content_surface); 2678fe6060f1SDimitry Andric return true; 2679fe6060f1SDimitry Andric } 2680fe6060f1SDimitry Andric 2681fe6060f1SDimitry Andric void SkipNextHiddenFields() { 2682fe6060f1SDimitry Andric while (true) { 2683fe6060f1SDimitry Andric if (m_delegate_sp->GetField(m_selection_index)->FieldDelegateIsVisible()) 2684fe6060f1SDimitry Andric return; 2685fe6060f1SDimitry Andric 2686fe6060f1SDimitry Andric if (m_selection_index == m_delegate_sp->GetNumberOfFields() - 1) { 2687fe6060f1SDimitry Andric m_selection_type = SelectionType::Action; 2688fe6060f1SDimitry Andric m_selection_index = 0; 2689fe6060f1SDimitry Andric return; 2690fe6060f1SDimitry Andric } 2691fe6060f1SDimitry Andric 2692fe6060f1SDimitry Andric m_selection_index++; 2693fe6060f1SDimitry Andric } 2694fe6060f1SDimitry Andric } 2695fe6060f1SDimitry Andric 2696fe6060f1SDimitry Andric HandleCharResult SelectNext(int key) { 2697fe6060f1SDimitry Andric if (m_selection_type == SelectionType::Action) { 2698fe6060f1SDimitry Andric if (m_selection_index < m_delegate_sp->GetNumberOfActions() - 1) { 2699fe6060f1SDimitry Andric m_selection_index++; 2700fe6060f1SDimitry Andric return eKeyHandled; 2701fe6060f1SDimitry Andric } 2702fe6060f1SDimitry Andric 2703fe6060f1SDimitry Andric m_selection_index = 0; 2704fe6060f1SDimitry Andric m_selection_type = SelectionType::Field; 2705fe6060f1SDimitry Andric SkipNextHiddenFields(); 2706fe6060f1SDimitry Andric if (m_selection_type == SelectionType::Field) { 2707fe6060f1SDimitry Andric FieldDelegate *next_field = m_delegate_sp->GetField(m_selection_index); 2708fe6060f1SDimitry Andric next_field->FieldDelegateSelectFirstElement(); 2709fe6060f1SDimitry Andric } 2710fe6060f1SDimitry Andric return eKeyHandled; 2711fe6060f1SDimitry Andric } 2712fe6060f1SDimitry Andric 2713fe6060f1SDimitry Andric FieldDelegate *field = m_delegate_sp->GetField(m_selection_index); 2714fe6060f1SDimitry Andric if (!field->FieldDelegateOnLastOrOnlyElement()) { 2715fe6060f1SDimitry Andric return field->FieldDelegateHandleChar(key); 2716fe6060f1SDimitry Andric } 2717fe6060f1SDimitry Andric 2718fe6060f1SDimitry Andric field->FieldDelegateExitCallback(); 2719fe6060f1SDimitry Andric 2720fe6060f1SDimitry Andric if (m_selection_index == m_delegate_sp->GetNumberOfFields() - 1) { 2721fe6060f1SDimitry Andric m_selection_type = SelectionType::Action; 2722fe6060f1SDimitry Andric m_selection_index = 0; 2723fe6060f1SDimitry Andric return eKeyHandled; 2724fe6060f1SDimitry Andric } 2725fe6060f1SDimitry Andric 2726fe6060f1SDimitry Andric m_selection_index++; 2727fe6060f1SDimitry Andric SkipNextHiddenFields(); 2728fe6060f1SDimitry Andric 2729fe6060f1SDimitry Andric if (m_selection_type == SelectionType::Field) { 2730fe6060f1SDimitry Andric FieldDelegate *next_field = m_delegate_sp->GetField(m_selection_index); 2731fe6060f1SDimitry Andric next_field->FieldDelegateSelectFirstElement(); 2732fe6060f1SDimitry Andric } 2733fe6060f1SDimitry Andric 2734fe6060f1SDimitry Andric return eKeyHandled; 2735fe6060f1SDimitry Andric } 2736fe6060f1SDimitry Andric 2737fe6060f1SDimitry Andric void SkipPreviousHiddenFields() { 2738fe6060f1SDimitry Andric while (true) { 2739fe6060f1SDimitry Andric if (m_delegate_sp->GetField(m_selection_index)->FieldDelegateIsVisible()) 2740fe6060f1SDimitry Andric return; 2741fe6060f1SDimitry Andric 2742fe6060f1SDimitry Andric if (m_selection_index == 0) { 2743fe6060f1SDimitry Andric m_selection_type = SelectionType::Action; 2744fe6060f1SDimitry Andric m_selection_index = 0; 2745fe6060f1SDimitry Andric return; 2746fe6060f1SDimitry Andric } 2747fe6060f1SDimitry Andric 2748fe6060f1SDimitry Andric m_selection_index--; 2749fe6060f1SDimitry Andric } 2750fe6060f1SDimitry Andric } 2751fe6060f1SDimitry Andric 2752fe6060f1SDimitry Andric HandleCharResult SelectPrevious(int key) { 2753fe6060f1SDimitry Andric if (m_selection_type == SelectionType::Action) { 2754fe6060f1SDimitry Andric if (m_selection_index > 0) { 2755fe6060f1SDimitry Andric m_selection_index--; 2756fe6060f1SDimitry Andric return eKeyHandled; 2757fe6060f1SDimitry Andric } 2758fe6060f1SDimitry Andric m_selection_index = m_delegate_sp->GetNumberOfFields() - 1; 2759fe6060f1SDimitry Andric m_selection_type = SelectionType::Field; 2760fe6060f1SDimitry Andric SkipPreviousHiddenFields(); 2761fe6060f1SDimitry Andric if (m_selection_type == SelectionType::Field) { 2762fe6060f1SDimitry Andric FieldDelegate *previous_field = 2763fe6060f1SDimitry Andric m_delegate_sp->GetField(m_selection_index); 2764fe6060f1SDimitry Andric previous_field->FieldDelegateSelectLastElement(); 2765fe6060f1SDimitry Andric } 2766fe6060f1SDimitry Andric return eKeyHandled; 2767fe6060f1SDimitry Andric } 2768fe6060f1SDimitry Andric 2769fe6060f1SDimitry Andric FieldDelegate *field = m_delegate_sp->GetField(m_selection_index); 2770fe6060f1SDimitry Andric if (!field->FieldDelegateOnFirstOrOnlyElement()) { 2771fe6060f1SDimitry Andric return field->FieldDelegateHandleChar(key); 2772fe6060f1SDimitry Andric } 2773fe6060f1SDimitry Andric 2774fe6060f1SDimitry Andric field->FieldDelegateExitCallback(); 2775fe6060f1SDimitry Andric 2776fe6060f1SDimitry Andric if (m_selection_index == 0) { 2777fe6060f1SDimitry Andric m_selection_type = SelectionType::Action; 2778fe6060f1SDimitry Andric m_selection_index = m_delegate_sp->GetNumberOfActions() - 1; 2779fe6060f1SDimitry Andric return eKeyHandled; 2780fe6060f1SDimitry Andric } 2781fe6060f1SDimitry Andric 2782fe6060f1SDimitry Andric m_selection_index--; 2783fe6060f1SDimitry Andric SkipPreviousHiddenFields(); 2784fe6060f1SDimitry Andric 2785fe6060f1SDimitry Andric if (m_selection_type == SelectionType::Field) { 2786fe6060f1SDimitry Andric FieldDelegate *previous_field = 2787fe6060f1SDimitry Andric m_delegate_sp->GetField(m_selection_index); 2788fe6060f1SDimitry Andric previous_field->FieldDelegateSelectLastElement(); 2789fe6060f1SDimitry Andric } 2790fe6060f1SDimitry Andric 2791fe6060f1SDimitry Andric return eKeyHandled; 2792fe6060f1SDimitry Andric } 2793fe6060f1SDimitry Andric 2794349cc55cSDimitry Andric void ExecuteAction(Window &window, int index) { 2795349cc55cSDimitry Andric FormAction &action = m_delegate_sp->GetAction(index); 2796fe6060f1SDimitry Andric action.Execute(window); 2797fe6060f1SDimitry Andric if (m_delegate_sp->HasError()) { 2798fe6060f1SDimitry Andric m_first_visible_line = 0; 2799fe6060f1SDimitry Andric m_selection_index = 0; 2800fe6060f1SDimitry Andric m_selection_type = SelectionType::Field; 2801fe6060f1SDimitry Andric } 2802fe6060f1SDimitry Andric } 2803fe6060f1SDimitry Andric 2804349cc55cSDimitry Andric // Always return eKeyHandled to absorb all events since forms are always 2805349cc55cSDimitry Andric // added as pop-ups that should take full control until canceled or submitted. 2806fe6060f1SDimitry Andric HandleCharResult WindowDelegateHandleChar(Window &window, int key) override { 2807fe6060f1SDimitry Andric switch (key) { 2808fe6060f1SDimitry Andric case '\r': 2809fe6060f1SDimitry Andric case '\n': 2810fe6060f1SDimitry Andric case KEY_ENTER: 2811fe6060f1SDimitry Andric if (m_selection_type == SelectionType::Action) { 2812349cc55cSDimitry Andric ExecuteAction(window, m_selection_index); 2813fe6060f1SDimitry Andric return eKeyHandled; 2814fe6060f1SDimitry Andric } 2815fe6060f1SDimitry Andric break; 2816349cc55cSDimitry Andric case KEY_ALT_ENTER: 2817349cc55cSDimitry Andric ExecuteAction(window, 0); 2818349cc55cSDimitry Andric return eKeyHandled; 2819fe6060f1SDimitry Andric case '\t': 2820349cc55cSDimitry Andric SelectNext(key); 2821349cc55cSDimitry Andric return eKeyHandled; 2822fe6060f1SDimitry Andric case KEY_SHIFT_TAB: 2823349cc55cSDimitry Andric SelectPrevious(key); 2824349cc55cSDimitry Andric return eKeyHandled; 2825fe6060f1SDimitry Andric case KEY_ESCAPE: 2826fe6060f1SDimitry Andric window.GetParent()->RemoveSubWindow(&window); 2827fe6060f1SDimitry Andric return eKeyHandled; 2828fe6060f1SDimitry Andric default: 2829fe6060f1SDimitry Andric break; 2830fe6060f1SDimitry Andric } 2831fe6060f1SDimitry Andric 2832fe6060f1SDimitry Andric // If the key wasn't handled and one of the fields is selected, pass the key 2833fe6060f1SDimitry Andric // to that field. 2834fe6060f1SDimitry Andric if (m_selection_type == SelectionType::Field) { 2835fe6060f1SDimitry Andric FieldDelegate *field = m_delegate_sp->GetField(m_selection_index); 2836349cc55cSDimitry Andric if (field->FieldDelegateHandleChar(key) == eKeyHandled) 2837349cc55cSDimitry Andric return eKeyHandled; 2838fe6060f1SDimitry Andric } 2839fe6060f1SDimitry Andric 2840349cc55cSDimitry Andric // If the key wasn't handled by the possibly selected field, handle some 2841349cc55cSDimitry Andric // extra keys for navigation. 2842349cc55cSDimitry Andric switch (key) { 2843349cc55cSDimitry Andric case KEY_DOWN: 2844349cc55cSDimitry Andric SelectNext(key); 2845349cc55cSDimitry Andric return eKeyHandled; 2846349cc55cSDimitry Andric case KEY_UP: 2847349cc55cSDimitry Andric SelectPrevious(key); 2848349cc55cSDimitry Andric return eKeyHandled; 2849349cc55cSDimitry Andric default: 2850349cc55cSDimitry Andric break; 2851349cc55cSDimitry Andric } 2852349cc55cSDimitry Andric 2853349cc55cSDimitry Andric return eKeyHandled; 2854fe6060f1SDimitry Andric } 2855fe6060f1SDimitry Andric 2856fe6060f1SDimitry Andric protected: 2857fe6060f1SDimitry Andric FormDelegateSP m_delegate_sp; 2858fe6060f1SDimitry Andric // The index of the currently selected SelectionType. 285981ad6265SDimitry Andric int m_selection_index = 0; 2860fe6060f1SDimitry Andric // See SelectionType class enum. 2861fe6060f1SDimitry Andric SelectionType m_selection_type; 2862fe6060f1SDimitry Andric // The first visible line from the pad. 286381ad6265SDimitry Andric int m_first_visible_line = 0; 2864fe6060f1SDimitry Andric }; 2865fe6060f1SDimitry Andric 2866fe6060f1SDimitry Andric /////////////////////////// 2867fe6060f1SDimitry Andric // Form Delegate Instances 2868fe6060f1SDimitry Andric /////////////////////////// 2869fe6060f1SDimitry Andric 2870fe6060f1SDimitry Andric class DetachOrKillProcessFormDelegate : public FormDelegate { 2871fe6060f1SDimitry Andric public: 2872fe6060f1SDimitry Andric DetachOrKillProcessFormDelegate(Process *process) : m_process(process) { 2873fe6060f1SDimitry Andric SetError("There is a running process, either detach or kill it."); 2874fe6060f1SDimitry Andric 2875fe6060f1SDimitry Andric m_keep_stopped_field = 2876fe6060f1SDimitry Andric AddBooleanField("Keep process stopped when detaching.", false); 2877fe6060f1SDimitry Andric 2878fe6060f1SDimitry Andric AddAction("Detach", [this](Window &window) { Detach(window); }); 2879fe6060f1SDimitry Andric AddAction("Kill", [this](Window &window) { Kill(window); }); 2880fe6060f1SDimitry Andric } 2881fe6060f1SDimitry Andric 2882fe6060f1SDimitry Andric std::string GetName() override { return "Detach/Kill Process"; } 2883fe6060f1SDimitry Andric 2884fe6060f1SDimitry Andric void Kill(Window &window) { 2885fe6060f1SDimitry Andric Status destroy_status(m_process->Destroy(false)); 2886fe6060f1SDimitry Andric if (destroy_status.Fail()) { 2887fe6060f1SDimitry Andric SetError("Failed to kill process."); 2888fe6060f1SDimitry Andric return; 2889fe6060f1SDimitry Andric } 2890fe6060f1SDimitry Andric window.GetParent()->RemoveSubWindow(&window); 2891fe6060f1SDimitry Andric } 2892fe6060f1SDimitry Andric 2893fe6060f1SDimitry Andric void Detach(Window &window) { 2894fe6060f1SDimitry Andric Status detach_status(m_process->Detach(m_keep_stopped_field->GetBoolean())); 2895fe6060f1SDimitry Andric if (detach_status.Fail()) { 2896fe6060f1SDimitry Andric SetError("Failed to detach from process."); 2897fe6060f1SDimitry Andric return; 2898fe6060f1SDimitry Andric } 2899fe6060f1SDimitry Andric window.GetParent()->RemoveSubWindow(&window); 2900fe6060f1SDimitry Andric } 2901fe6060f1SDimitry Andric 2902fe6060f1SDimitry Andric protected: 2903fe6060f1SDimitry Andric Process *m_process; 2904fe6060f1SDimitry Andric BooleanFieldDelegate *m_keep_stopped_field; 2905fe6060f1SDimitry Andric }; 2906fe6060f1SDimitry Andric 2907fe6060f1SDimitry Andric class ProcessAttachFormDelegate : public FormDelegate { 2908fe6060f1SDimitry Andric public: 2909fe6060f1SDimitry Andric ProcessAttachFormDelegate(Debugger &debugger, WindowSP main_window_sp) 2910fe6060f1SDimitry Andric : m_debugger(debugger), m_main_window_sp(main_window_sp) { 2911fe6060f1SDimitry Andric std::vector<std::string> types; 2912fe6060f1SDimitry Andric types.push_back(std::string("Name")); 2913fe6060f1SDimitry Andric types.push_back(std::string("PID")); 2914fe6060f1SDimitry Andric m_type_field = AddChoicesField("Attach By", 2, types); 2915fe6060f1SDimitry Andric m_pid_field = AddIntegerField("PID", 0, true); 2916fe6060f1SDimitry Andric m_name_field = 2917fe6060f1SDimitry Andric AddTextField("Process Name", GetDefaultProcessName().c_str(), true); 2918fe6060f1SDimitry Andric m_continue_field = AddBooleanField("Continue once attached.", false); 2919fe6060f1SDimitry Andric m_wait_for_field = AddBooleanField("Wait for process to launch.", false); 2920fe6060f1SDimitry Andric m_include_existing_field = 2921fe6060f1SDimitry Andric AddBooleanField("Include existing processes.", false); 2922fe6060f1SDimitry Andric m_show_advanced_field = AddBooleanField("Show advanced settings.", false); 2923fe6060f1SDimitry Andric m_plugin_field = AddProcessPluginField(); 2924fe6060f1SDimitry Andric 2925fe6060f1SDimitry Andric AddAction("Attach", [this](Window &window) { Attach(window); }); 2926fe6060f1SDimitry Andric } 2927fe6060f1SDimitry Andric 2928fe6060f1SDimitry Andric std::string GetName() override { return "Attach Process"; } 2929fe6060f1SDimitry Andric 2930fe6060f1SDimitry Andric void UpdateFieldsVisibility() override { 2931fe6060f1SDimitry Andric if (m_type_field->GetChoiceContent() == "Name") { 2932fe6060f1SDimitry Andric m_pid_field->FieldDelegateHide(); 2933fe6060f1SDimitry Andric m_name_field->FieldDelegateShow(); 2934fe6060f1SDimitry Andric m_wait_for_field->FieldDelegateShow(); 2935fe6060f1SDimitry Andric if (m_wait_for_field->GetBoolean()) 2936fe6060f1SDimitry Andric m_include_existing_field->FieldDelegateShow(); 2937fe6060f1SDimitry Andric else 2938fe6060f1SDimitry Andric m_include_existing_field->FieldDelegateHide(); 2939fe6060f1SDimitry Andric } else { 2940fe6060f1SDimitry Andric m_pid_field->FieldDelegateShow(); 2941fe6060f1SDimitry Andric m_name_field->FieldDelegateHide(); 2942fe6060f1SDimitry Andric m_wait_for_field->FieldDelegateHide(); 2943fe6060f1SDimitry Andric m_include_existing_field->FieldDelegateHide(); 2944fe6060f1SDimitry Andric } 2945fe6060f1SDimitry Andric if (m_show_advanced_field->GetBoolean()) 2946fe6060f1SDimitry Andric m_plugin_field->FieldDelegateShow(); 2947fe6060f1SDimitry Andric else 2948fe6060f1SDimitry Andric m_plugin_field->FieldDelegateHide(); 2949fe6060f1SDimitry Andric } 2950fe6060f1SDimitry Andric 2951fe6060f1SDimitry Andric // Get the basename of the target's main executable if available, empty string 2952fe6060f1SDimitry Andric // otherwise. 2953fe6060f1SDimitry Andric std::string GetDefaultProcessName() { 2954fe6060f1SDimitry Andric Target *target = m_debugger.GetSelectedTarget().get(); 2955fe6060f1SDimitry Andric if (target == nullptr) 2956fe6060f1SDimitry Andric return ""; 2957fe6060f1SDimitry Andric 2958fe6060f1SDimitry Andric ModuleSP module_sp = target->GetExecutableModule(); 2959fe6060f1SDimitry Andric if (!module_sp->IsExecutable()) 2960fe6060f1SDimitry Andric return ""; 2961fe6060f1SDimitry Andric 2962fe6060f1SDimitry Andric return module_sp->GetFileSpec().GetFilename().AsCString(); 2963fe6060f1SDimitry Andric } 2964fe6060f1SDimitry Andric 2965fe6060f1SDimitry Andric bool StopRunningProcess() { 2966fe6060f1SDimitry Andric ExecutionContext exe_ctx = 2967fe6060f1SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext(); 2968fe6060f1SDimitry Andric 2969fe6060f1SDimitry Andric if (!exe_ctx.HasProcessScope()) 2970fe6060f1SDimitry Andric return false; 2971fe6060f1SDimitry Andric 2972fe6060f1SDimitry Andric Process *process = exe_ctx.GetProcessPtr(); 2973fe6060f1SDimitry Andric if (!(process && process->IsAlive())) 2974fe6060f1SDimitry Andric return false; 2975fe6060f1SDimitry Andric 2976fe6060f1SDimitry Andric FormDelegateSP form_delegate_sp = 2977fe6060f1SDimitry Andric FormDelegateSP(new DetachOrKillProcessFormDelegate(process)); 2978fe6060f1SDimitry Andric Rect bounds = m_main_window_sp->GetCenteredRect(85, 8); 2979fe6060f1SDimitry Andric WindowSP form_window_sp = m_main_window_sp->CreateSubWindow( 2980fe6060f1SDimitry Andric form_delegate_sp->GetName().c_str(), bounds, true); 2981fe6060f1SDimitry Andric WindowDelegateSP window_delegate_sp = 2982fe6060f1SDimitry Andric WindowDelegateSP(new FormWindowDelegate(form_delegate_sp)); 2983fe6060f1SDimitry Andric form_window_sp->SetDelegate(window_delegate_sp); 2984fe6060f1SDimitry Andric 2985fe6060f1SDimitry Andric return true; 2986fe6060f1SDimitry Andric } 2987fe6060f1SDimitry Andric 2988fe6060f1SDimitry Andric Target *GetTarget() { 2989fe6060f1SDimitry Andric Target *target = m_debugger.GetSelectedTarget().get(); 2990fe6060f1SDimitry Andric 2991fe6060f1SDimitry Andric if (target != nullptr) 2992fe6060f1SDimitry Andric return target; 2993fe6060f1SDimitry Andric 2994fe6060f1SDimitry Andric TargetSP new_target_sp; 2995fe6060f1SDimitry Andric m_debugger.GetTargetList().CreateTarget( 2996fe6060f1SDimitry Andric m_debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp); 2997fe6060f1SDimitry Andric 2998fe6060f1SDimitry Andric target = new_target_sp.get(); 2999fe6060f1SDimitry Andric 3000fe6060f1SDimitry Andric if (target == nullptr) 3001fe6060f1SDimitry Andric SetError("Failed to create target."); 3002fe6060f1SDimitry Andric 3003fe6060f1SDimitry Andric m_debugger.GetTargetList().SetSelectedTarget(new_target_sp); 3004fe6060f1SDimitry Andric 3005fe6060f1SDimitry Andric return target; 3006fe6060f1SDimitry Andric } 3007fe6060f1SDimitry Andric 3008fe6060f1SDimitry Andric ProcessAttachInfo GetAttachInfo() { 3009fe6060f1SDimitry Andric ProcessAttachInfo attach_info; 3010fe6060f1SDimitry Andric attach_info.SetContinueOnceAttached(m_continue_field->GetBoolean()); 3011fe6060f1SDimitry Andric if (m_type_field->GetChoiceContent() == "Name") { 3012fe6060f1SDimitry Andric attach_info.GetExecutableFile().SetFile(m_name_field->GetText(), 3013fe6060f1SDimitry Andric FileSpec::Style::native); 3014fe6060f1SDimitry Andric attach_info.SetWaitForLaunch(m_wait_for_field->GetBoolean()); 3015fe6060f1SDimitry Andric if (m_wait_for_field->GetBoolean()) 3016fe6060f1SDimitry Andric attach_info.SetIgnoreExisting(!m_include_existing_field->GetBoolean()); 3017fe6060f1SDimitry Andric } else { 3018fe6060f1SDimitry Andric attach_info.SetProcessID(m_pid_field->GetInteger()); 3019fe6060f1SDimitry Andric } 3020fe6060f1SDimitry Andric attach_info.SetProcessPluginName(m_plugin_field->GetPluginName()); 3021fe6060f1SDimitry Andric 3022fe6060f1SDimitry Andric return attach_info; 3023fe6060f1SDimitry Andric } 3024fe6060f1SDimitry Andric 3025fe6060f1SDimitry Andric void Attach(Window &window) { 3026fe6060f1SDimitry Andric ClearError(); 3027fe6060f1SDimitry Andric 3028fe6060f1SDimitry Andric bool all_fields_are_valid = CheckFieldsValidity(); 3029fe6060f1SDimitry Andric if (!all_fields_are_valid) 3030fe6060f1SDimitry Andric return; 3031fe6060f1SDimitry Andric 3032fe6060f1SDimitry Andric bool process_is_running = StopRunningProcess(); 3033fe6060f1SDimitry Andric if (process_is_running) 3034fe6060f1SDimitry Andric return; 3035fe6060f1SDimitry Andric 3036fe6060f1SDimitry Andric Target *target = GetTarget(); 3037fe6060f1SDimitry Andric if (HasError()) 3038fe6060f1SDimitry Andric return; 3039fe6060f1SDimitry Andric 3040fe6060f1SDimitry Andric StreamString stream; 3041fe6060f1SDimitry Andric ProcessAttachInfo attach_info = GetAttachInfo(); 3042fe6060f1SDimitry Andric Status status = target->Attach(attach_info, &stream); 3043fe6060f1SDimitry Andric 3044fe6060f1SDimitry Andric if (status.Fail()) { 3045fe6060f1SDimitry Andric SetError(status.AsCString()); 3046fe6060f1SDimitry Andric return; 3047fe6060f1SDimitry Andric } 3048fe6060f1SDimitry Andric 3049fe6060f1SDimitry Andric ProcessSP process_sp(target->GetProcessSP()); 3050fe6060f1SDimitry Andric if (!process_sp) { 3051fe6060f1SDimitry Andric SetError("Attached sucessfully but target has no process."); 3052fe6060f1SDimitry Andric return; 3053fe6060f1SDimitry Andric } 3054fe6060f1SDimitry Andric 3055fe6060f1SDimitry Andric if (attach_info.GetContinueOnceAttached()) 3056fe6060f1SDimitry Andric process_sp->Resume(); 3057fe6060f1SDimitry Andric 3058fe6060f1SDimitry Andric window.GetParent()->RemoveSubWindow(&window); 3059fe6060f1SDimitry Andric } 3060fe6060f1SDimitry Andric 3061fe6060f1SDimitry Andric protected: 3062fe6060f1SDimitry Andric Debugger &m_debugger; 3063fe6060f1SDimitry Andric WindowSP m_main_window_sp; 3064fe6060f1SDimitry Andric 3065fe6060f1SDimitry Andric ChoicesFieldDelegate *m_type_field; 3066fe6060f1SDimitry Andric IntegerFieldDelegate *m_pid_field; 3067fe6060f1SDimitry Andric TextFieldDelegate *m_name_field; 3068fe6060f1SDimitry Andric BooleanFieldDelegate *m_continue_field; 3069fe6060f1SDimitry Andric BooleanFieldDelegate *m_wait_for_field; 3070fe6060f1SDimitry Andric BooleanFieldDelegate *m_include_existing_field; 3071fe6060f1SDimitry Andric BooleanFieldDelegate *m_show_advanced_field; 3072fe6060f1SDimitry Andric ProcessPluginFieldDelegate *m_plugin_field; 3073fe6060f1SDimitry Andric }; 3074fe6060f1SDimitry Andric 3075349cc55cSDimitry Andric class TargetCreateFormDelegate : public FormDelegate { 3076349cc55cSDimitry Andric public: 3077349cc55cSDimitry Andric TargetCreateFormDelegate(Debugger &debugger) : m_debugger(debugger) { 3078349cc55cSDimitry Andric m_executable_field = AddFileField("Executable", "", /*need_to_exist=*/true, 3079349cc55cSDimitry Andric /*required=*/true); 3080349cc55cSDimitry Andric m_core_file_field = AddFileField("Core File", "", /*need_to_exist=*/true, 3081349cc55cSDimitry Andric /*required=*/false); 3082349cc55cSDimitry Andric m_symbol_file_field = AddFileField( 3083349cc55cSDimitry Andric "Symbol File", "", /*need_to_exist=*/true, /*required=*/false); 3084349cc55cSDimitry Andric m_show_advanced_field = AddBooleanField("Show advanced settings.", false); 3085349cc55cSDimitry Andric m_remote_file_field = AddFileField( 3086349cc55cSDimitry Andric "Remote File", "", /*need_to_exist=*/false, /*required=*/false); 3087349cc55cSDimitry Andric m_arch_field = AddArchField("Architecture", "", /*required=*/false); 3088349cc55cSDimitry Andric m_platform_field = AddPlatformPluginField(debugger); 3089349cc55cSDimitry Andric m_load_dependent_files_field = 3090349cc55cSDimitry Andric AddChoicesField("Load Dependents", 3, GetLoadDependentFilesChoices()); 3091349cc55cSDimitry Andric 3092349cc55cSDimitry Andric AddAction("Create", [this](Window &window) { CreateTarget(window); }); 3093349cc55cSDimitry Andric } 3094349cc55cSDimitry Andric 3095349cc55cSDimitry Andric std::string GetName() override { return "Create Target"; } 3096349cc55cSDimitry Andric 3097349cc55cSDimitry Andric void UpdateFieldsVisibility() override { 3098349cc55cSDimitry Andric if (m_show_advanced_field->GetBoolean()) { 3099349cc55cSDimitry Andric m_remote_file_field->FieldDelegateShow(); 3100349cc55cSDimitry Andric m_arch_field->FieldDelegateShow(); 3101349cc55cSDimitry Andric m_platform_field->FieldDelegateShow(); 3102349cc55cSDimitry Andric m_load_dependent_files_field->FieldDelegateShow(); 3103349cc55cSDimitry Andric } else { 3104349cc55cSDimitry Andric m_remote_file_field->FieldDelegateHide(); 3105349cc55cSDimitry Andric m_arch_field->FieldDelegateHide(); 3106349cc55cSDimitry Andric m_platform_field->FieldDelegateHide(); 3107349cc55cSDimitry Andric m_load_dependent_files_field->FieldDelegateHide(); 3108349cc55cSDimitry Andric } 3109349cc55cSDimitry Andric } 3110349cc55cSDimitry Andric 3111349cc55cSDimitry Andric static constexpr const char *kLoadDependentFilesNo = "No"; 3112349cc55cSDimitry Andric static constexpr const char *kLoadDependentFilesYes = "Yes"; 3113349cc55cSDimitry Andric static constexpr const char *kLoadDependentFilesExecOnly = "Executable only"; 3114349cc55cSDimitry Andric 3115349cc55cSDimitry Andric std::vector<std::string> GetLoadDependentFilesChoices() { 3116bdd1243dSDimitry Andric std::vector<std::string> load_dependents_options; 3117bdd1243dSDimitry Andric load_dependents_options.push_back(kLoadDependentFilesExecOnly); 3118bdd1243dSDimitry Andric load_dependents_options.push_back(kLoadDependentFilesYes); 3119bdd1243dSDimitry Andric load_dependents_options.push_back(kLoadDependentFilesNo); 3120bdd1243dSDimitry Andric return load_dependents_options; 3121349cc55cSDimitry Andric } 3122349cc55cSDimitry Andric 3123349cc55cSDimitry Andric LoadDependentFiles GetLoadDependentFiles() { 3124349cc55cSDimitry Andric std::string choice = m_load_dependent_files_field->GetChoiceContent(); 3125349cc55cSDimitry Andric if (choice == kLoadDependentFilesNo) 3126349cc55cSDimitry Andric return eLoadDependentsNo; 3127349cc55cSDimitry Andric if (choice == kLoadDependentFilesYes) 3128349cc55cSDimitry Andric return eLoadDependentsYes; 3129349cc55cSDimitry Andric return eLoadDependentsDefault; 3130349cc55cSDimitry Andric } 3131349cc55cSDimitry Andric 3132349cc55cSDimitry Andric OptionGroupPlatform GetPlatformOptions() { 3133349cc55cSDimitry Andric OptionGroupPlatform platform_options(false); 3134349cc55cSDimitry Andric platform_options.SetPlatformName(m_platform_field->GetPluginName().c_str()); 3135349cc55cSDimitry Andric return platform_options; 3136349cc55cSDimitry Andric } 3137349cc55cSDimitry Andric 3138349cc55cSDimitry Andric TargetSP GetTarget() { 3139349cc55cSDimitry Andric OptionGroupPlatform platform_options = GetPlatformOptions(); 3140349cc55cSDimitry Andric TargetSP target_sp; 3141349cc55cSDimitry Andric Status status = m_debugger.GetTargetList().CreateTarget( 3142349cc55cSDimitry Andric m_debugger, m_executable_field->GetPath(), 3143349cc55cSDimitry Andric m_arch_field->GetArchString(), GetLoadDependentFiles(), 3144349cc55cSDimitry Andric &platform_options, target_sp); 3145349cc55cSDimitry Andric 3146349cc55cSDimitry Andric if (status.Fail()) { 3147349cc55cSDimitry Andric SetError(status.AsCString()); 3148349cc55cSDimitry Andric return nullptr; 3149349cc55cSDimitry Andric } 3150349cc55cSDimitry Andric 3151349cc55cSDimitry Andric m_debugger.GetTargetList().SetSelectedTarget(target_sp); 3152349cc55cSDimitry Andric 3153349cc55cSDimitry Andric return target_sp; 3154349cc55cSDimitry Andric } 3155349cc55cSDimitry Andric 3156349cc55cSDimitry Andric void SetSymbolFile(TargetSP target_sp) { 3157349cc55cSDimitry Andric if (!m_symbol_file_field->IsSpecified()) 3158349cc55cSDimitry Andric return; 3159349cc55cSDimitry Andric 3160349cc55cSDimitry Andric ModuleSP module_sp(target_sp->GetExecutableModule()); 3161349cc55cSDimitry Andric if (!module_sp) 3162349cc55cSDimitry Andric return; 3163349cc55cSDimitry Andric 3164349cc55cSDimitry Andric module_sp->SetSymbolFileFileSpec( 3165349cc55cSDimitry Andric m_symbol_file_field->GetResolvedFileSpec()); 3166349cc55cSDimitry Andric } 3167349cc55cSDimitry Andric 3168349cc55cSDimitry Andric void SetCoreFile(TargetSP target_sp) { 3169349cc55cSDimitry Andric if (!m_core_file_field->IsSpecified()) 3170349cc55cSDimitry Andric return; 3171349cc55cSDimitry Andric 3172349cc55cSDimitry Andric FileSpec core_file_spec = m_core_file_field->GetResolvedFileSpec(); 3173349cc55cSDimitry Andric 3174349cc55cSDimitry Andric FileSpec core_file_directory_spec; 3175bdd1243dSDimitry Andric core_file_directory_spec.SetDirectory(core_file_spec.GetDirectory()); 3176349cc55cSDimitry Andric target_sp->AppendExecutableSearchPaths(core_file_directory_spec); 3177349cc55cSDimitry Andric 3178349cc55cSDimitry Andric ProcessSP process_sp(target_sp->CreateProcess( 3179349cc55cSDimitry Andric m_debugger.GetListener(), llvm::StringRef(), &core_file_spec, false)); 3180349cc55cSDimitry Andric 3181349cc55cSDimitry Andric if (!process_sp) { 3182349cc55cSDimitry Andric SetError("Unable to find process plug-in for core file!"); 3183349cc55cSDimitry Andric return; 3184349cc55cSDimitry Andric } 3185349cc55cSDimitry Andric 3186349cc55cSDimitry Andric Status status = process_sp->LoadCore(); 3187349cc55cSDimitry Andric if (status.Fail()) { 3188349cc55cSDimitry Andric SetError("Can't find plug-in for core file!"); 3189349cc55cSDimitry Andric return; 3190349cc55cSDimitry Andric } 3191349cc55cSDimitry Andric } 3192349cc55cSDimitry Andric 3193349cc55cSDimitry Andric void SetRemoteFile(TargetSP target_sp) { 3194349cc55cSDimitry Andric if (!m_remote_file_field->IsSpecified()) 3195349cc55cSDimitry Andric return; 3196349cc55cSDimitry Andric 3197349cc55cSDimitry Andric ModuleSP module_sp(target_sp->GetExecutableModule()); 3198349cc55cSDimitry Andric if (!module_sp) 3199349cc55cSDimitry Andric return; 3200349cc55cSDimitry Andric 3201349cc55cSDimitry Andric FileSpec remote_file_spec = m_remote_file_field->GetFileSpec(); 3202349cc55cSDimitry Andric module_sp->SetPlatformFileSpec(remote_file_spec); 3203349cc55cSDimitry Andric } 3204349cc55cSDimitry Andric 3205349cc55cSDimitry Andric void RemoveTarget(TargetSP target_sp) { 3206349cc55cSDimitry Andric m_debugger.GetTargetList().DeleteTarget(target_sp); 3207349cc55cSDimitry Andric } 3208349cc55cSDimitry Andric 3209349cc55cSDimitry Andric void CreateTarget(Window &window) { 3210349cc55cSDimitry Andric ClearError(); 3211349cc55cSDimitry Andric 3212349cc55cSDimitry Andric bool all_fields_are_valid = CheckFieldsValidity(); 3213349cc55cSDimitry Andric if (!all_fields_are_valid) 3214349cc55cSDimitry Andric return; 3215349cc55cSDimitry Andric 3216349cc55cSDimitry Andric TargetSP target_sp = GetTarget(); 3217349cc55cSDimitry Andric if (HasError()) 3218349cc55cSDimitry Andric return; 3219349cc55cSDimitry Andric 3220349cc55cSDimitry Andric SetSymbolFile(target_sp); 3221349cc55cSDimitry Andric if (HasError()) { 3222349cc55cSDimitry Andric RemoveTarget(target_sp); 3223349cc55cSDimitry Andric return; 3224349cc55cSDimitry Andric } 3225349cc55cSDimitry Andric 3226349cc55cSDimitry Andric SetCoreFile(target_sp); 3227349cc55cSDimitry Andric if (HasError()) { 3228349cc55cSDimitry Andric RemoveTarget(target_sp); 3229349cc55cSDimitry Andric return; 3230349cc55cSDimitry Andric } 3231349cc55cSDimitry Andric 3232349cc55cSDimitry Andric SetRemoteFile(target_sp); 3233349cc55cSDimitry Andric if (HasError()) { 3234349cc55cSDimitry Andric RemoveTarget(target_sp); 3235349cc55cSDimitry Andric return; 3236349cc55cSDimitry Andric } 3237349cc55cSDimitry Andric 3238349cc55cSDimitry Andric window.GetParent()->RemoveSubWindow(&window); 3239349cc55cSDimitry Andric } 3240349cc55cSDimitry Andric 3241349cc55cSDimitry Andric protected: 3242349cc55cSDimitry Andric Debugger &m_debugger; 3243349cc55cSDimitry Andric 3244349cc55cSDimitry Andric FileFieldDelegate *m_executable_field; 3245349cc55cSDimitry Andric FileFieldDelegate *m_core_file_field; 3246349cc55cSDimitry Andric FileFieldDelegate *m_symbol_file_field; 3247349cc55cSDimitry Andric BooleanFieldDelegate *m_show_advanced_field; 3248349cc55cSDimitry Andric FileFieldDelegate *m_remote_file_field; 3249349cc55cSDimitry Andric ArchFieldDelegate *m_arch_field; 3250349cc55cSDimitry Andric PlatformPluginFieldDelegate *m_platform_field; 3251349cc55cSDimitry Andric ChoicesFieldDelegate *m_load_dependent_files_field; 3252349cc55cSDimitry Andric }; 3253349cc55cSDimitry Andric 3254349cc55cSDimitry Andric class ProcessLaunchFormDelegate : public FormDelegate { 3255349cc55cSDimitry Andric public: 3256349cc55cSDimitry Andric ProcessLaunchFormDelegate(Debugger &debugger, WindowSP main_window_sp) 3257349cc55cSDimitry Andric : m_debugger(debugger), m_main_window_sp(main_window_sp) { 3258349cc55cSDimitry Andric 3259349cc55cSDimitry Andric m_arguments_field = AddArgumentsField(); 3260349cc55cSDimitry Andric SetArgumentsFieldDefaultValue(); 3261349cc55cSDimitry Andric m_target_environment_field = 3262349cc55cSDimitry Andric AddEnvironmentVariableListField("Target Environment Variables"); 3263349cc55cSDimitry Andric SetTargetEnvironmentFieldDefaultValue(); 3264349cc55cSDimitry Andric m_working_directory_field = AddDirectoryField( 3265349cc55cSDimitry Andric "Working Directory", GetDefaultWorkingDirectory().c_str(), true, false); 3266349cc55cSDimitry Andric 3267349cc55cSDimitry Andric m_show_advanced_field = AddBooleanField("Show advanced settings.", false); 3268349cc55cSDimitry Andric 3269349cc55cSDimitry Andric m_stop_at_entry_field = AddBooleanField("Stop at entry point.", false); 3270349cc55cSDimitry Andric m_detach_on_error_field = 3271349cc55cSDimitry Andric AddBooleanField("Detach on error.", GetDefaultDetachOnError()); 3272349cc55cSDimitry Andric m_disable_aslr_field = 3273349cc55cSDimitry Andric AddBooleanField("Disable ASLR", GetDefaultDisableASLR()); 3274349cc55cSDimitry Andric m_plugin_field = AddProcessPluginField(); 3275349cc55cSDimitry Andric m_arch_field = AddArchField("Architecture", "", false); 3276349cc55cSDimitry Andric m_shell_field = AddFileField("Shell", "", true, false); 3277349cc55cSDimitry Andric m_expand_shell_arguments_field = 3278349cc55cSDimitry Andric AddBooleanField("Expand shell arguments.", false); 3279349cc55cSDimitry Andric 3280349cc55cSDimitry Andric m_disable_standard_io_field = 3281349cc55cSDimitry Andric AddBooleanField("Disable Standard IO", GetDefaultDisableStandardIO()); 3282349cc55cSDimitry Andric m_standard_output_field = 3283349cc55cSDimitry Andric AddFileField("Standard Output File", "", /*need_to_exist=*/false, 3284349cc55cSDimitry Andric /*required=*/false); 3285349cc55cSDimitry Andric m_standard_error_field = 3286349cc55cSDimitry Andric AddFileField("Standard Error File", "", /*need_to_exist=*/false, 3287349cc55cSDimitry Andric /*required=*/false); 3288349cc55cSDimitry Andric m_standard_input_field = 3289349cc55cSDimitry Andric AddFileField("Standard Input File", "", /*need_to_exist=*/false, 3290349cc55cSDimitry Andric /*required=*/false); 3291349cc55cSDimitry Andric 3292349cc55cSDimitry Andric m_show_inherited_environment_field = 3293349cc55cSDimitry Andric AddBooleanField("Show inherited environment variables.", false); 3294349cc55cSDimitry Andric m_inherited_environment_field = 3295349cc55cSDimitry Andric AddEnvironmentVariableListField("Inherited Environment Variables"); 3296349cc55cSDimitry Andric SetInheritedEnvironmentFieldDefaultValue(); 3297349cc55cSDimitry Andric 3298349cc55cSDimitry Andric AddAction("Launch", [this](Window &window) { Launch(window); }); 3299349cc55cSDimitry Andric } 3300349cc55cSDimitry Andric 3301349cc55cSDimitry Andric std::string GetName() override { return "Launch Process"; } 3302349cc55cSDimitry Andric 3303349cc55cSDimitry Andric void UpdateFieldsVisibility() override { 3304349cc55cSDimitry Andric if (m_show_advanced_field->GetBoolean()) { 3305349cc55cSDimitry Andric m_stop_at_entry_field->FieldDelegateShow(); 3306349cc55cSDimitry Andric m_detach_on_error_field->FieldDelegateShow(); 3307349cc55cSDimitry Andric m_disable_aslr_field->FieldDelegateShow(); 3308349cc55cSDimitry Andric m_plugin_field->FieldDelegateShow(); 3309349cc55cSDimitry Andric m_arch_field->FieldDelegateShow(); 3310349cc55cSDimitry Andric m_shell_field->FieldDelegateShow(); 3311349cc55cSDimitry Andric m_expand_shell_arguments_field->FieldDelegateShow(); 3312349cc55cSDimitry Andric m_disable_standard_io_field->FieldDelegateShow(); 3313349cc55cSDimitry Andric if (m_disable_standard_io_field->GetBoolean()) { 3314349cc55cSDimitry Andric m_standard_input_field->FieldDelegateHide(); 3315349cc55cSDimitry Andric m_standard_output_field->FieldDelegateHide(); 3316349cc55cSDimitry Andric m_standard_error_field->FieldDelegateHide(); 3317349cc55cSDimitry Andric } else { 3318349cc55cSDimitry Andric m_standard_input_field->FieldDelegateShow(); 3319349cc55cSDimitry Andric m_standard_output_field->FieldDelegateShow(); 3320349cc55cSDimitry Andric m_standard_error_field->FieldDelegateShow(); 3321349cc55cSDimitry Andric } 3322349cc55cSDimitry Andric m_show_inherited_environment_field->FieldDelegateShow(); 3323349cc55cSDimitry Andric if (m_show_inherited_environment_field->GetBoolean()) 3324349cc55cSDimitry Andric m_inherited_environment_field->FieldDelegateShow(); 3325349cc55cSDimitry Andric else 3326349cc55cSDimitry Andric m_inherited_environment_field->FieldDelegateHide(); 3327349cc55cSDimitry Andric } else { 3328349cc55cSDimitry Andric m_stop_at_entry_field->FieldDelegateHide(); 3329349cc55cSDimitry Andric m_detach_on_error_field->FieldDelegateHide(); 3330349cc55cSDimitry Andric m_disable_aslr_field->FieldDelegateHide(); 3331349cc55cSDimitry Andric m_plugin_field->FieldDelegateHide(); 3332349cc55cSDimitry Andric m_arch_field->FieldDelegateHide(); 3333349cc55cSDimitry Andric m_shell_field->FieldDelegateHide(); 3334349cc55cSDimitry Andric m_expand_shell_arguments_field->FieldDelegateHide(); 3335349cc55cSDimitry Andric m_disable_standard_io_field->FieldDelegateHide(); 3336349cc55cSDimitry Andric m_standard_input_field->FieldDelegateHide(); 3337349cc55cSDimitry Andric m_standard_output_field->FieldDelegateHide(); 3338349cc55cSDimitry Andric m_standard_error_field->FieldDelegateHide(); 3339349cc55cSDimitry Andric m_show_inherited_environment_field->FieldDelegateHide(); 3340349cc55cSDimitry Andric m_inherited_environment_field->FieldDelegateHide(); 3341349cc55cSDimitry Andric } 3342349cc55cSDimitry Andric } 3343349cc55cSDimitry Andric 3344349cc55cSDimitry Andric // Methods for setting the default value of the fields. 3345349cc55cSDimitry Andric 3346349cc55cSDimitry Andric void SetArgumentsFieldDefaultValue() { 3347349cc55cSDimitry Andric TargetSP target = m_debugger.GetSelectedTarget(); 3348349cc55cSDimitry Andric if (target == nullptr) 3349349cc55cSDimitry Andric return; 3350349cc55cSDimitry Andric 3351349cc55cSDimitry Andric const Args &target_arguments = 3352349cc55cSDimitry Andric target->GetProcessLaunchInfo().GetArguments(); 3353349cc55cSDimitry Andric m_arguments_field->AddArguments(target_arguments); 3354349cc55cSDimitry Andric } 3355349cc55cSDimitry Andric 3356349cc55cSDimitry Andric void SetTargetEnvironmentFieldDefaultValue() { 3357349cc55cSDimitry Andric TargetSP target = m_debugger.GetSelectedTarget(); 3358349cc55cSDimitry Andric if (target == nullptr) 3359349cc55cSDimitry Andric return; 3360349cc55cSDimitry Andric 3361349cc55cSDimitry Andric const Environment &target_environment = target->GetTargetEnvironment(); 3362349cc55cSDimitry Andric m_target_environment_field->AddEnvironmentVariables(target_environment); 3363349cc55cSDimitry Andric } 3364349cc55cSDimitry Andric 3365349cc55cSDimitry Andric void SetInheritedEnvironmentFieldDefaultValue() { 3366349cc55cSDimitry Andric TargetSP target = m_debugger.GetSelectedTarget(); 3367349cc55cSDimitry Andric if (target == nullptr) 3368349cc55cSDimitry Andric return; 3369349cc55cSDimitry Andric 3370349cc55cSDimitry Andric const Environment &inherited_environment = 3371349cc55cSDimitry Andric target->GetInheritedEnvironment(); 3372349cc55cSDimitry Andric m_inherited_environment_field->AddEnvironmentVariables( 3373349cc55cSDimitry Andric inherited_environment); 3374349cc55cSDimitry Andric } 3375349cc55cSDimitry Andric 3376349cc55cSDimitry Andric std::string GetDefaultWorkingDirectory() { 3377349cc55cSDimitry Andric TargetSP target = m_debugger.GetSelectedTarget(); 3378349cc55cSDimitry Andric if (target == nullptr) 3379349cc55cSDimitry Andric return ""; 3380349cc55cSDimitry Andric 3381349cc55cSDimitry Andric PlatformSP platform = target->GetPlatform(); 3382349cc55cSDimitry Andric return platform->GetWorkingDirectory().GetPath(); 3383349cc55cSDimitry Andric } 3384349cc55cSDimitry Andric 3385349cc55cSDimitry Andric bool GetDefaultDisableASLR() { 3386349cc55cSDimitry Andric TargetSP target = m_debugger.GetSelectedTarget(); 3387349cc55cSDimitry Andric if (target == nullptr) 3388349cc55cSDimitry Andric return false; 3389349cc55cSDimitry Andric 3390349cc55cSDimitry Andric return target->GetDisableASLR(); 3391349cc55cSDimitry Andric } 3392349cc55cSDimitry Andric 3393349cc55cSDimitry Andric bool GetDefaultDisableStandardIO() { 3394349cc55cSDimitry Andric TargetSP target = m_debugger.GetSelectedTarget(); 3395349cc55cSDimitry Andric if (target == nullptr) 3396349cc55cSDimitry Andric return true; 3397349cc55cSDimitry Andric 3398349cc55cSDimitry Andric return target->GetDisableSTDIO(); 3399349cc55cSDimitry Andric } 3400349cc55cSDimitry Andric 3401349cc55cSDimitry Andric bool GetDefaultDetachOnError() { 3402349cc55cSDimitry Andric TargetSP target = m_debugger.GetSelectedTarget(); 3403349cc55cSDimitry Andric if (target == nullptr) 3404349cc55cSDimitry Andric return true; 3405349cc55cSDimitry Andric 3406349cc55cSDimitry Andric return target->GetDetachOnError(); 3407349cc55cSDimitry Andric } 3408349cc55cSDimitry Andric 3409349cc55cSDimitry Andric // Methods for getting the necessary information and setting them to the 3410349cc55cSDimitry Andric // ProcessLaunchInfo. 3411349cc55cSDimitry Andric 3412349cc55cSDimitry Andric void GetExecutableSettings(ProcessLaunchInfo &launch_info) { 3413349cc55cSDimitry Andric TargetSP target = m_debugger.GetSelectedTarget(); 3414349cc55cSDimitry Andric ModuleSP executable_module = target->GetExecutableModule(); 3415349cc55cSDimitry Andric llvm::StringRef target_settings_argv0 = target->GetArg0(); 3416349cc55cSDimitry Andric 3417349cc55cSDimitry Andric if (!target_settings_argv0.empty()) { 3418349cc55cSDimitry Andric launch_info.GetArguments().AppendArgument(target_settings_argv0); 3419349cc55cSDimitry Andric launch_info.SetExecutableFile(executable_module->GetPlatformFileSpec(), 3420349cc55cSDimitry Andric false); 3421349cc55cSDimitry Andric return; 3422349cc55cSDimitry Andric } 3423349cc55cSDimitry Andric 3424349cc55cSDimitry Andric launch_info.SetExecutableFile(executable_module->GetPlatformFileSpec(), 3425349cc55cSDimitry Andric true); 3426349cc55cSDimitry Andric } 3427349cc55cSDimitry Andric 3428349cc55cSDimitry Andric void GetArguments(ProcessLaunchInfo &launch_info) { 3429349cc55cSDimitry Andric TargetSP target = m_debugger.GetSelectedTarget(); 3430349cc55cSDimitry Andric Args arguments = m_arguments_field->GetArguments(); 3431349cc55cSDimitry Andric launch_info.GetArguments().AppendArguments(arguments); 3432349cc55cSDimitry Andric } 3433349cc55cSDimitry Andric 3434349cc55cSDimitry Andric void GetEnvironment(ProcessLaunchInfo &launch_info) { 3435349cc55cSDimitry Andric Environment target_environment = 3436349cc55cSDimitry Andric m_target_environment_field->GetEnvironment(); 3437349cc55cSDimitry Andric Environment inherited_environment = 3438349cc55cSDimitry Andric m_inherited_environment_field->GetEnvironment(); 3439349cc55cSDimitry Andric launch_info.GetEnvironment().insert(target_environment.begin(), 3440349cc55cSDimitry Andric target_environment.end()); 3441349cc55cSDimitry Andric launch_info.GetEnvironment().insert(inherited_environment.begin(), 3442349cc55cSDimitry Andric inherited_environment.end()); 3443349cc55cSDimitry Andric } 3444349cc55cSDimitry Andric 3445349cc55cSDimitry Andric void GetWorkingDirectory(ProcessLaunchInfo &launch_info) { 3446349cc55cSDimitry Andric if (m_working_directory_field->IsSpecified()) 3447349cc55cSDimitry Andric launch_info.SetWorkingDirectory( 3448349cc55cSDimitry Andric m_working_directory_field->GetResolvedFileSpec()); 3449349cc55cSDimitry Andric } 3450349cc55cSDimitry Andric 3451349cc55cSDimitry Andric void GetStopAtEntry(ProcessLaunchInfo &launch_info) { 3452349cc55cSDimitry Andric if (m_stop_at_entry_field->GetBoolean()) 3453349cc55cSDimitry Andric launch_info.GetFlags().Set(eLaunchFlagStopAtEntry); 3454349cc55cSDimitry Andric else 3455349cc55cSDimitry Andric launch_info.GetFlags().Clear(eLaunchFlagStopAtEntry); 3456349cc55cSDimitry Andric } 3457349cc55cSDimitry Andric 3458349cc55cSDimitry Andric void GetDetachOnError(ProcessLaunchInfo &launch_info) { 3459349cc55cSDimitry Andric if (m_detach_on_error_field->GetBoolean()) 3460349cc55cSDimitry Andric launch_info.GetFlags().Set(eLaunchFlagDetachOnError); 3461349cc55cSDimitry Andric else 3462349cc55cSDimitry Andric launch_info.GetFlags().Clear(eLaunchFlagDetachOnError); 3463349cc55cSDimitry Andric } 3464349cc55cSDimitry Andric 3465349cc55cSDimitry Andric void GetDisableASLR(ProcessLaunchInfo &launch_info) { 3466349cc55cSDimitry Andric if (m_disable_aslr_field->GetBoolean()) 3467349cc55cSDimitry Andric launch_info.GetFlags().Set(eLaunchFlagDisableASLR); 3468349cc55cSDimitry Andric else 3469349cc55cSDimitry Andric launch_info.GetFlags().Clear(eLaunchFlagDisableASLR); 3470349cc55cSDimitry Andric } 3471349cc55cSDimitry Andric 3472349cc55cSDimitry Andric void GetPlugin(ProcessLaunchInfo &launch_info) { 3473349cc55cSDimitry Andric launch_info.SetProcessPluginName(m_plugin_field->GetPluginName()); 3474349cc55cSDimitry Andric } 3475349cc55cSDimitry Andric 3476349cc55cSDimitry Andric void GetArch(ProcessLaunchInfo &launch_info) { 3477349cc55cSDimitry Andric if (!m_arch_field->IsSpecified()) 3478349cc55cSDimitry Andric return; 3479349cc55cSDimitry Andric 3480349cc55cSDimitry Andric TargetSP target_sp = m_debugger.GetSelectedTarget(); 3481349cc55cSDimitry Andric PlatformSP platform_sp = 3482349cc55cSDimitry Andric target_sp ? target_sp->GetPlatform() : PlatformSP(); 3483349cc55cSDimitry Andric launch_info.GetArchitecture() = Platform::GetAugmentedArchSpec( 3484349cc55cSDimitry Andric platform_sp.get(), m_arch_field->GetArchString()); 3485349cc55cSDimitry Andric } 3486349cc55cSDimitry Andric 3487349cc55cSDimitry Andric void GetShell(ProcessLaunchInfo &launch_info) { 3488349cc55cSDimitry Andric if (!m_shell_field->IsSpecified()) 3489349cc55cSDimitry Andric return; 3490349cc55cSDimitry Andric 3491349cc55cSDimitry Andric launch_info.SetShell(m_shell_field->GetResolvedFileSpec()); 3492349cc55cSDimitry Andric launch_info.SetShellExpandArguments( 3493349cc55cSDimitry Andric m_expand_shell_arguments_field->GetBoolean()); 3494349cc55cSDimitry Andric } 3495349cc55cSDimitry Andric 3496349cc55cSDimitry Andric void GetStandardIO(ProcessLaunchInfo &launch_info) { 3497349cc55cSDimitry Andric if (m_disable_standard_io_field->GetBoolean()) { 3498349cc55cSDimitry Andric launch_info.GetFlags().Set(eLaunchFlagDisableSTDIO); 3499349cc55cSDimitry Andric return; 3500349cc55cSDimitry Andric } 3501349cc55cSDimitry Andric 3502349cc55cSDimitry Andric FileAction action; 3503349cc55cSDimitry Andric if (m_standard_input_field->IsSpecified()) { 3504bdd1243dSDimitry Andric if (action.Open(STDIN_FILENO, m_standard_input_field->GetFileSpec(), true, 3505bdd1243dSDimitry Andric false)) 3506349cc55cSDimitry Andric launch_info.AppendFileAction(action); 3507349cc55cSDimitry Andric } 3508349cc55cSDimitry Andric if (m_standard_output_field->IsSpecified()) { 3509bdd1243dSDimitry Andric if (action.Open(STDOUT_FILENO, m_standard_output_field->GetFileSpec(), 3510bdd1243dSDimitry Andric false, true)) 3511349cc55cSDimitry Andric launch_info.AppendFileAction(action); 3512349cc55cSDimitry Andric } 3513349cc55cSDimitry Andric if (m_standard_error_field->IsSpecified()) { 3514bdd1243dSDimitry Andric if (action.Open(STDERR_FILENO, m_standard_error_field->GetFileSpec(), 3515bdd1243dSDimitry Andric false, true)) 3516349cc55cSDimitry Andric launch_info.AppendFileAction(action); 3517349cc55cSDimitry Andric } 3518349cc55cSDimitry Andric } 3519349cc55cSDimitry Andric 3520349cc55cSDimitry Andric void GetInheritTCC(ProcessLaunchInfo &launch_info) { 3521349cc55cSDimitry Andric if (m_debugger.GetSelectedTarget()->GetInheritTCC()) 3522349cc55cSDimitry Andric launch_info.GetFlags().Set(eLaunchFlagInheritTCCFromParent); 3523349cc55cSDimitry Andric } 3524349cc55cSDimitry Andric 3525349cc55cSDimitry Andric ProcessLaunchInfo GetLaunchInfo() { 3526349cc55cSDimitry Andric ProcessLaunchInfo launch_info; 3527349cc55cSDimitry Andric 3528349cc55cSDimitry Andric GetExecutableSettings(launch_info); 3529349cc55cSDimitry Andric GetArguments(launch_info); 3530349cc55cSDimitry Andric GetEnvironment(launch_info); 3531349cc55cSDimitry Andric GetWorkingDirectory(launch_info); 3532349cc55cSDimitry Andric GetStopAtEntry(launch_info); 3533349cc55cSDimitry Andric GetDetachOnError(launch_info); 3534349cc55cSDimitry Andric GetDisableASLR(launch_info); 3535349cc55cSDimitry Andric GetPlugin(launch_info); 3536349cc55cSDimitry Andric GetArch(launch_info); 3537349cc55cSDimitry Andric GetShell(launch_info); 3538349cc55cSDimitry Andric GetStandardIO(launch_info); 3539349cc55cSDimitry Andric GetInheritTCC(launch_info); 3540349cc55cSDimitry Andric 3541349cc55cSDimitry Andric return launch_info; 3542349cc55cSDimitry Andric } 3543349cc55cSDimitry Andric 3544349cc55cSDimitry Andric bool StopRunningProcess() { 3545349cc55cSDimitry Andric ExecutionContext exe_ctx = 3546349cc55cSDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext(); 3547349cc55cSDimitry Andric 3548349cc55cSDimitry Andric if (!exe_ctx.HasProcessScope()) 3549349cc55cSDimitry Andric return false; 3550349cc55cSDimitry Andric 3551349cc55cSDimitry Andric Process *process = exe_ctx.GetProcessPtr(); 3552349cc55cSDimitry Andric if (!(process && process->IsAlive())) 3553349cc55cSDimitry Andric return false; 3554349cc55cSDimitry Andric 3555349cc55cSDimitry Andric FormDelegateSP form_delegate_sp = 3556349cc55cSDimitry Andric FormDelegateSP(new DetachOrKillProcessFormDelegate(process)); 3557349cc55cSDimitry Andric Rect bounds = m_main_window_sp->GetCenteredRect(85, 8); 3558349cc55cSDimitry Andric WindowSP form_window_sp = m_main_window_sp->CreateSubWindow( 3559349cc55cSDimitry Andric form_delegate_sp->GetName().c_str(), bounds, true); 3560349cc55cSDimitry Andric WindowDelegateSP window_delegate_sp = 3561349cc55cSDimitry Andric WindowDelegateSP(new FormWindowDelegate(form_delegate_sp)); 3562349cc55cSDimitry Andric form_window_sp->SetDelegate(window_delegate_sp); 3563349cc55cSDimitry Andric 3564349cc55cSDimitry Andric return true; 3565349cc55cSDimitry Andric } 3566349cc55cSDimitry Andric 3567349cc55cSDimitry Andric Target *GetTarget() { 3568349cc55cSDimitry Andric Target *target = m_debugger.GetSelectedTarget().get(); 3569349cc55cSDimitry Andric 3570349cc55cSDimitry Andric if (target == nullptr) { 3571349cc55cSDimitry Andric SetError("No target exists!"); 3572349cc55cSDimitry Andric return nullptr; 3573349cc55cSDimitry Andric } 3574349cc55cSDimitry Andric 3575349cc55cSDimitry Andric ModuleSP exe_module_sp = target->GetExecutableModule(); 3576349cc55cSDimitry Andric 3577349cc55cSDimitry Andric if (exe_module_sp == nullptr) { 3578349cc55cSDimitry Andric SetError("No executable in target!"); 3579349cc55cSDimitry Andric return nullptr; 3580349cc55cSDimitry Andric } 3581349cc55cSDimitry Andric 3582349cc55cSDimitry Andric return target; 3583349cc55cSDimitry Andric } 3584349cc55cSDimitry Andric 3585349cc55cSDimitry Andric void Launch(Window &window) { 3586349cc55cSDimitry Andric ClearError(); 3587349cc55cSDimitry Andric 3588349cc55cSDimitry Andric bool all_fields_are_valid = CheckFieldsValidity(); 3589349cc55cSDimitry Andric if (!all_fields_are_valid) 3590349cc55cSDimitry Andric return; 3591349cc55cSDimitry Andric 3592349cc55cSDimitry Andric bool process_is_running = StopRunningProcess(); 3593349cc55cSDimitry Andric if (process_is_running) 3594349cc55cSDimitry Andric return; 3595349cc55cSDimitry Andric 3596349cc55cSDimitry Andric Target *target = GetTarget(); 3597349cc55cSDimitry Andric if (HasError()) 3598349cc55cSDimitry Andric return; 3599349cc55cSDimitry Andric 3600349cc55cSDimitry Andric StreamString stream; 3601349cc55cSDimitry Andric ProcessLaunchInfo launch_info = GetLaunchInfo(); 3602349cc55cSDimitry Andric Status status = target->Launch(launch_info, &stream); 3603349cc55cSDimitry Andric 3604349cc55cSDimitry Andric if (status.Fail()) { 3605349cc55cSDimitry Andric SetError(status.AsCString()); 3606349cc55cSDimitry Andric return; 3607349cc55cSDimitry Andric } 3608349cc55cSDimitry Andric 3609349cc55cSDimitry Andric ProcessSP process_sp(target->GetProcessSP()); 3610349cc55cSDimitry Andric if (!process_sp) { 3611349cc55cSDimitry Andric SetError("Launched successfully but target has no process!"); 3612349cc55cSDimitry Andric return; 3613349cc55cSDimitry Andric } 3614349cc55cSDimitry Andric 3615349cc55cSDimitry Andric window.GetParent()->RemoveSubWindow(&window); 3616349cc55cSDimitry Andric } 3617349cc55cSDimitry Andric 3618349cc55cSDimitry Andric protected: 3619349cc55cSDimitry Andric Debugger &m_debugger; 3620349cc55cSDimitry Andric WindowSP m_main_window_sp; 3621349cc55cSDimitry Andric 3622349cc55cSDimitry Andric ArgumentsFieldDelegate *m_arguments_field; 3623349cc55cSDimitry Andric EnvironmentVariableListFieldDelegate *m_target_environment_field; 3624349cc55cSDimitry Andric DirectoryFieldDelegate *m_working_directory_field; 3625349cc55cSDimitry Andric 3626349cc55cSDimitry Andric BooleanFieldDelegate *m_show_advanced_field; 3627349cc55cSDimitry Andric 3628349cc55cSDimitry Andric BooleanFieldDelegate *m_stop_at_entry_field; 3629349cc55cSDimitry Andric BooleanFieldDelegate *m_detach_on_error_field; 3630349cc55cSDimitry Andric BooleanFieldDelegate *m_disable_aslr_field; 3631349cc55cSDimitry Andric ProcessPluginFieldDelegate *m_plugin_field; 3632349cc55cSDimitry Andric ArchFieldDelegate *m_arch_field; 3633349cc55cSDimitry Andric FileFieldDelegate *m_shell_field; 3634349cc55cSDimitry Andric BooleanFieldDelegate *m_expand_shell_arguments_field; 3635349cc55cSDimitry Andric BooleanFieldDelegate *m_disable_standard_io_field; 3636349cc55cSDimitry Andric FileFieldDelegate *m_standard_input_field; 3637349cc55cSDimitry Andric FileFieldDelegate *m_standard_output_field; 3638349cc55cSDimitry Andric FileFieldDelegate *m_standard_error_field; 3639349cc55cSDimitry Andric 3640349cc55cSDimitry Andric BooleanFieldDelegate *m_show_inherited_environment_field; 3641349cc55cSDimitry Andric EnvironmentVariableListFieldDelegate *m_inherited_environment_field; 3642349cc55cSDimitry Andric }; 3643349cc55cSDimitry Andric 3644349cc55cSDimitry Andric //////////// 3645349cc55cSDimitry Andric // Searchers 3646349cc55cSDimitry Andric //////////// 3647349cc55cSDimitry Andric 3648349cc55cSDimitry Andric class SearcherDelegate { 3649349cc55cSDimitry Andric public: 365081ad6265SDimitry Andric SearcherDelegate() = default; 3651349cc55cSDimitry Andric 3652349cc55cSDimitry Andric virtual ~SearcherDelegate() = default; 3653349cc55cSDimitry Andric 3654349cc55cSDimitry Andric virtual int GetNumberOfMatches() = 0; 3655349cc55cSDimitry Andric 3656349cc55cSDimitry Andric // Get the string that will be displayed for the match at the input index. 3657349cc55cSDimitry Andric virtual const std::string &GetMatchTextAtIndex(int index) = 0; 3658349cc55cSDimitry Andric 3659349cc55cSDimitry Andric // Update the matches of the search. This is executed every time the text 3660349cc55cSDimitry Andric // field handles an event. 3661349cc55cSDimitry Andric virtual void UpdateMatches(const std::string &text) = 0; 3662349cc55cSDimitry Andric 3663349cc55cSDimitry Andric // Execute the user callback given the index of some match. This is executed 3664349cc55cSDimitry Andric // once the user selects a match. 3665349cc55cSDimitry Andric virtual void ExecuteCallback(int match_index) = 0; 3666349cc55cSDimitry Andric }; 3667349cc55cSDimitry Andric 3668349cc55cSDimitry Andric typedef std::shared_ptr<SearcherDelegate> SearcherDelegateSP; 3669349cc55cSDimitry Andric 3670349cc55cSDimitry Andric class SearcherWindowDelegate : public WindowDelegate { 3671349cc55cSDimitry Andric public: 3672349cc55cSDimitry Andric SearcherWindowDelegate(SearcherDelegateSP &delegate_sp) 367381ad6265SDimitry Andric : m_delegate_sp(delegate_sp), m_text_field("Search", "", false) { 3674349cc55cSDimitry Andric ; 3675349cc55cSDimitry Andric } 3676349cc55cSDimitry Andric 3677349cc55cSDimitry Andric // A completion window is padded by one character from all sides. A text field 3678349cc55cSDimitry Andric // is first drawn for inputting the searcher request, then a list of matches 3679349cc55cSDimitry Andric // are displayed in a scrollable list. 3680349cc55cSDimitry Andric // 3681349cc55cSDimitry Andric // ___<Searcher Window Name>____________________________ 3682349cc55cSDimitry Andric // | | 3683349cc55cSDimitry Andric // | __[Search]_______________________________________ | 3684349cc55cSDimitry Andric // | | | | 3685349cc55cSDimitry Andric // | |_______________________________________________| | 3686349cc55cSDimitry Andric // | - Match 1. | 3687349cc55cSDimitry Andric // | - Match 2. | 3688349cc55cSDimitry Andric // | - ... | 3689349cc55cSDimitry Andric // | | 3690349cc55cSDimitry Andric // |____________________________[Press Esc to Cancel]__| 3691349cc55cSDimitry Andric // 3692349cc55cSDimitry Andric 3693349cc55cSDimitry Andric // Get the index of the last visible match. Assuming at least one match 3694349cc55cSDimitry Andric // exists. 3695349cc55cSDimitry Andric int GetLastVisibleMatch(int height) { 3696349cc55cSDimitry Andric int index = m_first_visible_match + height; 3697349cc55cSDimitry Andric return std::min(index, m_delegate_sp->GetNumberOfMatches()) - 1; 3698349cc55cSDimitry Andric } 3699349cc55cSDimitry Andric 3700349cc55cSDimitry Andric int GetNumberOfVisibleMatches(int height) { 3701349cc55cSDimitry Andric return GetLastVisibleMatch(height) - m_first_visible_match + 1; 3702349cc55cSDimitry Andric } 3703349cc55cSDimitry Andric 3704349cc55cSDimitry Andric void UpdateScrolling(Surface &surface) { 3705349cc55cSDimitry Andric if (m_selected_match < m_first_visible_match) { 3706349cc55cSDimitry Andric m_first_visible_match = m_selected_match; 3707349cc55cSDimitry Andric return; 3708349cc55cSDimitry Andric } 3709349cc55cSDimitry Andric 3710349cc55cSDimitry Andric int height = surface.GetHeight(); 3711349cc55cSDimitry Andric int last_visible_match = GetLastVisibleMatch(height); 3712349cc55cSDimitry Andric if (m_selected_match > last_visible_match) { 3713349cc55cSDimitry Andric m_first_visible_match = m_selected_match - height + 1; 3714349cc55cSDimitry Andric } 3715349cc55cSDimitry Andric } 3716349cc55cSDimitry Andric 3717349cc55cSDimitry Andric void DrawMatches(Surface &surface) { 3718349cc55cSDimitry Andric if (m_delegate_sp->GetNumberOfMatches() == 0) 3719349cc55cSDimitry Andric return; 3720349cc55cSDimitry Andric 3721349cc55cSDimitry Andric UpdateScrolling(surface); 3722349cc55cSDimitry Andric 3723349cc55cSDimitry Andric int count = GetNumberOfVisibleMatches(surface.GetHeight()); 3724349cc55cSDimitry Andric for (int i = 0; i < count; i++) { 3725349cc55cSDimitry Andric surface.MoveCursor(1, i); 3726349cc55cSDimitry Andric int current_match = m_first_visible_match + i; 3727349cc55cSDimitry Andric if (current_match == m_selected_match) 3728349cc55cSDimitry Andric surface.AttributeOn(A_REVERSE); 3729349cc55cSDimitry Andric surface.PutCString( 3730349cc55cSDimitry Andric m_delegate_sp->GetMatchTextAtIndex(current_match).c_str()); 3731349cc55cSDimitry Andric if (current_match == m_selected_match) 3732349cc55cSDimitry Andric surface.AttributeOff(A_REVERSE); 3733349cc55cSDimitry Andric } 3734349cc55cSDimitry Andric } 3735349cc55cSDimitry Andric 3736349cc55cSDimitry Andric void DrawContent(Surface &surface) { 3737349cc55cSDimitry Andric Rect content_bounds = surface.GetFrame(); 3738349cc55cSDimitry Andric Rect text_field_bounds, matchs_bounds; 3739349cc55cSDimitry Andric content_bounds.HorizontalSplit(m_text_field.FieldDelegateGetHeight(), 3740349cc55cSDimitry Andric text_field_bounds, matchs_bounds); 3741349cc55cSDimitry Andric Surface text_field_surface = surface.SubSurface(text_field_bounds); 3742349cc55cSDimitry Andric Surface matches_surface = surface.SubSurface(matchs_bounds); 3743349cc55cSDimitry Andric 3744349cc55cSDimitry Andric m_text_field.FieldDelegateDraw(text_field_surface, true); 3745349cc55cSDimitry Andric DrawMatches(matches_surface); 3746349cc55cSDimitry Andric } 3747349cc55cSDimitry Andric 3748349cc55cSDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override { 3749349cc55cSDimitry Andric window.Erase(); 3750349cc55cSDimitry Andric 3751349cc55cSDimitry Andric window.DrawTitleBox(window.GetName(), "Press Esc to Cancel"); 3752349cc55cSDimitry Andric 3753349cc55cSDimitry Andric Rect content_bounds = window.GetFrame(); 3754349cc55cSDimitry Andric content_bounds.Inset(2, 2); 3755349cc55cSDimitry Andric Surface content_surface = window.SubSurface(content_bounds); 3756349cc55cSDimitry Andric 3757349cc55cSDimitry Andric DrawContent(content_surface); 3758349cc55cSDimitry Andric return true; 3759349cc55cSDimitry Andric } 3760349cc55cSDimitry Andric 3761349cc55cSDimitry Andric void SelectNext() { 3762349cc55cSDimitry Andric if (m_selected_match != m_delegate_sp->GetNumberOfMatches() - 1) 3763349cc55cSDimitry Andric m_selected_match++; 3764349cc55cSDimitry Andric } 3765349cc55cSDimitry Andric 3766349cc55cSDimitry Andric void SelectPrevious() { 3767349cc55cSDimitry Andric if (m_selected_match != 0) 3768349cc55cSDimitry Andric m_selected_match--; 3769349cc55cSDimitry Andric } 3770349cc55cSDimitry Andric 3771349cc55cSDimitry Andric void ExecuteCallback(Window &window) { 3772349cc55cSDimitry Andric m_delegate_sp->ExecuteCallback(m_selected_match); 3773349cc55cSDimitry Andric window.GetParent()->RemoveSubWindow(&window); 3774349cc55cSDimitry Andric } 3775349cc55cSDimitry Andric 3776349cc55cSDimitry Andric void UpdateMatches() { 3777349cc55cSDimitry Andric m_delegate_sp->UpdateMatches(m_text_field.GetText()); 3778349cc55cSDimitry Andric m_selected_match = 0; 3779349cc55cSDimitry Andric } 3780349cc55cSDimitry Andric 3781349cc55cSDimitry Andric HandleCharResult WindowDelegateHandleChar(Window &window, int key) override { 3782349cc55cSDimitry Andric switch (key) { 3783349cc55cSDimitry Andric case '\r': 3784349cc55cSDimitry Andric case '\n': 3785349cc55cSDimitry Andric case KEY_ENTER: 3786349cc55cSDimitry Andric ExecuteCallback(window); 3787349cc55cSDimitry Andric return eKeyHandled; 3788349cc55cSDimitry Andric case '\t': 3789349cc55cSDimitry Andric case KEY_DOWN: 3790349cc55cSDimitry Andric SelectNext(); 3791349cc55cSDimitry Andric return eKeyHandled; 3792349cc55cSDimitry Andric case KEY_SHIFT_TAB: 3793349cc55cSDimitry Andric case KEY_UP: 3794349cc55cSDimitry Andric SelectPrevious(); 3795349cc55cSDimitry Andric return eKeyHandled; 3796349cc55cSDimitry Andric case KEY_ESCAPE: 3797349cc55cSDimitry Andric window.GetParent()->RemoveSubWindow(&window); 3798349cc55cSDimitry Andric return eKeyHandled; 3799349cc55cSDimitry Andric default: 3800349cc55cSDimitry Andric break; 3801349cc55cSDimitry Andric } 3802349cc55cSDimitry Andric 3803349cc55cSDimitry Andric if (m_text_field.FieldDelegateHandleChar(key) == eKeyHandled) 3804349cc55cSDimitry Andric UpdateMatches(); 3805349cc55cSDimitry Andric 3806349cc55cSDimitry Andric return eKeyHandled; 3807349cc55cSDimitry Andric } 3808349cc55cSDimitry Andric 3809349cc55cSDimitry Andric protected: 3810349cc55cSDimitry Andric SearcherDelegateSP m_delegate_sp; 3811349cc55cSDimitry Andric TextFieldDelegate m_text_field; 3812349cc55cSDimitry Andric // The index of the currently selected match. 381381ad6265SDimitry Andric int m_selected_match = 0; 3814349cc55cSDimitry Andric // The index of the first visible match. 381581ad6265SDimitry Andric int m_first_visible_match = 0; 3816349cc55cSDimitry Andric }; 3817349cc55cSDimitry Andric 3818349cc55cSDimitry Andric ////////////////////////////// 3819349cc55cSDimitry Andric // Searcher Delegate Instances 3820349cc55cSDimitry Andric ////////////////////////////// 3821349cc55cSDimitry Andric 3822349cc55cSDimitry Andric // This is a searcher delegate wrapper around CommandCompletions common 3823349cc55cSDimitry Andric // callbacks. The callbacks are only given the match string. The completion_mask 3824*06c3fb27SDimitry Andric // can be a combination of lldb::CompletionType. 3825349cc55cSDimitry Andric class CommonCompletionSearcherDelegate : public SearcherDelegate { 3826349cc55cSDimitry Andric public: 3827349cc55cSDimitry Andric typedef std::function<void(const std::string &)> CallbackType; 3828349cc55cSDimitry Andric 3829349cc55cSDimitry Andric CommonCompletionSearcherDelegate(Debugger &debugger, uint32_t completion_mask, 3830349cc55cSDimitry Andric CallbackType callback) 3831349cc55cSDimitry Andric : m_debugger(debugger), m_completion_mask(completion_mask), 3832349cc55cSDimitry Andric m_callback(callback) {} 3833349cc55cSDimitry Andric 3834349cc55cSDimitry Andric int GetNumberOfMatches() override { return m_matches.GetSize(); } 3835349cc55cSDimitry Andric 3836349cc55cSDimitry Andric const std::string &GetMatchTextAtIndex(int index) override { 3837349cc55cSDimitry Andric return m_matches[index]; 3838349cc55cSDimitry Andric } 3839349cc55cSDimitry Andric 3840349cc55cSDimitry Andric void UpdateMatches(const std::string &text) override { 3841349cc55cSDimitry Andric CompletionResult result; 3842349cc55cSDimitry Andric CompletionRequest request(text.c_str(), text.size(), result); 3843*06c3fb27SDimitry Andric lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( 3844349cc55cSDimitry Andric m_debugger.GetCommandInterpreter(), m_completion_mask, request, 3845349cc55cSDimitry Andric nullptr); 3846349cc55cSDimitry Andric result.GetMatches(m_matches); 3847349cc55cSDimitry Andric } 3848349cc55cSDimitry Andric 3849349cc55cSDimitry Andric void ExecuteCallback(int match_index) override { 3850349cc55cSDimitry Andric m_callback(m_matches[match_index]); 3851349cc55cSDimitry Andric } 3852349cc55cSDimitry Andric 3853349cc55cSDimitry Andric protected: 3854349cc55cSDimitry Andric Debugger &m_debugger; 3855*06c3fb27SDimitry Andric // A compound mask from lldb::CompletionType. 3856349cc55cSDimitry Andric uint32_t m_completion_mask; 3857349cc55cSDimitry Andric // A callback to execute once the user selects a match. The match is passed to 3858349cc55cSDimitry Andric // the callback as a string. 3859349cc55cSDimitry Andric CallbackType m_callback; 3860349cc55cSDimitry Andric StringList m_matches; 3861349cc55cSDimitry Andric }; 3862349cc55cSDimitry Andric 3863349cc55cSDimitry Andric //////// 3864349cc55cSDimitry Andric // Menus 3865349cc55cSDimitry Andric //////// 3866349cc55cSDimitry Andric 3867480093f4SDimitry Andric class MenuDelegate { 3868480093f4SDimitry Andric public: 3869480093f4SDimitry Andric virtual ~MenuDelegate() = default; 3870480093f4SDimitry Andric 3871480093f4SDimitry Andric virtual MenuActionResult MenuDelegateAction(Menu &menu) = 0; 3872480093f4SDimitry Andric }; 3873480093f4SDimitry Andric 3874480093f4SDimitry Andric class Menu : public WindowDelegate { 3875480093f4SDimitry Andric public: 3876480093f4SDimitry Andric enum class Type { Invalid, Bar, Item, Separator }; 3877480093f4SDimitry Andric 3878480093f4SDimitry Andric // Menubar or separator constructor 3879480093f4SDimitry Andric Menu(Type type); 3880480093f4SDimitry Andric 3881480093f4SDimitry Andric // Menuitem constructor 3882480093f4SDimitry Andric Menu(const char *name, const char *key_name, int key_value, 3883480093f4SDimitry Andric uint64_t identifier); 3884480093f4SDimitry Andric 3885480093f4SDimitry Andric ~Menu() override = default; 3886480093f4SDimitry Andric 3887480093f4SDimitry Andric const MenuDelegateSP &GetDelegate() const { return m_delegate_sp; } 3888480093f4SDimitry Andric 3889480093f4SDimitry Andric void SetDelegate(const MenuDelegateSP &delegate_sp) { 3890480093f4SDimitry Andric m_delegate_sp = delegate_sp; 3891480093f4SDimitry Andric } 3892480093f4SDimitry Andric 3893480093f4SDimitry Andric void RecalculateNameLengths(); 3894480093f4SDimitry Andric 3895480093f4SDimitry Andric void AddSubmenu(const MenuSP &menu_sp); 3896480093f4SDimitry Andric 3897480093f4SDimitry Andric int DrawAndRunMenu(Window &window); 3898480093f4SDimitry Andric 3899480093f4SDimitry Andric void DrawMenuTitle(Window &window, bool highlight); 3900480093f4SDimitry Andric 3901480093f4SDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override; 3902480093f4SDimitry Andric 3903480093f4SDimitry Andric HandleCharResult WindowDelegateHandleChar(Window &window, int key) override; 3904480093f4SDimitry Andric 3905480093f4SDimitry Andric MenuActionResult ActionPrivate(Menu &menu) { 3906480093f4SDimitry Andric MenuActionResult result = MenuActionResult::NotHandled; 3907480093f4SDimitry Andric if (m_delegate_sp) { 3908480093f4SDimitry Andric result = m_delegate_sp->MenuDelegateAction(menu); 3909480093f4SDimitry Andric if (result != MenuActionResult::NotHandled) 3910480093f4SDimitry Andric return result; 3911480093f4SDimitry Andric } else if (m_parent) { 3912480093f4SDimitry Andric result = m_parent->ActionPrivate(menu); 3913480093f4SDimitry Andric if (result != MenuActionResult::NotHandled) 3914480093f4SDimitry Andric return result; 3915480093f4SDimitry Andric } 3916480093f4SDimitry Andric return m_canned_result; 3917480093f4SDimitry Andric } 3918480093f4SDimitry Andric 3919480093f4SDimitry Andric MenuActionResult Action() { 3920480093f4SDimitry Andric // Call the recursive action so it can try to handle it with the menu 3921480093f4SDimitry Andric // delegate, and if not, try our parent menu 3922480093f4SDimitry Andric return ActionPrivate(*this); 3923480093f4SDimitry Andric } 3924480093f4SDimitry Andric 3925480093f4SDimitry Andric void SetCannedResult(MenuActionResult result) { m_canned_result = result; } 3926480093f4SDimitry Andric 3927480093f4SDimitry Andric Menus &GetSubmenus() { return m_submenus; } 3928480093f4SDimitry Andric 3929480093f4SDimitry Andric const Menus &GetSubmenus() const { return m_submenus; } 3930480093f4SDimitry Andric 3931480093f4SDimitry Andric int GetSelectedSubmenuIndex() const { return m_selected; } 3932480093f4SDimitry Andric 3933480093f4SDimitry Andric void SetSelectedSubmenuIndex(int idx) { m_selected = idx; } 3934480093f4SDimitry Andric 3935480093f4SDimitry Andric Type GetType() const { return m_type; } 3936480093f4SDimitry Andric 3937480093f4SDimitry Andric int GetStartingColumn() const { return m_start_col; } 3938480093f4SDimitry Andric 3939480093f4SDimitry Andric void SetStartingColumn(int col) { m_start_col = col; } 3940480093f4SDimitry Andric 3941480093f4SDimitry Andric int GetKeyValue() const { return m_key_value; } 3942480093f4SDimitry Andric 3943480093f4SDimitry Andric std::string &GetName() { return m_name; } 3944480093f4SDimitry Andric 3945480093f4SDimitry Andric int GetDrawWidth() const { 3946480093f4SDimitry Andric return m_max_submenu_name_length + m_max_submenu_key_name_length + 8; 3947480093f4SDimitry Andric } 3948480093f4SDimitry Andric 3949480093f4SDimitry Andric uint64_t GetIdentifier() const { return m_identifier; } 3950480093f4SDimitry Andric 3951480093f4SDimitry Andric void SetIdentifier(uint64_t identifier) { m_identifier = identifier; } 3952480093f4SDimitry Andric 3953480093f4SDimitry Andric protected: 3954480093f4SDimitry Andric std::string m_name; 3955480093f4SDimitry Andric std::string m_key_name; 3956480093f4SDimitry Andric uint64_t m_identifier; 3957480093f4SDimitry Andric Type m_type; 3958480093f4SDimitry Andric int m_key_value; 3959480093f4SDimitry Andric int m_start_col; 3960480093f4SDimitry Andric int m_max_submenu_name_length; 3961480093f4SDimitry Andric int m_max_submenu_key_name_length; 3962480093f4SDimitry Andric int m_selected; 3963480093f4SDimitry Andric Menu *m_parent; 3964480093f4SDimitry Andric Menus m_submenus; 3965480093f4SDimitry Andric WindowSP m_menu_window_sp; 3966480093f4SDimitry Andric MenuActionResult m_canned_result; 3967480093f4SDimitry Andric MenuDelegateSP m_delegate_sp; 3968480093f4SDimitry Andric }; 3969480093f4SDimitry Andric 3970480093f4SDimitry Andric // Menubar or separator constructor 3971480093f4SDimitry Andric Menu::Menu(Type type) 3972480093f4SDimitry Andric : m_name(), m_key_name(), m_identifier(0), m_type(type), m_key_value(0), 3973480093f4SDimitry Andric m_start_col(0), m_max_submenu_name_length(0), 3974480093f4SDimitry Andric m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr), 3975480093f4SDimitry Andric m_submenus(), m_canned_result(MenuActionResult::NotHandled), 3976480093f4SDimitry Andric m_delegate_sp() {} 3977480093f4SDimitry Andric 3978480093f4SDimitry Andric // Menuitem constructor 3979480093f4SDimitry Andric Menu::Menu(const char *name, const char *key_name, int key_value, 3980480093f4SDimitry Andric uint64_t identifier) 3981480093f4SDimitry Andric : m_name(), m_key_name(), m_identifier(identifier), m_type(Type::Invalid), 3982480093f4SDimitry Andric m_key_value(key_value), m_start_col(0), m_max_submenu_name_length(0), 3983480093f4SDimitry Andric m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr), 3984480093f4SDimitry Andric m_submenus(), m_canned_result(MenuActionResult::NotHandled), 3985480093f4SDimitry Andric m_delegate_sp() { 3986480093f4SDimitry Andric if (name && name[0]) { 3987480093f4SDimitry Andric m_name = name; 3988480093f4SDimitry Andric m_type = Type::Item; 3989480093f4SDimitry Andric if (key_name && key_name[0]) 3990480093f4SDimitry Andric m_key_name = key_name; 3991480093f4SDimitry Andric } else { 3992480093f4SDimitry Andric m_type = Type::Separator; 3993480093f4SDimitry Andric } 3994480093f4SDimitry Andric } 3995480093f4SDimitry Andric 3996480093f4SDimitry Andric void Menu::RecalculateNameLengths() { 3997480093f4SDimitry Andric m_max_submenu_name_length = 0; 3998480093f4SDimitry Andric m_max_submenu_key_name_length = 0; 3999480093f4SDimitry Andric Menus &submenus = GetSubmenus(); 4000480093f4SDimitry Andric const size_t num_submenus = submenus.size(); 4001480093f4SDimitry Andric for (size_t i = 0; i < num_submenus; ++i) { 4002480093f4SDimitry Andric Menu *submenu = submenus[i].get(); 4003480093f4SDimitry Andric if (static_cast<size_t>(m_max_submenu_name_length) < submenu->m_name.size()) 4004480093f4SDimitry Andric m_max_submenu_name_length = submenu->m_name.size(); 4005480093f4SDimitry Andric if (static_cast<size_t>(m_max_submenu_key_name_length) < 4006480093f4SDimitry Andric submenu->m_key_name.size()) 4007480093f4SDimitry Andric m_max_submenu_key_name_length = submenu->m_key_name.size(); 4008480093f4SDimitry Andric } 4009480093f4SDimitry Andric } 4010480093f4SDimitry Andric 4011480093f4SDimitry Andric void Menu::AddSubmenu(const MenuSP &menu_sp) { 4012480093f4SDimitry Andric menu_sp->m_parent = this; 4013480093f4SDimitry Andric if (static_cast<size_t>(m_max_submenu_name_length) < menu_sp->m_name.size()) 4014480093f4SDimitry Andric m_max_submenu_name_length = menu_sp->m_name.size(); 4015480093f4SDimitry Andric if (static_cast<size_t>(m_max_submenu_key_name_length) < 4016480093f4SDimitry Andric menu_sp->m_key_name.size()) 4017480093f4SDimitry Andric m_max_submenu_key_name_length = menu_sp->m_key_name.size(); 4018480093f4SDimitry Andric m_submenus.push_back(menu_sp); 4019480093f4SDimitry Andric } 4020480093f4SDimitry Andric 4021480093f4SDimitry Andric void Menu::DrawMenuTitle(Window &window, bool highlight) { 4022480093f4SDimitry Andric if (m_type == Type::Separator) { 4023480093f4SDimitry Andric window.MoveCursor(0, window.GetCursorY()); 4024480093f4SDimitry Andric window.PutChar(ACS_LTEE); 4025480093f4SDimitry Andric int width = window.GetWidth(); 4026480093f4SDimitry Andric if (width > 2) { 4027480093f4SDimitry Andric width -= 2; 4028480093f4SDimitry Andric for (int i = 0; i < width; ++i) 4029480093f4SDimitry Andric window.PutChar(ACS_HLINE); 4030480093f4SDimitry Andric } 4031480093f4SDimitry Andric window.PutChar(ACS_RTEE); 4032480093f4SDimitry Andric } else { 4033480093f4SDimitry Andric const int shortcut_key = m_key_value; 4034480093f4SDimitry Andric bool underlined_shortcut = false; 4035e8d8bef9SDimitry Andric const attr_t highlight_attr = A_REVERSE; 4036480093f4SDimitry Andric if (highlight) 4037e8d8bef9SDimitry Andric window.AttributeOn(highlight_attr); 40385ffd83dbSDimitry Andric if (llvm::isPrint(shortcut_key)) { 4039480093f4SDimitry Andric size_t lower_pos = m_name.find(tolower(shortcut_key)); 4040480093f4SDimitry Andric size_t upper_pos = m_name.find(toupper(shortcut_key)); 4041480093f4SDimitry Andric const char *name = m_name.c_str(); 4042480093f4SDimitry Andric size_t pos = std::min<size_t>(lower_pos, upper_pos); 4043480093f4SDimitry Andric if (pos != std::string::npos) { 4044480093f4SDimitry Andric underlined_shortcut = true; 4045480093f4SDimitry Andric if (pos > 0) { 4046480093f4SDimitry Andric window.PutCString(name, pos); 4047480093f4SDimitry Andric name += pos; 4048480093f4SDimitry Andric } 4049480093f4SDimitry Andric const attr_t shortcut_attr = A_UNDERLINE | A_BOLD; 4050480093f4SDimitry Andric window.AttributeOn(shortcut_attr); 4051480093f4SDimitry Andric window.PutChar(name[0]); 4052480093f4SDimitry Andric window.AttributeOff(shortcut_attr); 4053480093f4SDimitry Andric name++; 4054480093f4SDimitry Andric if (name[0]) 4055480093f4SDimitry Andric window.PutCString(name); 4056480093f4SDimitry Andric } 4057480093f4SDimitry Andric } 4058480093f4SDimitry Andric 4059480093f4SDimitry Andric if (!underlined_shortcut) { 4060480093f4SDimitry Andric window.PutCString(m_name.c_str()); 4061480093f4SDimitry Andric } 4062480093f4SDimitry Andric 4063480093f4SDimitry Andric if (highlight) 4064e8d8bef9SDimitry Andric window.AttributeOff(highlight_attr); 4065480093f4SDimitry Andric 4066480093f4SDimitry Andric if (m_key_name.empty()) { 40675ffd83dbSDimitry Andric if (!underlined_shortcut && llvm::isPrint(m_key_value)) { 4068e8d8bef9SDimitry Andric window.AttributeOn(COLOR_PAIR(MagentaOnWhite)); 4069480093f4SDimitry Andric window.Printf(" (%c)", m_key_value); 4070e8d8bef9SDimitry Andric window.AttributeOff(COLOR_PAIR(MagentaOnWhite)); 4071480093f4SDimitry Andric } 4072480093f4SDimitry Andric } else { 4073e8d8bef9SDimitry Andric window.AttributeOn(COLOR_PAIR(MagentaOnWhite)); 4074480093f4SDimitry Andric window.Printf(" (%s)", m_key_name.c_str()); 4075e8d8bef9SDimitry Andric window.AttributeOff(COLOR_PAIR(MagentaOnWhite)); 4076480093f4SDimitry Andric } 4077480093f4SDimitry Andric } 4078480093f4SDimitry Andric } 4079480093f4SDimitry Andric 4080480093f4SDimitry Andric bool Menu::WindowDelegateDraw(Window &window, bool force) { 4081480093f4SDimitry Andric Menus &submenus = GetSubmenus(); 4082480093f4SDimitry Andric const size_t num_submenus = submenus.size(); 4083480093f4SDimitry Andric const int selected_idx = GetSelectedSubmenuIndex(); 4084480093f4SDimitry Andric Menu::Type menu_type = GetType(); 4085480093f4SDimitry Andric switch (menu_type) { 4086480093f4SDimitry Andric case Menu::Type::Bar: { 4087e8d8bef9SDimitry Andric window.SetBackground(BlackOnWhite); 4088480093f4SDimitry Andric window.MoveCursor(0, 0); 4089480093f4SDimitry Andric for (size_t i = 0; i < num_submenus; ++i) { 4090480093f4SDimitry Andric Menu *menu = submenus[i].get(); 4091480093f4SDimitry Andric if (i > 0) 4092480093f4SDimitry Andric window.PutChar(' '); 4093480093f4SDimitry Andric menu->SetStartingColumn(window.GetCursorX()); 4094480093f4SDimitry Andric window.PutCString("| "); 4095480093f4SDimitry Andric menu->DrawMenuTitle(window, false); 4096480093f4SDimitry Andric } 4097480093f4SDimitry Andric window.PutCString(" |"); 4098480093f4SDimitry Andric } break; 4099480093f4SDimitry Andric 4100480093f4SDimitry Andric case Menu::Type::Item: { 4101480093f4SDimitry Andric int y = 1; 4102480093f4SDimitry Andric int x = 3; 4103480093f4SDimitry Andric // Draw the menu 4104480093f4SDimitry Andric int cursor_x = 0; 4105480093f4SDimitry Andric int cursor_y = 0; 4106480093f4SDimitry Andric window.Erase(); 4107e8d8bef9SDimitry Andric window.SetBackground(BlackOnWhite); 4108480093f4SDimitry Andric window.Box(); 4109480093f4SDimitry Andric for (size_t i = 0; i < num_submenus; ++i) { 4110480093f4SDimitry Andric const bool is_selected = (i == static_cast<size_t>(selected_idx)); 4111480093f4SDimitry Andric window.MoveCursor(x, y + i); 4112480093f4SDimitry Andric if (is_selected) { 4113480093f4SDimitry Andric // Remember where we want the cursor to be 4114480093f4SDimitry Andric cursor_x = x - 1; 4115480093f4SDimitry Andric cursor_y = y + i; 4116480093f4SDimitry Andric } 4117480093f4SDimitry Andric submenus[i]->DrawMenuTitle(window, is_selected); 4118480093f4SDimitry Andric } 4119480093f4SDimitry Andric window.MoveCursor(cursor_x, cursor_y); 4120480093f4SDimitry Andric } break; 4121480093f4SDimitry Andric 4122480093f4SDimitry Andric default: 4123480093f4SDimitry Andric case Menu::Type::Separator: 4124480093f4SDimitry Andric break; 4125480093f4SDimitry Andric } 4126480093f4SDimitry Andric return true; // Drawing handled... 4127480093f4SDimitry Andric } 4128480093f4SDimitry Andric 4129480093f4SDimitry Andric HandleCharResult Menu::WindowDelegateHandleChar(Window &window, int key) { 4130480093f4SDimitry Andric HandleCharResult result = eKeyNotHandled; 4131480093f4SDimitry Andric 4132480093f4SDimitry Andric Menus &submenus = GetSubmenus(); 4133480093f4SDimitry Andric const size_t num_submenus = submenus.size(); 4134480093f4SDimitry Andric const int selected_idx = GetSelectedSubmenuIndex(); 4135480093f4SDimitry Andric Menu::Type menu_type = GetType(); 4136480093f4SDimitry Andric if (menu_type == Menu::Type::Bar) { 4137480093f4SDimitry Andric MenuSP run_menu_sp; 4138480093f4SDimitry Andric switch (key) { 4139480093f4SDimitry Andric case KEY_DOWN: 4140480093f4SDimitry Andric case KEY_UP: 4141480093f4SDimitry Andric // Show last menu or first menu 4142480093f4SDimitry Andric if (selected_idx < static_cast<int>(num_submenus)) 4143480093f4SDimitry Andric run_menu_sp = submenus[selected_idx]; 4144480093f4SDimitry Andric else if (!submenus.empty()) 4145480093f4SDimitry Andric run_menu_sp = submenus.front(); 4146480093f4SDimitry Andric result = eKeyHandled; 4147480093f4SDimitry Andric break; 4148480093f4SDimitry Andric 4149480093f4SDimitry Andric case KEY_RIGHT: 4150480093f4SDimitry Andric ++m_selected; 4151480093f4SDimitry Andric if (m_selected >= static_cast<int>(num_submenus)) 4152480093f4SDimitry Andric m_selected = 0; 4153480093f4SDimitry Andric if (m_selected < static_cast<int>(num_submenus)) 4154480093f4SDimitry Andric run_menu_sp = submenus[m_selected]; 4155480093f4SDimitry Andric else if (!submenus.empty()) 4156480093f4SDimitry Andric run_menu_sp = submenus.front(); 4157480093f4SDimitry Andric result = eKeyHandled; 4158480093f4SDimitry Andric break; 4159480093f4SDimitry Andric 4160480093f4SDimitry Andric case KEY_LEFT: 4161480093f4SDimitry Andric --m_selected; 4162480093f4SDimitry Andric if (m_selected < 0) 4163480093f4SDimitry Andric m_selected = num_submenus - 1; 4164480093f4SDimitry Andric if (m_selected < static_cast<int>(num_submenus)) 4165480093f4SDimitry Andric run_menu_sp = submenus[m_selected]; 4166480093f4SDimitry Andric else if (!submenus.empty()) 4167480093f4SDimitry Andric run_menu_sp = submenus.front(); 4168480093f4SDimitry Andric result = eKeyHandled; 4169480093f4SDimitry Andric break; 4170480093f4SDimitry Andric 4171480093f4SDimitry Andric default: 4172480093f4SDimitry Andric for (size_t i = 0; i < num_submenus; ++i) { 4173480093f4SDimitry Andric if (submenus[i]->GetKeyValue() == key) { 4174480093f4SDimitry Andric SetSelectedSubmenuIndex(i); 4175480093f4SDimitry Andric run_menu_sp = submenus[i]; 4176480093f4SDimitry Andric result = eKeyHandled; 4177480093f4SDimitry Andric break; 4178480093f4SDimitry Andric } 4179480093f4SDimitry Andric } 4180480093f4SDimitry Andric break; 4181480093f4SDimitry Andric } 4182480093f4SDimitry Andric 4183480093f4SDimitry Andric if (run_menu_sp) { 4184480093f4SDimitry Andric // Run the action on this menu in case we need to populate the menu with 4185480093f4SDimitry Andric // dynamic content and also in case check marks, and any other menu 4186480093f4SDimitry Andric // decorations need to be calculated 4187480093f4SDimitry Andric if (run_menu_sp->Action() == MenuActionResult::Quit) 4188480093f4SDimitry Andric return eQuitApplication; 4189480093f4SDimitry Andric 4190480093f4SDimitry Andric Rect menu_bounds; 4191480093f4SDimitry Andric menu_bounds.origin.x = run_menu_sp->GetStartingColumn(); 4192480093f4SDimitry Andric menu_bounds.origin.y = 1; 4193480093f4SDimitry Andric menu_bounds.size.width = run_menu_sp->GetDrawWidth(); 4194480093f4SDimitry Andric menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2; 4195480093f4SDimitry Andric if (m_menu_window_sp) 4196480093f4SDimitry Andric window.GetParent()->RemoveSubWindow(m_menu_window_sp.get()); 4197480093f4SDimitry Andric 4198480093f4SDimitry Andric m_menu_window_sp = window.GetParent()->CreateSubWindow( 4199480093f4SDimitry Andric run_menu_sp->GetName().c_str(), menu_bounds, true); 4200480093f4SDimitry Andric m_menu_window_sp->SetDelegate(run_menu_sp); 4201480093f4SDimitry Andric } 4202480093f4SDimitry Andric } else if (menu_type == Menu::Type::Item) { 4203480093f4SDimitry Andric switch (key) { 4204480093f4SDimitry Andric case KEY_DOWN: 4205480093f4SDimitry Andric if (m_submenus.size() > 1) { 4206480093f4SDimitry Andric const int start_select = m_selected; 4207480093f4SDimitry Andric while (++m_selected != start_select) { 4208480093f4SDimitry Andric if (static_cast<size_t>(m_selected) >= num_submenus) 4209480093f4SDimitry Andric m_selected = 0; 4210480093f4SDimitry Andric if (m_submenus[m_selected]->GetType() == Type::Separator) 4211480093f4SDimitry Andric continue; 4212480093f4SDimitry Andric else 4213480093f4SDimitry Andric break; 4214480093f4SDimitry Andric } 4215480093f4SDimitry Andric return eKeyHandled; 4216480093f4SDimitry Andric } 4217480093f4SDimitry Andric break; 4218480093f4SDimitry Andric 4219480093f4SDimitry Andric case KEY_UP: 4220480093f4SDimitry Andric if (m_submenus.size() > 1) { 4221480093f4SDimitry Andric const int start_select = m_selected; 4222480093f4SDimitry Andric while (--m_selected != start_select) { 4223480093f4SDimitry Andric if (m_selected < static_cast<int>(0)) 4224480093f4SDimitry Andric m_selected = num_submenus - 1; 4225480093f4SDimitry Andric if (m_submenus[m_selected]->GetType() == Type::Separator) 4226480093f4SDimitry Andric continue; 4227480093f4SDimitry Andric else 4228480093f4SDimitry Andric break; 4229480093f4SDimitry Andric } 4230480093f4SDimitry Andric return eKeyHandled; 4231480093f4SDimitry Andric } 4232480093f4SDimitry Andric break; 4233480093f4SDimitry Andric 4234480093f4SDimitry Andric case KEY_RETURN: 4235480093f4SDimitry Andric if (static_cast<size_t>(selected_idx) < num_submenus) { 4236480093f4SDimitry Andric if (submenus[selected_idx]->Action() == MenuActionResult::Quit) 4237480093f4SDimitry Andric return eQuitApplication; 4238480093f4SDimitry Andric window.GetParent()->RemoveSubWindow(&window); 4239480093f4SDimitry Andric return eKeyHandled; 4240480093f4SDimitry Andric } 4241480093f4SDimitry Andric break; 4242480093f4SDimitry Andric 4243480093f4SDimitry Andric case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in 4244480093f4SDimitry Andric // case other chars are entered for escaped sequences 4245480093f4SDimitry Andric window.GetParent()->RemoveSubWindow(&window); 4246480093f4SDimitry Andric return eKeyHandled; 4247480093f4SDimitry Andric 4248480093f4SDimitry Andric default: 4249480093f4SDimitry Andric for (size_t i = 0; i < num_submenus; ++i) { 4250480093f4SDimitry Andric Menu *menu = submenus[i].get(); 4251480093f4SDimitry Andric if (menu->GetKeyValue() == key) { 4252480093f4SDimitry Andric SetSelectedSubmenuIndex(i); 4253480093f4SDimitry Andric window.GetParent()->RemoveSubWindow(&window); 4254480093f4SDimitry Andric if (menu->Action() == MenuActionResult::Quit) 4255480093f4SDimitry Andric return eQuitApplication; 4256480093f4SDimitry Andric return eKeyHandled; 4257480093f4SDimitry Andric } 4258480093f4SDimitry Andric } 4259480093f4SDimitry Andric break; 4260480093f4SDimitry Andric } 4261480093f4SDimitry Andric } else if (menu_type == Menu::Type::Separator) { 4262480093f4SDimitry Andric } 4263480093f4SDimitry Andric return result; 4264480093f4SDimitry Andric } 4265480093f4SDimitry Andric 4266480093f4SDimitry Andric class Application { 4267480093f4SDimitry Andric public: 426881ad6265SDimitry Andric Application(FILE *in, FILE *out) : m_window_sp(), m_in(in), m_out(out) {} 4269480093f4SDimitry Andric 4270480093f4SDimitry Andric ~Application() { 4271480093f4SDimitry Andric m_window_delegates.clear(); 4272480093f4SDimitry Andric m_window_sp.reset(); 4273480093f4SDimitry Andric if (m_screen) { 4274480093f4SDimitry Andric ::delscreen(m_screen); 4275480093f4SDimitry Andric m_screen = nullptr; 4276480093f4SDimitry Andric } 4277480093f4SDimitry Andric } 4278480093f4SDimitry Andric 4279480093f4SDimitry Andric void Initialize() { 4280480093f4SDimitry Andric m_screen = ::newterm(nullptr, m_out, m_in); 4281480093f4SDimitry Andric ::start_color(); 4282480093f4SDimitry Andric ::curs_set(0); 4283480093f4SDimitry Andric ::noecho(); 4284480093f4SDimitry Andric ::keypad(stdscr, TRUE); 4285480093f4SDimitry Andric } 4286480093f4SDimitry Andric 4287480093f4SDimitry Andric void Terminate() { ::endwin(); } 4288480093f4SDimitry Andric 4289480093f4SDimitry Andric void Run(Debugger &debugger) { 4290480093f4SDimitry Andric bool done = false; 4291480093f4SDimitry Andric int delay_in_tenths_of_a_second = 1; 4292480093f4SDimitry Andric 4293349cc55cSDimitry Andric // Alas the threading model in curses is a bit lame so we need to resort 4294349cc55cSDimitry Andric // to polling every 0.5 seconds. We could poll for stdin ourselves and 4295349cc55cSDimitry Andric // then pass the keys down but then we need to translate all of the escape 4296480093f4SDimitry Andric // sequences ourselves. So we resort to polling for input because we need 4297480093f4SDimitry Andric // to receive async process events while in this loop. 4298480093f4SDimitry Andric 4299349cc55cSDimitry Andric halfdelay(delay_in_tenths_of_a_second); // Poll using some number of 4300349cc55cSDimitry Andric // tenths of seconds seconds when 4301349cc55cSDimitry Andric // calling Window::GetChar() 4302480093f4SDimitry Andric 4303480093f4SDimitry Andric ListenerSP listener_sp( 4304480093f4SDimitry Andric Listener::MakeListener("lldb.IOHandler.curses.Application")); 4305480093f4SDimitry Andric ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass()); 4306480093f4SDimitry Andric debugger.EnableForwardEvents(listener_sp); 4307480093f4SDimitry Andric 4308e8d8bef9SDimitry Andric m_update_screen = true; 4309480093f4SDimitry Andric #if defined(__APPLE__) 4310480093f4SDimitry Andric std::deque<int> escape_chars; 4311480093f4SDimitry Andric #endif 4312480093f4SDimitry Andric 4313480093f4SDimitry Andric while (!done) { 4314e8d8bef9SDimitry Andric if (m_update_screen) { 4315480093f4SDimitry Andric m_window_sp->Draw(false); 4316480093f4SDimitry Andric // All windows should be calling Window::DeferredRefresh() instead of 4317480093f4SDimitry Andric // Window::Refresh() so we can do a single update and avoid any screen 4318480093f4SDimitry Andric // blinking 4319480093f4SDimitry Andric update_panels(); 4320480093f4SDimitry Andric 4321480093f4SDimitry Andric // Cursor hiding isn't working on MacOSX, so hide it in the top left 4322480093f4SDimitry Andric // corner 4323480093f4SDimitry Andric m_window_sp->MoveCursor(0, 0); 4324480093f4SDimitry Andric 4325480093f4SDimitry Andric doupdate(); 4326e8d8bef9SDimitry Andric m_update_screen = false; 4327480093f4SDimitry Andric } 4328480093f4SDimitry Andric 4329480093f4SDimitry Andric #if defined(__APPLE__) 4330480093f4SDimitry Andric // Terminal.app doesn't map its function keys correctly, F1-F4 default 4331480093f4SDimitry Andric // to: \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if 4332480093f4SDimitry Andric // possible 4333480093f4SDimitry Andric int ch; 4334480093f4SDimitry Andric if (escape_chars.empty()) 4335480093f4SDimitry Andric ch = m_window_sp->GetChar(); 4336480093f4SDimitry Andric else { 4337480093f4SDimitry Andric ch = escape_chars.front(); 4338480093f4SDimitry Andric escape_chars.pop_front(); 4339480093f4SDimitry Andric } 4340480093f4SDimitry Andric if (ch == KEY_ESCAPE) { 4341480093f4SDimitry Andric int ch2 = m_window_sp->GetChar(); 4342480093f4SDimitry Andric if (ch2 == 'O') { 4343480093f4SDimitry Andric int ch3 = m_window_sp->GetChar(); 4344480093f4SDimitry Andric switch (ch3) { 4345480093f4SDimitry Andric case 'P': 4346480093f4SDimitry Andric ch = KEY_F(1); 4347480093f4SDimitry Andric break; 4348480093f4SDimitry Andric case 'Q': 4349480093f4SDimitry Andric ch = KEY_F(2); 4350480093f4SDimitry Andric break; 4351480093f4SDimitry Andric case 'R': 4352480093f4SDimitry Andric ch = KEY_F(3); 4353480093f4SDimitry Andric break; 4354480093f4SDimitry Andric case 'S': 4355480093f4SDimitry Andric ch = KEY_F(4); 4356480093f4SDimitry Andric break; 4357480093f4SDimitry Andric default: 4358480093f4SDimitry Andric escape_chars.push_back(ch2); 4359480093f4SDimitry Andric if (ch3 != -1) 4360480093f4SDimitry Andric escape_chars.push_back(ch3); 4361480093f4SDimitry Andric break; 4362480093f4SDimitry Andric } 4363480093f4SDimitry Andric } else if (ch2 != -1) 4364480093f4SDimitry Andric escape_chars.push_back(ch2); 4365480093f4SDimitry Andric } 4366480093f4SDimitry Andric #else 4367480093f4SDimitry Andric int ch = m_window_sp->GetChar(); 4368480093f4SDimitry Andric 4369480093f4SDimitry Andric #endif 4370480093f4SDimitry Andric if (ch == -1) { 4371480093f4SDimitry Andric if (feof(m_in) || ferror(m_in)) { 4372480093f4SDimitry Andric done = true; 4373480093f4SDimitry Andric } else { 4374480093f4SDimitry Andric // Just a timeout from using halfdelay(), check for events 4375480093f4SDimitry Andric EventSP event_sp; 4376480093f4SDimitry Andric while (listener_sp->PeekAtNextEvent()) { 4377480093f4SDimitry Andric listener_sp->GetEvent(event_sp, std::chrono::seconds(0)); 4378480093f4SDimitry Andric 4379480093f4SDimitry Andric if (event_sp) { 4380480093f4SDimitry Andric Broadcaster *broadcaster = event_sp->GetBroadcaster(); 4381480093f4SDimitry Andric if (broadcaster) { 4382480093f4SDimitry Andric // uint32_t event_type = event_sp->GetType(); 4383480093f4SDimitry Andric ConstString broadcaster_class( 4384480093f4SDimitry Andric broadcaster->GetBroadcasterClass()); 4385480093f4SDimitry Andric if (broadcaster_class == broadcaster_class_process) { 4386e8d8bef9SDimitry Andric m_update_screen = true; 4387480093f4SDimitry Andric continue; // Don't get any key, just update our view 4388480093f4SDimitry Andric } 4389480093f4SDimitry Andric } 4390480093f4SDimitry Andric } 4391480093f4SDimitry Andric } 4392480093f4SDimitry Andric } 4393480093f4SDimitry Andric } else { 4394480093f4SDimitry Andric HandleCharResult key_result = m_window_sp->HandleChar(ch); 4395480093f4SDimitry Andric switch (key_result) { 4396480093f4SDimitry Andric case eKeyHandled: 4397e8d8bef9SDimitry Andric m_update_screen = true; 4398480093f4SDimitry Andric break; 4399480093f4SDimitry Andric case eKeyNotHandled: 4400e8d8bef9SDimitry Andric if (ch == 12) { // Ctrl+L, force full redraw 4401e8d8bef9SDimitry Andric redrawwin(m_window_sp->get()); 4402e8d8bef9SDimitry Andric m_update_screen = true; 4403e8d8bef9SDimitry Andric } 4404480093f4SDimitry Andric break; 4405480093f4SDimitry Andric case eQuitApplication: 4406480093f4SDimitry Andric done = true; 4407480093f4SDimitry Andric break; 4408480093f4SDimitry Andric } 4409480093f4SDimitry Andric } 4410480093f4SDimitry Andric } 4411480093f4SDimitry Andric 4412480093f4SDimitry Andric debugger.CancelForwardEvents(listener_sp); 4413480093f4SDimitry Andric } 4414480093f4SDimitry Andric 4415480093f4SDimitry Andric WindowSP &GetMainWindow() { 4416480093f4SDimitry Andric if (!m_window_sp) 4417480093f4SDimitry Andric m_window_sp = std::make_shared<Window>("main", stdscr, false); 4418480093f4SDimitry Andric return m_window_sp; 4419480093f4SDimitry Andric } 4420480093f4SDimitry Andric 4421e8d8bef9SDimitry Andric void TerminalSizeChanged() { 4422e8d8bef9SDimitry Andric ::endwin(); 4423e8d8bef9SDimitry Andric ::refresh(); 4424e8d8bef9SDimitry Andric Rect content_bounds = m_window_sp->GetFrame(); 4425e8d8bef9SDimitry Andric m_window_sp->SetBounds(content_bounds); 4426e8d8bef9SDimitry Andric if (WindowSP menubar_window_sp = m_window_sp->FindSubWindow("Menubar")) 4427e8d8bef9SDimitry Andric menubar_window_sp->SetBounds(content_bounds.MakeMenuBar()); 4428e8d8bef9SDimitry Andric if (WindowSP status_window_sp = m_window_sp->FindSubWindow("Status")) 4429e8d8bef9SDimitry Andric status_window_sp->SetBounds(content_bounds.MakeStatusBar()); 4430e8d8bef9SDimitry Andric 4431e8d8bef9SDimitry Andric WindowSP source_window_sp = m_window_sp->FindSubWindow("Source"); 4432e8d8bef9SDimitry Andric WindowSP variables_window_sp = m_window_sp->FindSubWindow("Variables"); 4433e8d8bef9SDimitry Andric WindowSP registers_window_sp = m_window_sp->FindSubWindow("Registers"); 4434e8d8bef9SDimitry Andric WindowSP threads_window_sp = m_window_sp->FindSubWindow("Threads"); 4435e8d8bef9SDimitry Andric 4436e8d8bef9SDimitry Andric Rect threads_bounds; 4437e8d8bef9SDimitry Andric Rect source_variables_bounds; 4438e8d8bef9SDimitry Andric content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds, 4439e8d8bef9SDimitry Andric threads_bounds); 4440e8d8bef9SDimitry Andric if (threads_window_sp) 4441e8d8bef9SDimitry Andric threads_window_sp->SetBounds(threads_bounds); 4442e8d8bef9SDimitry Andric else 4443e8d8bef9SDimitry Andric source_variables_bounds = content_bounds; 4444e8d8bef9SDimitry Andric 4445e8d8bef9SDimitry Andric Rect source_bounds; 4446e8d8bef9SDimitry Andric Rect variables_registers_bounds; 4447e8d8bef9SDimitry Andric source_variables_bounds.HorizontalSplitPercentage( 4448e8d8bef9SDimitry Andric 0.70, source_bounds, variables_registers_bounds); 4449e8d8bef9SDimitry Andric if (variables_window_sp || registers_window_sp) { 4450e8d8bef9SDimitry Andric if (variables_window_sp && registers_window_sp) { 4451e8d8bef9SDimitry Andric Rect variables_bounds; 4452e8d8bef9SDimitry Andric Rect registers_bounds; 4453e8d8bef9SDimitry Andric variables_registers_bounds.VerticalSplitPercentage( 4454e8d8bef9SDimitry Andric 0.50, variables_bounds, registers_bounds); 4455e8d8bef9SDimitry Andric variables_window_sp->SetBounds(variables_bounds); 4456e8d8bef9SDimitry Andric registers_window_sp->SetBounds(registers_bounds); 4457e8d8bef9SDimitry Andric } else if (variables_window_sp) { 4458e8d8bef9SDimitry Andric variables_window_sp->SetBounds(variables_registers_bounds); 4459e8d8bef9SDimitry Andric } else { 4460e8d8bef9SDimitry Andric registers_window_sp->SetBounds(variables_registers_bounds); 4461e8d8bef9SDimitry Andric } 4462e8d8bef9SDimitry Andric } else { 4463e8d8bef9SDimitry Andric source_bounds = source_variables_bounds; 4464e8d8bef9SDimitry Andric } 4465e8d8bef9SDimitry Andric 4466e8d8bef9SDimitry Andric source_window_sp->SetBounds(source_bounds); 4467e8d8bef9SDimitry Andric 4468e8d8bef9SDimitry Andric touchwin(stdscr); 4469e8d8bef9SDimitry Andric redrawwin(m_window_sp->get()); 4470e8d8bef9SDimitry Andric m_update_screen = true; 4471e8d8bef9SDimitry Andric } 4472e8d8bef9SDimitry Andric 4473480093f4SDimitry Andric protected: 4474480093f4SDimitry Andric WindowSP m_window_sp; 4475480093f4SDimitry Andric WindowDelegates m_window_delegates; 447681ad6265SDimitry Andric SCREEN *m_screen = nullptr; 4477480093f4SDimitry Andric FILE *m_in; 4478480093f4SDimitry Andric FILE *m_out; 4479e8d8bef9SDimitry Andric bool m_update_screen = false; 4480480093f4SDimitry Andric }; 4481480093f4SDimitry Andric 4482480093f4SDimitry Andric } // namespace curses 4483480093f4SDimitry Andric 4484480093f4SDimitry Andric using namespace curses; 4485480093f4SDimitry Andric 4486480093f4SDimitry Andric struct Row { 4487fe6060f1SDimitry Andric ValueObjectUpdater value; 4488480093f4SDimitry Andric Row *parent; 4489480093f4SDimitry Andric // The process stop ID when the children were calculated. 4490e8d8bef9SDimitry Andric uint32_t children_stop_id = 0; 4491e8d8bef9SDimitry Andric int row_idx = 0; 4492e8d8bef9SDimitry Andric int x = 1; 4493e8d8bef9SDimitry Andric int y = 1; 4494480093f4SDimitry Andric bool might_have_children; 4495e8d8bef9SDimitry Andric bool expanded = false; 4496e8d8bef9SDimitry Andric bool calculated_children = false; 4497480093f4SDimitry Andric std::vector<Row> children; 4498480093f4SDimitry Andric 4499480093f4SDimitry Andric Row(const ValueObjectSP &v, Row *p) 4500fe6060f1SDimitry Andric : value(v), parent(p), 4501e8d8bef9SDimitry Andric might_have_children(v ? v->MightHaveChildren() : false) {} 4502480093f4SDimitry Andric 4503480093f4SDimitry Andric size_t GetDepth() const { 4504480093f4SDimitry Andric if (parent) 4505480093f4SDimitry Andric return 1 + parent->GetDepth(); 4506480093f4SDimitry Andric return 0; 4507480093f4SDimitry Andric } 4508480093f4SDimitry Andric 4509480093f4SDimitry Andric void Expand() { expanded = true; } 4510480093f4SDimitry Andric 4511480093f4SDimitry Andric std::vector<Row> &GetChildren() { 4512480093f4SDimitry Andric ProcessSP process_sp = value.GetProcessSP(); 4513480093f4SDimitry Andric auto stop_id = process_sp->GetStopID(); 4514480093f4SDimitry Andric if (process_sp && stop_id != children_stop_id) { 4515480093f4SDimitry Andric children_stop_id = stop_id; 4516480093f4SDimitry Andric calculated_children = false; 4517480093f4SDimitry Andric } 4518480093f4SDimitry Andric if (!calculated_children) { 4519480093f4SDimitry Andric children.clear(); 4520480093f4SDimitry Andric calculated_children = true; 4521480093f4SDimitry Andric ValueObjectSP valobj = value.GetSP(); 4522480093f4SDimitry Andric if (valobj) { 4523480093f4SDimitry Andric const size_t num_children = valobj->GetNumChildren(); 4524480093f4SDimitry Andric for (size_t i = 0; i < num_children; ++i) { 4525*06c3fb27SDimitry Andric children.push_back(Row(valobj->GetChildAtIndex(i), this)); 4526480093f4SDimitry Andric } 4527480093f4SDimitry Andric } 4528480093f4SDimitry Andric } 4529480093f4SDimitry Andric return children; 4530480093f4SDimitry Andric } 4531480093f4SDimitry Andric 4532480093f4SDimitry Andric void Unexpand() { 4533480093f4SDimitry Andric expanded = false; 4534480093f4SDimitry Andric calculated_children = false; 4535480093f4SDimitry Andric children.clear(); 4536480093f4SDimitry Andric } 4537480093f4SDimitry Andric 4538480093f4SDimitry Andric void DrawTree(Window &window) { 4539480093f4SDimitry Andric if (parent) 4540480093f4SDimitry Andric parent->DrawTreeForChild(window, this, 0); 4541480093f4SDimitry Andric 454281ad6265SDimitry Andric if (might_have_children && 454381ad6265SDimitry Andric (!calculated_children || !GetChildren().empty())) { 4544480093f4SDimitry Andric // It we can get UTF8 characters to work we should try to use the 4545480093f4SDimitry Andric // "symbol" UTF8 string below 4546480093f4SDimitry Andric // const char *symbol = ""; 4547480093f4SDimitry Andric // if (row.expanded) 4548480093f4SDimitry Andric // symbol = "\xe2\x96\xbd "; 4549480093f4SDimitry Andric // else 4550480093f4SDimitry Andric // symbol = "\xe2\x96\xb7 "; 4551480093f4SDimitry Andric // window.PutCString (symbol); 4552480093f4SDimitry Andric 4553480093f4SDimitry Andric // The ACS_DARROW and ACS_RARROW don't look very nice they are just a 'v' 4554480093f4SDimitry Andric // or '>' character... 4555480093f4SDimitry Andric // if (expanded) 4556480093f4SDimitry Andric // window.PutChar (ACS_DARROW); 4557480093f4SDimitry Andric // else 4558480093f4SDimitry Andric // window.PutChar (ACS_RARROW); 4559480093f4SDimitry Andric // Since we can't find any good looking right arrow/down arrow symbols, 4560480093f4SDimitry Andric // just use a diamond... 4561480093f4SDimitry Andric window.PutChar(ACS_DIAMOND); 4562480093f4SDimitry Andric window.PutChar(ACS_HLINE); 4563480093f4SDimitry Andric } 4564480093f4SDimitry Andric } 4565480093f4SDimitry Andric 4566480093f4SDimitry Andric void DrawTreeForChild(Window &window, Row *child, uint32_t reverse_depth) { 4567480093f4SDimitry Andric if (parent) 4568480093f4SDimitry Andric parent->DrawTreeForChild(window, this, reverse_depth + 1); 4569480093f4SDimitry Andric 4570480093f4SDimitry Andric if (&GetChildren().back() == child) { 4571480093f4SDimitry Andric // Last child 4572480093f4SDimitry Andric if (reverse_depth == 0) { 4573480093f4SDimitry Andric window.PutChar(ACS_LLCORNER); 4574480093f4SDimitry Andric window.PutChar(ACS_HLINE); 4575480093f4SDimitry Andric } else { 4576480093f4SDimitry Andric window.PutChar(' '); 4577480093f4SDimitry Andric window.PutChar(' '); 4578480093f4SDimitry Andric } 4579480093f4SDimitry Andric } else { 4580480093f4SDimitry Andric if (reverse_depth == 0) { 4581480093f4SDimitry Andric window.PutChar(ACS_LTEE); 4582480093f4SDimitry Andric window.PutChar(ACS_HLINE); 4583480093f4SDimitry Andric } else { 4584480093f4SDimitry Andric window.PutChar(ACS_VLINE); 4585480093f4SDimitry Andric window.PutChar(' '); 4586480093f4SDimitry Andric } 4587480093f4SDimitry Andric } 4588480093f4SDimitry Andric } 4589480093f4SDimitry Andric }; 4590480093f4SDimitry Andric 4591480093f4SDimitry Andric struct DisplayOptions { 4592480093f4SDimitry Andric bool show_types; 4593480093f4SDimitry Andric }; 4594480093f4SDimitry Andric 4595480093f4SDimitry Andric class TreeItem; 4596480093f4SDimitry Andric 4597480093f4SDimitry Andric class TreeDelegate { 4598480093f4SDimitry Andric public: 4599480093f4SDimitry Andric TreeDelegate() = default; 4600480093f4SDimitry Andric virtual ~TreeDelegate() = default; 4601480093f4SDimitry Andric 4602480093f4SDimitry Andric virtual void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) = 0; 4603480093f4SDimitry Andric virtual void TreeDelegateGenerateChildren(TreeItem &item) = 0; 4604fe6060f1SDimitry Andric virtual void TreeDelegateUpdateSelection(TreeItem &root, int &selection_index, 46050eae32dcSDimitry Andric TreeItem *&selected_item) {} 4606349cc55cSDimitry Andric // This is invoked when a tree item is selected. If true is returned, the 4607349cc55cSDimitry Andric // views are updated. 4608349cc55cSDimitry Andric virtual bool TreeDelegateItemSelected(TreeItem &item) = 0; 4609fe6060f1SDimitry Andric virtual bool TreeDelegateExpandRootByDefault() { return false; } 4610349cc55cSDimitry Andric // This is mostly useful for root tree delegates. If false is returned, 4611349cc55cSDimitry Andric // drawing will be skipped completely. This is needed, for instance, in 4612349cc55cSDimitry Andric // skipping drawing of the threads tree if there is no running process. 4613349cc55cSDimitry Andric virtual bool TreeDelegateShouldDraw() { return true; } 4614480093f4SDimitry Andric }; 4615480093f4SDimitry Andric 4616480093f4SDimitry Andric typedef std::shared_ptr<TreeDelegate> TreeDelegateSP; 4617480093f4SDimitry Andric 4618480093f4SDimitry Andric class TreeItem { 4619480093f4SDimitry Andric public: 4620480093f4SDimitry Andric TreeItem(TreeItem *parent, TreeDelegate &delegate, bool might_have_children) 462181ad6265SDimitry Andric : m_parent(parent), m_delegate(delegate), m_children(), 462281ad6265SDimitry Andric m_might_have_children(might_have_children) { 4623fe6060f1SDimitry Andric if (m_parent == nullptr) 4624fe6060f1SDimitry Andric m_is_expanded = m_delegate.TreeDelegateExpandRootByDefault(); 4625fe6060f1SDimitry Andric } 4626480093f4SDimitry Andric 4627480093f4SDimitry Andric TreeItem &operator=(const TreeItem &rhs) { 4628480093f4SDimitry Andric if (this != &rhs) { 4629480093f4SDimitry Andric m_parent = rhs.m_parent; 4630480093f4SDimitry Andric m_delegate = rhs.m_delegate; 4631480093f4SDimitry Andric m_user_data = rhs.m_user_data; 4632480093f4SDimitry Andric m_identifier = rhs.m_identifier; 4633480093f4SDimitry Andric m_row_idx = rhs.m_row_idx; 4634480093f4SDimitry Andric m_children = rhs.m_children; 4635480093f4SDimitry Andric m_might_have_children = rhs.m_might_have_children; 4636480093f4SDimitry Andric m_is_expanded = rhs.m_is_expanded; 4637480093f4SDimitry Andric } 4638480093f4SDimitry Andric return *this; 4639480093f4SDimitry Andric } 4640480093f4SDimitry Andric 4641480093f4SDimitry Andric TreeItem(const TreeItem &) = default; 4642480093f4SDimitry Andric 4643480093f4SDimitry Andric size_t GetDepth() const { 4644480093f4SDimitry Andric if (m_parent) 4645480093f4SDimitry Andric return 1 + m_parent->GetDepth(); 4646480093f4SDimitry Andric return 0; 4647480093f4SDimitry Andric } 4648480093f4SDimitry Andric 4649480093f4SDimitry Andric int GetRowIndex() const { return m_row_idx; } 4650480093f4SDimitry Andric 4651480093f4SDimitry Andric void ClearChildren() { m_children.clear(); } 4652480093f4SDimitry Andric 4653480093f4SDimitry Andric void Resize(size_t n, const TreeItem &t) { m_children.resize(n, t); } 4654480093f4SDimitry Andric 4655480093f4SDimitry Andric TreeItem &operator[](size_t i) { return m_children[i]; } 4656480093f4SDimitry Andric 4657480093f4SDimitry Andric void SetRowIndex(int row_idx) { m_row_idx = row_idx; } 4658480093f4SDimitry Andric 4659480093f4SDimitry Andric size_t GetNumChildren() { 4660480093f4SDimitry Andric m_delegate.TreeDelegateGenerateChildren(*this); 4661480093f4SDimitry Andric return m_children.size(); 4662480093f4SDimitry Andric } 4663480093f4SDimitry Andric 4664480093f4SDimitry Andric void ItemWasSelected() { m_delegate.TreeDelegateItemSelected(*this); } 4665480093f4SDimitry Andric 4666480093f4SDimitry Andric void CalculateRowIndexes(int &row_idx) { 4667480093f4SDimitry Andric SetRowIndex(row_idx); 4668480093f4SDimitry Andric ++row_idx; 4669480093f4SDimitry Andric 4670480093f4SDimitry Andric const bool expanded = IsExpanded(); 4671480093f4SDimitry Andric 4672480093f4SDimitry Andric // The root item must calculate its children, or we must calculate the 4673480093f4SDimitry Andric // number of children if the item is expanded 4674480093f4SDimitry Andric if (m_parent == nullptr || expanded) 4675480093f4SDimitry Andric GetNumChildren(); 4676480093f4SDimitry Andric 4677480093f4SDimitry Andric for (auto &item : m_children) { 4678480093f4SDimitry Andric if (expanded) 4679480093f4SDimitry Andric item.CalculateRowIndexes(row_idx); 4680480093f4SDimitry Andric else 4681480093f4SDimitry Andric item.SetRowIndex(-1); 4682480093f4SDimitry Andric } 4683480093f4SDimitry Andric } 4684480093f4SDimitry Andric 4685480093f4SDimitry Andric TreeItem *GetParent() { return m_parent; } 4686480093f4SDimitry Andric 4687480093f4SDimitry Andric bool IsExpanded() const { return m_is_expanded; } 4688480093f4SDimitry Andric 4689480093f4SDimitry Andric void Expand() { m_is_expanded = true; } 4690480093f4SDimitry Andric 4691480093f4SDimitry Andric void Unexpand() { m_is_expanded = false; } 4692480093f4SDimitry Andric 4693480093f4SDimitry Andric bool Draw(Window &window, const int first_visible_row, 4694480093f4SDimitry Andric const uint32_t selected_row_idx, int &row_idx, int &num_rows_left) { 4695480093f4SDimitry Andric if (num_rows_left <= 0) 4696480093f4SDimitry Andric return false; 4697480093f4SDimitry Andric 4698480093f4SDimitry Andric if (m_row_idx >= first_visible_row) { 4699480093f4SDimitry Andric window.MoveCursor(2, row_idx + 1); 4700480093f4SDimitry Andric 4701480093f4SDimitry Andric if (m_parent) 4702480093f4SDimitry Andric m_parent->DrawTreeForChild(window, this, 0); 4703480093f4SDimitry Andric 4704480093f4SDimitry Andric if (m_might_have_children) { 4705480093f4SDimitry Andric // It we can get UTF8 characters to work we should try to use the 4706480093f4SDimitry Andric // "symbol" UTF8 string below 4707480093f4SDimitry Andric // const char *symbol = ""; 4708480093f4SDimitry Andric // if (row.expanded) 4709480093f4SDimitry Andric // symbol = "\xe2\x96\xbd "; 4710480093f4SDimitry Andric // else 4711480093f4SDimitry Andric // symbol = "\xe2\x96\xb7 "; 4712480093f4SDimitry Andric // window.PutCString (symbol); 4713480093f4SDimitry Andric 4714480093f4SDimitry Andric // The ACS_DARROW and ACS_RARROW don't look very nice they are just a 4715480093f4SDimitry Andric // 'v' or '>' character... 4716480093f4SDimitry Andric // if (expanded) 4717480093f4SDimitry Andric // window.PutChar (ACS_DARROW); 4718480093f4SDimitry Andric // else 4719480093f4SDimitry Andric // window.PutChar (ACS_RARROW); 4720480093f4SDimitry Andric // Since we can't find any good looking right arrow/down arrow symbols, 4721480093f4SDimitry Andric // just use a diamond... 4722480093f4SDimitry Andric window.PutChar(ACS_DIAMOND); 4723480093f4SDimitry Andric window.PutChar(ACS_HLINE); 4724480093f4SDimitry Andric } 4725480093f4SDimitry Andric bool highlight = (selected_row_idx == static_cast<size_t>(m_row_idx)) && 4726480093f4SDimitry Andric window.IsActive(); 4727480093f4SDimitry Andric 4728480093f4SDimitry Andric if (highlight) 4729480093f4SDimitry Andric window.AttributeOn(A_REVERSE); 4730480093f4SDimitry Andric 4731480093f4SDimitry Andric m_delegate.TreeDelegateDrawTreeItem(*this, window); 4732480093f4SDimitry Andric 4733480093f4SDimitry Andric if (highlight) 4734480093f4SDimitry Andric window.AttributeOff(A_REVERSE); 4735480093f4SDimitry Andric ++row_idx; 4736480093f4SDimitry Andric --num_rows_left; 4737480093f4SDimitry Andric } 4738480093f4SDimitry Andric 4739480093f4SDimitry Andric if (num_rows_left <= 0) 4740480093f4SDimitry Andric return false; // We are done drawing... 4741480093f4SDimitry Andric 4742480093f4SDimitry Andric if (IsExpanded()) { 4743480093f4SDimitry Andric for (auto &item : m_children) { 4744480093f4SDimitry Andric // If we displayed all the rows and item.Draw() returns false we are 4745480093f4SDimitry Andric // done drawing and can exit this for loop 4746480093f4SDimitry Andric if (!item.Draw(window, first_visible_row, selected_row_idx, row_idx, 4747480093f4SDimitry Andric num_rows_left)) 4748480093f4SDimitry Andric break; 4749480093f4SDimitry Andric } 4750480093f4SDimitry Andric } 4751480093f4SDimitry Andric return num_rows_left >= 0; // Return true if not done drawing yet 4752480093f4SDimitry Andric } 4753480093f4SDimitry Andric 4754480093f4SDimitry Andric void DrawTreeForChild(Window &window, TreeItem *child, 4755480093f4SDimitry Andric uint32_t reverse_depth) { 4756480093f4SDimitry Andric if (m_parent) 4757480093f4SDimitry Andric m_parent->DrawTreeForChild(window, this, reverse_depth + 1); 4758480093f4SDimitry Andric 4759480093f4SDimitry Andric if (&m_children.back() == child) { 4760480093f4SDimitry Andric // Last child 4761480093f4SDimitry Andric if (reverse_depth == 0) { 4762480093f4SDimitry Andric window.PutChar(ACS_LLCORNER); 4763480093f4SDimitry Andric window.PutChar(ACS_HLINE); 4764480093f4SDimitry Andric } else { 4765480093f4SDimitry Andric window.PutChar(' '); 4766480093f4SDimitry Andric window.PutChar(' '); 4767480093f4SDimitry Andric } 4768480093f4SDimitry Andric } else { 4769480093f4SDimitry Andric if (reverse_depth == 0) { 4770480093f4SDimitry Andric window.PutChar(ACS_LTEE); 4771480093f4SDimitry Andric window.PutChar(ACS_HLINE); 4772480093f4SDimitry Andric } else { 4773480093f4SDimitry Andric window.PutChar(ACS_VLINE); 4774480093f4SDimitry Andric window.PutChar(' '); 4775480093f4SDimitry Andric } 4776480093f4SDimitry Andric } 4777480093f4SDimitry Andric } 4778480093f4SDimitry Andric 4779480093f4SDimitry Andric TreeItem *GetItemForRowIndex(uint32_t row_idx) { 4780480093f4SDimitry Andric if (static_cast<uint32_t>(m_row_idx) == row_idx) 4781480093f4SDimitry Andric return this; 4782480093f4SDimitry Andric if (m_children.empty()) 4783480093f4SDimitry Andric return nullptr; 4784480093f4SDimitry Andric if (IsExpanded()) { 4785480093f4SDimitry Andric for (auto &item : m_children) { 4786480093f4SDimitry Andric TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx); 4787480093f4SDimitry Andric if (selected_item_ptr) 4788480093f4SDimitry Andric return selected_item_ptr; 4789480093f4SDimitry Andric } 4790480093f4SDimitry Andric } 4791480093f4SDimitry Andric return nullptr; 4792480093f4SDimitry Andric } 4793480093f4SDimitry Andric 4794480093f4SDimitry Andric void *GetUserData() const { return m_user_data; } 4795480093f4SDimitry Andric 4796480093f4SDimitry Andric void SetUserData(void *user_data) { m_user_data = user_data; } 4797480093f4SDimitry Andric 4798480093f4SDimitry Andric uint64_t GetIdentifier() const { return m_identifier; } 4799480093f4SDimitry Andric 4800480093f4SDimitry Andric void SetIdentifier(uint64_t identifier) { m_identifier = identifier; } 4801480093f4SDimitry Andric 4802349cc55cSDimitry Andric const std::string &GetText() const { return m_text; } 4803349cc55cSDimitry Andric 4804349cc55cSDimitry Andric void SetText(const char *text) { 4805349cc55cSDimitry Andric if (text == nullptr) { 4806349cc55cSDimitry Andric m_text.clear(); 4807349cc55cSDimitry Andric return; 4808349cc55cSDimitry Andric } 4809349cc55cSDimitry Andric m_text = text; 4810349cc55cSDimitry Andric } 4811349cc55cSDimitry Andric 4812480093f4SDimitry Andric void SetMightHaveChildren(bool b) { m_might_have_children = b; } 4813480093f4SDimitry Andric 4814480093f4SDimitry Andric protected: 4815480093f4SDimitry Andric TreeItem *m_parent; 4816480093f4SDimitry Andric TreeDelegate &m_delegate; 481781ad6265SDimitry Andric void *m_user_data = nullptr; 481881ad6265SDimitry Andric uint64_t m_identifier = 0; 4819349cc55cSDimitry Andric std::string m_text; 482081ad6265SDimitry Andric int m_row_idx = -1; // Zero based visible row index, -1 if not visible or for 482181ad6265SDimitry Andric // the root item 4822480093f4SDimitry Andric std::vector<TreeItem> m_children; 4823480093f4SDimitry Andric bool m_might_have_children; 482481ad6265SDimitry Andric bool m_is_expanded = false; 4825480093f4SDimitry Andric }; 4826480093f4SDimitry Andric 4827480093f4SDimitry Andric class TreeWindowDelegate : public WindowDelegate { 4828480093f4SDimitry Andric public: 4829480093f4SDimitry Andric TreeWindowDelegate(Debugger &debugger, const TreeDelegateSP &delegate_sp) 4830480093f4SDimitry Andric : m_debugger(debugger), m_delegate_sp(delegate_sp), 483181ad6265SDimitry Andric m_root(nullptr, *delegate_sp, true) {} 4832480093f4SDimitry Andric 4833480093f4SDimitry Andric int NumVisibleRows() const { return m_max_y - m_min_y; } 4834480093f4SDimitry Andric 4835480093f4SDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override { 4836480093f4SDimitry Andric m_min_x = 2; 4837480093f4SDimitry Andric m_min_y = 1; 4838480093f4SDimitry Andric m_max_x = window.GetWidth() - 1; 4839480093f4SDimitry Andric m_max_y = window.GetHeight() - 1; 4840480093f4SDimitry Andric 4841480093f4SDimitry Andric window.Erase(); 4842480093f4SDimitry Andric window.DrawTitleBox(window.GetName()); 4843480093f4SDimitry Andric 4844349cc55cSDimitry Andric if (!m_delegate_sp->TreeDelegateShouldDraw()) { 4845349cc55cSDimitry Andric m_selected_item = nullptr; 4846349cc55cSDimitry Andric return true; 4847349cc55cSDimitry Andric } 4848349cc55cSDimitry Andric 4849480093f4SDimitry Andric const int num_visible_rows = NumVisibleRows(); 4850480093f4SDimitry Andric m_num_rows = 0; 4851480093f4SDimitry Andric m_root.CalculateRowIndexes(m_num_rows); 4852fe6060f1SDimitry Andric m_delegate_sp->TreeDelegateUpdateSelection(m_root, m_selected_row_idx, 4853fe6060f1SDimitry Andric m_selected_item); 4854480093f4SDimitry Andric 4855480093f4SDimitry Andric // If we unexpanded while having something selected our total number of 4856480093f4SDimitry Andric // rows is less than the num visible rows, then make sure we show all the 4857480093f4SDimitry Andric // rows by setting the first visible row accordingly. 4858480093f4SDimitry Andric if (m_first_visible_row > 0 && m_num_rows < num_visible_rows) 4859480093f4SDimitry Andric m_first_visible_row = 0; 4860480093f4SDimitry Andric 4861480093f4SDimitry Andric // Make sure the selected row is always visible 4862480093f4SDimitry Andric if (m_selected_row_idx < m_first_visible_row) 4863480093f4SDimitry Andric m_first_visible_row = m_selected_row_idx; 4864480093f4SDimitry Andric else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx) 4865480093f4SDimitry Andric m_first_visible_row = m_selected_row_idx - num_visible_rows + 1; 4866480093f4SDimitry Andric 4867480093f4SDimitry Andric int row_idx = 0; 4868480093f4SDimitry Andric int num_rows_left = num_visible_rows; 4869480093f4SDimitry Andric m_root.Draw(window, m_first_visible_row, m_selected_row_idx, row_idx, 4870480093f4SDimitry Andric num_rows_left); 4871480093f4SDimitry Andric // Get the selected row 4872480093f4SDimitry Andric m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); 4873480093f4SDimitry Andric 4874480093f4SDimitry Andric return true; // Drawing handled 4875480093f4SDimitry Andric } 4876480093f4SDimitry Andric 4877480093f4SDimitry Andric const char *WindowDelegateGetHelpText() override { 4878480093f4SDimitry Andric return "Thread window keyboard shortcuts:"; 4879480093f4SDimitry Andric } 4880480093f4SDimitry Andric 4881480093f4SDimitry Andric KeyHelp *WindowDelegateGetKeyHelp() override { 4882480093f4SDimitry Andric static curses::KeyHelp g_source_view_key_help[] = { 4883480093f4SDimitry Andric {KEY_UP, "Select previous item"}, 4884480093f4SDimitry Andric {KEY_DOWN, "Select next item"}, 4885480093f4SDimitry Andric {KEY_RIGHT, "Expand the selected item"}, 4886480093f4SDimitry Andric {KEY_LEFT, 4887480093f4SDimitry Andric "Unexpand the selected item or select parent if not expanded"}, 4888480093f4SDimitry Andric {KEY_PPAGE, "Page up"}, 4889480093f4SDimitry Andric {KEY_NPAGE, "Page down"}, 4890480093f4SDimitry Andric {'h', "Show help dialog"}, 4891480093f4SDimitry Andric {' ', "Toggle item expansion"}, 4892480093f4SDimitry Andric {',', "Page up"}, 4893480093f4SDimitry Andric {'.', "Page down"}, 4894480093f4SDimitry Andric {'\0', nullptr}}; 4895480093f4SDimitry Andric return g_source_view_key_help; 4896480093f4SDimitry Andric } 4897480093f4SDimitry Andric 4898480093f4SDimitry Andric HandleCharResult WindowDelegateHandleChar(Window &window, int c) override { 4899480093f4SDimitry Andric switch (c) { 4900480093f4SDimitry Andric case ',': 4901480093f4SDimitry Andric case KEY_PPAGE: 4902480093f4SDimitry Andric // Page up key 4903480093f4SDimitry Andric if (m_first_visible_row > 0) { 4904480093f4SDimitry Andric if (m_first_visible_row > m_max_y) 4905480093f4SDimitry Andric m_first_visible_row -= m_max_y; 4906480093f4SDimitry Andric else 4907480093f4SDimitry Andric m_first_visible_row = 0; 4908480093f4SDimitry Andric m_selected_row_idx = m_first_visible_row; 4909480093f4SDimitry Andric m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); 4910480093f4SDimitry Andric if (m_selected_item) 4911480093f4SDimitry Andric m_selected_item->ItemWasSelected(); 4912480093f4SDimitry Andric } 4913480093f4SDimitry Andric return eKeyHandled; 4914480093f4SDimitry Andric 4915480093f4SDimitry Andric case '.': 4916480093f4SDimitry Andric case KEY_NPAGE: 4917480093f4SDimitry Andric // Page down key 4918480093f4SDimitry Andric if (m_num_rows > m_max_y) { 4919480093f4SDimitry Andric if (m_first_visible_row + m_max_y < m_num_rows) { 4920480093f4SDimitry Andric m_first_visible_row += m_max_y; 4921480093f4SDimitry Andric m_selected_row_idx = m_first_visible_row; 4922480093f4SDimitry Andric m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); 4923480093f4SDimitry Andric if (m_selected_item) 4924480093f4SDimitry Andric m_selected_item->ItemWasSelected(); 4925480093f4SDimitry Andric } 4926480093f4SDimitry Andric } 4927480093f4SDimitry Andric return eKeyHandled; 4928480093f4SDimitry Andric 4929480093f4SDimitry Andric case KEY_UP: 4930480093f4SDimitry Andric if (m_selected_row_idx > 0) { 4931480093f4SDimitry Andric --m_selected_row_idx; 4932480093f4SDimitry Andric m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); 4933480093f4SDimitry Andric if (m_selected_item) 4934480093f4SDimitry Andric m_selected_item->ItemWasSelected(); 4935480093f4SDimitry Andric } 4936480093f4SDimitry Andric return eKeyHandled; 4937480093f4SDimitry Andric 4938480093f4SDimitry Andric case KEY_DOWN: 4939480093f4SDimitry Andric if (m_selected_row_idx + 1 < m_num_rows) { 4940480093f4SDimitry Andric ++m_selected_row_idx; 4941480093f4SDimitry Andric m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); 4942480093f4SDimitry Andric if (m_selected_item) 4943480093f4SDimitry Andric m_selected_item->ItemWasSelected(); 4944480093f4SDimitry Andric } 4945480093f4SDimitry Andric return eKeyHandled; 4946480093f4SDimitry Andric 4947480093f4SDimitry Andric case KEY_RIGHT: 4948480093f4SDimitry Andric if (m_selected_item) { 4949480093f4SDimitry Andric if (!m_selected_item->IsExpanded()) 4950480093f4SDimitry Andric m_selected_item->Expand(); 4951480093f4SDimitry Andric } 4952480093f4SDimitry Andric return eKeyHandled; 4953480093f4SDimitry Andric 4954480093f4SDimitry Andric case KEY_LEFT: 4955480093f4SDimitry Andric if (m_selected_item) { 4956480093f4SDimitry Andric if (m_selected_item->IsExpanded()) 4957480093f4SDimitry Andric m_selected_item->Unexpand(); 4958480093f4SDimitry Andric else if (m_selected_item->GetParent()) { 4959480093f4SDimitry Andric m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex(); 4960480093f4SDimitry Andric m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); 4961480093f4SDimitry Andric if (m_selected_item) 4962480093f4SDimitry Andric m_selected_item->ItemWasSelected(); 4963480093f4SDimitry Andric } 4964480093f4SDimitry Andric } 4965480093f4SDimitry Andric return eKeyHandled; 4966480093f4SDimitry Andric 4967480093f4SDimitry Andric case ' ': 4968480093f4SDimitry Andric // Toggle expansion state when SPACE is pressed 4969480093f4SDimitry Andric if (m_selected_item) { 4970480093f4SDimitry Andric if (m_selected_item->IsExpanded()) 4971480093f4SDimitry Andric m_selected_item->Unexpand(); 4972480093f4SDimitry Andric else 4973480093f4SDimitry Andric m_selected_item->Expand(); 4974480093f4SDimitry Andric } 4975480093f4SDimitry Andric return eKeyHandled; 4976480093f4SDimitry Andric 4977480093f4SDimitry Andric case 'h': 4978480093f4SDimitry Andric window.CreateHelpSubwindow(); 4979480093f4SDimitry Andric return eKeyHandled; 4980480093f4SDimitry Andric 4981480093f4SDimitry Andric default: 4982480093f4SDimitry Andric break; 4983480093f4SDimitry Andric } 4984480093f4SDimitry Andric return eKeyNotHandled; 4985480093f4SDimitry Andric } 4986480093f4SDimitry Andric 4987480093f4SDimitry Andric protected: 4988480093f4SDimitry Andric Debugger &m_debugger; 4989480093f4SDimitry Andric TreeDelegateSP m_delegate_sp; 4990480093f4SDimitry Andric TreeItem m_root; 499181ad6265SDimitry Andric TreeItem *m_selected_item = nullptr; 499281ad6265SDimitry Andric int m_num_rows = 0; 499381ad6265SDimitry Andric int m_selected_row_idx = 0; 499481ad6265SDimitry Andric int m_first_visible_row = 0; 499581ad6265SDimitry Andric int m_min_x = 0; 499681ad6265SDimitry Andric int m_min_y = 0; 499781ad6265SDimitry Andric int m_max_x = 0; 499881ad6265SDimitry Andric int m_max_y = 0; 4999480093f4SDimitry Andric }; 5000480093f4SDimitry Andric 5001349cc55cSDimitry Andric // A tree delegate that just draws the text member of the tree item, it doesn't 5002349cc55cSDimitry Andric // have any children or actions. 5003349cc55cSDimitry Andric class TextTreeDelegate : public TreeDelegate { 5004349cc55cSDimitry Andric public: 5005349cc55cSDimitry Andric TextTreeDelegate() : TreeDelegate() {} 5006349cc55cSDimitry Andric 5007349cc55cSDimitry Andric ~TextTreeDelegate() override = default; 5008349cc55cSDimitry Andric 5009349cc55cSDimitry Andric void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override { 5010349cc55cSDimitry Andric window.PutCStringTruncated(1, item.GetText().c_str()); 5011349cc55cSDimitry Andric } 5012349cc55cSDimitry Andric 5013349cc55cSDimitry Andric void TreeDelegateGenerateChildren(TreeItem &item) override {} 5014349cc55cSDimitry Andric 5015349cc55cSDimitry Andric bool TreeDelegateItemSelected(TreeItem &item) override { return false; } 5016349cc55cSDimitry Andric }; 5017349cc55cSDimitry Andric 5018480093f4SDimitry Andric class FrameTreeDelegate : public TreeDelegate { 5019480093f4SDimitry Andric public: 5020480093f4SDimitry Andric FrameTreeDelegate() : TreeDelegate() { 5021480093f4SDimitry Andric FormatEntity::Parse( 502281ad6265SDimitry Andric "#${frame.index}: {${function.name}${function.pc-offset}}}", m_format); 5023480093f4SDimitry Andric } 5024480093f4SDimitry Andric 5025480093f4SDimitry Andric ~FrameTreeDelegate() override = default; 5026480093f4SDimitry Andric 5027480093f4SDimitry Andric void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override { 5028480093f4SDimitry Andric Thread *thread = (Thread *)item.GetUserData(); 5029480093f4SDimitry Andric if (thread) { 5030480093f4SDimitry Andric const uint64_t frame_idx = item.GetIdentifier(); 5031480093f4SDimitry Andric StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_idx); 5032480093f4SDimitry Andric if (frame_sp) { 5033480093f4SDimitry Andric StreamString strm; 5034480093f4SDimitry Andric const SymbolContext &sc = 5035480093f4SDimitry Andric frame_sp->GetSymbolContext(eSymbolContextEverything); 5036480093f4SDimitry Andric ExecutionContext exe_ctx(frame_sp); 5037480093f4SDimitry Andric if (FormatEntity::Format(m_format, strm, &sc, &exe_ctx, nullptr, 5038480093f4SDimitry Andric nullptr, false, false)) { 5039480093f4SDimitry Andric int right_pad = 1; 5040e8d8bef9SDimitry Andric window.PutCStringTruncated(right_pad, strm.GetString().str().c_str()); 5041480093f4SDimitry Andric } 5042480093f4SDimitry Andric } 5043480093f4SDimitry Andric } 5044480093f4SDimitry Andric } 5045480093f4SDimitry Andric 5046480093f4SDimitry Andric void TreeDelegateGenerateChildren(TreeItem &item) override { 5047480093f4SDimitry Andric // No children for frames yet... 5048480093f4SDimitry Andric } 5049480093f4SDimitry Andric 5050480093f4SDimitry Andric bool TreeDelegateItemSelected(TreeItem &item) override { 5051480093f4SDimitry Andric Thread *thread = (Thread *)item.GetUserData(); 5052480093f4SDimitry Andric if (thread) { 5053480093f4SDimitry Andric thread->GetProcess()->GetThreadList().SetSelectedThreadByID( 5054480093f4SDimitry Andric thread->GetID()); 5055480093f4SDimitry Andric const uint64_t frame_idx = item.GetIdentifier(); 5056480093f4SDimitry Andric thread->SetSelectedFrameByIndex(frame_idx); 5057480093f4SDimitry Andric return true; 5058480093f4SDimitry Andric } 5059480093f4SDimitry Andric return false; 5060480093f4SDimitry Andric } 5061480093f4SDimitry Andric 5062480093f4SDimitry Andric protected: 5063480093f4SDimitry Andric FormatEntity::Entry m_format; 5064480093f4SDimitry Andric }; 5065480093f4SDimitry Andric 5066480093f4SDimitry Andric class ThreadTreeDelegate : public TreeDelegate { 5067480093f4SDimitry Andric public: 5068480093f4SDimitry Andric ThreadTreeDelegate(Debugger &debugger) 506981ad6265SDimitry Andric : TreeDelegate(), m_debugger(debugger) { 5070480093f4SDimitry Andric FormatEntity::Parse("thread #${thread.index}: tid = ${thread.id}{, stop " 5071480093f4SDimitry Andric "reason = ${thread.stop-reason}}", 5072480093f4SDimitry Andric m_format); 5073480093f4SDimitry Andric } 5074480093f4SDimitry Andric 5075480093f4SDimitry Andric ~ThreadTreeDelegate() override = default; 5076480093f4SDimitry Andric 5077480093f4SDimitry Andric ProcessSP GetProcess() { 5078480093f4SDimitry Andric return m_debugger.GetCommandInterpreter() 5079480093f4SDimitry Andric .GetExecutionContext() 5080480093f4SDimitry Andric .GetProcessSP(); 5081480093f4SDimitry Andric } 5082480093f4SDimitry Andric 5083480093f4SDimitry Andric ThreadSP GetThread(const TreeItem &item) { 5084480093f4SDimitry Andric ProcessSP process_sp = GetProcess(); 5085480093f4SDimitry Andric if (process_sp) 5086480093f4SDimitry Andric return process_sp->GetThreadList().FindThreadByID(item.GetIdentifier()); 5087480093f4SDimitry Andric return ThreadSP(); 5088480093f4SDimitry Andric } 5089480093f4SDimitry Andric 5090480093f4SDimitry Andric void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override { 5091480093f4SDimitry Andric ThreadSP thread_sp = GetThread(item); 5092480093f4SDimitry Andric if (thread_sp) { 5093480093f4SDimitry Andric StreamString strm; 5094480093f4SDimitry Andric ExecutionContext exe_ctx(thread_sp); 5095480093f4SDimitry Andric if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr, 5096480093f4SDimitry Andric nullptr, false, false)) { 5097480093f4SDimitry Andric int right_pad = 1; 5098e8d8bef9SDimitry Andric window.PutCStringTruncated(right_pad, strm.GetString().str().c_str()); 5099480093f4SDimitry Andric } 5100480093f4SDimitry Andric } 5101480093f4SDimitry Andric } 5102480093f4SDimitry Andric 5103480093f4SDimitry Andric void TreeDelegateGenerateChildren(TreeItem &item) override { 5104480093f4SDimitry Andric ProcessSP process_sp = GetProcess(); 5105480093f4SDimitry Andric if (process_sp && process_sp->IsAlive()) { 5106480093f4SDimitry Andric StateType state = process_sp->GetState(); 5107480093f4SDimitry Andric if (StateIsStoppedState(state, true)) { 5108480093f4SDimitry Andric ThreadSP thread_sp = GetThread(item); 5109480093f4SDimitry Andric if (thread_sp) { 5110480093f4SDimitry Andric if (m_stop_id == process_sp->GetStopID() && 5111480093f4SDimitry Andric thread_sp->GetID() == m_tid) 5112480093f4SDimitry Andric return; // Children are already up to date 5113480093f4SDimitry Andric if (!m_frame_delegate_sp) { 5114480093f4SDimitry Andric // Always expand the thread item the first time we show it 5115480093f4SDimitry Andric m_frame_delegate_sp = std::make_shared<FrameTreeDelegate>(); 5116480093f4SDimitry Andric } 5117480093f4SDimitry Andric 5118480093f4SDimitry Andric m_stop_id = process_sp->GetStopID(); 5119480093f4SDimitry Andric m_tid = thread_sp->GetID(); 5120480093f4SDimitry Andric 5121480093f4SDimitry Andric TreeItem t(&item, *m_frame_delegate_sp, false); 5122480093f4SDimitry Andric size_t num_frames = thread_sp->GetStackFrameCount(); 5123480093f4SDimitry Andric item.Resize(num_frames, t); 5124480093f4SDimitry Andric for (size_t i = 0; i < num_frames; ++i) { 5125480093f4SDimitry Andric item[i].SetUserData(thread_sp.get()); 5126480093f4SDimitry Andric item[i].SetIdentifier(i); 5127480093f4SDimitry Andric } 5128480093f4SDimitry Andric } 5129480093f4SDimitry Andric return; 5130480093f4SDimitry Andric } 5131480093f4SDimitry Andric } 5132480093f4SDimitry Andric item.ClearChildren(); 5133480093f4SDimitry Andric } 5134480093f4SDimitry Andric 5135480093f4SDimitry Andric bool TreeDelegateItemSelected(TreeItem &item) override { 5136480093f4SDimitry Andric ProcessSP process_sp = GetProcess(); 5137480093f4SDimitry Andric if (process_sp && process_sp->IsAlive()) { 5138480093f4SDimitry Andric StateType state = process_sp->GetState(); 5139480093f4SDimitry Andric if (StateIsStoppedState(state, true)) { 5140480093f4SDimitry Andric ThreadSP thread_sp = GetThread(item); 5141480093f4SDimitry Andric if (thread_sp) { 5142480093f4SDimitry Andric ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList(); 5143480093f4SDimitry Andric std::lock_guard<std::recursive_mutex> guard(thread_list.GetMutex()); 5144480093f4SDimitry Andric ThreadSP selected_thread_sp = thread_list.GetSelectedThread(); 5145480093f4SDimitry Andric if (selected_thread_sp->GetID() != thread_sp->GetID()) { 5146480093f4SDimitry Andric thread_list.SetSelectedThreadByID(thread_sp->GetID()); 5147480093f4SDimitry Andric return true; 5148480093f4SDimitry Andric } 5149480093f4SDimitry Andric } 5150480093f4SDimitry Andric } 5151480093f4SDimitry Andric } 5152480093f4SDimitry Andric return false; 5153480093f4SDimitry Andric } 5154480093f4SDimitry Andric 5155480093f4SDimitry Andric protected: 5156480093f4SDimitry Andric Debugger &m_debugger; 5157480093f4SDimitry Andric std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp; 515881ad6265SDimitry Andric lldb::user_id_t m_tid = LLDB_INVALID_THREAD_ID; 515981ad6265SDimitry Andric uint32_t m_stop_id = UINT32_MAX; 5160480093f4SDimitry Andric FormatEntity::Entry m_format; 5161480093f4SDimitry Andric }; 5162480093f4SDimitry Andric 5163480093f4SDimitry Andric class ThreadsTreeDelegate : public TreeDelegate { 5164480093f4SDimitry Andric public: 5165480093f4SDimitry Andric ThreadsTreeDelegate(Debugger &debugger) 516681ad6265SDimitry Andric : TreeDelegate(), m_thread_delegate_sp(), m_debugger(debugger) { 5167480093f4SDimitry Andric FormatEntity::Parse("process ${process.id}{, name = ${process.name}}", 5168480093f4SDimitry Andric m_format); 5169480093f4SDimitry Andric } 5170480093f4SDimitry Andric 5171480093f4SDimitry Andric ~ThreadsTreeDelegate() override = default; 5172480093f4SDimitry Andric 5173480093f4SDimitry Andric ProcessSP GetProcess() { 5174480093f4SDimitry Andric return m_debugger.GetCommandInterpreter() 5175480093f4SDimitry Andric .GetExecutionContext() 5176480093f4SDimitry Andric .GetProcessSP(); 5177480093f4SDimitry Andric } 5178480093f4SDimitry Andric 5179349cc55cSDimitry Andric bool TreeDelegateShouldDraw() override { 5180349cc55cSDimitry Andric ProcessSP process = GetProcess(); 5181349cc55cSDimitry Andric if (!process) 5182349cc55cSDimitry Andric return false; 5183349cc55cSDimitry Andric 5184349cc55cSDimitry Andric if (StateIsRunningState(process->GetState())) 5185349cc55cSDimitry Andric return false; 5186349cc55cSDimitry Andric 5187349cc55cSDimitry Andric return true; 5188349cc55cSDimitry Andric } 5189349cc55cSDimitry Andric 5190480093f4SDimitry Andric void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override { 5191480093f4SDimitry Andric ProcessSP process_sp = GetProcess(); 5192480093f4SDimitry Andric if (process_sp && process_sp->IsAlive()) { 5193480093f4SDimitry Andric StreamString strm; 5194480093f4SDimitry Andric ExecutionContext exe_ctx(process_sp); 5195480093f4SDimitry Andric if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr, 5196480093f4SDimitry Andric nullptr, false, false)) { 5197480093f4SDimitry Andric int right_pad = 1; 5198e8d8bef9SDimitry Andric window.PutCStringTruncated(right_pad, strm.GetString().str().c_str()); 5199480093f4SDimitry Andric } 5200480093f4SDimitry Andric } 5201480093f4SDimitry Andric } 5202480093f4SDimitry Andric 5203480093f4SDimitry Andric void TreeDelegateGenerateChildren(TreeItem &item) override { 5204480093f4SDimitry Andric ProcessSP process_sp = GetProcess(); 5205fe6060f1SDimitry Andric m_update_selection = false; 5206480093f4SDimitry Andric if (process_sp && process_sp->IsAlive()) { 5207480093f4SDimitry Andric StateType state = process_sp->GetState(); 5208480093f4SDimitry Andric if (StateIsStoppedState(state, true)) { 5209480093f4SDimitry Andric const uint32_t stop_id = process_sp->GetStopID(); 5210480093f4SDimitry Andric if (m_stop_id == stop_id) 5211480093f4SDimitry Andric return; // Children are already up to date 5212480093f4SDimitry Andric 5213480093f4SDimitry Andric m_stop_id = stop_id; 5214fe6060f1SDimitry Andric m_update_selection = true; 5215480093f4SDimitry Andric 5216480093f4SDimitry Andric if (!m_thread_delegate_sp) { 5217480093f4SDimitry Andric // Always expand the thread item the first time we show it 5218480093f4SDimitry Andric // item.Expand(); 5219480093f4SDimitry Andric m_thread_delegate_sp = 5220480093f4SDimitry Andric std::make_shared<ThreadTreeDelegate>(m_debugger); 5221480093f4SDimitry Andric } 5222480093f4SDimitry Andric 5223480093f4SDimitry Andric TreeItem t(&item, *m_thread_delegate_sp, false); 5224480093f4SDimitry Andric ThreadList &threads = process_sp->GetThreadList(); 5225480093f4SDimitry Andric std::lock_guard<std::recursive_mutex> guard(threads.GetMutex()); 5226fe6060f1SDimitry Andric ThreadSP selected_thread = threads.GetSelectedThread(); 5227480093f4SDimitry Andric size_t num_threads = threads.GetSize(); 5228480093f4SDimitry Andric item.Resize(num_threads, t); 5229480093f4SDimitry Andric for (size_t i = 0; i < num_threads; ++i) { 5230fe6060f1SDimitry Andric ThreadSP thread = threads.GetThreadAtIndex(i); 5231fe6060f1SDimitry Andric item[i].SetIdentifier(thread->GetID()); 5232480093f4SDimitry Andric item[i].SetMightHaveChildren(true); 5233fe6060f1SDimitry Andric if (selected_thread->GetID() == thread->GetID()) 5234fe6060f1SDimitry Andric item[i].Expand(); 5235480093f4SDimitry Andric } 5236480093f4SDimitry Andric return; 5237480093f4SDimitry Andric } 5238480093f4SDimitry Andric } 5239480093f4SDimitry Andric item.ClearChildren(); 5240480093f4SDimitry Andric } 5241480093f4SDimitry Andric 5242fe6060f1SDimitry Andric void TreeDelegateUpdateSelection(TreeItem &root, int &selection_index, 5243fe6060f1SDimitry Andric TreeItem *&selected_item) override { 5244fe6060f1SDimitry Andric if (!m_update_selection) 5245fe6060f1SDimitry Andric return; 5246fe6060f1SDimitry Andric 5247fe6060f1SDimitry Andric ProcessSP process_sp = GetProcess(); 5248fe6060f1SDimitry Andric if (!(process_sp && process_sp->IsAlive())) 5249fe6060f1SDimitry Andric return; 5250fe6060f1SDimitry Andric 5251fe6060f1SDimitry Andric StateType state = process_sp->GetState(); 5252fe6060f1SDimitry Andric if (!StateIsStoppedState(state, true)) 5253fe6060f1SDimitry Andric return; 5254fe6060f1SDimitry Andric 5255fe6060f1SDimitry Andric ThreadList &threads = process_sp->GetThreadList(); 5256fe6060f1SDimitry Andric std::lock_guard<std::recursive_mutex> guard(threads.GetMutex()); 5257fe6060f1SDimitry Andric ThreadSP selected_thread = threads.GetSelectedThread(); 5258fe6060f1SDimitry Andric size_t num_threads = threads.GetSize(); 5259fe6060f1SDimitry Andric for (size_t i = 0; i < num_threads; ++i) { 5260fe6060f1SDimitry Andric ThreadSP thread = threads.GetThreadAtIndex(i); 5261fe6060f1SDimitry Andric if (selected_thread->GetID() == thread->GetID()) { 5262*06c3fb27SDimitry Andric selected_item = 5263*06c3fb27SDimitry Andric &root[i][thread->GetSelectedFrameIndex(SelectMostRelevantFrame)]; 5264fe6060f1SDimitry Andric selection_index = selected_item->GetRowIndex(); 5265fe6060f1SDimitry Andric return; 5266fe6060f1SDimitry Andric } 5267fe6060f1SDimitry Andric } 5268fe6060f1SDimitry Andric } 5269fe6060f1SDimitry Andric 5270480093f4SDimitry Andric bool TreeDelegateItemSelected(TreeItem &item) override { return false; } 5271480093f4SDimitry Andric 5272fe6060f1SDimitry Andric bool TreeDelegateExpandRootByDefault() override { return true; } 5273fe6060f1SDimitry Andric 5274480093f4SDimitry Andric protected: 5275480093f4SDimitry Andric std::shared_ptr<ThreadTreeDelegate> m_thread_delegate_sp; 5276480093f4SDimitry Andric Debugger &m_debugger; 527781ad6265SDimitry Andric uint32_t m_stop_id = UINT32_MAX; 527881ad6265SDimitry Andric bool m_update_selection = false; 5279480093f4SDimitry Andric FormatEntity::Entry m_format; 5280480093f4SDimitry Andric }; 5281480093f4SDimitry Andric 5282349cc55cSDimitry Andric class BreakpointLocationTreeDelegate : public TreeDelegate { 5283349cc55cSDimitry Andric public: 5284349cc55cSDimitry Andric BreakpointLocationTreeDelegate(Debugger &debugger) 5285349cc55cSDimitry Andric : TreeDelegate(), m_debugger(debugger) {} 5286349cc55cSDimitry Andric 5287349cc55cSDimitry Andric ~BreakpointLocationTreeDelegate() override = default; 5288349cc55cSDimitry Andric 5289349cc55cSDimitry Andric Process *GetProcess() { 5290349cc55cSDimitry Andric ExecutionContext exe_ctx( 5291349cc55cSDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext()); 5292349cc55cSDimitry Andric return exe_ctx.GetProcessPtr(); 5293349cc55cSDimitry Andric } 5294349cc55cSDimitry Andric 5295349cc55cSDimitry Andric BreakpointLocationSP GetBreakpointLocation(const TreeItem &item) { 5296349cc55cSDimitry Andric Breakpoint *breakpoint = (Breakpoint *)item.GetUserData(); 5297349cc55cSDimitry Andric return breakpoint->GetLocationAtIndex(item.GetIdentifier()); 5298349cc55cSDimitry Andric } 5299349cc55cSDimitry Andric 5300349cc55cSDimitry Andric void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override { 5301349cc55cSDimitry Andric BreakpointLocationSP breakpoint_location = GetBreakpointLocation(item); 5302349cc55cSDimitry Andric Process *process = GetProcess(); 5303349cc55cSDimitry Andric StreamString stream; 5304349cc55cSDimitry Andric stream.Printf("%i.%i: ", breakpoint_location->GetBreakpoint().GetID(), 5305349cc55cSDimitry Andric breakpoint_location->GetID()); 5306349cc55cSDimitry Andric Address address = breakpoint_location->GetAddress(); 5307349cc55cSDimitry Andric address.Dump(&stream, process, Address::DumpStyleResolvedDescription, 5308349cc55cSDimitry Andric Address::DumpStyleInvalid); 5309349cc55cSDimitry Andric window.PutCStringTruncated(1, stream.GetString().str().c_str()); 5310349cc55cSDimitry Andric } 5311349cc55cSDimitry Andric 5312349cc55cSDimitry Andric StringList ComputeDetailsList(BreakpointLocationSP breakpoint_location) { 5313349cc55cSDimitry Andric StringList details; 5314349cc55cSDimitry Andric 5315349cc55cSDimitry Andric Address address = breakpoint_location->GetAddress(); 5316349cc55cSDimitry Andric SymbolContext symbol_context; 5317349cc55cSDimitry Andric address.CalculateSymbolContext(&symbol_context); 5318349cc55cSDimitry Andric 5319349cc55cSDimitry Andric if (symbol_context.module_sp) { 5320349cc55cSDimitry Andric StreamString module_stream; 5321349cc55cSDimitry Andric module_stream.PutCString("module = "); 5322349cc55cSDimitry Andric symbol_context.module_sp->GetFileSpec().Dump( 5323349cc55cSDimitry Andric module_stream.AsRawOstream()); 5324349cc55cSDimitry Andric details.AppendString(module_stream.GetString()); 5325349cc55cSDimitry Andric } 5326349cc55cSDimitry Andric 5327349cc55cSDimitry Andric if (symbol_context.comp_unit != nullptr) { 5328349cc55cSDimitry Andric StreamString compile_unit_stream; 5329349cc55cSDimitry Andric compile_unit_stream.PutCString("compile unit = "); 5330349cc55cSDimitry Andric symbol_context.comp_unit->GetPrimaryFile().GetFilename().Dump( 5331349cc55cSDimitry Andric &compile_unit_stream); 5332349cc55cSDimitry Andric details.AppendString(compile_unit_stream.GetString()); 5333349cc55cSDimitry Andric 5334349cc55cSDimitry Andric if (symbol_context.function != nullptr) { 5335349cc55cSDimitry Andric StreamString function_stream; 5336349cc55cSDimitry Andric function_stream.PutCString("function = "); 5337349cc55cSDimitry Andric function_stream.PutCString( 5338349cc55cSDimitry Andric symbol_context.function->GetName().AsCString("<unknown>")); 5339349cc55cSDimitry Andric details.AppendString(function_stream.GetString()); 5340349cc55cSDimitry Andric } 5341349cc55cSDimitry Andric 5342349cc55cSDimitry Andric if (symbol_context.line_entry.line > 0) { 5343349cc55cSDimitry Andric StreamString location_stream; 5344349cc55cSDimitry Andric location_stream.PutCString("location = "); 5345349cc55cSDimitry Andric symbol_context.line_entry.DumpStopContext(&location_stream, true); 5346349cc55cSDimitry Andric details.AppendString(location_stream.GetString()); 5347349cc55cSDimitry Andric } 5348349cc55cSDimitry Andric 5349349cc55cSDimitry Andric } else { 5350349cc55cSDimitry Andric if (symbol_context.symbol) { 5351349cc55cSDimitry Andric StreamString symbol_stream; 5352349cc55cSDimitry Andric if (breakpoint_location->IsReExported()) 5353349cc55cSDimitry Andric symbol_stream.PutCString("re-exported target = "); 5354349cc55cSDimitry Andric else 5355349cc55cSDimitry Andric symbol_stream.PutCString("symbol = "); 5356349cc55cSDimitry Andric symbol_stream.PutCString( 5357349cc55cSDimitry Andric symbol_context.symbol->GetName().AsCString("<unknown>")); 5358349cc55cSDimitry Andric details.AppendString(symbol_stream.GetString()); 5359349cc55cSDimitry Andric } 5360349cc55cSDimitry Andric } 5361349cc55cSDimitry Andric 5362349cc55cSDimitry Andric Process *process = GetProcess(); 5363349cc55cSDimitry Andric 5364349cc55cSDimitry Andric StreamString address_stream; 5365349cc55cSDimitry Andric address.Dump(&address_stream, process, Address::DumpStyleLoadAddress, 5366349cc55cSDimitry Andric Address::DumpStyleModuleWithFileAddress); 5367349cc55cSDimitry Andric details.AppendString(address_stream.GetString()); 5368349cc55cSDimitry Andric 5369349cc55cSDimitry Andric BreakpointSiteSP breakpoint_site = breakpoint_location->GetBreakpointSite(); 5370349cc55cSDimitry Andric if (breakpoint_location->IsIndirect() && breakpoint_site) { 5371349cc55cSDimitry Andric Address resolved_address; 5372349cc55cSDimitry Andric resolved_address.SetLoadAddress(breakpoint_site->GetLoadAddress(), 5373349cc55cSDimitry Andric &breakpoint_location->GetTarget()); 5374349cc55cSDimitry Andric Symbol *resolved_symbol = resolved_address.CalculateSymbolContextSymbol(); 5375349cc55cSDimitry Andric if (resolved_symbol) { 5376349cc55cSDimitry Andric StreamString indirect_target_stream; 5377349cc55cSDimitry Andric indirect_target_stream.PutCString("indirect target = "); 5378349cc55cSDimitry Andric indirect_target_stream.PutCString( 5379349cc55cSDimitry Andric resolved_symbol->GetName().GetCString()); 5380349cc55cSDimitry Andric details.AppendString(indirect_target_stream.GetString()); 5381349cc55cSDimitry Andric } 5382349cc55cSDimitry Andric } 5383349cc55cSDimitry Andric 5384349cc55cSDimitry Andric bool is_resolved = breakpoint_location->IsResolved(); 5385349cc55cSDimitry Andric StreamString resolved_stream; 5386349cc55cSDimitry Andric resolved_stream.Printf("resolved = %s", is_resolved ? "true" : "false"); 5387349cc55cSDimitry Andric details.AppendString(resolved_stream.GetString()); 5388349cc55cSDimitry Andric 5389349cc55cSDimitry Andric bool is_hardware = is_resolved && breakpoint_site->IsHardware(); 5390349cc55cSDimitry Andric StreamString hardware_stream; 5391349cc55cSDimitry Andric hardware_stream.Printf("hardware = %s", is_hardware ? "true" : "false"); 5392349cc55cSDimitry Andric details.AppendString(hardware_stream.GetString()); 5393349cc55cSDimitry Andric 5394349cc55cSDimitry Andric StreamString hit_count_stream; 5395349cc55cSDimitry Andric hit_count_stream.Printf("hit count = %-4u", 5396349cc55cSDimitry Andric breakpoint_location->GetHitCount()); 5397349cc55cSDimitry Andric details.AppendString(hit_count_stream.GetString()); 5398349cc55cSDimitry Andric 5399349cc55cSDimitry Andric return details; 5400349cc55cSDimitry Andric } 5401349cc55cSDimitry Andric 5402349cc55cSDimitry Andric void TreeDelegateGenerateChildren(TreeItem &item) override { 5403349cc55cSDimitry Andric BreakpointLocationSP breakpoint_location = GetBreakpointLocation(item); 5404349cc55cSDimitry Andric StringList details = ComputeDetailsList(breakpoint_location); 5405349cc55cSDimitry Andric 5406349cc55cSDimitry Andric if (!m_string_delegate_sp) 5407349cc55cSDimitry Andric m_string_delegate_sp = std::make_shared<TextTreeDelegate>(); 5408349cc55cSDimitry Andric TreeItem details_tree_item(&item, *m_string_delegate_sp, false); 5409349cc55cSDimitry Andric 5410349cc55cSDimitry Andric item.Resize(details.GetSize(), details_tree_item); 5411349cc55cSDimitry Andric for (size_t i = 0; i < details.GetSize(); i++) { 5412349cc55cSDimitry Andric item[i].SetText(details.GetStringAtIndex(i)); 5413349cc55cSDimitry Andric } 5414349cc55cSDimitry Andric } 5415349cc55cSDimitry Andric 5416349cc55cSDimitry Andric bool TreeDelegateItemSelected(TreeItem &item) override { return false; } 5417349cc55cSDimitry Andric 5418349cc55cSDimitry Andric protected: 5419349cc55cSDimitry Andric Debugger &m_debugger; 5420349cc55cSDimitry Andric std::shared_ptr<TextTreeDelegate> m_string_delegate_sp; 5421349cc55cSDimitry Andric }; 5422349cc55cSDimitry Andric 5423349cc55cSDimitry Andric class BreakpointTreeDelegate : public TreeDelegate { 5424349cc55cSDimitry Andric public: 5425349cc55cSDimitry Andric BreakpointTreeDelegate(Debugger &debugger) 5426349cc55cSDimitry Andric : TreeDelegate(), m_debugger(debugger), 5427349cc55cSDimitry Andric m_breakpoint_location_delegate_sp() {} 5428349cc55cSDimitry Andric 5429349cc55cSDimitry Andric ~BreakpointTreeDelegate() override = default; 5430349cc55cSDimitry Andric 5431349cc55cSDimitry Andric BreakpointSP GetBreakpoint(const TreeItem &item) { 5432349cc55cSDimitry Andric TargetSP target = m_debugger.GetSelectedTarget(); 5433349cc55cSDimitry Andric BreakpointList &breakpoints = target->GetBreakpointList(false); 5434349cc55cSDimitry Andric return breakpoints.GetBreakpointAtIndex(item.GetIdentifier()); 5435349cc55cSDimitry Andric } 5436349cc55cSDimitry Andric 5437349cc55cSDimitry Andric void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override { 5438349cc55cSDimitry Andric BreakpointSP breakpoint = GetBreakpoint(item); 5439349cc55cSDimitry Andric StreamString stream; 5440349cc55cSDimitry Andric stream.Format("{0}: ", breakpoint->GetID()); 5441349cc55cSDimitry Andric breakpoint->GetResolverDescription(&stream); 5442349cc55cSDimitry Andric breakpoint->GetFilterDescription(&stream); 5443349cc55cSDimitry Andric window.PutCStringTruncated(1, stream.GetString().str().c_str()); 5444349cc55cSDimitry Andric } 5445349cc55cSDimitry Andric 5446349cc55cSDimitry Andric void TreeDelegateGenerateChildren(TreeItem &item) override { 5447349cc55cSDimitry Andric BreakpointSP breakpoint = GetBreakpoint(item); 5448349cc55cSDimitry Andric 5449349cc55cSDimitry Andric if (!m_breakpoint_location_delegate_sp) 5450349cc55cSDimitry Andric m_breakpoint_location_delegate_sp = 5451349cc55cSDimitry Andric std::make_shared<BreakpointLocationTreeDelegate>(m_debugger); 5452349cc55cSDimitry Andric TreeItem breakpoint_location_tree_item( 5453349cc55cSDimitry Andric &item, *m_breakpoint_location_delegate_sp, true); 5454349cc55cSDimitry Andric 5455349cc55cSDimitry Andric item.Resize(breakpoint->GetNumLocations(), breakpoint_location_tree_item); 5456349cc55cSDimitry Andric for (size_t i = 0; i < breakpoint->GetNumLocations(); i++) { 5457349cc55cSDimitry Andric item[i].SetIdentifier(i); 5458349cc55cSDimitry Andric item[i].SetUserData(breakpoint.get()); 5459349cc55cSDimitry Andric } 5460349cc55cSDimitry Andric } 5461349cc55cSDimitry Andric 5462349cc55cSDimitry Andric bool TreeDelegateItemSelected(TreeItem &item) override { return false; } 5463349cc55cSDimitry Andric 5464349cc55cSDimitry Andric protected: 5465349cc55cSDimitry Andric Debugger &m_debugger; 5466349cc55cSDimitry Andric std::shared_ptr<BreakpointLocationTreeDelegate> 5467349cc55cSDimitry Andric m_breakpoint_location_delegate_sp; 5468349cc55cSDimitry Andric }; 5469349cc55cSDimitry Andric 5470349cc55cSDimitry Andric class BreakpointsTreeDelegate : public TreeDelegate { 5471349cc55cSDimitry Andric public: 5472349cc55cSDimitry Andric BreakpointsTreeDelegate(Debugger &debugger) 5473349cc55cSDimitry Andric : TreeDelegate(), m_debugger(debugger), m_breakpoint_delegate_sp() {} 5474349cc55cSDimitry Andric 5475349cc55cSDimitry Andric ~BreakpointsTreeDelegate() override = default; 5476349cc55cSDimitry Andric 5477349cc55cSDimitry Andric bool TreeDelegateShouldDraw() override { 5478349cc55cSDimitry Andric TargetSP target = m_debugger.GetSelectedTarget(); 5479349cc55cSDimitry Andric if (!target) 5480349cc55cSDimitry Andric return false; 5481349cc55cSDimitry Andric 5482349cc55cSDimitry Andric return true; 5483349cc55cSDimitry Andric } 5484349cc55cSDimitry Andric 5485349cc55cSDimitry Andric void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override { 5486349cc55cSDimitry Andric window.PutCString("Breakpoints"); 5487349cc55cSDimitry Andric } 5488349cc55cSDimitry Andric 5489349cc55cSDimitry Andric void TreeDelegateGenerateChildren(TreeItem &item) override { 5490349cc55cSDimitry Andric TargetSP target = m_debugger.GetSelectedTarget(); 5491349cc55cSDimitry Andric 5492349cc55cSDimitry Andric BreakpointList &breakpoints = target->GetBreakpointList(false); 5493349cc55cSDimitry Andric std::unique_lock<std::recursive_mutex> lock; 5494349cc55cSDimitry Andric breakpoints.GetListMutex(lock); 5495349cc55cSDimitry Andric 5496349cc55cSDimitry Andric if (!m_breakpoint_delegate_sp) 5497349cc55cSDimitry Andric m_breakpoint_delegate_sp = 5498349cc55cSDimitry Andric std::make_shared<BreakpointTreeDelegate>(m_debugger); 5499349cc55cSDimitry Andric TreeItem breakpoint_tree_item(&item, *m_breakpoint_delegate_sp, true); 5500349cc55cSDimitry Andric 5501349cc55cSDimitry Andric item.Resize(breakpoints.GetSize(), breakpoint_tree_item); 5502349cc55cSDimitry Andric for (size_t i = 0; i < breakpoints.GetSize(); i++) { 5503349cc55cSDimitry Andric item[i].SetIdentifier(i); 5504349cc55cSDimitry Andric } 5505349cc55cSDimitry Andric } 5506349cc55cSDimitry Andric 5507349cc55cSDimitry Andric bool TreeDelegateItemSelected(TreeItem &item) override { return false; } 5508349cc55cSDimitry Andric 5509349cc55cSDimitry Andric bool TreeDelegateExpandRootByDefault() override { return true; } 5510349cc55cSDimitry Andric 5511349cc55cSDimitry Andric protected: 5512349cc55cSDimitry Andric Debugger &m_debugger; 5513349cc55cSDimitry Andric std::shared_ptr<BreakpointTreeDelegate> m_breakpoint_delegate_sp; 5514349cc55cSDimitry Andric }; 5515349cc55cSDimitry Andric 5516480093f4SDimitry Andric class ValueObjectListDelegate : public WindowDelegate { 5517480093f4SDimitry Andric public: 5518fe6060f1SDimitry Andric ValueObjectListDelegate() : m_rows() {} 5519480093f4SDimitry Andric 552081ad6265SDimitry Andric ValueObjectListDelegate(ValueObjectList &valobj_list) : m_rows() { 5521480093f4SDimitry Andric SetValues(valobj_list); 5522480093f4SDimitry Andric } 5523480093f4SDimitry Andric 5524480093f4SDimitry Andric ~ValueObjectListDelegate() override = default; 5525480093f4SDimitry Andric 5526480093f4SDimitry Andric void SetValues(ValueObjectList &valobj_list) { 5527480093f4SDimitry Andric m_selected_row = nullptr; 5528480093f4SDimitry Andric m_selected_row_idx = 0; 5529480093f4SDimitry Andric m_first_visible_row = 0; 5530480093f4SDimitry Andric m_num_rows = 0; 5531480093f4SDimitry Andric m_rows.clear(); 5532480093f4SDimitry Andric for (auto &valobj_sp : valobj_list.GetObjects()) 5533480093f4SDimitry Andric m_rows.push_back(Row(valobj_sp, nullptr)); 5534480093f4SDimitry Andric } 5535480093f4SDimitry Andric 5536480093f4SDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override { 5537480093f4SDimitry Andric m_num_rows = 0; 5538480093f4SDimitry Andric m_min_x = 2; 5539480093f4SDimitry Andric m_min_y = 1; 5540480093f4SDimitry Andric m_max_x = window.GetWidth() - 1; 5541480093f4SDimitry Andric m_max_y = window.GetHeight() - 1; 5542480093f4SDimitry Andric 5543480093f4SDimitry Andric window.Erase(); 5544480093f4SDimitry Andric window.DrawTitleBox(window.GetName()); 5545480093f4SDimitry Andric 5546480093f4SDimitry Andric const int num_visible_rows = NumVisibleRows(); 5547480093f4SDimitry Andric const int num_rows = CalculateTotalNumberRows(m_rows); 5548480093f4SDimitry Andric 5549480093f4SDimitry Andric // If we unexpanded while having something selected our total number of 5550480093f4SDimitry Andric // rows is less than the num visible rows, then make sure we show all the 5551480093f4SDimitry Andric // rows by setting the first visible row accordingly. 5552480093f4SDimitry Andric if (m_first_visible_row > 0 && num_rows < num_visible_rows) 5553480093f4SDimitry Andric m_first_visible_row = 0; 5554480093f4SDimitry Andric 5555480093f4SDimitry Andric // Make sure the selected row is always visible 5556480093f4SDimitry Andric if (m_selected_row_idx < m_first_visible_row) 5557480093f4SDimitry Andric m_first_visible_row = m_selected_row_idx; 5558480093f4SDimitry Andric else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx) 5559480093f4SDimitry Andric m_first_visible_row = m_selected_row_idx - num_visible_rows + 1; 5560480093f4SDimitry Andric 5561480093f4SDimitry Andric DisplayRows(window, m_rows, g_options); 5562480093f4SDimitry Andric 5563480093f4SDimitry Andric // Get the selected row 5564480093f4SDimitry Andric m_selected_row = GetRowForRowIndex(m_selected_row_idx); 5565480093f4SDimitry Andric // Keep the cursor on the selected row so the highlight and the cursor are 5566480093f4SDimitry Andric // always on the same line 5567480093f4SDimitry Andric if (m_selected_row) 5568480093f4SDimitry Andric window.MoveCursor(m_selected_row->x, m_selected_row->y); 5569480093f4SDimitry Andric 5570480093f4SDimitry Andric return true; // Drawing handled 5571480093f4SDimitry Andric } 5572480093f4SDimitry Andric 5573480093f4SDimitry Andric KeyHelp *WindowDelegateGetKeyHelp() override { 5574480093f4SDimitry Andric static curses::KeyHelp g_source_view_key_help[] = { 5575480093f4SDimitry Andric {KEY_UP, "Select previous item"}, 5576480093f4SDimitry Andric {KEY_DOWN, "Select next item"}, 5577480093f4SDimitry Andric {KEY_RIGHT, "Expand selected item"}, 5578480093f4SDimitry Andric {KEY_LEFT, "Unexpand selected item or select parent if not expanded"}, 5579480093f4SDimitry Andric {KEY_PPAGE, "Page up"}, 5580480093f4SDimitry Andric {KEY_NPAGE, "Page down"}, 5581480093f4SDimitry Andric {'A', "Format as annotated address"}, 5582480093f4SDimitry Andric {'b', "Format as binary"}, 5583480093f4SDimitry Andric {'B', "Format as hex bytes with ASCII"}, 5584480093f4SDimitry Andric {'c', "Format as character"}, 5585480093f4SDimitry Andric {'d', "Format as a signed integer"}, 5586480093f4SDimitry Andric {'D', "Format selected value using the default format for the type"}, 5587480093f4SDimitry Andric {'f', "Format as float"}, 5588480093f4SDimitry Andric {'h', "Show help dialog"}, 5589480093f4SDimitry Andric {'i', "Format as instructions"}, 5590480093f4SDimitry Andric {'o', "Format as octal"}, 5591480093f4SDimitry Andric {'p', "Format as pointer"}, 5592480093f4SDimitry Andric {'s', "Format as C string"}, 5593480093f4SDimitry Andric {'t', "Toggle showing/hiding type names"}, 5594480093f4SDimitry Andric {'u', "Format as an unsigned integer"}, 5595480093f4SDimitry Andric {'x', "Format as hex"}, 5596480093f4SDimitry Andric {'X', "Format as uppercase hex"}, 5597480093f4SDimitry Andric {' ', "Toggle item expansion"}, 5598480093f4SDimitry Andric {',', "Page up"}, 5599480093f4SDimitry Andric {'.', "Page down"}, 5600480093f4SDimitry Andric {'\0', nullptr}}; 5601480093f4SDimitry Andric return g_source_view_key_help; 5602480093f4SDimitry Andric } 5603480093f4SDimitry Andric 5604480093f4SDimitry Andric HandleCharResult WindowDelegateHandleChar(Window &window, int c) override { 5605480093f4SDimitry Andric switch (c) { 5606480093f4SDimitry Andric case 'x': 5607480093f4SDimitry Andric case 'X': 5608480093f4SDimitry Andric case 'o': 5609480093f4SDimitry Andric case 's': 5610480093f4SDimitry Andric case 'u': 5611480093f4SDimitry Andric case 'd': 5612480093f4SDimitry Andric case 'D': 5613480093f4SDimitry Andric case 'i': 5614480093f4SDimitry Andric case 'A': 5615480093f4SDimitry Andric case 'p': 5616480093f4SDimitry Andric case 'c': 5617480093f4SDimitry Andric case 'b': 5618480093f4SDimitry Andric case 'B': 5619480093f4SDimitry Andric case 'f': 5620480093f4SDimitry Andric // Change the format for the currently selected item 5621480093f4SDimitry Andric if (m_selected_row) { 5622480093f4SDimitry Andric auto valobj_sp = m_selected_row->value.GetSP(); 5623480093f4SDimitry Andric if (valobj_sp) 5624480093f4SDimitry Andric valobj_sp->SetFormat(FormatForChar(c)); 5625480093f4SDimitry Andric } 5626480093f4SDimitry Andric return eKeyHandled; 5627480093f4SDimitry Andric 5628480093f4SDimitry Andric case 't': 5629480093f4SDimitry Andric // Toggle showing type names 5630480093f4SDimitry Andric g_options.show_types = !g_options.show_types; 5631480093f4SDimitry Andric return eKeyHandled; 5632480093f4SDimitry Andric 5633480093f4SDimitry Andric case ',': 5634480093f4SDimitry Andric case KEY_PPAGE: 5635480093f4SDimitry Andric // Page up key 5636480093f4SDimitry Andric if (m_first_visible_row > 0) { 5637480093f4SDimitry Andric if (static_cast<int>(m_first_visible_row) > m_max_y) 5638480093f4SDimitry Andric m_first_visible_row -= m_max_y; 5639480093f4SDimitry Andric else 5640480093f4SDimitry Andric m_first_visible_row = 0; 5641480093f4SDimitry Andric m_selected_row_idx = m_first_visible_row; 5642480093f4SDimitry Andric } 5643480093f4SDimitry Andric return eKeyHandled; 5644480093f4SDimitry Andric 5645480093f4SDimitry Andric case '.': 5646480093f4SDimitry Andric case KEY_NPAGE: 5647480093f4SDimitry Andric // Page down key 5648480093f4SDimitry Andric if (m_num_rows > static_cast<size_t>(m_max_y)) { 5649480093f4SDimitry Andric if (m_first_visible_row + m_max_y < m_num_rows) { 5650480093f4SDimitry Andric m_first_visible_row += m_max_y; 5651480093f4SDimitry Andric m_selected_row_idx = m_first_visible_row; 5652480093f4SDimitry Andric } 5653480093f4SDimitry Andric } 5654480093f4SDimitry Andric return eKeyHandled; 5655480093f4SDimitry Andric 5656480093f4SDimitry Andric case KEY_UP: 5657480093f4SDimitry Andric if (m_selected_row_idx > 0) 5658480093f4SDimitry Andric --m_selected_row_idx; 5659480093f4SDimitry Andric return eKeyHandled; 5660480093f4SDimitry Andric 5661480093f4SDimitry Andric case KEY_DOWN: 5662480093f4SDimitry Andric if (m_selected_row_idx + 1 < m_num_rows) 5663480093f4SDimitry Andric ++m_selected_row_idx; 5664480093f4SDimitry Andric return eKeyHandled; 5665480093f4SDimitry Andric 5666480093f4SDimitry Andric case KEY_RIGHT: 5667480093f4SDimitry Andric if (m_selected_row) { 5668480093f4SDimitry Andric if (!m_selected_row->expanded) 5669480093f4SDimitry Andric m_selected_row->Expand(); 5670480093f4SDimitry Andric } 5671480093f4SDimitry Andric return eKeyHandled; 5672480093f4SDimitry Andric 5673480093f4SDimitry Andric case KEY_LEFT: 5674480093f4SDimitry Andric if (m_selected_row) { 5675480093f4SDimitry Andric if (m_selected_row->expanded) 5676480093f4SDimitry Andric m_selected_row->Unexpand(); 5677480093f4SDimitry Andric else if (m_selected_row->parent) 5678480093f4SDimitry Andric m_selected_row_idx = m_selected_row->parent->row_idx; 5679480093f4SDimitry Andric } 5680480093f4SDimitry Andric return eKeyHandled; 5681480093f4SDimitry Andric 5682480093f4SDimitry Andric case ' ': 5683480093f4SDimitry Andric // Toggle expansion state when SPACE is pressed 5684480093f4SDimitry Andric if (m_selected_row) { 5685480093f4SDimitry Andric if (m_selected_row->expanded) 5686480093f4SDimitry Andric m_selected_row->Unexpand(); 5687480093f4SDimitry Andric else 5688480093f4SDimitry Andric m_selected_row->Expand(); 5689480093f4SDimitry Andric } 5690480093f4SDimitry Andric return eKeyHandled; 5691480093f4SDimitry Andric 5692480093f4SDimitry Andric case 'h': 5693480093f4SDimitry Andric window.CreateHelpSubwindow(); 5694480093f4SDimitry Andric return eKeyHandled; 5695480093f4SDimitry Andric 5696480093f4SDimitry Andric default: 5697480093f4SDimitry Andric break; 5698480093f4SDimitry Andric } 5699480093f4SDimitry Andric return eKeyNotHandled; 5700480093f4SDimitry Andric } 5701480093f4SDimitry Andric 5702480093f4SDimitry Andric protected: 5703480093f4SDimitry Andric std::vector<Row> m_rows; 5704fe6060f1SDimitry Andric Row *m_selected_row = nullptr; 5705fe6060f1SDimitry Andric uint32_t m_selected_row_idx = 0; 5706fe6060f1SDimitry Andric uint32_t m_first_visible_row = 0; 5707fe6060f1SDimitry Andric uint32_t m_num_rows = 0; 5708bdd1243dSDimitry Andric int m_min_x = 0; 5709bdd1243dSDimitry Andric int m_min_y = 0; 5710fe6060f1SDimitry Andric int m_max_x = 0; 5711fe6060f1SDimitry Andric int m_max_y = 0; 5712480093f4SDimitry Andric 5713480093f4SDimitry Andric static Format FormatForChar(int c) { 5714480093f4SDimitry Andric switch (c) { 5715480093f4SDimitry Andric case 'x': 5716480093f4SDimitry Andric return eFormatHex; 5717480093f4SDimitry Andric case 'X': 5718480093f4SDimitry Andric return eFormatHexUppercase; 5719480093f4SDimitry Andric case 'o': 5720480093f4SDimitry Andric return eFormatOctal; 5721480093f4SDimitry Andric case 's': 5722480093f4SDimitry Andric return eFormatCString; 5723480093f4SDimitry Andric case 'u': 5724480093f4SDimitry Andric return eFormatUnsigned; 5725480093f4SDimitry Andric case 'd': 5726480093f4SDimitry Andric return eFormatDecimal; 5727480093f4SDimitry Andric case 'D': 5728480093f4SDimitry Andric return eFormatDefault; 5729480093f4SDimitry Andric case 'i': 5730480093f4SDimitry Andric return eFormatInstruction; 5731480093f4SDimitry Andric case 'A': 5732480093f4SDimitry Andric return eFormatAddressInfo; 5733480093f4SDimitry Andric case 'p': 5734480093f4SDimitry Andric return eFormatPointer; 5735480093f4SDimitry Andric case 'c': 5736480093f4SDimitry Andric return eFormatChar; 5737480093f4SDimitry Andric case 'b': 5738480093f4SDimitry Andric return eFormatBinary; 5739480093f4SDimitry Andric case 'B': 5740480093f4SDimitry Andric return eFormatBytesWithASCII; 5741480093f4SDimitry Andric case 'f': 5742480093f4SDimitry Andric return eFormatFloat; 5743480093f4SDimitry Andric } 5744480093f4SDimitry Andric return eFormatDefault; 5745480093f4SDimitry Andric } 5746480093f4SDimitry Andric 5747480093f4SDimitry Andric bool DisplayRowObject(Window &window, Row &row, DisplayOptions &options, 5748480093f4SDimitry Andric bool highlight, bool last_child) { 5749480093f4SDimitry Andric ValueObject *valobj = row.value.GetSP().get(); 5750480093f4SDimitry Andric 5751480093f4SDimitry Andric if (valobj == nullptr) 5752480093f4SDimitry Andric return false; 5753480093f4SDimitry Andric 5754480093f4SDimitry Andric const char *type_name = 5755480093f4SDimitry Andric options.show_types ? valobj->GetTypeName().GetCString() : nullptr; 5756480093f4SDimitry Andric const char *name = valobj->GetName().GetCString(); 5757480093f4SDimitry Andric const char *value = valobj->GetValueAsCString(); 5758480093f4SDimitry Andric const char *summary = valobj->GetSummaryAsCString(); 5759480093f4SDimitry Andric 5760480093f4SDimitry Andric window.MoveCursor(row.x, row.y); 5761480093f4SDimitry Andric 5762480093f4SDimitry Andric row.DrawTree(window); 5763480093f4SDimitry Andric 5764480093f4SDimitry Andric if (highlight) 5765480093f4SDimitry Andric window.AttributeOn(A_REVERSE); 5766480093f4SDimitry Andric 5767480093f4SDimitry Andric if (type_name && type_name[0]) 5768e8d8bef9SDimitry Andric window.PrintfTruncated(1, "(%s) ", type_name); 5769480093f4SDimitry Andric 5770480093f4SDimitry Andric if (name && name[0]) 5771e8d8bef9SDimitry Andric window.PutCStringTruncated(1, name); 5772480093f4SDimitry Andric 5773480093f4SDimitry Andric attr_t changd_attr = 0; 5774480093f4SDimitry Andric if (valobj->GetValueDidChange()) 5775e8d8bef9SDimitry Andric changd_attr = COLOR_PAIR(RedOnBlack) | A_BOLD; 5776480093f4SDimitry Andric 5777480093f4SDimitry Andric if (value && value[0]) { 5778e8d8bef9SDimitry Andric window.PutCStringTruncated(1, " = "); 5779480093f4SDimitry Andric if (changd_attr) 5780480093f4SDimitry Andric window.AttributeOn(changd_attr); 5781e8d8bef9SDimitry Andric window.PutCStringTruncated(1, value); 5782480093f4SDimitry Andric if (changd_attr) 5783480093f4SDimitry Andric window.AttributeOff(changd_attr); 5784480093f4SDimitry Andric } 5785480093f4SDimitry Andric 5786480093f4SDimitry Andric if (summary && summary[0]) { 5787e8d8bef9SDimitry Andric window.PutCStringTruncated(1, " "); 5788480093f4SDimitry Andric if (changd_attr) 5789480093f4SDimitry Andric window.AttributeOn(changd_attr); 5790e8d8bef9SDimitry Andric window.PutCStringTruncated(1, summary); 5791480093f4SDimitry Andric if (changd_attr) 5792480093f4SDimitry Andric window.AttributeOff(changd_attr); 5793480093f4SDimitry Andric } 5794480093f4SDimitry Andric 5795480093f4SDimitry Andric if (highlight) 5796480093f4SDimitry Andric window.AttributeOff(A_REVERSE); 5797480093f4SDimitry Andric 5798480093f4SDimitry Andric return true; 5799480093f4SDimitry Andric } 5800480093f4SDimitry Andric 5801480093f4SDimitry Andric void DisplayRows(Window &window, std::vector<Row> &rows, 5802480093f4SDimitry Andric DisplayOptions &options) { 5803480093f4SDimitry Andric // > 0x25B7 5804480093f4SDimitry Andric // \/ 0x25BD 5805480093f4SDimitry Andric 5806480093f4SDimitry Andric bool window_is_active = window.IsActive(); 5807480093f4SDimitry Andric for (auto &row : rows) { 5808480093f4SDimitry Andric const bool last_child = row.parent && &rows[rows.size() - 1] == &row; 5809480093f4SDimitry Andric // Save the row index in each Row structure 5810480093f4SDimitry Andric row.row_idx = m_num_rows; 5811480093f4SDimitry Andric if ((m_num_rows >= m_first_visible_row) && 5812480093f4SDimitry Andric ((m_num_rows - m_first_visible_row) < 5813480093f4SDimitry Andric static_cast<size_t>(NumVisibleRows()))) { 5814480093f4SDimitry Andric row.x = m_min_x; 5815480093f4SDimitry Andric row.y = m_num_rows - m_first_visible_row + 1; 5816480093f4SDimitry Andric if (DisplayRowObject(window, row, options, 5817480093f4SDimitry Andric window_is_active && 5818480093f4SDimitry Andric m_num_rows == m_selected_row_idx, 5819480093f4SDimitry Andric last_child)) { 5820480093f4SDimitry Andric ++m_num_rows; 5821480093f4SDimitry Andric } else { 5822480093f4SDimitry Andric row.x = 0; 5823480093f4SDimitry Andric row.y = 0; 5824480093f4SDimitry Andric } 5825480093f4SDimitry Andric } else { 5826480093f4SDimitry Andric row.x = 0; 5827480093f4SDimitry Andric row.y = 0; 5828480093f4SDimitry Andric ++m_num_rows; 5829480093f4SDimitry Andric } 5830480093f4SDimitry Andric 583181ad6265SDimitry Andric if (row.expanded) { 5832480093f4SDimitry Andric auto &children = row.GetChildren(); 583381ad6265SDimitry Andric if (!children.empty()) { 5834480093f4SDimitry Andric DisplayRows(window, children, options); 5835480093f4SDimitry Andric } 5836480093f4SDimitry Andric } 5837480093f4SDimitry Andric } 583881ad6265SDimitry Andric } 5839480093f4SDimitry Andric 5840480093f4SDimitry Andric int CalculateTotalNumberRows(std::vector<Row> &rows) { 5841480093f4SDimitry Andric int row_count = 0; 5842480093f4SDimitry Andric for (auto &row : rows) { 5843480093f4SDimitry Andric ++row_count; 5844480093f4SDimitry Andric if (row.expanded) 5845480093f4SDimitry Andric row_count += CalculateTotalNumberRows(row.GetChildren()); 5846480093f4SDimitry Andric } 5847480093f4SDimitry Andric return row_count; 5848480093f4SDimitry Andric } 5849480093f4SDimitry Andric 5850480093f4SDimitry Andric static Row *GetRowForRowIndexImpl(std::vector<Row> &rows, size_t &row_index) { 5851480093f4SDimitry Andric for (auto &row : rows) { 5852480093f4SDimitry Andric if (row_index == 0) 5853480093f4SDimitry Andric return &row; 5854480093f4SDimitry Andric else { 5855480093f4SDimitry Andric --row_index; 585681ad6265SDimitry Andric if (row.expanded) { 5857480093f4SDimitry Andric auto &children = row.GetChildren(); 585881ad6265SDimitry Andric if (!children.empty()) { 5859480093f4SDimitry Andric Row *result = GetRowForRowIndexImpl(children, row_index); 5860480093f4SDimitry Andric if (result) 5861480093f4SDimitry Andric return result; 5862480093f4SDimitry Andric } 5863480093f4SDimitry Andric } 5864480093f4SDimitry Andric } 586581ad6265SDimitry Andric } 5866480093f4SDimitry Andric return nullptr; 5867480093f4SDimitry Andric } 5868480093f4SDimitry Andric 5869480093f4SDimitry Andric Row *GetRowForRowIndex(size_t row_index) { 5870480093f4SDimitry Andric return GetRowForRowIndexImpl(m_rows, row_index); 5871480093f4SDimitry Andric } 5872480093f4SDimitry Andric 5873480093f4SDimitry Andric int NumVisibleRows() const { return m_max_y - m_min_y; } 5874480093f4SDimitry Andric 5875480093f4SDimitry Andric static DisplayOptions g_options; 5876480093f4SDimitry Andric }; 5877480093f4SDimitry Andric 5878480093f4SDimitry Andric class FrameVariablesWindowDelegate : public ValueObjectListDelegate { 5879480093f4SDimitry Andric public: 5880480093f4SDimitry Andric FrameVariablesWindowDelegate(Debugger &debugger) 588181ad6265SDimitry Andric : ValueObjectListDelegate(), m_debugger(debugger) {} 5882480093f4SDimitry Andric 5883480093f4SDimitry Andric ~FrameVariablesWindowDelegate() override = default; 5884480093f4SDimitry Andric 5885480093f4SDimitry Andric const char *WindowDelegateGetHelpText() override { 5886480093f4SDimitry Andric return "Frame variable window keyboard shortcuts:"; 5887480093f4SDimitry Andric } 5888480093f4SDimitry Andric 5889480093f4SDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override { 5890480093f4SDimitry Andric ExecutionContext exe_ctx( 5891480093f4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext()); 5892480093f4SDimitry Andric Process *process = exe_ctx.GetProcessPtr(); 5893480093f4SDimitry Andric Block *frame_block = nullptr; 5894480093f4SDimitry Andric StackFrame *frame = nullptr; 5895480093f4SDimitry Andric 5896480093f4SDimitry Andric if (process) { 5897480093f4SDimitry Andric StateType state = process->GetState(); 5898480093f4SDimitry Andric if (StateIsStoppedState(state, true)) { 5899480093f4SDimitry Andric frame = exe_ctx.GetFramePtr(); 5900480093f4SDimitry Andric if (frame) 5901480093f4SDimitry Andric frame_block = frame->GetFrameBlock(); 5902480093f4SDimitry Andric } else if (StateIsRunningState(state)) { 5903480093f4SDimitry Andric return true; // Don't do any updating when we are running 5904480093f4SDimitry Andric } 5905480093f4SDimitry Andric } 5906480093f4SDimitry Andric 5907480093f4SDimitry Andric ValueObjectList local_values; 5908480093f4SDimitry Andric if (frame_block) { 5909480093f4SDimitry Andric // Only update the variables if they have changed 5910480093f4SDimitry Andric if (m_frame_block != frame_block) { 5911480093f4SDimitry Andric m_frame_block = frame_block; 5912480093f4SDimitry Andric 5913bdd1243dSDimitry Andric VariableList *locals = frame->GetVariableList(true, nullptr); 5914480093f4SDimitry Andric if (locals) { 5915480093f4SDimitry Andric const DynamicValueType use_dynamic = eDynamicDontRunTarget; 5916480093f4SDimitry Andric for (const VariableSP &local_sp : *locals) { 5917480093f4SDimitry Andric ValueObjectSP value_sp = 5918480093f4SDimitry Andric frame->GetValueObjectForFrameVariable(local_sp, use_dynamic); 5919480093f4SDimitry Andric if (value_sp) { 5920480093f4SDimitry Andric ValueObjectSP synthetic_value_sp = value_sp->GetSyntheticValue(); 5921480093f4SDimitry Andric if (synthetic_value_sp) 5922480093f4SDimitry Andric local_values.Append(synthetic_value_sp); 5923480093f4SDimitry Andric else 5924480093f4SDimitry Andric local_values.Append(value_sp); 5925480093f4SDimitry Andric } 5926480093f4SDimitry Andric } 5927480093f4SDimitry Andric // Update the values 5928480093f4SDimitry Andric SetValues(local_values); 5929480093f4SDimitry Andric } 5930480093f4SDimitry Andric } 5931480093f4SDimitry Andric } else { 5932480093f4SDimitry Andric m_frame_block = nullptr; 5933480093f4SDimitry Andric // Update the values with an empty list if there is no frame 5934480093f4SDimitry Andric SetValues(local_values); 5935480093f4SDimitry Andric } 5936480093f4SDimitry Andric 5937480093f4SDimitry Andric return ValueObjectListDelegate::WindowDelegateDraw(window, force); 5938480093f4SDimitry Andric } 5939480093f4SDimitry Andric 5940480093f4SDimitry Andric protected: 5941480093f4SDimitry Andric Debugger &m_debugger; 594281ad6265SDimitry Andric Block *m_frame_block = nullptr; 5943480093f4SDimitry Andric }; 5944480093f4SDimitry Andric 5945480093f4SDimitry Andric class RegistersWindowDelegate : public ValueObjectListDelegate { 5946480093f4SDimitry Andric public: 5947480093f4SDimitry Andric RegistersWindowDelegate(Debugger &debugger) 5948480093f4SDimitry Andric : ValueObjectListDelegate(), m_debugger(debugger) {} 5949480093f4SDimitry Andric 5950480093f4SDimitry Andric ~RegistersWindowDelegate() override = default; 5951480093f4SDimitry Andric 5952480093f4SDimitry Andric const char *WindowDelegateGetHelpText() override { 5953480093f4SDimitry Andric return "Register window keyboard shortcuts:"; 5954480093f4SDimitry Andric } 5955480093f4SDimitry Andric 5956480093f4SDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override { 5957480093f4SDimitry Andric ExecutionContext exe_ctx( 5958480093f4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext()); 5959480093f4SDimitry Andric StackFrame *frame = exe_ctx.GetFramePtr(); 5960480093f4SDimitry Andric 5961480093f4SDimitry Andric ValueObjectList value_list; 5962480093f4SDimitry Andric if (frame) { 5963480093f4SDimitry Andric if (frame->GetStackID() != m_stack_id) { 5964480093f4SDimitry Andric m_stack_id = frame->GetStackID(); 5965480093f4SDimitry Andric RegisterContextSP reg_ctx(frame->GetRegisterContext()); 5966480093f4SDimitry Andric if (reg_ctx) { 5967480093f4SDimitry Andric const uint32_t num_sets = reg_ctx->GetRegisterSetCount(); 5968480093f4SDimitry Andric for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) { 5969480093f4SDimitry Andric value_list.Append( 5970480093f4SDimitry Andric ValueObjectRegisterSet::Create(frame, reg_ctx, set_idx)); 5971480093f4SDimitry Andric } 5972480093f4SDimitry Andric } 5973480093f4SDimitry Andric SetValues(value_list); 5974480093f4SDimitry Andric } 5975480093f4SDimitry Andric } else { 5976480093f4SDimitry Andric Process *process = exe_ctx.GetProcessPtr(); 5977480093f4SDimitry Andric if (process && process->IsAlive()) 5978480093f4SDimitry Andric return true; // Don't do any updating if we are running 5979480093f4SDimitry Andric else { 5980480093f4SDimitry Andric // Update the values with an empty list if there is no process or the 5981480093f4SDimitry Andric // process isn't alive anymore 5982480093f4SDimitry Andric SetValues(value_list); 5983480093f4SDimitry Andric } 5984480093f4SDimitry Andric } 5985480093f4SDimitry Andric return ValueObjectListDelegate::WindowDelegateDraw(window, force); 5986480093f4SDimitry Andric } 5987480093f4SDimitry Andric 5988480093f4SDimitry Andric protected: 5989480093f4SDimitry Andric Debugger &m_debugger; 5990480093f4SDimitry Andric StackID m_stack_id; 5991480093f4SDimitry Andric }; 5992480093f4SDimitry Andric 5993480093f4SDimitry Andric static const char *CursesKeyToCString(int ch) { 5994480093f4SDimitry Andric static char g_desc[32]; 5995480093f4SDimitry Andric if (ch >= KEY_F0 && ch < KEY_F0 + 64) { 5996480093f4SDimitry Andric snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0); 5997480093f4SDimitry Andric return g_desc; 5998480093f4SDimitry Andric } 5999480093f4SDimitry Andric switch (ch) { 6000480093f4SDimitry Andric case KEY_DOWN: 6001480093f4SDimitry Andric return "down"; 6002480093f4SDimitry Andric case KEY_UP: 6003480093f4SDimitry Andric return "up"; 6004480093f4SDimitry Andric case KEY_LEFT: 6005480093f4SDimitry Andric return "left"; 6006480093f4SDimitry Andric case KEY_RIGHT: 6007480093f4SDimitry Andric return "right"; 6008480093f4SDimitry Andric case KEY_HOME: 6009480093f4SDimitry Andric return "home"; 6010480093f4SDimitry Andric case KEY_BACKSPACE: 6011480093f4SDimitry Andric return "backspace"; 6012480093f4SDimitry Andric case KEY_DL: 6013480093f4SDimitry Andric return "delete-line"; 6014480093f4SDimitry Andric case KEY_IL: 6015480093f4SDimitry Andric return "insert-line"; 6016480093f4SDimitry Andric case KEY_DC: 6017480093f4SDimitry Andric return "delete-char"; 6018480093f4SDimitry Andric case KEY_IC: 6019480093f4SDimitry Andric return "insert-char"; 6020480093f4SDimitry Andric case KEY_CLEAR: 6021480093f4SDimitry Andric return "clear"; 6022480093f4SDimitry Andric case KEY_EOS: 6023480093f4SDimitry Andric return "clear-to-eos"; 6024480093f4SDimitry Andric case KEY_EOL: 6025480093f4SDimitry Andric return "clear-to-eol"; 6026480093f4SDimitry Andric case KEY_SF: 6027480093f4SDimitry Andric return "scroll-forward"; 6028480093f4SDimitry Andric case KEY_SR: 6029480093f4SDimitry Andric return "scroll-backward"; 6030480093f4SDimitry Andric case KEY_NPAGE: 6031480093f4SDimitry Andric return "page-down"; 6032480093f4SDimitry Andric case KEY_PPAGE: 6033480093f4SDimitry Andric return "page-up"; 6034480093f4SDimitry Andric case KEY_STAB: 6035480093f4SDimitry Andric return "set-tab"; 6036480093f4SDimitry Andric case KEY_CTAB: 6037480093f4SDimitry Andric return "clear-tab"; 6038480093f4SDimitry Andric case KEY_CATAB: 6039480093f4SDimitry Andric return "clear-all-tabs"; 6040480093f4SDimitry Andric case KEY_ENTER: 6041480093f4SDimitry Andric return "enter"; 6042480093f4SDimitry Andric case KEY_PRINT: 6043480093f4SDimitry Andric return "print"; 6044480093f4SDimitry Andric case KEY_LL: 6045480093f4SDimitry Andric return "lower-left key"; 6046480093f4SDimitry Andric case KEY_A1: 6047480093f4SDimitry Andric return "upper left of keypad"; 6048480093f4SDimitry Andric case KEY_A3: 6049480093f4SDimitry Andric return "upper right of keypad"; 6050480093f4SDimitry Andric case KEY_B2: 6051480093f4SDimitry Andric return "center of keypad"; 6052480093f4SDimitry Andric case KEY_C1: 6053480093f4SDimitry Andric return "lower left of keypad"; 6054480093f4SDimitry Andric case KEY_C3: 6055480093f4SDimitry Andric return "lower right of keypad"; 6056480093f4SDimitry Andric case KEY_BTAB: 6057480093f4SDimitry Andric return "back-tab key"; 6058480093f4SDimitry Andric case KEY_BEG: 6059480093f4SDimitry Andric return "begin key"; 6060480093f4SDimitry Andric case KEY_CANCEL: 6061480093f4SDimitry Andric return "cancel key"; 6062480093f4SDimitry Andric case KEY_CLOSE: 6063480093f4SDimitry Andric return "close key"; 6064480093f4SDimitry Andric case KEY_COMMAND: 6065480093f4SDimitry Andric return "command key"; 6066480093f4SDimitry Andric case KEY_COPY: 6067480093f4SDimitry Andric return "copy key"; 6068480093f4SDimitry Andric case KEY_CREATE: 6069480093f4SDimitry Andric return "create key"; 6070480093f4SDimitry Andric case KEY_END: 6071480093f4SDimitry Andric return "end key"; 6072480093f4SDimitry Andric case KEY_EXIT: 6073480093f4SDimitry Andric return "exit key"; 6074480093f4SDimitry Andric case KEY_FIND: 6075480093f4SDimitry Andric return "find key"; 6076480093f4SDimitry Andric case KEY_HELP: 6077480093f4SDimitry Andric return "help key"; 6078480093f4SDimitry Andric case KEY_MARK: 6079480093f4SDimitry Andric return "mark key"; 6080480093f4SDimitry Andric case KEY_MESSAGE: 6081480093f4SDimitry Andric return "message key"; 6082480093f4SDimitry Andric case KEY_MOVE: 6083480093f4SDimitry Andric return "move key"; 6084480093f4SDimitry Andric case KEY_NEXT: 6085480093f4SDimitry Andric return "next key"; 6086480093f4SDimitry Andric case KEY_OPEN: 6087480093f4SDimitry Andric return "open key"; 6088480093f4SDimitry Andric case KEY_OPTIONS: 6089480093f4SDimitry Andric return "options key"; 6090480093f4SDimitry Andric case KEY_PREVIOUS: 6091480093f4SDimitry Andric return "previous key"; 6092480093f4SDimitry Andric case KEY_REDO: 6093480093f4SDimitry Andric return "redo key"; 6094480093f4SDimitry Andric case KEY_REFERENCE: 6095480093f4SDimitry Andric return "reference key"; 6096480093f4SDimitry Andric case KEY_REFRESH: 6097480093f4SDimitry Andric return "refresh key"; 6098480093f4SDimitry Andric case KEY_REPLACE: 6099480093f4SDimitry Andric return "replace key"; 6100480093f4SDimitry Andric case KEY_RESTART: 6101480093f4SDimitry Andric return "restart key"; 6102480093f4SDimitry Andric case KEY_RESUME: 6103480093f4SDimitry Andric return "resume key"; 6104480093f4SDimitry Andric case KEY_SAVE: 6105480093f4SDimitry Andric return "save key"; 6106480093f4SDimitry Andric case KEY_SBEG: 6107480093f4SDimitry Andric return "shifted begin key"; 6108480093f4SDimitry Andric case KEY_SCANCEL: 6109480093f4SDimitry Andric return "shifted cancel key"; 6110480093f4SDimitry Andric case KEY_SCOMMAND: 6111480093f4SDimitry Andric return "shifted command key"; 6112480093f4SDimitry Andric case KEY_SCOPY: 6113480093f4SDimitry Andric return "shifted copy key"; 6114480093f4SDimitry Andric case KEY_SCREATE: 6115480093f4SDimitry Andric return "shifted create key"; 6116480093f4SDimitry Andric case KEY_SDC: 6117480093f4SDimitry Andric return "shifted delete-character key"; 6118480093f4SDimitry Andric case KEY_SDL: 6119480093f4SDimitry Andric return "shifted delete-line key"; 6120480093f4SDimitry Andric case KEY_SELECT: 6121480093f4SDimitry Andric return "select key"; 6122480093f4SDimitry Andric case KEY_SEND: 6123480093f4SDimitry Andric return "shifted end key"; 6124480093f4SDimitry Andric case KEY_SEOL: 6125480093f4SDimitry Andric return "shifted clear-to-end-of-line key"; 6126480093f4SDimitry Andric case KEY_SEXIT: 6127480093f4SDimitry Andric return "shifted exit key"; 6128480093f4SDimitry Andric case KEY_SFIND: 6129480093f4SDimitry Andric return "shifted find key"; 6130480093f4SDimitry Andric case KEY_SHELP: 6131480093f4SDimitry Andric return "shifted help key"; 6132480093f4SDimitry Andric case KEY_SHOME: 6133480093f4SDimitry Andric return "shifted home key"; 6134480093f4SDimitry Andric case KEY_SIC: 6135480093f4SDimitry Andric return "shifted insert-character key"; 6136480093f4SDimitry Andric case KEY_SLEFT: 6137480093f4SDimitry Andric return "shifted left-arrow key"; 6138480093f4SDimitry Andric case KEY_SMESSAGE: 6139480093f4SDimitry Andric return "shifted message key"; 6140480093f4SDimitry Andric case KEY_SMOVE: 6141480093f4SDimitry Andric return "shifted move key"; 6142480093f4SDimitry Andric case KEY_SNEXT: 6143480093f4SDimitry Andric return "shifted next key"; 6144480093f4SDimitry Andric case KEY_SOPTIONS: 6145480093f4SDimitry Andric return "shifted options key"; 6146480093f4SDimitry Andric case KEY_SPREVIOUS: 6147480093f4SDimitry Andric return "shifted previous key"; 6148480093f4SDimitry Andric case KEY_SPRINT: 6149480093f4SDimitry Andric return "shifted print key"; 6150480093f4SDimitry Andric case KEY_SREDO: 6151480093f4SDimitry Andric return "shifted redo key"; 6152480093f4SDimitry Andric case KEY_SREPLACE: 6153480093f4SDimitry Andric return "shifted replace key"; 6154480093f4SDimitry Andric case KEY_SRIGHT: 6155480093f4SDimitry Andric return "shifted right-arrow key"; 6156480093f4SDimitry Andric case KEY_SRSUME: 6157480093f4SDimitry Andric return "shifted resume key"; 6158480093f4SDimitry Andric case KEY_SSAVE: 6159480093f4SDimitry Andric return "shifted save key"; 6160480093f4SDimitry Andric case KEY_SSUSPEND: 6161480093f4SDimitry Andric return "shifted suspend key"; 6162480093f4SDimitry Andric case KEY_SUNDO: 6163480093f4SDimitry Andric return "shifted undo key"; 6164480093f4SDimitry Andric case KEY_SUSPEND: 6165480093f4SDimitry Andric return "suspend key"; 6166480093f4SDimitry Andric case KEY_UNDO: 6167480093f4SDimitry Andric return "undo key"; 6168480093f4SDimitry Andric case KEY_MOUSE: 6169480093f4SDimitry Andric return "Mouse event has occurred"; 6170480093f4SDimitry Andric case KEY_RESIZE: 6171480093f4SDimitry Andric return "Terminal resize event"; 6172480093f4SDimitry Andric #ifdef KEY_EVENT 6173480093f4SDimitry Andric case KEY_EVENT: 6174480093f4SDimitry Andric return "We were interrupted by an event"; 6175480093f4SDimitry Andric #endif 6176480093f4SDimitry Andric case KEY_RETURN: 6177480093f4SDimitry Andric return "return"; 6178480093f4SDimitry Andric case ' ': 6179480093f4SDimitry Andric return "space"; 6180480093f4SDimitry Andric case '\t': 6181480093f4SDimitry Andric return "tab"; 6182480093f4SDimitry Andric case KEY_ESCAPE: 6183480093f4SDimitry Andric return "escape"; 6184480093f4SDimitry Andric default: 61855ffd83dbSDimitry Andric if (llvm::isPrint(ch)) 6186480093f4SDimitry Andric snprintf(g_desc, sizeof(g_desc), "%c", ch); 6187480093f4SDimitry Andric else 6188480093f4SDimitry Andric snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch); 6189480093f4SDimitry Andric return g_desc; 6190480093f4SDimitry Andric } 6191480093f4SDimitry Andric return nullptr; 6192480093f4SDimitry Andric } 6193480093f4SDimitry Andric 6194480093f4SDimitry Andric HelpDialogDelegate::HelpDialogDelegate(const char *text, 6195480093f4SDimitry Andric KeyHelp *key_help_array) 619681ad6265SDimitry Andric : m_text() { 6197480093f4SDimitry Andric if (text && text[0]) { 6198480093f4SDimitry Andric m_text.SplitIntoLines(text); 6199480093f4SDimitry Andric m_text.AppendString(""); 6200480093f4SDimitry Andric } 6201480093f4SDimitry Andric if (key_help_array) { 6202480093f4SDimitry Andric for (KeyHelp *key = key_help_array; key->ch; ++key) { 6203480093f4SDimitry Andric StreamString key_description; 6204480093f4SDimitry Andric key_description.Printf("%10s - %s", CursesKeyToCString(key->ch), 6205480093f4SDimitry Andric key->description); 6206480093f4SDimitry Andric m_text.AppendString(key_description.GetString()); 6207480093f4SDimitry Andric } 6208480093f4SDimitry Andric } 6209480093f4SDimitry Andric } 6210480093f4SDimitry Andric 6211480093f4SDimitry Andric HelpDialogDelegate::~HelpDialogDelegate() = default; 6212480093f4SDimitry Andric 6213480093f4SDimitry Andric bool HelpDialogDelegate::WindowDelegateDraw(Window &window, bool force) { 6214480093f4SDimitry Andric window.Erase(); 6215480093f4SDimitry Andric const int window_height = window.GetHeight(); 6216480093f4SDimitry Andric int x = 2; 6217480093f4SDimitry Andric int y = 1; 6218480093f4SDimitry Andric const int min_y = y; 6219480093f4SDimitry Andric const int max_y = window_height - 1 - y; 6220480093f4SDimitry Andric const size_t num_visible_lines = max_y - min_y + 1; 6221480093f4SDimitry Andric const size_t num_lines = m_text.GetSize(); 6222480093f4SDimitry Andric const char *bottom_message; 6223480093f4SDimitry Andric if (num_lines <= num_visible_lines) 6224480093f4SDimitry Andric bottom_message = "Press any key to exit"; 6225480093f4SDimitry Andric else 6226480093f4SDimitry Andric bottom_message = "Use arrows to scroll, any other key to exit"; 6227480093f4SDimitry Andric window.DrawTitleBox(window.GetName(), bottom_message); 6228480093f4SDimitry Andric while (y <= max_y) { 6229480093f4SDimitry Andric window.MoveCursor(x, y); 6230480093f4SDimitry Andric window.PutCStringTruncated( 6231e8d8bef9SDimitry Andric 1, m_text.GetStringAtIndex(m_first_visible_line + y - min_y)); 6232480093f4SDimitry Andric ++y; 6233480093f4SDimitry Andric } 6234480093f4SDimitry Andric return true; 6235480093f4SDimitry Andric } 6236480093f4SDimitry Andric 6237480093f4SDimitry Andric HandleCharResult HelpDialogDelegate::WindowDelegateHandleChar(Window &window, 6238480093f4SDimitry Andric int key) { 6239480093f4SDimitry Andric bool done = false; 6240480093f4SDimitry Andric const size_t num_lines = m_text.GetSize(); 6241480093f4SDimitry Andric const size_t num_visible_lines = window.GetHeight() - 2; 6242480093f4SDimitry Andric 6243480093f4SDimitry Andric if (num_lines <= num_visible_lines) { 6244480093f4SDimitry Andric done = true; 6245480093f4SDimitry Andric // If we have all lines visible and don't need scrolling, then any key 6246480093f4SDimitry Andric // press will cause us to exit 6247480093f4SDimitry Andric } else { 6248480093f4SDimitry Andric switch (key) { 6249480093f4SDimitry Andric case KEY_UP: 6250480093f4SDimitry Andric if (m_first_visible_line > 0) 6251480093f4SDimitry Andric --m_first_visible_line; 6252480093f4SDimitry Andric break; 6253480093f4SDimitry Andric 6254480093f4SDimitry Andric case KEY_DOWN: 6255480093f4SDimitry Andric if (m_first_visible_line + num_visible_lines < num_lines) 6256480093f4SDimitry Andric ++m_first_visible_line; 6257480093f4SDimitry Andric break; 6258480093f4SDimitry Andric 6259480093f4SDimitry Andric case KEY_PPAGE: 6260480093f4SDimitry Andric case ',': 6261480093f4SDimitry Andric if (m_first_visible_line > 0) { 6262480093f4SDimitry Andric if (static_cast<size_t>(m_first_visible_line) >= num_visible_lines) 6263480093f4SDimitry Andric m_first_visible_line -= num_visible_lines; 6264480093f4SDimitry Andric else 6265480093f4SDimitry Andric m_first_visible_line = 0; 6266480093f4SDimitry Andric } 6267480093f4SDimitry Andric break; 6268480093f4SDimitry Andric 6269480093f4SDimitry Andric case KEY_NPAGE: 6270480093f4SDimitry Andric case '.': 6271480093f4SDimitry Andric if (m_first_visible_line + num_visible_lines < num_lines) { 6272480093f4SDimitry Andric m_first_visible_line += num_visible_lines; 6273480093f4SDimitry Andric if (static_cast<size_t>(m_first_visible_line) > num_lines) 6274480093f4SDimitry Andric m_first_visible_line = num_lines - num_visible_lines; 6275480093f4SDimitry Andric } 6276480093f4SDimitry Andric break; 6277480093f4SDimitry Andric 6278480093f4SDimitry Andric default: 6279480093f4SDimitry Andric done = true; 6280480093f4SDimitry Andric break; 6281480093f4SDimitry Andric } 6282480093f4SDimitry Andric } 6283480093f4SDimitry Andric if (done) 6284480093f4SDimitry Andric window.GetParent()->RemoveSubWindow(&window); 6285480093f4SDimitry Andric return eKeyHandled; 6286480093f4SDimitry Andric } 6287480093f4SDimitry Andric 6288480093f4SDimitry Andric class ApplicationDelegate : public WindowDelegate, public MenuDelegate { 6289480093f4SDimitry Andric public: 6290480093f4SDimitry Andric enum { 6291480093f4SDimitry Andric eMenuID_LLDB = 1, 6292480093f4SDimitry Andric eMenuID_LLDBAbout, 6293480093f4SDimitry Andric eMenuID_LLDBExit, 6294480093f4SDimitry Andric 6295480093f4SDimitry Andric eMenuID_Target, 6296480093f4SDimitry Andric eMenuID_TargetCreate, 6297480093f4SDimitry Andric eMenuID_TargetDelete, 6298480093f4SDimitry Andric 6299480093f4SDimitry Andric eMenuID_Process, 6300480093f4SDimitry Andric eMenuID_ProcessAttach, 6301e8d8bef9SDimitry Andric eMenuID_ProcessDetachResume, 6302e8d8bef9SDimitry Andric eMenuID_ProcessDetachSuspended, 6303480093f4SDimitry Andric eMenuID_ProcessLaunch, 6304480093f4SDimitry Andric eMenuID_ProcessContinue, 6305480093f4SDimitry Andric eMenuID_ProcessHalt, 6306480093f4SDimitry Andric eMenuID_ProcessKill, 6307480093f4SDimitry Andric 6308480093f4SDimitry Andric eMenuID_Thread, 6309480093f4SDimitry Andric eMenuID_ThreadStepIn, 6310480093f4SDimitry Andric eMenuID_ThreadStepOver, 6311480093f4SDimitry Andric eMenuID_ThreadStepOut, 6312480093f4SDimitry Andric 6313480093f4SDimitry Andric eMenuID_View, 6314480093f4SDimitry Andric eMenuID_ViewBacktrace, 6315480093f4SDimitry Andric eMenuID_ViewRegisters, 6316480093f4SDimitry Andric eMenuID_ViewSource, 6317480093f4SDimitry Andric eMenuID_ViewVariables, 6318349cc55cSDimitry Andric eMenuID_ViewBreakpoints, 6319480093f4SDimitry Andric 6320480093f4SDimitry Andric eMenuID_Help, 6321480093f4SDimitry Andric eMenuID_HelpGUIHelp 6322480093f4SDimitry Andric }; 6323480093f4SDimitry Andric 6324480093f4SDimitry Andric ApplicationDelegate(Application &app, Debugger &debugger) 6325480093f4SDimitry Andric : WindowDelegate(), MenuDelegate(), m_app(app), m_debugger(debugger) {} 6326480093f4SDimitry Andric 6327480093f4SDimitry Andric ~ApplicationDelegate() override = default; 6328480093f4SDimitry Andric 6329480093f4SDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override { 6330480093f4SDimitry Andric return false; // Drawing not handled, let standard window drawing happen 6331480093f4SDimitry Andric } 6332480093f4SDimitry Andric 6333480093f4SDimitry Andric HandleCharResult WindowDelegateHandleChar(Window &window, int key) override { 6334480093f4SDimitry Andric switch (key) { 6335480093f4SDimitry Andric case '\t': 6336480093f4SDimitry Andric window.SelectNextWindowAsActive(); 6337480093f4SDimitry Andric return eKeyHandled; 6338480093f4SDimitry Andric 6339fe6060f1SDimitry Andric case KEY_SHIFT_TAB: 6340e8d8bef9SDimitry Andric window.SelectPreviousWindowAsActive(); 6341e8d8bef9SDimitry Andric return eKeyHandled; 6342e8d8bef9SDimitry Andric 6343480093f4SDimitry Andric case 'h': 6344480093f4SDimitry Andric window.CreateHelpSubwindow(); 6345480093f4SDimitry Andric return eKeyHandled; 6346480093f4SDimitry Andric 6347480093f4SDimitry Andric case KEY_ESCAPE: 6348480093f4SDimitry Andric return eQuitApplication; 6349480093f4SDimitry Andric 6350480093f4SDimitry Andric default: 6351480093f4SDimitry Andric break; 6352480093f4SDimitry Andric } 6353480093f4SDimitry Andric return eKeyNotHandled; 6354480093f4SDimitry Andric } 6355480093f4SDimitry Andric 6356480093f4SDimitry Andric const char *WindowDelegateGetHelpText() override { 6357480093f4SDimitry Andric return "Welcome to the LLDB curses GUI.\n\n" 6358480093f4SDimitry Andric "Press the TAB key to change the selected view.\n" 6359480093f4SDimitry Andric "Each view has its own keyboard shortcuts, press 'h' to open a " 6360480093f4SDimitry Andric "dialog to display them.\n\n" 6361480093f4SDimitry Andric "Common key bindings for all views:"; 6362480093f4SDimitry Andric } 6363480093f4SDimitry Andric 6364480093f4SDimitry Andric KeyHelp *WindowDelegateGetKeyHelp() override { 6365480093f4SDimitry Andric static curses::KeyHelp g_source_view_key_help[] = { 6366480093f4SDimitry Andric {'\t', "Select next view"}, 6367e8d8bef9SDimitry Andric {KEY_BTAB, "Select previous view"}, 6368480093f4SDimitry Andric {'h', "Show help dialog with view specific key bindings"}, 6369480093f4SDimitry Andric {',', "Page up"}, 6370480093f4SDimitry Andric {'.', "Page down"}, 6371480093f4SDimitry Andric {KEY_UP, "Select previous"}, 6372480093f4SDimitry Andric {KEY_DOWN, "Select next"}, 6373480093f4SDimitry Andric {KEY_LEFT, "Unexpand or select parent"}, 6374480093f4SDimitry Andric {KEY_RIGHT, "Expand"}, 6375480093f4SDimitry Andric {KEY_PPAGE, "Page up"}, 6376480093f4SDimitry Andric {KEY_NPAGE, "Page down"}, 6377480093f4SDimitry Andric {'\0', nullptr}}; 6378480093f4SDimitry Andric return g_source_view_key_help; 6379480093f4SDimitry Andric } 6380480093f4SDimitry Andric 6381480093f4SDimitry Andric MenuActionResult MenuDelegateAction(Menu &menu) override { 6382480093f4SDimitry Andric switch (menu.GetIdentifier()) { 6383349cc55cSDimitry Andric case eMenuID_TargetCreate: { 6384349cc55cSDimitry Andric WindowSP main_window_sp = m_app.GetMainWindow(); 6385349cc55cSDimitry Andric FormDelegateSP form_delegate_sp = 6386349cc55cSDimitry Andric FormDelegateSP(new TargetCreateFormDelegate(m_debugger)); 6387349cc55cSDimitry Andric Rect bounds = main_window_sp->GetCenteredRect(80, 19); 6388349cc55cSDimitry Andric WindowSP form_window_sp = main_window_sp->CreateSubWindow( 6389349cc55cSDimitry Andric form_delegate_sp->GetName().c_str(), bounds, true); 6390349cc55cSDimitry Andric WindowDelegateSP window_delegate_sp = 6391349cc55cSDimitry Andric WindowDelegateSP(new FormWindowDelegate(form_delegate_sp)); 6392349cc55cSDimitry Andric form_window_sp->SetDelegate(window_delegate_sp); 6393349cc55cSDimitry Andric return MenuActionResult::Handled; 6394349cc55cSDimitry Andric } 6395480093f4SDimitry Andric case eMenuID_ThreadStepIn: { 6396480093f4SDimitry Andric ExecutionContext exe_ctx = 6397480093f4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext(); 6398480093f4SDimitry Andric if (exe_ctx.HasThreadScope()) { 6399480093f4SDimitry Andric Process *process = exe_ctx.GetProcessPtr(); 6400480093f4SDimitry Andric if (process && process->IsAlive() && 6401480093f4SDimitry Andric StateIsStoppedState(process->GetState(), true)) 6402480093f4SDimitry Andric exe_ctx.GetThreadRef().StepIn(true); 6403480093f4SDimitry Andric } 6404480093f4SDimitry Andric } 6405480093f4SDimitry Andric return MenuActionResult::Handled; 6406480093f4SDimitry Andric 6407480093f4SDimitry Andric case eMenuID_ThreadStepOut: { 6408480093f4SDimitry Andric ExecutionContext exe_ctx = 6409480093f4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext(); 6410480093f4SDimitry Andric if (exe_ctx.HasThreadScope()) { 6411480093f4SDimitry Andric Process *process = exe_ctx.GetProcessPtr(); 6412480093f4SDimitry Andric if (process && process->IsAlive() && 641381ad6265SDimitry Andric StateIsStoppedState(process->GetState(), true)) { 641481ad6265SDimitry Andric Thread *thread = exe_ctx.GetThreadPtr(); 6415*06c3fb27SDimitry Andric uint32_t frame_idx = 6416*06c3fb27SDimitry Andric thread->GetSelectedFrameIndex(SelectMostRelevantFrame); 641781ad6265SDimitry Andric exe_ctx.GetThreadRef().StepOut(frame_idx); 641881ad6265SDimitry Andric } 6419480093f4SDimitry Andric } 6420480093f4SDimitry Andric } 6421480093f4SDimitry Andric return MenuActionResult::Handled; 6422480093f4SDimitry Andric 6423480093f4SDimitry Andric case eMenuID_ThreadStepOver: { 6424480093f4SDimitry Andric ExecutionContext exe_ctx = 6425480093f4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext(); 6426480093f4SDimitry Andric if (exe_ctx.HasThreadScope()) { 6427480093f4SDimitry Andric Process *process = exe_ctx.GetProcessPtr(); 6428480093f4SDimitry Andric if (process && process->IsAlive() && 6429480093f4SDimitry Andric StateIsStoppedState(process->GetState(), true)) 6430480093f4SDimitry Andric exe_ctx.GetThreadRef().StepOver(true); 6431480093f4SDimitry Andric } 6432480093f4SDimitry Andric } 6433480093f4SDimitry Andric return MenuActionResult::Handled; 6434480093f4SDimitry Andric 6435fe6060f1SDimitry Andric case eMenuID_ProcessAttach: { 6436fe6060f1SDimitry Andric WindowSP main_window_sp = m_app.GetMainWindow(); 6437fe6060f1SDimitry Andric FormDelegateSP form_delegate_sp = FormDelegateSP( 6438fe6060f1SDimitry Andric new ProcessAttachFormDelegate(m_debugger, main_window_sp)); 6439fe6060f1SDimitry Andric Rect bounds = main_window_sp->GetCenteredRect(80, 22); 6440fe6060f1SDimitry Andric WindowSP form_window_sp = main_window_sp->CreateSubWindow( 6441fe6060f1SDimitry Andric form_delegate_sp->GetName().c_str(), bounds, true); 6442fe6060f1SDimitry Andric WindowDelegateSP window_delegate_sp = 6443fe6060f1SDimitry Andric WindowDelegateSP(new FormWindowDelegate(form_delegate_sp)); 6444fe6060f1SDimitry Andric form_window_sp->SetDelegate(window_delegate_sp); 6445fe6060f1SDimitry Andric return MenuActionResult::Handled; 6446fe6060f1SDimitry Andric } 6447349cc55cSDimitry Andric case eMenuID_ProcessLaunch: { 6448349cc55cSDimitry Andric WindowSP main_window_sp = m_app.GetMainWindow(); 6449349cc55cSDimitry Andric FormDelegateSP form_delegate_sp = FormDelegateSP( 6450349cc55cSDimitry Andric new ProcessLaunchFormDelegate(m_debugger, main_window_sp)); 6451349cc55cSDimitry Andric Rect bounds = main_window_sp->GetCenteredRect(80, 22); 6452349cc55cSDimitry Andric WindowSP form_window_sp = main_window_sp->CreateSubWindow( 6453349cc55cSDimitry Andric form_delegate_sp->GetName().c_str(), bounds, true); 6454349cc55cSDimitry Andric WindowDelegateSP window_delegate_sp = 6455349cc55cSDimitry Andric WindowDelegateSP(new FormWindowDelegate(form_delegate_sp)); 6456349cc55cSDimitry Andric form_window_sp->SetDelegate(window_delegate_sp); 6457349cc55cSDimitry Andric return MenuActionResult::Handled; 6458349cc55cSDimitry Andric } 6459fe6060f1SDimitry Andric 6460480093f4SDimitry Andric case eMenuID_ProcessContinue: { 6461480093f4SDimitry Andric ExecutionContext exe_ctx = 6462480093f4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext(); 6463480093f4SDimitry Andric if (exe_ctx.HasProcessScope()) { 6464480093f4SDimitry Andric Process *process = exe_ctx.GetProcessPtr(); 6465480093f4SDimitry Andric if (process && process->IsAlive() && 6466480093f4SDimitry Andric StateIsStoppedState(process->GetState(), true)) 6467480093f4SDimitry Andric process->Resume(); 6468480093f4SDimitry Andric } 6469480093f4SDimitry Andric } 6470480093f4SDimitry Andric return MenuActionResult::Handled; 6471480093f4SDimitry Andric 6472480093f4SDimitry Andric case eMenuID_ProcessKill: { 6473480093f4SDimitry Andric ExecutionContext exe_ctx = 6474480093f4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext(); 6475480093f4SDimitry Andric if (exe_ctx.HasProcessScope()) { 6476480093f4SDimitry Andric Process *process = exe_ctx.GetProcessPtr(); 6477480093f4SDimitry Andric if (process && process->IsAlive()) 6478480093f4SDimitry Andric process->Destroy(false); 6479480093f4SDimitry Andric } 6480480093f4SDimitry Andric } 6481480093f4SDimitry Andric return MenuActionResult::Handled; 6482480093f4SDimitry Andric 6483480093f4SDimitry Andric case eMenuID_ProcessHalt: { 6484480093f4SDimitry Andric ExecutionContext exe_ctx = 6485480093f4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext(); 6486480093f4SDimitry Andric if (exe_ctx.HasProcessScope()) { 6487480093f4SDimitry Andric Process *process = exe_ctx.GetProcessPtr(); 6488480093f4SDimitry Andric if (process && process->IsAlive()) 6489480093f4SDimitry Andric process->Halt(); 6490480093f4SDimitry Andric } 6491480093f4SDimitry Andric } 6492480093f4SDimitry Andric return MenuActionResult::Handled; 6493480093f4SDimitry Andric 6494e8d8bef9SDimitry Andric case eMenuID_ProcessDetachResume: 6495e8d8bef9SDimitry Andric case eMenuID_ProcessDetachSuspended: { 6496480093f4SDimitry Andric ExecutionContext exe_ctx = 6497480093f4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext(); 6498480093f4SDimitry Andric if (exe_ctx.HasProcessScope()) { 6499480093f4SDimitry Andric Process *process = exe_ctx.GetProcessPtr(); 6500480093f4SDimitry Andric if (process && process->IsAlive()) 6501e8d8bef9SDimitry Andric process->Detach(menu.GetIdentifier() == 6502e8d8bef9SDimitry Andric eMenuID_ProcessDetachSuspended); 6503480093f4SDimitry Andric } 6504480093f4SDimitry Andric } 6505480093f4SDimitry Andric return MenuActionResult::Handled; 6506480093f4SDimitry Andric 6507480093f4SDimitry Andric case eMenuID_Process: { 6508480093f4SDimitry Andric // Populate the menu with all of the threads if the process is stopped 6509480093f4SDimitry Andric // when the Process menu gets selected and is about to display its 6510480093f4SDimitry Andric // submenu. 6511480093f4SDimitry Andric Menus &submenus = menu.GetSubmenus(); 6512480093f4SDimitry Andric ExecutionContext exe_ctx = 6513480093f4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext(); 6514480093f4SDimitry Andric Process *process = exe_ctx.GetProcessPtr(); 6515480093f4SDimitry Andric if (process && process->IsAlive() && 6516480093f4SDimitry Andric StateIsStoppedState(process->GetState(), true)) { 6517480093f4SDimitry Andric if (submenus.size() == 7) 6518480093f4SDimitry Andric menu.AddSubmenu(MenuSP(new Menu(Menu::Type::Separator))); 6519480093f4SDimitry Andric else if (submenus.size() > 8) 6520480093f4SDimitry Andric submenus.erase(submenus.begin() + 8, submenus.end()); 6521480093f4SDimitry Andric 6522480093f4SDimitry Andric ThreadList &threads = process->GetThreadList(); 6523480093f4SDimitry Andric std::lock_guard<std::recursive_mutex> guard(threads.GetMutex()); 6524480093f4SDimitry Andric size_t num_threads = threads.GetSize(); 6525480093f4SDimitry Andric for (size_t i = 0; i < num_threads; ++i) { 6526480093f4SDimitry Andric ThreadSP thread_sp = threads.GetThreadAtIndex(i); 6527480093f4SDimitry Andric char menu_char = '\0'; 6528480093f4SDimitry Andric if (i < 9) 6529480093f4SDimitry Andric menu_char = '1' + i; 6530480093f4SDimitry Andric StreamString thread_menu_title; 6531480093f4SDimitry Andric thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID()); 6532480093f4SDimitry Andric const char *thread_name = thread_sp->GetName(); 6533480093f4SDimitry Andric if (thread_name && thread_name[0]) 6534480093f4SDimitry Andric thread_menu_title.Printf(" %s", thread_name); 6535480093f4SDimitry Andric else { 6536480093f4SDimitry Andric const char *queue_name = thread_sp->GetQueueName(); 6537480093f4SDimitry Andric if (queue_name && queue_name[0]) 6538480093f4SDimitry Andric thread_menu_title.Printf(" %s", queue_name); 6539480093f4SDimitry Andric } 6540480093f4SDimitry Andric menu.AddSubmenu( 6541480093f4SDimitry Andric MenuSP(new Menu(thread_menu_title.GetString().str().c_str(), 6542480093f4SDimitry Andric nullptr, menu_char, thread_sp->GetID()))); 6543480093f4SDimitry Andric } 6544480093f4SDimitry Andric } else if (submenus.size() > 7) { 6545480093f4SDimitry Andric // Remove the separator and any other thread submenu items that were 6546480093f4SDimitry Andric // previously added 6547480093f4SDimitry Andric submenus.erase(submenus.begin() + 7, submenus.end()); 6548480093f4SDimitry Andric } 6549349cc55cSDimitry Andric // Since we are adding and removing items we need to recalculate the 6550349cc55cSDimitry Andric // name lengths 6551480093f4SDimitry Andric menu.RecalculateNameLengths(); 6552480093f4SDimitry Andric } 6553480093f4SDimitry Andric return MenuActionResult::Handled; 6554480093f4SDimitry Andric 6555480093f4SDimitry Andric case eMenuID_ViewVariables: { 6556480093f4SDimitry Andric WindowSP main_window_sp = m_app.GetMainWindow(); 6557480093f4SDimitry Andric WindowSP source_window_sp = main_window_sp->FindSubWindow("Source"); 6558480093f4SDimitry Andric WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables"); 6559480093f4SDimitry Andric WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers"); 6560480093f4SDimitry Andric const Rect source_bounds = source_window_sp->GetBounds(); 6561480093f4SDimitry Andric 6562480093f4SDimitry Andric if (variables_window_sp) { 6563480093f4SDimitry Andric const Rect variables_bounds = variables_window_sp->GetBounds(); 6564480093f4SDimitry Andric 6565480093f4SDimitry Andric main_window_sp->RemoveSubWindow(variables_window_sp.get()); 6566480093f4SDimitry Andric 6567480093f4SDimitry Andric if (registers_window_sp) { 6568480093f4SDimitry Andric // We have a registers window, so give all the area back to the 6569480093f4SDimitry Andric // registers window 6570480093f4SDimitry Andric Rect registers_bounds = variables_bounds; 6571480093f4SDimitry Andric registers_bounds.size.width = source_bounds.size.width; 6572480093f4SDimitry Andric registers_window_sp->SetBounds(registers_bounds); 6573480093f4SDimitry Andric } else { 6574480093f4SDimitry Andric // We have no registers window showing so give the bottom area back 6575480093f4SDimitry Andric // to the source view 6576480093f4SDimitry Andric source_window_sp->Resize(source_bounds.size.width, 6577480093f4SDimitry Andric source_bounds.size.height + 6578480093f4SDimitry Andric variables_bounds.size.height); 6579480093f4SDimitry Andric } 6580480093f4SDimitry Andric } else { 6581480093f4SDimitry Andric Rect new_variables_rect; 6582480093f4SDimitry Andric if (registers_window_sp) { 6583480093f4SDimitry Andric // We have a registers window so split the area of the registers 6584480093f4SDimitry Andric // window into two columns where the left hand side will be the 6585480093f4SDimitry Andric // variables and the right hand side will be the registers 6586480093f4SDimitry Andric const Rect variables_bounds = registers_window_sp->GetBounds(); 6587480093f4SDimitry Andric Rect new_registers_rect; 6588480093f4SDimitry Andric variables_bounds.VerticalSplitPercentage(0.50, new_variables_rect, 6589480093f4SDimitry Andric new_registers_rect); 6590480093f4SDimitry Andric registers_window_sp->SetBounds(new_registers_rect); 6591480093f4SDimitry Andric } else { 6592e8d8bef9SDimitry Andric // No registers window, grab the bottom part of the source window 6593480093f4SDimitry Andric Rect new_source_rect; 6594480093f4SDimitry Andric source_bounds.HorizontalSplitPercentage(0.70, new_source_rect, 6595480093f4SDimitry Andric new_variables_rect); 6596480093f4SDimitry Andric source_window_sp->SetBounds(new_source_rect); 6597480093f4SDimitry Andric } 6598480093f4SDimitry Andric WindowSP new_window_sp = main_window_sp->CreateSubWindow( 6599480093f4SDimitry Andric "Variables", new_variables_rect, false); 6600480093f4SDimitry Andric new_window_sp->SetDelegate( 6601480093f4SDimitry Andric WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger))); 6602480093f4SDimitry Andric } 6603480093f4SDimitry Andric touchwin(stdscr); 6604480093f4SDimitry Andric } 6605480093f4SDimitry Andric return MenuActionResult::Handled; 6606480093f4SDimitry Andric 6607480093f4SDimitry Andric case eMenuID_ViewRegisters: { 6608480093f4SDimitry Andric WindowSP main_window_sp = m_app.GetMainWindow(); 6609480093f4SDimitry Andric WindowSP source_window_sp = main_window_sp->FindSubWindow("Source"); 6610480093f4SDimitry Andric WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables"); 6611480093f4SDimitry Andric WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers"); 6612480093f4SDimitry Andric const Rect source_bounds = source_window_sp->GetBounds(); 6613480093f4SDimitry Andric 6614480093f4SDimitry Andric if (registers_window_sp) { 6615480093f4SDimitry Andric if (variables_window_sp) { 6616480093f4SDimitry Andric const Rect variables_bounds = variables_window_sp->GetBounds(); 6617480093f4SDimitry Andric 6618480093f4SDimitry Andric // We have a variables window, so give all the area back to the 6619480093f4SDimitry Andric // variables window 6620480093f4SDimitry Andric variables_window_sp->Resize(variables_bounds.size.width + 6621480093f4SDimitry Andric registers_window_sp->GetWidth(), 6622480093f4SDimitry Andric variables_bounds.size.height); 6623480093f4SDimitry Andric } else { 6624480093f4SDimitry Andric // We have no variables window showing so give the bottom area back 6625480093f4SDimitry Andric // to the source view 6626480093f4SDimitry Andric source_window_sp->Resize(source_bounds.size.width, 6627480093f4SDimitry Andric source_bounds.size.height + 6628480093f4SDimitry Andric registers_window_sp->GetHeight()); 6629480093f4SDimitry Andric } 6630480093f4SDimitry Andric main_window_sp->RemoveSubWindow(registers_window_sp.get()); 6631480093f4SDimitry Andric } else { 6632480093f4SDimitry Andric Rect new_regs_rect; 6633480093f4SDimitry Andric if (variables_window_sp) { 6634480093f4SDimitry Andric // We have a variables window, split it into two columns where the 6635480093f4SDimitry Andric // left hand side will be the variables and the right hand side will 6636480093f4SDimitry Andric // be the registers 6637480093f4SDimitry Andric const Rect variables_bounds = variables_window_sp->GetBounds(); 6638480093f4SDimitry Andric Rect new_vars_rect; 6639480093f4SDimitry Andric variables_bounds.VerticalSplitPercentage(0.50, new_vars_rect, 6640480093f4SDimitry Andric new_regs_rect); 6641480093f4SDimitry Andric variables_window_sp->SetBounds(new_vars_rect); 6642480093f4SDimitry Andric } else { 6643e8d8bef9SDimitry Andric // No variables window, grab the bottom part of the source window 6644480093f4SDimitry Andric Rect new_source_rect; 6645480093f4SDimitry Andric source_bounds.HorizontalSplitPercentage(0.70, new_source_rect, 6646480093f4SDimitry Andric new_regs_rect); 6647480093f4SDimitry Andric source_window_sp->SetBounds(new_source_rect); 6648480093f4SDimitry Andric } 6649480093f4SDimitry Andric WindowSP new_window_sp = 6650480093f4SDimitry Andric main_window_sp->CreateSubWindow("Registers", new_regs_rect, false); 6651480093f4SDimitry Andric new_window_sp->SetDelegate( 6652480093f4SDimitry Andric WindowDelegateSP(new RegistersWindowDelegate(m_debugger))); 6653480093f4SDimitry Andric } 6654480093f4SDimitry Andric touchwin(stdscr); 6655480093f4SDimitry Andric } 6656480093f4SDimitry Andric return MenuActionResult::Handled; 6657480093f4SDimitry Andric 6658349cc55cSDimitry Andric case eMenuID_ViewBreakpoints: { 6659349cc55cSDimitry Andric WindowSP main_window_sp = m_app.GetMainWindow(); 6660349cc55cSDimitry Andric WindowSP threads_window_sp = main_window_sp->FindSubWindow("Threads"); 6661349cc55cSDimitry Andric WindowSP breakpoints_window_sp = 6662349cc55cSDimitry Andric main_window_sp->FindSubWindow("Breakpoints"); 6663349cc55cSDimitry Andric const Rect threads_bounds = threads_window_sp->GetBounds(); 6664349cc55cSDimitry Andric 6665349cc55cSDimitry Andric // If a breakpoints window already exists, remove it and give the area 6666349cc55cSDimitry Andric // it used to occupy to the threads window. If it doesn't exist, split 6667349cc55cSDimitry Andric // the threads window horizontally into two windows where the top window 6668349cc55cSDimitry Andric // is the threads window and the bottom window is a newly added 6669349cc55cSDimitry Andric // breakpoints window. 6670349cc55cSDimitry Andric if (breakpoints_window_sp) { 6671349cc55cSDimitry Andric threads_window_sp->Resize(threads_bounds.size.width, 6672349cc55cSDimitry Andric threads_bounds.size.height + 6673349cc55cSDimitry Andric breakpoints_window_sp->GetHeight()); 6674349cc55cSDimitry Andric main_window_sp->RemoveSubWindow(breakpoints_window_sp.get()); 6675349cc55cSDimitry Andric } else { 6676349cc55cSDimitry Andric Rect new_threads_bounds, breakpoints_bounds; 6677349cc55cSDimitry Andric threads_bounds.HorizontalSplitPercentage(0.70, new_threads_bounds, 6678349cc55cSDimitry Andric breakpoints_bounds); 6679349cc55cSDimitry Andric threads_window_sp->SetBounds(new_threads_bounds); 6680349cc55cSDimitry Andric breakpoints_window_sp = main_window_sp->CreateSubWindow( 6681349cc55cSDimitry Andric "Breakpoints", breakpoints_bounds, false); 6682349cc55cSDimitry Andric TreeDelegateSP breakpoints_delegate_sp( 6683349cc55cSDimitry Andric new BreakpointsTreeDelegate(m_debugger)); 6684349cc55cSDimitry Andric breakpoints_window_sp->SetDelegate(WindowDelegateSP( 6685349cc55cSDimitry Andric new TreeWindowDelegate(m_debugger, breakpoints_delegate_sp))); 6686349cc55cSDimitry Andric } 6687349cc55cSDimitry Andric touchwin(stdscr); 6688349cc55cSDimitry Andric return MenuActionResult::Handled; 6689349cc55cSDimitry Andric } 6690349cc55cSDimitry Andric 6691480093f4SDimitry Andric case eMenuID_HelpGUIHelp: 6692480093f4SDimitry Andric m_app.GetMainWindow()->CreateHelpSubwindow(); 6693480093f4SDimitry Andric return MenuActionResult::Handled; 6694480093f4SDimitry Andric 6695480093f4SDimitry Andric default: 6696480093f4SDimitry Andric break; 6697480093f4SDimitry Andric } 6698480093f4SDimitry Andric 6699480093f4SDimitry Andric return MenuActionResult::NotHandled; 6700480093f4SDimitry Andric } 6701480093f4SDimitry Andric 6702480093f4SDimitry Andric protected: 6703480093f4SDimitry Andric Application &m_app; 6704480093f4SDimitry Andric Debugger &m_debugger; 6705480093f4SDimitry Andric }; 6706480093f4SDimitry Andric 6707480093f4SDimitry Andric class StatusBarWindowDelegate : public WindowDelegate { 6708480093f4SDimitry Andric public: 6709480093f4SDimitry Andric StatusBarWindowDelegate(Debugger &debugger) : m_debugger(debugger) { 6710480093f4SDimitry Andric FormatEntity::Parse("Thread: ${thread.id%tid}", m_format); 6711480093f4SDimitry Andric } 6712480093f4SDimitry Andric 6713480093f4SDimitry Andric ~StatusBarWindowDelegate() override = default; 6714480093f4SDimitry Andric 6715480093f4SDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override { 6716480093f4SDimitry Andric ExecutionContext exe_ctx = 6717480093f4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext(); 6718480093f4SDimitry Andric Process *process = exe_ctx.GetProcessPtr(); 6719480093f4SDimitry Andric Thread *thread = exe_ctx.GetThreadPtr(); 6720480093f4SDimitry Andric StackFrame *frame = exe_ctx.GetFramePtr(); 6721480093f4SDimitry Andric window.Erase(); 6722e8d8bef9SDimitry Andric window.SetBackground(BlackOnWhite); 6723480093f4SDimitry Andric window.MoveCursor(0, 0); 6724480093f4SDimitry Andric if (process) { 6725480093f4SDimitry Andric const StateType state = process->GetState(); 6726480093f4SDimitry Andric window.Printf("Process: %5" PRIu64 " %10s", process->GetID(), 6727480093f4SDimitry Andric StateAsCString(state)); 6728480093f4SDimitry Andric 6729480093f4SDimitry Andric if (StateIsStoppedState(state, true)) { 6730480093f4SDimitry Andric StreamString strm; 6731480093f4SDimitry Andric if (thread && FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, 6732480093f4SDimitry Andric nullptr, nullptr, false, false)) { 6733480093f4SDimitry Andric window.MoveCursor(40, 0); 6734e8d8bef9SDimitry Andric window.PutCStringTruncated(1, strm.GetString().str().c_str()); 6735480093f4SDimitry Andric } 6736480093f4SDimitry Andric 6737480093f4SDimitry Andric window.MoveCursor(60, 0); 6738480093f4SDimitry Andric if (frame) 6739480093f4SDimitry Andric window.Printf("Frame: %3u PC = 0x%16.16" PRIx64, 6740480093f4SDimitry Andric frame->GetFrameIndex(), 6741480093f4SDimitry Andric frame->GetFrameCodeAddress().GetOpcodeLoadAddress( 6742480093f4SDimitry Andric exe_ctx.GetTargetPtr())); 6743480093f4SDimitry Andric } else if (state == eStateExited) { 6744480093f4SDimitry Andric const char *exit_desc = process->GetExitDescription(); 6745480093f4SDimitry Andric const int exit_status = process->GetExitStatus(); 6746480093f4SDimitry Andric if (exit_desc && exit_desc[0]) 6747480093f4SDimitry Andric window.Printf(" with status = %i (%s)", exit_status, exit_desc); 6748480093f4SDimitry Andric else 6749480093f4SDimitry Andric window.Printf(" with status = %i", exit_status); 6750480093f4SDimitry Andric } 6751480093f4SDimitry Andric } 6752480093f4SDimitry Andric return true; 6753480093f4SDimitry Andric } 6754480093f4SDimitry Andric 6755480093f4SDimitry Andric protected: 6756480093f4SDimitry Andric Debugger &m_debugger; 6757480093f4SDimitry Andric FormatEntity::Entry m_format; 6758480093f4SDimitry Andric }; 6759480093f4SDimitry Andric 6760480093f4SDimitry Andric class SourceFileWindowDelegate : public WindowDelegate { 6761480093f4SDimitry Andric public: 6762480093f4SDimitry Andric SourceFileWindowDelegate(Debugger &debugger) 6763480093f4SDimitry Andric : WindowDelegate(), m_debugger(debugger), m_sc(), m_file_sp(), 676481ad6265SDimitry Andric m_disassembly_sp(), m_disassembly_range(), m_title() {} 6765480093f4SDimitry Andric 6766480093f4SDimitry Andric ~SourceFileWindowDelegate() override = default; 6767480093f4SDimitry Andric 6768480093f4SDimitry Andric void Update(const SymbolContext &sc) { m_sc = sc; } 6769480093f4SDimitry Andric 6770480093f4SDimitry Andric uint32_t NumVisibleLines() const { return m_max_y - m_min_y; } 6771480093f4SDimitry Andric 6772480093f4SDimitry Andric const char *WindowDelegateGetHelpText() override { 6773480093f4SDimitry Andric return "Source/Disassembly window keyboard shortcuts:"; 6774480093f4SDimitry Andric } 6775480093f4SDimitry Andric 6776480093f4SDimitry Andric KeyHelp *WindowDelegateGetKeyHelp() override { 6777480093f4SDimitry Andric static curses::KeyHelp g_source_view_key_help[] = { 6778480093f4SDimitry Andric {KEY_RETURN, "Run to selected line with one shot breakpoint"}, 6779480093f4SDimitry Andric {KEY_UP, "Select previous source line"}, 6780480093f4SDimitry Andric {KEY_DOWN, "Select next source line"}, 6781e8d8bef9SDimitry Andric {KEY_LEFT, "Scroll to the left"}, 6782e8d8bef9SDimitry Andric {KEY_RIGHT, "Scroll to the right"}, 6783480093f4SDimitry Andric {KEY_PPAGE, "Page up"}, 6784480093f4SDimitry Andric {KEY_NPAGE, "Page down"}, 6785480093f4SDimitry Andric {'b', "Set breakpoint on selected source/disassembly line"}, 6786480093f4SDimitry Andric {'c', "Continue process"}, 6787480093f4SDimitry Andric {'D', "Detach with process suspended"}, 6788480093f4SDimitry Andric {'h', "Show help dialog"}, 6789480093f4SDimitry Andric {'n', "Step over (source line)"}, 6790480093f4SDimitry Andric {'N', "Step over (single instruction)"}, 6791e8d8bef9SDimitry Andric {'f', "Step out (finish)"}, 6792480093f4SDimitry Andric {'s', "Step in (source line)"}, 6793480093f4SDimitry Andric {'S', "Step in (single instruction)"}, 6794e8d8bef9SDimitry Andric {'u', "Frame up"}, 6795e8d8bef9SDimitry Andric {'d', "Frame down"}, 6796480093f4SDimitry Andric {',', "Page up"}, 6797480093f4SDimitry Andric {'.', "Page down"}, 6798480093f4SDimitry Andric {'\0', nullptr}}; 6799480093f4SDimitry Andric return g_source_view_key_help; 6800480093f4SDimitry Andric } 6801480093f4SDimitry Andric 6802480093f4SDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override { 6803480093f4SDimitry Andric ExecutionContext exe_ctx = 6804480093f4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext(); 6805480093f4SDimitry Andric Process *process = exe_ctx.GetProcessPtr(); 6806480093f4SDimitry Andric Thread *thread = nullptr; 6807480093f4SDimitry Andric 6808480093f4SDimitry Andric bool update_location = false; 6809480093f4SDimitry Andric if (process) { 6810480093f4SDimitry Andric StateType state = process->GetState(); 6811480093f4SDimitry Andric if (StateIsStoppedState(state, true)) { 6812480093f4SDimitry Andric // We are stopped, so it is ok to 6813480093f4SDimitry Andric update_location = true; 6814480093f4SDimitry Andric } 6815480093f4SDimitry Andric } 6816480093f4SDimitry Andric 6817480093f4SDimitry Andric m_min_x = 1; 6818480093f4SDimitry Andric m_min_y = 2; 6819480093f4SDimitry Andric m_max_x = window.GetMaxX() - 1; 6820480093f4SDimitry Andric m_max_y = window.GetMaxY() - 1; 6821480093f4SDimitry Andric 6822480093f4SDimitry Andric const uint32_t num_visible_lines = NumVisibleLines(); 6823480093f4SDimitry Andric StackFrameSP frame_sp; 6824480093f4SDimitry Andric bool set_selected_line_to_pc = false; 6825480093f4SDimitry Andric 6826480093f4SDimitry Andric if (update_location) { 6827bdd1243dSDimitry Andric const bool process_alive = process->IsAlive(); 6828480093f4SDimitry Andric bool thread_changed = false; 6829480093f4SDimitry Andric if (process_alive) { 6830480093f4SDimitry Andric thread = exe_ctx.GetThreadPtr(); 6831480093f4SDimitry Andric if (thread) { 6832*06c3fb27SDimitry Andric frame_sp = thread->GetSelectedFrame(SelectMostRelevantFrame); 6833480093f4SDimitry Andric auto tid = thread->GetID(); 6834480093f4SDimitry Andric thread_changed = tid != m_tid; 6835480093f4SDimitry Andric m_tid = tid; 6836480093f4SDimitry Andric } else { 6837480093f4SDimitry Andric if (m_tid != LLDB_INVALID_THREAD_ID) { 6838480093f4SDimitry Andric thread_changed = true; 6839480093f4SDimitry Andric m_tid = LLDB_INVALID_THREAD_ID; 6840480093f4SDimitry Andric } 6841480093f4SDimitry Andric } 6842480093f4SDimitry Andric } 6843480093f4SDimitry Andric const uint32_t stop_id = process ? process->GetStopID() : 0; 6844480093f4SDimitry Andric const bool stop_id_changed = stop_id != m_stop_id; 6845480093f4SDimitry Andric bool frame_changed = false; 6846480093f4SDimitry Andric m_stop_id = stop_id; 6847480093f4SDimitry Andric m_title.Clear(); 6848480093f4SDimitry Andric if (frame_sp) { 6849480093f4SDimitry Andric m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything); 6850480093f4SDimitry Andric if (m_sc.module_sp) { 6851480093f4SDimitry Andric m_title.Printf( 6852480093f4SDimitry Andric "%s", m_sc.module_sp->GetFileSpec().GetFilename().GetCString()); 6853480093f4SDimitry Andric ConstString func_name = m_sc.GetFunctionName(); 6854480093f4SDimitry Andric if (func_name) 6855480093f4SDimitry Andric m_title.Printf("`%s", func_name.GetCString()); 6856480093f4SDimitry Andric } 6857480093f4SDimitry Andric const uint32_t frame_idx = frame_sp->GetFrameIndex(); 6858480093f4SDimitry Andric frame_changed = frame_idx != m_frame_idx; 6859480093f4SDimitry Andric m_frame_idx = frame_idx; 6860480093f4SDimitry Andric } else { 6861480093f4SDimitry Andric m_sc.Clear(true); 6862480093f4SDimitry Andric frame_changed = m_frame_idx != UINT32_MAX; 6863480093f4SDimitry Andric m_frame_idx = UINT32_MAX; 6864480093f4SDimitry Andric } 6865480093f4SDimitry Andric 6866480093f4SDimitry Andric const bool context_changed = 6867480093f4SDimitry Andric thread_changed || frame_changed || stop_id_changed; 6868480093f4SDimitry Andric 6869480093f4SDimitry Andric if (process_alive) { 6870480093f4SDimitry Andric if (m_sc.line_entry.IsValid()) { 6871480093f4SDimitry Andric m_pc_line = m_sc.line_entry.line; 6872480093f4SDimitry Andric if (m_pc_line != UINT32_MAX) 6873480093f4SDimitry Andric --m_pc_line; // Convert to zero based line number... 6874480093f4SDimitry Andric // Update the selected line if the stop ID changed... 6875480093f4SDimitry Andric if (context_changed) 6876480093f4SDimitry Andric m_selected_line = m_pc_line; 6877480093f4SDimitry Andric 6878480093f4SDimitry Andric if (m_file_sp && m_file_sp->GetFileSpec() == m_sc.line_entry.file) { 6879349cc55cSDimitry Andric // Same file, nothing to do, we should either have the lines or 6880349cc55cSDimitry Andric // not (source file missing) 6881480093f4SDimitry Andric if (m_selected_line >= static_cast<size_t>(m_first_visible_line)) { 6882480093f4SDimitry Andric if (m_selected_line >= m_first_visible_line + num_visible_lines) 6883480093f4SDimitry Andric m_first_visible_line = m_selected_line - 10; 6884480093f4SDimitry Andric } else { 6885480093f4SDimitry Andric if (m_selected_line > 10) 6886480093f4SDimitry Andric m_first_visible_line = m_selected_line - 10; 6887480093f4SDimitry Andric else 6888480093f4SDimitry Andric m_first_visible_line = 0; 6889480093f4SDimitry Andric } 6890480093f4SDimitry Andric } else { 6891480093f4SDimitry Andric // File changed, set selected line to the line with the PC 6892480093f4SDimitry Andric m_selected_line = m_pc_line; 6893480093f4SDimitry Andric m_file_sp = 6894480093f4SDimitry Andric m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file); 6895480093f4SDimitry Andric if (m_file_sp) { 6896480093f4SDimitry Andric const size_t num_lines = m_file_sp->GetNumLines(); 6897480093f4SDimitry Andric m_line_width = 1; 6898480093f4SDimitry Andric for (size_t n = num_lines; n >= 10; n = n / 10) 6899480093f4SDimitry Andric ++m_line_width; 6900480093f4SDimitry Andric 6901480093f4SDimitry Andric if (num_lines < num_visible_lines || 6902480093f4SDimitry Andric m_selected_line < num_visible_lines) 6903480093f4SDimitry Andric m_first_visible_line = 0; 6904480093f4SDimitry Andric else 6905480093f4SDimitry Andric m_first_visible_line = m_selected_line - 10; 6906480093f4SDimitry Andric } 6907480093f4SDimitry Andric } 6908480093f4SDimitry Andric } else { 6909480093f4SDimitry Andric m_file_sp.reset(); 6910480093f4SDimitry Andric } 6911480093f4SDimitry Andric 6912480093f4SDimitry Andric if (!m_file_sp || m_file_sp->GetNumLines() == 0) { 6913480093f4SDimitry Andric // Show disassembly 6914480093f4SDimitry Andric bool prefer_file_cache = false; 6915480093f4SDimitry Andric if (m_sc.function) { 6916480093f4SDimitry Andric if (m_disassembly_scope != m_sc.function) { 6917480093f4SDimitry Andric m_disassembly_scope = m_sc.function; 6918480093f4SDimitry Andric m_disassembly_sp = m_sc.function->GetInstructions( 6919fe6060f1SDimitry Andric exe_ctx, nullptr, !prefer_file_cache); 6920480093f4SDimitry Andric if (m_disassembly_sp) { 6921480093f4SDimitry Andric set_selected_line_to_pc = true; 6922480093f4SDimitry Andric m_disassembly_range = m_sc.function->GetAddressRange(); 6923480093f4SDimitry Andric } else { 6924480093f4SDimitry Andric m_disassembly_range.Clear(); 6925480093f4SDimitry Andric } 6926480093f4SDimitry Andric } else { 6927480093f4SDimitry Andric set_selected_line_to_pc = context_changed; 6928480093f4SDimitry Andric } 6929480093f4SDimitry Andric } else if (m_sc.symbol) { 6930480093f4SDimitry Andric if (m_disassembly_scope != m_sc.symbol) { 6931480093f4SDimitry Andric m_disassembly_scope = m_sc.symbol; 6932480093f4SDimitry Andric m_disassembly_sp = m_sc.symbol->GetInstructions( 6933480093f4SDimitry Andric exe_ctx, nullptr, prefer_file_cache); 6934480093f4SDimitry Andric if (m_disassembly_sp) { 6935480093f4SDimitry Andric set_selected_line_to_pc = true; 6936480093f4SDimitry Andric m_disassembly_range.GetBaseAddress() = 6937480093f4SDimitry Andric m_sc.symbol->GetAddress(); 6938480093f4SDimitry Andric m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize()); 6939480093f4SDimitry Andric } else { 6940480093f4SDimitry Andric m_disassembly_range.Clear(); 6941480093f4SDimitry Andric } 6942480093f4SDimitry Andric } else { 6943480093f4SDimitry Andric set_selected_line_to_pc = context_changed; 6944480093f4SDimitry Andric } 6945480093f4SDimitry Andric } 6946480093f4SDimitry Andric } 6947480093f4SDimitry Andric } else { 6948480093f4SDimitry Andric m_pc_line = UINT32_MAX; 6949480093f4SDimitry Andric } 6950480093f4SDimitry Andric } 6951480093f4SDimitry Andric 6952480093f4SDimitry Andric const int window_width = window.GetWidth(); 6953480093f4SDimitry Andric window.Erase(); 6954480093f4SDimitry Andric window.DrawTitleBox("Sources"); 6955480093f4SDimitry Andric if (!m_title.GetString().empty()) { 6956480093f4SDimitry Andric window.AttributeOn(A_REVERSE); 6957480093f4SDimitry Andric window.MoveCursor(1, 1); 6958480093f4SDimitry Andric window.PutChar(' '); 6959e8d8bef9SDimitry Andric window.PutCStringTruncated(1, m_title.GetString().str().c_str()); 6960480093f4SDimitry Andric int x = window.GetCursorX(); 6961480093f4SDimitry Andric if (x < window_width - 1) { 6962480093f4SDimitry Andric window.Printf("%*s", window_width - x - 1, ""); 6963480093f4SDimitry Andric } 6964480093f4SDimitry Andric window.AttributeOff(A_REVERSE); 6965480093f4SDimitry Andric } 6966480093f4SDimitry Andric 6967480093f4SDimitry Andric Target *target = exe_ctx.GetTargetPtr(); 6968480093f4SDimitry Andric const size_t num_source_lines = GetNumSourceLines(); 6969480093f4SDimitry Andric if (num_source_lines > 0) { 6970480093f4SDimitry Andric // Display source 6971480093f4SDimitry Andric BreakpointLines bp_lines; 6972480093f4SDimitry Andric if (target) { 6973480093f4SDimitry Andric BreakpointList &bp_list = target->GetBreakpointList(); 6974480093f4SDimitry Andric const size_t num_bps = bp_list.GetSize(); 6975480093f4SDimitry Andric for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) { 6976480093f4SDimitry Andric BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx); 6977480093f4SDimitry Andric const size_t num_bps_locs = bp_sp->GetNumLocations(); 6978480093f4SDimitry Andric for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) { 6979480093f4SDimitry Andric BreakpointLocationSP bp_loc_sp = 6980480093f4SDimitry Andric bp_sp->GetLocationAtIndex(bp_loc_idx); 6981480093f4SDimitry Andric LineEntry bp_loc_line_entry; 6982480093f4SDimitry Andric if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry( 6983480093f4SDimitry Andric bp_loc_line_entry)) { 6984480093f4SDimitry Andric if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file) { 6985480093f4SDimitry Andric bp_lines.insert(bp_loc_line_entry.line); 6986480093f4SDimitry Andric } 6987480093f4SDimitry Andric } 6988480093f4SDimitry Andric } 6989480093f4SDimitry Andric } 6990480093f4SDimitry Andric } 6991480093f4SDimitry Andric 6992480093f4SDimitry Andric for (size_t i = 0; i < num_visible_lines; ++i) { 6993480093f4SDimitry Andric const uint32_t curr_line = m_first_visible_line + i; 6994480093f4SDimitry Andric if (curr_line < num_source_lines) { 6995480093f4SDimitry Andric const int line_y = m_min_y + i; 6996480093f4SDimitry Andric window.MoveCursor(1, line_y); 6997480093f4SDimitry Andric const bool is_pc_line = curr_line == m_pc_line; 6998480093f4SDimitry Andric const bool line_is_selected = m_selected_line == curr_line; 699981ad6265SDimitry Andric // Highlight the line as the PC line first (done by passing 700081ad6265SDimitry Andric // argument to OutputColoredStringTruncated()), then if the selected 700181ad6265SDimitry Andric // line isn't the same as the PC line, highlight it differently. 7002480093f4SDimitry Andric attr_t highlight_attr = 0; 7003480093f4SDimitry Andric attr_t bp_attr = 0; 700481ad6265SDimitry Andric if (line_is_selected && !is_pc_line) 700581ad6265SDimitry Andric highlight_attr = A_REVERSE; 7006480093f4SDimitry Andric 7007480093f4SDimitry Andric if (bp_lines.find(curr_line + 1) != bp_lines.end()) 7008e8d8bef9SDimitry Andric bp_attr = COLOR_PAIR(BlackOnWhite); 7009480093f4SDimitry Andric 7010480093f4SDimitry Andric if (bp_attr) 7011480093f4SDimitry Andric window.AttributeOn(bp_attr); 7012480093f4SDimitry Andric 7013480093f4SDimitry Andric window.Printf(" %*u ", m_line_width, curr_line + 1); 7014480093f4SDimitry Andric 7015480093f4SDimitry Andric if (bp_attr) 7016480093f4SDimitry Andric window.AttributeOff(bp_attr); 7017480093f4SDimitry Andric 7018480093f4SDimitry Andric window.PutChar(ACS_VLINE); 7019480093f4SDimitry Andric // Mark the line with the PC with a diamond 7020480093f4SDimitry Andric if (is_pc_line) 7021480093f4SDimitry Andric window.PutChar(ACS_DIAMOND); 7022480093f4SDimitry Andric else 7023480093f4SDimitry Andric window.PutChar(' '); 7024480093f4SDimitry Andric 7025480093f4SDimitry Andric if (highlight_attr) 7026480093f4SDimitry Andric window.AttributeOn(highlight_attr); 7027e8d8bef9SDimitry Andric 7028e8d8bef9SDimitry Andric StreamString lineStream; 702981ad6265SDimitry Andric 7030bdd1243dSDimitry Andric std::optional<size_t> column; 703181ad6265SDimitry Andric if (is_pc_line && m_sc.line_entry.IsValid() && m_sc.line_entry.column) 703281ad6265SDimitry Andric column = m_sc.line_entry.column - 1; 703381ad6265SDimitry Andric m_file_sp->DisplaySourceLines(curr_line + 1, column, 0, 0, 703481ad6265SDimitry Andric &lineStream); 7035e8d8bef9SDimitry Andric StringRef line = lineStream.GetString(); 7036e8d8bef9SDimitry Andric if (line.endswith("\n")) 7037e8d8bef9SDimitry Andric line = line.drop_back(); 7038e8d8bef9SDimitry Andric bool wasWritten = window.OutputColoredStringTruncated( 703981ad6265SDimitry Andric 1, line, m_first_visible_column, is_pc_line); 704081ad6265SDimitry Andric if (!wasWritten && (line_is_selected || is_pc_line)) { 704181ad6265SDimitry Andric // Draw an empty space to show the selected/PC line if empty, 7042e8d8bef9SDimitry Andric // or draw '<' if nothing is visible because of scrolling too much 7043e8d8bef9SDimitry Andric // to the right. 7044e8d8bef9SDimitry Andric window.PutCStringTruncated( 7045e8d8bef9SDimitry Andric 1, line.empty() && m_first_visible_column == 0 ? " " : "<"); 7046e8d8bef9SDimitry Andric } 7047480093f4SDimitry Andric 7048480093f4SDimitry Andric if (is_pc_line && frame_sp && 7049480093f4SDimitry Andric frame_sp->GetConcreteFrameIndex() == 0) { 7050480093f4SDimitry Andric StopInfoSP stop_info_sp; 7051480093f4SDimitry Andric if (thread) 7052480093f4SDimitry Andric stop_info_sp = thread->GetStopInfo(); 7053480093f4SDimitry Andric if (stop_info_sp) { 7054480093f4SDimitry Andric const char *stop_description = stop_info_sp->GetDescription(); 7055480093f4SDimitry Andric if (stop_description && stop_description[0]) { 7056480093f4SDimitry Andric size_t stop_description_len = strlen(stop_description); 7057480093f4SDimitry Andric int desc_x = window_width - stop_description_len - 16; 7058e8d8bef9SDimitry Andric if (desc_x - window.GetCursorX() > 0) 7059480093f4SDimitry Andric window.Printf("%*s", desc_x - window.GetCursorX(), ""); 7060e8d8bef9SDimitry Andric window.MoveCursor(window_width - stop_description_len - 16, 7061e8d8bef9SDimitry Andric line_y); 7062e8d8bef9SDimitry Andric const attr_t stop_reason_attr = COLOR_PAIR(WhiteOnBlue); 7063e8d8bef9SDimitry Andric window.AttributeOn(stop_reason_attr); 7064e8d8bef9SDimitry Andric window.PrintfTruncated(1, " <<< Thread %u: %s ", 7065e8d8bef9SDimitry Andric thread->GetIndexID(), stop_description); 7066e8d8bef9SDimitry Andric window.AttributeOff(stop_reason_attr); 7067480093f4SDimitry Andric } 7068480093f4SDimitry Andric } else { 7069480093f4SDimitry Andric window.Printf("%*s", window_width - window.GetCursorX() - 1, ""); 7070480093f4SDimitry Andric } 7071480093f4SDimitry Andric } 7072480093f4SDimitry Andric if (highlight_attr) 7073480093f4SDimitry Andric window.AttributeOff(highlight_attr); 7074480093f4SDimitry Andric } else { 7075480093f4SDimitry Andric break; 7076480093f4SDimitry Andric } 7077480093f4SDimitry Andric } 7078480093f4SDimitry Andric } else { 7079480093f4SDimitry Andric size_t num_disassembly_lines = GetNumDisassemblyLines(); 7080480093f4SDimitry Andric if (num_disassembly_lines > 0) { 7081480093f4SDimitry Andric // Display disassembly 7082480093f4SDimitry Andric BreakpointAddrs bp_file_addrs; 7083480093f4SDimitry Andric Target *target = exe_ctx.GetTargetPtr(); 7084480093f4SDimitry Andric if (target) { 7085480093f4SDimitry Andric BreakpointList &bp_list = target->GetBreakpointList(); 7086480093f4SDimitry Andric const size_t num_bps = bp_list.GetSize(); 7087480093f4SDimitry Andric for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) { 7088480093f4SDimitry Andric BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx); 7089480093f4SDimitry Andric const size_t num_bps_locs = bp_sp->GetNumLocations(); 7090480093f4SDimitry Andric for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; 7091480093f4SDimitry Andric ++bp_loc_idx) { 7092480093f4SDimitry Andric BreakpointLocationSP bp_loc_sp = 7093480093f4SDimitry Andric bp_sp->GetLocationAtIndex(bp_loc_idx); 7094480093f4SDimitry Andric LineEntry bp_loc_line_entry; 7095480093f4SDimitry Andric const lldb::addr_t file_addr = 7096480093f4SDimitry Andric bp_loc_sp->GetAddress().GetFileAddress(); 7097480093f4SDimitry Andric if (file_addr != LLDB_INVALID_ADDRESS) { 7098480093f4SDimitry Andric if (m_disassembly_range.ContainsFileAddress(file_addr)) 7099480093f4SDimitry Andric bp_file_addrs.insert(file_addr); 7100480093f4SDimitry Andric } 7101480093f4SDimitry Andric } 7102480093f4SDimitry Andric } 7103480093f4SDimitry Andric } 7104480093f4SDimitry Andric 7105480093f4SDimitry Andric const attr_t selected_highlight_attr = A_REVERSE; 7106e8d8bef9SDimitry Andric const attr_t pc_highlight_attr = COLOR_PAIR(WhiteOnBlue); 7107480093f4SDimitry Andric 7108480093f4SDimitry Andric StreamString strm; 7109480093f4SDimitry Andric 7110480093f4SDimitry Andric InstructionList &insts = m_disassembly_sp->GetInstructionList(); 7111480093f4SDimitry Andric Address pc_address; 7112480093f4SDimitry Andric 7113480093f4SDimitry Andric if (frame_sp) 7114480093f4SDimitry Andric pc_address = frame_sp->GetFrameCodeAddress(); 7115480093f4SDimitry Andric const uint32_t pc_idx = 7116480093f4SDimitry Andric pc_address.IsValid() 7117480093f4SDimitry Andric ? insts.GetIndexOfInstructionAtAddress(pc_address) 7118480093f4SDimitry Andric : UINT32_MAX; 7119480093f4SDimitry Andric if (set_selected_line_to_pc) { 7120480093f4SDimitry Andric m_selected_line = pc_idx; 7121480093f4SDimitry Andric } 7122480093f4SDimitry Andric 7123480093f4SDimitry Andric const uint32_t non_visible_pc_offset = (num_visible_lines / 5); 7124480093f4SDimitry Andric if (static_cast<size_t>(m_first_visible_line) >= num_disassembly_lines) 7125480093f4SDimitry Andric m_first_visible_line = 0; 7126480093f4SDimitry Andric 7127480093f4SDimitry Andric if (pc_idx < num_disassembly_lines) { 7128480093f4SDimitry Andric if (pc_idx < static_cast<uint32_t>(m_first_visible_line) || 7129480093f4SDimitry Andric pc_idx >= m_first_visible_line + num_visible_lines) 7130480093f4SDimitry Andric m_first_visible_line = pc_idx - non_visible_pc_offset; 7131480093f4SDimitry Andric } 7132480093f4SDimitry Andric 7133480093f4SDimitry Andric for (size_t i = 0; i < num_visible_lines; ++i) { 7134480093f4SDimitry Andric const uint32_t inst_idx = m_first_visible_line + i; 7135480093f4SDimitry Andric Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get(); 7136480093f4SDimitry Andric if (!inst) 7137480093f4SDimitry Andric break; 7138480093f4SDimitry Andric 7139480093f4SDimitry Andric const int line_y = m_min_y + i; 7140480093f4SDimitry Andric window.MoveCursor(1, line_y); 7141480093f4SDimitry Andric const bool is_pc_line = frame_sp && inst_idx == pc_idx; 7142480093f4SDimitry Andric const bool line_is_selected = m_selected_line == inst_idx; 7143349cc55cSDimitry Andric // Highlight the line as the PC line first, then if the selected 7144349cc55cSDimitry Andric // line isn't the same as the PC line, highlight it differently 7145480093f4SDimitry Andric attr_t highlight_attr = 0; 7146480093f4SDimitry Andric attr_t bp_attr = 0; 7147480093f4SDimitry Andric if (is_pc_line) 7148480093f4SDimitry Andric highlight_attr = pc_highlight_attr; 7149480093f4SDimitry Andric else if (line_is_selected) 7150480093f4SDimitry Andric highlight_attr = selected_highlight_attr; 7151480093f4SDimitry Andric 7152480093f4SDimitry Andric if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) != 7153480093f4SDimitry Andric bp_file_addrs.end()) 7154e8d8bef9SDimitry Andric bp_attr = COLOR_PAIR(BlackOnWhite); 7155480093f4SDimitry Andric 7156480093f4SDimitry Andric if (bp_attr) 7157480093f4SDimitry Andric window.AttributeOn(bp_attr); 7158480093f4SDimitry Andric 7159480093f4SDimitry Andric window.Printf(" 0x%16.16llx ", 7160480093f4SDimitry Andric static_cast<unsigned long long>( 7161480093f4SDimitry Andric inst->GetAddress().GetLoadAddress(target))); 7162480093f4SDimitry Andric 7163480093f4SDimitry Andric if (bp_attr) 7164480093f4SDimitry Andric window.AttributeOff(bp_attr); 7165480093f4SDimitry Andric 7166480093f4SDimitry Andric window.PutChar(ACS_VLINE); 7167480093f4SDimitry Andric // Mark the line with the PC with a diamond 7168480093f4SDimitry Andric if (is_pc_line) 7169480093f4SDimitry Andric window.PutChar(ACS_DIAMOND); 7170480093f4SDimitry Andric else 7171480093f4SDimitry Andric window.PutChar(' '); 7172480093f4SDimitry Andric 7173480093f4SDimitry Andric if (highlight_attr) 7174480093f4SDimitry Andric window.AttributeOn(highlight_attr); 7175480093f4SDimitry Andric 7176480093f4SDimitry Andric const char *mnemonic = inst->GetMnemonic(&exe_ctx); 7177480093f4SDimitry Andric const char *operands = inst->GetOperands(&exe_ctx); 7178480093f4SDimitry Andric const char *comment = inst->GetComment(&exe_ctx); 7179480093f4SDimitry Andric 7180480093f4SDimitry Andric if (mnemonic != nullptr && mnemonic[0] == '\0') 7181480093f4SDimitry Andric mnemonic = nullptr; 7182480093f4SDimitry Andric if (operands != nullptr && operands[0] == '\0') 7183480093f4SDimitry Andric operands = nullptr; 7184480093f4SDimitry Andric if (comment != nullptr && comment[0] == '\0') 7185480093f4SDimitry Andric comment = nullptr; 7186480093f4SDimitry Andric 7187480093f4SDimitry Andric strm.Clear(); 7188480093f4SDimitry Andric 7189480093f4SDimitry Andric if (mnemonic != nullptr && operands != nullptr && comment != nullptr) 7190480093f4SDimitry Andric strm.Printf("%-8s %-25s ; %s", mnemonic, operands, comment); 7191480093f4SDimitry Andric else if (mnemonic != nullptr && operands != nullptr) 7192480093f4SDimitry Andric strm.Printf("%-8s %s", mnemonic, operands); 7193480093f4SDimitry Andric else if (mnemonic != nullptr) 7194480093f4SDimitry Andric strm.Printf("%s", mnemonic); 7195480093f4SDimitry Andric 7196480093f4SDimitry Andric int right_pad = 1; 7197e8d8bef9SDimitry Andric window.PutCStringTruncated( 7198e8d8bef9SDimitry Andric right_pad, 7199e8d8bef9SDimitry Andric strm.GetString().substr(m_first_visible_column).data()); 7200480093f4SDimitry Andric 7201480093f4SDimitry Andric if (is_pc_line && frame_sp && 7202480093f4SDimitry Andric frame_sp->GetConcreteFrameIndex() == 0) { 7203480093f4SDimitry Andric StopInfoSP stop_info_sp; 7204480093f4SDimitry Andric if (thread) 7205480093f4SDimitry Andric stop_info_sp = thread->GetStopInfo(); 7206480093f4SDimitry Andric if (stop_info_sp) { 7207480093f4SDimitry Andric const char *stop_description = stop_info_sp->GetDescription(); 7208480093f4SDimitry Andric if (stop_description && stop_description[0]) { 7209480093f4SDimitry Andric size_t stop_description_len = strlen(stop_description); 7210480093f4SDimitry Andric int desc_x = window_width - stop_description_len - 16; 7211e8d8bef9SDimitry Andric if (desc_x - window.GetCursorX() > 0) 7212480093f4SDimitry Andric window.Printf("%*s", desc_x - window.GetCursorX(), ""); 7213e8d8bef9SDimitry Andric window.MoveCursor(window_width - stop_description_len - 15, 7214e8d8bef9SDimitry Andric line_y); 7215bdd1243dSDimitry Andric if (thread) 7216e8d8bef9SDimitry Andric window.PrintfTruncated(1, "<<< Thread %u: %s ", 7217bdd1243dSDimitry Andric thread->GetIndexID(), 7218bdd1243dSDimitry Andric stop_description); 7219480093f4SDimitry Andric } 7220480093f4SDimitry Andric } else { 7221480093f4SDimitry Andric window.Printf("%*s", window_width - window.GetCursorX() - 1, ""); 7222480093f4SDimitry Andric } 7223480093f4SDimitry Andric } 7224480093f4SDimitry Andric if (highlight_attr) 7225480093f4SDimitry Andric window.AttributeOff(highlight_attr); 7226480093f4SDimitry Andric } 7227480093f4SDimitry Andric } 7228480093f4SDimitry Andric } 7229480093f4SDimitry Andric return true; // Drawing handled 7230480093f4SDimitry Andric } 7231480093f4SDimitry Andric 7232480093f4SDimitry Andric size_t GetNumLines() { 7233480093f4SDimitry Andric size_t num_lines = GetNumSourceLines(); 7234480093f4SDimitry Andric if (num_lines == 0) 7235480093f4SDimitry Andric num_lines = GetNumDisassemblyLines(); 7236480093f4SDimitry Andric return num_lines; 7237480093f4SDimitry Andric } 7238480093f4SDimitry Andric 7239480093f4SDimitry Andric size_t GetNumSourceLines() const { 7240480093f4SDimitry Andric if (m_file_sp) 7241480093f4SDimitry Andric return m_file_sp->GetNumLines(); 7242480093f4SDimitry Andric return 0; 7243480093f4SDimitry Andric } 7244480093f4SDimitry Andric 7245480093f4SDimitry Andric size_t GetNumDisassemblyLines() const { 7246480093f4SDimitry Andric if (m_disassembly_sp) 7247480093f4SDimitry Andric return m_disassembly_sp->GetInstructionList().GetSize(); 7248480093f4SDimitry Andric return 0; 7249480093f4SDimitry Andric } 7250480093f4SDimitry Andric 7251480093f4SDimitry Andric HandleCharResult WindowDelegateHandleChar(Window &window, int c) override { 7252480093f4SDimitry Andric const uint32_t num_visible_lines = NumVisibleLines(); 7253480093f4SDimitry Andric const size_t num_lines = GetNumLines(); 7254480093f4SDimitry Andric 7255480093f4SDimitry Andric switch (c) { 7256480093f4SDimitry Andric case ',': 7257480093f4SDimitry Andric case KEY_PPAGE: 7258480093f4SDimitry Andric // Page up key 7259480093f4SDimitry Andric if (static_cast<uint32_t>(m_first_visible_line) > num_visible_lines) 7260480093f4SDimitry Andric m_first_visible_line -= num_visible_lines; 7261480093f4SDimitry Andric else 7262480093f4SDimitry Andric m_first_visible_line = 0; 7263480093f4SDimitry Andric m_selected_line = m_first_visible_line; 7264480093f4SDimitry Andric return eKeyHandled; 7265480093f4SDimitry Andric 7266480093f4SDimitry Andric case '.': 7267480093f4SDimitry Andric case KEY_NPAGE: 7268480093f4SDimitry Andric // Page down key 7269480093f4SDimitry Andric { 7270480093f4SDimitry Andric if (m_first_visible_line + num_visible_lines < num_lines) 7271480093f4SDimitry Andric m_first_visible_line += num_visible_lines; 7272480093f4SDimitry Andric else if (num_lines < num_visible_lines) 7273480093f4SDimitry Andric m_first_visible_line = 0; 7274480093f4SDimitry Andric else 7275480093f4SDimitry Andric m_first_visible_line = num_lines - num_visible_lines; 7276480093f4SDimitry Andric m_selected_line = m_first_visible_line; 7277480093f4SDimitry Andric } 7278480093f4SDimitry Andric return eKeyHandled; 7279480093f4SDimitry Andric 7280480093f4SDimitry Andric case KEY_UP: 7281480093f4SDimitry Andric if (m_selected_line > 0) { 7282480093f4SDimitry Andric m_selected_line--; 7283480093f4SDimitry Andric if (static_cast<size_t>(m_first_visible_line) > m_selected_line) 7284480093f4SDimitry Andric m_first_visible_line = m_selected_line; 7285480093f4SDimitry Andric } 7286480093f4SDimitry Andric return eKeyHandled; 7287480093f4SDimitry Andric 7288480093f4SDimitry Andric case KEY_DOWN: 7289480093f4SDimitry Andric if (m_selected_line + 1 < num_lines) { 7290480093f4SDimitry Andric m_selected_line++; 7291480093f4SDimitry Andric if (m_first_visible_line + num_visible_lines < m_selected_line) 7292480093f4SDimitry Andric m_first_visible_line++; 7293480093f4SDimitry Andric } 7294480093f4SDimitry Andric return eKeyHandled; 7295480093f4SDimitry Andric 7296e8d8bef9SDimitry Andric case KEY_LEFT: 7297e8d8bef9SDimitry Andric if (m_first_visible_column > 0) 7298e8d8bef9SDimitry Andric --m_first_visible_column; 7299e8d8bef9SDimitry Andric return eKeyHandled; 7300e8d8bef9SDimitry Andric 7301e8d8bef9SDimitry Andric case KEY_RIGHT: 7302e8d8bef9SDimitry Andric ++m_first_visible_column; 7303e8d8bef9SDimitry Andric return eKeyHandled; 7304e8d8bef9SDimitry Andric 7305480093f4SDimitry Andric case '\r': 7306480093f4SDimitry Andric case '\n': 7307480093f4SDimitry Andric case KEY_ENTER: 7308480093f4SDimitry Andric // Set a breakpoint and run to the line using a one shot breakpoint 7309480093f4SDimitry Andric if (GetNumSourceLines() > 0) { 7310480093f4SDimitry Andric ExecutionContext exe_ctx = 7311480093f4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext(); 7312480093f4SDimitry Andric if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive()) { 7313480093f4SDimitry Andric BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint( 7314480093f4SDimitry Andric nullptr, // Don't limit the breakpoint to certain modules 7315480093f4SDimitry Andric m_file_sp->GetFileSpec(), // Source file 7316480093f4SDimitry Andric m_selected_line + 7317480093f4SDimitry Andric 1, // Source line number (m_selected_line is zero based) 7318480093f4SDimitry Andric 0, // Unspecified column. 7319480093f4SDimitry Andric 0, // No offset 7320480093f4SDimitry Andric eLazyBoolCalculate, // Check inlines using global setting 7321480093f4SDimitry Andric eLazyBoolCalculate, // Skip prologue using global setting, 7322480093f4SDimitry Andric false, // internal 7323480093f4SDimitry Andric false, // request_hardware 7324480093f4SDimitry Andric eLazyBoolCalculate); // move_to_nearest_code 7325480093f4SDimitry Andric // Make breakpoint one shot 7326fe6060f1SDimitry Andric bp_sp->GetOptions().SetOneShot(true); 7327480093f4SDimitry Andric exe_ctx.GetProcessRef().Resume(); 7328480093f4SDimitry Andric } 7329480093f4SDimitry Andric } else if (m_selected_line < GetNumDisassemblyLines()) { 7330480093f4SDimitry Andric const Instruction *inst = m_disassembly_sp->GetInstructionList() 7331480093f4SDimitry Andric .GetInstructionAtIndex(m_selected_line) 7332480093f4SDimitry Andric .get(); 7333480093f4SDimitry Andric ExecutionContext exe_ctx = 7334480093f4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext(); 7335480093f4SDimitry Andric if (exe_ctx.HasTargetScope()) { 7336480093f4SDimitry Andric Address addr = inst->GetAddress(); 7337480093f4SDimitry Andric BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint( 7338480093f4SDimitry Andric addr, // lldb_private::Address 7339480093f4SDimitry Andric false, // internal 7340480093f4SDimitry Andric false); // request_hardware 7341480093f4SDimitry Andric // Make breakpoint one shot 7342fe6060f1SDimitry Andric bp_sp->GetOptions().SetOneShot(true); 7343480093f4SDimitry Andric exe_ctx.GetProcessRef().Resume(); 7344480093f4SDimitry Andric } 7345480093f4SDimitry Andric } 7346480093f4SDimitry Andric return eKeyHandled; 7347480093f4SDimitry Andric 7348480093f4SDimitry Andric case 'b': // 'b' == toggle breakpoint on currently selected line 7349e8d8bef9SDimitry Andric ToggleBreakpointOnSelectedLine(); 7350480093f4SDimitry Andric return eKeyHandled; 7351480093f4SDimitry Andric 7352480093f4SDimitry Andric case 'D': // 'D' == detach and keep stopped 7353480093f4SDimitry Andric { 7354480093f4SDimitry Andric ExecutionContext exe_ctx = 7355480093f4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext(); 7356480093f4SDimitry Andric if (exe_ctx.HasProcessScope()) 7357e8d8bef9SDimitry Andric exe_ctx.GetProcessRef().Detach(true); 7358480093f4SDimitry Andric } 7359480093f4SDimitry Andric return eKeyHandled; 7360480093f4SDimitry Andric 7361480093f4SDimitry Andric case 'c': 7362480093f4SDimitry Andric // 'c' == continue 7363480093f4SDimitry Andric { 7364480093f4SDimitry Andric ExecutionContext exe_ctx = 7365480093f4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext(); 7366480093f4SDimitry Andric if (exe_ctx.HasProcessScope()) 7367480093f4SDimitry Andric exe_ctx.GetProcessRef().Resume(); 7368480093f4SDimitry Andric } 7369480093f4SDimitry Andric return eKeyHandled; 7370480093f4SDimitry Andric 7371e8d8bef9SDimitry Andric case 'f': 7372e8d8bef9SDimitry Andric // 'f' == step out (finish) 7373480093f4SDimitry Andric { 7374480093f4SDimitry Andric ExecutionContext exe_ctx = 7375480093f4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext(); 7376480093f4SDimitry Andric if (exe_ctx.HasThreadScope() && 7377480093f4SDimitry Andric StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) { 737881ad6265SDimitry Andric Thread *thread = exe_ctx.GetThreadPtr(); 7379*06c3fb27SDimitry Andric uint32_t frame_idx = 7380*06c3fb27SDimitry Andric thread->GetSelectedFrameIndex(SelectMostRelevantFrame); 738181ad6265SDimitry Andric exe_ctx.GetThreadRef().StepOut(frame_idx); 7382480093f4SDimitry Andric } 7383480093f4SDimitry Andric } 7384480093f4SDimitry Andric return eKeyHandled; 7385480093f4SDimitry Andric 7386480093f4SDimitry Andric case 'n': // 'n' == step over 7387480093f4SDimitry Andric case 'N': // 'N' == step over instruction 7388480093f4SDimitry Andric { 7389480093f4SDimitry Andric ExecutionContext exe_ctx = 7390480093f4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext(); 7391480093f4SDimitry Andric if (exe_ctx.HasThreadScope() && 7392480093f4SDimitry Andric StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) { 7393480093f4SDimitry Andric bool source_step = (c == 'n'); 7394480093f4SDimitry Andric exe_ctx.GetThreadRef().StepOver(source_step); 7395480093f4SDimitry Andric } 7396480093f4SDimitry Andric } 7397480093f4SDimitry Andric return eKeyHandled; 7398480093f4SDimitry Andric 7399480093f4SDimitry Andric case 's': // 's' == step into 7400480093f4SDimitry Andric case 'S': // 'S' == step into instruction 7401480093f4SDimitry Andric { 7402480093f4SDimitry Andric ExecutionContext exe_ctx = 7403480093f4SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext(); 7404480093f4SDimitry Andric if (exe_ctx.HasThreadScope() && 7405480093f4SDimitry Andric StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) { 7406480093f4SDimitry Andric bool source_step = (c == 's'); 7407480093f4SDimitry Andric exe_ctx.GetThreadRef().StepIn(source_step); 7408480093f4SDimitry Andric } 7409480093f4SDimitry Andric } 7410480093f4SDimitry Andric return eKeyHandled; 7411480093f4SDimitry Andric 7412e8d8bef9SDimitry Andric case 'u': // 'u' == frame up 7413e8d8bef9SDimitry Andric case 'd': // 'd' == frame down 7414e8d8bef9SDimitry Andric { 7415e8d8bef9SDimitry Andric ExecutionContext exe_ctx = 7416e8d8bef9SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext(); 7417e8d8bef9SDimitry Andric if (exe_ctx.HasThreadScope()) { 7418e8d8bef9SDimitry Andric Thread *thread = exe_ctx.GetThreadPtr(); 7419*06c3fb27SDimitry Andric uint32_t frame_idx = 7420*06c3fb27SDimitry Andric thread->GetSelectedFrameIndex(SelectMostRelevantFrame); 7421e8d8bef9SDimitry Andric if (frame_idx == UINT32_MAX) 7422e8d8bef9SDimitry Andric frame_idx = 0; 7423e8d8bef9SDimitry Andric if (c == 'u' && frame_idx + 1 < thread->GetStackFrameCount()) 7424e8d8bef9SDimitry Andric ++frame_idx; 7425e8d8bef9SDimitry Andric else if (c == 'd' && frame_idx > 0) 7426e8d8bef9SDimitry Andric --frame_idx; 7427e8d8bef9SDimitry Andric if (thread->SetSelectedFrameByIndex(frame_idx, true)) 7428*06c3fb27SDimitry Andric exe_ctx.SetFrameSP(thread->GetSelectedFrame(SelectMostRelevantFrame)); 7429e8d8bef9SDimitry Andric } 7430e8d8bef9SDimitry Andric } 7431e8d8bef9SDimitry Andric return eKeyHandled; 7432e8d8bef9SDimitry Andric 7433480093f4SDimitry Andric case 'h': 7434480093f4SDimitry Andric window.CreateHelpSubwindow(); 7435480093f4SDimitry Andric return eKeyHandled; 7436480093f4SDimitry Andric 7437480093f4SDimitry Andric default: 7438480093f4SDimitry Andric break; 7439480093f4SDimitry Andric } 7440480093f4SDimitry Andric return eKeyNotHandled; 7441480093f4SDimitry Andric } 7442480093f4SDimitry Andric 7443e8d8bef9SDimitry Andric void ToggleBreakpointOnSelectedLine() { 7444e8d8bef9SDimitry Andric ExecutionContext exe_ctx = 7445e8d8bef9SDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext(); 7446e8d8bef9SDimitry Andric if (!exe_ctx.HasTargetScope()) 7447e8d8bef9SDimitry Andric return; 7448e8d8bef9SDimitry Andric if (GetNumSourceLines() > 0) { 7449e8d8bef9SDimitry Andric // Source file breakpoint. 7450e8d8bef9SDimitry Andric BreakpointList &bp_list = exe_ctx.GetTargetRef().GetBreakpointList(); 7451e8d8bef9SDimitry Andric const size_t num_bps = bp_list.GetSize(); 7452e8d8bef9SDimitry Andric for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) { 7453e8d8bef9SDimitry Andric BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx); 7454e8d8bef9SDimitry Andric const size_t num_bps_locs = bp_sp->GetNumLocations(); 7455e8d8bef9SDimitry Andric for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) { 7456e8d8bef9SDimitry Andric BreakpointLocationSP bp_loc_sp = 7457e8d8bef9SDimitry Andric bp_sp->GetLocationAtIndex(bp_loc_idx); 7458e8d8bef9SDimitry Andric LineEntry bp_loc_line_entry; 7459e8d8bef9SDimitry Andric if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry( 7460e8d8bef9SDimitry Andric bp_loc_line_entry)) { 7461e8d8bef9SDimitry Andric if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file && 7462e8d8bef9SDimitry Andric m_selected_line + 1 == bp_loc_line_entry.line) { 7463e8d8bef9SDimitry Andric bool removed = 7464e8d8bef9SDimitry Andric exe_ctx.GetTargetRef().RemoveBreakpointByID(bp_sp->GetID()); 7465e8d8bef9SDimitry Andric assert(removed); 7466e8d8bef9SDimitry Andric UNUSED_IF_ASSERT_DISABLED(removed); 7467e8d8bef9SDimitry Andric return; // Existing breakpoint removed. 7468e8d8bef9SDimitry Andric } 7469e8d8bef9SDimitry Andric } 7470e8d8bef9SDimitry Andric } 7471e8d8bef9SDimitry Andric } 7472e8d8bef9SDimitry Andric // No breakpoint found on the location, add it. 7473e8d8bef9SDimitry Andric BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint( 7474e8d8bef9SDimitry Andric nullptr, // Don't limit the breakpoint to certain modules 7475e8d8bef9SDimitry Andric m_file_sp->GetFileSpec(), // Source file 7476e8d8bef9SDimitry Andric m_selected_line + 7477e8d8bef9SDimitry Andric 1, // Source line number (m_selected_line is zero based) 7478e8d8bef9SDimitry Andric 0, // No column specified. 7479e8d8bef9SDimitry Andric 0, // No offset 7480e8d8bef9SDimitry Andric eLazyBoolCalculate, // Check inlines using global setting 7481e8d8bef9SDimitry Andric eLazyBoolCalculate, // Skip prologue using global setting, 7482e8d8bef9SDimitry Andric false, // internal 7483e8d8bef9SDimitry Andric false, // request_hardware 7484e8d8bef9SDimitry Andric eLazyBoolCalculate); // move_to_nearest_code 7485e8d8bef9SDimitry Andric } else { 7486e8d8bef9SDimitry Andric // Disassembly breakpoint. 7487e8d8bef9SDimitry Andric assert(GetNumDisassemblyLines() > 0); 7488e8d8bef9SDimitry Andric assert(m_selected_line < GetNumDisassemblyLines()); 7489e8d8bef9SDimitry Andric const Instruction *inst = m_disassembly_sp->GetInstructionList() 7490e8d8bef9SDimitry Andric .GetInstructionAtIndex(m_selected_line) 7491e8d8bef9SDimitry Andric .get(); 7492e8d8bef9SDimitry Andric Address addr = inst->GetAddress(); 7493e8d8bef9SDimitry Andric // Try to find it. 7494e8d8bef9SDimitry Andric BreakpointList &bp_list = exe_ctx.GetTargetRef().GetBreakpointList(); 7495e8d8bef9SDimitry Andric const size_t num_bps = bp_list.GetSize(); 7496e8d8bef9SDimitry Andric for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) { 7497e8d8bef9SDimitry Andric BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx); 7498e8d8bef9SDimitry Andric const size_t num_bps_locs = bp_sp->GetNumLocations(); 7499e8d8bef9SDimitry Andric for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) { 7500e8d8bef9SDimitry Andric BreakpointLocationSP bp_loc_sp = 7501e8d8bef9SDimitry Andric bp_sp->GetLocationAtIndex(bp_loc_idx); 7502e8d8bef9SDimitry Andric LineEntry bp_loc_line_entry; 7503e8d8bef9SDimitry Andric const lldb::addr_t file_addr = 7504e8d8bef9SDimitry Andric bp_loc_sp->GetAddress().GetFileAddress(); 7505e8d8bef9SDimitry Andric if (file_addr == addr.GetFileAddress()) { 7506e8d8bef9SDimitry Andric bool removed = 7507e8d8bef9SDimitry Andric exe_ctx.GetTargetRef().RemoveBreakpointByID(bp_sp->GetID()); 7508e8d8bef9SDimitry Andric assert(removed); 7509e8d8bef9SDimitry Andric UNUSED_IF_ASSERT_DISABLED(removed); 7510e8d8bef9SDimitry Andric return; // Existing breakpoint removed. 7511e8d8bef9SDimitry Andric } 7512e8d8bef9SDimitry Andric } 7513e8d8bef9SDimitry Andric } 7514e8d8bef9SDimitry Andric // No breakpoint found on the address, add it. 7515e8d8bef9SDimitry Andric BreakpointSP bp_sp = 7516e8d8bef9SDimitry Andric exe_ctx.GetTargetRef().CreateBreakpoint(addr, // lldb_private::Address 7517e8d8bef9SDimitry Andric false, // internal 7518e8d8bef9SDimitry Andric false); // request_hardware 7519e8d8bef9SDimitry Andric } 7520e8d8bef9SDimitry Andric } 7521e8d8bef9SDimitry Andric 7522480093f4SDimitry Andric protected: 7523480093f4SDimitry Andric typedef std::set<uint32_t> BreakpointLines; 7524480093f4SDimitry Andric typedef std::set<lldb::addr_t> BreakpointAddrs; 7525480093f4SDimitry Andric 7526480093f4SDimitry Andric Debugger &m_debugger; 7527480093f4SDimitry Andric SymbolContext m_sc; 7528480093f4SDimitry Andric SourceManager::FileSP m_file_sp; 752981ad6265SDimitry Andric SymbolContextScope *m_disassembly_scope = nullptr; 7530480093f4SDimitry Andric lldb::DisassemblerSP m_disassembly_sp; 7531480093f4SDimitry Andric AddressRange m_disassembly_range; 7532480093f4SDimitry Andric StreamString m_title; 753381ad6265SDimitry Andric lldb::user_id_t m_tid = LLDB_INVALID_THREAD_ID; 753481ad6265SDimitry Andric int m_line_width = 4; 753581ad6265SDimitry Andric uint32_t m_selected_line = 0; // The selected line 753681ad6265SDimitry Andric uint32_t m_pc_line = 0; // The line with the PC 753781ad6265SDimitry Andric uint32_t m_stop_id = 0; 753881ad6265SDimitry Andric uint32_t m_frame_idx = UINT32_MAX; 753981ad6265SDimitry Andric int m_first_visible_line = 0; 754081ad6265SDimitry Andric int m_first_visible_column = 0; 754181ad6265SDimitry Andric int m_min_x = 0; 754281ad6265SDimitry Andric int m_min_y = 0; 754381ad6265SDimitry Andric int m_max_x = 0; 754481ad6265SDimitry Andric int m_max_y = 0; 7545480093f4SDimitry Andric }; 7546480093f4SDimitry Andric 7547480093f4SDimitry Andric DisplayOptions ValueObjectListDelegate::g_options = {true}; 7548480093f4SDimitry Andric 7549480093f4SDimitry Andric IOHandlerCursesGUI::IOHandlerCursesGUI(Debugger &debugger) 7550480093f4SDimitry Andric : IOHandler(debugger, IOHandler::Type::Curses) {} 7551480093f4SDimitry Andric 7552480093f4SDimitry Andric void IOHandlerCursesGUI::Activate() { 7553480093f4SDimitry Andric IOHandler::Activate(); 7554480093f4SDimitry Andric if (!m_app_ap) { 75555ffd83dbSDimitry Andric m_app_ap = std::make_unique<Application>(GetInputFILE(), GetOutputFILE()); 7556480093f4SDimitry Andric 7557480093f4SDimitry Andric // This is both a window and a menu delegate 7558480093f4SDimitry Andric std::shared_ptr<ApplicationDelegate> app_delegate_sp( 7559480093f4SDimitry Andric new ApplicationDelegate(*m_app_ap, m_debugger)); 7560480093f4SDimitry Andric 7561480093f4SDimitry Andric MenuDelegateSP app_menu_delegate_sp = 7562480093f4SDimitry Andric std::static_pointer_cast<MenuDelegate>(app_delegate_sp); 7563480093f4SDimitry Andric MenuSP lldb_menu_sp( 7564480093f4SDimitry Andric new Menu("LLDB", "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB)); 7565480093f4SDimitry Andric MenuSP exit_menuitem_sp( 7566480093f4SDimitry Andric new Menu("Exit", nullptr, 'x', ApplicationDelegate::eMenuID_LLDBExit)); 7567480093f4SDimitry Andric exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit); 7568480093f4SDimitry Andric lldb_menu_sp->AddSubmenu(MenuSP(new Menu( 7569480093f4SDimitry Andric "About LLDB", nullptr, 'a', ApplicationDelegate::eMenuID_LLDBAbout))); 7570480093f4SDimitry Andric lldb_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator))); 7571480093f4SDimitry Andric lldb_menu_sp->AddSubmenu(exit_menuitem_sp); 7572480093f4SDimitry Andric 7573480093f4SDimitry Andric MenuSP target_menu_sp(new Menu("Target", "F2", KEY_F(2), 7574480093f4SDimitry Andric ApplicationDelegate::eMenuID_Target)); 7575480093f4SDimitry Andric target_menu_sp->AddSubmenu(MenuSP(new Menu( 7576480093f4SDimitry Andric "Create", nullptr, 'c', ApplicationDelegate::eMenuID_TargetCreate))); 7577480093f4SDimitry Andric target_menu_sp->AddSubmenu(MenuSP(new Menu( 7578480093f4SDimitry Andric "Delete", nullptr, 'd', ApplicationDelegate::eMenuID_TargetDelete))); 7579480093f4SDimitry Andric 7580480093f4SDimitry Andric MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3), 7581480093f4SDimitry Andric ApplicationDelegate::eMenuID_Process)); 7582480093f4SDimitry Andric process_menu_sp->AddSubmenu(MenuSP(new Menu( 7583480093f4SDimitry Andric "Attach", nullptr, 'a', ApplicationDelegate::eMenuID_ProcessAttach))); 7584e8d8bef9SDimitry Andric process_menu_sp->AddSubmenu( 7585e8d8bef9SDimitry Andric MenuSP(new Menu("Detach and resume", nullptr, 'd', 7586e8d8bef9SDimitry Andric ApplicationDelegate::eMenuID_ProcessDetachResume))); 7587e8d8bef9SDimitry Andric process_menu_sp->AddSubmenu( 7588e8d8bef9SDimitry Andric MenuSP(new Menu("Detach suspended", nullptr, 's', 7589e8d8bef9SDimitry Andric ApplicationDelegate::eMenuID_ProcessDetachSuspended))); 7590480093f4SDimitry Andric process_menu_sp->AddSubmenu(MenuSP(new Menu( 7591480093f4SDimitry Andric "Launch", nullptr, 'l', ApplicationDelegate::eMenuID_ProcessLaunch))); 7592480093f4SDimitry Andric process_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator))); 7593480093f4SDimitry Andric process_menu_sp->AddSubmenu( 7594480093f4SDimitry Andric MenuSP(new Menu("Continue", nullptr, 'c', 7595480093f4SDimitry Andric ApplicationDelegate::eMenuID_ProcessContinue))); 7596480093f4SDimitry Andric process_menu_sp->AddSubmenu(MenuSP(new Menu( 7597480093f4SDimitry Andric "Halt", nullptr, 'h', ApplicationDelegate::eMenuID_ProcessHalt))); 7598480093f4SDimitry Andric process_menu_sp->AddSubmenu(MenuSP(new Menu( 7599480093f4SDimitry Andric "Kill", nullptr, 'k', ApplicationDelegate::eMenuID_ProcessKill))); 7600480093f4SDimitry Andric 7601480093f4SDimitry Andric MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4), 7602480093f4SDimitry Andric ApplicationDelegate::eMenuID_Thread)); 7603480093f4SDimitry Andric thread_menu_sp->AddSubmenu(MenuSP(new Menu( 7604480093f4SDimitry Andric "Step In", nullptr, 'i', ApplicationDelegate::eMenuID_ThreadStepIn))); 7605480093f4SDimitry Andric thread_menu_sp->AddSubmenu( 7606480093f4SDimitry Andric MenuSP(new Menu("Step Over", nullptr, 'v', 7607480093f4SDimitry Andric ApplicationDelegate::eMenuID_ThreadStepOver))); 7608480093f4SDimitry Andric thread_menu_sp->AddSubmenu(MenuSP(new Menu( 7609480093f4SDimitry Andric "Step Out", nullptr, 'o', ApplicationDelegate::eMenuID_ThreadStepOut))); 7610480093f4SDimitry Andric 7611480093f4SDimitry Andric MenuSP view_menu_sp( 7612480093f4SDimitry Andric new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View)); 7613480093f4SDimitry Andric view_menu_sp->AddSubmenu( 7614349cc55cSDimitry Andric MenuSP(new Menu("Backtrace", nullptr, 't', 7615480093f4SDimitry Andric ApplicationDelegate::eMenuID_ViewBacktrace))); 7616480093f4SDimitry Andric view_menu_sp->AddSubmenu( 7617480093f4SDimitry Andric MenuSP(new Menu("Registers", nullptr, 'r', 7618480093f4SDimitry Andric ApplicationDelegate::eMenuID_ViewRegisters))); 7619480093f4SDimitry Andric view_menu_sp->AddSubmenu(MenuSP(new Menu( 7620480093f4SDimitry Andric "Source", nullptr, 's', ApplicationDelegate::eMenuID_ViewSource))); 7621480093f4SDimitry Andric view_menu_sp->AddSubmenu( 7622480093f4SDimitry Andric MenuSP(new Menu("Variables", nullptr, 'v', 7623480093f4SDimitry Andric ApplicationDelegate::eMenuID_ViewVariables))); 7624349cc55cSDimitry Andric view_menu_sp->AddSubmenu( 7625349cc55cSDimitry Andric MenuSP(new Menu("Breakpoints", nullptr, 'b', 7626349cc55cSDimitry Andric ApplicationDelegate::eMenuID_ViewBreakpoints))); 7627480093f4SDimitry Andric 7628480093f4SDimitry Andric MenuSP help_menu_sp( 7629480093f4SDimitry Andric new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help)); 7630480093f4SDimitry Andric help_menu_sp->AddSubmenu(MenuSP(new Menu( 7631480093f4SDimitry Andric "GUI Help", nullptr, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp))); 7632480093f4SDimitry Andric 7633480093f4SDimitry Andric m_app_ap->Initialize(); 7634480093f4SDimitry Andric WindowSP &main_window_sp = m_app_ap->GetMainWindow(); 7635480093f4SDimitry Andric 7636480093f4SDimitry Andric MenuSP menubar_sp(new Menu(Menu::Type::Bar)); 7637480093f4SDimitry Andric menubar_sp->AddSubmenu(lldb_menu_sp); 7638480093f4SDimitry Andric menubar_sp->AddSubmenu(target_menu_sp); 7639480093f4SDimitry Andric menubar_sp->AddSubmenu(process_menu_sp); 7640480093f4SDimitry Andric menubar_sp->AddSubmenu(thread_menu_sp); 7641480093f4SDimitry Andric menubar_sp->AddSubmenu(view_menu_sp); 7642480093f4SDimitry Andric menubar_sp->AddSubmenu(help_menu_sp); 7643480093f4SDimitry Andric menubar_sp->SetDelegate(app_menu_delegate_sp); 7644480093f4SDimitry Andric 7645480093f4SDimitry Andric Rect content_bounds = main_window_sp->GetFrame(); 7646480093f4SDimitry Andric Rect menubar_bounds = content_bounds.MakeMenuBar(); 7647480093f4SDimitry Andric Rect status_bounds = content_bounds.MakeStatusBar(); 7648480093f4SDimitry Andric Rect source_bounds; 7649480093f4SDimitry Andric Rect variables_bounds; 7650480093f4SDimitry Andric Rect threads_bounds; 7651480093f4SDimitry Andric Rect source_variables_bounds; 7652480093f4SDimitry Andric content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds, 7653480093f4SDimitry Andric threads_bounds); 7654480093f4SDimitry Andric source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds, 7655480093f4SDimitry Andric variables_bounds); 7656480093f4SDimitry Andric 7657480093f4SDimitry Andric WindowSP menubar_window_sp = 7658480093f4SDimitry Andric main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false); 7659480093f4SDimitry Andric // Let the menubar get keys if the active window doesn't handle the keys 7660480093f4SDimitry Andric // that are typed so it can respond to menubar key presses. 7661480093f4SDimitry Andric menubar_window_sp->SetCanBeActive( 7662480093f4SDimitry Andric false); // Don't let the menubar become the active window 7663480093f4SDimitry Andric menubar_window_sp->SetDelegate(menubar_sp); 7664480093f4SDimitry Andric 7665480093f4SDimitry Andric WindowSP source_window_sp( 7666480093f4SDimitry Andric main_window_sp->CreateSubWindow("Source", source_bounds, true)); 7667480093f4SDimitry Andric WindowSP variables_window_sp( 7668480093f4SDimitry Andric main_window_sp->CreateSubWindow("Variables", variables_bounds, false)); 7669480093f4SDimitry Andric WindowSP threads_window_sp( 7670480093f4SDimitry Andric main_window_sp->CreateSubWindow("Threads", threads_bounds, false)); 7671480093f4SDimitry Andric WindowSP status_window_sp( 7672480093f4SDimitry Andric main_window_sp->CreateSubWindow("Status", status_bounds, false)); 7673480093f4SDimitry Andric status_window_sp->SetCanBeActive( 7674480093f4SDimitry Andric false); // Don't let the status bar become the active window 7675480093f4SDimitry Andric main_window_sp->SetDelegate( 7676480093f4SDimitry Andric std::static_pointer_cast<WindowDelegate>(app_delegate_sp)); 7677480093f4SDimitry Andric source_window_sp->SetDelegate( 7678480093f4SDimitry Andric WindowDelegateSP(new SourceFileWindowDelegate(m_debugger))); 7679480093f4SDimitry Andric variables_window_sp->SetDelegate( 7680480093f4SDimitry Andric WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger))); 7681480093f4SDimitry Andric TreeDelegateSP thread_delegate_sp(new ThreadsTreeDelegate(m_debugger)); 7682480093f4SDimitry Andric threads_window_sp->SetDelegate(WindowDelegateSP( 7683480093f4SDimitry Andric new TreeWindowDelegate(m_debugger, thread_delegate_sp))); 7684480093f4SDimitry Andric status_window_sp->SetDelegate( 7685480093f4SDimitry Andric WindowDelegateSP(new StatusBarWindowDelegate(m_debugger))); 7686480093f4SDimitry Andric 7687e8d8bef9SDimitry Andric // All colors with black background. 7688e8d8bef9SDimitry Andric init_pair(1, COLOR_BLACK, COLOR_BLACK); 7689e8d8bef9SDimitry Andric init_pair(2, COLOR_RED, COLOR_BLACK); 7690e8d8bef9SDimitry Andric init_pair(3, COLOR_GREEN, COLOR_BLACK); 7691e8d8bef9SDimitry Andric init_pair(4, COLOR_YELLOW, COLOR_BLACK); 7692e8d8bef9SDimitry Andric init_pair(5, COLOR_BLUE, COLOR_BLACK); 7693e8d8bef9SDimitry Andric init_pair(6, COLOR_MAGENTA, COLOR_BLACK); 7694e8d8bef9SDimitry Andric init_pair(7, COLOR_CYAN, COLOR_BLACK); 7695e8d8bef9SDimitry Andric init_pair(8, COLOR_WHITE, COLOR_BLACK); 7696e8d8bef9SDimitry Andric // All colors with blue background. 7697e8d8bef9SDimitry Andric init_pair(9, COLOR_BLACK, COLOR_BLUE); 7698e8d8bef9SDimitry Andric init_pair(10, COLOR_RED, COLOR_BLUE); 7699e8d8bef9SDimitry Andric init_pair(11, COLOR_GREEN, COLOR_BLUE); 7700e8d8bef9SDimitry Andric init_pair(12, COLOR_YELLOW, COLOR_BLUE); 7701e8d8bef9SDimitry Andric init_pair(13, COLOR_BLUE, COLOR_BLUE); 7702e8d8bef9SDimitry Andric init_pair(14, COLOR_MAGENTA, COLOR_BLUE); 7703e8d8bef9SDimitry Andric init_pair(15, COLOR_CYAN, COLOR_BLUE); 7704e8d8bef9SDimitry Andric init_pair(16, COLOR_WHITE, COLOR_BLUE); 7705e8d8bef9SDimitry Andric // These must match the order in the color indexes enum. 7706e8d8bef9SDimitry Andric init_pair(17, COLOR_BLACK, COLOR_WHITE); 7707e8d8bef9SDimitry Andric init_pair(18, COLOR_MAGENTA, COLOR_WHITE); 7708e8d8bef9SDimitry Andric static_assert(LastColorPairIndex == 18, "Color indexes do not match."); 7709fe6060f1SDimitry Andric 7710fe6060f1SDimitry Andric define_key("\033[Z", KEY_SHIFT_TAB); 7711349cc55cSDimitry Andric define_key("\033\015", KEY_ALT_ENTER); 7712480093f4SDimitry Andric } 7713480093f4SDimitry Andric } 7714480093f4SDimitry Andric 7715480093f4SDimitry Andric void IOHandlerCursesGUI::Deactivate() { m_app_ap->Terminate(); } 7716480093f4SDimitry Andric 7717480093f4SDimitry Andric void IOHandlerCursesGUI::Run() { 7718480093f4SDimitry Andric m_app_ap->Run(m_debugger); 7719480093f4SDimitry Andric SetIsDone(true); 7720480093f4SDimitry Andric } 7721480093f4SDimitry Andric 7722480093f4SDimitry Andric IOHandlerCursesGUI::~IOHandlerCursesGUI() = default; 7723480093f4SDimitry Andric 7724480093f4SDimitry Andric void IOHandlerCursesGUI::Cancel() {} 7725480093f4SDimitry Andric 772681ad6265SDimitry Andric bool IOHandlerCursesGUI::Interrupt() { 772781ad6265SDimitry Andric return m_debugger.GetCommandInterpreter().IOHandlerInterrupt(*this); 772881ad6265SDimitry Andric } 7729480093f4SDimitry Andric 7730480093f4SDimitry Andric void IOHandlerCursesGUI::GotEOF() {} 7731480093f4SDimitry Andric 7732e8d8bef9SDimitry Andric void IOHandlerCursesGUI::TerminalSizeChanged() { 7733e8d8bef9SDimitry Andric m_app_ap->TerminalSizeChanged(); 7734e8d8bef9SDimitry Andric } 7735e8d8bef9SDimitry Andric 7736480093f4SDimitry Andric #endif // LLDB_ENABLE_CURSES 7737