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"
45 using namespace lldb_private;
47 IOHandler::IOHandler (Debugger &debugger, IOHandler::Type type) :
50 StreamFileSP(), // Adopt STDIN from top input reader
51 StreamFileSP(), // Adopt STDOUT from top input reader
52 StreamFileSP(), // Adopt STDERR from top input reader
57 IOHandler::IOHandler (Debugger &debugger,
59 const lldb::StreamFileSP &input_sp,
60 const lldb::StreamFileSP &output_sp,
61 const lldb::StreamFileSP &error_sp,
63 m_debugger (debugger),
64 m_input_sp (input_sp),
65 m_output_sp (output_sp),
66 m_error_sp (error_sp),
74 // If any files are not specified, then adopt them from the top input reader.
75 if (!m_input_sp || !m_output_sp || !m_error_sp)
76 debugger.AdoptTopIOHandlerFilesIfInvalid (m_input_sp,
81 IOHandler::~IOHandler() = default;
84 IOHandler::GetInputFD()
87 return m_input_sp->GetFile().GetDescriptor();
92 IOHandler::GetOutputFD()
95 return m_output_sp->GetFile().GetDescriptor();
100 IOHandler::GetErrorFD()
103 return m_error_sp->GetFile().GetDescriptor();
108 IOHandler::GetInputFILE()
111 return m_input_sp->GetFile().GetStream();
116 IOHandler::GetOutputFILE()
119 return m_output_sp->GetFile().GetStream();
124 IOHandler::GetErrorFILE()
127 return m_error_sp->GetFile().GetStream();
132 IOHandler::GetInputStreamFile()
138 IOHandler::GetOutputStreamFile()
144 IOHandler::GetErrorStreamFile()
150 IOHandler::GetIsInteractive ()
152 return GetInputStreamFile()->GetFile().GetIsInteractive ();
156 IOHandler::GetIsRealTerminal ()
158 return GetInputStreamFile()->GetFile().GetIsRealTerminal();
162 IOHandler::SetPopped (bool b)
164 m_popped.SetValue(b, eBroadcastOnChange);
168 IOHandler::WaitForPop ()
170 m_popped.WaitForValueEqualTo(true);
174 IOHandlerStack::PrintAsync (Stream *stream, const char *s, size_t len)
178 Mutex::Locker locker (m_mutex);
180 m_top->PrintAsync (stream, s, len);
184 IOHandlerConfirm::IOHandlerConfirm (Debugger &debugger,
186 bool default_response) :
187 IOHandlerEditline(debugger,
188 IOHandler::Type::Confirm,
189 NULL, // NULL editline_name means no history loaded/saved
191 NULL, // No continuation prompt
193 false, // Don't colorize the prompt (i.e. the confirm message.)
196 m_default_response (default_response),
197 m_user_response (default_response)
199 StreamString prompt_stream;
200 prompt_stream.PutCString(prompt);
201 if (m_default_response)
202 prompt_stream.Printf(": [Y/n] ");
204 prompt_stream.Printf(": [y/N] ");
206 SetPrompt (prompt_stream.GetString().c_str());
210 IOHandlerConfirm::~IOHandlerConfirm() = default;
213 IOHandlerConfirm::IOHandlerComplete (IOHandler &io_handler,
214 const char *current_line,
216 const char *last_char,
217 int skip_first_n_matches,
221 if (current_line == cursor)
223 if (m_default_response)
225 matches.AppendString("y");
229 matches.AppendString("n");
232 return matches.GetSize();
236 IOHandlerConfirm::IOHandlerInputComplete (IOHandler &io_handler, std::string &line)
240 // User just hit enter, set the response to the default
241 m_user_response = m_default_response;
242 io_handler.SetIsDone(true);
246 if (line.size() == 1)
252 m_user_response = true;
253 io_handler.SetIsDone(true);
257 m_user_response = false;
258 io_handler.SetIsDone(true);
265 if (line == "yes" || line == "YES" || line == "Yes")
267 m_user_response = true;
268 io_handler.SetIsDone(true);
270 else if (line == "no" || line == "NO" || line == "No")
272 m_user_response = false;
273 io_handler.SetIsDone(true);
278 IOHandlerDelegate::IOHandlerComplete (IOHandler &io_handler,
279 const char *current_line,
281 const char *last_char,
282 int skip_first_n_matches,
286 switch (m_completion)
288 case Completion::None:
291 case Completion::LLDBCommand:
292 return io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion (current_line,
295 skip_first_n_matches,
299 case Completion::Expression:
301 bool word_complete = false;
302 const char *word_start = cursor;
303 if (cursor > current_line)
305 while (word_start > current_line && !isspace(*word_start))
307 CommandCompletions::InvokeCommonCompletionCallbacks (io_handler.GetDebugger().GetCommandInterpreter(),
308 CommandCompletions::eVariablePathCompletion,
310 skip_first_n_matches,
316 size_t num_matches = matches.GetSize();
319 std::string common_prefix;
320 matches.LongestCommonPrefix (common_prefix);
321 const size_t partial_name_len = strlen(word_start);
323 // If we matched a unique single command, add a space...
324 // Only do this if the completer told us this was a complete word, however...
325 if (num_matches == 1 && word_complete)
327 common_prefix.push_back(' ');
329 common_prefix.erase (0, partial_name_len);
330 matches.InsertStringAtIndex(0, std::move(common_prefix));
340 IOHandlerEditline::IOHandlerEditline (Debugger &debugger,
341 IOHandler::Type type,
342 const char *editline_name, // Used for saving history files
344 const char *continuation_prompt,
347 uint32_t line_number_start,
348 IOHandlerDelegate &delegate) :
349 IOHandlerEditline(debugger,
351 StreamFileSP(), // Inherit input from top input reader
352 StreamFileSP(), // Inherit output from top input reader
353 StreamFileSP(), // Inherit error from top input reader
355 editline_name, // Used for saving history files
365 IOHandlerEditline::IOHandlerEditline (Debugger &debugger,
366 IOHandler::Type type,
367 const lldb::StreamFileSP &input_sp,
368 const lldb::StreamFileSP &output_sp,
369 const lldb::StreamFileSP &error_sp,
371 const char *editline_name, // Used for saving history files
373 const char *continuation_prompt,
376 uint32_t line_number_start,
377 IOHandlerDelegate &delegate) :
378 IOHandler (debugger, type, input_sp, output_sp, error_sp, flags),
379 #ifndef LLDB_DISABLE_LIBEDIT
382 m_delegate (delegate),
384 m_continuation_prompt(),
385 m_current_lines_ptr (NULL),
386 m_base_line_number (line_number_start),
387 m_curr_line_idx (UINT32_MAX),
388 m_multi_line (multi_line),
389 m_color_prompts (color_prompts),
390 m_interrupt_exits (true),
395 #ifndef LLDB_DISABLE_LIBEDIT
396 bool use_editline = false;
398 use_editline = m_input_sp->GetFile().GetIsRealTerminal();
402 m_editline_ap.reset(new Editline (editline_name,
407 m_editline_ap->SetIsInputCompleteCallback (IsInputCompleteCallback, this);
408 m_editline_ap->SetAutoCompleteCallback (AutoCompleteCallback, this);
409 // See if the delegate supports fixing indentation
410 const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters();
413 // The delegate does support indentation, hook it up so when any indentation
414 // character is typed, the delegate gets a chance to fix it
415 m_editline_ap->SetFixIndentationCallback (FixIndentationCallback, this, indent_chars);
419 SetBaseLineNumber (m_base_line_number);
420 SetPrompt(prompt ? prompt : "");
421 SetContinuationPrompt(continuation_prompt);
424 IOHandlerEditline::~IOHandlerEditline ()
426 #ifndef LLDB_DISABLE_LIBEDIT
427 m_editline_ap.reset();
432 IOHandlerEditline::Activate ()
434 IOHandler::Activate();
435 m_delegate.IOHandlerActivated(*this);
439 IOHandlerEditline::Deactivate ()
441 IOHandler::Deactivate();
442 m_delegate.IOHandlerDeactivated(*this);
446 IOHandlerEditline::GetLine (std::string &line, bool &interrupted)
448 #ifndef LLDB_DISABLE_LIBEDIT
451 return m_editline_ap->GetLine (line, interrupted);
458 FILE *in = GetInputFILE();
461 if (GetIsInteractive())
463 const char *prompt = NULL;
465 if (m_multi_line && m_curr_line_idx > 0)
466 prompt = GetContinuationPrompt();
469 prompt = GetPrompt();
471 if (prompt && prompt[0])
473 FILE *out = GetOutputFILE();
476 ::fprintf(out, "%s", prompt);
483 bool got_line = false;
487 if (fgets(buffer, sizeof(buffer), in) == NULL)
489 const int saved_errno = errno;
494 if (saved_errno != EINTR)
501 size_t buffer_len = strlen(buffer);
502 assert (buffer[buffer_len] == '\0');
503 char last_char = buffer[buffer_len-1];
504 if (last_char == '\r' || last_char == '\n')
507 // Strip trailing newlines
508 while (last_char == '\r' || last_char == '\n')
513 last_char = buffer[buffer_len-1];
516 line.append(buffer, buffer_len);
520 // We might have gotten a newline on a line by itself
521 // make sure to return true in this case.
526 // No more input file, we are done...
530 #ifndef LLDB_DISABLE_LIBEDIT
536 #ifndef LLDB_DISABLE_LIBEDIT
538 IOHandlerEditline::IsInputCompleteCallback (Editline *editline,
542 IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton;
543 return editline_reader->m_delegate.IOHandlerIsInputComplete(*editline_reader, lines);
547 IOHandlerEditline::FixIndentationCallback (Editline *editline,
548 const StringList &lines,
552 IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton;
553 return editline_reader->m_delegate.IOHandlerFixIndentation(*editline_reader, lines, cursor_position);
557 IOHandlerEditline::AutoCompleteCallback (const char *current_line,
559 const char *last_char,
560 int skip_first_n_matches,
565 IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton;
567 return editline_reader->m_delegate.IOHandlerComplete (*editline_reader,
571 skip_first_n_matches,
579 IOHandlerEditline::GetPrompt ()
581 #ifndef LLDB_DISABLE_LIBEDIT
584 return m_editline_ap->GetPrompt ();
589 if (m_prompt.empty())
591 #ifndef LLDB_DISABLE_LIBEDIT
594 return m_prompt.c_str();
598 IOHandlerEditline::SetPrompt (const char *p)
604 #ifndef LLDB_DISABLE_LIBEDIT
606 m_editline_ap->SetPrompt (m_prompt.empty() ? NULL : m_prompt.c_str());
612 IOHandlerEditline::GetContinuationPrompt ()
614 if (m_continuation_prompt.empty())
616 return m_continuation_prompt.c_str();
620 IOHandlerEditline::SetContinuationPrompt (const char *p)
623 m_continuation_prompt = p;
625 m_continuation_prompt.clear();
627 #ifndef LLDB_DISABLE_LIBEDIT
629 m_editline_ap->SetContinuationPrompt (m_continuation_prompt.empty() ? NULL : m_continuation_prompt.c_str());
634 IOHandlerEditline::SetBaseLineNumber (uint32_t line)
636 m_base_line_number = line;
640 IOHandlerEditline::GetCurrentLineIndex () const
642 #ifndef LLDB_DISABLE_LIBEDIT
644 return m_editline_ap->GetCurrentLine();
646 return m_curr_line_idx;
650 IOHandlerEditline::GetLines (StringList &lines, bool &interrupted)
652 m_current_lines_ptr = &lines;
654 bool success = false;
655 #ifndef LLDB_DISABLE_LIBEDIT
658 return m_editline_ap->GetLines (m_base_line_number, lines, interrupted);
668 // Show line numbers if we are asked to
670 if (m_base_line_number > 0 && GetIsInteractive())
672 FILE *out = GetOutputFILE();
674 ::fprintf(out, "%u%s", m_base_line_number + (uint32_t)lines.GetSize(), GetPrompt() == NULL ? " " : "");
677 m_curr_line_idx = lines.GetSize();
679 bool interrupted = false;
680 if (GetLine(line, interrupted) && !interrupted)
682 lines.AppendString(line);
683 done = m_delegate.IOHandlerIsInputComplete(*this, lines);
690 success = lines.GetSize() > 0;
691 #ifndef LLDB_DISABLE_LIBEDIT
697 // Each IOHandler gets to run until it is done. It should read data
698 // from the "in" and place output into "out" and "err and return
701 IOHandlerEditline::Run ()
706 bool interrupted = false;
710 if (GetLines (lines, interrupted))
714 m_done = m_interrupt_exits;
715 m_delegate.IOHandlerInputInterrupted (*this, line);
720 line = lines.CopyList();
721 m_delegate.IOHandlerInputComplete (*this, line);
731 if (GetLine(line, interrupted))
734 m_delegate.IOHandlerInputInterrupted (*this, line);
736 m_delegate.IOHandlerInputComplete (*this, line);
747 IOHandlerEditline::Cancel ()
749 #ifndef LLDB_DISABLE_LIBEDIT
751 m_editline_ap->Cancel ();
756 IOHandlerEditline::Interrupt ()
758 // Let the delgate handle it first
759 if (m_delegate.IOHandlerInterrupt(*this))
762 #ifndef LLDB_DISABLE_LIBEDIT
764 return m_editline_ap->Interrupt();
770 IOHandlerEditline::GotEOF()
772 #ifndef LLDB_DISABLE_LIBEDIT
774 m_editline_ap->Interrupt();
779 IOHandlerEditline::PrintAsync (Stream *stream, const char *s, size_t len)
781 #ifndef LLDB_DISABLE_LIBEDIT
783 m_editline_ap->PrintAsync(stream, s, len);
786 IOHandler::PrintAsync(stream, s, len);
789 // we may want curses to be disabled for some builds
790 // for instance, windows
791 #ifndef LLDB_DISABLE_CURSES
793 #include "lldb/Core/ValueObject.h"
794 #include "lldb/Symbol/VariableList.h"
795 #include "lldb/Target/Target.h"
796 #include "lldb/Target/Process.h"
797 #include "lldb/Target/Thread.h"
798 #include "lldb/Target/StackFrame.h"
800 #define KEY_RETURN 10
801 #define KEY_ESCAPE 27
808 class WindowDelegate;
809 typedef std::shared_ptr<Menu> MenuSP;
810 typedef std::shared_ptr<MenuDelegate> MenuDelegateSP;
811 typedef std::shared_ptr<Window> WindowSP;
812 typedef std::shared_ptr<WindowDelegate> WindowDelegateSP;
813 typedef std::vector<MenuSP> Menus;
814 typedef std::vector<WindowSP> Windows;
815 typedef std::vector<WindowDelegateSP> WindowDelegates;
818 type summary add -s "x=${var.x}, y=${var.y}" curses::Point
819 type summary add -s "w=${var.width}, h=${var.height}" curses::Size
820 type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect
828 Point (int _x = 0, int _y = 0) :
842 operator += (const Point &rhs)
852 printf ("(x=%i, y=%i)\n", x, y);
856 bool operator == (const Point &lhs, const Point &rhs)
858 return lhs.x == rhs.x && lhs.y == rhs.y;
861 bool operator != (const Point &lhs, const Point &rhs)
863 return lhs.x != rhs.x || lhs.y != rhs.y;
870 Size (int w = 0, int h = 0) :
886 printf ("(w=%i, h=%i)\n", width, height);
890 bool operator == (const Size &lhs, const Size &rhs)
892 return lhs.width == rhs.width && lhs.height == rhs.height;
895 bool operator != (const Size &lhs, const Size &rhs)
897 return lhs.width != rhs.width || lhs.height != rhs.height;
911 Rect (const Point &p, const Size &s) :
927 printf ("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width, size.height);
933 if (size.width > w*2)
937 if (size.height > h*2)
942 // Return a status bar rectangle which is the last line of
943 // this rectangle. This rectangle will be modified to not
944 // include the status bar area.
951 status_bar.origin.x = origin.x;
952 status_bar.origin.y = size.height;
953 status_bar.size.width = size.width;
954 status_bar.size.height = 1;
960 // Return a menubar rectangle which is the first line of
961 // this rectangle. This rectangle will be modified to not
962 // include the menubar area.
969 menubar.origin.x = origin.x;
970 menubar.origin.y = origin.y;
971 menubar.size.width = size.width;
972 menubar.size.height = 1;
980 HorizontalSplitPercentage (float top_percentage, Rect &top, Rect &bottom) const
982 float top_height = top_percentage * size.height;
983 HorizontalSplit (top_height, top, bottom);
987 HorizontalSplit (int top_height, Rect &top, Rect &bottom) const
990 if (top_height < size.height)
992 top.size.height = top_height;
993 bottom.origin.x = origin.x;
994 bottom.origin.y = origin.y + top.size.height;
995 bottom.size.width = size.width;
996 bottom.size.height = size.height - top.size.height;
1005 VerticalSplitPercentage (float left_percentage, Rect &left, Rect &right) const
1007 float left_width = left_percentage * size.width;
1008 VerticalSplit (left_width, left, right);
1012 VerticalSplit (int left_width, Rect &left, Rect &right) const
1015 if (left_width < size.width)
1017 left.size.width = left_width;
1018 right.origin.x = origin.x + left.size.width;
1019 right.origin.y = origin.y;
1020 right.size.width = size.width - left.size.width;
1021 right.size.height = size.height;
1030 bool operator == (const Rect &lhs, const Rect &rhs)
1032 return lhs.origin == rhs.origin && lhs.size == rhs.size;
1035 bool operator != (const Rect &lhs, const Rect &rhs)
1037 return lhs.origin != rhs.origin || lhs.size != rhs.size;
1040 enum HandleCharResult
1044 eQuitApplication = 2
1047 enum class MenuActionResult
1051 Quit // Exit all menus and quit
1057 const char *description;
1060 class WindowDelegate
1064 ~WindowDelegate() = default;
1067 WindowDelegateDraw (Window &window, bool force)
1069 return false; // Drawing not handled
1072 virtual HandleCharResult
1073 WindowDelegateHandleChar (Window &window, int key)
1075 return eKeyNotHandled;
1078 virtual const char *
1079 WindowDelegateGetHelpText ()
1085 WindowDelegateGetKeyHelp ()
1091 class HelpDialogDelegate :
1092 public WindowDelegate
1095 HelpDialogDelegate (const char *text, KeyHelp *key_help_array);
1097 ~HelpDialogDelegate() override;
1100 WindowDelegateDraw (Window &window, bool force) override;
1103 WindowDelegateHandleChar (Window &window, int key) override;
1108 return m_text.GetSize();
1112 GetMaxLineLength () const
1114 return m_text.GetMaxStringLength();
1119 int m_first_visible_line;
1125 Window (const char *name) :
1132 m_curr_active_window_idx (UINT32_MAX),
1133 m_prev_active_window_idx (UINT32_MAX),
1135 m_needs_update (true),
1136 m_can_activate (true),
1141 Window (const char *name, WINDOW *w, bool del = true) :
1148 m_curr_active_window_idx (UINT32_MAX),
1149 m_prev_active_window_idx (UINT32_MAX),
1151 m_needs_update (true),
1152 m_can_activate (true),
1159 Window (const char *name, const Rect &bounds) :
1165 m_curr_active_window_idx (UINT32_MAX),
1166 m_prev_active_window_idx (UINT32_MAX),
1168 m_needs_update (true),
1169 m_can_activate (true),
1172 Reset (::newwin (bounds.size.height, bounds.size.width, bounds.origin.y, bounds.origin.y));
1178 RemoveSubWindows ();
1183 Reset (WINDOW *w = NULL, bool del = true)
1190 ::del_panel (m_panel);
1193 if (m_window && m_delete)
1195 ::delwin (m_window);
1202 m_panel = ::new_panel (m_window);
1207 void AttributeOn (attr_t attr) { ::wattron (m_window, attr); }
1208 void AttributeOff (attr_t attr) { ::wattroff (m_window, attr); }
1209 void Box (chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) { ::box(m_window, v_char, h_char); }
1210 void Clear () { ::wclear (m_window); }
1211 void Erase () { ::werase (m_window); }
1212 Rect GetBounds () { return Rect (GetParentOrigin(), GetSize()); } // Get the rectangle in our parent window
1213 int GetChar () { return ::wgetch (m_window); }
1214 int GetCursorX () { return getcurx (m_window); }
1215 int GetCursorY () { return getcury (m_window); }
1216 Rect GetFrame () { return Rect (Point(), GetSize()); } // Get our rectangle in our own coordinate system
1217 Point GetParentOrigin() { return Point (GetParentX(), GetParentY()); }
1218 Size GetSize() { return Size (GetWidth(), GetHeight()); }
1219 int GetParentX () { return getparx (m_window); }
1220 int GetParentY () { return getpary (m_window); }
1221 int GetMaxX() { return getmaxx (m_window); }
1222 int GetMaxY() { return getmaxy (m_window); }
1223 int GetWidth() { return GetMaxX(); }
1224 int GetHeight() { return GetMaxY(); }
1225 void MoveCursor (int x, int y) { ::wmove (m_window, y, x); }
1226 void MoveWindow (int x, int y) { MoveWindow(Point(x,y)); }
1227 void Resize (int w, int h) { ::wresize(m_window, h, w); }
1228 void Resize (const Size &size) { ::wresize(m_window, size.height, size.width); }
1229 void PutChar (int ch) { ::waddch (m_window, ch); }
1230 void PutCString (const char *s, int len = -1) { ::waddnstr (m_window, s, len); }
1231 void Refresh () { ::wrefresh (m_window); }
1232 void DeferredRefresh ()
1234 // We are using panels, so we don't need to call this...
1235 //::wnoutrefresh(m_window);
1237 void SetBackground (int color_pair_idx) { ::wbkgd (m_window,COLOR_PAIR(color_pair_idx)); }
1238 void UnderlineOn () { AttributeOn(A_UNDERLINE); }
1239 void UnderlineOff () { AttributeOff(A_UNDERLINE); }
1241 void PutCStringTruncated (const char *s, int right_pad)
1243 int bytes_left = GetWidth() - GetCursorX();
1244 if (bytes_left > right_pad)
1246 bytes_left -= right_pad;
1247 ::waddnstr (m_window, s, bytes_left);
1252 MoveWindow (const Point &origin)
1254 const bool moving_window = origin != GetParentOrigin();
1255 if (m_is_subwin && moving_window)
1257 // Can't move subwindows, must delete and re-create
1258 Size size = GetSize();
1259 Reset (::subwin (m_parent->m_window,
1267 ::mvwin (m_window, origin.y, origin.x);
1272 SetBounds (const Rect &bounds)
1274 const bool moving_window = bounds.origin != GetParentOrigin();
1275 if (m_is_subwin && moving_window)
1277 // Can't move subwindows, must delete and re-create
1278 Reset (::subwin (m_parent->m_window,
1282 bounds.origin.x), true);
1287 MoveWindow(bounds.origin);
1288 Resize (bounds.size);
1293 Printf (const char *format, ...) __attribute__ ((format (printf, 2, 3)))
1296 va_start (args, format);
1297 vwprintw(m_window, format, args);
1304 ::touchwin (m_window);
1310 CreateSubWindow (const char *name, const Rect &bounds, bool make_active)
1312 WindowSP subwindow_sp;
1315 subwindow_sp.reset(new Window(name, ::subwin (m_window,
1319 bounds.origin.x), true));
1320 subwindow_sp->m_is_subwin = true;
1324 subwindow_sp.reset(new Window(name, ::newwin (bounds.size.height,
1327 bounds.origin.x), true));
1328 subwindow_sp->m_is_subwin = false;
1330 subwindow_sp->m_parent = this;
1333 m_prev_active_window_idx = m_curr_active_window_idx;
1334 m_curr_active_window_idx = m_subwindows.size();
1336 m_subwindows.push_back(subwindow_sp);
1337 ::top_panel (subwindow_sp->m_panel);
1338 m_needs_update = true;
1339 return subwindow_sp;
1343 RemoveSubWindow (Window *window)
1345 Windows::iterator pos, end = m_subwindows.end();
1347 for (pos = m_subwindows.begin(); pos != end; ++pos, ++i)
1349 if ((*pos).get() == window)
1351 if (m_prev_active_window_idx == i)
1352 m_prev_active_window_idx = UINT32_MAX;
1353 else if (m_prev_active_window_idx != UINT32_MAX && m_prev_active_window_idx > i)
1354 --m_prev_active_window_idx;
1356 if (m_curr_active_window_idx == i)
1357 m_curr_active_window_idx = UINT32_MAX;
1358 else if (m_curr_active_window_idx != UINT32_MAX && m_curr_active_window_idx > i)
1359 --m_curr_active_window_idx;
1361 m_subwindows.erase(pos);
1362 m_needs_update = true;
1366 ::touchwin (stdscr);
1374 FindSubWindow (const char *name)
1376 Windows::iterator pos, end = m_subwindows.end();
1378 for (pos = m_subwindows.begin(); pos != end; ++pos, ++i)
1380 if ((*pos)->m_name.compare(name) == 0)
1389 m_curr_active_window_idx = UINT32_MAX;
1390 m_prev_active_window_idx = UINT32_MAX;
1391 for (Windows::iterator pos = m_subwindows.begin();
1392 pos != m_subwindows.end();
1393 pos = m_subwindows.erase(pos))
1400 ::touchwin (stdscr);
1414 //----------------------------------------------------------------------
1415 // Window drawing utilities
1416 //----------------------------------------------------------------------
1418 DrawTitleBox (const char *title, const char *bottom_message = NULL)
1422 attr = A_BOLD | COLOR_PAIR(2);
1431 if (title && title[0])
1438 if (bottom_message && bottom_message[0])
1440 int bottom_message_length = strlen(bottom_message);
1441 int x = GetWidth() - 3 - (bottom_message_length + 2);
1445 MoveCursor (x, GetHeight() - 1);
1447 PutCString(bottom_message);
1452 MoveCursor (1, GetHeight() - 1);
1454 PutCStringTruncated (bottom_message, 1);
1465 if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw (*this, force))
1468 for (auto &subwindow_sp : m_subwindows)
1469 subwindow_sp->Draw(force);
1473 CreateHelpSubwindow ()
1477 const char *text = m_delegate_sp->WindowDelegateGetHelpText ();
1478 KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp ();
1479 if ((text && text[0]) || key_help)
1481 std::auto_ptr<HelpDialogDelegate> help_delegate_ap(new HelpDialogDelegate(text, key_help));
1482 const size_t num_lines = help_delegate_ap->GetNumLines();
1483 const size_t max_length = help_delegate_ap->GetMaxLineLength();
1484 Rect bounds = GetBounds();
1486 if (max_length + 4 < static_cast<size_t>(bounds.size.width))
1488 bounds.origin.x += (bounds.size.width - max_length + 4)/2;
1489 bounds.size.width = max_length + 4;
1493 if (bounds.size.width > 100)
1495 const int inset_w = bounds.size.width / 4;
1496 bounds.origin.x += inset_w;
1497 bounds.size.width -= 2*inset_w;
1501 if (num_lines + 2 < static_cast<size_t>(bounds.size.height))
1503 bounds.origin.y += (bounds.size.height - num_lines + 2)/2;
1504 bounds.size.height = num_lines + 2;
1508 if (bounds.size.height > 100)
1510 const int inset_h = bounds.size.height / 4;
1511 bounds.origin.y += inset_h;
1512 bounds.size.height -= 2*inset_h;
1515 WindowSP help_window_sp;
1516 Window *parent_window = GetParent();
1518 help_window_sp = parent_window->CreateSubWindow("Help", bounds, true);
1520 help_window_sp = CreateSubWindow("Help", bounds, true);
1521 help_window_sp->SetDelegate(WindowDelegateSP(help_delegate_ap.release()));
1528 virtual HandleCharResult
1529 HandleChar (int key)
1531 // Always check the active window first
1532 HandleCharResult result = eKeyNotHandled;
1533 WindowSP active_window_sp = GetActiveWindow ();
1534 if (active_window_sp)
1536 result = active_window_sp->HandleChar (key);
1537 if (result != eKeyNotHandled)
1543 result = m_delegate_sp->WindowDelegateHandleChar (*this, key);
1544 if (result != eKeyNotHandled)
1548 // Then check for any windows that want any keys
1549 // that weren't handled. This is typically only
1551 // Make a copy of the subwindows in case any HandleChar()
1552 // functions muck with the subwindows. If we don't do this,
1553 // we can crash when iterating over the subwindows.
1554 Windows subwindows (m_subwindows);
1555 for (auto subwindow_sp : subwindows)
1557 if (subwindow_sp->m_can_activate == false)
1559 HandleCharResult result = subwindow_sp->HandleChar(key);
1560 if (result != eKeyNotHandled)
1565 return eKeyNotHandled;
1569 SetActiveWindow (Window *window)
1571 const size_t num_subwindows = m_subwindows.size();
1572 for (size_t i=0; i<num_subwindows; ++i)
1574 if (m_subwindows[i].get() == window)
1576 m_prev_active_window_idx = m_curr_active_window_idx;
1577 ::top_panel (window->m_panel);
1578 m_curr_active_window_idx = i;
1588 if (!m_subwindows.empty())
1590 if (m_curr_active_window_idx >= m_subwindows.size())
1592 if (m_prev_active_window_idx < m_subwindows.size())
1594 m_curr_active_window_idx = m_prev_active_window_idx;
1595 m_prev_active_window_idx = UINT32_MAX;
1597 else if (IsActive())
1599 m_prev_active_window_idx = UINT32_MAX;
1600 m_curr_active_window_idx = UINT32_MAX;
1602 // Find first window that wants to be active if this window is active
1603 const size_t num_subwindows = m_subwindows.size();
1604 for (size_t i=0; i<num_subwindows; ++i)
1606 if (m_subwindows[i]->GetCanBeActive())
1608 m_curr_active_window_idx = i;
1615 if (m_curr_active_window_idx < m_subwindows.size())
1616 return m_subwindows[m_curr_active_window_idx];
1622 GetCanBeActive () const
1624 return m_can_activate;
1628 SetCanBeActive (bool b)
1633 const WindowDelegateSP &
1634 GetDelegate () const
1636 return m_delegate_sp;
1640 SetDelegate (const WindowDelegateSP &delegate_sp)
1642 m_delegate_sp = delegate_sp;
1655 return m_parent->GetActiveWindow().get() == this;
1657 return true; // Top level window is always active
1661 SelectNextWindowAsActive ()
1663 // Move active focus to next window
1664 const size_t num_subwindows = m_subwindows.size();
1665 if (m_curr_active_window_idx == UINT32_MAX)
1668 for (auto subwindow_sp : m_subwindows)
1670 if (subwindow_sp->GetCanBeActive())
1672 m_curr_active_window_idx = idx;
1678 else if (m_curr_active_window_idx + 1 < num_subwindows)
1680 bool handled = false;
1681 m_prev_active_window_idx = m_curr_active_window_idx;
1682 for (size_t idx=m_curr_active_window_idx + 1; idx<num_subwindows; ++idx)
1684 if (m_subwindows[idx]->GetCanBeActive())
1686 m_curr_active_window_idx = idx;
1693 for (size_t idx=0; idx<=m_prev_active_window_idx; ++idx)
1695 if (m_subwindows[idx]->GetCanBeActive())
1697 m_curr_active_window_idx = idx;
1705 m_prev_active_window_idx = m_curr_active_window_idx;
1706 for (size_t idx=0; idx<num_subwindows; ++idx)
1708 if (m_subwindows[idx]->GetCanBeActive())
1710 m_curr_active_window_idx = idx;
1720 return m_name.c_str();
1728 Windows m_subwindows;
1729 WindowDelegateSP m_delegate_sp;
1730 uint32_t m_curr_active_window_idx;
1731 uint32_t m_prev_active_window_idx;
1733 bool m_needs_update;
1734 bool m_can_activate;
1738 DISALLOW_COPY_AND_ASSIGN(Window);
1744 virtual ~MenuDelegate() = default;
1746 virtual MenuActionResult
1747 MenuDelegateAction (Menu &menu) = 0;
1750 class Menu : public WindowDelegate
1761 // Menubar or separator constructor
1764 // Menuitem constructor
1765 Menu (const char *name,
1766 const char *key_name,
1768 uint64_t identifier);
1770 ~Menu() override = default;
1772 const MenuDelegateSP &
1773 GetDelegate () const
1775 return m_delegate_sp;
1779 SetDelegate (const MenuDelegateSP &delegate_sp)
1781 m_delegate_sp = delegate_sp;
1785 RecalculateNameLengths();
1788 AddSubmenu (const MenuSP &menu_sp);
1791 DrawAndRunMenu (Window &window);
1794 DrawMenuTitle (Window &window, bool highlight);
1797 WindowDelegateDraw (Window &window, bool force) override;
1800 WindowDelegateHandleChar (Window &window, int key) override;
1803 ActionPrivate (Menu &menu)
1805 MenuActionResult result = MenuActionResult::NotHandled;
1808 result = m_delegate_sp->MenuDelegateAction (menu);
1809 if (result != MenuActionResult::NotHandled)
1814 result = m_parent->ActionPrivate(menu);
1815 if (result != MenuActionResult::NotHandled)
1818 return m_canned_result;
1824 // Call the recursive action so it can try to handle it
1825 // with the menu delegate, and if not, try our parent menu
1826 return ActionPrivate (*this);
1830 SetCannedResult (MenuActionResult result)
1832 m_canned_result = result;
1848 GetSelectedSubmenuIndex () const
1854 SetSelectedSubmenuIndex (int idx)
1866 GetStartingColumn() const
1872 SetStartingColumn(int col)
1884 SetKeyValue(int key_value)
1886 m_key_value = key_value;
1902 GetDrawWidth () const
1904 return m_max_submenu_name_length + m_max_submenu_key_name_length + 8;
1908 GetIdentifier() const
1910 return m_identifier;
1914 SetIdentifier (uint64_t identifier)
1916 m_identifier = identifier;
1921 std::string m_key_name;
1922 uint64_t m_identifier;
1926 int m_max_submenu_name_length;
1927 int m_max_submenu_key_name_length;
1931 WindowSP m_menu_window_sp;
1932 MenuActionResult m_canned_result;
1933 MenuDelegateSP m_delegate_sp;
1936 // Menubar or separator constructor
1937 Menu::Menu (Type type) :
1944 m_max_submenu_name_length (0),
1945 m_max_submenu_key_name_length (0),
1949 m_canned_result (MenuActionResult::NotHandled),
1954 // Menuitem constructor
1955 Menu::Menu (const char *name,
1956 const char *key_name,
1958 uint64_t identifier) :
1961 m_identifier (identifier),
1962 m_type (Type::Invalid),
1963 m_key_value (key_value),
1965 m_max_submenu_name_length (0),
1966 m_max_submenu_key_name_length (0),
1970 m_canned_result (MenuActionResult::NotHandled),
1973 if (name && name[0])
1976 m_type = Type::Item;
1977 if (key_name && key_name[0])
1978 m_key_name = key_name;
1982 m_type = Type::Separator;
1987 Menu::RecalculateNameLengths()
1989 m_max_submenu_name_length = 0;
1990 m_max_submenu_key_name_length = 0;
1991 Menus &submenus = GetSubmenus();
1992 const size_t num_submenus = submenus.size();
1993 for (size_t i=0; i<num_submenus; ++i)
1995 Menu *submenu = submenus[i].get();
1996 if (static_cast<size_t>(m_max_submenu_name_length) < submenu->m_name.size())
1997 m_max_submenu_name_length = submenu->m_name.size();
1998 if (static_cast<size_t>(m_max_submenu_key_name_length) < submenu->m_key_name.size())
1999 m_max_submenu_key_name_length = submenu->m_key_name.size();
2004 Menu::AddSubmenu (const MenuSP &menu_sp)
2006 menu_sp->m_parent = this;
2007 if (static_cast<size_t>(m_max_submenu_name_length) < menu_sp->m_name.size())
2008 m_max_submenu_name_length = menu_sp->m_name.size();
2009 if (static_cast<size_t>(m_max_submenu_key_name_length) < menu_sp->m_key_name.size())
2010 m_max_submenu_key_name_length = menu_sp->m_key_name.size();
2011 m_submenus.push_back(menu_sp);
2015 Menu::DrawMenuTitle (Window &window, bool highlight)
2017 if (m_type == Type::Separator)
2019 window.MoveCursor(0, window.GetCursorY());
2020 window.PutChar(ACS_LTEE);
2021 int width = window.GetWidth();
2025 for (int i=0; i< width; ++i)
2026 window.PutChar(ACS_HLINE);
2028 window.PutChar(ACS_RTEE);
2032 const int shortcut_key = m_key_value;
2033 bool underlined_shortcut = false;
2034 const attr_t hilgight_attr = A_REVERSE;
2036 window.AttributeOn(hilgight_attr);
2037 if (isprint(shortcut_key))
2039 size_t lower_pos = m_name.find(tolower(shortcut_key));
2040 size_t upper_pos = m_name.find(toupper(shortcut_key));
2041 const char *name = m_name.c_str();
2042 size_t pos = std::min<size_t>(lower_pos, upper_pos);
2043 if (pos != std::string::npos)
2045 underlined_shortcut = true;
2048 window.PutCString(name, pos);
2051 const attr_t shortcut_attr = A_UNDERLINE|A_BOLD;
2052 window.AttributeOn (shortcut_attr);
2053 window.PutChar(name[0]);
2054 window.AttributeOff(shortcut_attr);
2057 window.PutCString(name);
2061 if (!underlined_shortcut)
2063 window.PutCString(m_name.c_str());
2067 window.AttributeOff(hilgight_attr);
2069 if (m_key_name.empty())
2071 if (!underlined_shortcut && isprint(m_key_value))
2073 window.AttributeOn (COLOR_PAIR(3));
2074 window.Printf (" (%c)", m_key_value);
2075 window.AttributeOff (COLOR_PAIR(3));
2080 window.AttributeOn (COLOR_PAIR(3));
2081 window.Printf (" (%s)", m_key_name.c_str());
2082 window.AttributeOff (COLOR_PAIR(3));
2088 Menu::WindowDelegateDraw (Window &window, bool force)
2090 Menus &submenus = GetSubmenus();
2091 const size_t num_submenus = submenus.size();
2092 const int selected_idx = GetSelectedSubmenuIndex();
2093 Menu::Type menu_type = GetType ();
2096 case Menu::Type::Bar:
2098 window.SetBackground(2);
2099 window.MoveCursor(0, 0);
2100 for (size_t i=0; i<num_submenus; ++i)
2102 Menu *menu = submenus[i].get();
2104 window.PutChar(' ');
2105 menu->SetStartingColumn (window.GetCursorX());
2106 window.PutCString("| ");
2107 menu->DrawMenuTitle (window, false);
2109 window.PutCString(" |");
2110 window.DeferredRefresh();
2114 case Menu::Type::Item:
2122 window.SetBackground(2);
2124 for (size_t i=0; i<num_submenus; ++i)
2126 const bool is_selected =
2127 (i == static_cast<size_t>(selected_idx));
2128 window.MoveCursor(x, y + i);
2131 // Remember where we want the cursor to be
2135 submenus[i]->DrawMenuTitle (window, is_selected);
2137 window.MoveCursor(cursor_x, cursor_y);
2138 window.DeferredRefresh();
2143 case Menu::Type::Separator:
2146 return true; // Drawing handled...
2150 Menu::WindowDelegateHandleChar (Window &window, int key)
2152 HandleCharResult result = eKeyNotHandled;
2154 Menus &submenus = GetSubmenus();
2155 const size_t num_submenus = submenus.size();
2156 const int selected_idx = GetSelectedSubmenuIndex();
2157 Menu::Type menu_type = GetType ();
2158 if (menu_type == Menu::Type::Bar)
2165 // Show last menu or first menu
2166 if (selected_idx < static_cast<int>(num_submenus))
2167 run_menu_sp = submenus[selected_idx];
2168 else if (!submenus.empty())
2169 run_menu_sp = submenus.front();
2170 result = eKeyHandled;
2176 if (m_selected >= static_cast<int>(num_submenus))
2178 if (m_selected < static_cast<int>(num_submenus))
2179 run_menu_sp = submenus[m_selected];
2180 else if (!submenus.empty())
2181 run_menu_sp = submenus.front();
2182 result = eKeyHandled;
2190 m_selected = num_submenus - 1;
2191 if (m_selected < static_cast<int>(num_submenus))
2192 run_menu_sp = submenus[m_selected];
2193 else if (!submenus.empty())
2194 run_menu_sp = submenus.front();
2195 result = eKeyHandled;
2200 for (size_t i=0; i<num_submenus; ++i)
2202 if (submenus[i]->GetKeyValue() == key)
2204 SetSelectedSubmenuIndex(i);
2205 run_menu_sp = submenus[i];
2206 result = eKeyHandled;
2215 // Run the action on this menu in case we need to populate the
2216 // menu with dynamic content and also in case check marks, and
2217 // any other menu decorations need to be calculated
2218 if (run_menu_sp->Action() == MenuActionResult::Quit)
2219 return eQuitApplication;
2222 menu_bounds.origin.x = run_menu_sp->GetStartingColumn();
2223 menu_bounds.origin.y = 1;
2224 menu_bounds.size.width = run_menu_sp->GetDrawWidth();
2225 menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2;
2226 if (m_menu_window_sp)
2227 window.GetParent()->RemoveSubWindow(m_menu_window_sp.get());
2229 m_menu_window_sp = window.GetParent()->CreateSubWindow (run_menu_sp->GetName().c_str(),
2232 m_menu_window_sp->SetDelegate (run_menu_sp);
2235 else if (menu_type == Menu::Type::Item)
2240 if (m_submenus.size() > 1)
2242 const int start_select = m_selected;
2243 while (++m_selected != start_select)
2245 if (static_cast<size_t>(m_selected) >= num_submenus)
2247 if (m_submenus[m_selected]->GetType() == Type::Separator)
2257 if (m_submenus.size() > 1)
2259 const int start_select = m_selected;
2260 while (--m_selected != start_select)
2262 if (m_selected < static_cast<int>(0))
2263 m_selected = num_submenus - 1;
2264 if (m_submenus[m_selected]->GetType() == Type::Separator)
2274 if (static_cast<size_t>(selected_idx) < num_submenus)
2276 if (submenus[selected_idx]->Action() == MenuActionResult::Quit)
2277 return eQuitApplication;
2278 window.GetParent()->RemoveSubWindow(&window);
2283 case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in case other chars are entered for escaped sequences
2284 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;
2306 else if (menu_type == Menu::Type::Separator)
2315 Application (FILE *in, FILE *out) :
2326 m_window_delegates.clear();
2327 m_window_sp.reset();
2330 ::delscreen(m_screen);
2338 ::setlocale(LC_ALL, "");
2339 ::setlocale(LC_CTYPE, "");
2343 m_screen = ::newterm(NULL, m_out, m_in);
2348 ::keypad(stdscr,TRUE);
2358 Run (Debugger &debugger)
2361 int delay_in_tenths_of_a_second = 1;
2363 // Alas the threading model in curses is a bit lame so we need to
2364 // resort to polling every 0.5 seconds. We could poll for stdin
2365 // ourselves and then pass the keys down but then we need to
2366 // translate all of the escape sequences ourselves. So we resort to
2367 // polling for input because we need to receive async process events
2368 // while in this loop.
2370 halfdelay(delay_in_tenths_of_a_second); // Poll using some number of tenths of seconds seconds when calling Window::GetChar()
2372 ListenerSP listener_sp (new Listener ("lldb.IOHandler.curses.Application"));
2373 ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass());
2374 ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass());
2375 ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass());
2376 debugger.EnableForwardEvents (listener_sp);
2379 #if defined(__APPLE__)
2380 std::deque<int> escape_chars;
2387 m_window_sp->Draw(false);
2388 // All windows should be calling Window::DeferredRefresh() instead
2389 // of Window::Refresh() so we can do a single update and avoid
2390 // any screen blinking
2393 // Cursor hiding isn't working on MacOSX, so hide it in the top left corner
2394 m_window_sp->MoveCursor(0, 0);
2400 #if defined(__APPLE__)
2401 // Terminal.app doesn't map its function keys correctly, F1-F4 default to:
2402 // \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if possible
2404 if (escape_chars.empty())
2405 ch = m_window_sp->GetChar();
2408 ch = escape_chars.front();
2409 escape_chars.pop_front();
2411 if (ch == KEY_ESCAPE)
2413 int ch2 = m_window_sp->GetChar();
2416 int ch3 = m_window_sp->GetChar();
2419 case 'P': ch = KEY_F(1); break;
2420 case 'Q': ch = KEY_F(2); break;
2421 case 'R': ch = KEY_F(3); break;
2422 case 'S': ch = KEY_F(4); break;
2424 escape_chars.push_back(ch2);
2426 escape_chars.push_back(ch3);
2431 escape_chars.push_back(ch2);
2434 int ch = m_window_sp->GetChar();
2439 if (feof(m_in) || ferror(m_in))
2445 // Just a timeout from using halfdelay(), check for events
2447 while (listener_sp->PeekAtNextEvent())
2449 listener_sp->GetNextEvent(event_sp);
2453 Broadcaster *broadcaster = event_sp->GetBroadcaster();
2456 //uint32_t event_type = event_sp->GetType();
2457 ConstString broadcaster_class (broadcaster->GetBroadcasterClass());
2458 if (broadcaster_class == broadcaster_class_process)
2460 debugger.GetCommandInterpreter().UpdateExecutionContext(NULL);
2462 continue; // Don't get any key, just update our view
2471 HandleCharResult key_result = m_window_sp->HandleChar(ch);
2475 debugger.GetCommandInterpreter().UpdateExecutionContext(NULL);
2478 case eKeyNotHandled:
2480 case eQuitApplication:
2487 debugger.CancelForwardEvents (listener_sp);
2494 m_window_sp.reset (new Window ("main", stdscr, false));
2499 GetWindowDelegates ()
2501 return m_window_delegates;
2505 WindowSP m_window_sp;
2506 WindowDelegates m_window_delegates;
2512 } // namespace curses
2514 using namespace curses;
2518 ValueObjectSP valobj;
2523 bool might_have_children;
2525 bool calculated_children;
2526 std::vector<Row> children;
2528 Row (const ValueObjectSP &v, Row *p) :
2534 might_have_children (v ? v->MightHaveChildren() : false),
2536 calculated_children (false),
2545 return 1 + parent->GetDepth();
2553 if (!calculated_children)
2555 calculated_children = true;
2558 const size_t num_children = valobj->GetNumChildren();
2559 for (size_t i=0; i<num_children; ++i)
2561 children.push_back(Row (valobj->GetChildAtIndex(i, true), this));
2574 DrawTree (Window &window)
2577 parent->DrawTreeForChild (window, this, 0);
2579 if (might_have_children)
2581 // It we can get UTF8 characters to work we should try to use the "symbol"
2582 // UTF8 string below
2583 // const char *symbol = "";
2584 // if (row.expanded)
2585 // symbol = "\xe2\x96\xbd ";
2587 // symbol = "\xe2\x96\xb7 ";
2588 // window.PutCString (symbol);
2590 // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
2591 // 'v' or '>' character...
2593 // window.PutChar (ACS_DARROW);
2595 // window.PutChar (ACS_RARROW);
2596 // Since we can't find any good looking right arrow/down arrow
2597 // symbols, just use a diamond...
2598 window.PutChar (ACS_DIAMOND);
2599 window.PutChar (ACS_HLINE);
2604 DrawTreeForChild (Window &window, Row *child, uint32_t reverse_depth)
2607 parent->DrawTreeForChild (window, this, reverse_depth + 1);
2609 if (&children.back() == child)
2612 if (reverse_depth == 0)
2614 window.PutChar (ACS_LLCORNER);
2615 window.PutChar (ACS_HLINE);
2619 window.PutChar (' ');
2620 window.PutChar (' ');
2625 if (reverse_depth == 0)
2627 window.PutChar (ACS_LTEE);
2628 window.PutChar (ACS_HLINE);
2632 window.PutChar (ACS_VLINE);
2633 window.PutChar (' ');
2639 struct DisplayOptions
2650 virtual ~TreeDelegate() = default;
2652 virtual void TreeDelegateDrawTreeItem (TreeItem &item, Window &window) = 0;
2653 virtual void TreeDelegateGenerateChildren (TreeItem &item) = 0;
2654 virtual bool TreeDelegateItemSelected (TreeItem &item) = 0; // Return true if we need to update views
2657 typedef std::shared_ptr<TreeDelegate> TreeDelegateSP;
2663 TreeItem (TreeItem *parent, TreeDelegate &delegate, bool might_have_children) :
2665 m_delegate (delegate),
2670 m_might_have_children (might_have_children),
2671 m_is_expanded (false)
2676 operator=(const TreeItem &rhs)
2680 m_parent = rhs.m_parent;
2681 m_delegate = rhs.m_delegate;
2682 m_user_data = rhs.m_user_data;
2683 m_identifier = rhs.m_identifier;
2684 m_row_idx = rhs.m_row_idx;
2685 m_children = rhs.m_children;
2686 m_might_have_children = rhs.m_might_have_children;
2687 m_is_expanded = rhs.m_is_expanded;
2696 return 1 + m_parent->GetDepth();
2701 GetRowIndex () const
2713 Resize (size_t n, const TreeItem &t)
2715 m_children.resize(n, t);
2719 operator [](size_t i)
2721 return m_children[i];
2725 SetRowIndex (int row_idx)
2727 m_row_idx = row_idx;
2733 m_delegate.TreeDelegateGenerateChildren (*this);
2734 return m_children.size();
2740 m_delegate.TreeDelegateItemSelected(*this);
2744 CalculateRowIndexes (int &row_idx)
2746 SetRowIndex(row_idx);
2749 const bool expanded = IsExpanded();
2751 // The root item must calculate its children,
2752 // or we must calculate the number of children
2753 // if the item is expanded
2754 if (m_parent == NULL || expanded)
2757 for (auto &item : m_children)
2760 item.CalculateRowIndexes(row_idx);
2762 item.SetRowIndex(-1);
2775 return m_is_expanded;
2781 m_is_expanded = true;
2787 m_is_expanded = false;
2791 Draw (Window &window,
2792 const int first_visible_row,
2793 const uint32_t selected_row_idx,
2797 if (num_rows_left <= 0)
2800 if (m_row_idx >= first_visible_row)
2802 window.MoveCursor(2, row_idx + 1);
2805 m_parent->DrawTreeForChild (window, this, 0);
2807 if (m_might_have_children)
2809 // It we can get UTF8 characters to work we should try to use the "symbol"
2810 // UTF8 string below
2811 // const char *symbol = "";
2812 // if (row.expanded)
2813 // symbol = "\xe2\x96\xbd ";
2815 // symbol = "\xe2\x96\xb7 ";
2816 // window.PutCString (symbol);
2818 // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
2819 // 'v' or '>' character...
2821 // window.PutChar (ACS_DARROW);
2823 // window.PutChar (ACS_RARROW);
2824 // Since we can't find any good looking right arrow/down arrow
2825 // symbols, just use a diamond...
2826 window.PutChar (ACS_DIAMOND);
2827 window.PutChar (ACS_HLINE);
2830 (selected_row_idx == static_cast<size_t>(m_row_idx)) && window.IsActive();
2833 window.AttributeOn(A_REVERSE);
2835 m_delegate.TreeDelegateDrawTreeItem(*this, window);
2838 window.AttributeOff(A_REVERSE);
2843 if (num_rows_left <= 0)
2844 return false; // We are done drawing...
2848 for (auto &item : m_children)
2850 // If we displayed all the rows and item.Draw() returns
2851 // false we are done drawing and can exit this for loop
2852 if (item.Draw(window, first_visible_row, selected_row_idx, row_idx, num_rows_left) == false)
2856 return num_rows_left >= 0; // Return true if not done drawing yet
2860 DrawTreeForChild (Window &window, TreeItem *child, uint32_t reverse_depth)
2863 m_parent->DrawTreeForChild (window, this, reverse_depth + 1);
2865 if (&m_children.back() == child)
2868 if (reverse_depth == 0)
2870 window.PutChar (ACS_LLCORNER);
2871 window.PutChar (ACS_HLINE);
2875 window.PutChar (' ');
2876 window.PutChar (' ');
2881 if (reverse_depth == 0)
2883 window.PutChar (ACS_LTEE);
2884 window.PutChar (ACS_HLINE);
2888 window.PutChar (ACS_VLINE);
2889 window.PutChar (' ');
2895 GetItemForRowIndex (uint32_t row_idx)
2897 if (static_cast<uint32_t>(m_row_idx) == row_idx)
2899 if (m_children.empty())
2903 for (auto &item : m_children)
2905 TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx);
2906 if (selected_item_ptr)
2907 return selected_item_ptr;
2920 SetUserData (void *user_data)
2922 m_user_data = user_data;
2926 GetIdentifier() const
2928 return m_identifier;
2932 SetIdentifier (uint64_t identifier)
2934 m_identifier = identifier;
2938 SetMightHaveChildren (bool b)
2940 m_might_have_children = b;
2945 TreeDelegate &m_delegate;
2947 uint64_t m_identifier;
2948 int m_row_idx; // Zero based visible row index, -1 if not visible or for the root item
2949 std::vector<TreeItem> m_children;
2950 bool m_might_have_children;
2954 class TreeWindowDelegate : public WindowDelegate
2957 TreeWindowDelegate (Debugger &debugger, const TreeDelegateSP &delegate_sp) :
2958 m_debugger (debugger),
2959 m_delegate_sp (delegate_sp),
2960 m_root (NULL, *delegate_sp, true),
2961 m_selected_item (NULL),
2963 m_selected_row_idx (0),
2964 m_first_visible_row (0),
2973 NumVisibleRows () const
2975 return m_max_y - m_min_y;
2979 WindowDelegateDraw (Window &window, bool force) override
2981 ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext());
2982 Process *process = exe_ctx.GetProcessPtr();
2984 bool display_content = false;
2987 StateType state = process->GetState();
2988 if (StateIsStoppedState(state, true))
2990 // We are stopped, so it is ok to
2991 display_content = true;
2993 else if (StateIsRunningState(state))
2995 return true; // Don't do any updating when we are running
3001 m_max_x = window.GetWidth() - 1;
3002 m_max_y = window.GetHeight() - 1;
3005 window.DrawTitleBox (window.GetName());
3007 if (display_content)
3009 const int num_visible_rows = NumVisibleRows();
3011 m_root.CalculateRowIndexes(m_num_rows);
3013 // If we unexpanded while having something selected our
3014 // total number of rows is less than the num visible rows,
3015 // then make sure we show all the rows by setting the first
3016 // visible row accordingly.
3017 if (m_first_visible_row > 0 && m_num_rows < num_visible_rows)
3018 m_first_visible_row = 0;
3020 // Make sure the selected row is always visible
3021 if (m_selected_row_idx < m_first_visible_row)
3022 m_first_visible_row = m_selected_row_idx;
3023 else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
3024 m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
3027 int num_rows_left = num_visible_rows;
3028 m_root.Draw (window, m_first_visible_row, m_selected_row_idx, row_idx, num_rows_left);
3029 // Get the selected row
3030 m_selected_item = m_root.GetItemForRowIndex (m_selected_row_idx);
3034 m_selected_item = NULL;
3037 window.DeferredRefresh();
3040 return true; // Drawing handled
3044 WindowDelegateGetHelpText () override
3046 return "Thread window keyboard shortcuts:";
3050 WindowDelegateGetKeyHelp () override
3052 static curses::KeyHelp g_source_view_key_help[] = {
3053 { KEY_UP, "Select previous item" },
3054 { KEY_DOWN, "Select next item" },
3055 { KEY_RIGHT, "Expand the selected item" },
3056 { KEY_LEFT, "Unexpand the selected item or select parent if not expanded" },
3057 { KEY_PPAGE, "Page up" },
3058 { KEY_NPAGE, "Page down" },
3059 { 'h', "Show help dialog" },
3060 { ' ', "Toggle item expansion" },
3062 { '.', "Page down" },
3065 return g_source_view_key_help;
3069 WindowDelegateHandleChar (Window &window, int c) override
3076 if (m_first_visible_row > 0)
3078 if (m_first_visible_row > m_max_y)
3079 m_first_visible_row -= m_max_y;
3081 m_first_visible_row = 0;
3082 m_selected_row_idx = m_first_visible_row;
3083 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3084 if (m_selected_item)
3085 m_selected_item->ItemWasSelected ();
3092 if (m_num_rows > m_max_y)
3094 if (m_first_visible_row + m_max_y < m_num_rows)
3096 m_first_visible_row += m_max_y;
3097 m_selected_row_idx = m_first_visible_row;
3098 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3099 if (m_selected_item)
3100 m_selected_item->ItemWasSelected ();
3106 if (m_selected_row_idx > 0)
3108 --m_selected_row_idx;
3109 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3110 if (m_selected_item)
3111 m_selected_item->ItemWasSelected ();
3116 if (m_selected_row_idx + 1 < m_num_rows)
3118 ++m_selected_row_idx;
3119 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3120 if (m_selected_item)
3121 m_selected_item->ItemWasSelected ();
3126 if (m_selected_item)
3128 if (!m_selected_item->IsExpanded())
3129 m_selected_item->Expand();
3134 if (m_selected_item)
3136 if (m_selected_item->IsExpanded())
3137 m_selected_item->Unexpand();
3138 else if (m_selected_item->GetParent())
3140 m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex();
3141 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3142 if (m_selected_item)
3143 m_selected_item->ItemWasSelected ();
3149 // Toggle expansion state when SPACE is pressed
3150 if (m_selected_item)
3152 if (m_selected_item->IsExpanded())
3153 m_selected_item->Unexpand();
3155 m_selected_item->Expand();
3160 window.CreateHelpSubwindow ();
3166 return eKeyNotHandled;
3170 Debugger &m_debugger;
3171 TreeDelegateSP m_delegate_sp;
3173 TreeItem *m_selected_item;
3175 int m_selected_row_idx;
3176 int m_first_visible_row;
3183 class FrameTreeDelegate : public TreeDelegate
3186 FrameTreeDelegate () :
3189 FormatEntity::Parse ("frame #${frame.index}: {${function.name}${function.pc-offset}}}",
3193 ~FrameTreeDelegate() override = default;
3196 TreeDelegateDrawTreeItem (TreeItem &item, Window &window) override
3198 Thread* thread = (Thread*)item.GetUserData();
3201 const uint64_t frame_idx = item.GetIdentifier();
3202 StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_idx);
3206 const SymbolContext &sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
3207 ExecutionContext exe_ctx (frame_sp);
3208 if (FormatEntity::Format(m_format, strm, &sc, &exe_ctx, NULL, NULL, false, false))
3211 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
3218 TreeDelegateGenerateChildren (TreeItem &item) override
3220 // No children for frames yet...
3224 TreeDelegateItemSelected (TreeItem &item) override
3226 Thread* thread = (Thread*)item.GetUserData();
3229 thread->GetProcess()->GetThreadList().SetSelectedThreadByID(thread->GetID());
3230 const uint64_t frame_idx = item.GetIdentifier();
3231 thread->SetSelectedFrameByIndex(frame_idx);
3238 FormatEntity::Entry m_format;
3241 class ThreadTreeDelegate : public TreeDelegate
3244 ThreadTreeDelegate (Debugger &debugger) :
3246 m_debugger (debugger),
3247 m_tid (LLDB_INVALID_THREAD_ID),
3248 m_stop_id (UINT32_MAX)
3250 FormatEntity::Parse ("thread #${thread.index}: tid = ${thread.id}{, stop reason = ${thread.stop-reason}}",
3254 ~ThreadTreeDelegate() override = default;
3259 return m_debugger.GetCommandInterpreter().GetExecutionContext().GetProcessSP();
3263 GetThread (const TreeItem &item)
3265 ProcessSP process_sp = GetProcess ();
3267 return process_sp->GetThreadList().FindThreadByID(item.GetIdentifier());
3272 TreeDelegateDrawTreeItem (TreeItem &item, Window &window) override
3274 ThreadSP thread_sp = GetThread (item);
3278 ExecutionContext exe_ctx (thread_sp);
3279 if (FormatEntity::Format (m_format, strm, NULL, &exe_ctx, NULL, NULL, false, false))
3282 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
3288 TreeDelegateGenerateChildren (TreeItem &item) override
3290 ProcessSP process_sp = GetProcess ();
3291 if (process_sp && process_sp->IsAlive())
3293 StateType state = process_sp->GetState();
3294 if (StateIsStoppedState(state, true))
3296 ThreadSP thread_sp = GetThread (item);
3299 if (m_stop_id == process_sp->GetStopID() && thread_sp->GetID() == m_tid)
3300 return; // Children are already up to date
3301 if (!m_frame_delegate_sp)
3303 // Always expand the thread item the first time we show it
3304 m_frame_delegate_sp.reset (new FrameTreeDelegate());
3307 m_stop_id = process_sp->GetStopID();
3308 m_tid = thread_sp->GetID();
3310 TreeItem t (&item, *m_frame_delegate_sp, false);
3311 size_t num_frames = thread_sp->GetStackFrameCount();
3312 item.Resize (num_frames, t);
3313 for (size_t i=0; i<num_frames; ++i)
3315 item[i].SetUserData(thread_sp.get());
3316 item[i].SetIdentifier(i);
3322 item.ClearChildren();
3326 TreeDelegateItemSelected (TreeItem &item) override
3328 ProcessSP process_sp = GetProcess ();
3329 if (process_sp && process_sp->IsAlive())
3331 StateType state = process_sp->GetState();
3332 if (StateIsStoppedState(state, true))
3334 ThreadSP thread_sp = GetThread (item);
3337 ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList();
3338 Mutex::Locker locker (thread_list.GetMutex());
3339 ThreadSP selected_thread_sp = thread_list.GetSelectedThread();
3340 if (selected_thread_sp->GetID() != thread_sp->GetID())
3342 thread_list.SetSelectedThreadByID(thread_sp->GetID());
3352 Debugger &m_debugger;
3353 std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp;
3354 lldb::user_id_t m_tid;
3356 FormatEntity::Entry m_format;
3359 class ThreadsTreeDelegate : public TreeDelegate
3362 ThreadsTreeDelegate (Debugger &debugger) :
3364 m_thread_delegate_sp (),
3365 m_debugger (debugger),
3366 m_stop_id (UINT32_MAX)
3368 FormatEntity::Parse("process ${process.id}{, name = ${process.name}}",
3372 ~ThreadsTreeDelegate() override = default;
3377 return m_debugger.GetCommandInterpreter().GetExecutionContext().GetProcessSP();
3381 TreeDelegateDrawTreeItem (TreeItem &item, Window &window) override
3383 ProcessSP process_sp = GetProcess ();
3384 if (process_sp && process_sp->IsAlive())
3387 ExecutionContext exe_ctx (process_sp);
3388 if (FormatEntity::Format (m_format, strm, NULL, &exe_ctx, NULL, NULL, false, false))
3391 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
3397 TreeDelegateGenerateChildren (TreeItem &item) override
3399 ProcessSP process_sp = GetProcess ();
3400 if (process_sp && process_sp->IsAlive())
3402 StateType state = process_sp->GetState();
3403 if (StateIsStoppedState(state, true))
3405 const uint32_t stop_id = process_sp->GetStopID();
3406 if (m_stop_id == stop_id)
3407 return; // Children are already up to date
3409 m_stop_id = stop_id;
3411 if (!m_thread_delegate_sp)
3413 // Always expand the thread item the first time we show it
3415 m_thread_delegate_sp.reset (new ThreadTreeDelegate(m_debugger));
3418 TreeItem t (&item, *m_thread_delegate_sp, false);
3419 ThreadList &threads = process_sp->GetThreadList();
3420 Mutex::Locker locker (threads.GetMutex());
3421 size_t num_threads = threads.GetSize();
3422 item.Resize (num_threads, t);
3423 for (size_t i=0; i<num_threads; ++i)
3425 item[i].SetIdentifier(threads.GetThreadAtIndex(i)->GetID());
3426 item[i].SetMightHaveChildren(true);
3431 item.ClearChildren();
3435 TreeDelegateItemSelected (TreeItem &item) override
3441 std::shared_ptr<ThreadTreeDelegate> m_thread_delegate_sp;
3442 Debugger &m_debugger;
3444 FormatEntity::Entry m_format;
3447 class ValueObjectListDelegate : public WindowDelegate
3450 ValueObjectListDelegate () :
3453 m_selected_row (NULL),
3454 m_selected_row_idx (0),
3455 m_first_visible_row (0),
3462 ValueObjectListDelegate (ValueObjectList &valobj_list) :
3463 m_valobj_list (valobj_list),
3465 m_selected_row (NULL),
3466 m_selected_row_idx (0),
3467 m_first_visible_row (0),
3472 SetValues (valobj_list);
3475 ~ValueObjectListDelegate() override = default;
3478 SetValues (ValueObjectList &valobj_list)
3480 m_selected_row = NULL;
3481 m_selected_row_idx = 0;
3482 m_first_visible_row = 0;
3485 m_valobj_list = valobj_list;
3486 const size_t num_values = m_valobj_list.GetSize();
3487 for (size_t i=0; i<num_values; ++i)
3488 m_rows.push_back(Row(m_valobj_list.GetValueObjectAtIndex(i), NULL));
3492 WindowDelegateDraw (Window &window, bool force) override
3497 m_max_x = window.GetWidth() - 1;
3498 m_max_y = window.GetHeight() - 1;
3501 window.DrawTitleBox (window.GetName());
3503 const int num_visible_rows = NumVisibleRows();
3504 const int num_rows = CalculateTotalNumberRows (m_rows);
3506 // If we unexpanded while having something selected our
3507 // total number of rows is less than the num visible rows,
3508 // then make sure we show all the rows by setting the first
3509 // visible row accordingly.
3510 if (m_first_visible_row > 0 && num_rows < num_visible_rows)
3511 m_first_visible_row = 0;
3513 // Make sure the selected row is always visible
3514 if (m_selected_row_idx < m_first_visible_row)
3515 m_first_visible_row = m_selected_row_idx;
3516 else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
3517 m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
3519 DisplayRows (window, m_rows, g_options);
3521 window.DeferredRefresh();
3523 // Get the selected row
3524 m_selected_row = GetRowForRowIndex (m_selected_row_idx);
3525 // Keep the cursor on the selected row so the highlight and the cursor
3526 // are always on the same line
3528 window.MoveCursor (m_selected_row->x,
3531 return true; // Drawing handled
3535 WindowDelegateGetKeyHelp () override
3537 static curses::KeyHelp g_source_view_key_help[] = {
3538 { KEY_UP, "Select previous item" },
3539 { KEY_DOWN, "Select next item" },
3540 { KEY_RIGHT, "Expand selected item" },
3541 { KEY_LEFT, "Unexpand selected item or select parent if not expanded" },
3542 { KEY_PPAGE, "Page up" },
3543 { KEY_NPAGE, "Page down" },
3544 { 'A', "Format as annotated address" },
3545 { 'b', "Format as binary" },
3546 { 'B', "Format as hex bytes with ASCII" },
3547 { 'c', "Format as character" },
3548 { 'd', "Format as a signed integer" },
3549 { 'D', "Format selected value using the default format for the type" },
3550 { 'f', "Format as float" },
3551 { 'h', "Show help dialog" },
3552 { 'i', "Format as instructions" },
3553 { 'o', "Format as octal" },
3554 { 'p', "Format as pointer" },
3555 { 's', "Format as C string" },
3556 { 't', "Toggle showing/hiding type names" },
3557 { 'u', "Format as an unsigned integer" },
3558 { 'x', "Format as hex" },
3559 { 'X', "Format as uppercase hex" },
3560 { ' ', "Toggle item expansion" },
3562 { '.', "Page down" },
3565 return g_source_view_key_help;
3569 WindowDelegateHandleChar (Window &window, int c) override
3587 // Change the format for the currently selected item
3589 m_selected_row->valobj->SetFormat (FormatForChar (c));
3593 // Toggle showing type names
3594 g_options.show_types = !g_options.show_types;
3600 if (m_first_visible_row > 0)
3602 if (static_cast<int>(m_first_visible_row) > m_max_y)
3603 m_first_visible_row -= m_max_y;
3605 m_first_visible_row = 0;
3606 m_selected_row_idx = m_first_visible_row;
3613 if (m_num_rows > static_cast<size_t>(m_max_y))
3615 if (m_first_visible_row + m_max_y < m_num_rows)
3617 m_first_visible_row += m_max_y;
3618 m_selected_row_idx = m_first_visible_row;
3624 if (m_selected_row_idx > 0)
3625 --m_selected_row_idx;
3629 if (m_selected_row_idx + 1 < m_num_rows)
3630 ++m_selected_row_idx;
3636 if (!m_selected_row->expanded)
3637 m_selected_row->Expand();
3644 if (m_selected_row->expanded)
3645 m_selected_row->Unexpand();
3646 else if (m_selected_row->parent)
3647 m_selected_row_idx = m_selected_row->parent->row_idx;
3652 // Toggle expansion state when SPACE is pressed
3655 if (m_selected_row->expanded)
3656 m_selected_row->Unexpand();
3658 m_selected_row->Expand();
3663 window.CreateHelpSubwindow ();
3669 return eKeyNotHandled;
3673 ValueObjectList m_valobj_list;
3674 std::vector<Row> m_rows;
3675 Row *m_selected_row;
3676 uint32_t m_selected_row_idx;
3677 uint32_t m_first_visible_row;
3678 uint32_t m_num_rows;
3685 FormatForChar (int c)
3689 case 'x': return eFormatHex;
3690 case 'X': return eFormatHexUppercase;
3691 case 'o': return eFormatOctal;
3692 case 's': return eFormatCString;
3693 case 'u': return eFormatUnsigned;
3694 case 'd': return eFormatDecimal;
3695 case 'D': return eFormatDefault;
3696 case 'i': return eFormatInstruction;
3697 case 'A': return eFormatAddressInfo;
3698 case 'p': return eFormatPointer;
3699 case 'c': return eFormatChar;
3700 case 'b': return eFormatBinary;
3701 case 'B': return eFormatBytesWithASCII;
3702 case 'f': return eFormatFloat;
3704 return eFormatDefault;
3708 DisplayRowObject (Window &window,
3710 DisplayOptions &options,
3714 ValueObject *valobj = row.valobj.get();
3719 const char *type_name = options.show_types ? valobj->GetTypeName().GetCString() : NULL;
3720 const char *name = valobj->GetName().GetCString();
3721 const char *value = valobj->GetValueAsCString ();
3722 const char *summary = valobj->GetSummaryAsCString ();
3724 window.MoveCursor (row.x, row.y);
3726 row.DrawTree (window);
3729 window.AttributeOn(A_REVERSE);
3731 if (type_name && type_name[0])
3732 window.Printf ("(%s) ", type_name);
3734 if (name && name[0])
3735 window.PutCString(name);
3737 attr_t changd_attr = 0;
3738 if (valobj->GetValueDidChange())
3739 changd_attr = COLOR_PAIR(5) | A_BOLD;
3741 if (value && value[0])
3743 window.PutCString(" = ");
3745 window.AttributeOn(changd_attr);
3746 window.PutCString (value);
3748 window.AttributeOff(changd_attr);
3751 if (summary && summary[0])
3753 window.PutChar(' ');
3755 window.AttributeOn(changd_attr);
3756 window.PutCString(summary);
3758 window.AttributeOff(changd_attr);
3762 window.AttributeOff (A_REVERSE);
3768 DisplayRows (Window &window,
3769 std::vector<Row> &rows,
3770 DisplayOptions &options)
3775 bool window_is_active = window.IsActive();
3776 for (auto &row : rows)
3778 const bool last_child = row.parent && &rows[rows.size()-1] == &row;
3779 // Save the row index in each Row structure
3780 row.row_idx = m_num_rows;
3781 if ((m_num_rows >= m_first_visible_row) &&
3782 ((m_num_rows - m_first_visible_row) < static_cast<size_t>(NumVisibleRows())))
3785 row.y = m_num_rows - m_first_visible_row + 1;
3786 if (DisplayRowObject (window,
3789 window_is_active && m_num_rows == m_selected_row_idx,
3807 if (row.expanded && !row.children.empty())
3809 DisplayRows (window,
3817 CalculateTotalNumberRows (const std::vector<Row> &rows)
3820 for (const auto &row : rows)
3824 row_count += CalculateTotalNumberRows(row.children);
3830 GetRowForRowIndexImpl (std::vector<Row> &rows, size_t &row_index)
3832 for (auto &row : rows)
3839 if (row.expanded && !row.children.empty())
3841 Row *result = GetRowForRowIndexImpl (row.children, row_index);
3851 GetRowForRowIndex (size_t row_index)
3853 return GetRowForRowIndexImpl (m_rows, row_index);
3857 NumVisibleRows () const
3859 return m_max_y - m_min_y;
3862 static DisplayOptions g_options;
3865 class FrameVariablesWindowDelegate : public ValueObjectListDelegate
3868 FrameVariablesWindowDelegate (Debugger &debugger) :
3869 ValueObjectListDelegate (),
3870 m_debugger (debugger),
3871 m_frame_block (NULL)
3875 ~FrameVariablesWindowDelegate() override = default;
3878 WindowDelegateGetHelpText () override
3880 return "Frame variable window keyboard shortcuts:";
3884 WindowDelegateDraw (Window &window, bool force) override
3886 ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext());
3887 Process *process = exe_ctx.GetProcessPtr();
3888 Block *frame_block = NULL;
3889 StackFrame *frame = NULL;
3893 StateType state = process->GetState();
3894 if (StateIsStoppedState(state, true))
3896 frame = exe_ctx.GetFramePtr();
3898 frame_block = frame->GetFrameBlock ();
3900 else if (StateIsRunningState(state))
3902 return true; // Don't do any updating when we are running
3906 ValueObjectList local_values;
3909 // Only update the variables if they have changed
3910 if (m_frame_block != frame_block)
3912 m_frame_block = frame_block;
3914 VariableList *locals = frame->GetVariableList(true);
3917 const DynamicValueType use_dynamic = eDynamicDontRunTarget;
3918 const size_t num_locals = locals->GetSize();
3919 for (size_t i=0; i<num_locals; ++i)
3921 ValueObjectSP value_sp = frame->GetValueObjectForFrameVariable (locals->GetVariableAtIndex(i), use_dynamic);
3924 ValueObjectSP synthetic_value_sp = value_sp->GetSyntheticValue();
3925 if (synthetic_value_sp)
3926 local_values.Append(synthetic_value_sp);
3928 local_values.Append(value_sp);
3932 // Update the values
3933 SetValues(local_values);
3939 m_frame_block = NULL;
3940 // Update the values with an empty list if there is no frame
3941 SetValues(local_values);
3944 return ValueObjectListDelegate::WindowDelegateDraw (window, force);
3948 Debugger &m_debugger;
3949 Block *m_frame_block;
3952 class RegistersWindowDelegate : public ValueObjectListDelegate
3955 RegistersWindowDelegate (Debugger &debugger) :
3956 ValueObjectListDelegate (),
3957 m_debugger (debugger)
3961 ~RegistersWindowDelegate() override = default;
3964 WindowDelegateGetHelpText () override
3966 return "Register window keyboard shortcuts:";
3970 WindowDelegateDraw (Window &window, bool force) override
3972 ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext());
3973 StackFrame *frame = exe_ctx.GetFramePtr();
3975 ValueObjectList value_list;
3978 if (frame->GetStackID() != m_stack_id)
3980 m_stack_id = frame->GetStackID();
3981 RegisterContextSP reg_ctx (frame->GetRegisterContext());
3984 const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
3985 for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx)
3987 value_list.Append(ValueObjectRegisterSet::Create (frame, reg_ctx, set_idx));
3990 SetValues(value_list);
3995 Process *process = exe_ctx.GetProcessPtr();
3996 if (process && process->IsAlive())
3997 return true; // Don't do any updating if we are running
4000 // Update the values with an empty list if there
4001 // is no process or the process isn't alive anymore
4002 SetValues(value_list);
4005 return ValueObjectListDelegate::WindowDelegateDraw (window, force);
4009 Debugger &m_debugger;
4014 CursesKeyToCString (int ch)
4016 static char g_desc[32];
4017 if (ch >= KEY_F0 && ch < KEY_F0 + 64)
4019 snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0);
4024 case KEY_DOWN: return "down";
4025 case KEY_UP: return "up";
4026 case KEY_LEFT: return "left";
4027 case KEY_RIGHT: return "right";
4028 case KEY_HOME: return "home";
4029 case KEY_BACKSPACE: return "backspace";
4030 case KEY_DL: return "delete-line";
4031 case KEY_IL: return "insert-line";
4032 case KEY_DC: return "delete-char";
4033 case KEY_IC: return "insert-char";
4034 case KEY_CLEAR: return "clear";
4035 case KEY_EOS: return "clear-to-eos";
4036 case KEY_EOL: return "clear-to-eol";
4037 case KEY_SF: return "scroll-forward";
4038 case KEY_SR: return "scroll-backward";
4039 case KEY_NPAGE: return "page-down";
4040 case KEY_PPAGE: return "page-up";
4041 case KEY_STAB: return "set-tab";
4042 case KEY_CTAB: return "clear-tab";
4043 case KEY_CATAB: return "clear-all-tabs";
4044 case KEY_ENTER: return "enter";
4045 case KEY_PRINT: return "print";
4046 case KEY_LL: return "lower-left key";
4047 case KEY_A1: return "upper left of keypad";
4048 case KEY_A3: return "upper right of keypad";
4049 case KEY_B2: return "center of keypad";
4050 case KEY_C1: return "lower left of keypad";
4051 case KEY_C3: return "lower right of keypad";
4052 case KEY_BTAB: return "back-tab key";
4053 case KEY_BEG: return "begin key";
4054 case KEY_CANCEL: return "cancel key";
4055 case KEY_CLOSE: return "close key";
4056 case KEY_COMMAND: return "command key";
4057 case KEY_COPY: return "copy key";
4058 case KEY_CREATE: return "create key";
4059 case KEY_END: return "end key";
4060 case KEY_EXIT: return "exit key";
4061 case KEY_FIND: return "find key";
4062 case KEY_HELP: return "help key";
4063 case KEY_MARK: return "mark key";
4064 case KEY_MESSAGE: return "message key";
4065 case KEY_MOVE: return "move key";
4066 case KEY_NEXT: return "next key";
4067 case KEY_OPEN: return "open key";
4068 case KEY_OPTIONS: return "options key";
4069 case KEY_PREVIOUS: return "previous key";
4070 case KEY_REDO: return "redo key";
4071 case KEY_REFERENCE: return "reference key";
4072 case KEY_REFRESH: return "refresh key";
4073 case KEY_REPLACE: return "replace key";
4074 case KEY_RESTART: return "restart key";
4075 case KEY_RESUME: return "resume key";
4076 case KEY_SAVE: return "save key";
4077 case KEY_SBEG: return "shifted begin key";
4078 case KEY_SCANCEL: return "shifted cancel key";
4079 case KEY_SCOMMAND: return "shifted command key";
4080 case KEY_SCOPY: return "shifted copy key";
4081 case KEY_SCREATE: return "shifted create key";
4082 case KEY_SDC: return "shifted delete-character key";
4083 case KEY_SDL: return "shifted delete-line key";
4084 case KEY_SELECT: return "select key";
4085 case KEY_SEND: return "shifted end key";
4086 case KEY_SEOL: return "shifted clear-to-end-of-line key";
4087 case KEY_SEXIT: return "shifted exit key";
4088 case KEY_SFIND: return "shifted find key";
4089 case KEY_SHELP: return "shifted help key";
4090 case KEY_SHOME: return "shifted home key";
4091 case KEY_SIC: return "shifted insert-character key";
4092 case KEY_SLEFT: return "shifted left-arrow key";
4093 case KEY_SMESSAGE: return "shifted message key";
4094 case KEY_SMOVE: return "shifted move key";
4095 case KEY_SNEXT: return "shifted next key";
4096 case KEY_SOPTIONS: return "shifted options key";
4097 case KEY_SPREVIOUS: return "shifted previous key";
4098 case KEY_SPRINT: return "shifted print key";
4099 case KEY_SREDO: return "shifted redo key";
4100 case KEY_SREPLACE: return "shifted replace key";
4101 case KEY_SRIGHT: return "shifted right-arrow key";
4102 case KEY_SRSUME: return "shifted resume key";
4103 case KEY_SSAVE: return "shifted save key";
4104 case KEY_SSUSPEND: return "shifted suspend key";
4105 case KEY_SUNDO: return "shifted undo key";
4106 case KEY_SUSPEND: return "suspend key";
4107 case KEY_UNDO: return "undo key";
4108 case KEY_MOUSE: return "Mouse event has occurred";
4109 case KEY_RESIZE: return "Terminal resize event";
4111 case KEY_EVENT: return "We were interrupted by an event";
4113 case KEY_RETURN: return "return";
4114 case ' ': return "space";
4115 case '\t': return "tab";
4116 case KEY_ESCAPE: return "escape";
4119 snprintf(g_desc, sizeof(g_desc), "%c", ch);
4121 snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch);
4127 HelpDialogDelegate::HelpDialogDelegate (const char *text, KeyHelp *key_help_array) :
4129 m_first_visible_line (0)
4131 if (text && text[0])
4133 m_text.SplitIntoLines(text);
4134 m_text.AppendString("");
4138 for (KeyHelp *key = key_help_array; key->ch; ++key)
4140 StreamString key_description;
4141 key_description.Printf("%10s - %s", CursesKeyToCString(key->ch), key->description);
4142 m_text.AppendString(std::move(key_description.GetString()));
4147 HelpDialogDelegate::~HelpDialogDelegate() = default;
4150 HelpDialogDelegate::WindowDelegateDraw (Window &window, bool force)
4153 const int window_height = window.GetHeight();
4156 const int min_y = y;
4157 const int max_y = window_height - 1 - y;
4158 const size_t num_visible_lines = max_y - min_y + 1;
4159 const size_t num_lines = m_text.GetSize();
4160 const char *bottom_message;
4161 if (num_lines <= num_visible_lines)
4162 bottom_message = "Press any key to exit";
4164 bottom_message = "Use arrows to scroll, any other key to exit";
4165 window.DrawTitleBox(window.GetName(), bottom_message);
4168 window.MoveCursor(x, y);
4169 window.PutCStringTruncated(m_text.GetStringAtIndex(m_first_visible_line + y - min_y), 1);
4176 HelpDialogDelegate::WindowDelegateHandleChar (Window &window, int key)
4179 const size_t num_lines = m_text.GetSize();
4180 const size_t num_visible_lines = window.GetHeight() - 2;
4182 if (num_lines <= num_visible_lines)
4185 // If we have all lines visible and don't need scrolling, then any
4186 // key press will cause us to exit
4193 if (m_first_visible_line > 0)
4194 --m_first_visible_line;
4198 if (m_first_visible_line + num_visible_lines < num_lines)
4199 ++m_first_visible_line;
4204 if (m_first_visible_line > 0)
4206 if (static_cast<size_t>(m_first_visible_line) >= num_visible_lines)
4207 m_first_visible_line -= num_visible_lines;
4209 m_first_visible_line = 0;
4215 if (m_first_visible_line + num_visible_lines < num_lines)
4217 m_first_visible_line += num_visible_lines;
4218 if (static_cast<size_t>(m_first_visible_line) > num_lines)
4219 m_first_visible_line = num_lines - num_visible_lines;
4229 window.GetParent()->RemoveSubWindow(&window);
4233 class ApplicationDelegate :
4234 public WindowDelegate,
4244 eMenuID_TargetCreate,
4245 eMenuID_TargetDelete,
4248 eMenuID_ProcessAttach,
4249 eMenuID_ProcessDetach,
4250 eMenuID_ProcessLaunch,
4251 eMenuID_ProcessContinue,
4252 eMenuID_ProcessHalt,
4253 eMenuID_ProcessKill,
4256 eMenuID_ThreadStepIn,
4257 eMenuID_ThreadStepOver,
4258 eMenuID_ThreadStepOut,
4261 eMenuID_ViewBacktrace,
4262 eMenuID_ViewRegisters,
4264 eMenuID_ViewVariables,
4270 ApplicationDelegate (Application &app, Debugger &debugger) :
4274 m_debugger (debugger)
4278 ~ApplicationDelegate() override = default;
4281 WindowDelegateDraw (Window &window, bool force) override
4283 return false; // Drawing not handled, let standard window drawing happen
4287 WindowDelegateHandleChar (Window &window, int key) override
4292 window.SelectNextWindowAsActive();
4296 window.CreateHelpSubwindow();
4300 return eQuitApplication;
4305 return eKeyNotHandled;
4309 WindowDelegateGetHelpText () override
4311 return "Welcome to the LLDB curses GUI.\n\n"
4312 "Press the TAB key to change the selected view.\n"
4313 "Each view has its own keyboard shortcuts, press 'h' to open a dialog to display them.\n\n"
4314 "Common key bindings for all views:";
4318 WindowDelegateGetKeyHelp () override
4320 static curses::KeyHelp g_source_view_key_help[] = {
4321 { '\t', "Select next view" },
4322 { 'h', "Show help dialog with view specific key bindings" },
4324 { '.', "Page down" },
4325 { KEY_UP, "Select previous" },
4326 { KEY_DOWN, "Select next" },
4327 { KEY_LEFT, "Unexpand or select parent" },
4328 { KEY_RIGHT, "Expand" },
4329 { KEY_PPAGE, "Page up" },
4330 { KEY_NPAGE, "Page down" },
4333 return g_source_view_key_help;
4337 MenuDelegateAction (Menu &menu) override
4339 switch (menu.GetIdentifier())
4341 case eMenuID_ThreadStepIn:
4343 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4344 if (exe_ctx.HasThreadScope())
4346 Process *process = exe_ctx.GetProcessPtr();
4347 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4348 exe_ctx.GetThreadRef().StepIn(true);
4351 return MenuActionResult::Handled;
4353 case eMenuID_ThreadStepOut:
4355 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4356 if (exe_ctx.HasThreadScope())
4358 Process *process = exe_ctx.GetProcessPtr();
4359 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4360 exe_ctx.GetThreadRef().StepOut();
4363 return MenuActionResult::Handled;
4365 case eMenuID_ThreadStepOver:
4367 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4368 if (exe_ctx.HasThreadScope())
4370 Process *process = exe_ctx.GetProcessPtr();
4371 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4372 exe_ctx.GetThreadRef().StepOver(true);
4375 return MenuActionResult::Handled;
4377 case eMenuID_ProcessContinue:
4379 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4380 if (exe_ctx.HasProcessScope())
4382 Process *process = exe_ctx.GetProcessPtr();
4383 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4387 return MenuActionResult::Handled;
4389 case eMenuID_ProcessKill:
4391 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4392 if (exe_ctx.HasProcessScope())
4394 Process *process = exe_ctx.GetProcessPtr();
4395 if (process && process->IsAlive())
4396 process->Destroy(false);
4399 return MenuActionResult::Handled;
4401 case eMenuID_ProcessHalt:
4403 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4404 if (exe_ctx.HasProcessScope())
4406 Process *process = exe_ctx.GetProcessPtr();
4407 if (process && process->IsAlive())
4411 return MenuActionResult::Handled;
4413 case eMenuID_ProcessDetach:
4415 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4416 if (exe_ctx.HasProcessScope())
4418 Process *process = exe_ctx.GetProcessPtr();
4419 if (process && process->IsAlive())
4420 process->Detach(false);
4423 return MenuActionResult::Handled;
4425 case eMenuID_Process:
4427 // Populate the menu with all of the threads if the process is stopped when
4428 // the Process menu gets selected and is about to display its submenu.
4429 Menus &submenus = menu.GetSubmenus();
4430 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4431 Process *process = exe_ctx.GetProcessPtr();
4432 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4434 if (submenus.size() == 7)
4435 menu.AddSubmenu (MenuSP (new Menu(Menu::Type::Separator)));
4436 else if (submenus.size() > 8)
4437 submenus.erase (submenus.begin() + 8, submenus.end());
4439 ThreadList &threads = process->GetThreadList();
4440 Mutex::Locker locker (threads.GetMutex());
4441 size_t num_threads = threads.GetSize();
4442 for (size_t i=0; i<num_threads; ++i)
4444 ThreadSP thread_sp = threads.GetThreadAtIndex(i);
4445 char menu_char = '\0';
4447 menu_char = '1' + i;
4448 StreamString thread_menu_title;
4449 thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID());
4450 const char *thread_name = thread_sp->GetName();
4451 if (thread_name && thread_name[0])
4452 thread_menu_title.Printf (" %s", thread_name);
4455 const char *queue_name = thread_sp->GetQueueName();
4456 if (queue_name && queue_name[0])
4457 thread_menu_title.Printf (" %s", queue_name);
4459 menu.AddSubmenu (MenuSP (new Menu(thread_menu_title.GetString().c_str(), NULL, menu_char, thread_sp->GetID())));
4462 else if (submenus.size() > 7)
4464 // Remove the separator and any other thread submenu items
4465 // that were previously added
4466 submenus.erase (submenus.begin() + 7, submenus.end());
4468 // Since we are adding and removing items we need to recalculate the name lengths
4469 menu.RecalculateNameLengths();
4471 return MenuActionResult::Handled;
4473 case eMenuID_ViewVariables:
4475 WindowSP main_window_sp = m_app.GetMainWindow();
4476 WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
4477 WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
4478 WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
4479 const Rect source_bounds = source_window_sp->GetBounds();
4481 if (variables_window_sp)
4483 const Rect variables_bounds = variables_window_sp->GetBounds();
4485 main_window_sp->RemoveSubWindow(variables_window_sp.get());
4487 if (registers_window_sp)
4489 // We have a registers window, so give all the area back to the registers window
4490 Rect registers_bounds = variables_bounds;
4491 registers_bounds.size.width = source_bounds.size.width;
4492 registers_window_sp->SetBounds(registers_bounds);
4496 // We have no registers window showing so give the bottom
4497 // area back to the source view
4498 source_window_sp->Resize (source_bounds.size.width,
4499 source_bounds.size.height + variables_bounds.size.height);
4504 Rect new_variables_rect;
4505 if (registers_window_sp)
4507 // We have a registers window so split the area of the registers
4508 // window into two columns where the left hand side will be the
4509 // variables and the right hand side will be the registers
4510 const Rect variables_bounds = registers_window_sp->GetBounds();
4511 Rect new_registers_rect;
4512 variables_bounds.VerticalSplitPercentage (0.50, new_variables_rect, new_registers_rect);
4513 registers_window_sp->SetBounds (new_registers_rect);
4517 // No variables window, grab the bottom part of the source window
4518 Rect new_source_rect;
4519 source_bounds.HorizontalSplitPercentage (0.70, new_source_rect, new_variables_rect);
4520 source_window_sp->SetBounds (new_source_rect);
4522 WindowSP new_window_sp = main_window_sp->CreateSubWindow ("Variables",
4525 new_window_sp->SetDelegate (WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
4529 return MenuActionResult::Handled;
4531 case eMenuID_ViewRegisters:
4533 WindowSP main_window_sp = m_app.GetMainWindow();
4534 WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
4535 WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
4536 WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
4537 const Rect source_bounds = source_window_sp->GetBounds();
4539 if (registers_window_sp)
4541 if (variables_window_sp)
4543 const Rect variables_bounds = variables_window_sp->GetBounds();
4545 // We have a variables window, so give all the area back to the variables window
4546 variables_window_sp->Resize (variables_bounds.size.width + registers_window_sp->GetWidth(),
4547 variables_bounds.size.height);
4551 // We have no variables window showing so give the bottom
4552 // area back to the source view
4553 source_window_sp->Resize (source_bounds.size.width,
4554 source_bounds.size.height + registers_window_sp->GetHeight());
4556 main_window_sp->RemoveSubWindow(registers_window_sp.get());
4561 if (variables_window_sp)
4563 // We have a variables window, split it into two columns
4564 // where the left hand side will be the variables and the
4565 // right hand side will be the registers
4566 const Rect variables_bounds = variables_window_sp->GetBounds();
4568 variables_bounds.VerticalSplitPercentage (0.50, new_vars_rect, new_regs_rect);
4569 variables_window_sp->SetBounds (new_vars_rect);
4573 // No registers window, grab the bottom part of the source window
4574 Rect new_source_rect;
4575 source_bounds.HorizontalSplitPercentage (0.70, new_source_rect, new_regs_rect);
4576 source_window_sp->SetBounds (new_source_rect);
4578 WindowSP new_window_sp = main_window_sp->CreateSubWindow ("Registers",
4581 new_window_sp->SetDelegate (WindowDelegateSP(new RegistersWindowDelegate(m_debugger)));
4585 return MenuActionResult::Handled;
4587 case eMenuID_HelpGUIHelp:
4588 m_app.GetMainWindow ()->CreateHelpSubwindow();
4589 return MenuActionResult::Handled;
4595 return MenuActionResult::NotHandled;
4599 Debugger &m_debugger;
4602 class StatusBarWindowDelegate : public WindowDelegate
4605 StatusBarWindowDelegate (Debugger &debugger) :
4606 m_debugger (debugger)
4608 FormatEntity::Parse("Thread: ${thread.id%tid}",
4612 ~StatusBarWindowDelegate() override = default;
4615 WindowDelegateDraw (Window &window, bool force) override
4617 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4618 Process *process = exe_ctx.GetProcessPtr();
4619 Thread *thread = exe_ctx.GetThreadPtr();
4620 StackFrame *frame = exe_ctx.GetFramePtr();
4622 window.SetBackground(2);
4623 window.MoveCursor (0, 0);
4626 const StateType state = process->GetState();
4627 window.Printf ("Process: %5" PRIu64 " %10s", process->GetID(), StateAsCString(state));
4629 if (StateIsStoppedState(state, true))
4632 if (thread && FormatEntity::Format (m_format, strm, NULL, &exe_ctx, NULL, NULL, false, false))
4634 window.MoveCursor (40, 0);
4635 window.PutCStringTruncated(strm.GetString().c_str(), 1);
4638 window.MoveCursor (60, 0);
4640 window.Printf ("Frame: %3u PC = 0x%16.16" PRIx64, frame->GetFrameIndex(), frame->GetFrameCodeAddress().GetOpcodeLoadAddress (exe_ctx.GetTargetPtr()));
4642 else if (state == eStateExited)
4644 const char *exit_desc = process->GetExitDescription();
4645 const int exit_status = process->GetExitStatus();
4646 if (exit_desc && exit_desc[0])
4647 window.Printf (" with status = %i (%s)", exit_status, exit_desc);
4649 window.Printf (" with status = %i", exit_status);
4652 window.DeferredRefresh();
4657 Debugger &m_debugger;
4658 FormatEntity::Entry m_format;
4661 class SourceFileWindowDelegate : public WindowDelegate
4664 SourceFileWindowDelegate (Debugger &debugger) :
4666 m_debugger (debugger),
4669 m_disassembly_scope (NULL),
4670 m_disassembly_sp (),
4671 m_disassembly_range (),
4674 m_selected_line (0),
4677 m_frame_idx (UINT32_MAX),
4678 m_first_visible_line (0),
4686 ~SourceFileWindowDelegate() override = default;
4689 Update (const SymbolContext &sc)
4695 NumVisibleLines () const
4697 return m_max_y - m_min_y;
4701 WindowDelegateGetHelpText () override
4703 return "Source/Disassembly window keyboard shortcuts:";
4707 WindowDelegateGetKeyHelp () override
4709 static curses::KeyHelp g_source_view_key_help[] = {
4710 { KEY_RETURN, "Run to selected line with one shot breakpoint" },
4711 { KEY_UP, "Select previous source line" },
4712 { KEY_DOWN, "Select next source line" },
4713 { KEY_PPAGE, "Page up" },
4714 { KEY_NPAGE, "Page down" },
4715 { 'b', "Set breakpoint on selected source/disassembly line" },
4716 { 'c', "Continue process" },
4717 { 'd', "Detach and resume process" },
4718 { 'D', "Detach with process suspended" },
4719 { 'h', "Show help dialog" },
4720 { 'k', "Kill process" },
4721 { 'n', "Step over (source line)" },
4722 { 'N', "Step over (single instruction)" },
4723 { 'o', "Step out" },
4724 { 's', "Step in (source line)" },
4725 { 'S', "Step in (single instruction)" },
4727 { '.', "Page down" },
4730 return g_source_view_key_help;
4734 WindowDelegateDraw (Window &window, bool force) override
4736 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4737 Process *process = exe_ctx.GetProcessPtr();
4738 Thread *thread = NULL;
4740 bool update_location = false;
4743 StateType state = process->GetState();
4744 if (StateIsStoppedState(state, true))
4746 // We are stopped, so it is ok to
4747 update_location = true;
4753 m_max_x = window.GetMaxX()-1;
4754 m_max_y = window.GetMaxY()-1;
4756 const uint32_t num_visible_lines = NumVisibleLines();
4757 StackFrameSP frame_sp;
4758 bool set_selected_line_to_pc = false;
4760 if (update_location)
4762 const bool process_alive = process ? process->IsAlive() : false;
4763 bool thread_changed = false;
4766 thread = exe_ctx.GetThreadPtr();
4769 frame_sp = thread->GetSelectedFrame();
4770 auto tid = thread->GetID();
4771 thread_changed = tid != m_tid;
4776 if (m_tid != LLDB_INVALID_THREAD_ID)
4778 thread_changed = true;
4779 m_tid = LLDB_INVALID_THREAD_ID;
4783 const uint32_t stop_id = process ? process->GetStopID() : 0;
4784 const bool stop_id_changed = stop_id != m_stop_id;
4785 bool frame_changed = false;
4786 m_stop_id = stop_id;
4790 m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
4793 m_title.Printf("%s", m_sc.module_sp->GetFileSpec().GetFilename().GetCString());
4794 ConstString func_name = m_sc.GetFunctionName();
4796 m_title.Printf("`%s", func_name.GetCString());
4798 const uint32_t frame_idx = frame_sp->GetFrameIndex();
4799 frame_changed = frame_idx != m_frame_idx;
4800 m_frame_idx = frame_idx;
4805 frame_changed = m_frame_idx != UINT32_MAX;
4806 m_frame_idx = UINT32_MAX;
4809 const bool context_changed = thread_changed || frame_changed || stop_id_changed;
4813 if (m_sc.line_entry.IsValid())
4815 m_pc_line = m_sc.line_entry.line;
4816 if (m_pc_line != UINT32_MAX)
4817 --m_pc_line; // Convert to zero based line number...
4818 // Update the selected line if the stop ID changed...
4819 if (context_changed)
4820 m_selected_line = m_pc_line;
4822 if (m_file_sp && m_file_sp->FileSpecMatches(m_sc.line_entry.file))
4824 // Same file, nothing to do, we should either have the
4825 // lines or not (source file missing)
4826 if (m_selected_line >= static_cast<size_t>(m_first_visible_line))
4828 if (m_selected_line >= m_first_visible_line + num_visible_lines)
4829 m_first_visible_line = m_selected_line - 10;
4833 if (m_selected_line > 10)
4834 m_first_visible_line = m_selected_line - 10;
4836 m_first_visible_line = 0;
4841 // File changed, set selected line to the line with the PC
4842 m_selected_line = m_pc_line;
4843 m_file_sp = m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file);
4846 const size_t num_lines = m_file_sp->GetNumLines();
4847 int m_line_width = 1;
4848 for (size_t n = num_lines; n >= 10; n = n / 10)
4851 snprintf (m_line_format, sizeof(m_line_format), " %%%iu ", m_line_width);
4852 if (num_lines < num_visible_lines || m_selected_line < num_visible_lines)
4853 m_first_visible_line = 0;
4855 m_first_visible_line = m_selected_line - 10;
4864 if (!m_file_sp || m_file_sp->GetNumLines() == 0)
4867 bool prefer_file_cache = false;
4870 if (m_disassembly_scope != m_sc.function)
4872 m_disassembly_scope = m_sc.function;
4873 m_disassembly_sp = m_sc.function->GetInstructions (exe_ctx, NULL, prefer_file_cache);
4874 if (m_disassembly_sp)
4876 set_selected_line_to_pc = true;
4877 m_disassembly_range = m_sc.function->GetAddressRange();
4881 m_disassembly_range.Clear();
4886 set_selected_line_to_pc = context_changed;
4889 else if (m_sc.symbol)
4891 if (m_disassembly_scope != m_sc.symbol)
4893 m_disassembly_scope = m_sc.symbol;
4894 m_disassembly_sp = m_sc.symbol->GetInstructions (exe_ctx, NULL, prefer_file_cache);
4895 if (m_disassembly_sp)
4897 set_selected_line_to_pc = true;
4898 m_disassembly_range.GetBaseAddress() = m_sc.symbol->GetAddress();
4899 m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize());
4903 m_disassembly_range.Clear();
4908 set_selected_line_to_pc = context_changed;
4915 m_pc_line = UINT32_MAX;
4919 const int window_width = window.GetWidth();
4921 window.DrawTitleBox ("Sources");
4922 if (!m_title.GetString().empty())
4924 window.AttributeOn(A_REVERSE);
4925 window.MoveCursor(1, 1);
4926 window.PutChar(' ');
4927 window.PutCStringTruncated(m_title.GetString().c_str(), 1);
4928 int x = window.GetCursorX();
4929 if (x < window_width - 1)
4931 window.Printf ("%*s", window_width - x - 1, "");
4933 window.AttributeOff(A_REVERSE);
4936 Target *target = exe_ctx.GetTargetPtr();
4937 const size_t num_source_lines = GetNumSourceLines();
4938 if (num_source_lines > 0)
4941 BreakpointLines bp_lines;
4944 BreakpointList &bp_list = target->GetBreakpointList();
4945 const size_t num_bps = bp_list.GetSize();
4946 for (size_t bp_idx=0; bp_idx<num_bps; ++bp_idx)
4948 BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
4949 const size_t num_bps_locs = bp_sp->GetNumLocations();
4950 for (size_t bp_loc_idx=0; bp_loc_idx<num_bps_locs; ++bp_loc_idx)
4952 BreakpointLocationSP bp_loc_sp = bp_sp->GetLocationAtIndex(bp_loc_idx);
4953 LineEntry bp_loc_line_entry;
4954 if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry (bp_loc_line_entry))
4956 if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file)
4958 bp_lines.insert(bp_loc_line_entry.line);
4965 const attr_t selected_highlight_attr = A_REVERSE;
4966 const attr_t pc_highlight_attr = COLOR_PAIR(1);
4968 for (size_t i=0; i<num_visible_lines; ++i)
4970 const uint32_t curr_line = m_first_visible_line + i;
4971 if (curr_line < num_source_lines)
4973 const int line_y = m_min_y+i;
4974 window.MoveCursor(1, line_y);
4975 const bool is_pc_line = curr_line == m_pc_line;
4976 const bool line_is_selected = m_selected_line == curr_line;
4977 // Highlight the line as the PC line first, then if the selected line
4978 // isn't the same as the PC line, highlight it differently
4979 attr_t highlight_attr = 0;
4982 highlight_attr = pc_highlight_attr;
4983 else if (line_is_selected)
4984 highlight_attr = selected_highlight_attr;
4986 if (bp_lines.find(curr_line+1) != bp_lines.end())
4987 bp_attr = COLOR_PAIR(2);
4990 window.AttributeOn(bp_attr);
4992 window.Printf (m_line_format, curr_line + 1);
4995 window.AttributeOff(bp_attr);
4997 window.PutChar(ACS_VLINE);
4998 // Mark the line with the PC with a diamond
5000 window.PutChar(ACS_DIAMOND);
5002 window.PutChar(' ');
5005 window.AttributeOn(highlight_attr);
5006 const uint32_t line_len = m_file_sp->GetLineLength(curr_line + 1, false);
5008 window.PutCString(m_file_sp->PeekLineData(curr_line + 1), line_len);
5010 if (is_pc_line && frame_sp && frame_sp->GetConcreteFrameIndex() == 0)
5012 StopInfoSP stop_info_sp;
5014 stop_info_sp = thread->GetStopInfo();
5017 const char *stop_description = stop_info_sp->GetDescription();
5018 if (stop_description && stop_description[0])
5020 size_t stop_description_len = strlen(stop_description);
5021 int desc_x = window_width - stop_description_len - 16;
5022 window.Printf ("%*s", desc_x - window.GetCursorX(), "");
5023 //window.MoveCursor(window_width - stop_description_len - 15, line_y);
5024 window.Printf ("<<< Thread %u: %s ", thread->GetIndexID(), stop_description);
5029 window.Printf ("%*s", window_width - window.GetCursorX() - 1, "");
5033 window.AttributeOff(highlight_attr);
5043 size_t num_disassembly_lines = GetNumDisassemblyLines();
5044 if (num_disassembly_lines > 0)
5046 // Display disassembly
5047 BreakpointAddrs bp_file_addrs;
5048 Target *target = exe_ctx.GetTargetPtr();
5051 BreakpointList &bp_list = target->GetBreakpointList();
5052 const size_t num_bps = bp_list.GetSize();
5053 for (size_t bp_idx=0; bp_idx<num_bps; ++bp_idx)
5055 BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
5056 const size_t num_bps_locs = bp_sp->GetNumLocations();
5057 for (size_t bp_loc_idx=0; bp_loc_idx<num_bps_locs; ++bp_loc_idx)
5059 BreakpointLocationSP bp_loc_sp = bp_sp->GetLocationAtIndex(bp_loc_idx);
5060 LineEntry bp_loc_line_entry;
5061 const lldb::addr_t file_addr = bp_loc_sp->GetAddress().GetFileAddress();
5062 if (file_addr != LLDB_INVALID_ADDRESS)
5064 if (m_disassembly_range.ContainsFileAddress(file_addr))
5065 bp_file_addrs.insert(file_addr);
5071 const attr_t selected_highlight_attr = A_REVERSE;
5072 const attr_t pc_highlight_attr = COLOR_PAIR(1);
5076 InstructionList &insts = m_disassembly_sp->GetInstructionList();
5080 pc_address = frame_sp->GetFrameCodeAddress();
5081 const uint32_t pc_idx = pc_address.IsValid() ? insts.GetIndexOfInstructionAtAddress (pc_address) : UINT32_MAX;
5082 if (set_selected_line_to_pc)
5084 m_selected_line = pc_idx;
5087 const uint32_t non_visible_pc_offset = (num_visible_lines / 5);
5088 if (static_cast<size_t>(m_first_visible_line) >= num_disassembly_lines)
5089 m_first_visible_line = 0;
5091 if (pc_idx < num_disassembly_lines)
5093 if (pc_idx < static_cast<uint32_t>(m_first_visible_line) ||
5094 pc_idx >= m_first_visible_line + num_visible_lines)
5095 m_first_visible_line = pc_idx - non_visible_pc_offset;
5098 for (size_t i=0; i<num_visible_lines; ++i)
5100 const uint32_t inst_idx = m_first_visible_line + i;
5101 Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get();
5105 const int line_y = m_min_y+i;
5106 window.MoveCursor(1, line_y);
5107 const bool is_pc_line = frame_sp && inst_idx == pc_idx;
5108 const bool line_is_selected = m_selected_line == inst_idx;
5109 // Highlight the line as the PC line first, then if the selected line
5110 // isn't the same as the PC line, highlight it differently
5111 attr_t highlight_attr = 0;
5114 highlight_attr = pc_highlight_attr;
5115 else if (line_is_selected)
5116 highlight_attr = selected_highlight_attr;
5118 if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) != bp_file_addrs.end())
5119 bp_attr = COLOR_PAIR(2);
5122 window.AttributeOn(bp_attr);
5124 window.Printf (" 0x%16.16llx ",
5125 static_cast<unsigned long long>(inst->GetAddress().GetLoadAddress(target)));
5128 window.AttributeOff(bp_attr);
5130 window.PutChar(ACS_VLINE);
5131 // Mark the line with the PC with a diamond
5133 window.PutChar(ACS_DIAMOND);
5135 window.PutChar(' ');
5138 window.AttributeOn(highlight_attr);
5140 const char *mnemonic = inst->GetMnemonic(&exe_ctx);
5141 const char *operands = inst->GetOperands(&exe_ctx);
5142 const char *comment = inst->GetComment(&exe_ctx);
5144 if (mnemonic && mnemonic[0] == '\0')
5146 if (operands && operands[0] == '\0')
5148 if (comment && comment[0] == '\0')
5153 if (mnemonic && operands && comment)
5154 strm.Printf ("%-8s %-25s ; %s", mnemonic, operands, comment);
5155 else if (mnemonic && operands)
5156 strm.Printf ("%-8s %s", mnemonic, operands);
5158 strm.Printf ("%s", mnemonic);
5161 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
5163 if (is_pc_line && frame_sp && frame_sp->GetConcreteFrameIndex() == 0)
5165 StopInfoSP stop_info_sp;
5167 stop_info_sp = thread->GetStopInfo();
5170 const char *stop_description = stop_info_sp->GetDescription();
5171 if (stop_description && stop_description[0])
5173 size_t stop_description_len = strlen(stop_description);
5174 int desc_x = window_width - stop_description_len - 16;
5175 window.Printf ("%*s", desc_x - window.GetCursorX(), "");
5176 //window.MoveCursor(window_width - stop_description_len - 15, line_y);
5177 window.Printf ("<<< Thread %u: %s ", thread->GetIndexID(), stop_description);
5182 window.Printf ("%*s", window_width - window.GetCursorX() - 1, "");
5186 window.AttributeOff(highlight_attr);
5190 window.DeferredRefresh();
5191 return true; // Drawing handled
5197 size_t num_lines = GetNumSourceLines();
5199 num_lines = GetNumDisassemblyLines();
5204 GetNumSourceLines () const
5207 return m_file_sp->GetNumLines();
5212 GetNumDisassemblyLines () const
5214 if (m_disassembly_sp)
5215 return m_disassembly_sp->GetInstructionList().GetSize();
5220 WindowDelegateHandleChar (Window &window, int c) override
5222 const uint32_t num_visible_lines = NumVisibleLines();
5223 const size_t num_lines = GetNumLines ();
5230 if (static_cast<uint32_t>(m_first_visible_line) > num_visible_lines)
5231 m_first_visible_line -= num_visible_lines;
5233 m_first_visible_line = 0;
5234 m_selected_line = m_first_visible_line;
5241 if (m_first_visible_line + num_visible_lines < num_lines)
5242 m_first_visible_line += num_visible_lines;
5243 else if (num_lines < num_visible_lines)
5244 m_first_visible_line = 0;
5246 m_first_visible_line = num_lines - num_visible_lines;
5247 m_selected_line = m_first_visible_line;
5252 if (m_selected_line > 0)
5255 if (static_cast<size_t>(m_first_visible_line) > m_selected_line)
5256 m_first_visible_line = m_selected_line;
5261 if (m_selected_line + 1 < num_lines)
5264 if (m_first_visible_line + num_visible_lines < m_selected_line)
5265 m_first_visible_line++;
5272 // Set a breakpoint and run to the line using a one shot breakpoint
5273 if (GetNumSourceLines() > 0)
5275 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5276 if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive())
5278 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (NULL, // Don't limit the breakpoint to certain modules
5279 m_file_sp->GetFileSpec(), // Source file
5280 m_selected_line + 1, // Source line number (m_selected_line is zero based)
5281 eLazyBoolCalculate, // Check inlines using global setting
5282 eLazyBoolCalculate, // Skip prologue using global setting,
5284 false, // request_hardware
5285 eLazyBoolCalculate); // move_to_nearest_code
5286 // Make breakpoint one shot
5287 bp_sp->GetOptions()->SetOneShot(true);
5288 exe_ctx.GetProcessRef().Resume();
5291 else if (m_selected_line < GetNumDisassemblyLines())
5293 const Instruction *inst = m_disassembly_sp->GetInstructionList().GetInstructionAtIndex(m_selected_line).get();
5294 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5295 if (exe_ctx.HasTargetScope())
5297 Address addr = inst->GetAddress();
5298 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (addr, // lldb_private::Address
5300 false); // request_hardware
5301 // Make breakpoint one shot
5302 bp_sp->GetOptions()->SetOneShot(true);
5303 exe_ctx.GetProcessRef().Resume();
5308 case 'b': // 'b' == toggle breakpoint on currently selected line
5309 if (m_selected_line < GetNumSourceLines())
5311 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5312 if (exe_ctx.HasTargetScope())
5314 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (NULL, // Don't limit the breakpoint to certain modules
5315 m_file_sp->GetFileSpec(), // Source file
5316 m_selected_line + 1, // Source line number (m_selected_line is zero based)
5317 eLazyBoolCalculate, // Check inlines using global setting
5318 eLazyBoolCalculate, // Skip prologue using global setting,
5320 false, // request_hardware
5321 eLazyBoolCalculate); // move_to_nearest_code
5324 else if (m_selected_line < GetNumDisassemblyLines())
5326 const Instruction *inst = m_disassembly_sp->GetInstructionList().GetInstructionAtIndex(m_selected_line).get();
5327 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5328 if (exe_ctx.HasTargetScope())
5330 Address addr = inst->GetAddress();
5331 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (addr, // lldb_private::Address
5333 false); // request_hardware
5338 case 'd': // 'd' == detach and let run
5339 case 'D': // 'D' == detach and keep stopped
5341 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5342 if (exe_ctx.HasProcessScope())
5343 exe_ctx.GetProcessRef().Detach(c == 'D');
5350 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5351 if (exe_ctx.HasProcessScope())
5352 exe_ctx.GetProcessRef().Destroy(false);
5359 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5360 if (exe_ctx.HasProcessScope())
5361 exe_ctx.GetProcessRef().Resume();
5368 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5369 if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true))
5371 exe_ctx.GetThreadRef().StepOut();
5376 case 'n': // 'n' == step over
5377 case 'N': // 'N' == step over instruction
5379 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5380 if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true))
5382 bool source_step = (c == 'n');
5383 exe_ctx.GetThreadRef().StepOver(source_step);
5388 case 's': // 's' == step into
5389 case 'S': // 'S' == step into instruction
5391 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5392 if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true))
5394 bool source_step = (c == 's');
5395 exe_ctx.GetThreadRef().StepIn(source_step);
5401 window.CreateHelpSubwindow ();
5407 return eKeyNotHandled;
5411 typedef std::set<uint32_t> BreakpointLines;
5412 typedef std::set<lldb::addr_t> BreakpointAddrs;
5414 Debugger &m_debugger;
5416 SourceManager::FileSP m_file_sp;
5417 SymbolContextScope *m_disassembly_scope;
5418 lldb::DisassemblerSP m_disassembly_sp;
5419 AddressRange m_disassembly_range;
5420 StreamString m_title;
5421 lldb::user_id_t m_tid;
5422 char m_line_format[8];
5424 uint32_t m_selected_line; // The selected line
5425 uint32_t m_pc_line; // The line with the PC
5427 uint32_t m_frame_idx;
5428 int m_first_visible_line;
5435 DisplayOptions ValueObjectListDelegate::g_options = { true };
5437 IOHandlerCursesGUI::IOHandlerCursesGUI (Debugger &debugger) :
5438 IOHandler (debugger, IOHandler::Type::Curses)
5443 IOHandlerCursesGUI::Activate ()
5445 IOHandler::Activate();
5448 m_app_ap.reset (new Application (GetInputFILE(), GetOutputFILE()));
5450 // This is both a window and a menu delegate
5451 std::shared_ptr<ApplicationDelegate> app_delegate_sp(new ApplicationDelegate(*m_app_ap, m_debugger));
5453 MenuDelegateSP app_menu_delegate_sp = std::static_pointer_cast<MenuDelegate>(app_delegate_sp);
5454 MenuSP lldb_menu_sp(new Menu("LLDB" , "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB));
5455 MenuSP exit_menuitem_sp(new Menu("Exit", NULL, 'x', ApplicationDelegate::eMenuID_LLDBExit));
5456 exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit);
5457 lldb_menu_sp->AddSubmenu (MenuSP (new Menu("About LLDB", NULL, 'a', ApplicationDelegate::eMenuID_LLDBAbout)));
5458 lldb_menu_sp->AddSubmenu (MenuSP (new Menu(Menu::Type::Separator)));
5459 lldb_menu_sp->AddSubmenu (exit_menuitem_sp);
5461 MenuSP target_menu_sp(new Menu("Target" ,"F2", KEY_F(2), ApplicationDelegate::eMenuID_Target));
5462 target_menu_sp->AddSubmenu (MenuSP (new Menu("Create", NULL, 'c', ApplicationDelegate::eMenuID_TargetCreate)));
5463 target_menu_sp->AddSubmenu (MenuSP (new Menu("Delete", NULL, 'd', ApplicationDelegate::eMenuID_TargetDelete)));
5465 MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3), ApplicationDelegate::eMenuID_Process));
5466 process_menu_sp->AddSubmenu (MenuSP (new Menu("Attach" , NULL, 'a', ApplicationDelegate::eMenuID_ProcessAttach)));
5467 process_menu_sp->AddSubmenu (MenuSP (new Menu("Detach" , NULL, 'd', ApplicationDelegate::eMenuID_ProcessDetach)));
5468 process_menu_sp->AddSubmenu (MenuSP (new Menu("Launch" , NULL, 'l', ApplicationDelegate::eMenuID_ProcessLaunch)));
5469 process_menu_sp->AddSubmenu (MenuSP (new Menu(Menu::Type::Separator)));
5470 process_menu_sp->AddSubmenu (MenuSP (new Menu("Continue", NULL, 'c', ApplicationDelegate::eMenuID_ProcessContinue)));
5471 process_menu_sp->AddSubmenu (MenuSP (new Menu("Halt" , NULL, 'h', ApplicationDelegate::eMenuID_ProcessHalt)));
5472 process_menu_sp->AddSubmenu (MenuSP (new Menu("Kill" , NULL, 'k', ApplicationDelegate::eMenuID_ProcessKill)));
5474 MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4), ApplicationDelegate::eMenuID_Thread));
5475 thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step In" , NULL, 'i', ApplicationDelegate::eMenuID_ThreadStepIn)));
5476 thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step Over", NULL, 'v', ApplicationDelegate::eMenuID_ThreadStepOver)));
5477 thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step Out" , NULL, 'o', ApplicationDelegate::eMenuID_ThreadStepOut)));
5479 MenuSP view_menu_sp(new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View));
5480 view_menu_sp->AddSubmenu (MenuSP (new Menu("Backtrace", NULL, 'b', ApplicationDelegate::eMenuID_ViewBacktrace)));
5481 view_menu_sp->AddSubmenu (MenuSP (new Menu("Registers", NULL, 'r', ApplicationDelegate::eMenuID_ViewRegisters)));
5482 view_menu_sp->AddSubmenu (MenuSP (new Menu("Source" , NULL, 's', ApplicationDelegate::eMenuID_ViewSource)));
5483 view_menu_sp->AddSubmenu (MenuSP (new Menu("Variables", NULL, 'v', ApplicationDelegate::eMenuID_ViewVariables)));
5485 MenuSP help_menu_sp(new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help));
5486 help_menu_sp->AddSubmenu (MenuSP (new Menu("GUI Help", NULL, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp)));
5488 m_app_ap->Initialize();
5489 WindowSP &main_window_sp = m_app_ap->GetMainWindow();
5491 MenuSP menubar_sp(new Menu(Menu::Type::Bar));
5492 menubar_sp->AddSubmenu (lldb_menu_sp);
5493 menubar_sp->AddSubmenu (target_menu_sp);
5494 menubar_sp->AddSubmenu (process_menu_sp);
5495 menubar_sp->AddSubmenu (thread_menu_sp);
5496 menubar_sp->AddSubmenu (view_menu_sp);
5497 menubar_sp->AddSubmenu (help_menu_sp);
5498 menubar_sp->SetDelegate(app_menu_delegate_sp);
5500 Rect content_bounds = main_window_sp->GetFrame();
5501 Rect menubar_bounds = content_bounds.MakeMenuBar();
5502 Rect status_bounds = content_bounds.MakeStatusBar();
5504 Rect variables_bounds;
5505 Rect threads_bounds;
5506 Rect source_variables_bounds;
5507 content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds, threads_bounds);
5508 source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds, variables_bounds);
5510 WindowSP menubar_window_sp = main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false);
5511 // Let the menubar get keys if the active window doesn't handle the
5512 // keys that are typed so it can respond to menubar key presses.
5513 menubar_window_sp->SetCanBeActive(false); // Don't let the menubar become the active window
5514 menubar_window_sp->SetDelegate(menubar_sp);
5516 WindowSP source_window_sp (main_window_sp->CreateSubWindow("Source",
5519 WindowSP variables_window_sp (main_window_sp->CreateSubWindow("Variables",
5522 WindowSP threads_window_sp (main_window_sp->CreateSubWindow("Threads",
5525 WindowSP status_window_sp (main_window_sp->CreateSubWindow("Status",
5528 status_window_sp->SetCanBeActive(false); // Don't let the status bar become the active window
5529 main_window_sp->SetDelegate (std::static_pointer_cast<WindowDelegate>(app_delegate_sp));
5530 source_window_sp->SetDelegate (WindowDelegateSP(new SourceFileWindowDelegate(m_debugger)));
5531 variables_window_sp->SetDelegate (WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
5532 TreeDelegateSP thread_delegate_sp (new ThreadsTreeDelegate(m_debugger));
5533 threads_window_sp->SetDelegate (WindowDelegateSP(new TreeWindowDelegate(m_debugger, thread_delegate_sp)));
5534 status_window_sp->SetDelegate (WindowDelegateSP(new StatusBarWindowDelegate(m_debugger)));
5536 // Show the main help window once the first time the curses GUI is launched
5537 static bool g_showed_help = false;
5540 g_showed_help = true;
5541 main_window_sp->CreateHelpSubwindow();
5544 init_pair (1, COLOR_WHITE , COLOR_BLUE );
5545 init_pair (2, COLOR_BLACK , COLOR_WHITE );
5546 init_pair (3, COLOR_MAGENTA , COLOR_WHITE );
5547 init_pair (4, COLOR_MAGENTA , COLOR_BLACK );
5548 init_pair (5, COLOR_RED , COLOR_BLACK );
5553 IOHandlerCursesGUI::Deactivate ()
5555 m_app_ap->Terminate();
5559 IOHandlerCursesGUI::Run ()
5561 m_app_ap->Run(m_debugger);
5565 IOHandlerCursesGUI::~IOHandlerCursesGUI() = default;
5568 IOHandlerCursesGUI::Cancel ()
5573 IOHandlerCursesGUI::Interrupt ()
5579 IOHandlerCursesGUI::GotEOF()
5583 #endif // LLDB_DISABLE_CURSES