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