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 #include "lldb/lldb-python.h"
15 #include "lldb/Breakpoint/BreakpointLocation.h"
16 #include "lldb/Core/IOHandler.h"
17 #include "lldb/Core/Debugger.h"
18 #include "lldb/Core/State.h"
19 #include "lldb/Core/StreamFile.h"
20 #include "lldb/Core/ValueObjectRegister.h"
21 #include "lldb/Host/Editline.h"
22 #include "lldb/Interpreter/CommandCompletions.h"
23 #include "lldb/Interpreter/CommandInterpreter.h"
24 #include "lldb/Symbol/Block.h"
25 #include "lldb/Symbol/Function.h"
26 #include "lldb/Symbol/Symbol.h"
27 #include "lldb/Target/RegisterContext.h"
28 #include "lldb/Target/ThreadPlan.h"
30 #ifndef LLDB_DISABLE_CURSES
36 using namespace lldb_private;
38 IOHandler::IOHandler (Debugger &debugger) :
40 StreamFileSP(), // Adopt STDIN from top input reader
41 StreamFileSP(), // Adopt STDOUT from top input reader
42 StreamFileSP(), // Adopt STDERR from top input reader
48 IOHandler::IOHandler (Debugger &debugger,
49 const lldb::StreamFileSP &input_sp,
50 const lldb::StreamFileSP &output_sp,
51 const lldb::StreamFileSP &error_sp,
53 m_debugger (debugger),
54 m_input_sp (input_sp),
55 m_output_sp (output_sp),
56 m_error_sp (error_sp),
62 // If any files are not specified, then adopt them from the top input reader.
63 if (!m_input_sp || !m_output_sp || !m_error_sp)
64 debugger.AdoptTopIOHandlerFilesIfInvalid (m_input_sp,
69 IOHandler::~IOHandler()
75 IOHandler::GetInputFD()
78 return m_input_sp->GetFile().GetDescriptor();
83 IOHandler::GetOutputFD()
86 return m_output_sp->GetFile().GetDescriptor();
91 IOHandler::GetErrorFD()
94 return m_error_sp->GetFile().GetDescriptor();
99 IOHandler::GetInputFILE()
102 return m_input_sp->GetFile().GetStream();
107 IOHandler::GetOutputFILE()
110 return m_output_sp->GetFile().GetStream();
115 IOHandler::GetErrorFILE()
118 return m_error_sp->GetFile().GetStream();
123 IOHandler::GetInputStreamFile()
129 IOHandler::GetOutputStreamFile()
136 IOHandler::GetErrorStreamFile()
142 IOHandler::GetIsInteractive ()
144 return GetInputStreamFile()->GetFile().GetIsInteractive ();
148 IOHandler::GetIsRealTerminal ()
150 return GetInputStreamFile()->GetFile().GetIsRealTerminal();
153 IOHandlerConfirm::IOHandlerConfirm (Debugger &debugger,
155 bool default_response) :
156 IOHandlerEditline(debugger,
157 NULL, // NULL editline_name means no history loaded/saved
161 m_default_response (default_response),
162 m_user_response (default_response)
164 StreamString prompt_stream;
165 prompt_stream.PutCString(prompt);
166 if (m_default_response)
167 prompt_stream.Printf(": [Y/n] ");
169 prompt_stream.Printf(": [y/N] ");
171 SetPrompt (prompt_stream.GetString().c_str());
176 IOHandlerConfirm::~IOHandlerConfirm ()
181 IOHandlerConfirm::IOHandlerComplete (IOHandler &io_handler,
182 const char *current_line,
184 const char *last_char,
185 int skip_first_n_matches,
189 if (current_line == cursor)
191 if (m_default_response)
193 matches.AppendString("y");
197 matches.AppendString("n");
200 return matches.GetSize();
204 IOHandlerConfirm::IOHandlerInputComplete (IOHandler &io_handler, std::string &line)
208 // User just hit enter, set the response to the default
209 m_user_response = m_default_response;
210 io_handler.SetIsDone(true);
214 if (line.size() == 1)
220 m_user_response = true;
221 io_handler.SetIsDone(true);
225 m_user_response = false;
226 io_handler.SetIsDone(true);
233 if (line == "yes" || line == "YES" || line == "Yes")
235 m_user_response = true;
236 io_handler.SetIsDone(true);
238 else if (line == "no" || line == "NO" || line == "No")
240 m_user_response = false;
241 io_handler.SetIsDone(true);
246 IOHandlerDelegate::IOHandlerComplete (IOHandler &io_handler,
247 const char *current_line,
249 const char *last_char,
250 int skip_first_n_matches,
254 switch (m_completion)
256 case Completion::None:
259 case Completion::LLDBCommand:
260 return io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion (current_line,
263 skip_first_n_matches,
267 case Completion::Expression:
269 bool word_complete = false;
270 const char *word_start = cursor;
271 if (cursor > current_line)
273 while (word_start > current_line && !isspace(*word_start))
275 CommandCompletions::InvokeCommonCompletionCallbacks (io_handler.GetDebugger().GetCommandInterpreter(),
276 CommandCompletions::eVariablePathCompletion,
278 skip_first_n_matches,
284 size_t num_matches = matches.GetSize();
287 std::string common_prefix;
288 matches.LongestCommonPrefix (common_prefix);
289 const size_t partial_name_len = strlen(word_start);
291 // If we matched a unique single command, add a space...
292 // Only do this if the completer told us this was a complete word, however...
293 if (num_matches == 1 && word_complete)
295 common_prefix.push_back(' ');
297 common_prefix.erase (0, partial_name_len);
298 matches.InsertStringAtIndex(0, std::move(common_prefix));
310 IOHandlerEditline::IOHandlerEditline (Debugger &debugger,
311 const char *editline_name, // Used for saving history files
314 IOHandlerDelegate &delegate) :
315 IOHandlerEditline(debugger,
316 StreamFileSP(), // Inherit input from top input reader
317 StreamFileSP(), // Inherit output from top input reader
318 StreamFileSP(), // Inherit error from top input reader
320 editline_name, // Used for saving history files
327 IOHandlerEditline::IOHandlerEditline (Debugger &debugger,
328 const lldb::StreamFileSP &input_sp,
329 const lldb::StreamFileSP &output_sp,
330 const lldb::StreamFileSP &error_sp,
332 const char *editline_name, // Used for saving history files
335 IOHandlerDelegate &delegate) :
336 IOHandler (debugger, input_sp, output_sp, error_sp, flags),
338 m_delegate (delegate),
340 m_multi_line (multi_line)
344 bool use_editline = false;
347 use_editline = m_input_sp->GetFile().GetIsRealTerminal();
354 m_editline_ap.reset(new Editline (editline_name,
355 prompt ? prompt : "",
359 m_editline_ap->SetLineCompleteCallback (LineCompletedCallback, this);
360 m_editline_ap->SetAutoCompleteCallback (AutoCompleteCallback, this);
365 IOHandlerEditline::~IOHandlerEditline ()
367 m_editline_ap.reset();
372 IOHandlerEditline::GetLine (std::string &line)
376 return m_editline_ap->GetLine(line).Success();
382 FILE *in = GetInputFILE();
385 if (GetIsInteractive())
387 const char *prompt = GetPrompt();
388 if (prompt && prompt[0])
390 FILE *out = GetOutputFILE();
393 ::fprintf(out, "%s", prompt);
400 bool got_line = false;
403 if (fgets(buffer, sizeof(buffer), in) == NULL)
408 size_t buffer_len = strlen(buffer);
409 assert (buffer[buffer_len] == '\0');
410 char last_char = buffer[buffer_len-1];
411 if (last_char == '\r' || last_char == '\n')
414 // Strip trailing newlines
415 while (last_char == '\r' || last_char == '\n')
420 last_char = buffer[buffer_len-1];
423 line.append(buffer, buffer_len);
426 // We might have gotten a newline on a line by itself
427 // make sure to return true in this case.
432 // No more input file, we are done...
441 IOHandlerEditline::LineCompletedCallback (Editline *editline,
447 IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton;
448 return editline_reader->m_delegate.IOHandlerLinesUpdated(*editline_reader, lines, line_idx, error);
452 IOHandlerEditline::AutoCompleteCallback (const char *current_line,
454 const char *last_char,
455 int skip_first_n_matches,
460 IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton;
462 return editline_reader->m_delegate.IOHandlerComplete (*editline_reader,
466 skip_first_n_matches,
473 IOHandlerEditline::GetPrompt ()
476 return m_editline_ap->GetPrompt ();
477 else if (m_prompt.empty())
479 return m_prompt.c_str();
483 IOHandlerEditline::SetPrompt (const char *p)
490 m_editline_ap->SetPrompt (m_prompt.empty() ? NULL : m_prompt.c_str());
495 IOHandlerEditline::GetLines (StringList &lines)
497 bool success = false;
500 std::string end_token;
501 success = m_editline_ap->GetLines(end_token, lines).Success();
505 LineStatus lines_status = LineStatus::Success;
507 while (lines_status == LineStatus::Success)
512 lines.AppendString(line);
514 lines_status = m_delegate.IOHandlerLinesUpdated(*this, lines, lines.GetSize() - 1, error);
518 lines_status = LineStatus::Done;
521 success = lines.GetSize() > 0;
526 // Each IOHandler gets to run until it is done. It should read data
527 // from the "in" and place output into "out" and "err and return
530 IOHandlerEditline::Run ()
538 if (GetLines (lines))
540 line = lines.CopyList();
541 m_delegate.IOHandlerInputComplete(*this, line);
552 m_delegate.IOHandlerInputComplete(*this, line);
563 IOHandlerEditline::Hide ()
565 if (m_editline_ap && m_editline_ap->GettingLine())
566 m_editline_ap->Hide();
571 IOHandlerEditline::Refresh ()
573 if (m_editline_ap && m_editline_ap->GettingLine())
574 m_editline_ap->Refresh();
577 const char *prompt = GetPrompt();
578 if (prompt && prompt[0])
580 FILE *out = GetOutputFILE();
583 ::fprintf(out, "%s", prompt);
591 IOHandlerEditline::Cancel ()
594 m_editline_ap->Interrupt ();
598 IOHandlerEditline::Interrupt ()
601 m_editline_ap->Interrupt();
605 IOHandlerEditline::GotEOF()
608 m_editline_ap->Interrupt();
611 // we may want curses to be disabled for some builds
612 // for instance, windows
613 #ifndef LLDB_DISABLE_CURSES
615 #include "lldb/Core/ValueObject.h"
616 #include "lldb/Symbol/VariableList.h"
617 #include "lldb/Target/Target.h"
618 #include "lldb/Target/Process.h"
619 #include "lldb/Target/Thread.h"
620 #include "lldb/Target/StackFrame.h"
622 #define KEY_RETURN 10
623 #define KEY_ESCAPE 27
630 class WindowDelegate;
631 typedef std::shared_ptr<Menu> MenuSP;
632 typedef std::shared_ptr<MenuDelegate> MenuDelegateSP;
633 typedef std::shared_ptr<Window> WindowSP;
634 typedef std::shared_ptr<WindowDelegate> WindowDelegateSP;
635 typedef std::vector<MenuSP> Menus;
636 typedef std::vector<WindowSP> Windows;
637 typedef std::vector<WindowDelegateSP> WindowDelegates;
640 type summary add -s "x=${var.x}, y=${var.y}" curses::Point
641 type summary add -s "w=${var.width}, h=${var.height}" curses::Size
642 type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect
649 Point (int _x = 0, int _y = 0) :
663 operator += (const Point &rhs)
673 printf ("(x=%i, y=%i)\n", x, y);
678 bool operator == (const Point &lhs, const Point &rhs)
680 return lhs.x == rhs.x && lhs.y == rhs.y;
682 bool operator != (const Point &lhs, const Point &rhs)
684 return lhs.x != rhs.x || lhs.y != rhs.y;
691 Size (int w = 0, int h = 0) :
707 printf ("(w=%i, h=%i)\n", width, height);
712 bool operator == (const Size &lhs, const Size &rhs)
714 return lhs.width == rhs.width && lhs.height == rhs.height;
716 bool operator != (const Size &lhs, const Size &rhs)
718 return lhs.width != rhs.width || lhs.height != rhs.height;
732 Rect (const Point &p, const Size &s) :
748 printf ("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width, size.height);
754 if (size.width > w*2)
758 if (size.height > h*2)
762 // Return a status bar rectangle which is the last line of
763 // this rectangle. This rectangle will be modified to not
764 // include the status bar area.
771 status_bar.origin.x = origin.x;
772 status_bar.origin.y = size.height;
773 status_bar.size.width = size.width;
774 status_bar.size.height = 1;
780 // Return a menubar rectangle which is the first line of
781 // this rectangle. This rectangle will be modified to not
782 // include the menubar area.
789 menubar.origin.x = origin.x;
790 menubar.origin.y = origin.y;
791 menubar.size.width = size.width;
792 menubar.size.height = 1;
800 HorizontalSplitPercentage (float top_percentage, Rect &top, Rect &bottom) const
802 float top_height = top_percentage * size.height;
803 HorizontalSplit (top_height, top, bottom);
807 HorizontalSplit (int top_height, Rect &top, Rect &bottom) const
810 if (top_height < size.height)
812 top.size.height = top_height;
813 bottom.origin.x = origin.x;
814 bottom.origin.y = origin.y + top.size.height;
815 bottom.size.width = size.width;
816 bottom.size.height = size.height - top.size.height;
825 VerticalSplitPercentage (float left_percentage, Rect &left, Rect &right) const
827 float left_width = left_percentage * size.width;
828 VerticalSplit (left_width, left, right);
833 VerticalSplit (int left_width, Rect &left, Rect &right) const
836 if (left_width < size.width)
838 left.size.width = left_width;
839 right.origin.x = origin.x + left.size.width;
840 right.origin.y = origin.y;
841 right.size.width = size.width - left.size.width;
842 right.size.height = size.height;
851 bool operator == (const Rect &lhs, const Rect &rhs)
853 return lhs.origin == rhs.origin && lhs.size == rhs.size;
855 bool operator != (const Rect &lhs, const Rect &rhs)
857 return lhs.origin != rhs.origin || lhs.size != rhs.size;
860 enum HandleCharResult
867 enum class MenuActionResult
871 Quit // Exit all menus and quit
877 const char *description;
889 WindowDelegateDraw (Window &window, bool force)
891 return false; // Drawing not handled
894 virtual HandleCharResult
895 WindowDelegateHandleChar (Window &window, int key)
897 return eKeyNotHandled;
901 WindowDelegateGetHelpText ()
907 WindowDelegateGetKeyHelp ()
913 class HelpDialogDelegate :
914 public WindowDelegate
917 HelpDialogDelegate (const char *text, KeyHelp *key_help_array);
920 ~HelpDialogDelegate();
923 WindowDelegateDraw (Window &window, bool force);
925 virtual HandleCharResult
926 WindowDelegateHandleChar (Window &window, int key);
931 return m_text.GetSize();
935 GetMaxLineLength () const
937 return m_text.GetMaxStringLength();
942 int m_first_visible_line;
950 Window (const char *name) :
957 m_curr_active_window_idx (UINT32_MAX),
958 m_prev_active_window_idx (UINT32_MAX),
960 m_needs_update (true),
961 m_can_activate (true),
966 Window (const char *name, WINDOW *w, bool del = true) :
973 m_curr_active_window_idx (UINT32_MAX),
974 m_prev_active_window_idx (UINT32_MAX),
976 m_needs_update (true),
977 m_can_activate (true),
984 Window (const char *name, const Rect &bounds) :
990 m_curr_active_window_idx (UINT32_MAX),
991 m_prev_active_window_idx (UINT32_MAX),
993 m_needs_update (true),
994 m_can_activate (true),
997 Reset (::newwin (bounds.size.height, bounds.size.width, bounds.origin.y, bounds.origin.y));
1003 RemoveSubWindows ();
1008 Reset (WINDOW *w = NULL, bool del = true)
1015 ::del_panel (m_panel);
1018 if (m_window && m_delete)
1020 ::delwin (m_window);
1027 m_panel = ::new_panel (m_window);
1032 void AttributeOn (attr_t attr) { ::wattron (m_window, attr); }
1033 void AttributeOff (attr_t attr) { ::wattroff (m_window, attr); }
1034 void Box (chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) { ::box(m_window, v_char, h_char); }
1035 void Clear () { ::wclear (m_window); }
1036 void Erase () { ::werase (m_window); }
1037 Rect GetBounds () { return Rect (GetParentOrigin(), GetSize()); } // Get the rectangle in our parent window
1038 int GetChar () { return ::wgetch (m_window); }
1039 int GetCursorX () { return getcurx (m_window); }
1040 int GetCursorY () { return getcury (m_window); }
1041 Rect GetFrame () { return Rect (Point(), GetSize()); } // Get our rectangle in our own coordinate system
1042 Point GetParentOrigin() { return Point (GetParentX(), GetParentY()); }
1043 Size GetSize() { return Size (GetWidth(), GetHeight()); }
1044 int GetParentX () { return getparx (m_window); }
1045 int GetParentY () { return getpary (m_window); }
1046 int GetMaxX() { return getmaxx (m_window); }
1047 int GetMaxY() { return getmaxy (m_window); }
1048 int GetWidth() { return GetMaxX(); }
1049 int GetHeight() { return GetMaxY(); }
1050 void MoveCursor (int x, int y) { ::wmove (m_window, y, x); }
1051 void MoveWindow (int x, int y) { MoveWindow(Point(x,y)); }
1052 void Resize (int w, int h) { ::wresize(m_window, h, w); }
1053 void Resize (const Size &size) { ::wresize(m_window, size.height, size.width); }
1054 void PutChar (int ch) { ::waddch (m_window, ch); }
1055 void PutCString (const char *s, int len = -1) { ::waddnstr (m_window, s, len); }
1056 void Refresh () { ::wrefresh (m_window); }
1057 void DeferredRefresh ()
1059 // We are using panels, so we don't need to call this...
1060 //::wnoutrefresh(m_window);
1062 void SetBackground (int color_pair_idx) { ::wbkgd (m_window,COLOR_PAIR(color_pair_idx)); }
1063 void UnderlineOn () { AttributeOn(A_UNDERLINE); }
1064 void UnderlineOff () { AttributeOff(A_UNDERLINE); }
1066 void PutCStringTruncated (const char *s, int right_pad)
1068 int bytes_left = GetWidth() - GetCursorX();
1069 if (bytes_left > right_pad)
1071 bytes_left -= right_pad;
1072 ::waddnstr (m_window, s, bytes_left);
1077 MoveWindow (const Point &origin)
1079 const bool moving_window = origin != GetParentOrigin();
1080 if (m_is_subwin && moving_window)
1082 // Can't move subwindows, must delete and re-create
1083 Size size = GetSize();
1084 Reset (::subwin (m_parent->m_window,
1092 ::mvwin (m_window, origin.y, origin.x);
1097 SetBounds (const Rect &bounds)
1099 const bool moving_window = bounds.origin != GetParentOrigin();
1100 if (m_is_subwin && moving_window)
1102 // Can't move subwindows, must delete and re-create
1103 Reset (::subwin (m_parent->m_window,
1107 bounds.origin.x), true);
1112 MoveWindow(bounds.origin);
1113 Resize (bounds.size);
1118 Printf (const char *format, ...) __attribute__ ((format (printf, 2, 3)))
1121 va_start (args, format);
1122 vwprintw(m_window, format, args);
1129 ::touchwin (m_window);
1135 CreateSubWindow (const char *name, const Rect &bounds, bool make_active)
1137 WindowSP subwindow_sp;
1140 subwindow_sp.reset(new Window(name, ::subwin (m_window,
1144 bounds.origin.x), true));
1145 subwindow_sp->m_is_subwin = true;
1149 subwindow_sp.reset(new Window(name, ::newwin (bounds.size.height,
1152 bounds.origin.x), true));
1153 subwindow_sp->m_is_subwin = false;
1155 subwindow_sp->m_parent = this;
1158 m_prev_active_window_idx = m_curr_active_window_idx;
1159 m_curr_active_window_idx = m_subwindows.size();
1161 m_subwindows.push_back(subwindow_sp);
1162 ::top_panel (subwindow_sp->m_panel);
1163 m_needs_update = true;
1164 return subwindow_sp;
1168 RemoveSubWindow (Window *window)
1170 Windows::iterator pos, end = m_subwindows.end();
1172 for (pos = m_subwindows.begin(); pos != end; ++pos, ++i)
1174 if ((*pos).get() == window)
1176 if (m_prev_active_window_idx == i)
1177 m_prev_active_window_idx = UINT32_MAX;
1178 else if (m_prev_active_window_idx != UINT32_MAX && m_prev_active_window_idx > i)
1179 --m_prev_active_window_idx;
1181 if (m_curr_active_window_idx == i)
1182 m_curr_active_window_idx = UINT32_MAX;
1183 else if (m_curr_active_window_idx != UINT32_MAX && m_curr_active_window_idx > i)
1184 --m_curr_active_window_idx;
1186 m_subwindows.erase(pos);
1187 m_needs_update = true;
1191 ::touchwin (stdscr);
1199 FindSubWindow (const char *name)
1201 Windows::iterator pos, end = m_subwindows.end();
1203 for (pos = m_subwindows.begin(); pos != end; ++pos, ++i)
1205 if ((*pos)->m_name.compare(name) == 0)
1214 m_curr_active_window_idx = UINT32_MAX;
1215 m_prev_active_window_idx = UINT32_MAX;
1216 for (Windows::iterator pos = m_subwindows.begin();
1217 pos != m_subwindows.end();
1218 pos = m_subwindows.erase(pos))
1225 ::touchwin (stdscr);
1239 //----------------------------------------------------------------------
1240 // Window drawing utilities
1241 //----------------------------------------------------------------------
1243 DrawTitleBox (const char *title, const char *bottom_message = NULL)
1247 attr = A_BOLD | COLOR_PAIR(2);
1256 if (title && title[0])
1263 if (bottom_message && bottom_message[0])
1265 int bottom_message_length = strlen(bottom_message);
1266 int x = GetWidth() - 3 - (bottom_message_length + 2);
1270 MoveCursor (x, GetHeight() - 1);
1272 PutCString(bottom_message);
1277 MoveCursor (1, GetHeight() - 1);
1279 PutCStringTruncated (bottom_message, 1);
1290 if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw (*this, force))
1293 for (auto &subwindow_sp : m_subwindows)
1294 subwindow_sp->Draw(force);
1298 CreateHelpSubwindow ()
1302 const char *text = m_delegate_sp->WindowDelegateGetHelpText ();
1303 KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp ();
1304 if ((text && text[0]) || key_help)
1306 std::auto_ptr<HelpDialogDelegate> help_delegate_ap(new HelpDialogDelegate(text, key_help));
1307 const size_t num_lines = help_delegate_ap->GetNumLines();
1308 const size_t max_length = help_delegate_ap->GetMaxLineLength();
1309 Rect bounds = GetBounds();
1311 if (max_length + 4 < bounds.size.width)
1313 bounds.origin.x += (bounds.size.width - max_length + 4)/2;
1314 bounds.size.width = max_length + 4;
1318 if (bounds.size.width > 100)
1320 const int inset_w = bounds.size.width / 4;
1321 bounds.origin.x += inset_w;
1322 bounds.size.width -= 2*inset_w;
1326 if (num_lines + 2 < bounds.size.height)
1328 bounds.origin.y += (bounds.size.height - num_lines + 2)/2;
1329 bounds.size.height = num_lines + 2;
1333 if (bounds.size.height > 100)
1335 const int inset_h = bounds.size.height / 4;
1336 bounds.origin.y += inset_h;
1337 bounds.size.height -= 2*inset_h;
1340 WindowSP help_window_sp;
1341 Window *parent_window = GetParent();
1343 help_window_sp = parent_window->CreateSubWindow("Help", bounds, true);
1345 help_window_sp = CreateSubWindow("Help", bounds, true);
1346 help_window_sp->SetDelegate(WindowDelegateSP(help_delegate_ap.release()));
1353 virtual HandleCharResult
1354 HandleChar (int key)
1356 // Always check the active window first
1357 HandleCharResult result = eKeyNotHandled;
1358 WindowSP active_window_sp = GetActiveWindow ();
1359 if (active_window_sp)
1361 result = active_window_sp->HandleChar (key);
1362 if (result != eKeyNotHandled)
1368 result = m_delegate_sp->WindowDelegateHandleChar (*this, key);
1369 if (result != eKeyNotHandled)
1373 // Then check for any windows that want any keys
1374 // that weren't handled. This is typically only
1376 // Make a copy of the subwindows in case any HandleChar()
1377 // functions muck with the subwindows. If we don't do this,
1378 // we can crash when iterating over the subwindows.
1379 Windows subwindows (m_subwindows);
1380 for (auto subwindow_sp : subwindows)
1382 if (subwindow_sp->m_can_activate == false)
1384 HandleCharResult result = subwindow_sp->HandleChar(key);
1385 if (result != eKeyNotHandled)
1390 return eKeyNotHandled;
1394 SetActiveWindow (Window *window)
1396 const size_t num_subwindows = m_subwindows.size();
1397 for (size_t i=0; i<num_subwindows; ++i)
1399 if (m_subwindows[i].get() == window)
1401 m_prev_active_window_idx = m_curr_active_window_idx;
1402 ::top_panel (window->m_panel);
1403 m_curr_active_window_idx = i;
1413 if (!m_subwindows.empty())
1415 if (m_curr_active_window_idx >= m_subwindows.size())
1417 if (m_prev_active_window_idx < m_subwindows.size())
1419 m_curr_active_window_idx = m_prev_active_window_idx;
1420 m_prev_active_window_idx = UINT32_MAX;
1422 else if (IsActive())
1424 m_prev_active_window_idx = UINT32_MAX;
1425 m_curr_active_window_idx = UINT32_MAX;
1427 // Find first window that wants to be active if this window is active
1428 const size_t num_subwindows = m_subwindows.size();
1429 for (size_t i=0; i<num_subwindows; ++i)
1431 if (m_subwindows[i]->GetCanBeActive())
1433 m_curr_active_window_idx = i;
1440 if (m_curr_active_window_idx < m_subwindows.size())
1441 return m_subwindows[m_curr_active_window_idx];
1447 GetCanBeActive () const
1449 return m_can_activate;
1453 SetCanBeActive (bool b)
1458 const WindowDelegateSP &
1459 GetDelegate () const
1461 return m_delegate_sp;
1465 SetDelegate (const WindowDelegateSP &delegate_sp)
1467 m_delegate_sp = delegate_sp;
1480 return m_parent->GetActiveWindow().get() == this;
1482 return true; // Top level window is always active
1486 SelectNextWindowAsActive ()
1488 // Move active focus to next window
1489 const size_t num_subwindows = m_subwindows.size();
1490 if (m_curr_active_window_idx == UINT32_MAX)
1493 for (auto subwindow_sp : m_subwindows)
1495 if (subwindow_sp->GetCanBeActive())
1497 m_curr_active_window_idx = idx;
1503 else if (m_curr_active_window_idx + 1 < num_subwindows)
1505 bool handled = false;
1506 m_prev_active_window_idx = m_curr_active_window_idx;
1507 for (size_t idx=m_curr_active_window_idx + 1; idx<num_subwindows; ++idx)
1509 if (m_subwindows[idx]->GetCanBeActive())
1511 m_curr_active_window_idx = idx;
1518 for (size_t idx=0; idx<=m_prev_active_window_idx; ++idx)
1520 if (m_subwindows[idx]->GetCanBeActive())
1522 m_curr_active_window_idx = idx;
1530 m_prev_active_window_idx = m_curr_active_window_idx;
1531 for (size_t idx=0; idx<num_subwindows; ++idx)
1533 if (m_subwindows[idx]->GetCanBeActive())
1535 m_curr_active_window_idx = idx;
1545 return m_name.c_str();
1552 Windows m_subwindows;
1553 WindowDelegateSP m_delegate_sp;
1554 uint32_t m_curr_active_window_idx;
1555 uint32_t m_prev_active_window_idx;
1557 bool m_needs_update;
1558 bool m_can_activate;
1562 DISALLOW_COPY_AND_ASSIGN(Window);
1568 virtual ~MenuDelegate() {}
1570 virtual MenuActionResult
1571 MenuDelegateAction (Menu &menu) = 0;
1574 class Menu : public WindowDelegate
1585 // Menubar or separator constructor
1588 // Menuitem constructor
1589 Menu (const char *name,
1590 const char *key_name,
1592 uint64_t identifier);
1599 const MenuDelegateSP &
1600 GetDelegate () const
1602 return m_delegate_sp;
1606 SetDelegate (const MenuDelegateSP &delegate_sp)
1608 m_delegate_sp = delegate_sp;
1612 RecalculateNameLengths();
1615 AddSubmenu (const MenuSP &menu_sp);
1618 DrawAndRunMenu (Window &window);
1621 DrawMenuTitle (Window &window, bool highlight);
1624 WindowDelegateDraw (Window &window, bool force);
1626 virtual HandleCharResult
1627 WindowDelegateHandleChar (Window &window, int key);
1630 ActionPrivate (Menu &menu)
1632 MenuActionResult result = MenuActionResult::NotHandled;
1635 result = m_delegate_sp->MenuDelegateAction (menu);
1636 if (result != MenuActionResult::NotHandled)
1641 result = m_parent->ActionPrivate(menu);
1642 if (result != MenuActionResult::NotHandled)
1645 return m_canned_result;
1651 // Call the recursive action so it can try to handle it
1652 // with the menu delegate, and if not, try our parent menu
1653 return ActionPrivate (*this);
1657 SetCannedResult (MenuActionResult result)
1659 m_canned_result = result;
1675 GetSelectedSubmenuIndex () const
1681 SetSelectedSubmenuIndex (int idx)
1693 GetStartingColumn() const
1699 SetStartingColumn(int col)
1711 SetKeyValue(int key_value)
1713 m_key_value = key_value;
1729 GetDrawWidth () const
1731 return m_max_submenu_name_length + m_max_submenu_key_name_length + 8;
1736 GetIdentifier() const
1738 return m_identifier;
1742 SetIdentifier (uint64_t identifier)
1744 m_identifier = identifier;
1749 std::string m_key_name;
1750 uint64_t m_identifier;
1754 int m_max_submenu_name_length;
1755 int m_max_submenu_key_name_length;
1759 WindowSP m_menu_window_sp;
1760 MenuActionResult m_canned_result;
1761 MenuDelegateSP m_delegate_sp;
1764 // Menubar or separator constructor
1765 Menu::Menu (Type type) :
1772 m_max_submenu_name_length (0),
1773 m_max_submenu_key_name_length (0),
1777 m_canned_result (MenuActionResult::NotHandled),
1782 // Menuitem constructor
1783 Menu::Menu (const char *name,
1784 const char *key_name,
1786 uint64_t identifier) :
1789 m_identifier (identifier),
1790 m_type (Type::Invalid),
1791 m_key_value (key_value),
1793 m_max_submenu_name_length (0),
1794 m_max_submenu_key_name_length (0),
1798 m_canned_result (MenuActionResult::NotHandled),
1801 if (name && name[0])
1804 m_type = Type::Item;
1805 if (key_name && key_name[0])
1806 m_key_name = key_name;
1810 m_type = Type::Separator;
1815 Menu::RecalculateNameLengths()
1817 m_max_submenu_name_length = 0;
1818 m_max_submenu_key_name_length = 0;
1819 Menus &submenus = GetSubmenus();
1820 const size_t num_submenus = submenus.size();
1821 for (size_t i=0; i<num_submenus; ++i)
1823 Menu *submenu = submenus[i].get();
1824 if (m_max_submenu_name_length < submenu->m_name.size())
1825 m_max_submenu_name_length = submenu->m_name.size();
1826 if (m_max_submenu_key_name_length < submenu->m_key_name.size())
1827 m_max_submenu_key_name_length = submenu->m_key_name.size();
1832 Menu::AddSubmenu (const MenuSP &menu_sp)
1834 menu_sp->m_parent = this;
1835 if (m_max_submenu_name_length < menu_sp->m_name.size())
1836 m_max_submenu_name_length = menu_sp->m_name.size();
1837 if (m_max_submenu_key_name_length < menu_sp->m_key_name.size())
1838 m_max_submenu_key_name_length = menu_sp->m_key_name.size();
1839 m_submenus.push_back(menu_sp);
1843 Menu::DrawMenuTitle (Window &window, bool highlight)
1845 if (m_type == Type::Separator)
1847 window.MoveCursor(0, window.GetCursorY());
1848 window.PutChar(ACS_LTEE);
1849 int width = window.GetWidth();
1853 for (size_t i=0; i< width; ++i)
1854 window.PutChar(ACS_HLINE);
1856 window.PutChar(ACS_RTEE);
1860 const int shortcut_key = m_key_value;
1861 bool underlined_shortcut = false;
1862 const attr_t hilgight_attr = A_REVERSE;
1864 window.AttributeOn(hilgight_attr);
1865 if (isprint(shortcut_key))
1867 size_t lower_pos = m_name.find(tolower(shortcut_key));
1868 size_t upper_pos = m_name.find(toupper(shortcut_key));
1869 const char *name = m_name.c_str();
1870 size_t pos = std::min<size_t>(lower_pos, upper_pos);
1871 if (pos != std::string::npos)
1873 underlined_shortcut = true;
1876 window.PutCString(name, pos);
1879 const attr_t shortcut_attr = A_UNDERLINE|A_BOLD;
1880 window.AttributeOn (shortcut_attr);
1881 window.PutChar(name[0]);
1882 window.AttributeOff(shortcut_attr);
1885 window.PutCString(name);
1889 if (!underlined_shortcut)
1891 window.PutCString(m_name.c_str());
1895 window.AttributeOff(hilgight_attr);
1897 if (m_key_name.empty())
1899 if (!underlined_shortcut && isprint(m_key_value))
1901 window.AttributeOn (COLOR_PAIR(3));
1902 window.Printf (" (%c)", m_key_value);
1903 window.AttributeOff (COLOR_PAIR(3));
1908 window.AttributeOn (COLOR_PAIR(3));
1909 window.Printf (" (%s)", m_key_name.c_str());
1910 window.AttributeOff (COLOR_PAIR(3));
1916 Menu::WindowDelegateDraw (Window &window, bool force)
1918 Menus &submenus = GetSubmenus();
1919 const size_t num_submenus = submenus.size();
1920 const int selected_idx = GetSelectedSubmenuIndex();
1921 Menu::Type menu_type = GetType ();
1924 case Menu::Type::Bar:
1926 window.SetBackground(2);
1927 window.MoveCursor(0, 0);
1928 for (size_t i=0; i<num_submenus; ++i)
1930 Menu *menu = submenus[i].get();
1932 window.PutChar(' ');
1933 menu->SetStartingColumn (window.GetCursorX());
1934 window.PutCString("| ");
1935 menu->DrawMenuTitle (window, false);
1937 window.PutCString(" |");
1938 window.DeferredRefresh();
1942 case Menu::Type::Item:
1950 window.SetBackground(2);
1952 for (size_t i=0; i<num_submenus; ++i)
1954 const bool is_selected = i == selected_idx;
1955 window.MoveCursor(x, y + i);
1958 // Remember where we want the cursor to be
1962 submenus[i]->DrawMenuTitle (window, is_selected);
1964 window.MoveCursor(cursor_x, cursor_y);
1965 window.DeferredRefresh();
1970 case Menu::Type::Separator:
1973 return true; // Drawing handled...
1977 Menu::WindowDelegateHandleChar (Window &window, int key)
1979 HandleCharResult result = eKeyNotHandled;
1981 Menus &submenus = GetSubmenus();
1982 const size_t num_submenus = submenus.size();
1983 const int selected_idx = GetSelectedSubmenuIndex();
1984 Menu::Type menu_type = GetType ();
1985 if (menu_type == Menu::Type::Bar)
1992 // Show last menu or first menu
1993 if (selected_idx < num_submenus)
1994 run_menu_sp = submenus[selected_idx];
1995 else if (!submenus.empty())
1996 run_menu_sp = submenus.front();
1997 result = eKeyHandled;
2003 if (m_selected >= num_submenus)
2005 if (m_selected < num_submenus)
2006 run_menu_sp = submenus[m_selected];
2007 else if (!submenus.empty())
2008 run_menu_sp = submenus.front();
2009 result = eKeyHandled;
2017 m_selected = num_submenus - 1;
2018 if (m_selected < num_submenus)
2019 run_menu_sp = submenus[m_selected];
2020 else if (!submenus.empty())
2021 run_menu_sp = submenus.front();
2022 result = eKeyHandled;
2027 for (size_t i=0; i<num_submenus; ++i)
2029 if (submenus[i]->GetKeyValue() == key)
2031 SetSelectedSubmenuIndex(i);
2032 run_menu_sp = submenus[i];
2033 result = eKeyHandled;
2042 // Run the action on this menu in case we need to populate the
2043 // menu with dynamic content and also in case check marks, and
2044 // any other menu decorations need to be caclulated
2045 if (run_menu_sp->Action() == MenuActionResult::Quit)
2046 return eQuitApplication;
2049 menu_bounds.origin.x = run_menu_sp->GetStartingColumn();
2050 menu_bounds.origin.y = 1;
2051 menu_bounds.size.width = run_menu_sp->GetDrawWidth();
2052 menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2;
2053 if (m_menu_window_sp)
2054 window.GetParent()->RemoveSubWindow(m_menu_window_sp.get());
2056 m_menu_window_sp = window.GetParent()->CreateSubWindow (run_menu_sp->GetName().c_str(),
2059 m_menu_window_sp->SetDelegate (run_menu_sp);
2062 else if (menu_type == Menu::Type::Item)
2067 if (m_submenus.size() > 1)
2069 const int start_select = m_selected;
2070 while (++m_selected != start_select)
2072 if (m_selected >= num_submenus)
2074 if (m_submenus[m_selected]->GetType() == Type::Separator)
2084 if (m_submenus.size() > 1)
2086 const int start_select = m_selected;
2087 while (--m_selected != start_select)
2090 m_selected = num_submenus - 1;
2091 if (m_submenus[m_selected]->GetType() == Type::Separator)
2101 if (selected_idx < num_submenus)
2103 if (submenus[selected_idx]->Action() == MenuActionResult::Quit)
2104 return eQuitApplication;
2105 window.GetParent()->RemoveSubWindow(&window);
2110 case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in case other chars are entered for escaped sequences
2111 window.GetParent()->RemoveSubWindow(&window);
2116 bool handled = false;
2117 for (size_t i=0; i<num_submenus; ++i)
2119 Menu *menu = submenus[i].get();
2120 if (menu->GetKeyValue() == key)
2123 SetSelectedSubmenuIndex(i);
2124 window.GetParent()->RemoveSubWindow(&window);
2125 if (menu->Action() == MenuActionResult::Quit)
2126 return eQuitApplication;
2135 else if (menu_type == Menu::Type::Separator)
2146 Application (FILE *in, FILE *out) :
2157 m_window_delegates.clear();
2158 m_window_sp.reset();
2161 ::delscreen(m_screen);
2169 ::setlocale(LC_ALL, "");
2170 ::setlocale(LC_CTYPE, "");
2174 m_screen = ::newterm(NULL, m_out, m_in);
2179 ::keypad(stdscr,TRUE);
2189 Run (Debugger &debugger)
2192 int delay_in_tenths_of_a_second = 1;
2194 // Alas the threading model in curses is a bit lame so we need to
2195 // resort to polling every 0.5 seconds. We could poll for stdin
2196 // ourselves and then pass the keys down but then we need to
2197 // translate all of the escape sequences ourselves. So we resort to
2198 // polling for input because we need to receive async process events
2199 // while in this loop.
2201 halfdelay(delay_in_tenths_of_a_second); // Poll using some number of tenths of seconds seconds when calling Window::GetChar()
2203 ListenerSP listener_sp (new Listener ("lldb.IOHandler.curses.Application"));
2204 ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass());
2205 ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass());
2206 ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass());
2207 debugger.EnableForwardEvents (listener_sp);
2210 #if defined(__APPLE__)
2211 std::deque<int> escape_chars;
2218 m_window_sp->Draw(false);
2219 // All windows should be calling Window::DeferredRefresh() instead
2220 // of Window::Refresh() so we can do a single update and avoid
2221 // any screen blinking
2224 // Cursor hiding isn't working on MacOSX, so hide it in the top left corner
2225 m_window_sp->MoveCursor(0, 0);
2231 #if defined(__APPLE__)
2232 // Terminal.app doesn't map its function keys correctly, F1-F4 default to:
2233 // \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if possible
2235 if (escape_chars.empty())
2236 ch = m_window_sp->GetChar();
2239 ch = escape_chars.front();
2240 escape_chars.pop_front();
2242 if (ch == KEY_ESCAPE)
2244 int ch2 = m_window_sp->GetChar();
2247 int ch3 = m_window_sp->GetChar();
2250 case 'P': ch = KEY_F(1); break;
2251 case 'Q': ch = KEY_F(2); break;
2252 case 'R': ch = KEY_F(3); break;
2253 case 'S': ch = KEY_F(4); break;
2255 escape_chars.push_back(ch2);
2257 escape_chars.push_back(ch3);
2262 escape_chars.push_back(ch2);
2265 int ch = m_window_sp->GetChar();
2270 if (feof(m_in) || ferror(m_in))
2276 // Just a timeout from using halfdelay(), check for events
2278 while (listener_sp->PeekAtNextEvent())
2280 listener_sp->GetNextEvent(event_sp);
2284 Broadcaster *broadcaster = event_sp->GetBroadcaster();
2287 //uint32_t event_type = event_sp->GetType();
2288 ConstString broadcaster_class (broadcaster->GetBroadcasterClass());
2289 if (broadcaster_class == broadcaster_class_process)
2292 continue; // Don't get any key, just update our view
2301 HandleCharResult key_result = m_window_sp->HandleChar(ch);
2307 case eKeyNotHandled:
2309 case eQuitApplication:
2316 debugger.CancelForwardEvents (listener_sp);
2324 m_window_sp.reset (new Window ("main", stdscr, false));
2329 GetWindowDelegates ()
2331 return m_window_delegates;
2335 WindowSP m_window_sp;
2336 WindowDelegates m_window_delegates;
2343 } // namespace curses
2346 using namespace curses;
2350 ValueObjectSP valobj;
2355 bool might_have_children;
2357 bool calculated_children;
2358 std::vector<Row> children;
2360 Row (const ValueObjectSP &v, Row *p) :
2366 might_have_children (v ? v->MightHaveChildren() : false),
2368 calculated_children (false),
2377 return 1 + parent->GetDepth();
2385 if (!calculated_children)
2387 calculated_children = true;
2390 const size_t num_children = valobj->GetNumChildren();
2391 for (size_t i=0; i<num_children; ++i)
2393 children.push_back(Row (valobj->GetChildAtIndex(i, true), this));
2406 DrawTree (Window &window)
2409 parent->DrawTreeForChild (window, this, 0);
2411 if (might_have_children)
2413 // It we can get UTF8 characters to work we should try to use the "symbol"
2414 // UTF8 string below
2415 // const char *symbol = "";
2416 // if (row.expanded)
2417 // symbol = "\xe2\x96\xbd ";
2419 // symbol = "\xe2\x96\xb7 ";
2420 // window.PutCString (symbol);
2422 // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
2423 // 'v' or '>' character...
2425 // window.PutChar (ACS_DARROW);
2427 // window.PutChar (ACS_RARROW);
2428 // Since we can't find any good looking right arrow/down arrow
2429 // symbols, just use a diamond...
2430 window.PutChar (ACS_DIAMOND);
2431 window.PutChar (ACS_HLINE);
2436 DrawTreeForChild (Window &window, Row *child, uint32_t reverse_depth)
2439 parent->DrawTreeForChild (window, this, reverse_depth + 1);
2441 if (&children.back() == child)
2444 if (reverse_depth == 0)
2446 window.PutChar (ACS_LLCORNER);
2447 window.PutChar (ACS_HLINE);
2451 window.PutChar (' ');
2452 window.PutChar (' ');
2457 if (reverse_depth == 0)
2459 window.PutChar (ACS_LTEE);
2460 window.PutChar (ACS_HLINE);
2464 window.PutChar (ACS_VLINE);
2465 window.PutChar (' ');
2471 struct DisplayOptions
2482 virtual ~TreeDelegate() {}
2483 virtual void TreeDelegateDrawTreeItem (TreeItem &item, Window &window) = 0;
2484 virtual void TreeDelegateGenerateChildren (TreeItem &item) = 0;
2485 virtual bool TreeDelegateItemSelected (TreeItem &item) = 0; // Return true if we need to update views
2487 typedef std::shared_ptr<TreeDelegate> TreeDelegateSP;
2493 TreeItem (TreeItem *parent, TreeDelegate &delegate, bool might_have_children) :
2495 m_delegate (delegate),
2499 m_might_have_children (might_have_children),
2500 m_is_expanded (false)
2505 operator=(const TreeItem &rhs)
2509 m_parent = rhs.m_parent;
2510 m_delegate = rhs.m_delegate;
2511 m_identifier = rhs.m_identifier;
2512 m_row_idx = rhs.m_row_idx;
2513 m_children = rhs.m_children;
2514 m_might_have_children = rhs.m_might_have_children;
2515 m_is_expanded = rhs.m_is_expanded;
2524 return 1 + m_parent->GetDepth();
2529 GetRowIndex () const
2541 Resize (size_t n, const TreeItem &t)
2543 m_children.resize(n, t);
2547 operator [](size_t i)
2549 return m_children[i];
2553 SetRowIndex (int row_idx)
2555 m_row_idx = row_idx;
2561 m_delegate.TreeDelegateGenerateChildren (*this);
2562 return m_children.size();
2568 m_delegate.TreeDelegateItemSelected(*this);
2571 CalculateRowIndexes (int &row_idx)
2573 SetRowIndex(row_idx);
2576 // The root item must calculate its children
2577 if (m_parent == NULL)
2580 const bool expanded = IsExpanded();
2581 for (auto &item : m_children)
2584 item.CalculateRowIndexes(row_idx);
2586 item.SetRowIndex(-1);
2599 return m_is_expanded;
2605 m_is_expanded = true;
2611 m_is_expanded = false;
2615 Draw (Window &window,
2616 const int first_visible_row,
2617 const uint32_t selected_row_idx,
2621 if (num_rows_left <= 0)
2624 if (m_row_idx >= first_visible_row)
2626 window.MoveCursor(2, row_idx + 1);
2629 m_parent->DrawTreeForChild (window, this, 0);
2631 if (m_might_have_children)
2633 // It we can get UTF8 characters to work we should try to use the "symbol"
2634 // UTF8 string below
2635 // const char *symbol = "";
2636 // if (row.expanded)
2637 // symbol = "\xe2\x96\xbd ";
2639 // symbol = "\xe2\x96\xb7 ";
2640 // window.PutCString (symbol);
2642 // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
2643 // 'v' or '>' character...
2645 // window.PutChar (ACS_DARROW);
2647 // window.PutChar (ACS_RARROW);
2648 // Since we can't find any good looking right arrow/down arrow
2649 // symbols, just use a diamond...
2650 window.PutChar (ACS_DIAMOND);
2651 window.PutChar (ACS_HLINE);
2653 bool highlight = (selected_row_idx == m_row_idx) && window.IsActive();
2656 window.AttributeOn(A_REVERSE);
2658 m_delegate.TreeDelegateDrawTreeItem(*this, window);
2661 window.AttributeOff(A_REVERSE);
2666 if (num_rows_left <= 0)
2667 return false; // We are done drawing...
2671 for (auto &item : m_children)
2673 // If we displayed all the rows and item.Draw() returns
2674 // false we are done drawing and can exit this for loop
2675 if (item.Draw(window, first_visible_row, selected_row_idx, row_idx, num_rows_left) == false)
2679 return num_rows_left >= 0; // Return true if not done drawing yet
2683 DrawTreeForChild (Window &window, TreeItem *child, uint32_t reverse_depth)
2686 m_parent->DrawTreeForChild (window, this, reverse_depth + 1);
2688 if (&m_children.back() == child)
2691 if (reverse_depth == 0)
2693 window.PutChar (ACS_LLCORNER);
2694 window.PutChar (ACS_HLINE);
2698 window.PutChar (' ');
2699 window.PutChar (' ');
2704 if (reverse_depth == 0)
2706 window.PutChar (ACS_LTEE);
2707 window.PutChar (ACS_HLINE);
2711 window.PutChar (ACS_VLINE);
2712 window.PutChar (' ');
2718 GetItemForRowIndex (uint32_t row_idx)
2720 if (m_row_idx == row_idx)
2722 if (m_children.empty())
2724 if (m_children.back().m_row_idx < row_idx)
2728 for (auto &item : m_children)
2730 TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx);
2731 if (selected_item_ptr)
2732 return selected_item_ptr;
2739 // GetUserData() const
2741 // return m_user_data;
2745 // SetUserData (void *user_data)
2747 // m_user_data = user_data;
2750 GetIdentifier() const
2752 return m_identifier;
2756 SetIdentifier (uint64_t identifier)
2758 m_identifier = identifier;
2764 TreeDelegate &m_delegate;
2765 //void *m_user_data;
2766 uint64_t m_identifier;
2767 int m_row_idx; // Zero based visible row index, -1 if not visible or for the root item
2768 std::vector<TreeItem> m_children;
2769 bool m_might_have_children;
2774 class TreeWindowDelegate : public WindowDelegate
2777 TreeWindowDelegate (Debugger &debugger, const TreeDelegateSP &delegate_sp) :
2778 m_debugger (debugger),
2779 m_delegate_sp (delegate_sp),
2780 m_root (NULL, *delegate_sp, true),
2781 m_selected_item (NULL),
2783 m_selected_row_idx (0),
2784 m_first_visible_row (0),
2793 NumVisibleRows () const
2795 return m_max_y - m_min_y;
2799 WindowDelegateDraw (Window &window, bool force)
2801 ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext());
2802 Process *process = exe_ctx.GetProcessPtr();
2804 bool display_content = false;
2807 StateType state = process->GetState();
2808 if (StateIsStoppedState(state, true))
2810 // We are stopped, so it is ok to
2811 display_content = true;
2813 else if (StateIsRunningState(state))
2815 return true; // Don't do any updating when we are running
2821 m_max_x = window.GetWidth() - 1;
2822 m_max_y = window.GetHeight() - 1;
2825 window.DrawTitleBox (window.GetName());
2827 if (display_content)
2829 const int num_visible_rows = NumVisibleRows();
2831 m_root.CalculateRowIndexes(m_num_rows);
2833 // If we unexpanded while having something selected our
2834 // total number of rows is less than the num visible rows,
2835 // then make sure we show all the rows by setting the first
2836 // visible row accordingly.
2837 if (m_first_visible_row > 0 && m_num_rows < num_visible_rows)
2838 m_first_visible_row = 0;
2840 // Make sure the selected row is always visible
2841 if (m_selected_row_idx < m_first_visible_row)
2842 m_first_visible_row = m_selected_row_idx;
2843 else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
2844 m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
2847 int num_rows_left = num_visible_rows;
2848 m_root.Draw (window, m_first_visible_row, m_selected_row_idx, row_idx, num_rows_left);
2849 // Get the selected row
2850 m_selected_item = m_root.GetItemForRowIndex (m_selected_row_idx);
2854 m_selected_item = NULL;
2857 window.DeferredRefresh();
2860 return true; // Drawing handled
2864 virtual const char *
2865 WindowDelegateGetHelpText ()
2867 return "Thread window keyboard shortcuts:";
2871 WindowDelegateGetKeyHelp ()
2873 static curses::KeyHelp g_source_view_key_help[] = {
2874 { KEY_UP, "Select previous item" },
2875 { KEY_DOWN, "Select next item" },
2876 { KEY_RIGHT, "Expand the selected item" },
2877 { KEY_LEFT, "Unexpand the selected item or select parent if not expanded" },
2878 { KEY_PPAGE, "Page up" },
2879 { KEY_NPAGE, "Page down" },
2880 { 'h', "Show help dialog" },
2881 { ' ', "Toggle item expansion" },
2883 { '.', "Page down" },
2886 return g_source_view_key_help;
2889 virtual HandleCharResult
2890 WindowDelegateHandleChar (Window &window, int c)
2897 if (m_first_visible_row > 0)
2899 if (m_first_visible_row > m_max_y)
2900 m_first_visible_row -= m_max_y;
2902 m_first_visible_row = 0;
2903 m_selected_row_idx = m_first_visible_row;
2904 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
2905 if (m_selected_item)
2906 m_selected_item->ItemWasSelected ();
2913 if (m_num_rows > m_max_y)
2915 if (m_first_visible_row + m_max_y < m_num_rows)
2917 m_first_visible_row += m_max_y;
2918 m_selected_row_idx = m_first_visible_row;
2919 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
2920 if (m_selected_item)
2921 m_selected_item->ItemWasSelected ();
2927 if (m_selected_row_idx > 0)
2929 --m_selected_row_idx;
2930 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
2931 if (m_selected_item)
2932 m_selected_item->ItemWasSelected ();
2936 if (m_selected_row_idx + 1 < m_num_rows)
2938 ++m_selected_row_idx;
2939 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
2940 if (m_selected_item)
2941 m_selected_item->ItemWasSelected ();
2946 if (m_selected_item)
2948 if (!m_selected_item->IsExpanded())
2949 m_selected_item->Expand();
2954 if (m_selected_item)
2956 if (m_selected_item->IsExpanded())
2957 m_selected_item->Unexpand();
2958 else if (m_selected_item->GetParent())
2960 m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex();
2961 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
2962 if (m_selected_item)
2963 m_selected_item->ItemWasSelected ();
2969 // Toggle expansion state when SPACE is pressed
2970 if (m_selected_item)
2972 if (m_selected_item->IsExpanded())
2973 m_selected_item->Unexpand();
2975 m_selected_item->Expand();
2980 window.CreateHelpSubwindow ();
2986 return eKeyNotHandled;
2990 Debugger &m_debugger;
2991 TreeDelegateSP m_delegate_sp;
2993 TreeItem *m_selected_item;
2995 int m_selected_row_idx;
2996 int m_first_visible_row;
3004 class FrameTreeDelegate : public TreeDelegate
3007 FrameTreeDelegate (const ThreadSP &thread_sp) :
3012 m_thread_wp = thread_sp;
3015 virtual ~FrameTreeDelegate()
3020 TreeDelegateDrawTreeItem (TreeItem &item, Window &window)
3022 ThreadSP thread_sp = m_thread_wp.lock();
3025 const uint64_t frame_idx = item.GetIdentifier();
3026 StackFrameSP frame_sp = thread_sp->GetStackFrameAtIndex(frame_idx);
3030 const SymbolContext &sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
3031 ExecutionContext exe_ctx (frame_sp);
3032 //const char *frame_format = "frame #${frame.index}: ${module.file.basename}{`${function.name}${function.pc-offset}}}";
3033 const char *frame_format = "frame #${frame.index}: {${function.name}${function.pc-offset}}}";
3034 if (Debugger::FormatPrompt (frame_format, &sc, &exe_ctx, NULL, strm))
3037 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
3043 TreeDelegateGenerateChildren (TreeItem &item)
3045 // No children for frames yet...
3049 TreeDelegateItemSelected (TreeItem &item)
3051 ThreadSP thread_sp = m_thread_wp.lock();
3054 const uint64_t frame_idx = item.GetIdentifier();
3055 thread_sp->SetSelectedFrameByIndex(frame_idx);
3061 SetThread (ThreadSP thread_sp)
3063 m_thread_wp = thread_sp;
3067 ThreadWP m_thread_wp;
3070 class ThreadTreeDelegate : public TreeDelegate
3073 ThreadTreeDelegate (Debugger &debugger) :
3075 m_debugger (debugger),
3077 m_tid (LLDB_INVALID_THREAD_ID),
3078 m_stop_id (UINT32_MAX)
3083 ~ThreadTreeDelegate()
3088 TreeDelegateDrawTreeItem (TreeItem &item, Window &window)
3090 ThreadSP thread_sp = m_thread_wp.lock();
3094 ExecutionContext exe_ctx (thread_sp);
3095 const char *format = "thread #${thread.index}: tid = ${thread.id}{, stop reason = ${thread.stop-reason}}";
3096 if (Debugger::FormatPrompt (format, NULL, &exe_ctx, NULL, strm))
3099 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
3104 TreeDelegateGenerateChildren (TreeItem &item)
3106 TargetSP target_sp (m_debugger.GetSelectedTarget());
3109 ProcessSP process_sp = target_sp->GetProcessSP();
3110 if (process_sp && process_sp->IsAlive())
3112 StateType state = process_sp->GetState();
3113 if (StateIsStoppedState(state, true))
3115 ThreadSP thread_sp = process_sp->GetThreadList().GetSelectedThread();
3118 if (m_stop_id == process_sp->GetStopID() && thread_sp->GetID() == m_tid)
3119 return; // Children are already up to date
3120 if (m_frame_delegate_sp)
3121 m_frame_delegate_sp->SetThread(thread_sp);
3124 // Always expand the thread item the first time we show it
3126 m_frame_delegate_sp.reset (new FrameTreeDelegate(thread_sp));
3129 m_stop_id = process_sp->GetStopID();
3130 m_thread_wp = thread_sp;
3131 m_tid = thread_sp->GetID();
3133 TreeItem t (&item, *m_frame_delegate_sp, false);
3134 size_t num_frames = thread_sp->GetStackFrameCount();
3135 item.Resize (num_frames, t);
3136 for (size_t i=0; i<num_frames; ++i)
3138 item[i].SetIdentifier(i);
3145 item.ClearChildren();
3149 TreeDelegateItemSelected (TreeItem &item)
3151 ThreadSP thread_sp = m_thread_wp.lock();
3154 ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList();
3155 Mutex::Locker locker (thread_list.GetMutex());
3156 ThreadSP selected_thread_sp = thread_list.GetSelectedThread();
3157 if (selected_thread_sp->GetID() != thread_sp->GetID())
3159 thread_list.SetSelectedThreadByID(thread_sp->GetID());
3167 Debugger &m_debugger;
3168 ThreadWP m_thread_wp;
3169 std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp;
3170 lldb::user_id_t m_tid;
3174 class ValueObjectListDelegate : public WindowDelegate
3177 ValueObjectListDelegate () :
3180 m_selected_row (NULL),
3181 m_selected_row_idx (0),
3182 m_first_visible_row (0),
3189 ValueObjectListDelegate (ValueObjectList &valobj_list) :
3190 m_valobj_list (valobj_list),
3192 m_selected_row (NULL),
3193 m_selected_row_idx (0),
3194 m_first_visible_row (0),
3199 SetValues (valobj_list);
3203 ~ValueObjectListDelegate()
3208 SetValues (ValueObjectList &valobj_list)
3210 m_selected_row = NULL;
3211 m_selected_row_idx = 0;
3212 m_first_visible_row = 0;
3215 m_valobj_list = valobj_list;
3216 const size_t num_values = m_valobj_list.GetSize();
3217 for (size_t i=0; i<num_values; ++i)
3218 m_rows.push_back(Row(m_valobj_list.GetValueObjectAtIndex(i), NULL));
3222 WindowDelegateDraw (Window &window, bool force)
3227 m_max_x = window.GetWidth() - 1;
3228 m_max_y = window.GetHeight() - 1;
3231 window.DrawTitleBox (window.GetName());
3233 const int num_visible_rows = NumVisibleRows();
3234 const int num_rows = CalculateTotalNumberRows (m_rows);
3236 // If we unexpanded while having something selected our
3237 // total number of rows is less than the num visible rows,
3238 // then make sure we show all the rows by setting the first
3239 // visible row accordingly.
3240 if (m_first_visible_row > 0 && num_rows < num_visible_rows)
3241 m_first_visible_row = 0;
3243 // Make sure the selected row is always visible
3244 if (m_selected_row_idx < m_first_visible_row)
3245 m_first_visible_row = m_selected_row_idx;
3246 else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
3247 m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
3249 DisplayRows (window, m_rows, g_options);
3251 window.DeferredRefresh();
3253 // Get the selected row
3254 m_selected_row = GetRowForRowIndex (m_selected_row_idx);
3255 // Keep the cursor on the selected row so the highlight and the cursor
3256 // are always on the same line
3258 window.MoveCursor (m_selected_row->x,
3261 return true; // Drawing handled
3265 WindowDelegateGetKeyHelp ()
3267 static curses::KeyHelp g_source_view_key_help[] = {
3268 { KEY_UP, "Select previous item" },
3269 { KEY_DOWN, "Select next item" },
3270 { KEY_RIGHT, "Expand selected item" },
3271 { KEY_LEFT, "Unexpand selected item or select parent if not expanded" },
3272 { KEY_PPAGE, "Page up" },
3273 { KEY_NPAGE, "Page down" },
3274 { 'A', "Format as annotated address" },
3275 { 'b', "Format as binary" },
3276 { 'B', "Format as hex bytes with ASCII" },
3277 { 'c', "Format as character" },
3278 { 'd', "Format as a signed integer" },
3279 { 'D', "Format selected value using the default format for the type" },
3280 { 'f', "Format as float" },
3281 { 'h', "Show help dialog" },
3282 { 'i', "Format as instructions" },
3283 { 'o', "Format as octal" },
3284 { 'p', "Format as pointer" },
3285 { 's', "Format as C string" },
3286 { 't', "Toggle showing/hiding type names" },
3287 { 'u', "Format as an unsigned integer" },
3288 { 'x', "Format as hex" },
3289 { 'X', "Format as uppercase hex" },
3290 { ' ', "Toggle item expansion" },
3292 { '.', "Page down" },
3295 return g_source_view_key_help;
3299 virtual HandleCharResult
3300 WindowDelegateHandleChar (Window &window, int c)
3318 // Change the format for the currently selected item
3320 m_selected_row->valobj->SetFormat (FormatForChar (c));
3324 // Toggle showing type names
3325 g_options.show_types = !g_options.show_types;
3331 if (m_first_visible_row > 0)
3333 if (m_first_visible_row > m_max_y)
3334 m_first_visible_row -= m_max_y;
3336 m_first_visible_row = 0;
3337 m_selected_row_idx = m_first_visible_row;
3344 if (m_num_rows > m_max_y)
3346 if (m_first_visible_row + m_max_y < m_num_rows)
3348 m_first_visible_row += m_max_y;
3349 m_selected_row_idx = m_first_visible_row;
3355 if (m_selected_row_idx > 0)
3356 --m_selected_row_idx;
3359 if (m_selected_row_idx + 1 < m_num_rows)
3360 ++m_selected_row_idx;
3366 if (!m_selected_row->expanded)
3367 m_selected_row->Expand();
3374 if (m_selected_row->expanded)
3375 m_selected_row->Unexpand();
3376 else if (m_selected_row->parent)
3377 m_selected_row_idx = m_selected_row->parent->row_idx;
3382 // Toggle expansion state when SPACE is pressed
3385 if (m_selected_row->expanded)
3386 m_selected_row->Unexpand();
3388 m_selected_row->Expand();
3393 window.CreateHelpSubwindow ();
3399 return eKeyNotHandled;
3403 ValueObjectList m_valobj_list;
3404 std::vector<Row> m_rows;
3405 Row *m_selected_row;
3406 uint32_t m_selected_row_idx;
3407 uint32_t m_first_visible_row;
3408 uint32_t m_num_rows;
3415 FormatForChar (int c)
3419 case 'x': return eFormatHex;
3420 case 'X': return eFormatHexUppercase;
3421 case 'o': return eFormatOctal;
3422 case 's': return eFormatCString;
3423 case 'u': return eFormatUnsigned;
3424 case 'd': return eFormatDecimal;
3425 case 'D': return eFormatDefault;
3426 case 'i': return eFormatInstruction;
3427 case 'A': return eFormatAddressInfo;
3428 case 'p': return eFormatPointer;
3429 case 'c': return eFormatChar;
3430 case 'b': return eFormatBinary;
3431 case 'B': return eFormatBytesWithASCII;
3432 case 'f': return eFormatFloat;
3434 return eFormatDefault;
3438 DisplayRowObject (Window &window,
3440 DisplayOptions &options,
3444 ValueObject *valobj = row.valobj.get();
3449 const char *type_name = options.show_types ? valobj->GetTypeName().GetCString() : NULL;
3450 const char *name = valobj->GetName().GetCString();
3451 const char *value = valobj->GetValueAsCString ();
3452 const char *summary = valobj->GetSummaryAsCString ();
3454 window.MoveCursor (row.x, row.y);
3456 row.DrawTree (window);
3459 window.AttributeOn(A_REVERSE);
3461 if (type_name && type_name[0])
3462 window.Printf ("(%s) ", type_name);
3464 if (name && name[0])
3465 window.PutCString(name);
3467 attr_t changd_attr = 0;
3468 if (valobj->GetValueDidChange())
3469 changd_attr = COLOR_PAIR(5) | A_BOLD;
3471 if (value && value[0])
3473 window.PutCString(" = ");
3475 window.AttributeOn(changd_attr);
3476 window.PutCString (value);
3478 window.AttributeOff(changd_attr);
3481 if (summary && summary[0])
3483 window.PutChar(' ');
3485 window.AttributeOn(changd_attr);
3486 window.PutCString(summary);
3488 window.AttributeOff(changd_attr);
3492 window.AttributeOff (A_REVERSE);
3497 DisplayRows (Window &window,
3498 std::vector<Row> &rows,
3499 DisplayOptions &options)
3504 bool window_is_active = window.IsActive();
3505 for (auto &row : rows)
3507 const bool last_child = row.parent && &rows[rows.size()-1] == &row;
3508 // Save the row index in each Row structure
3509 row.row_idx = m_num_rows;
3510 if ((m_num_rows >= m_first_visible_row) &&
3511 ((m_num_rows - m_first_visible_row) < NumVisibleRows()))
3514 row.y = m_num_rows - m_first_visible_row + 1;
3515 if (DisplayRowObject (window,
3518 window_is_active && m_num_rows == m_selected_row_idx,
3536 if (row.expanded && !row.children.empty())
3538 DisplayRows (window,
3546 CalculateTotalNumberRows (const std::vector<Row> &rows)
3549 for (const auto &row : rows)
3553 row_count += CalculateTotalNumberRows(row.children);
3558 GetRowForRowIndexImpl (std::vector<Row> &rows, size_t &row_index)
3560 for (auto &row : rows)
3567 if (row.expanded && !row.children.empty())
3569 Row *result = GetRowForRowIndexImpl (row.children, row_index);
3579 GetRowForRowIndex (size_t row_index)
3581 return GetRowForRowIndexImpl (m_rows, row_index);
3585 NumVisibleRows () const
3587 return m_max_y - m_min_y;
3590 static DisplayOptions g_options;
3593 class FrameVariablesWindowDelegate : public ValueObjectListDelegate
3596 FrameVariablesWindowDelegate (Debugger &debugger) :
3597 ValueObjectListDelegate (),
3598 m_debugger (debugger),
3599 m_frame_block (NULL)
3604 ~FrameVariablesWindowDelegate()
3608 virtual const char *
3609 WindowDelegateGetHelpText ()
3611 return "Frame variable window keyboard shortcuts:";
3615 WindowDelegateDraw (Window &window, bool force)
3617 ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext());
3618 Process *process = exe_ctx.GetProcessPtr();
3619 Block *frame_block = NULL;
3620 StackFrame *frame = NULL;
3624 StateType state = process->GetState();
3625 if (StateIsStoppedState(state, true))
3627 frame = exe_ctx.GetFramePtr();
3629 frame_block = frame->GetFrameBlock ();
3631 else if (StateIsRunningState(state))
3633 return true; // Don't do any updating when we are running
3637 ValueObjectList local_values;
3640 // Only update the variables if they have changed
3641 if (m_frame_block != frame_block)
3643 m_frame_block = frame_block;
3645 VariableList *locals = frame->GetVariableList(true);
3648 const DynamicValueType use_dynamic = eDynamicDontRunTarget;
3649 const size_t num_locals = locals->GetSize();
3650 for (size_t i=0; i<num_locals; ++i)
3651 local_values.Append(frame->GetValueObjectForFrameVariable (locals->GetVariableAtIndex(i), use_dynamic));
3652 // Update the values
3653 SetValues(local_values);
3659 m_frame_block = NULL;
3660 // Update the values with an empty list if there is no frame
3661 SetValues(local_values);
3664 return ValueObjectListDelegate::WindowDelegateDraw (window, force);
3669 Debugger &m_debugger;
3670 Block *m_frame_block;
3674 class RegistersWindowDelegate : public ValueObjectListDelegate
3677 RegistersWindowDelegate (Debugger &debugger) :
3678 ValueObjectListDelegate (),
3679 m_debugger (debugger)
3684 ~RegistersWindowDelegate()
3688 virtual const char *
3689 WindowDelegateGetHelpText ()
3691 return "Register window keyboard shortcuts:";
3695 WindowDelegateDraw (Window &window, bool force)
3697 ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext());
3698 StackFrame *frame = exe_ctx.GetFramePtr();
3700 ValueObjectList value_list;
3703 if (frame->GetStackID() != m_stack_id)
3705 m_stack_id = frame->GetStackID();
3706 RegisterContextSP reg_ctx (frame->GetRegisterContext());
3709 const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
3710 for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx)
3712 value_list.Append(ValueObjectRegisterSet::Create (frame, reg_ctx, set_idx));
3715 SetValues(value_list);
3720 Process *process = exe_ctx.GetProcessPtr();
3721 if (process && process->IsAlive())
3722 return true; // Don't do any updating if we are running
3725 // Update the values with an empty list if there
3726 // is no process or the process isn't alive anymore
3727 SetValues(value_list);
3730 return ValueObjectListDelegate::WindowDelegateDraw (window, force);
3734 Debugger &m_debugger;
3739 CursesKeyToCString (int ch)
3741 static char g_desc[32];
3742 if (ch >= KEY_F0 && ch < KEY_F0 + 64)
3744 snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0);
3749 case KEY_DOWN: return "down";
3750 case KEY_UP: return "up";
3751 case KEY_LEFT: return "left";
3752 case KEY_RIGHT: return "right";
3753 case KEY_HOME: return "home";
3754 case KEY_BACKSPACE: return "backspace";
3755 case KEY_DL: return "delete-line";
3756 case KEY_IL: return "insert-line";
3757 case KEY_DC: return "delete-char";
3758 case KEY_IC: return "insert-char";
3759 case KEY_CLEAR: return "clear";
3760 case KEY_EOS: return "clear-to-eos";
3761 case KEY_EOL: return "clear-to-eol";
3762 case KEY_SF: return "scroll-forward";
3763 case KEY_SR: return "scroll-backward";
3764 case KEY_NPAGE: return "page-down";
3765 case KEY_PPAGE: return "page-up";
3766 case KEY_STAB: return "set-tab";
3767 case KEY_CTAB: return "clear-tab";
3768 case KEY_CATAB: return "clear-all-tabs";
3769 case KEY_ENTER: return "enter";
3770 case KEY_PRINT: return "print";
3771 case KEY_LL: return "lower-left key";
3772 case KEY_A1: return "upper left of keypad";
3773 case KEY_A3: return "upper right of keypad";
3774 case KEY_B2: return "center of keypad";
3775 case KEY_C1: return "lower left of keypad";
3776 case KEY_C3: return "lower right of keypad";
3777 case KEY_BTAB: return "back-tab key";
3778 case KEY_BEG: return "begin key";
3779 case KEY_CANCEL: return "cancel key";
3780 case KEY_CLOSE: return "close key";
3781 case KEY_COMMAND: return "command key";
3782 case KEY_COPY: return "copy key";
3783 case KEY_CREATE: return "create key";
3784 case KEY_END: return "end key";
3785 case KEY_EXIT: return "exit key";
3786 case KEY_FIND: return "find key";
3787 case KEY_HELP: return "help key";
3788 case KEY_MARK: return "mark key";
3789 case KEY_MESSAGE: return "message key";
3790 case KEY_MOVE: return "move key";
3791 case KEY_NEXT: return "next key";
3792 case KEY_OPEN: return "open key";
3793 case KEY_OPTIONS: return "options key";
3794 case KEY_PREVIOUS: return "previous key";
3795 case KEY_REDO: return "redo key";
3796 case KEY_REFERENCE: return "reference key";
3797 case KEY_REFRESH: return "refresh key";
3798 case KEY_REPLACE: return "replace key";
3799 case KEY_RESTART: return "restart key";
3800 case KEY_RESUME: return "resume key";
3801 case KEY_SAVE: return "save key";
3802 case KEY_SBEG: return "shifted begin key";
3803 case KEY_SCANCEL: return "shifted cancel key";
3804 case KEY_SCOMMAND: return "shifted command key";
3805 case KEY_SCOPY: return "shifted copy key";
3806 case KEY_SCREATE: return "shifted create key";
3807 case KEY_SDC: return "shifted delete-character key";
3808 case KEY_SDL: return "shifted delete-line key";
3809 case KEY_SELECT: return "select key";
3810 case KEY_SEND: return "shifted end key";
3811 case KEY_SEOL: return "shifted clear-to-end-of-line key";
3812 case KEY_SEXIT: return "shifted exit key";
3813 case KEY_SFIND: return "shifted find key";
3814 case KEY_SHELP: return "shifted help key";
3815 case KEY_SHOME: return "shifted home key";
3816 case KEY_SIC: return "shifted insert-character key";
3817 case KEY_SLEFT: return "shifted left-arrow key";
3818 case KEY_SMESSAGE: return "shifted message key";
3819 case KEY_SMOVE: return "shifted move key";
3820 case KEY_SNEXT: return "shifted next key";
3821 case KEY_SOPTIONS: return "shifted options key";
3822 case KEY_SPREVIOUS: return "shifted previous key";
3823 case KEY_SPRINT: return "shifted print key";
3824 case KEY_SREDO: return "shifted redo key";
3825 case KEY_SREPLACE: return "shifted replace key";
3826 case KEY_SRIGHT: return "shifted right-arrow key";
3827 case KEY_SRSUME: return "shifted resume key";
3828 case KEY_SSAVE: return "shifted save key";
3829 case KEY_SSUSPEND: return "shifted suspend key";
3830 case KEY_SUNDO: return "shifted undo key";
3831 case KEY_SUSPEND: return "suspend key";
3832 case KEY_UNDO: return "undo key";
3833 case KEY_MOUSE: return "Mouse event has occurred";
3834 case KEY_RESIZE: return "Terminal resize event";
3835 case KEY_EVENT: return "We were interrupted by an event";
3836 case KEY_RETURN: return "return";
3837 case ' ': return "space";
3838 case '\t': return "tab";
3839 case KEY_ESCAPE: return "escape";
3842 snprintf(g_desc, sizeof(g_desc), "%c", ch);
3844 snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch);
3850 HelpDialogDelegate::HelpDialogDelegate (const char *text, KeyHelp *key_help_array) :
3852 m_first_visible_line (0)
3854 if (text && text[0])
3856 m_text.SplitIntoLines(text);
3857 m_text.AppendString("");
3861 for (KeyHelp *key = key_help_array; key->ch; ++key)
3863 StreamString key_description;
3864 key_description.Printf("%10s - %s", CursesKeyToCString(key->ch), key->description);
3865 m_text.AppendString(std::move(key_description.GetString()));
3870 HelpDialogDelegate::~HelpDialogDelegate()
3875 HelpDialogDelegate::WindowDelegateDraw (Window &window, bool force)
3878 const int window_height = window.GetHeight();
3881 const int min_y = y;
3882 const int max_y = window_height - 1 - y;
3883 const int num_visible_lines = max_y - min_y + 1;
3884 const size_t num_lines = m_text.GetSize();
3885 const char *bottom_message;
3886 if (num_lines <= num_visible_lines)
3887 bottom_message = "Press any key to exit";
3889 bottom_message = "Use arrows to scroll, any other key to exit";
3890 window.DrawTitleBox(window.GetName(), bottom_message);
3893 window.MoveCursor(x, y);
3894 window.PutCStringTruncated(m_text.GetStringAtIndex(m_first_visible_line + y - min_y), 1);
3901 HelpDialogDelegate::WindowDelegateHandleChar (Window &window, int key)
3904 const size_t num_lines = m_text.GetSize();
3905 const size_t num_visible_lines = window.GetHeight() - 2;
3907 if (num_lines <= num_visible_lines)
3910 // If we have all lines visible and don't need scrolling, then any
3911 // key press will cause us to exit
3918 if (m_first_visible_line > 0)
3919 --m_first_visible_line;
3923 if (m_first_visible_line + num_visible_lines < num_lines)
3924 ++m_first_visible_line;
3929 if (m_first_visible_line > 0)
3931 if (m_first_visible_line >= num_visible_lines)
3932 m_first_visible_line -= num_visible_lines;
3934 m_first_visible_line = 0;
3939 if (m_first_visible_line + num_visible_lines < num_lines)
3941 m_first_visible_line += num_visible_lines;
3942 if (m_first_visible_line > num_lines)
3943 m_first_visible_line = num_lines - num_visible_lines;
3952 window.GetParent()->RemoveSubWindow(&window);
3956 class ApplicationDelegate :
3957 public WindowDelegate,
3967 eMenuID_TargetCreate,
3968 eMenuID_TargetDelete,
3971 eMenuID_ProcessAttach,
3972 eMenuID_ProcessDetach,
3973 eMenuID_ProcessLaunch,
3974 eMenuID_ProcessContinue,
3975 eMenuID_ProcessHalt,
3976 eMenuID_ProcessKill,
3979 eMenuID_ThreadStepIn,
3980 eMenuID_ThreadStepOver,
3981 eMenuID_ThreadStepOut,
3984 eMenuID_ViewBacktrace,
3985 eMenuID_ViewRegisters,
3987 eMenuID_ViewVariables,
3993 ApplicationDelegate (Application &app, Debugger &debugger) :
3997 m_debugger (debugger)
4002 ~ApplicationDelegate ()
4006 WindowDelegateDraw (Window &window, bool force)
4008 return false; // Drawing not handled, let standard window drawing happen
4011 virtual HandleCharResult
4012 WindowDelegateHandleChar (Window &window, int key)
4017 window.SelectNextWindowAsActive();
4021 window.CreateHelpSubwindow();
4025 return eQuitApplication;
4030 return eKeyNotHandled;
4034 virtual const char *
4035 WindowDelegateGetHelpText ()
4037 return "Welcome to the LLDB curses GUI.\n\n"
4038 "Press the TAB key to change the selected view.\n"
4039 "Each view has its own keyboard shortcuts, press 'h' to open a dialog to display them.\n\n"
4040 "Common key bindings for all views:";
4044 WindowDelegateGetKeyHelp ()
4046 static curses::KeyHelp g_source_view_key_help[] = {
4047 { '\t', "Select next view" },
4048 { 'h', "Show help dialog with view specific key bindings" },
4050 { '.', "Page down" },
4051 { KEY_UP, "Select previous" },
4052 { KEY_DOWN, "Select next" },
4053 { KEY_LEFT, "Unexpand or select parent" },
4054 { KEY_RIGHT, "Expand" },
4055 { KEY_PPAGE, "Page up" },
4056 { KEY_NPAGE, "Page down" },
4059 return g_source_view_key_help;
4062 virtual MenuActionResult
4063 MenuDelegateAction (Menu &menu)
4065 switch (menu.GetIdentifier())
4067 case eMenuID_ThreadStepIn:
4069 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4070 if (exe_ctx.HasThreadScope())
4072 Process *process = exe_ctx.GetProcessPtr();
4073 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4074 exe_ctx.GetThreadRef().StepIn(true, true);
4077 return MenuActionResult::Handled;
4079 case eMenuID_ThreadStepOut:
4081 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4082 if (exe_ctx.HasThreadScope())
4084 Process *process = exe_ctx.GetProcessPtr();
4085 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4086 exe_ctx.GetThreadRef().StepOut();
4089 return MenuActionResult::Handled;
4091 case eMenuID_ThreadStepOver:
4093 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4094 if (exe_ctx.HasThreadScope())
4096 Process *process = exe_ctx.GetProcessPtr();
4097 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4098 exe_ctx.GetThreadRef().StepOver(true);
4101 return MenuActionResult::Handled;
4103 case eMenuID_ProcessContinue:
4105 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4106 if (exe_ctx.HasProcessScope())
4108 Process *process = exe_ctx.GetProcessPtr();
4109 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4113 return MenuActionResult::Handled;
4115 case eMenuID_ProcessKill:
4117 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4118 if (exe_ctx.HasProcessScope())
4120 Process *process = exe_ctx.GetProcessPtr();
4121 if (process && process->IsAlive())
4125 return MenuActionResult::Handled;
4127 case eMenuID_ProcessHalt:
4129 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4130 if (exe_ctx.HasProcessScope())
4132 Process *process = exe_ctx.GetProcessPtr();
4133 if (process && process->IsAlive())
4137 return MenuActionResult::Handled;
4139 case eMenuID_ProcessDetach:
4141 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4142 if (exe_ctx.HasProcessScope())
4144 Process *process = exe_ctx.GetProcessPtr();
4145 if (process && process->IsAlive())
4146 process->Detach(false);
4149 return MenuActionResult::Handled;
4151 case eMenuID_Process:
4153 // Populate the menu with all of the threads if the process is stopped when
4154 // the Process menu gets selected and is about to display its submenu.
4155 Menus &submenus = menu.GetSubmenus();
4156 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4157 Process *process = exe_ctx.GetProcessPtr();
4158 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4160 if (submenus.size() == 7)
4161 menu.AddSubmenu (MenuSP (new Menu(Menu::Type::Separator)));
4162 else if (submenus.size() > 8)
4163 submenus.erase (submenus.begin() + 8, submenus.end());
4165 ThreadList &threads = process->GetThreadList();
4166 Mutex::Locker locker (threads.GetMutex());
4167 size_t num_threads = threads.GetSize();
4168 for (size_t i=0; i<num_threads; ++i)
4170 ThreadSP thread_sp = threads.GetThreadAtIndex(i);
4171 char menu_char = '\0';
4173 menu_char = '1' + i;
4174 StreamString thread_menu_title;
4175 thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID());
4176 const char *thread_name = thread_sp->GetName();
4177 if (thread_name && thread_name[0])
4178 thread_menu_title.Printf (" %s", thread_name);
4181 const char *queue_name = thread_sp->GetQueueName();
4182 if (queue_name && queue_name[0])
4183 thread_menu_title.Printf (" %s", queue_name);
4185 menu.AddSubmenu (MenuSP (new Menu(thread_menu_title.GetString().c_str(), NULL, menu_char, thread_sp->GetID())));
4188 else if (submenus.size() > 7)
4190 // Remove the separator and any other thread submenu items
4191 // that were previously added
4192 submenus.erase (submenus.begin() + 7, submenus.end());
4194 // Since we are adding and removing items we need to recalculate the name lengths
4195 menu.RecalculateNameLengths();
4197 return MenuActionResult::Handled;
4199 case eMenuID_ViewVariables:
4201 WindowSP main_window_sp = m_app.GetMainWindow();
4202 WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
4203 WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
4204 WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
4205 const Rect source_bounds = source_window_sp->GetBounds();
4207 if (variables_window_sp)
4209 const Rect variables_bounds = variables_window_sp->GetBounds();
4211 main_window_sp->RemoveSubWindow(variables_window_sp.get());
4213 if (registers_window_sp)
4215 // We have a registers window, so give all the area back to the registers window
4216 Rect registers_bounds = variables_bounds;
4217 registers_bounds.size.width = source_bounds.size.width;
4218 registers_window_sp->SetBounds(registers_bounds);
4222 // We have no registers window showing so give the bottom
4223 // area back to the source view
4224 source_window_sp->Resize (source_bounds.size.width,
4225 source_bounds.size.height + variables_bounds.size.height);
4230 Rect new_variables_rect;
4231 if (registers_window_sp)
4233 // We have a registers window so split the area of the registers
4234 // window into two columns where the left hand side will be the
4235 // variables and the right hand side will be the registers
4236 const Rect variables_bounds = registers_window_sp->GetBounds();
4237 Rect new_registers_rect;
4238 variables_bounds.VerticalSplitPercentage (0.50, new_variables_rect, new_registers_rect);
4239 registers_window_sp->SetBounds (new_registers_rect);
4243 // No variables window, grab the bottom part of the source window
4244 Rect new_source_rect;
4245 source_bounds.HorizontalSplitPercentage (0.70, new_source_rect, new_variables_rect);
4246 source_window_sp->SetBounds (new_source_rect);
4248 WindowSP new_window_sp = main_window_sp->CreateSubWindow ("Variables",
4251 new_window_sp->SetDelegate (WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
4255 return MenuActionResult::Handled;
4257 case eMenuID_ViewRegisters:
4259 WindowSP main_window_sp = m_app.GetMainWindow();
4260 WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
4261 WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
4262 WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
4263 const Rect source_bounds = source_window_sp->GetBounds();
4265 if (registers_window_sp)
4267 if (variables_window_sp)
4269 const Rect variables_bounds = variables_window_sp->GetBounds();
4271 // We have a variables window, so give all the area back to the variables window
4272 variables_window_sp->Resize (variables_bounds.size.width + registers_window_sp->GetWidth(),
4273 variables_bounds.size.height);
4277 // We have no variables window showing so give the bottom
4278 // area back to the source view
4279 source_window_sp->Resize (source_bounds.size.width,
4280 source_bounds.size.height + registers_window_sp->GetHeight());
4282 main_window_sp->RemoveSubWindow(registers_window_sp.get());
4287 if (variables_window_sp)
4289 // We have a variables window, split it into two columns
4290 // where the left hand side will be the variables and the
4291 // right hand side will be the registers
4292 const Rect variables_bounds = variables_window_sp->GetBounds();
4294 variables_bounds.VerticalSplitPercentage (0.50, new_vars_rect, new_regs_rect);
4295 variables_window_sp->SetBounds (new_vars_rect);
4299 // No registers window, grab the bottom part of the source window
4300 Rect new_source_rect;
4301 source_bounds.HorizontalSplitPercentage (0.70, new_source_rect, new_regs_rect);
4302 source_window_sp->SetBounds (new_source_rect);
4304 WindowSP new_window_sp = main_window_sp->CreateSubWindow ("Registers",
4307 new_window_sp->SetDelegate (WindowDelegateSP(new RegistersWindowDelegate(m_debugger)));
4311 return MenuActionResult::Handled;
4313 case eMenuID_HelpGUIHelp:
4314 m_app.GetMainWindow ()->CreateHelpSubwindow();
4315 return MenuActionResult::Handled;
4321 return MenuActionResult::NotHandled;
4325 Debugger &m_debugger;
4329 class StatusBarWindowDelegate : public WindowDelegate
4332 StatusBarWindowDelegate (Debugger &debugger) :
4333 m_debugger (debugger)
4338 ~StatusBarWindowDelegate ()
4342 WindowDelegateDraw (Window &window, bool force)
4344 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4345 Process *process = exe_ctx.GetProcessPtr();
4346 Thread *thread = exe_ctx.GetThreadPtr();
4347 StackFrame *frame = exe_ctx.GetFramePtr();
4349 window.SetBackground(2);
4350 window.MoveCursor (0, 0);
4353 const StateType state = process->GetState();
4354 window.Printf ("Process: %5" PRIu64 " %10s", process->GetID(), StateAsCString(state));
4356 if (StateIsStoppedState(state, true))
4358 window.MoveCursor (40, 0);
4360 window.Printf ("Thread: 0x%4.4" PRIx64, thread->GetID());
4362 window.MoveCursor (60, 0);
4364 window.Printf ("Frame: %3u PC = 0x%16.16" PRIx64, frame->GetFrameIndex(), frame->GetFrameCodeAddress().GetOpcodeLoadAddress (exe_ctx.GetTargetPtr()));
4366 else if (state == eStateExited)
4368 const char *exit_desc = process->GetExitDescription();
4369 const int exit_status = process->GetExitStatus();
4370 if (exit_desc && exit_desc[0])
4371 window.Printf (" with status = %i (%s)", exit_status, exit_desc);
4373 window.Printf (" with status = %i", exit_status);
4376 window.DeferredRefresh();
4381 Debugger &m_debugger;
4384 class SourceFileWindowDelegate : public WindowDelegate
4387 SourceFileWindowDelegate (Debugger &debugger) :
4389 m_debugger (debugger),
4392 m_disassembly_scope (NULL),
4393 m_disassembly_sp (),
4394 m_disassembly_range (),
4396 m_selected_line (0),
4399 m_frame_idx (UINT32_MAX),
4400 m_first_visible_line (0),
4410 ~SourceFileWindowDelegate()
4415 Update (const SymbolContext &sc)
4421 NumVisibleLines () const
4423 return m_max_y - m_min_y;
4426 virtual const char *
4427 WindowDelegateGetHelpText ()
4429 return "Source/Disassembly window keyboard shortcuts:";
4433 WindowDelegateGetKeyHelp ()
4435 static curses::KeyHelp g_source_view_key_help[] = {
4436 { KEY_RETURN, "Run to selected line with one shot breakpoint" },
4437 { KEY_UP, "Select previous source line" },
4438 { KEY_DOWN, "Select next source line" },
4439 { KEY_PPAGE, "Page up" },
4440 { KEY_NPAGE, "Page down" },
4441 { 'b', "Set breakpoint on selected source/disassembly line" },
4442 { 'c', "Continue process" },
4443 { 'd', "Detach and resume process" },
4444 { 'D', "Detach with process suspended" },
4445 { 'h', "Show help dialog" },
4446 { 'k', "Kill process" },
4447 { 'n', "Step over (source line)" },
4448 { 'N', "Step over (single instruction)" },
4449 { 'o', "Step out" },
4450 { 's', "Step in (source line)" },
4451 { 'S', "Step in (single instruction)" },
4453 { '.', "Page down" },
4456 return g_source_view_key_help;
4460 WindowDelegateDraw (Window &window, bool force)
4462 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4463 Process *process = exe_ctx.GetProcessPtr();
4464 Thread *thread = NULL;
4466 bool update_location = false;
4469 StateType state = process->GetState();
4470 if (StateIsStoppedState(state, true))
4472 // We are stopped, so it is ok to
4473 update_location = true;
4479 m_max_x = window.GetMaxX()-1;
4480 m_max_y = window.GetMaxY()-1;
4482 const uint32_t num_visible_lines = NumVisibleLines();
4483 StackFrameSP frame_sp;
4484 bool set_selected_line_to_pc = false;
4487 if (update_location)
4490 const bool process_alive = process ? process->IsAlive() : false;
4491 bool thread_changed = false;
4494 thread = exe_ctx.GetThreadPtr();
4497 frame_sp = thread->GetSelectedFrame();
4498 auto tid = thread->GetID();
4499 thread_changed = tid != m_tid;
4504 if (m_tid != LLDB_INVALID_THREAD_ID)
4506 thread_changed = true;
4507 m_tid = LLDB_INVALID_THREAD_ID;
4511 const uint32_t stop_id = process ? process->GetStopID() : 0;
4512 const bool stop_id_changed = stop_id != m_stop_id;
4513 bool frame_changed = false;
4514 m_stop_id = stop_id;
4517 m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
4518 const uint32_t frame_idx = frame_sp->GetFrameIndex();
4519 frame_changed = frame_idx != m_frame_idx;
4520 m_frame_idx = frame_idx;
4525 frame_changed = m_frame_idx != UINT32_MAX;
4526 m_frame_idx = UINT32_MAX;
4529 const bool context_changed = thread_changed || frame_changed || stop_id_changed;
4533 if (m_sc.line_entry.IsValid())
4535 m_pc_line = m_sc.line_entry.line;
4536 if (m_pc_line != UINT32_MAX)
4537 --m_pc_line; // Convert to zero based line number...
4538 // Update the selected line if the stop ID changed...
4539 if (context_changed)
4540 m_selected_line = m_pc_line;
4542 if (m_file_sp && m_file_sp->FileSpecMatches(m_sc.line_entry.file))
4544 // Same file, nothing to do, we should either have the
4545 // lines or not (source file missing)
4546 if (m_selected_line >= m_first_visible_line)
4548 if (m_selected_line >= m_first_visible_line + num_visible_lines)
4549 m_first_visible_line = m_selected_line - 10;
4553 if (m_selected_line > 10)
4554 m_first_visible_line = m_selected_line - 10;
4556 m_first_visible_line = 0;
4561 // File changed, set selected line to the line with the PC
4562 m_selected_line = m_pc_line;
4563 m_file_sp = m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file);
4566 const size_t num_lines = m_file_sp->GetNumLines();
4567 int m_line_width = 1;
4568 for (size_t n = num_lines; n >= 10; n = n / 10)
4571 snprintf (m_line_format, sizeof(m_line_format), " %%%iu ", m_line_width);
4572 if (num_lines < num_visible_lines || m_selected_line < num_visible_lines)
4573 m_first_visible_line = 0;
4575 m_first_visible_line = m_selected_line - 10;
4584 if (!m_file_sp || m_file_sp->GetNumLines() == 0)
4587 bool prefer_file_cache = false;
4590 if (m_disassembly_scope != m_sc.function)
4592 m_disassembly_scope = m_sc.function;
4593 m_disassembly_sp = m_sc.function->GetInstructions (exe_ctx, NULL, prefer_file_cache);
4594 if (m_disassembly_sp)
4596 set_selected_line_to_pc = true;
4597 m_disassembly_range = m_sc.function->GetAddressRange();
4601 m_disassembly_range.Clear();
4606 set_selected_line_to_pc = context_changed;
4609 else if (m_sc.symbol)
4611 if (m_disassembly_scope != m_sc.symbol)
4613 m_disassembly_scope = m_sc.symbol;
4614 m_disassembly_sp = m_sc.symbol->GetInstructions (exe_ctx, NULL, prefer_file_cache);
4615 if (m_disassembly_sp)
4617 set_selected_line_to_pc = true;
4618 m_disassembly_range.GetBaseAddress() = m_sc.symbol->GetAddress();
4619 m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize());
4623 m_disassembly_range.Clear();
4628 set_selected_line_to_pc = context_changed;
4635 m_pc_line = UINT32_MAX;
4641 window.DrawTitleBox ("Sources");
4644 Target *target = exe_ctx.GetTargetPtr();
4645 const size_t num_source_lines = GetNumSourceLines();
4646 if (num_source_lines > 0)
4649 BreakpointLines bp_lines;
4652 BreakpointList &bp_list = target->GetBreakpointList();
4653 const size_t num_bps = bp_list.GetSize();
4654 for (size_t bp_idx=0; bp_idx<num_bps; ++bp_idx)
4656 BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
4657 const size_t num_bps_locs = bp_sp->GetNumLocations();
4658 for (size_t bp_loc_idx=0; bp_loc_idx<num_bps_locs; ++bp_loc_idx)
4660 BreakpointLocationSP bp_loc_sp = bp_sp->GetLocationAtIndex(bp_loc_idx);
4661 LineEntry bp_loc_line_entry;
4662 if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry (bp_loc_line_entry))
4664 if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file)
4666 bp_lines.insert(bp_loc_line_entry.line);
4674 const attr_t selected_highlight_attr = A_REVERSE;
4675 const attr_t pc_highlight_attr = COLOR_PAIR(1);
4677 for (int i=0; i<num_visible_lines; ++i)
4679 const uint32_t curr_line = m_first_visible_line + i;
4680 if (curr_line < num_source_lines)
4682 const int line_y = 1+i;
4683 window.MoveCursor(1, line_y);
4684 const bool is_pc_line = curr_line == m_pc_line;
4685 const bool line_is_selected = m_selected_line == curr_line;
4686 // Highlight the line as the PC line first, then if the selected line
4687 // isn't the same as the PC line, highlight it differently
4688 attr_t highlight_attr = 0;
4691 highlight_attr = pc_highlight_attr;
4692 else if (line_is_selected)
4693 highlight_attr = selected_highlight_attr;
4695 if (bp_lines.find(curr_line+1) != bp_lines.end())
4696 bp_attr = COLOR_PAIR(2);
4699 window.AttributeOn(bp_attr);
4701 window.Printf (m_line_format, curr_line + 1);
4704 window.AttributeOff(bp_attr);
4706 window.PutChar(ACS_VLINE);
4707 // Mark the line with the PC with a diamond
4709 window.PutChar(ACS_DIAMOND);
4711 window.PutChar(' ');
4714 window.AttributeOn(highlight_attr);
4715 const uint32_t line_len = m_file_sp->GetLineLength(curr_line + 1, false);
4717 window.PutCString(m_file_sp->PeekLineData(curr_line + 1), line_len);
4719 if (is_pc_line && frame_sp && frame_sp->GetConcreteFrameIndex() == 0)
4721 StopInfoSP stop_info_sp;
4723 stop_info_sp = thread->GetStopInfo();
4726 const char *stop_description = stop_info_sp->GetDescription();
4727 if (stop_description && stop_description[0])
4729 size_t stop_description_len = strlen(stop_description);
4730 int desc_x = window.GetWidth() - stop_description_len - 16;
4731 window.Printf ("%*s", desc_x - window.GetCursorX(), "");
4732 //window.MoveCursor(window.GetWidth() - stop_description_len - 15, line_y);
4733 window.Printf ("<<< Thread %u: %s ", thread->GetIndexID(), stop_description);
4738 window.Printf ("%*s", window.GetWidth() - window.GetCursorX() - 1, "");
4742 window.AttributeOff(highlight_attr);
4753 size_t num_disassembly_lines = GetNumDisassemblyLines();
4754 if (num_disassembly_lines > 0)
4756 // Display disassembly
4757 BreakpointAddrs bp_file_addrs;
4758 Target *target = exe_ctx.GetTargetPtr();
4761 BreakpointList &bp_list = target->GetBreakpointList();
4762 const size_t num_bps = bp_list.GetSize();
4763 for (size_t bp_idx=0; bp_idx<num_bps; ++bp_idx)
4765 BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
4766 const size_t num_bps_locs = bp_sp->GetNumLocations();
4767 for (size_t bp_loc_idx=0; bp_loc_idx<num_bps_locs; ++bp_loc_idx)
4769 BreakpointLocationSP bp_loc_sp = bp_sp->GetLocationAtIndex(bp_loc_idx);
4770 LineEntry bp_loc_line_entry;
4771 const lldb::addr_t file_addr = bp_loc_sp->GetAddress().GetFileAddress();
4772 if (file_addr != LLDB_INVALID_ADDRESS)
4774 if (m_disassembly_range.ContainsFileAddress(file_addr))
4775 bp_file_addrs.insert(file_addr);
4782 const attr_t selected_highlight_attr = A_REVERSE;
4783 const attr_t pc_highlight_attr = COLOR_PAIR(1);
4787 InstructionList &insts = m_disassembly_sp->GetInstructionList();
4791 pc_address = frame_sp->GetFrameCodeAddress();
4792 const uint32_t pc_idx = pc_address.IsValid() ? insts.GetIndexOfInstructionAtAddress (pc_address) : UINT32_MAX;
4793 if (set_selected_line_to_pc)
4795 m_selected_line = pc_idx;
4798 const uint32_t non_visible_pc_offset = (num_visible_lines / 5);
4799 if (m_first_visible_line >= num_disassembly_lines)
4800 m_first_visible_line = 0;
4802 if (pc_idx < num_disassembly_lines)
4804 if (pc_idx < m_first_visible_line ||
4805 pc_idx >= m_first_visible_line + num_visible_lines)
4806 m_first_visible_line = pc_idx - non_visible_pc_offset;
4809 for (size_t i=0; i<num_visible_lines; ++i)
4811 const uint32_t inst_idx = m_first_visible_line + i;
4812 Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get();
4816 window.MoveCursor(1, i+1);
4817 const bool is_pc_line = frame_sp && inst_idx == pc_idx;
4818 const bool line_is_selected = m_selected_line == inst_idx;
4819 // Highlight the line as the PC line first, then if the selected line
4820 // isn't the same as the PC line, highlight it differently
4821 attr_t highlight_attr = 0;
4824 highlight_attr = pc_highlight_attr;
4825 else if (line_is_selected)
4826 highlight_attr = selected_highlight_attr;
4828 if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) != bp_file_addrs.end())
4829 bp_attr = COLOR_PAIR(2);
4832 window.AttributeOn(bp_attr);
4834 window.Printf (" 0x%16.16llx ", inst->GetAddress().GetLoadAddress(target));
4837 window.AttributeOff(bp_attr);
4839 window.PutChar(ACS_VLINE);
4840 // Mark the line with the PC with a diamond
4842 window.PutChar(ACS_DIAMOND);
4844 window.PutChar(' ');
4847 window.AttributeOn(highlight_attr);
4849 const char *mnemonic = inst->GetMnemonic(&exe_ctx);
4850 const char *operands = inst->GetOperands(&exe_ctx);
4851 const char *comment = inst->GetComment(&exe_ctx);
4853 if (mnemonic && mnemonic[0] == '\0')
4855 if (operands && operands[0] == '\0')
4857 if (comment && comment[0] == '\0')
4862 if (mnemonic && operands && comment)
4863 strm.Printf ("%-8s %-25s ; %s", mnemonic, operands, comment);
4864 else if (mnemonic && operands)
4865 strm.Printf ("%-8s %s", mnemonic, operands);
4867 strm.Printf ("%s", mnemonic);
4870 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
4872 if (is_pc_line && frame_sp && frame_sp->GetConcreteFrameIndex() == 0)
4874 StopInfoSP stop_info_sp;
4876 stop_info_sp = thread->GetStopInfo();
4879 const char *stop_description = stop_info_sp->GetDescription();
4880 if (stop_description && stop_description[0])
4882 size_t stop_description_len = strlen(stop_description);
4883 int desc_x = window.GetWidth() - stop_description_len - 16;
4884 window.Printf ("%*s", desc_x - window.GetCursorX(), "");
4885 //window.MoveCursor(window.GetWidth() - stop_description_len - 15, line_y);
4886 window.Printf ("<<< Thread %u: %s ", thread->GetIndexID(), stop_description);
4891 window.Printf ("%*s", window.GetWidth() - window.GetCursorX() - 1, "");
4895 window.AttributeOff(highlight_attr);
4899 window.DeferredRefresh();
4900 return true; // Drawing handled
4906 size_t num_lines = GetNumSourceLines();
4908 num_lines = GetNumDisassemblyLines();
4913 GetNumSourceLines () const
4916 return m_file_sp->GetNumLines();
4920 GetNumDisassemblyLines () const
4922 if (m_disassembly_sp)
4923 return m_disassembly_sp->GetInstructionList().GetSize();
4927 virtual HandleCharResult
4928 WindowDelegateHandleChar (Window &window, int c)
4930 const uint32_t num_visible_lines = NumVisibleLines();
4931 const size_t num_lines = GetNumLines ();
4938 if (m_first_visible_line > num_visible_lines)
4939 m_first_visible_line -= num_visible_lines;
4941 m_first_visible_line = 0;
4942 m_selected_line = m_first_visible_line;
4949 if (m_first_visible_line + num_visible_lines < num_lines)
4950 m_first_visible_line += num_visible_lines;
4951 else if (num_lines < num_visible_lines)
4952 m_first_visible_line = 0;
4954 m_first_visible_line = num_lines - num_visible_lines;
4955 m_selected_line = m_first_visible_line;
4960 if (m_selected_line > 0)
4963 if (m_first_visible_line > m_selected_line)
4964 m_first_visible_line = m_selected_line;
4969 if (m_selected_line + 1 < num_lines)
4972 if (m_first_visible_line + num_visible_lines < m_selected_line)
4973 m_first_visible_line++;
4980 // Set a breakpoint and run to the line using a one shot breakpoint
4981 if (GetNumSourceLines() > 0)
4983 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4984 if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive())
4986 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (NULL, // Don't limit the breakpoint to certain modules
4987 m_file_sp->GetFileSpec(), // Source file
4988 m_selected_line + 1, // Source line number (m_selected_line is zero based)
4989 eLazyBoolCalculate, // Check inlines using global setting
4990 eLazyBoolCalculate, // Skip prologue using global setting,
4992 false); // request_hardware
4993 // Make breakpoint one shot
4994 bp_sp->GetOptions()->SetOneShot(true);
4995 exe_ctx.GetProcessRef().Resume();
4998 else if (m_selected_line < GetNumDisassemblyLines())
5000 const Instruction *inst = m_disassembly_sp->GetInstructionList().GetInstructionAtIndex(m_selected_line).get();
5001 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5002 if (exe_ctx.HasTargetScope())
5004 Address addr = inst->GetAddress();
5005 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (addr, // lldb_private::Address
5007 false); // request_hardware
5008 // Make breakpoint one shot
5009 bp_sp->GetOptions()->SetOneShot(true);
5010 exe_ctx.GetProcessRef().Resume();
5015 case 'b': // 'b' == toggle breakpoint on currently selected line
5016 if (m_selected_line < GetNumSourceLines())
5018 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5019 if (exe_ctx.HasTargetScope())
5021 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (NULL, // Don't limit the breakpoint to certain modules
5022 m_file_sp->GetFileSpec(), // Source file
5023 m_selected_line + 1, // Source line number (m_selected_line is zero based)
5024 eLazyBoolCalculate, // Check inlines using global setting
5025 eLazyBoolCalculate, // Skip prologue using global setting,
5027 false); // request_hardware
5030 else if (m_selected_line < GetNumDisassemblyLines())
5032 const Instruction *inst = m_disassembly_sp->GetInstructionList().GetInstructionAtIndex(m_selected_line).get();
5033 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5034 if (exe_ctx.HasTargetScope())
5036 Address addr = inst->GetAddress();
5037 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (addr, // lldb_private::Address
5039 false); // request_hardware
5044 case 'd': // 'd' == detach and let run
5045 case 'D': // 'D' == detach and keep stopped
5047 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5048 if (exe_ctx.HasProcessScope())
5049 exe_ctx.GetProcessRef().Detach(c == 'D');
5056 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5057 if (exe_ctx.HasProcessScope())
5058 exe_ctx.GetProcessRef().Destroy();
5065 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5066 if (exe_ctx.HasProcessScope())
5067 exe_ctx.GetProcessRef().Resume();
5074 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5075 if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true))
5077 exe_ctx.GetThreadRef().StepOut();
5081 case 'n': // 'n' == step over
5082 case 'N': // 'N' == step over instruction
5084 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5085 if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true))
5087 bool source_step = (c == 'n');
5088 exe_ctx.GetThreadRef().StepOver(source_step);
5092 case 's': // 's' == step into
5093 case 'S': // 'S' == step into instruction
5095 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5096 if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true))
5098 bool source_step = (c == 's');
5099 bool avoid_code_without_debug_info = true;
5100 exe_ctx.GetThreadRef().StepIn(source_step, avoid_code_without_debug_info);
5106 window.CreateHelpSubwindow ();
5112 return eKeyNotHandled;
5116 typedef std::set<uint32_t> BreakpointLines;
5117 typedef std::set<lldb::addr_t> BreakpointAddrs;
5119 Debugger &m_debugger;
5121 SourceManager::FileSP m_file_sp;
5122 SymbolContextScope *m_disassembly_scope;
5123 lldb::DisassemblerSP m_disassembly_sp;
5124 AddressRange m_disassembly_range;
5125 lldb::user_id_t m_tid;
5126 char m_line_format[8];
5128 uint32_t m_selected_line; // The selected line
5129 uint32_t m_pc_line; // The line with the PC
5131 uint32_t m_frame_idx;
5132 int m_first_visible_line;
5140 DisplayOptions ValueObjectListDelegate::g_options = { true };
5142 IOHandlerCursesGUI::IOHandlerCursesGUI (Debugger &debugger) :
5143 IOHandler (debugger)
5148 IOHandlerCursesGUI::Activate ()
5150 IOHandler::Activate();
5153 m_app_ap.reset (new Application (GetInputFILE(), GetOutputFILE()));
5156 // This is both a window and a menu delegate
5157 std::shared_ptr<ApplicationDelegate> app_delegate_sp(new ApplicationDelegate(*m_app_ap, m_debugger));
5159 MenuDelegateSP app_menu_delegate_sp = std::static_pointer_cast<MenuDelegate>(app_delegate_sp);
5160 MenuSP lldb_menu_sp(new Menu("LLDB" , "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB));
5161 MenuSP exit_menuitem_sp(new Menu("Exit", NULL, 'x', ApplicationDelegate::eMenuID_LLDBExit));
5162 exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit);
5163 lldb_menu_sp->AddSubmenu (MenuSP (new Menu("About LLDB", NULL, 'a', ApplicationDelegate::eMenuID_LLDBAbout)));
5164 lldb_menu_sp->AddSubmenu (MenuSP (new Menu(Menu::Type::Separator)));
5165 lldb_menu_sp->AddSubmenu (exit_menuitem_sp);
5167 MenuSP target_menu_sp(new Menu("Target" ,"F2", KEY_F(2), ApplicationDelegate::eMenuID_Target));
5168 target_menu_sp->AddSubmenu (MenuSP (new Menu("Create", NULL, 'c', ApplicationDelegate::eMenuID_TargetCreate)));
5169 target_menu_sp->AddSubmenu (MenuSP (new Menu("Delete", NULL, 'd', ApplicationDelegate::eMenuID_TargetDelete)));
5171 MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3), ApplicationDelegate::eMenuID_Process));
5172 process_menu_sp->AddSubmenu (MenuSP (new Menu("Attach" , NULL, 'a', ApplicationDelegate::eMenuID_ProcessAttach)));
5173 process_menu_sp->AddSubmenu (MenuSP (new Menu("Detach" , NULL, 'd', ApplicationDelegate::eMenuID_ProcessDetach)));
5174 process_menu_sp->AddSubmenu (MenuSP (new Menu("Launch" , NULL, 'l', ApplicationDelegate::eMenuID_ProcessLaunch)));
5175 process_menu_sp->AddSubmenu (MenuSP (new Menu(Menu::Type::Separator)));
5176 process_menu_sp->AddSubmenu (MenuSP (new Menu("Continue", NULL, 'c', ApplicationDelegate::eMenuID_ProcessContinue)));
5177 process_menu_sp->AddSubmenu (MenuSP (new Menu("Halt" , NULL, 'h', ApplicationDelegate::eMenuID_ProcessHalt)));
5178 process_menu_sp->AddSubmenu (MenuSP (new Menu("Kill" , NULL, 'k', ApplicationDelegate::eMenuID_ProcessKill)));
5180 MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4), ApplicationDelegate::eMenuID_Thread));
5181 thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step In" , NULL, 'i', ApplicationDelegate::eMenuID_ThreadStepIn)));
5182 thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step Over", NULL, 'v', ApplicationDelegate::eMenuID_ThreadStepOver)));
5183 thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step Out" , NULL, 'o', ApplicationDelegate::eMenuID_ThreadStepOut)));
5185 MenuSP view_menu_sp(new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View));
5186 view_menu_sp->AddSubmenu (MenuSP (new Menu("Backtrace", NULL, 'b', ApplicationDelegate::eMenuID_ViewBacktrace)));
5187 view_menu_sp->AddSubmenu (MenuSP (new Menu("Registers", NULL, 'r', ApplicationDelegate::eMenuID_ViewRegisters)));
5188 view_menu_sp->AddSubmenu (MenuSP (new Menu("Source" , NULL, 's', ApplicationDelegate::eMenuID_ViewSource)));
5189 view_menu_sp->AddSubmenu (MenuSP (new Menu("Variables", NULL, 'v', ApplicationDelegate::eMenuID_ViewVariables)));
5191 MenuSP help_menu_sp(new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help));
5192 help_menu_sp->AddSubmenu (MenuSP (new Menu("GUI Help", NULL, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp)));
5194 m_app_ap->Initialize();
5195 WindowSP &main_window_sp = m_app_ap->GetMainWindow();
5197 MenuSP menubar_sp(new Menu(Menu::Type::Bar));
5198 menubar_sp->AddSubmenu (lldb_menu_sp);
5199 menubar_sp->AddSubmenu (target_menu_sp);
5200 menubar_sp->AddSubmenu (process_menu_sp);
5201 menubar_sp->AddSubmenu (thread_menu_sp);
5202 menubar_sp->AddSubmenu (view_menu_sp);
5203 menubar_sp->AddSubmenu (help_menu_sp);
5204 menubar_sp->SetDelegate(app_menu_delegate_sp);
5206 Rect content_bounds = main_window_sp->GetFrame();
5207 Rect menubar_bounds = content_bounds.MakeMenuBar();
5208 Rect status_bounds = content_bounds.MakeStatusBar();
5210 Rect variables_bounds;
5211 Rect threads_bounds;
5212 Rect source_variables_bounds;
5213 content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds, threads_bounds);
5214 source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds, variables_bounds);
5216 WindowSP menubar_window_sp = main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false);
5217 // Let the menubar get keys if the active window doesn't handle the
5218 // keys that are typed so it can respond to menubar key presses.
5219 menubar_window_sp->SetCanBeActive(false); // Don't let the menubar become the active window
5220 menubar_window_sp->SetDelegate(menubar_sp);
5222 WindowSP source_window_sp (main_window_sp->CreateSubWindow("Source",
5225 WindowSP variables_window_sp (main_window_sp->CreateSubWindow("Variables",
5228 WindowSP threads_window_sp (main_window_sp->CreateSubWindow("Threads",
5231 WindowSP status_window_sp (main_window_sp->CreateSubWindow("Status",
5234 status_window_sp->SetCanBeActive(false); // Don't let the status bar become the active window
5235 main_window_sp->SetDelegate (std::static_pointer_cast<WindowDelegate>(app_delegate_sp));
5236 source_window_sp->SetDelegate (WindowDelegateSP(new SourceFileWindowDelegate(m_debugger)));
5237 variables_window_sp->SetDelegate (WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
5238 TreeDelegateSP thread_delegate_sp (new ThreadTreeDelegate(m_debugger));
5239 threads_window_sp->SetDelegate (WindowDelegateSP(new TreeWindowDelegate(m_debugger, thread_delegate_sp)));
5240 status_window_sp->SetDelegate (WindowDelegateSP(new StatusBarWindowDelegate(m_debugger)));
5242 // Show the main help window once the first time the curses GUI is launched
5243 static bool g_showed_help = false;
5246 g_showed_help = true;
5247 main_window_sp->CreateHelpSubwindow();
5250 init_pair (1, COLOR_WHITE , COLOR_BLUE );
5251 init_pair (2, COLOR_BLACK , COLOR_WHITE );
5252 init_pair (3, COLOR_MAGENTA , COLOR_WHITE );
5253 init_pair (4, COLOR_MAGENTA , COLOR_BLACK );
5254 init_pair (5, COLOR_RED , COLOR_BLACK );
5260 IOHandlerCursesGUI::Deactivate ()
5262 m_app_ap->Terminate();
5266 IOHandlerCursesGUI::Run ()
5268 m_app_ap->Run(m_debugger);
5273 IOHandlerCursesGUI::~IOHandlerCursesGUI ()
5279 IOHandlerCursesGUI::Hide ()
5285 IOHandlerCursesGUI::Refresh ()
5290 IOHandlerCursesGUI::Cancel ()
5295 IOHandlerCursesGUI::Interrupt ()
5301 IOHandlerCursesGUI::GotEOF()
5305 #endif // #ifndef LLDB_DISABLE_CURSES