1 //===-- IOHandler.cpp -------------------------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
11 #ifndef LLDB_DISABLE_CURSES
17 #if defined(__APPLE__)
22 // Other libraries and framework includes
24 #include "lldb/Breakpoint/BreakpointLocation.h"
25 #include "lldb/Core/IOHandler.h"
26 #include "lldb/Core/Debugger.h"
27 #include "lldb/Core/Module.h"
28 #include "lldb/Core/State.h"
29 #include "lldb/Core/StreamFile.h"
30 #include "lldb/Core/ValueObjectRegister.h"
31 #ifndef LLDB_DISABLE_LIBEDIT
32 #include "lldb/Host/Editline.h"
34 #include "lldb/Interpreter/CommandCompletions.h"
35 #include "lldb/Interpreter/CommandInterpreter.h"
36 #include "lldb/Symbol/Block.h"
37 #include "lldb/Symbol/Function.h"
38 #include "lldb/Symbol/Symbol.h"
39 #include "lldb/Target/RegisterContext.h"
40 #include "lldb/Target/ThreadPlan.h"
41 #ifndef LLDB_DISABLE_CURSES
42 #include "lldb/Core/ValueObject.h"
43 #include "lldb/Symbol/VariableList.h"
44 #include "lldb/Target/Target.h"
45 #include "lldb/Target/Process.h"
46 #include "lldb/Target/Thread.h"
47 #include "lldb/Target/StackFrame.h"
55 using namespace lldb_private;
57 IOHandler::IOHandler (Debugger &debugger, IOHandler::Type type) :
60 StreamFileSP(), // Adopt STDIN from top input reader
61 StreamFileSP(), // Adopt STDOUT from top input reader
62 StreamFileSP(), // Adopt STDERR from top input reader
67 IOHandler::IOHandler (Debugger &debugger,
69 const lldb::StreamFileSP &input_sp,
70 const lldb::StreamFileSP &output_sp,
71 const lldb::StreamFileSP &error_sp,
73 m_debugger (debugger),
74 m_input_sp (input_sp),
75 m_output_sp (output_sp),
76 m_error_sp (error_sp),
84 // If any files are not specified, then adopt them from the top input reader.
85 if (!m_input_sp || !m_output_sp || !m_error_sp)
86 debugger.AdoptTopIOHandlerFilesIfInvalid (m_input_sp,
91 IOHandler::~IOHandler() = default;
94 IOHandler::GetInputFD()
96 return (m_input_sp ? m_input_sp->GetFile().GetDescriptor() : -1);
100 IOHandler::GetOutputFD()
102 return (m_output_sp ? m_output_sp->GetFile().GetDescriptor() : -1);
106 IOHandler::GetErrorFD()
108 return (m_error_sp ? m_error_sp->GetFile().GetDescriptor() : -1);
112 IOHandler::GetInputFILE()
114 return (m_input_sp ? m_input_sp->GetFile().GetStream() : nullptr);
118 IOHandler::GetOutputFILE()
120 return (m_output_sp ? m_output_sp->GetFile().GetStream() : nullptr);
124 IOHandler::GetErrorFILE()
126 return (m_error_sp ? m_error_sp->GetFile().GetStream() : nullptr);
130 IOHandler::GetInputStreamFile()
136 IOHandler::GetOutputStreamFile()
142 IOHandler::GetErrorStreamFile()
148 IOHandler::GetIsInteractive ()
150 return GetInputStreamFile()->GetFile().GetIsInteractive ();
154 IOHandler::GetIsRealTerminal ()
156 return GetInputStreamFile()->GetFile().GetIsRealTerminal();
160 IOHandler::SetPopped (bool b)
162 m_popped.SetValue(b, eBroadcastOnChange);
166 IOHandler::WaitForPop ()
168 m_popped.WaitForValueEqualTo(true);
172 IOHandlerStack::PrintAsync(Stream *stream, const char *s, size_t len)
176 std::lock_guard<std::recursive_mutex> guard(m_mutex);
178 m_top->PrintAsync(stream, s, len);
182 IOHandlerConfirm::IOHandlerConfirm (Debugger &debugger,
184 bool default_response) :
185 IOHandlerEditline(debugger,
186 IOHandler::Type::Confirm,
187 nullptr, // nullptr editline_name means no history loaded/saved
188 nullptr, // No prompt
189 nullptr, // No continuation prompt
191 false, // Don't colorize the prompt (i.e. the confirm message.)
194 m_default_response (default_response),
195 m_user_response (default_response)
197 StreamString prompt_stream;
198 prompt_stream.PutCString(prompt);
199 if (m_default_response)
200 prompt_stream.Printf(": [Y/n] ");
202 prompt_stream.Printf(": [y/N] ");
204 SetPrompt (prompt_stream.GetString().c_str());
207 IOHandlerConfirm::~IOHandlerConfirm() = default;
210 IOHandlerConfirm::IOHandlerComplete (IOHandler &io_handler,
211 const char *current_line,
213 const char *last_char,
214 int skip_first_n_matches,
218 if (current_line == cursor)
220 if (m_default_response)
222 matches.AppendString("y");
226 matches.AppendString("n");
229 return matches.GetSize();
233 IOHandlerConfirm::IOHandlerInputComplete (IOHandler &io_handler, std::string &line)
237 // User just hit enter, set the response to the default
238 m_user_response = m_default_response;
239 io_handler.SetIsDone(true);
243 if (line.size() == 1)
249 m_user_response = true;
250 io_handler.SetIsDone(true);
254 m_user_response = false;
255 io_handler.SetIsDone(true);
262 if (line == "yes" || line == "YES" || line == "Yes")
264 m_user_response = true;
265 io_handler.SetIsDone(true);
267 else if (line == "no" || line == "NO" || line == "No")
269 m_user_response = false;
270 io_handler.SetIsDone(true);
275 IOHandlerDelegate::IOHandlerComplete (IOHandler &io_handler,
276 const char *current_line,
278 const char *last_char,
279 int skip_first_n_matches,
283 switch (m_completion)
285 case Completion::None:
288 case Completion::LLDBCommand:
289 return io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion (current_line,
292 skip_first_n_matches,
296 case Completion::Expression:
298 bool word_complete = false;
299 const char *word_start = cursor;
300 if (cursor > current_line)
302 while (word_start > current_line && !isspace(*word_start))
304 CommandCompletions::InvokeCommonCompletionCallbacks(io_handler.GetDebugger().GetCommandInterpreter(),
305 CommandCompletions::eVariablePathCompletion,
307 skip_first_n_matches,
313 size_t num_matches = matches.GetSize();
316 std::string common_prefix;
317 matches.LongestCommonPrefix (common_prefix);
318 const size_t partial_name_len = strlen(word_start);
320 // If we matched a unique single command, add a space...
321 // Only do this if the completer told us this was a complete word, however...
322 if (num_matches == 1 && word_complete)
324 common_prefix.push_back(' ');
326 common_prefix.erase (0, partial_name_len);
327 matches.InsertStringAtIndex(0, std::move(common_prefix));
337 IOHandlerEditline::IOHandlerEditline (Debugger &debugger,
338 IOHandler::Type type,
339 const char *editline_name, // Used for saving history files
341 const char *continuation_prompt,
344 uint32_t line_number_start,
345 IOHandlerDelegate &delegate) :
346 IOHandlerEditline(debugger,
348 StreamFileSP(), // Inherit input from top input reader
349 StreamFileSP(), // Inherit output from top input reader
350 StreamFileSP(), // Inherit error from top input reader
352 editline_name, // Used for saving history files
362 IOHandlerEditline::IOHandlerEditline (Debugger &debugger,
363 IOHandler::Type type,
364 const lldb::StreamFileSP &input_sp,
365 const lldb::StreamFileSP &output_sp,
366 const lldb::StreamFileSP &error_sp,
368 const char *editline_name, // Used for saving history files
370 const char *continuation_prompt,
373 uint32_t line_number_start,
374 IOHandlerDelegate &delegate) :
375 IOHandler (debugger, type, input_sp, output_sp, error_sp, flags),
376 #ifndef LLDB_DISABLE_LIBEDIT
379 m_delegate (delegate),
381 m_continuation_prompt(),
382 m_current_lines_ptr(nullptr),
383 m_base_line_number (line_number_start),
384 m_curr_line_idx (UINT32_MAX),
385 m_multi_line (multi_line),
386 m_color_prompts (color_prompts),
387 m_interrupt_exits (true),
392 #ifndef LLDB_DISABLE_LIBEDIT
393 bool use_editline = false;
395 use_editline = m_input_sp->GetFile().GetIsRealTerminal();
399 m_editline_ap.reset(new Editline (editline_name,
404 m_editline_ap->SetIsInputCompleteCallback (IsInputCompleteCallback, this);
405 m_editline_ap->SetAutoCompleteCallback (AutoCompleteCallback, this);
406 // See if the delegate supports fixing indentation
407 const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters();
410 // The delegate does support indentation, hook it up so when any indentation
411 // character is typed, the delegate gets a chance to fix it
412 m_editline_ap->SetFixIndentationCallback (FixIndentationCallback, this, indent_chars);
416 SetBaseLineNumber (m_base_line_number);
417 SetPrompt(prompt ? prompt : "");
418 SetContinuationPrompt(continuation_prompt);
421 IOHandlerEditline::~IOHandlerEditline ()
423 #ifndef LLDB_DISABLE_LIBEDIT
424 m_editline_ap.reset();
429 IOHandlerEditline::Activate ()
431 IOHandler::Activate();
432 m_delegate.IOHandlerActivated(*this);
436 IOHandlerEditline::Deactivate ()
438 IOHandler::Deactivate();
439 m_delegate.IOHandlerDeactivated(*this);
443 IOHandlerEditline::GetLine (std::string &line, bool &interrupted)
445 #ifndef LLDB_DISABLE_LIBEDIT
448 return m_editline_ap->GetLine (line, interrupted);
455 FILE *in = GetInputFILE();
458 if (GetIsInteractive())
460 const char *prompt = nullptr;
462 if (m_multi_line && m_curr_line_idx > 0)
463 prompt = GetContinuationPrompt();
465 if (prompt == nullptr)
466 prompt = GetPrompt();
468 if (prompt && prompt[0])
470 FILE *out = GetOutputFILE();
473 ::fprintf(out, "%s", prompt);
480 bool got_line = false;
484 if (fgets(buffer, sizeof(buffer), in) == nullptr)
486 const int saved_errno = errno;
491 if (saved_errno != EINTR)
498 size_t buffer_len = strlen(buffer);
499 assert (buffer[buffer_len] == '\0');
500 char last_char = buffer[buffer_len-1];
501 if (last_char == '\r' || last_char == '\n')
504 // Strip trailing newlines
505 while (last_char == '\r' || last_char == '\n')
510 last_char = buffer[buffer_len-1];
513 line.append(buffer, buffer_len);
517 // We might have gotten a newline on a line by itself
518 // make sure to return true in this case.
523 // No more input file, we are done...
527 #ifndef LLDB_DISABLE_LIBEDIT
532 #ifndef LLDB_DISABLE_LIBEDIT
534 IOHandlerEditline::IsInputCompleteCallback (Editline *editline,
538 IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton;
539 return editline_reader->m_delegate.IOHandlerIsInputComplete(*editline_reader, lines);
543 IOHandlerEditline::FixIndentationCallback (Editline *editline,
544 const StringList &lines,
548 IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton;
549 return editline_reader->m_delegate.IOHandlerFixIndentation(*editline_reader, lines, cursor_position);
553 IOHandlerEditline::AutoCompleteCallback (const char *current_line,
555 const char *last_char,
556 int skip_first_n_matches,
561 IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton;
563 return editline_reader->m_delegate.IOHandlerComplete (*editline_reader,
567 skip_first_n_matches,
575 IOHandlerEditline::GetPrompt ()
577 #ifndef LLDB_DISABLE_LIBEDIT
580 return m_editline_ap->GetPrompt ();
585 if (m_prompt.empty())
587 #ifndef LLDB_DISABLE_LIBEDIT
590 return m_prompt.c_str();
594 IOHandlerEditline::SetPrompt (const char *p)
600 #ifndef LLDB_DISABLE_LIBEDIT
602 m_editline_ap->SetPrompt(m_prompt.empty() ? nullptr : m_prompt.c_str());
608 IOHandlerEditline::GetContinuationPrompt ()
610 return (m_continuation_prompt.empty() ? nullptr : m_continuation_prompt.c_str());
614 IOHandlerEditline::SetContinuationPrompt (const char *p)
617 m_continuation_prompt = p;
619 m_continuation_prompt.clear();
621 #ifndef LLDB_DISABLE_LIBEDIT
623 m_editline_ap->SetContinuationPrompt(m_continuation_prompt.empty() ? nullptr : m_continuation_prompt.c_str());
628 IOHandlerEditline::SetBaseLineNumber (uint32_t line)
630 m_base_line_number = line;
634 IOHandlerEditline::GetCurrentLineIndex () const
636 #ifndef LLDB_DISABLE_LIBEDIT
638 return m_editline_ap->GetCurrentLine();
640 return m_curr_line_idx;
644 IOHandlerEditline::GetLines (StringList &lines, bool &interrupted)
646 m_current_lines_ptr = &lines;
648 bool success = false;
649 #ifndef LLDB_DISABLE_LIBEDIT
652 return m_editline_ap->GetLines (m_base_line_number, lines, interrupted);
662 // Show line numbers if we are asked to
664 if (m_base_line_number > 0 && GetIsInteractive())
666 FILE *out = GetOutputFILE();
668 ::fprintf(out, "%u%s", m_base_line_number + (uint32_t)lines.GetSize(), GetPrompt() == nullptr ? " " : "");
671 m_curr_line_idx = lines.GetSize();
673 bool interrupted = false;
674 if (GetLine(line, interrupted) && !interrupted)
676 lines.AppendString(line);
677 done = m_delegate.IOHandlerIsInputComplete(*this, lines);
684 success = lines.GetSize() > 0;
685 #ifndef LLDB_DISABLE_LIBEDIT
691 // Each IOHandler gets to run until it is done. It should read data
692 // from the "in" and place output into "out" and "err and return
695 IOHandlerEditline::Run ()
700 bool interrupted = false;
704 if (GetLines (lines, interrupted))
708 m_done = m_interrupt_exits;
709 m_delegate.IOHandlerInputInterrupted (*this, line);
714 line = lines.CopyList();
715 m_delegate.IOHandlerInputComplete (*this, line);
725 if (GetLine(line, interrupted))
728 m_delegate.IOHandlerInputInterrupted (*this, line);
730 m_delegate.IOHandlerInputComplete (*this, line);
741 IOHandlerEditline::Cancel ()
743 #ifndef LLDB_DISABLE_LIBEDIT
745 m_editline_ap->Cancel ();
750 IOHandlerEditline::Interrupt ()
752 // Let the delgate handle it first
753 if (m_delegate.IOHandlerInterrupt(*this))
756 #ifndef LLDB_DISABLE_LIBEDIT
758 return m_editline_ap->Interrupt();
764 IOHandlerEditline::GotEOF()
766 #ifndef LLDB_DISABLE_LIBEDIT
768 m_editline_ap->Interrupt();
773 IOHandlerEditline::PrintAsync (Stream *stream, const char *s, size_t len)
775 #ifndef LLDB_DISABLE_LIBEDIT
777 m_editline_ap->PrintAsync(stream, s, len);
781 const char *prompt = GetPrompt();
785 // Back up over previous prompt using Windows API
786 CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info;
787 HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
788 GetConsoleScreenBufferInfo(console_handle, &screen_buffer_info);
789 COORD coord = screen_buffer_info.dwCursorPosition;
790 coord.X -= strlen(prompt);
793 SetConsoleCursorPosition(console_handle, coord);
796 IOHandler::PrintAsync(stream, s, len);
798 IOHandler::PrintAsync(GetOutputStreamFile().get(), prompt, strlen(prompt));
802 // we may want curses to be disabled for some builds
803 // for instance, windows
804 #ifndef LLDB_DISABLE_CURSES
806 #define KEY_RETURN 10
807 #define KEY_ESCAPE 27
814 class WindowDelegate;
815 typedef std::shared_ptr<Menu> MenuSP;
816 typedef std::shared_ptr<MenuDelegate> MenuDelegateSP;
817 typedef std::shared_ptr<Window> WindowSP;
818 typedef std::shared_ptr<WindowDelegate> WindowDelegateSP;
819 typedef std::vector<MenuSP> Menus;
820 typedef std::vector<WindowSP> Windows;
821 typedef std::vector<WindowDelegateSP> WindowDelegates;
824 type summary add -s "x=${var.x}, y=${var.y}" curses::Point
825 type summary add -s "w=${var.width}, h=${var.height}" curses::Size
826 type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect
834 Point (int _x = 0, int _y = 0) :
848 operator += (const Point &rhs)
858 printf ("(x=%i, y=%i)\n", x, y);
862 bool operator == (const Point &lhs, const Point &rhs)
864 return lhs.x == rhs.x && lhs.y == rhs.y;
867 bool operator != (const Point &lhs, const Point &rhs)
869 return lhs.x != rhs.x || lhs.y != rhs.y;
876 Size (int w = 0, int h = 0) :
892 printf ("(w=%i, h=%i)\n", width, height);
896 bool operator == (const Size &lhs, const Size &rhs)
898 return lhs.width == rhs.width && lhs.height == rhs.height;
901 bool operator != (const Size &lhs, const Size &rhs)
903 return lhs.width != rhs.width || lhs.height != rhs.height;
917 Rect (const Point &p, const Size &s) :
933 printf ("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width, size.height);
939 if (size.width > w*2)
943 if (size.height > h*2)
948 // Return a status bar rectangle which is the last line of
949 // this rectangle. This rectangle will be modified to not
950 // include the status bar area.
957 status_bar.origin.x = origin.x;
958 status_bar.origin.y = size.height;
959 status_bar.size.width = size.width;
960 status_bar.size.height = 1;
966 // Return a menubar rectangle which is the first line of
967 // this rectangle. This rectangle will be modified to not
968 // include the menubar area.
975 menubar.origin.x = origin.x;
976 menubar.origin.y = origin.y;
977 menubar.size.width = size.width;
978 menubar.size.height = 1;
986 HorizontalSplitPercentage (float top_percentage, Rect &top, Rect &bottom) const
988 float top_height = top_percentage * size.height;
989 HorizontalSplit (top_height, top, bottom);
993 HorizontalSplit (int top_height, Rect &top, Rect &bottom) const
996 if (top_height < size.height)
998 top.size.height = top_height;
999 bottom.origin.x = origin.x;
1000 bottom.origin.y = origin.y + top.size.height;
1001 bottom.size.width = size.width;
1002 bottom.size.height = size.height - top.size.height;
1011 VerticalSplitPercentage (float left_percentage, Rect &left, Rect &right) const
1013 float left_width = left_percentage * size.width;
1014 VerticalSplit (left_width, left, right);
1018 VerticalSplit (int left_width, Rect &left, Rect &right) const
1021 if (left_width < size.width)
1023 left.size.width = left_width;
1024 right.origin.x = origin.x + left.size.width;
1025 right.origin.y = origin.y;
1026 right.size.width = size.width - left.size.width;
1027 right.size.height = size.height;
1036 bool operator == (const Rect &lhs, const Rect &rhs)
1038 return lhs.origin == rhs.origin && lhs.size == rhs.size;
1041 bool operator != (const Rect &lhs, const Rect &rhs)
1043 return lhs.origin != rhs.origin || lhs.size != rhs.size;
1046 enum HandleCharResult
1050 eQuitApplication = 2
1053 enum class MenuActionResult
1057 Quit // Exit all menus and quit
1063 const char *description;
1066 class WindowDelegate
1070 ~WindowDelegate() = default;
1073 WindowDelegateDraw (Window &window, bool force)
1075 return false; // Drawing not handled
1078 virtual HandleCharResult
1079 WindowDelegateHandleChar (Window &window, int key)
1081 return eKeyNotHandled;
1084 virtual const char *
1085 WindowDelegateGetHelpText ()
1091 WindowDelegateGetKeyHelp ()
1097 class HelpDialogDelegate :
1098 public WindowDelegate
1101 HelpDialogDelegate (const char *text, KeyHelp *key_help_array);
1103 ~HelpDialogDelegate() override;
1106 WindowDelegateDraw (Window &window, bool force) override;
1109 WindowDelegateHandleChar (Window &window, int key) override;
1114 return m_text.GetSize();
1118 GetMaxLineLength () const
1120 return m_text.GetMaxStringLength();
1125 int m_first_visible_line;
1131 Window (const char *name) :
1138 m_curr_active_window_idx (UINT32_MAX),
1139 m_prev_active_window_idx (UINT32_MAX),
1141 m_needs_update (true),
1142 m_can_activate (true),
1147 Window (const char *name, WINDOW *w, bool del = true) :
1154 m_curr_active_window_idx (UINT32_MAX),
1155 m_prev_active_window_idx (UINT32_MAX),
1157 m_needs_update (true),
1158 m_can_activate (true),
1165 Window (const char *name, const Rect &bounds) :
1171 m_curr_active_window_idx (UINT32_MAX),
1172 m_prev_active_window_idx (UINT32_MAX),
1174 m_needs_update (true),
1175 m_can_activate (true),
1178 Reset (::newwin (bounds.size.height, bounds.size.width, bounds.origin.y, bounds.origin.y));
1184 RemoveSubWindows ();
1189 Reset(WINDOW *w = nullptr, bool del = true)
1196 ::del_panel (m_panel);
1199 if (m_window && m_delete)
1201 ::delwin (m_window);
1208 m_panel = ::new_panel (m_window);
1213 void AttributeOn (attr_t attr) { ::wattron (m_window, attr); }
1214 void AttributeOff (attr_t attr) { ::wattroff (m_window, attr); }
1215 void Box (chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) { ::box(m_window, v_char, h_char); }
1216 void Clear () { ::wclear (m_window); }
1217 void Erase () { ::werase (m_window); }
1218 Rect GetBounds () { return Rect (GetParentOrigin(), GetSize()); } // Get the rectangle in our parent window
1219 int GetChar () { return ::wgetch (m_window); }
1220 int GetCursorX () { return getcurx (m_window); }
1221 int GetCursorY () { return getcury (m_window); }
1222 Rect GetFrame () { return Rect (Point(), GetSize()); } // Get our rectangle in our own coordinate system
1223 Point GetParentOrigin() { return Point (GetParentX(), GetParentY()); }
1224 Size GetSize() { return Size (GetWidth(), GetHeight()); }
1225 int GetParentX () { return getparx (m_window); }
1226 int GetParentY () { return getpary (m_window); }
1227 int GetMaxX() { return getmaxx (m_window); }
1228 int GetMaxY() { return getmaxy (m_window); }
1229 int GetWidth() { return GetMaxX(); }
1230 int GetHeight() { return GetMaxY(); }
1231 void MoveCursor (int x, int y) { ::wmove (m_window, y, x); }
1232 void MoveWindow (int x, int y) { MoveWindow(Point(x,y)); }
1233 void Resize (int w, int h) { ::wresize(m_window, h, w); }
1234 void Resize (const Size &size) { ::wresize(m_window, size.height, size.width); }
1235 void PutChar (int ch) { ::waddch (m_window, ch); }
1236 void PutCString (const char *s, int len = -1) { ::waddnstr (m_window, s, len); }
1237 void Refresh () { ::wrefresh (m_window); }
1238 void DeferredRefresh ()
1240 // We are using panels, so we don't need to call this...
1241 //::wnoutrefresh(m_window);
1243 void SetBackground (int color_pair_idx) { ::wbkgd (m_window,COLOR_PAIR(color_pair_idx)); }
1244 void UnderlineOn () { AttributeOn(A_UNDERLINE); }
1245 void UnderlineOff () { AttributeOff(A_UNDERLINE); }
1247 void PutCStringTruncated (const char *s, int right_pad)
1249 int bytes_left = GetWidth() - GetCursorX();
1250 if (bytes_left > right_pad)
1252 bytes_left -= right_pad;
1253 ::waddnstr (m_window, s, bytes_left);
1258 MoveWindow (const Point &origin)
1260 const bool moving_window = origin != GetParentOrigin();
1261 if (m_is_subwin && moving_window)
1263 // Can't move subwindows, must delete and re-create
1264 Size size = GetSize();
1265 Reset (::subwin (m_parent->m_window,
1273 ::mvwin (m_window, origin.y, origin.x);
1278 SetBounds (const Rect &bounds)
1280 const bool moving_window = bounds.origin != GetParentOrigin();
1281 if (m_is_subwin && moving_window)
1283 // Can't move subwindows, must delete and re-create
1284 Reset (::subwin (m_parent->m_window,
1288 bounds.origin.x), true);
1293 MoveWindow(bounds.origin);
1294 Resize (bounds.size);
1299 Printf (const char *format, ...) __attribute__ ((format (printf, 2, 3)))
1302 va_start (args, format);
1303 vwprintw(m_window, format, args);
1310 ::touchwin (m_window);
1316 CreateSubWindow (const char *name, const Rect &bounds, bool make_active)
1318 WindowSP subwindow_sp;
1321 subwindow_sp.reset(new Window(name, ::subwin (m_window,
1325 bounds.origin.x), true));
1326 subwindow_sp->m_is_subwin = true;
1330 subwindow_sp.reset(new Window(name, ::newwin (bounds.size.height,
1333 bounds.origin.x), true));
1334 subwindow_sp->m_is_subwin = false;
1336 subwindow_sp->m_parent = this;
1339 m_prev_active_window_idx = m_curr_active_window_idx;
1340 m_curr_active_window_idx = m_subwindows.size();
1342 m_subwindows.push_back(subwindow_sp);
1343 ::top_panel (subwindow_sp->m_panel);
1344 m_needs_update = true;
1345 return subwindow_sp;
1349 RemoveSubWindow (Window *window)
1351 Windows::iterator pos, end = m_subwindows.end();
1353 for (pos = m_subwindows.begin(); pos != end; ++pos, ++i)
1355 if ((*pos).get() == window)
1357 if (m_prev_active_window_idx == i)
1358 m_prev_active_window_idx = UINT32_MAX;
1359 else if (m_prev_active_window_idx != UINT32_MAX && m_prev_active_window_idx > i)
1360 --m_prev_active_window_idx;
1362 if (m_curr_active_window_idx == i)
1363 m_curr_active_window_idx = UINT32_MAX;
1364 else if (m_curr_active_window_idx != UINT32_MAX && m_curr_active_window_idx > i)
1365 --m_curr_active_window_idx;
1367 m_subwindows.erase(pos);
1368 m_needs_update = true;
1372 ::touchwin (stdscr);
1380 FindSubWindow (const char *name)
1382 Windows::iterator pos, end = m_subwindows.end();
1384 for (pos = m_subwindows.begin(); pos != end; ++pos, ++i)
1386 if ((*pos)->m_name.compare(name) == 0)
1395 m_curr_active_window_idx = UINT32_MAX;
1396 m_prev_active_window_idx = UINT32_MAX;
1397 for (Windows::iterator pos = m_subwindows.begin();
1398 pos != m_subwindows.end();
1399 pos = m_subwindows.erase(pos))
1406 ::touchwin (stdscr);
1420 //----------------------------------------------------------------------
1421 // Window drawing utilities
1422 //----------------------------------------------------------------------
1424 DrawTitleBox(const char *title, const char *bottom_message = nullptr)
1428 attr = A_BOLD | COLOR_PAIR(2);
1437 if (title && title[0])
1444 if (bottom_message && bottom_message[0])
1446 int bottom_message_length = strlen(bottom_message);
1447 int x = GetWidth() - 3 - (bottom_message_length + 2);
1451 MoveCursor (x, GetHeight() - 1);
1453 PutCString(bottom_message);
1458 MoveCursor (1, GetHeight() - 1);
1460 PutCStringTruncated (bottom_message, 1);
1470 if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw (*this, force))
1473 for (auto &subwindow_sp : m_subwindows)
1474 subwindow_sp->Draw(force);
1478 CreateHelpSubwindow ()
1482 const char *text = m_delegate_sp->WindowDelegateGetHelpText ();
1483 KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp ();
1484 if ((text && text[0]) || key_help)
1486 std::auto_ptr<HelpDialogDelegate> help_delegate_ap(new HelpDialogDelegate(text, key_help));
1487 const size_t num_lines = help_delegate_ap->GetNumLines();
1488 const size_t max_length = help_delegate_ap->GetMaxLineLength();
1489 Rect bounds = GetBounds();
1491 if (max_length + 4 < static_cast<size_t>(bounds.size.width))
1493 bounds.origin.x += (bounds.size.width - max_length + 4)/2;
1494 bounds.size.width = max_length + 4;
1498 if (bounds.size.width > 100)
1500 const int inset_w = bounds.size.width / 4;
1501 bounds.origin.x += inset_w;
1502 bounds.size.width -= 2*inset_w;
1506 if (num_lines + 2 < static_cast<size_t>(bounds.size.height))
1508 bounds.origin.y += (bounds.size.height - num_lines + 2)/2;
1509 bounds.size.height = num_lines + 2;
1513 if (bounds.size.height > 100)
1515 const int inset_h = bounds.size.height / 4;
1516 bounds.origin.y += inset_h;
1517 bounds.size.height -= 2*inset_h;
1520 WindowSP help_window_sp;
1521 Window *parent_window = GetParent();
1523 help_window_sp = parent_window->CreateSubWindow("Help", bounds, true);
1525 help_window_sp = CreateSubWindow("Help", bounds, true);
1526 help_window_sp->SetDelegate(WindowDelegateSP(help_delegate_ap.release()));
1533 virtual HandleCharResult
1534 HandleChar (int key)
1536 // Always check the active window first
1537 HandleCharResult result = eKeyNotHandled;
1538 WindowSP active_window_sp = GetActiveWindow ();
1539 if (active_window_sp)
1541 result = active_window_sp->HandleChar (key);
1542 if (result != eKeyNotHandled)
1548 result = m_delegate_sp->WindowDelegateHandleChar (*this, key);
1549 if (result != eKeyNotHandled)
1553 // Then check for any windows that want any keys
1554 // that weren't handled. This is typically only
1556 // Make a copy of the subwindows in case any HandleChar()
1557 // functions muck with the subwindows. If we don't do this,
1558 // we can crash when iterating over the subwindows.
1559 Windows subwindows (m_subwindows);
1560 for (auto subwindow_sp : subwindows)
1562 if (!subwindow_sp->m_can_activate)
1564 HandleCharResult result = subwindow_sp->HandleChar(key);
1565 if (result != eKeyNotHandled)
1570 return eKeyNotHandled;
1574 SetActiveWindow (Window *window)
1576 const size_t num_subwindows = m_subwindows.size();
1577 for (size_t i = 0; i < num_subwindows; ++i)
1579 if (m_subwindows[i].get() == window)
1581 m_prev_active_window_idx = m_curr_active_window_idx;
1582 ::top_panel (window->m_panel);
1583 m_curr_active_window_idx = i;
1593 if (!m_subwindows.empty())
1595 if (m_curr_active_window_idx >= m_subwindows.size())
1597 if (m_prev_active_window_idx < m_subwindows.size())
1599 m_curr_active_window_idx = m_prev_active_window_idx;
1600 m_prev_active_window_idx = UINT32_MAX;
1602 else if (IsActive())
1604 m_prev_active_window_idx = UINT32_MAX;
1605 m_curr_active_window_idx = UINT32_MAX;
1607 // Find first window that wants to be active if this window is active
1608 const size_t num_subwindows = m_subwindows.size();
1609 for (size_t i = 0; i < num_subwindows; ++i)
1611 if (m_subwindows[i]->GetCanBeActive())
1613 m_curr_active_window_idx = i;
1620 if (m_curr_active_window_idx < m_subwindows.size())
1621 return m_subwindows[m_curr_active_window_idx];
1627 GetCanBeActive () const
1629 return m_can_activate;
1633 SetCanBeActive (bool b)
1638 const WindowDelegateSP &
1639 GetDelegate () const
1641 return m_delegate_sp;
1645 SetDelegate (const WindowDelegateSP &delegate_sp)
1647 m_delegate_sp = delegate_sp;
1660 return m_parent->GetActiveWindow().get() == this;
1662 return true; // Top level window is always active
1666 SelectNextWindowAsActive ()
1668 // Move active focus to next window
1669 const size_t num_subwindows = m_subwindows.size();
1670 if (m_curr_active_window_idx == UINT32_MAX)
1673 for (auto subwindow_sp : m_subwindows)
1675 if (subwindow_sp->GetCanBeActive())
1677 m_curr_active_window_idx = idx;
1683 else if (m_curr_active_window_idx + 1 < num_subwindows)
1685 bool handled = false;
1686 m_prev_active_window_idx = m_curr_active_window_idx;
1687 for (size_t idx=m_curr_active_window_idx + 1; idx<num_subwindows; ++idx)
1689 if (m_subwindows[idx]->GetCanBeActive())
1691 m_curr_active_window_idx = idx;
1698 for (size_t idx=0; idx<=m_prev_active_window_idx; ++idx)
1700 if (m_subwindows[idx]->GetCanBeActive())
1702 m_curr_active_window_idx = idx;
1710 m_prev_active_window_idx = m_curr_active_window_idx;
1711 for (size_t idx=0; idx<num_subwindows; ++idx)
1713 if (m_subwindows[idx]->GetCanBeActive())
1715 m_curr_active_window_idx = idx;
1725 return m_name.c_str();
1733 Windows m_subwindows;
1734 WindowDelegateSP m_delegate_sp;
1735 uint32_t m_curr_active_window_idx;
1736 uint32_t m_prev_active_window_idx;
1738 bool m_needs_update;
1739 bool m_can_activate;
1743 DISALLOW_COPY_AND_ASSIGN(Window);
1749 virtual ~MenuDelegate() = default;
1751 virtual MenuActionResult
1752 MenuDelegateAction (Menu &menu) = 0;
1755 class Menu : public WindowDelegate
1766 // Menubar or separator constructor
1769 // Menuitem constructor
1770 Menu (const char *name,
1771 const char *key_name,
1773 uint64_t identifier);
1775 ~Menu() override = default;
1777 const MenuDelegateSP &
1778 GetDelegate () const
1780 return m_delegate_sp;
1784 SetDelegate (const MenuDelegateSP &delegate_sp)
1786 m_delegate_sp = delegate_sp;
1790 RecalculateNameLengths();
1793 AddSubmenu (const MenuSP &menu_sp);
1796 DrawAndRunMenu (Window &window);
1799 DrawMenuTitle (Window &window, bool highlight);
1802 WindowDelegateDraw (Window &window, bool force) override;
1805 WindowDelegateHandleChar (Window &window, int key) override;
1808 ActionPrivate (Menu &menu)
1810 MenuActionResult result = MenuActionResult::NotHandled;
1813 result = m_delegate_sp->MenuDelegateAction (menu);
1814 if (result != MenuActionResult::NotHandled)
1819 result = m_parent->ActionPrivate(menu);
1820 if (result != MenuActionResult::NotHandled)
1823 return m_canned_result;
1829 // Call the recursive action so it can try to handle it
1830 // with the menu delegate, and if not, try our parent menu
1831 return ActionPrivate (*this);
1835 SetCannedResult (MenuActionResult result)
1837 m_canned_result = result;
1853 GetSelectedSubmenuIndex () const
1859 SetSelectedSubmenuIndex (int idx)
1871 GetStartingColumn() const
1877 SetStartingColumn(int col)
1889 SetKeyValue(int key_value)
1891 m_key_value = key_value;
1907 GetDrawWidth () const
1909 return m_max_submenu_name_length + m_max_submenu_key_name_length + 8;
1913 GetIdentifier() const
1915 return m_identifier;
1919 SetIdentifier (uint64_t identifier)
1921 m_identifier = identifier;
1926 std::string m_key_name;
1927 uint64_t m_identifier;
1931 int m_max_submenu_name_length;
1932 int m_max_submenu_key_name_length;
1936 WindowSP m_menu_window_sp;
1937 MenuActionResult m_canned_result;
1938 MenuDelegateSP m_delegate_sp;
1941 // Menubar or separator constructor
1942 Menu::Menu (Type type) :
1949 m_max_submenu_name_length (0),
1950 m_max_submenu_key_name_length (0),
1954 m_canned_result (MenuActionResult::NotHandled),
1959 // Menuitem constructor
1960 Menu::Menu (const char *name,
1961 const char *key_name,
1963 uint64_t identifier) :
1966 m_identifier (identifier),
1967 m_type (Type::Invalid),
1968 m_key_value (key_value),
1970 m_max_submenu_name_length (0),
1971 m_max_submenu_key_name_length (0),
1975 m_canned_result (MenuActionResult::NotHandled),
1978 if (name && name[0])
1981 m_type = Type::Item;
1982 if (key_name && key_name[0])
1983 m_key_name = key_name;
1987 m_type = Type::Separator;
1992 Menu::RecalculateNameLengths()
1994 m_max_submenu_name_length = 0;
1995 m_max_submenu_key_name_length = 0;
1996 Menus &submenus = GetSubmenus();
1997 const size_t num_submenus = submenus.size();
1998 for (size_t i = 0; i < num_submenus; ++i)
2000 Menu *submenu = submenus[i].get();
2001 if (static_cast<size_t>(m_max_submenu_name_length) < submenu->m_name.size())
2002 m_max_submenu_name_length = submenu->m_name.size();
2003 if (static_cast<size_t>(m_max_submenu_key_name_length) < submenu->m_key_name.size())
2004 m_max_submenu_key_name_length = submenu->m_key_name.size();
2009 Menu::AddSubmenu (const MenuSP &menu_sp)
2011 menu_sp->m_parent = this;
2012 if (static_cast<size_t>(m_max_submenu_name_length) < menu_sp->m_name.size())
2013 m_max_submenu_name_length = menu_sp->m_name.size();
2014 if (static_cast<size_t>(m_max_submenu_key_name_length) < menu_sp->m_key_name.size())
2015 m_max_submenu_key_name_length = menu_sp->m_key_name.size();
2016 m_submenus.push_back(menu_sp);
2020 Menu::DrawMenuTitle (Window &window, bool highlight)
2022 if (m_type == Type::Separator)
2024 window.MoveCursor(0, window.GetCursorY());
2025 window.PutChar(ACS_LTEE);
2026 int width = window.GetWidth();
2030 for (int i = 0; i < width; ++i)
2031 window.PutChar(ACS_HLINE);
2033 window.PutChar(ACS_RTEE);
2037 const int shortcut_key = m_key_value;
2038 bool underlined_shortcut = false;
2039 const attr_t hilgight_attr = A_REVERSE;
2041 window.AttributeOn(hilgight_attr);
2042 if (isprint(shortcut_key))
2044 size_t lower_pos = m_name.find(tolower(shortcut_key));
2045 size_t upper_pos = m_name.find(toupper(shortcut_key));
2046 const char *name = m_name.c_str();
2047 size_t pos = std::min<size_t>(lower_pos, upper_pos);
2048 if (pos != std::string::npos)
2050 underlined_shortcut = true;
2053 window.PutCString(name, pos);
2056 const attr_t shortcut_attr = A_UNDERLINE|A_BOLD;
2057 window.AttributeOn (shortcut_attr);
2058 window.PutChar(name[0]);
2059 window.AttributeOff(shortcut_attr);
2062 window.PutCString(name);
2066 if (!underlined_shortcut)
2068 window.PutCString(m_name.c_str());
2072 window.AttributeOff(hilgight_attr);
2074 if (m_key_name.empty())
2076 if (!underlined_shortcut && isprint(m_key_value))
2078 window.AttributeOn (COLOR_PAIR(3));
2079 window.Printf (" (%c)", m_key_value);
2080 window.AttributeOff (COLOR_PAIR(3));
2085 window.AttributeOn (COLOR_PAIR(3));
2086 window.Printf (" (%s)", m_key_name.c_str());
2087 window.AttributeOff (COLOR_PAIR(3));
2093 Menu::WindowDelegateDraw (Window &window, bool force)
2095 Menus &submenus = GetSubmenus();
2096 const size_t num_submenus = submenus.size();
2097 const int selected_idx = GetSelectedSubmenuIndex();
2098 Menu::Type menu_type = GetType ();
2101 case Menu::Type::Bar:
2103 window.SetBackground(2);
2104 window.MoveCursor(0, 0);
2105 for (size_t i = 0; i < num_submenus; ++i)
2107 Menu *menu = submenus[i].get();
2109 window.PutChar(' ');
2110 menu->SetStartingColumn (window.GetCursorX());
2111 window.PutCString("| ");
2112 menu->DrawMenuTitle (window, false);
2114 window.PutCString(" |");
2115 window.DeferredRefresh();
2119 case Menu::Type::Item:
2127 window.SetBackground(2);
2129 for (size_t i = 0; i < num_submenus; ++i)
2131 const bool is_selected =
2132 (i == static_cast<size_t>(selected_idx));
2133 window.MoveCursor(x, y + i);
2136 // Remember where we want the cursor to be
2140 submenus[i]->DrawMenuTitle (window, is_selected);
2142 window.MoveCursor(cursor_x, cursor_y);
2143 window.DeferredRefresh();
2148 case Menu::Type::Separator:
2151 return true; // Drawing handled...
2155 Menu::WindowDelegateHandleChar (Window &window, int key)
2157 HandleCharResult result = eKeyNotHandled;
2159 Menus &submenus = GetSubmenus();
2160 const size_t num_submenus = submenus.size();
2161 const int selected_idx = GetSelectedSubmenuIndex();
2162 Menu::Type menu_type = GetType ();
2163 if (menu_type == Menu::Type::Bar)
2170 // Show last menu or first menu
2171 if (selected_idx < static_cast<int>(num_submenus))
2172 run_menu_sp = submenus[selected_idx];
2173 else if (!submenus.empty())
2174 run_menu_sp = submenus.front();
2175 result = eKeyHandled;
2180 if (m_selected >= static_cast<int>(num_submenus))
2182 if (m_selected < static_cast<int>(num_submenus))
2183 run_menu_sp = submenus[m_selected];
2184 else if (!submenus.empty())
2185 run_menu_sp = submenus.front();
2186 result = eKeyHandled;
2192 m_selected = num_submenus - 1;
2193 if (m_selected < static_cast<int>(num_submenus))
2194 run_menu_sp = submenus[m_selected];
2195 else if (!submenus.empty())
2196 run_menu_sp = submenus.front();
2197 result = eKeyHandled;
2201 for (size_t i = 0; i < num_submenus; ++i)
2203 if (submenus[i]->GetKeyValue() == key)
2205 SetSelectedSubmenuIndex(i);
2206 run_menu_sp = submenus[i];
2207 result = eKeyHandled;
2216 // Run the action on this menu in case we need to populate the
2217 // menu with dynamic content and also in case check marks, and
2218 // any other menu decorations need to be calculated
2219 if (run_menu_sp->Action() == MenuActionResult::Quit)
2220 return eQuitApplication;
2223 menu_bounds.origin.x = run_menu_sp->GetStartingColumn();
2224 menu_bounds.origin.y = 1;
2225 menu_bounds.size.width = run_menu_sp->GetDrawWidth();
2226 menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2;
2227 if (m_menu_window_sp)
2228 window.GetParent()->RemoveSubWindow(m_menu_window_sp.get());
2230 m_menu_window_sp = window.GetParent()->CreateSubWindow (run_menu_sp->GetName().c_str(),
2233 m_menu_window_sp->SetDelegate (run_menu_sp);
2236 else if (menu_type == Menu::Type::Item)
2241 if (m_submenus.size() > 1)
2243 const int start_select = m_selected;
2244 while (++m_selected != start_select)
2246 if (static_cast<size_t>(m_selected) >= num_submenus)
2248 if (m_submenus[m_selected]->GetType() == Type::Separator)
2258 if (m_submenus.size() > 1)
2260 const int start_select = m_selected;
2261 while (--m_selected != start_select)
2263 if (m_selected < static_cast<int>(0))
2264 m_selected = num_submenus - 1;
2265 if (m_submenus[m_selected]->GetType() == Type::Separator)
2275 if (static_cast<size_t>(selected_idx) < num_submenus)
2277 if (submenus[selected_idx]->Action() == MenuActionResult::Quit)
2278 return eQuitApplication;
2279 window.GetParent()->RemoveSubWindow(&window);
2284 case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in case other chars are entered for escaped sequences
2285 window.GetParent()->RemoveSubWindow(&window);
2289 for (size_t i = 0; i < num_submenus; ++i)
2291 Menu *menu = submenus[i].get();
2292 if (menu->GetKeyValue() == key)
2294 SetSelectedSubmenuIndex(i);
2295 window.GetParent()->RemoveSubWindow(&window);
2296 if (menu->Action() == MenuActionResult::Quit)
2297 return eQuitApplication;
2304 else if (menu_type == Menu::Type::Separator)
2313 Application (FILE *in, FILE *out) :
2323 m_window_delegates.clear();
2324 m_window_sp.reset();
2327 ::delscreen(m_screen);
2335 ::setlocale(LC_ALL, "");
2336 ::setlocale(LC_CTYPE, "");
2340 m_screen = ::newterm(nullptr, m_out, m_in);
2345 ::keypad(stdscr,TRUE);
2355 Run (Debugger &debugger)
2358 int delay_in_tenths_of_a_second = 1;
2360 // Alas the threading model in curses is a bit lame so we need to
2361 // resort to polling every 0.5 seconds. We could poll for stdin
2362 // ourselves and then pass the keys down but then we need to
2363 // translate all of the escape sequences ourselves. So we resort to
2364 // polling for input because we need to receive async process events
2365 // while in this loop.
2367 halfdelay(delay_in_tenths_of_a_second); // Poll using some number of tenths of seconds seconds when calling Window::GetChar()
2369 ListenerSP listener_sp (Listener::MakeListener("lldb.IOHandler.curses.Application"));
2370 ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass());
2371 ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass());
2372 ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass());
2373 debugger.EnableForwardEvents (listener_sp);
2376 #if defined(__APPLE__)
2377 std::deque<int> escape_chars;
2384 m_window_sp->Draw(false);
2385 // All windows should be calling Window::DeferredRefresh() instead
2386 // of Window::Refresh() so we can do a single update and avoid
2387 // any screen blinking
2390 // Cursor hiding isn't working on MacOSX, so hide it in the top left corner
2391 m_window_sp->MoveCursor(0, 0);
2397 #if defined(__APPLE__)
2398 // Terminal.app doesn't map its function keys correctly, F1-F4 default to:
2399 // \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if possible
2401 if (escape_chars.empty())
2402 ch = m_window_sp->GetChar();
2405 ch = escape_chars.front();
2406 escape_chars.pop_front();
2408 if (ch == KEY_ESCAPE)
2410 int ch2 = m_window_sp->GetChar();
2413 int ch3 = m_window_sp->GetChar();
2416 case 'P': ch = KEY_F(1); break;
2417 case 'Q': ch = KEY_F(2); break;
2418 case 'R': ch = KEY_F(3); break;
2419 case 'S': ch = KEY_F(4); break;
2421 escape_chars.push_back(ch2);
2423 escape_chars.push_back(ch3);
2428 escape_chars.push_back(ch2);
2431 int ch = m_window_sp->GetChar();
2436 if (feof(m_in) || ferror(m_in))
2442 // Just a timeout from using halfdelay(), check for events
2444 while (listener_sp->PeekAtNextEvent())
2446 listener_sp->GetNextEvent(event_sp);
2450 Broadcaster *broadcaster = event_sp->GetBroadcaster();
2453 //uint32_t event_type = event_sp->GetType();
2454 ConstString broadcaster_class (broadcaster->GetBroadcasterClass());
2455 if (broadcaster_class == broadcaster_class_process)
2457 debugger.GetCommandInterpreter().UpdateExecutionContext(nullptr);
2459 continue; // Don't get any key, just update our view
2468 HandleCharResult key_result = m_window_sp->HandleChar(ch);
2472 debugger.GetCommandInterpreter().UpdateExecutionContext(nullptr);
2475 case eKeyNotHandled:
2477 case eQuitApplication:
2484 debugger.CancelForwardEvents (listener_sp);
2491 m_window_sp.reset (new Window ("main", stdscr, false));
2496 GetWindowDelegates ()
2498 return m_window_delegates;
2502 WindowSP m_window_sp;
2503 WindowDelegates m_window_delegates;
2509 } // namespace curses
2511 using namespace curses;
2515 ValueObjectSP valobj;
2520 bool might_have_children;
2522 bool calculated_children;
2523 std::vector<Row> children;
2525 Row (const ValueObjectSP &v, Row *p) :
2531 might_have_children (v ? v->MightHaveChildren() : false),
2533 calculated_children (false),
2542 return 1 + parent->GetDepth();
2550 if (!calculated_children)
2552 calculated_children = true;
2555 const size_t num_children = valobj->GetNumChildren();
2556 for (size_t i = 0; i < num_children; ++i)
2558 children.push_back(Row (valobj->GetChildAtIndex(i, true), this));
2571 DrawTree (Window &window)
2574 parent->DrawTreeForChild (window, this, 0);
2576 if (might_have_children)
2578 // It we can get UTF8 characters to work we should try to use the "symbol"
2579 // UTF8 string below
2580 // const char *symbol = "";
2581 // if (row.expanded)
2582 // symbol = "\xe2\x96\xbd ";
2584 // symbol = "\xe2\x96\xb7 ";
2585 // window.PutCString (symbol);
2587 // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
2588 // 'v' or '>' character...
2590 // window.PutChar (ACS_DARROW);
2592 // window.PutChar (ACS_RARROW);
2593 // Since we can't find any good looking right arrow/down arrow
2594 // symbols, just use a diamond...
2595 window.PutChar (ACS_DIAMOND);
2596 window.PutChar (ACS_HLINE);
2601 DrawTreeForChild (Window &window, Row *child, uint32_t reverse_depth)
2604 parent->DrawTreeForChild (window, this, reverse_depth + 1);
2606 if (&children.back() == child)
2609 if (reverse_depth == 0)
2611 window.PutChar (ACS_LLCORNER);
2612 window.PutChar (ACS_HLINE);
2616 window.PutChar (' ');
2617 window.PutChar (' ');
2622 if (reverse_depth == 0)
2624 window.PutChar (ACS_LTEE);
2625 window.PutChar (ACS_HLINE);
2629 window.PutChar (ACS_VLINE);
2630 window.PutChar (' ');
2636 struct DisplayOptions
2646 TreeDelegate() = default;
2647 virtual ~TreeDelegate() = default;
2649 virtual void TreeDelegateDrawTreeItem (TreeItem &item, Window &window) = 0;
2650 virtual void TreeDelegateGenerateChildren (TreeItem &item) = 0;
2651 virtual bool TreeDelegateItemSelected (TreeItem &item) = 0; // Return true if we need to update views
2654 typedef std::shared_ptr<TreeDelegate> TreeDelegateSP;
2659 TreeItem (TreeItem *parent, TreeDelegate &delegate, bool might_have_children) :
2661 m_delegate (delegate),
2662 m_user_data(nullptr),
2666 m_might_have_children (might_have_children),
2667 m_is_expanded (false)
2672 operator=(const TreeItem &rhs)
2676 m_parent = rhs.m_parent;
2677 m_delegate = rhs.m_delegate;
2678 m_user_data = rhs.m_user_data;
2679 m_identifier = rhs.m_identifier;
2680 m_row_idx = rhs.m_row_idx;
2681 m_children = rhs.m_children;
2682 m_might_have_children = rhs.m_might_have_children;
2683 m_is_expanded = rhs.m_is_expanded;
2692 return 1 + m_parent->GetDepth();
2697 GetRowIndex () const
2709 Resize (size_t n, const TreeItem &t)
2711 m_children.resize(n, t);
2715 operator [](size_t i)
2717 return m_children[i];
2721 SetRowIndex (int row_idx)
2723 m_row_idx = row_idx;
2729 m_delegate.TreeDelegateGenerateChildren (*this);
2730 return m_children.size();
2736 m_delegate.TreeDelegateItemSelected(*this);
2740 CalculateRowIndexes (int &row_idx)
2742 SetRowIndex(row_idx);
2745 const bool expanded = IsExpanded();
2747 // The root item must calculate its children,
2748 // or we must calculate the number of children
2749 // if the item is expanded
2750 if (m_parent == nullptr || expanded)
2753 for (auto &item : m_children)
2756 item.CalculateRowIndexes(row_idx);
2758 item.SetRowIndex(-1);
2771 return m_is_expanded;
2777 m_is_expanded = true;
2783 m_is_expanded = false;
2787 Draw (Window &window,
2788 const int first_visible_row,
2789 const uint32_t selected_row_idx,
2793 if (num_rows_left <= 0)
2796 if (m_row_idx >= first_visible_row)
2798 window.MoveCursor(2, row_idx + 1);
2801 m_parent->DrawTreeForChild (window, this, 0);
2803 if (m_might_have_children)
2805 // It we can get UTF8 characters to work we should try to use the "symbol"
2806 // UTF8 string below
2807 // const char *symbol = "";
2808 // if (row.expanded)
2809 // symbol = "\xe2\x96\xbd ";
2811 // symbol = "\xe2\x96\xb7 ";
2812 // window.PutCString (symbol);
2814 // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
2815 // 'v' or '>' character...
2817 // window.PutChar (ACS_DARROW);
2819 // window.PutChar (ACS_RARROW);
2820 // Since we can't find any good looking right arrow/down arrow
2821 // symbols, just use a diamond...
2822 window.PutChar (ACS_DIAMOND);
2823 window.PutChar (ACS_HLINE);
2826 (selected_row_idx == static_cast<size_t>(m_row_idx)) && window.IsActive();
2829 window.AttributeOn(A_REVERSE);
2831 m_delegate.TreeDelegateDrawTreeItem(*this, window);
2834 window.AttributeOff(A_REVERSE);
2839 if (num_rows_left <= 0)
2840 return false; // We are done drawing...
2844 for (auto &item : m_children)
2846 // If we displayed all the rows and item.Draw() returns
2847 // false we are done drawing and can exit this for loop
2848 if (!item.Draw(window, first_visible_row, selected_row_idx, row_idx, num_rows_left))
2852 return num_rows_left >= 0; // Return true if not done drawing yet
2856 DrawTreeForChild (Window &window, TreeItem *child, uint32_t reverse_depth)
2859 m_parent->DrawTreeForChild (window, this, reverse_depth + 1);
2861 if (&m_children.back() == child)
2864 if (reverse_depth == 0)
2866 window.PutChar (ACS_LLCORNER);
2867 window.PutChar (ACS_HLINE);
2871 window.PutChar (' ');
2872 window.PutChar (' ');
2877 if (reverse_depth == 0)
2879 window.PutChar (ACS_LTEE);
2880 window.PutChar (ACS_HLINE);
2884 window.PutChar (ACS_VLINE);
2885 window.PutChar (' ');
2891 GetItemForRowIndex (uint32_t row_idx)
2893 if (static_cast<uint32_t>(m_row_idx) == row_idx)
2895 if (m_children.empty())
2899 for (auto &item : m_children)
2901 TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx);
2902 if (selected_item_ptr)
2903 return selected_item_ptr;
2916 SetUserData (void *user_data)
2918 m_user_data = user_data;
2922 GetIdentifier() const
2924 return m_identifier;
2928 SetIdentifier (uint64_t identifier)
2930 m_identifier = identifier;
2934 SetMightHaveChildren (bool b)
2936 m_might_have_children = b;
2941 TreeDelegate &m_delegate;
2943 uint64_t m_identifier;
2944 int m_row_idx; // Zero based visible row index, -1 if not visible or for the root item
2945 std::vector<TreeItem> m_children;
2946 bool m_might_have_children;
2950 class TreeWindowDelegate : public WindowDelegate
2953 TreeWindowDelegate (Debugger &debugger, const TreeDelegateSP &delegate_sp) :
2954 m_debugger (debugger),
2955 m_delegate_sp (delegate_sp),
2956 m_root(nullptr, *delegate_sp, true),
2957 m_selected_item(nullptr),
2959 m_selected_row_idx (0),
2960 m_first_visible_row (0),
2969 NumVisibleRows () const
2971 return m_max_y - m_min_y;
2975 WindowDelegateDraw (Window &window, bool force) override
2977 ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext());
2978 Process *process = exe_ctx.GetProcessPtr();
2980 bool display_content = false;
2983 StateType state = process->GetState();
2984 if (StateIsStoppedState(state, true))
2986 // We are stopped, so it is ok to
2987 display_content = true;
2989 else if (StateIsRunningState(state))
2991 return true; // Don't do any updating when we are running
2997 m_max_x = window.GetWidth() - 1;
2998 m_max_y = window.GetHeight() - 1;
3001 window.DrawTitleBox (window.GetName());
3003 if (display_content)
3005 const int num_visible_rows = NumVisibleRows();
3007 m_root.CalculateRowIndexes(m_num_rows);
3009 // If we unexpanded while having something selected our
3010 // total number of rows is less than the num visible rows,
3011 // then make sure we show all the rows by setting the first
3012 // visible row accordingly.
3013 if (m_first_visible_row > 0 && m_num_rows < num_visible_rows)
3014 m_first_visible_row = 0;
3016 // Make sure the selected row is always visible
3017 if (m_selected_row_idx < m_first_visible_row)
3018 m_first_visible_row = m_selected_row_idx;
3019 else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
3020 m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
3023 int num_rows_left = num_visible_rows;
3024 m_root.Draw (window, m_first_visible_row, m_selected_row_idx, row_idx, num_rows_left);
3025 // Get the selected row
3026 m_selected_item = m_root.GetItemForRowIndex (m_selected_row_idx);
3030 m_selected_item = nullptr;
3033 window.DeferredRefresh();
3035 return true; // Drawing handled
3039 WindowDelegateGetHelpText () override
3041 return "Thread window keyboard shortcuts:";
3045 WindowDelegateGetKeyHelp () override
3047 static curses::KeyHelp g_source_view_key_help[] = {
3048 { KEY_UP, "Select previous item" },
3049 { KEY_DOWN, "Select next item" },
3050 { KEY_RIGHT, "Expand the selected item" },
3051 { KEY_LEFT, "Unexpand the selected item or select parent if not expanded" },
3052 { KEY_PPAGE, "Page up" },
3053 { KEY_NPAGE, "Page down" },
3054 { 'h', "Show help dialog" },
3055 { ' ', "Toggle item expansion" },
3057 { '.', "Page down" },
3060 return g_source_view_key_help;
3064 WindowDelegateHandleChar (Window &window, int c) override
3071 if (m_first_visible_row > 0)
3073 if (m_first_visible_row > m_max_y)
3074 m_first_visible_row -= m_max_y;
3076 m_first_visible_row = 0;
3077 m_selected_row_idx = m_first_visible_row;
3078 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3079 if (m_selected_item)
3080 m_selected_item->ItemWasSelected ();
3087 if (m_num_rows > m_max_y)
3089 if (m_first_visible_row + m_max_y < m_num_rows)
3091 m_first_visible_row += m_max_y;
3092 m_selected_row_idx = m_first_visible_row;
3093 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3094 if (m_selected_item)
3095 m_selected_item->ItemWasSelected ();
3101 if (m_selected_row_idx > 0)
3103 --m_selected_row_idx;
3104 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3105 if (m_selected_item)
3106 m_selected_item->ItemWasSelected ();
3111 if (m_selected_row_idx + 1 < m_num_rows)
3113 ++m_selected_row_idx;
3114 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3115 if (m_selected_item)
3116 m_selected_item->ItemWasSelected ();
3121 if (m_selected_item)
3123 if (!m_selected_item->IsExpanded())
3124 m_selected_item->Expand();
3129 if (m_selected_item)
3131 if (m_selected_item->IsExpanded())
3132 m_selected_item->Unexpand();
3133 else if (m_selected_item->GetParent())
3135 m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex();
3136 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3137 if (m_selected_item)
3138 m_selected_item->ItemWasSelected ();
3144 // Toggle expansion state when SPACE is pressed
3145 if (m_selected_item)
3147 if (m_selected_item->IsExpanded())
3148 m_selected_item->Unexpand();
3150 m_selected_item->Expand();
3155 window.CreateHelpSubwindow ();
3161 return eKeyNotHandled;
3165 Debugger &m_debugger;
3166 TreeDelegateSP m_delegate_sp;
3168 TreeItem *m_selected_item;
3170 int m_selected_row_idx;
3171 int m_first_visible_row;
3178 class FrameTreeDelegate : public TreeDelegate
3181 FrameTreeDelegate () :
3184 FormatEntity::Parse ("frame #${frame.index}: {${function.name}${function.pc-offset}}}",
3188 ~FrameTreeDelegate() override = default;
3191 TreeDelegateDrawTreeItem (TreeItem &item, Window &window) override
3193 Thread* thread = (Thread*)item.GetUserData();
3196 const uint64_t frame_idx = item.GetIdentifier();
3197 StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_idx);
3201 const SymbolContext &sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
3202 ExecutionContext exe_ctx (frame_sp);
3203 if (FormatEntity::Format(m_format, strm, &sc, &exe_ctx, nullptr, nullptr, false, false))
3206 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
3213 TreeDelegateGenerateChildren (TreeItem &item) override
3215 // No children for frames yet...
3219 TreeDelegateItemSelected (TreeItem &item) override
3221 Thread* thread = (Thread*)item.GetUserData();
3224 thread->GetProcess()->GetThreadList().SetSelectedThreadByID(thread->GetID());
3225 const uint64_t frame_idx = item.GetIdentifier();
3226 thread->SetSelectedFrameByIndex(frame_idx);
3233 FormatEntity::Entry m_format;
3236 class ThreadTreeDelegate : public TreeDelegate
3239 ThreadTreeDelegate (Debugger &debugger) :
3241 m_debugger (debugger),
3242 m_tid (LLDB_INVALID_THREAD_ID),
3243 m_stop_id (UINT32_MAX)
3245 FormatEntity::Parse ("thread #${thread.index}: tid = ${thread.id}{, stop reason = ${thread.stop-reason}}",
3249 ~ThreadTreeDelegate() override = default;
3254 return m_debugger.GetCommandInterpreter().GetExecutionContext().GetProcessSP();
3258 GetThread (const TreeItem &item)
3260 ProcessSP process_sp = GetProcess ();
3262 return process_sp->GetThreadList().FindThreadByID(item.GetIdentifier());
3267 TreeDelegateDrawTreeItem (TreeItem &item, Window &window) override
3269 ThreadSP thread_sp = GetThread (item);
3273 ExecutionContext exe_ctx (thread_sp);
3274 if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr, nullptr, false, false))
3277 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
3283 TreeDelegateGenerateChildren (TreeItem &item) override
3285 ProcessSP process_sp = GetProcess ();
3286 if (process_sp && process_sp->IsAlive())
3288 StateType state = process_sp->GetState();
3289 if (StateIsStoppedState(state, true))
3291 ThreadSP thread_sp = GetThread (item);
3294 if (m_stop_id == process_sp->GetStopID() && thread_sp->GetID() == m_tid)
3295 return; // Children are already up to date
3296 if (!m_frame_delegate_sp)
3298 // Always expand the thread item the first time we show it
3299 m_frame_delegate_sp.reset (new FrameTreeDelegate());
3302 m_stop_id = process_sp->GetStopID();
3303 m_tid = thread_sp->GetID();
3305 TreeItem t (&item, *m_frame_delegate_sp, false);
3306 size_t num_frames = thread_sp->GetStackFrameCount();
3307 item.Resize (num_frames, t);
3308 for (size_t i = 0; i < num_frames; ++i)
3310 item[i].SetUserData(thread_sp.get());
3311 item[i].SetIdentifier(i);
3317 item.ClearChildren();
3321 TreeDelegateItemSelected (TreeItem &item) override
3323 ProcessSP process_sp = GetProcess ();
3324 if (process_sp && process_sp->IsAlive())
3326 StateType state = process_sp->GetState();
3327 if (StateIsStoppedState(state, true))
3329 ThreadSP thread_sp = GetThread (item);
3332 ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList();
3333 std::lock_guard<std::recursive_mutex> guard(thread_list.GetMutex());
3334 ThreadSP selected_thread_sp = thread_list.GetSelectedThread();
3335 if (selected_thread_sp->GetID() != thread_sp->GetID())
3337 thread_list.SetSelectedThreadByID(thread_sp->GetID());
3347 Debugger &m_debugger;
3348 std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp;
3349 lldb::user_id_t m_tid;
3351 FormatEntity::Entry m_format;
3354 class ThreadsTreeDelegate : public TreeDelegate
3357 ThreadsTreeDelegate (Debugger &debugger) :
3359 m_thread_delegate_sp (),
3360 m_debugger (debugger),
3361 m_stop_id (UINT32_MAX)
3363 FormatEntity::Parse("process ${process.id}{, name = ${process.name}}",
3367 ~ThreadsTreeDelegate() override = default;
3372 return m_debugger.GetCommandInterpreter().GetExecutionContext().GetProcessSP();
3376 TreeDelegateDrawTreeItem (TreeItem &item, Window &window) override
3378 ProcessSP process_sp = GetProcess ();
3379 if (process_sp && process_sp->IsAlive())
3382 ExecutionContext exe_ctx (process_sp);
3383 if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr, nullptr, false, false))
3386 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
3392 TreeDelegateGenerateChildren (TreeItem &item) override
3394 ProcessSP process_sp = GetProcess ();
3395 if (process_sp && process_sp->IsAlive())
3397 StateType state = process_sp->GetState();
3398 if (StateIsStoppedState(state, true))
3400 const uint32_t stop_id = process_sp->GetStopID();
3401 if (m_stop_id == stop_id)
3402 return; // Children are already up to date
3404 m_stop_id = stop_id;
3406 if (!m_thread_delegate_sp)
3408 // Always expand the thread item the first time we show it
3410 m_thread_delegate_sp.reset (new ThreadTreeDelegate(m_debugger));
3413 TreeItem t (&item, *m_thread_delegate_sp, false);
3414 ThreadList &threads = process_sp->GetThreadList();
3415 std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
3416 size_t num_threads = threads.GetSize();
3417 item.Resize (num_threads, t);
3418 for (size_t i = 0; i < num_threads; ++i)
3420 item[i].SetIdentifier(threads.GetThreadAtIndex(i)->GetID());
3421 item[i].SetMightHaveChildren(true);
3426 item.ClearChildren();
3430 TreeDelegateItemSelected (TreeItem &item) override
3436 std::shared_ptr<ThreadTreeDelegate> m_thread_delegate_sp;
3437 Debugger &m_debugger;
3439 FormatEntity::Entry m_format;
3442 class ValueObjectListDelegate : public WindowDelegate
3445 ValueObjectListDelegate () :
3448 m_selected_row(nullptr),
3449 m_selected_row_idx (0),
3450 m_first_visible_row (0),
3457 ValueObjectListDelegate (ValueObjectList &valobj_list) :
3458 m_valobj_list (valobj_list),
3460 m_selected_row(nullptr),
3461 m_selected_row_idx (0),
3462 m_first_visible_row (0),
3467 SetValues (valobj_list);
3470 ~ValueObjectListDelegate() override = default;
3473 SetValues (ValueObjectList &valobj_list)
3475 m_selected_row = nullptr;
3476 m_selected_row_idx = 0;
3477 m_first_visible_row = 0;
3480 m_valobj_list = valobj_list;
3481 const size_t num_values = m_valobj_list.GetSize();
3482 for (size_t i = 0; i < num_values; ++i)
3483 m_rows.push_back(Row(m_valobj_list.GetValueObjectAtIndex(i), nullptr));
3487 WindowDelegateDraw (Window &window, bool force) override
3492 m_max_x = window.GetWidth() - 1;
3493 m_max_y = window.GetHeight() - 1;
3496 window.DrawTitleBox (window.GetName());
3498 const int num_visible_rows = NumVisibleRows();
3499 const int num_rows = CalculateTotalNumberRows (m_rows);
3501 // If we unexpanded while having something selected our
3502 // total number of rows is less than the num visible rows,
3503 // then make sure we show all the rows by setting the first
3504 // visible row accordingly.
3505 if (m_first_visible_row > 0 && num_rows < num_visible_rows)
3506 m_first_visible_row = 0;
3508 // Make sure the selected row is always visible
3509 if (m_selected_row_idx < m_first_visible_row)
3510 m_first_visible_row = m_selected_row_idx;
3511 else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
3512 m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
3514 DisplayRows (window, m_rows, g_options);
3516 window.DeferredRefresh();
3518 // Get the selected row
3519 m_selected_row = GetRowForRowIndex (m_selected_row_idx);
3520 // Keep the cursor on the selected row so the highlight and the cursor
3521 // are always on the same line
3523 window.MoveCursor (m_selected_row->x,
3526 return true; // Drawing handled
3530 WindowDelegateGetKeyHelp () override
3532 static curses::KeyHelp g_source_view_key_help[] = {
3533 { KEY_UP, "Select previous item" },
3534 { KEY_DOWN, "Select next item" },
3535 { KEY_RIGHT, "Expand selected item" },
3536 { KEY_LEFT, "Unexpand selected item or select parent if not expanded" },
3537 { KEY_PPAGE, "Page up" },
3538 { KEY_NPAGE, "Page down" },
3539 { 'A', "Format as annotated address" },
3540 { 'b', "Format as binary" },
3541 { 'B', "Format as hex bytes with ASCII" },
3542 { 'c', "Format as character" },
3543 { 'd', "Format as a signed integer" },
3544 { 'D', "Format selected value using the default format for the type" },
3545 { 'f', "Format as float" },
3546 { 'h', "Show help dialog" },
3547 { 'i', "Format as instructions" },
3548 { 'o', "Format as octal" },
3549 { 'p', "Format as pointer" },
3550 { 's', "Format as C string" },
3551 { 't', "Toggle showing/hiding type names" },
3552 { 'u', "Format as an unsigned integer" },
3553 { 'x', "Format as hex" },
3554 { 'X', "Format as uppercase hex" },
3555 { ' ', "Toggle item expansion" },
3557 { '.', "Page down" },
3560 return g_source_view_key_help;
3564 WindowDelegateHandleChar (Window &window, int c) override
3582 // Change the format for the currently selected item
3584 m_selected_row->valobj->SetFormat (FormatForChar (c));
3588 // Toggle showing type names
3589 g_options.show_types = !g_options.show_types;
3595 if (m_first_visible_row > 0)
3597 if (static_cast<int>(m_first_visible_row) > m_max_y)
3598 m_first_visible_row -= m_max_y;
3600 m_first_visible_row = 0;
3601 m_selected_row_idx = m_first_visible_row;
3608 if (m_num_rows > static_cast<size_t>(m_max_y))
3610 if (m_first_visible_row + m_max_y < m_num_rows)
3612 m_first_visible_row += m_max_y;
3613 m_selected_row_idx = m_first_visible_row;
3619 if (m_selected_row_idx > 0)
3620 --m_selected_row_idx;
3624 if (m_selected_row_idx + 1 < m_num_rows)
3625 ++m_selected_row_idx;
3631 if (!m_selected_row->expanded)
3632 m_selected_row->Expand();
3639 if (m_selected_row->expanded)
3640 m_selected_row->Unexpand();
3641 else if (m_selected_row->parent)
3642 m_selected_row_idx = m_selected_row->parent->row_idx;
3647 // Toggle expansion state when SPACE is pressed
3650 if (m_selected_row->expanded)
3651 m_selected_row->Unexpand();
3653 m_selected_row->Expand();
3658 window.CreateHelpSubwindow ();
3664 return eKeyNotHandled;
3668 ValueObjectList m_valobj_list;
3669 std::vector<Row> m_rows;
3670 Row *m_selected_row;
3671 uint32_t m_selected_row_idx;
3672 uint32_t m_first_visible_row;
3673 uint32_t m_num_rows;
3680 FormatForChar (int c)
3684 case 'x': return eFormatHex;
3685 case 'X': return eFormatHexUppercase;
3686 case 'o': return eFormatOctal;
3687 case 's': return eFormatCString;
3688 case 'u': return eFormatUnsigned;
3689 case 'd': return eFormatDecimal;
3690 case 'D': return eFormatDefault;
3691 case 'i': return eFormatInstruction;
3692 case 'A': return eFormatAddressInfo;
3693 case 'p': return eFormatPointer;
3694 case 'c': return eFormatChar;
3695 case 'b': return eFormatBinary;
3696 case 'B': return eFormatBytesWithASCII;
3697 case 'f': return eFormatFloat;
3699 return eFormatDefault;
3703 DisplayRowObject (Window &window,
3705 DisplayOptions &options,
3709 ValueObject *valobj = row.valobj.get();
3711 if (valobj == nullptr)
3714 const char *type_name = options.show_types ? valobj->GetTypeName().GetCString() : nullptr;
3715 const char *name = valobj->GetName().GetCString();
3716 const char *value = valobj->GetValueAsCString ();
3717 const char *summary = valobj->GetSummaryAsCString ();
3719 window.MoveCursor (row.x, row.y);
3721 row.DrawTree (window);
3724 window.AttributeOn(A_REVERSE);
3726 if (type_name && type_name[0])
3727 window.Printf ("(%s) ", type_name);
3729 if (name && name[0])
3730 window.PutCString(name);
3732 attr_t changd_attr = 0;
3733 if (valobj->GetValueDidChange())
3734 changd_attr = COLOR_PAIR(5) | A_BOLD;
3736 if (value && value[0])
3738 window.PutCString(" = ");
3740 window.AttributeOn(changd_attr);
3741 window.PutCString (value);
3743 window.AttributeOff(changd_attr);
3746 if (summary && summary[0])
3748 window.PutChar(' ');
3750 window.AttributeOn(changd_attr);
3751 window.PutCString(summary);
3753 window.AttributeOff(changd_attr);
3757 window.AttributeOff (A_REVERSE);
3763 DisplayRows (Window &window,
3764 std::vector<Row> &rows,
3765 DisplayOptions &options)
3770 bool window_is_active = window.IsActive();
3771 for (auto &row : rows)
3773 const bool last_child = row.parent && &rows[rows.size()-1] == &row;
3774 // Save the row index in each Row structure
3775 row.row_idx = m_num_rows;
3776 if ((m_num_rows >= m_first_visible_row) &&
3777 ((m_num_rows - m_first_visible_row) < static_cast<size_t>(NumVisibleRows())))
3780 row.y = m_num_rows - m_first_visible_row + 1;
3781 if (DisplayRowObject (window,
3784 window_is_active && m_num_rows == m_selected_row_idx,
3802 if (row.expanded && !row.children.empty())
3804 DisplayRows (window,
3812 CalculateTotalNumberRows (const std::vector<Row> &rows)
3815 for (const auto &row : rows)
3819 row_count += CalculateTotalNumberRows(row.children);
3825 GetRowForRowIndexImpl (std::vector<Row> &rows, size_t &row_index)
3827 for (auto &row : rows)
3834 if (row.expanded && !row.children.empty())
3836 Row *result = GetRowForRowIndexImpl (row.children, row_index);
3846 GetRowForRowIndex (size_t row_index)
3848 return GetRowForRowIndexImpl (m_rows, row_index);
3852 NumVisibleRows () const
3854 return m_max_y - m_min_y;
3857 static DisplayOptions g_options;
3860 class FrameVariablesWindowDelegate : public ValueObjectListDelegate
3863 FrameVariablesWindowDelegate (Debugger &debugger) :
3864 ValueObjectListDelegate (),
3865 m_debugger (debugger),
3866 m_frame_block(nullptr)
3870 ~FrameVariablesWindowDelegate() override = default;
3873 WindowDelegateGetHelpText () override
3875 return "Frame variable window keyboard shortcuts:";
3879 WindowDelegateDraw (Window &window, bool force) override
3881 ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext());
3882 Process *process = exe_ctx.GetProcessPtr();
3883 Block *frame_block = nullptr;
3884 StackFrame *frame = nullptr;
3888 StateType state = process->GetState();
3889 if (StateIsStoppedState(state, true))
3891 frame = exe_ctx.GetFramePtr();
3893 frame_block = frame->GetFrameBlock ();
3895 else if (StateIsRunningState(state))
3897 return true; // Don't do any updating when we are running
3901 ValueObjectList local_values;
3904 // Only update the variables if they have changed
3905 if (m_frame_block != frame_block)
3907 m_frame_block = frame_block;
3909 VariableList *locals = frame->GetVariableList(true);
3912 const DynamicValueType use_dynamic = eDynamicDontRunTarget;
3913 const size_t num_locals = locals->GetSize();
3914 for (size_t i = 0; i < num_locals; ++i)
3916 ValueObjectSP value_sp = frame->GetValueObjectForFrameVariable (locals->GetVariableAtIndex(i), use_dynamic);
3919 ValueObjectSP synthetic_value_sp = value_sp->GetSyntheticValue();
3920 if (synthetic_value_sp)
3921 local_values.Append(synthetic_value_sp);
3923 local_values.Append(value_sp);
3926 // Update the values
3927 SetValues(local_values);
3933 m_frame_block = nullptr;
3934 // Update the values with an empty list if there is no frame
3935 SetValues(local_values);
3938 return ValueObjectListDelegate::WindowDelegateDraw (window, force);
3942 Debugger &m_debugger;
3943 Block *m_frame_block;
3946 class RegistersWindowDelegate : public ValueObjectListDelegate
3949 RegistersWindowDelegate (Debugger &debugger) :
3950 ValueObjectListDelegate (),
3951 m_debugger (debugger)
3955 ~RegistersWindowDelegate() override = default;
3958 WindowDelegateGetHelpText () override
3960 return "Register window keyboard shortcuts:";
3964 WindowDelegateDraw (Window &window, bool force) override
3966 ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext());
3967 StackFrame *frame = exe_ctx.GetFramePtr();
3969 ValueObjectList value_list;
3972 if (frame->GetStackID() != m_stack_id)
3974 m_stack_id = frame->GetStackID();
3975 RegisterContextSP reg_ctx (frame->GetRegisterContext());
3978 const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
3979 for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx)
3981 value_list.Append(ValueObjectRegisterSet::Create (frame, reg_ctx, set_idx));
3984 SetValues(value_list);
3989 Process *process = exe_ctx.GetProcessPtr();
3990 if (process && process->IsAlive())
3991 return true; // Don't do any updating if we are running
3994 // Update the values with an empty list if there
3995 // is no process or the process isn't alive anymore
3996 SetValues(value_list);
3999 return ValueObjectListDelegate::WindowDelegateDraw (window, force);
4003 Debugger &m_debugger;
4008 CursesKeyToCString (int ch)
4010 static char g_desc[32];
4011 if (ch >= KEY_F0 && ch < KEY_F0 + 64)
4013 snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0);
4018 case KEY_DOWN: return "down";
4019 case KEY_UP: return "up";
4020 case KEY_LEFT: return "left";
4021 case KEY_RIGHT: return "right";
4022 case KEY_HOME: return "home";
4023 case KEY_BACKSPACE: return "backspace";
4024 case KEY_DL: return "delete-line";
4025 case KEY_IL: return "insert-line";
4026 case KEY_DC: return "delete-char";
4027 case KEY_IC: return "insert-char";
4028 case KEY_CLEAR: return "clear";
4029 case KEY_EOS: return "clear-to-eos";
4030 case KEY_EOL: return "clear-to-eol";
4031 case KEY_SF: return "scroll-forward";
4032 case KEY_SR: return "scroll-backward";
4033 case KEY_NPAGE: return "page-down";
4034 case KEY_PPAGE: return "page-up";
4035 case KEY_STAB: return "set-tab";
4036 case KEY_CTAB: return "clear-tab";
4037 case KEY_CATAB: return "clear-all-tabs";
4038 case KEY_ENTER: return "enter";
4039 case KEY_PRINT: return "print";
4040 case KEY_LL: return "lower-left key";
4041 case KEY_A1: return "upper left of keypad";
4042 case KEY_A3: return "upper right of keypad";
4043 case KEY_B2: return "center of keypad";
4044 case KEY_C1: return "lower left of keypad";
4045 case KEY_C3: return "lower right of keypad";
4046 case KEY_BTAB: return "back-tab key";
4047 case KEY_BEG: return "begin key";
4048 case KEY_CANCEL: return "cancel key";
4049 case KEY_CLOSE: return "close key";
4050 case KEY_COMMAND: return "command key";
4051 case KEY_COPY: return "copy key";
4052 case KEY_CREATE: return "create key";
4053 case KEY_END: return "end key";
4054 case KEY_EXIT: return "exit key";
4055 case KEY_FIND: return "find key";
4056 case KEY_HELP: return "help key";
4057 case KEY_MARK: return "mark key";
4058 case KEY_MESSAGE: return "message key";
4059 case KEY_MOVE: return "move key";
4060 case KEY_NEXT: return "next key";
4061 case KEY_OPEN: return "open key";
4062 case KEY_OPTIONS: return "options key";
4063 case KEY_PREVIOUS: return "previous key";
4064 case KEY_REDO: return "redo key";
4065 case KEY_REFERENCE: return "reference key";
4066 case KEY_REFRESH: return "refresh key";
4067 case KEY_REPLACE: return "replace key";
4068 case KEY_RESTART: return "restart key";
4069 case KEY_RESUME: return "resume key";
4070 case KEY_SAVE: return "save key";
4071 case KEY_SBEG: return "shifted begin key";
4072 case KEY_SCANCEL: return "shifted cancel key";
4073 case KEY_SCOMMAND: return "shifted command key";
4074 case KEY_SCOPY: return "shifted copy key";
4075 case KEY_SCREATE: return "shifted create key";
4076 case KEY_SDC: return "shifted delete-character key";
4077 case KEY_SDL: return "shifted delete-line key";
4078 case KEY_SELECT: return "select key";
4079 case KEY_SEND: return "shifted end key";
4080 case KEY_SEOL: return "shifted clear-to-end-of-line key";
4081 case KEY_SEXIT: return "shifted exit key";
4082 case KEY_SFIND: return "shifted find key";
4083 case KEY_SHELP: return "shifted help key";
4084 case KEY_SHOME: return "shifted home key";
4085 case KEY_SIC: return "shifted insert-character key";
4086 case KEY_SLEFT: return "shifted left-arrow key";
4087 case KEY_SMESSAGE: return "shifted message key";
4088 case KEY_SMOVE: return "shifted move key";
4089 case KEY_SNEXT: return "shifted next key";
4090 case KEY_SOPTIONS: return "shifted options key";
4091 case KEY_SPREVIOUS: return "shifted previous key";
4092 case KEY_SPRINT: return "shifted print key";
4093 case KEY_SREDO: return "shifted redo key";
4094 case KEY_SREPLACE: return "shifted replace key";
4095 case KEY_SRIGHT: return "shifted right-arrow key";
4096 case KEY_SRSUME: return "shifted resume key";
4097 case KEY_SSAVE: return "shifted save key";
4098 case KEY_SSUSPEND: return "shifted suspend key";
4099 case KEY_SUNDO: return "shifted undo key";
4100 case KEY_SUSPEND: return "suspend key";
4101 case KEY_UNDO: return "undo key";
4102 case KEY_MOUSE: return "Mouse event has occurred";
4103 case KEY_RESIZE: return "Terminal resize event";
4105 case KEY_EVENT: return "We were interrupted by an event";
4107 case KEY_RETURN: return "return";
4108 case ' ': return "space";
4109 case '\t': return "tab";
4110 case KEY_ESCAPE: return "escape";
4113 snprintf(g_desc, sizeof(g_desc), "%c", ch);
4115 snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch);
4121 HelpDialogDelegate::HelpDialogDelegate (const char *text, KeyHelp *key_help_array) :
4123 m_first_visible_line (0)
4125 if (text && text[0])
4127 m_text.SplitIntoLines(text);
4128 m_text.AppendString("");
4132 for (KeyHelp *key = key_help_array; key->ch; ++key)
4134 StreamString key_description;
4135 key_description.Printf("%10s - %s", CursesKeyToCString(key->ch), key->description);
4136 m_text.AppendString(std::move(key_description.GetString()));
4141 HelpDialogDelegate::~HelpDialogDelegate() = default;
4144 HelpDialogDelegate::WindowDelegateDraw (Window &window, bool force)
4147 const int window_height = window.GetHeight();
4150 const int min_y = y;
4151 const int max_y = window_height - 1 - y;
4152 const size_t num_visible_lines = max_y - min_y + 1;
4153 const size_t num_lines = m_text.GetSize();
4154 const char *bottom_message;
4155 if (num_lines <= num_visible_lines)
4156 bottom_message = "Press any key to exit";
4158 bottom_message = "Use arrows to scroll, any other key to exit";
4159 window.DrawTitleBox(window.GetName(), bottom_message);
4162 window.MoveCursor(x, y);
4163 window.PutCStringTruncated(m_text.GetStringAtIndex(m_first_visible_line + y - min_y), 1);
4170 HelpDialogDelegate::WindowDelegateHandleChar (Window &window, int key)
4173 const size_t num_lines = m_text.GetSize();
4174 const size_t num_visible_lines = window.GetHeight() - 2;
4176 if (num_lines <= num_visible_lines)
4179 // If we have all lines visible and don't need scrolling, then any
4180 // key press will cause us to exit
4187 if (m_first_visible_line > 0)
4188 --m_first_visible_line;
4192 if (m_first_visible_line + num_visible_lines < num_lines)
4193 ++m_first_visible_line;
4198 if (m_first_visible_line > 0)
4200 if (static_cast<size_t>(m_first_visible_line) >= num_visible_lines)
4201 m_first_visible_line -= num_visible_lines;
4203 m_first_visible_line = 0;
4209 if (m_first_visible_line + num_visible_lines < num_lines)
4211 m_first_visible_line += num_visible_lines;
4212 if (static_cast<size_t>(m_first_visible_line) > num_lines)
4213 m_first_visible_line = num_lines - num_visible_lines;
4223 window.GetParent()->RemoveSubWindow(&window);
4227 class ApplicationDelegate :
4228 public WindowDelegate,
4238 eMenuID_TargetCreate,
4239 eMenuID_TargetDelete,
4242 eMenuID_ProcessAttach,
4243 eMenuID_ProcessDetach,
4244 eMenuID_ProcessLaunch,
4245 eMenuID_ProcessContinue,
4246 eMenuID_ProcessHalt,
4247 eMenuID_ProcessKill,
4250 eMenuID_ThreadStepIn,
4251 eMenuID_ThreadStepOver,
4252 eMenuID_ThreadStepOut,
4255 eMenuID_ViewBacktrace,
4256 eMenuID_ViewRegisters,
4258 eMenuID_ViewVariables,
4264 ApplicationDelegate (Application &app, Debugger &debugger) :
4268 m_debugger (debugger)
4272 ~ApplicationDelegate() override = default;
4275 WindowDelegateDraw (Window &window, bool force) override
4277 return false; // Drawing not handled, let standard window drawing happen
4281 WindowDelegateHandleChar (Window &window, int key) override
4286 window.SelectNextWindowAsActive();
4290 window.CreateHelpSubwindow();
4294 return eQuitApplication;
4299 return eKeyNotHandled;
4303 WindowDelegateGetHelpText () override
4305 return "Welcome to the LLDB curses GUI.\n\n"
4306 "Press the TAB key to change the selected view.\n"
4307 "Each view has its own keyboard shortcuts, press 'h' to open a dialog to display them.\n\n"
4308 "Common key bindings for all views:";
4312 WindowDelegateGetKeyHelp () override
4314 static curses::KeyHelp g_source_view_key_help[] = {
4315 { '\t', "Select next view" },
4316 { 'h', "Show help dialog with view specific key bindings" },
4318 { '.', "Page down" },
4319 { KEY_UP, "Select previous" },
4320 { KEY_DOWN, "Select next" },
4321 { KEY_LEFT, "Unexpand or select parent" },
4322 { KEY_RIGHT, "Expand" },
4323 { KEY_PPAGE, "Page up" },
4324 { KEY_NPAGE, "Page down" },
4327 return g_source_view_key_help;
4331 MenuDelegateAction (Menu &menu) override
4333 switch (menu.GetIdentifier())
4335 case eMenuID_ThreadStepIn:
4337 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4338 if (exe_ctx.HasThreadScope())
4340 Process *process = exe_ctx.GetProcessPtr();
4341 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4342 exe_ctx.GetThreadRef().StepIn(true);
4345 return MenuActionResult::Handled;
4347 case eMenuID_ThreadStepOut:
4349 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4350 if (exe_ctx.HasThreadScope())
4352 Process *process = exe_ctx.GetProcessPtr();
4353 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4354 exe_ctx.GetThreadRef().StepOut();
4357 return MenuActionResult::Handled;
4359 case eMenuID_ThreadStepOver:
4361 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4362 if (exe_ctx.HasThreadScope())
4364 Process *process = exe_ctx.GetProcessPtr();
4365 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4366 exe_ctx.GetThreadRef().StepOver(true);
4369 return MenuActionResult::Handled;
4371 case eMenuID_ProcessContinue:
4373 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4374 if (exe_ctx.HasProcessScope())
4376 Process *process = exe_ctx.GetProcessPtr();
4377 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4381 return MenuActionResult::Handled;
4383 case eMenuID_ProcessKill:
4385 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4386 if (exe_ctx.HasProcessScope())
4388 Process *process = exe_ctx.GetProcessPtr();
4389 if (process && process->IsAlive())
4390 process->Destroy(false);
4393 return MenuActionResult::Handled;
4395 case eMenuID_ProcessHalt:
4397 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4398 if (exe_ctx.HasProcessScope())
4400 Process *process = exe_ctx.GetProcessPtr();
4401 if (process && process->IsAlive())
4405 return MenuActionResult::Handled;
4407 case eMenuID_ProcessDetach:
4409 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4410 if (exe_ctx.HasProcessScope())
4412 Process *process = exe_ctx.GetProcessPtr();
4413 if (process && process->IsAlive())
4414 process->Detach(false);
4417 return MenuActionResult::Handled;
4419 case eMenuID_Process:
4421 // Populate the menu with all of the threads if the process is stopped when
4422 // the Process menu gets selected and is about to display its submenu.
4423 Menus &submenus = menu.GetSubmenus();
4424 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4425 Process *process = exe_ctx.GetProcessPtr();
4426 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4428 if (submenus.size() == 7)
4429 menu.AddSubmenu (MenuSP (new Menu(Menu::Type::Separator)));
4430 else if (submenus.size() > 8)
4431 submenus.erase (submenus.begin() + 8, submenus.end());
4433 ThreadList &threads = process->GetThreadList();
4434 std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
4435 size_t num_threads = threads.GetSize();
4436 for (size_t i = 0; i < num_threads; ++i)
4438 ThreadSP thread_sp = threads.GetThreadAtIndex(i);
4439 char menu_char = '\0';
4441 menu_char = '1' + i;
4442 StreamString thread_menu_title;
4443 thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID());
4444 const char *thread_name = thread_sp->GetName();
4445 if (thread_name && thread_name[0])
4446 thread_menu_title.Printf (" %s", thread_name);
4449 const char *queue_name = thread_sp->GetQueueName();
4450 if (queue_name && queue_name[0])
4451 thread_menu_title.Printf (" %s", queue_name);
4453 menu.AddSubmenu(MenuSP(new Menu(thread_menu_title.GetString().c_str(), nullptr, menu_char, thread_sp->GetID())));
4456 else if (submenus.size() > 7)
4458 // Remove the separator and any other thread submenu items
4459 // that were previously added
4460 submenus.erase (submenus.begin() + 7, submenus.end());
4462 // Since we are adding and removing items we need to recalculate the name lengths
4463 menu.RecalculateNameLengths();
4465 return MenuActionResult::Handled;
4467 case eMenuID_ViewVariables:
4469 WindowSP main_window_sp = m_app.GetMainWindow();
4470 WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
4471 WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
4472 WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
4473 const Rect source_bounds = source_window_sp->GetBounds();
4475 if (variables_window_sp)
4477 const Rect variables_bounds = variables_window_sp->GetBounds();
4479 main_window_sp->RemoveSubWindow(variables_window_sp.get());
4481 if (registers_window_sp)
4483 // We have a registers window, so give all the area back to the registers window
4484 Rect registers_bounds = variables_bounds;
4485 registers_bounds.size.width = source_bounds.size.width;
4486 registers_window_sp->SetBounds(registers_bounds);
4490 // We have no registers window showing so give the bottom
4491 // area back to the source view
4492 source_window_sp->Resize (source_bounds.size.width,
4493 source_bounds.size.height + variables_bounds.size.height);
4498 Rect new_variables_rect;
4499 if (registers_window_sp)
4501 // We have a registers window so split the area of the registers
4502 // window into two columns where the left hand side will be the
4503 // variables and the right hand side will be the registers
4504 const Rect variables_bounds = registers_window_sp->GetBounds();
4505 Rect new_registers_rect;
4506 variables_bounds.VerticalSplitPercentage (0.50, new_variables_rect, new_registers_rect);
4507 registers_window_sp->SetBounds (new_registers_rect);
4511 // No variables window, grab the bottom part of the source window
4512 Rect new_source_rect;
4513 source_bounds.HorizontalSplitPercentage (0.70, new_source_rect, new_variables_rect);
4514 source_window_sp->SetBounds (new_source_rect);
4516 WindowSP new_window_sp = main_window_sp->CreateSubWindow ("Variables",
4519 new_window_sp->SetDelegate (WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
4523 return MenuActionResult::Handled;
4525 case eMenuID_ViewRegisters:
4527 WindowSP main_window_sp = m_app.GetMainWindow();
4528 WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
4529 WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
4530 WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
4531 const Rect source_bounds = source_window_sp->GetBounds();
4533 if (registers_window_sp)
4535 if (variables_window_sp)
4537 const Rect variables_bounds = variables_window_sp->GetBounds();
4539 // We have a variables window, so give all the area back to the variables window
4540 variables_window_sp->Resize (variables_bounds.size.width + registers_window_sp->GetWidth(),
4541 variables_bounds.size.height);
4545 // We have no variables window showing so give the bottom
4546 // area back to the source view
4547 source_window_sp->Resize (source_bounds.size.width,
4548 source_bounds.size.height + registers_window_sp->GetHeight());
4550 main_window_sp->RemoveSubWindow(registers_window_sp.get());
4555 if (variables_window_sp)
4557 // We have a variables window, split it into two columns
4558 // where the left hand side will be the variables and the
4559 // right hand side will be the registers
4560 const Rect variables_bounds = variables_window_sp->GetBounds();
4562 variables_bounds.VerticalSplitPercentage (0.50, new_vars_rect, new_regs_rect);
4563 variables_window_sp->SetBounds (new_vars_rect);
4567 // No registers window, grab the bottom part of the source window
4568 Rect new_source_rect;
4569 source_bounds.HorizontalSplitPercentage (0.70, new_source_rect, new_regs_rect);
4570 source_window_sp->SetBounds (new_source_rect);
4572 WindowSP new_window_sp = main_window_sp->CreateSubWindow ("Registers",
4575 new_window_sp->SetDelegate (WindowDelegateSP(new RegistersWindowDelegate(m_debugger)));
4579 return MenuActionResult::Handled;
4581 case eMenuID_HelpGUIHelp:
4582 m_app.GetMainWindow ()->CreateHelpSubwindow();
4583 return MenuActionResult::Handled;
4589 return MenuActionResult::NotHandled;
4593 Debugger &m_debugger;
4596 class StatusBarWindowDelegate : public WindowDelegate
4599 StatusBarWindowDelegate (Debugger &debugger) :
4600 m_debugger (debugger)
4602 FormatEntity::Parse("Thread: ${thread.id%tid}",
4606 ~StatusBarWindowDelegate() override = default;
4609 WindowDelegateDraw (Window &window, bool force) override
4611 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4612 Process *process = exe_ctx.GetProcessPtr();
4613 Thread *thread = exe_ctx.GetThreadPtr();
4614 StackFrame *frame = exe_ctx.GetFramePtr();
4616 window.SetBackground(2);
4617 window.MoveCursor (0, 0);
4620 const StateType state = process->GetState();
4621 window.Printf ("Process: %5" PRIu64 " %10s", process->GetID(), StateAsCString(state));
4623 if (StateIsStoppedState(state, true))
4626 if (thread && FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr, nullptr, false, false))
4628 window.MoveCursor (40, 0);
4629 window.PutCStringTruncated(strm.GetString().c_str(), 1);
4632 window.MoveCursor (60, 0);
4634 window.Printf ("Frame: %3u PC = 0x%16.16" PRIx64, frame->GetFrameIndex(), frame->GetFrameCodeAddress().GetOpcodeLoadAddress (exe_ctx.GetTargetPtr()));
4636 else if (state == eStateExited)
4638 const char *exit_desc = process->GetExitDescription();
4639 const int exit_status = process->GetExitStatus();
4640 if (exit_desc && exit_desc[0])
4641 window.Printf (" with status = %i (%s)", exit_status, exit_desc);
4643 window.Printf (" with status = %i", exit_status);
4646 window.DeferredRefresh();
4651 Debugger &m_debugger;
4652 FormatEntity::Entry m_format;
4655 class SourceFileWindowDelegate : public WindowDelegate
4658 SourceFileWindowDelegate (Debugger &debugger) :
4660 m_debugger (debugger),
4663 m_disassembly_scope(nullptr),
4664 m_disassembly_sp (),
4665 m_disassembly_range (),
4668 m_selected_line (0),
4671 m_frame_idx (UINT32_MAX),
4672 m_first_visible_line (0),
4680 ~SourceFileWindowDelegate() override = default;
4683 Update (const SymbolContext &sc)
4689 NumVisibleLines () const
4691 return m_max_y - m_min_y;
4695 WindowDelegateGetHelpText () override
4697 return "Source/Disassembly window keyboard shortcuts:";
4701 WindowDelegateGetKeyHelp () override
4703 static curses::KeyHelp g_source_view_key_help[] = {
4704 { KEY_RETURN, "Run to selected line with one shot breakpoint" },
4705 { KEY_UP, "Select previous source line" },
4706 { KEY_DOWN, "Select next source line" },
4707 { KEY_PPAGE, "Page up" },
4708 { KEY_NPAGE, "Page down" },
4709 { 'b', "Set breakpoint on selected source/disassembly line" },
4710 { 'c', "Continue process" },
4711 { 'd', "Detach and resume process" },
4712 { 'D', "Detach with process suspended" },
4713 { 'h', "Show help dialog" },
4714 { 'k', "Kill process" },
4715 { 'n', "Step over (source line)" },
4716 { 'N', "Step over (single instruction)" },
4717 { 'o', "Step out" },
4718 { 's', "Step in (source line)" },
4719 { 'S', "Step in (single instruction)" },
4721 { '.', "Page down" },
4724 return g_source_view_key_help;
4728 WindowDelegateDraw (Window &window, bool force) override
4730 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4731 Process *process = exe_ctx.GetProcessPtr();
4732 Thread *thread = nullptr;
4734 bool update_location = false;
4737 StateType state = process->GetState();
4738 if (StateIsStoppedState(state, true))
4740 // We are stopped, so it is ok to
4741 update_location = true;
4747 m_max_x = window.GetMaxX()-1;
4748 m_max_y = window.GetMaxY()-1;
4750 const uint32_t num_visible_lines = NumVisibleLines();
4751 StackFrameSP frame_sp;
4752 bool set_selected_line_to_pc = false;
4754 if (update_location)
4756 const bool process_alive = process ? process->IsAlive() : false;
4757 bool thread_changed = false;
4760 thread = exe_ctx.GetThreadPtr();
4763 frame_sp = thread->GetSelectedFrame();
4764 auto tid = thread->GetID();
4765 thread_changed = tid != m_tid;
4770 if (m_tid != LLDB_INVALID_THREAD_ID)
4772 thread_changed = true;
4773 m_tid = LLDB_INVALID_THREAD_ID;
4777 const uint32_t stop_id = process ? process->GetStopID() : 0;
4778 const bool stop_id_changed = stop_id != m_stop_id;
4779 bool frame_changed = false;
4780 m_stop_id = stop_id;
4784 m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
4787 m_title.Printf("%s", m_sc.module_sp->GetFileSpec().GetFilename().GetCString());
4788 ConstString func_name = m_sc.GetFunctionName();
4790 m_title.Printf("`%s", func_name.GetCString());
4792 const uint32_t frame_idx = frame_sp->GetFrameIndex();
4793 frame_changed = frame_idx != m_frame_idx;
4794 m_frame_idx = frame_idx;
4799 frame_changed = m_frame_idx != UINT32_MAX;
4800 m_frame_idx = UINT32_MAX;
4803 const bool context_changed = thread_changed || frame_changed || stop_id_changed;
4807 if (m_sc.line_entry.IsValid())
4809 m_pc_line = m_sc.line_entry.line;
4810 if (m_pc_line != UINT32_MAX)
4811 --m_pc_line; // Convert to zero based line number...
4812 // Update the selected line if the stop ID changed...
4813 if (context_changed)
4814 m_selected_line = m_pc_line;
4816 if (m_file_sp && m_file_sp->FileSpecMatches(m_sc.line_entry.file))
4818 // Same file, nothing to do, we should either have the
4819 // lines or not (source file missing)
4820 if (m_selected_line >= static_cast<size_t>(m_first_visible_line))
4822 if (m_selected_line >= m_first_visible_line + num_visible_lines)
4823 m_first_visible_line = m_selected_line - 10;
4827 if (m_selected_line > 10)
4828 m_first_visible_line = m_selected_line - 10;
4830 m_first_visible_line = 0;
4835 // File changed, set selected line to the line with the PC
4836 m_selected_line = m_pc_line;
4837 m_file_sp = m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file);
4840 const size_t num_lines = m_file_sp->GetNumLines();
4841 int m_line_width = 1;
4842 for (size_t n = num_lines; n >= 10; n = n / 10)
4845 snprintf (m_line_format, sizeof(m_line_format), " %%%iu ", m_line_width);
4846 if (num_lines < num_visible_lines || m_selected_line < num_visible_lines)
4847 m_first_visible_line = 0;
4849 m_first_visible_line = m_selected_line - 10;
4858 if (!m_file_sp || m_file_sp->GetNumLines() == 0)
4861 bool prefer_file_cache = false;
4864 if (m_disassembly_scope != m_sc.function)
4866 m_disassembly_scope = m_sc.function;
4867 m_disassembly_sp = m_sc.function->GetInstructions(exe_ctx, nullptr, prefer_file_cache);
4868 if (m_disassembly_sp)
4870 set_selected_line_to_pc = true;
4871 m_disassembly_range = m_sc.function->GetAddressRange();
4875 m_disassembly_range.Clear();
4880 set_selected_line_to_pc = context_changed;
4883 else if (m_sc.symbol)
4885 if (m_disassembly_scope != m_sc.symbol)
4887 m_disassembly_scope = m_sc.symbol;
4888 m_disassembly_sp = m_sc.symbol->GetInstructions(exe_ctx, nullptr, prefer_file_cache);
4889 if (m_disassembly_sp)
4891 set_selected_line_to_pc = true;
4892 m_disassembly_range.GetBaseAddress() = m_sc.symbol->GetAddress();
4893 m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize());
4897 m_disassembly_range.Clear();
4902 set_selected_line_to_pc = context_changed;
4909 m_pc_line = UINT32_MAX;
4913 const int window_width = window.GetWidth();
4915 window.DrawTitleBox ("Sources");
4916 if (!m_title.GetString().empty())
4918 window.AttributeOn(A_REVERSE);
4919 window.MoveCursor(1, 1);
4920 window.PutChar(' ');
4921 window.PutCStringTruncated(m_title.GetString().c_str(), 1);
4922 int x = window.GetCursorX();
4923 if (x < window_width - 1)
4925 window.Printf ("%*s", window_width - x - 1, "");
4927 window.AttributeOff(A_REVERSE);
4930 Target *target = exe_ctx.GetTargetPtr();
4931 const size_t num_source_lines = GetNumSourceLines();
4932 if (num_source_lines > 0)
4935 BreakpointLines bp_lines;
4938 BreakpointList &bp_list = target->GetBreakpointList();
4939 const size_t num_bps = bp_list.GetSize();
4940 for (size_t bp_idx=0; bp_idx<num_bps; ++bp_idx)
4942 BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
4943 const size_t num_bps_locs = bp_sp->GetNumLocations();
4944 for (size_t bp_loc_idx=0; bp_loc_idx<num_bps_locs; ++bp_loc_idx)
4946 BreakpointLocationSP bp_loc_sp = bp_sp->GetLocationAtIndex(bp_loc_idx);
4947 LineEntry bp_loc_line_entry;
4948 if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry (bp_loc_line_entry))
4950 if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file)
4952 bp_lines.insert(bp_loc_line_entry.line);
4959 const attr_t selected_highlight_attr = A_REVERSE;
4960 const attr_t pc_highlight_attr = COLOR_PAIR(1);
4962 for (size_t i = 0; i < num_visible_lines; ++i)
4964 const uint32_t curr_line = m_first_visible_line + i;
4965 if (curr_line < num_source_lines)
4967 const int line_y = m_min_y+i;
4968 window.MoveCursor(1, line_y);
4969 const bool is_pc_line = curr_line == m_pc_line;
4970 const bool line_is_selected = m_selected_line == curr_line;
4971 // Highlight the line as the PC line first, then if the selected line
4972 // isn't the same as the PC line, highlight it differently
4973 attr_t highlight_attr = 0;
4976 highlight_attr = pc_highlight_attr;
4977 else if (line_is_selected)
4978 highlight_attr = selected_highlight_attr;
4980 if (bp_lines.find(curr_line+1) != bp_lines.end())
4981 bp_attr = COLOR_PAIR(2);
4984 window.AttributeOn(bp_attr);
4986 window.Printf (m_line_format, curr_line + 1);
4989 window.AttributeOff(bp_attr);
4991 window.PutChar(ACS_VLINE);
4992 // Mark the line with the PC with a diamond
4994 window.PutChar(ACS_DIAMOND);
4996 window.PutChar(' ');
4999 window.AttributeOn(highlight_attr);
5000 const uint32_t line_len = m_file_sp->GetLineLength(curr_line + 1, false);
5002 window.PutCString(m_file_sp->PeekLineData(curr_line + 1), line_len);
5004 if (is_pc_line && frame_sp && frame_sp->GetConcreteFrameIndex() == 0)
5006 StopInfoSP stop_info_sp;
5008 stop_info_sp = thread->GetStopInfo();
5011 const char *stop_description = stop_info_sp->GetDescription();
5012 if (stop_description && stop_description[0])
5014 size_t stop_description_len = strlen(stop_description);
5015 int desc_x = window_width - stop_description_len - 16;
5016 window.Printf ("%*s", desc_x - window.GetCursorX(), "");
5017 //window.MoveCursor(window_width - stop_description_len - 15, line_y);
5018 window.Printf ("<<< Thread %u: %s ", thread->GetIndexID(), stop_description);
5023 window.Printf ("%*s", window_width - window.GetCursorX() - 1, "");
5027 window.AttributeOff(highlight_attr);
5037 size_t num_disassembly_lines = GetNumDisassemblyLines();
5038 if (num_disassembly_lines > 0)
5040 // Display disassembly
5041 BreakpointAddrs bp_file_addrs;
5042 Target *target = exe_ctx.GetTargetPtr();
5045 BreakpointList &bp_list = target->GetBreakpointList();
5046 const size_t num_bps = bp_list.GetSize();
5047 for (size_t bp_idx=0; bp_idx<num_bps; ++bp_idx)
5049 BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
5050 const size_t num_bps_locs = bp_sp->GetNumLocations();
5051 for (size_t bp_loc_idx=0; bp_loc_idx<num_bps_locs; ++bp_loc_idx)
5053 BreakpointLocationSP bp_loc_sp = bp_sp->GetLocationAtIndex(bp_loc_idx);
5054 LineEntry bp_loc_line_entry;
5055 const lldb::addr_t file_addr = bp_loc_sp->GetAddress().GetFileAddress();
5056 if (file_addr != LLDB_INVALID_ADDRESS)
5058 if (m_disassembly_range.ContainsFileAddress(file_addr))
5059 bp_file_addrs.insert(file_addr);
5065 const attr_t selected_highlight_attr = A_REVERSE;
5066 const attr_t pc_highlight_attr = COLOR_PAIR(1);
5070 InstructionList &insts = m_disassembly_sp->GetInstructionList();
5074 pc_address = frame_sp->GetFrameCodeAddress();
5075 const uint32_t pc_idx = pc_address.IsValid() ? insts.GetIndexOfInstructionAtAddress (pc_address) : UINT32_MAX;
5076 if (set_selected_line_to_pc)
5078 m_selected_line = pc_idx;
5081 const uint32_t non_visible_pc_offset = (num_visible_lines / 5);
5082 if (static_cast<size_t>(m_first_visible_line) >= num_disassembly_lines)
5083 m_first_visible_line = 0;
5085 if (pc_idx < num_disassembly_lines)
5087 if (pc_idx < static_cast<uint32_t>(m_first_visible_line) ||
5088 pc_idx >= m_first_visible_line + num_visible_lines)
5089 m_first_visible_line = pc_idx - non_visible_pc_offset;
5092 for (size_t i = 0; i < num_visible_lines; ++i)
5094 const uint32_t inst_idx = m_first_visible_line + i;
5095 Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get();
5099 const int line_y = m_min_y+i;
5100 window.MoveCursor(1, line_y);
5101 const bool is_pc_line = frame_sp && inst_idx == pc_idx;
5102 const bool line_is_selected = m_selected_line == inst_idx;
5103 // Highlight the line as the PC line first, then if the selected line
5104 // isn't the same as the PC line, highlight it differently
5105 attr_t highlight_attr = 0;
5108 highlight_attr = pc_highlight_attr;
5109 else if (line_is_selected)
5110 highlight_attr = selected_highlight_attr;
5112 if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) != bp_file_addrs.end())
5113 bp_attr = COLOR_PAIR(2);
5116 window.AttributeOn(bp_attr);
5118 window.Printf (" 0x%16.16llx ",
5119 static_cast<unsigned long long>(inst->GetAddress().GetLoadAddress(target)));
5122 window.AttributeOff(bp_attr);
5124 window.PutChar(ACS_VLINE);
5125 // Mark the line with the PC with a diamond
5127 window.PutChar(ACS_DIAMOND);
5129 window.PutChar(' ');
5132 window.AttributeOn(highlight_attr);
5134 const char *mnemonic = inst->GetMnemonic(&exe_ctx);
5135 const char *operands = inst->GetOperands(&exe_ctx);
5136 const char *comment = inst->GetComment(&exe_ctx);
5138 if (mnemonic != nullptr && mnemonic[0] == '\0')
5140 if (operands != nullptr && operands[0] == '\0')
5142 if (comment != nullptr && comment[0] == '\0')
5147 if (mnemonic != nullptr && operands != nullptr && comment != nullptr)
5148 strm.Printf ("%-8s %-25s ; %s", mnemonic, operands, comment);
5149 else if (mnemonic != nullptr && operands != nullptr)
5150 strm.Printf ("%-8s %s", mnemonic, operands);
5151 else if (mnemonic != nullptr)
5152 strm.Printf ("%s", mnemonic);
5155 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
5157 if (is_pc_line && frame_sp && frame_sp->GetConcreteFrameIndex() == 0)
5159 StopInfoSP stop_info_sp;
5161 stop_info_sp = thread->GetStopInfo();
5164 const char *stop_description = stop_info_sp->GetDescription();
5165 if (stop_description && stop_description[0])
5167 size_t stop_description_len = strlen(stop_description);
5168 int desc_x = window_width - stop_description_len - 16;
5169 window.Printf ("%*s", desc_x - window.GetCursorX(), "");
5170 //window.MoveCursor(window_width - stop_description_len - 15, line_y);
5171 window.Printf ("<<< Thread %u: %s ", thread->GetIndexID(), stop_description);
5176 window.Printf ("%*s", window_width - window.GetCursorX() - 1, "");
5180 window.AttributeOff(highlight_attr);
5184 window.DeferredRefresh();
5185 return true; // Drawing handled
5191 size_t num_lines = GetNumSourceLines();
5193 num_lines = GetNumDisassemblyLines();
5198 GetNumSourceLines () const
5201 return m_file_sp->GetNumLines();
5206 GetNumDisassemblyLines () const
5208 if (m_disassembly_sp)
5209 return m_disassembly_sp->GetInstructionList().GetSize();
5214 WindowDelegateHandleChar (Window &window, int c) override
5216 const uint32_t num_visible_lines = NumVisibleLines();
5217 const size_t num_lines = GetNumLines ();
5224 if (static_cast<uint32_t>(m_first_visible_line) > num_visible_lines)
5225 m_first_visible_line -= num_visible_lines;
5227 m_first_visible_line = 0;
5228 m_selected_line = m_first_visible_line;
5235 if (m_first_visible_line + num_visible_lines < num_lines)
5236 m_first_visible_line += num_visible_lines;
5237 else if (num_lines < num_visible_lines)
5238 m_first_visible_line = 0;
5240 m_first_visible_line = num_lines - num_visible_lines;
5241 m_selected_line = m_first_visible_line;
5246 if (m_selected_line > 0)
5249 if (static_cast<size_t>(m_first_visible_line) > m_selected_line)
5250 m_first_visible_line = m_selected_line;
5255 if (m_selected_line + 1 < num_lines)
5258 if (m_first_visible_line + num_visible_lines < m_selected_line)
5259 m_first_visible_line++;
5266 // Set a breakpoint and run to the line using a one shot breakpoint
5267 if (GetNumSourceLines() > 0)
5269 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5270 if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive())
5272 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(nullptr, // Don't limit the breakpoint to certain modules
5273 m_file_sp->GetFileSpec(), // Source file
5274 m_selected_line + 1, // Source line number (m_selected_line is zero based)
5276 eLazyBoolCalculate, // Check inlines using global setting
5277 eLazyBoolCalculate, // Skip prologue using global setting,
5279 false, // request_hardware
5280 eLazyBoolCalculate); // move_to_nearest_code
5281 // Make breakpoint one shot
5282 bp_sp->GetOptions()->SetOneShot(true);
5283 exe_ctx.GetProcessRef().Resume();
5286 else if (m_selected_line < GetNumDisassemblyLines())
5288 const Instruction *inst = m_disassembly_sp->GetInstructionList().GetInstructionAtIndex(m_selected_line).get();
5289 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5290 if (exe_ctx.HasTargetScope())
5292 Address addr = inst->GetAddress();
5293 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (addr, // lldb_private::Address
5295 false); // request_hardware
5296 // Make breakpoint one shot
5297 bp_sp->GetOptions()->SetOneShot(true);
5298 exe_ctx.GetProcessRef().Resume();
5303 case 'b': // 'b' == toggle breakpoint on currently selected line
5304 if (m_selected_line < GetNumSourceLines())
5306 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5307 if (exe_ctx.HasTargetScope())
5309 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(nullptr, // Don't limit the breakpoint to certain modules
5310 m_file_sp->GetFileSpec(), // Source file
5311 m_selected_line + 1, // Source line number (m_selected_line is zero based)
5313 eLazyBoolCalculate, // Check inlines using global setting
5314 eLazyBoolCalculate, // Skip prologue using global setting,
5316 false, // request_hardware
5317 eLazyBoolCalculate); // move_to_nearest_code
5320 else if (m_selected_line < GetNumDisassemblyLines())
5322 const Instruction *inst = m_disassembly_sp->GetInstructionList().GetInstructionAtIndex(m_selected_line).get();
5323 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5324 if (exe_ctx.HasTargetScope())
5326 Address addr = inst->GetAddress();
5327 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (addr, // lldb_private::Address
5329 false); // request_hardware
5334 case 'd': // 'd' == detach and let run
5335 case 'D': // 'D' == detach and keep stopped
5337 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5338 if (exe_ctx.HasProcessScope())
5339 exe_ctx.GetProcessRef().Detach(c == 'D');
5346 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5347 if (exe_ctx.HasProcessScope())
5348 exe_ctx.GetProcessRef().Destroy(false);
5355 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5356 if (exe_ctx.HasProcessScope())
5357 exe_ctx.GetProcessRef().Resume();
5364 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5365 if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true))
5367 exe_ctx.GetThreadRef().StepOut();
5372 case 'n': // 'n' == step over
5373 case 'N': // 'N' == step over instruction
5375 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5376 if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true))
5378 bool source_step = (c == 'n');
5379 exe_ctx.GetThreadRef().StepOver(source_step);
5384 case 's': // 's' == step into
5385 case 'S': // 'S' == step into instruction
5387 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5388 if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true))
5390 bool source_step = (c == 's');
5391 exe_ctx.GetThreadRef().StepIn(source_step);
5397 window.CreateHelpSubwindow ();
5403 return eKeyNotHandled;
5407 typedef std::set<uint32_t> BreakpointLines;
5408 typedef std::set<lldb::addr_t> BreakpointAddrs;
5410 Debugger &m_debugger;
5412 SourceManager::FileSP m_file_sp;
5413 SymbolContextScope *m_disassembly_scope;
5414 lldb::DisassemblerSP m_disassembly_sp;
5415 AddressRange m_disassembly_range;
5416 StreamString m_title;
5417 lldb::user_id_t m_tid;
5418 char m_line_format[8];
5420 uint32_t m_selected_line; // The selected line
5421 uint32_t m_pc_line; // The line with the PC
5423 uint32_t m_frame_idx;
5424 int m_first_visible_line;
5431 DisplayOptions ValueObjectListDelegate::g_options = { true };
5433 IOHandlerCursesGUI::IOHandlerCursesGUI (Debugger &debugger) :
5434 IOHandler (debugger, IOHandler::Type::Curses)
5439 IOHandlerCursesGUI::Activate ()
5441 IOHandler::Activate();
5444 m_app_ap.reset (new Application (GetInputFILE(), GetOutputFILE()));
5446 // This is both a window and a menu delegate
5447 std::shared_ptr<ApplicationDelegate> app_delegate_sp(new ApplicationDelegate(*m_app_ap, m_debugger));
5449 MenuDelegateSP app_menu_delegate_sp = std::static_pointer_cast<MenuDelegate>(app_delegate_sp);
5450 MenuSP lldb_menu_sp(new Menu("LLDB" , "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB));
5451 MenuSP exit_menuitem_sp(new Menu("Exit", nullptr, 'x', ApplicationDelegate::eMenuID_LLDBExit));
5452 exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit);
5453 lldb_menu_sp->AddSubmenu (MenuSP (new Menu("About LLDB", nullptr, 'a', ApplicationDelegate::eMenuID_LLDBAbout)));
5454 lldb_menu_sp->AddSubmenu (MenuSP (new Menu(Menu::Type::Separator)));
5455 lldb_menu_sp->AddSubmenu (exit_menuitem_sp);
5457 MenuSP target_menu_sp(new Menu("Target" ,"F2", KEY_F(2), ApplicationDelegate::eMenuID_Target));
5458 target_menu_sp->AddSubmenu (MenuSP (new Menu("Create", nullptr, 'c', ApplicationDelegate::eMenuID_TargetCreate)));
5459 target_menu_sp->AddSubmenu (MenuSP (new Menu("Delete", nullptr, 'd', ApplicationDelegate::eMenuID_TargetDelete)));
5461 MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3), ApplicationDelegate::eMenuID_Process));
5462 process_menu_sp->AddSubmenu (MenuSP (new Menu("Attach" , nullptr, 'a', ApplicationDelegate::eMenuID_ProcessAttach)));
5463 process_menu_sp->AddSubmenu (MenuSP (new Menu("Detach" , nullptr, 'd', ApplicationDelegate::eMenuID_ProcessDetach)));
5464 process_menu_sp->AddSubmenu (MenuSP (new Menu("Launch" , nullptr, 'l', ApplicationDelegate::eMenuID_ProcessLaunch)));
5465 process_menu_sp->AddSubmenu (MenuSP (new Menu(Menu::Type::Separator)));
5466 process_menu_sp->AddSubmenu (MenuSP (new Menu("Continue", nullptr, 'c', ApplicationDelegate::eMenuID_ProcessContinue)));
5467 process_menu_sp->AddSubmenu (MenuSP (new Menu("Halt" , nullptr, 'h', ApplicationDelegate::eMenuID_ProcessHalt)));
5468 process_menu_sp->AddSubmenu (MenuSP (new Menu("Kill" , nullptr, 'k', ApplicationDelegate::eMenuID_ProcessKill)));
5470 MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4), ApplicationDelegate::eMenuID_Thread));
5471 thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step In" , nullptr, 'i', ApplicationDelegate::eMenuID_ThreadStepIn)));
5472 thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step Over", nullptr, 'v', ApplicationDelegate::eMenuID_ThreadStepOver)));
5473 thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step Out" , nullptr, 'o', ApplicationDelegate::eMenuID_ThreadStepOut)));
5475 MenuSP view_menu_sp(new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View));
5476 view_menu_sp->AddSubmenu (MenuSP (new Menu("Backtrace", nullptr, 'b', ApplicationDelegate::eMenuID_ViewBacktrace)));
5477 view_menu_sp->AddSubmenu (MenuSP (new Menu("Registers", nullptr, 'r', ApplicationDelegate::eMenuID_ViewRegisters)));
5478 view_menu_sp->AddSubmenu (MenuSP (new Menu("Source" , nullptr, 's', ApplicationDelegate::eMenuID_ViewSource)));
5479 view_menu_sp->AddSubmenu (MenuSP (new Menu("Variables", nullptr, 'v', ApplicationDelegate::eMenuID_ViewVariables)));
5481 MenuSP help_menu_sp(new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help));
5482 help_menu_sp->AddSubmenu (MenuSP (new Menu("GUI Help", nullptr, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp)));
5484 m_app_ap->Initialize();
5485 WindowSP &main_window_sp = m_app_ap->GetMainWindow();
5487 MenuSP menubar_sp(new Menu(Menu::Type::Bar));
5488 menubar_sp->AddSubmenu (lldb_menu_sp);
5489 menubar_sp->AddSubmenu (target_menu_sp);
5490 menubar_sp->AddSubmenu (process_menu_sp);
5491 menubar_sp->AddSubmenu (thread_menu_sp);
5492 menubar_sp->AddSubmenu (view_menu_sp);
5493 menubar_sp->AddSubmenu (help_menu_sp);
5494 menubar_sp->SetDelegate(app_menu_delegate_sp);
5496 Rect content_bounds = main_window_sp->GetFrame();
5497 Rect menubar_bounds = content_bounds.MakeMenuBar();
5498 Rect status_bounds = content_bounds.MakeStatusBar();
5500 Rect variables_bounds;
5501 Rect threads_bounds;
5502 Rect source_variables_bounds;
5503 content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds, threads_bounds);
5504 source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds, variables_bounds);
5506 WindowSP menubar_window_sp = main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false);
5507 // Let the menubar get keys if the active window doesn't handle the
5508 // keys that are typed so it can respond to menubar key presses.
5509 menubar_window_sp->SetCanBeActive(false); // Don't let the menubar become the active window
5510 menubar_window_sp->SetDelegate(menubar_sp);
5512 WindowSP source_window_sp (main_window_sp->CreateSubWindow("Source",
5515 WindowSP variables_window_sp (main_window_sp->CreateSubWindow("Variables",
5518 WindowSP threads_window_sp (main_window_sp->CreateSubWindow("Threads",
5521 WindowSP status_window_sp (main_window_sp->CreateSubWindow("Status",
5524 status_window_sp->SetCanBeActive(false); // Don't let the status bar become the active window
5525 main_window_sp->SetDelegate (std::static_pointer_cast<WindowDelegate>(app_delegate_sp));
5526 source_window_sp->SetDelegate (WindowDelegateSP(new SourceFileWindowDelegate(m_debugger)));
5527 variables_window_sp->SetDelegate (WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
5528 TreeDelegateSP thread_delegate_sp (new ThreadsTreeDelegate(m_debugger));
5529 threads_window_sp->SetDelegate (WindowDelegateSP(new TreeWindowDelegate(m_debugger, thread_delegate_sp)));
5530 status_window_sp->SetDelegate (WindowDelegateSP(new StatusBarWindowDelegate(m_debugger)));
5532 // Show the main help window once the first time the curses GUI is launched
5533 static bool g_showed_help = false;
5536 g_showed_help = true;
5537 main_window_sp->CreateHelpSubwindow();
5540 init_pair (1, COLOR_WHITE , COLOR_BLUE );
5541 init_pair (2, COLOR_BLACK , COLOR_WHITE );
5542 init_pair (3, COLOR_MAGENTA , COLOR_WHITE );
5543 init_pair (4, COLOR_MAGENTA , COLOR_BLACK );
5544 init_pair (5, COLOR_RED , COLOR_BLACK );
5549 IOHandlerCursesGUI::Deactivate ()
5551 m_app_ap->Terminate();
5555 IOHandlerCursesGUI::Run ()
5557 m_app_ap->Run(m_debugger);
5561 IOHandlerCursesGUI::~IOHandlerCursesGUI() = default;
5564 IOHandlerCursesGUI::Cancel ()
5569 IOHandlerCursesGUI::Interrupt ()
5575 IOHandlerCursesGUI::GotEOF()
5579 #endif // LLDB_DISABLE_CURSES