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/Module.h"
19 #include "lldb/Core/State.h"
20 #include "lldb/Core/StreamFile.h"
21 #include "lldb/Core/ValueObjectRegister.h"
22 #ifndef LLDB_DISABLE_LIBEDIT
23 #include "lldb/Host/Editline.h"
25 #include "lldb/Interpreter/CommandCompletions.h"
26 #include "lldb/Interpreter/CommandInterpreter.h"
27 #include "lldb/Symbol/Block.h"
28 #include "lldb/Symbol/Function.h"
29 #include "lldb/Symbol/Symbol.h"
30 #include "lldb/Target/RegisterContext.h"
31 #include "lldb/Target/ThreadPlan.h"
33 #ifndef LLDB_DISABLE_CURSES
39 using namespace lldb_private;
41 IOHandler::IOHandler (Debugger &debugger, IOHandler::Type type) :
44 StreamFileSP(), // Adopt STDIN from top input reader
45 StreamFileSP(), // Adopt STDOUT from top input reader
46 StreamFileSP(), // Adopt STDERR from top input reader
52 IOHandler::IOHandler (Debugger &debugger,
54 const lldb::StreamFileSP &input_sp,
55 const lldb::StreamFileSP &output_sp,
56 const lldb::StreamFileSP &error_sp,
58 m_debugger (debugger),
59 m_input_sp (input_sp),
60 m_output_sp (output_sp),
61 m_error_sp (error_sp),
69 // If any files are not specified, then adopt them from the top input reader.
70 if (!m_input_sp || !m_output_sp || !m_error_sp)
71 debugger.AdoptTopIOHandlerFilesIfInvalid (m_input_sp,
76 IOHandler::~IOHandler()
82 IOHandler::GetInputFD()
85 return m_input_sp->GetFile().GetDescriptor();
90 IOHandler::GetOutputFD()
93 return m_output_sp->GetFile().GetDescriptor();
98 IOHandler::GetErrorFD()
101 return m_error_sp->GetFile().GetDescriptor();
106 IOHandler::GetInputFILE()
109 return m_input_sp->GetFile().GetStream();
114 IOHandler::GetOutputFILE()
117 return m_output_sp->GetFile().GetStream();
122 IOHandler::GetErrorFILE()
125 return m_error_sp->GetFile().GetStream();
130 IOHandler::GetInputStreamFile()
136 IOHandler::GetOutputStreamFile()
143 IOHandler::GetErrorStreamFile()
149 IOHandler::GetIsInteractive ()
151 return GetInputStreamFile()->GetFile().GetIsInteractive ();
155 IOHandler::GetIsRealTerminal ()
157 return GetInputStreamFile()->GetFile().GetIsRealTerminal();
161 IOHandler::SetPopped (bool b)
163 m_popped.SetValue(b, eBroadcastOnChange);
167 IOHandler::WaitForPop ()
169 m_popped.WaitForValueEqualTo(true);
172 IOHandlerConfirm::IOHandlerConfirm (Debugger &debugger,
174 bool default_response) :
175 IOHandlerEditline(debugger,
176 IOHandler::Type::Confirm,
177 NULL, // NULL editline_name means no history loaded/saved
179 NULL, // No continuation prompt
181 false, // Don't colorize the prompt (i.e. the confirm message.)
184 m_default_response (default_response),
185 m_user_response (default_response)
187 StreamString prompt_stream;
188 prompt_stream.PutCString(prompt);
189 if (m_default_response)
190 prompt_stream.Printf(": [Y/n] ");
192 prompt_stream.Printf(": [y/N] ");
194 SetPrompt (prompt_stream.GetString().c_str());
199 IOHandlerConfirm::~IOHandlerConfirm ()
204 IOHandlerConfirm::IOHandlerComplete (IOHandler &io_handler,
205 const char *current_line,
207 const char *last_char,
208 int skip_first_n_matches,
212 if (current_line == cursor)
214 if (m_default_response)
216 matches.AppendString("y");
220 matches.AppendString("n");
223 return matches.GetSize();
227 IOHandlerConfirm::IOHandlerInputComplete (IOHandler &io_handler, std::string &line)
231 // User just hit enter, set the response to the default
232 m_user_response = m_default_response;
233 io_handler.SetIsDone(true);
237 if (line.size() == 1)
243 m_user_response = true;
244 io_handler.SetIsDone(true);
248 m_user_response = false;
249 io_handler.SetIsDone(true);
256 if (line == "yes" || line == "YES" || line == "Yes")
258 m_user_response = true;
259 io_handler.SetIsDone(true);
261 else if (line == "no" || line == "NO" || line == "No")
263 m_user_response = false;
264 io_handler.SetIsDone(true);
269 IOHandlerDelegate::IOHandlerComplete (IOHandler &io_handler,
270 const char *current_line,
272 const char *last_char,
273 int skip_first_n_matches,
277 switch (m_completion)
279 case Completion::None:
282 case Completion::LLDBCommand:
283 return io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion (current_line,
286 skip_first_n_matches,
290 case Completion::Expression:
292 bool word_complete = false;
293 const char *word_start = cursor;
294 if (cursor > current_line)
296 while (word_start > current_line && !isspace(*word_start))
298 CommandCompletions::InvokeCommonCompletionCallbacks (io_handler.GetDebugger().GetCommandInterpreter(),
299 CommandCompletions::eVariablePathCompletion,
301 skip_first_n_matches,
307 size_t num_matches = matches.GetSize();
310 std::string common_prefix;
311 matches.LongestCommonPrefix (common_prefix);
312 const size_t partial_name_len = strlen(word_start);
314 // If we matched a unique single command, add a space...
315 // Only do this if the completer told us this was a complete word, however...
316 if (num_matches == 1 && word_complete)
318 common_prefix.push_back(' ');
320 common_prefix.erase (0, partial_name_len);
321 matches.InsertStringAtIndex(0, std::move(common_prefix));
333 IOHandlerEditline::IOHandlerEditline (Debugger &debugger,
334 IOHandler::Type type,
335 const char *editline_name, // Used for saving history files
337 const char *continuation_prompt,
340 uint32_t line_number_start,
341 IOHandlerDelegate &delegate) :
342 IOHandlerEditline(debugger,
344 StreamFileSP(), // Inherit input from top input reader
345 StreamFileSP(), // Inherit output from top input reader
346 StreamFileSP(), // Inherit error from top input reader
348 editline_name, // Used for saving history files
358 IOHandlerEditline::IOHandlerEditline (Debugger &debugger,
359 IOHandler::Type type,
360 const lldb::StreamFileSP &input_sp,
361 const lldb::StreamFileSP &output_sp,
362 const lldb::StreamFileSP &error_sp,
364 const char *editline_name, // Used for saving history files
366 const char *continuation_prompt,
369 uint32_t line_number_start,
370 IOHandlerDelegate &delegate) :
371 IOHandler (debugger, type, input_sp, output_sp, error_sp, flags),
372 #ifndef LLDB_DISABLE_LIBEDIT
375 m_delegate (delegate),
377 m_continuation_prompt(),
378 m_current_lines_ptr (NULL),
379 m_base_line_number (line_number_start),
380 m_curr_line_idx (UINT32_MAX),
381 m_multi_line (multi_line),
382 m_color_prompts (color_prompts),
383 m_interrupt_exits (true)
387 #ifndef LLDB_DISABLE_LIBEDIT
388 bool use_editline = false;
390 use_editline = m_input_sp->GetFile().GetIsRealTerminal();
394 m_editline_ap.reset(new Editline (editline_name,
399 m_editline_ap->SetIsInputCompleteCallback (IsInputCompleteCallback, this);
400 m_editline_ap->SetAutoCompleteCallback (AutoCompleteCallback, this);
401 // See if the delegate supports fixing indentation
402 const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters();
405 // The delegate does support indentation, hook it up so when any indentation
406 // character is typed, the delegate gets a chance to fix it
407 m_editline_ap->SetFixIndentationCallback (FixIndentationCallback, this, indent_chars);
411 SetBaseLineNumber (m_base_line_number);
412 SetPrompt(prompt ? prompt : "");
413 SetContinuationPrompt(continuation_prompt);
416 IOHandlerEditline::~IOHandlerEditline ()
418 #ifndef LLDB_DISABLE_LIBEDIT
419 m_editline_ap.reset();
424 IOHandlerEditline::Activate ()
426 IOHandler::Activate();
427 m_delegate.IOHandlerActivated(*this);
431 IOHandlerEditline::Deactivate ()
433 IOHandler::Deactivate();
434 m_delegate.IOHandlerDeactivated(*this);
439 IOHandlerEditline::GetLine (std::string &line, bool &interrupted)
441 #ifndef LLDB_DISABLE_LIBEDIT
444 return m_editline_ap->GetLine (line, interrupted);
451 FILE *in = GetInputFILE();
454 if (GetIsInteractive())
456 const char *prompt = NULL;
458 if (m_multi_line && m_curr_line_idx > 0)
459 prompt = GetContinuationPrompt();
462 prompt = GetPrompt();
464 if (prompt && prompt[0])
466 FILE *out = GetOutputFILE();
469 ::fprintf(out, "%s", prompt);
476 bool got_line = false;
479 if (fgets(buffer, sizeof(buffer), in) == NULL)
481 const int saved_errno = errno;
486 if (saved_errno != EINTR)
493 size_t buffer_len = strlen(buffer);
494 assert (buffer[buffer_len] == '\0');
495 char last_char = buffer[buffer_len-1];
496 if (last_char == '\r' || last_char == '\n')
499 // Strip trailing newlines
500 while (last_char == '\r' || last_char == '\n')
505 last_char = buffer[buffer_len-1];
508 line.append(buffer, buffer_len);
511 // We might have gotten a newline on a line by itself
512 // make sure to return true in this case.
517 // No more input file, we are done...
521 #ifndef LLDB_DISABLE_LIBEDIT
527 #ifndef LLDB_DISABLE_LIBEDIT
529 IOHandlerEditline::IsInputCompleteCallback (Editline *editline,
533 IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton;
534 return editline_reader->m_delegate.IOHandlerIsInputComplete(*editline_reader, lines);
538 IOHandlerEditline::FixIndentationCallback (Editline *editline,
539 const StringList &lines,
543 IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton;
544 return editline_reader->m_delegate.IOHandlerFixIndentation(*editline_reader, lines, cursor_position);
548 IOHandlerEditline::AutoCompleteCallback (const char *current_line,
550 const char *last_char,
551 int skip_first_n_matches,
556 IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton;
558 return editline_reader->m_delegate.IOHandlerComplete (*editline_reader,
562 skip_first_n_matches,
570 IOHandlerEditline::GetPrompt ()
572 #ifndef LLDB_DISABLE_LIBEDIT
575 return m_editline_ap->GetPrompt ();
580 if (m_prompt.empty())
582 #ifndef LLDB_DISABLE_LIBEDIT
585 return m_prompt.c_str();
589 IOHandlerEditline::SetPrompt (const char *p)
595 #ifndef LLDB_DISABLE_LIBEDIT
597 m_editline_ap->SetPrompt (m_prompt.empty() ? NULL : m_prompt.c_str());
603 IOHandlerEditline::GetContinuationPrompt ()
605 if (m_continuation_prompt.empty())
607 return m_continuation_prompt.c_str();
612 IOHandlerEditline::SetContinuationPrompt (const char *p)
615 m_continuation_prompt = p;
617 m_continuation_prompt.clear();
619 #ifndef LLDB_DISABLE_LIBEDIT
621 m_editline_ap->SetContinuationPrompt (m_continuation_prompt.empty() ? NULL : m_continuation_prompt.c_str());
627 IOHandlerEditline::SetBaseLineNumber (uint32_t line)
629 m_base_line_number = line;
633 IOHandlerEditline::GetCurrentLineIndex () const
635 #ifndef LLDB_DISABLE_LIBEDIT
637 return m_editline_ap->GetCurrentLine();
639 return m_curr_line_idx;
643 IOHandlerEditline::GetLines (StringList &lines, bool &interrupted)
645 m_current_lines_ptr = &lines;
647 bool success = false;
648 #ifndef LLDB_DISABLE_LIBEDIT
651 return m_editline_ap->GetLines (m_base_line_number, lines, interrupted);
661 // Show line numbers if we are asked to
663 if (m_base_line_number > 0 && GetIsInteractive())
665 FILE *out = GetOutputFILE();
667 ::fprintf(out, "%u%s", m_base_line_number + (uint32_t)lines.GetSize(), GetPrompt() == NULL ? " " : "");
670 m_curr_line_idx = lines.GetSize();
672 bool interrupted = false;
673 if (GetLine(line, interrupted) && !interrupted)
675 lines.AppendString(line);
676 done = m_delegate.IOHandlerIsInputComplete(*this, lines);
683 success = lines.GetSize() > 0;
684 #ifndef LLDB_DISABLE_LIBEDIT
690 // Each IOHandler gets to run until it is done. It should read data
691 // from the "in" and place output into "out" and "err and return
694 IOHandlerEditline::Run ()
699 bool interrupted = false;
703 if (GetLines (lines, interrupted))
707 m_done = m_interrupt_exits;
708 m_delegate.IOHandlerInputInterrupted (*this, line);
713 line = lines.CopyList();
714 m_delegate.IOHandlerInputComplete (*this, line);
724 if (GetLine(line, interrupted))
727 m_delegate.IOHandlerInputInterrupted (*this, line);
729 m_delegate.IOHandlerInputComplete (*this, line);
740 IOHandlerEditline::Hide ()
742 #ifndef LLDB_DISABLE_LIBEDIT
744 m_editline_ap->Hide();
750 IOHandlerEditline::Refresh ()
752 #ifndef LLDB_DISABLE_LIBEDIT
755 m_editline_ap->Refresh();
760 const char *prompt = GetPrompt();
761 if (prompt && prompt[0])
763 FILE *out = GetOutputFILE();
766 ::fprintf(out, "%s", prompt);
770 #ifndef LLDB_DISABLE_LIBEDIT
776 IOHandlerEditline::Cancel ()
778 #ifndef LLDB_DISABLE_LIBEDIT
780 m_editline_ap->Interrupt ();
785 IOHandlerEditline::Interrupt ()
787 // Let the delgate handle it first
788 if (m_delegate.IOHandlerInterrupt(*this))
791 #ifndef LLDB_DISABLE_LIBEDIT
793 return m_editline_ap->Interrupt();
799 IOHandlerEditline::GotEOF()
801 #ifndef LLDB_DISABLE_LIBEDIT
803 m_editline_ap->Interrupt();
807 // we may want curses to be disabled for some builds
808 // for instance, windows
809 #ifndef LLDB_DISABLE_CURSES
811 #include "lldb/Core/ValueObject.h"
812 #include "lldb/Symbol/VariableList.h"
813 #include "lldb/Target/Target.h"
814 #include "lldb/Target/Process.h"
815 #include "lldb/Target/Thread.h"
816 #include "lldb/Target/StackFrame.h"
818 #define KEY_RETURN 10
819 #define KEY_ESCAPE 27
826 class WindowDelegate;
827 typedef std::shared_ptr<Menu> MenuSP;
828 typedef std::shared_ptr<MenuDelegate> MenuDelegateSP;
829 typedef std::shared_ptr<Window> WindowSP;
830 typedef std::shared_ptr<WindowDelegate> WindowDelegateSP;
831 typedef std::vector<MenuSP> Menus;
832 typedef std::vector<WindowSP> Windows;
833 typedef std::vector<WindowDelegateSP> WindowDelegates;
836 type summary add -s "x=${var.x}, y=${var.y}" curses::Point
837 type summary add -s "w=${var.width}, h=${var.height}" curses::Size
838 type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect
845 Point (int _x = 0, int _y = 0) :
859 operator += (const Point &rhs)
869 printf ("(x=%i, y=%i)\n", x, y);
874 bool operator == (const Point &lhs, const Point &rhs)
876 return lhs.x == rhs.x && lhs.y == rhs.y;
878 bool operator != (const Point &lhs, const Point &rhs)
880 return lhs.x != rhs.x || lhs.y != rhs.y;
887 Size (int w = 0, int h = 0) :
903 printf ("(w=%i, h=%i)\n", width, height);
908 bool operator == (const Size &lhs, const Size &rhs)
910 return lhs.width == rhs.width && lhs.height == rhs.height;
912 bool operator != (const Size &lhs, const Size &rhs)
914 return lhs.width != rhs.width || lhs.height != rhs.height;
928 Rect (const Point &p, const Size &s) :
944 printf ("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width, size.height);
950 if (size.width > w*2)
954 if (size.height > h*2)
958 // Return a status bar rectangle which is the last line of
959 // this rectangle. This rectangle will be modified to not
960 // include the status bar area.
967 status_bar.origin.x = origin.x;
968 status_bar.origin.y = size.height;
969 status_bar.size.width = size.width;
970 status_bar.size.height = 1;
976 // Return a menubar rectangle which is the first line of
977 // this rectangle. This rectangle will be modified to not
978 // include the menubar area.
985 menubar.origin.x = origin.x;
986 menubar.origin.y = origin.y;
987 menubar.size.width = size.width;
988 menubar.size.height = 1;
996 HorizontalSplitPercentage (float top_percentage, Rect &top, Rect &bottom) const
998 float top_height = top_percentage * size.height;
999 HorizontalSplit (top_height, top, bottom);
1003 HorizontalSplit (int top_height, Rect &top, Rect &bottom) const
1006 if (top_height < size.height)
1008 top.size.height = top_height;
1009 bottom.origin.x = origin.x;
1010 bottom.origin.y = origin.y + top.size.height;
1011 bottom.size.width = size.width;
1012 bottom.size.height = size.height - top.size.height;
1021 VerticalSplitPercentage (float left_percentage, Rect &left, Rect &right) const
1023 float left_width = left_percentage * size.width;
1024 VerticalSplit (left_width, left, right);
1029 VerticalSplit (int left_width, Rect &left, Rect &right) const
1032 if (left_width < size.width)
1034 left.size.width = left_width;
1035 right.origin.x = origin.x + left.size.width;
1036 right.origin.y = origin.y;
1037 right.size.width = size.width - left.size.width;
1038 right.size.height = size.height;
1047 bool operator == (const Rect &lhs, const Rect &rhs)
1049 return lhs.origin == rhs.origin && lhs.size == rhs.size;
1051 bool operator != (const Rect &lhs, const Rect &rhs)
1053 return lhs.origin != rhs.origin || lhs.size != rhs.size;
1056 enum HandleCharResult
1060 eQuitApplication = 2
1063 enum class MenuActionResult
1067 Quit // Exit all menus and quit
1073 const char *description;
1076 class WindowDelegate
1085 WindowDelegateDraw (Window &window, bool force)
1087 return false; // Drawing not handled
1090 virtual HandleCharResult
1091 WindowDelegateHandleChar (Window &window, int key)
1093 return eKeyNotHandled;
1096 virtual const char *
1097 WindowDelegateGetHelpText ()
1103 WindowDelegateGetKeyHelp ()
1109 class HelpDialogDelegate :
1110 public WindowDelegate
1113 HelpDialogDelegate (const char *text, KeyHelp *key_help_array);
1116 ~HelpDialogDelegate();
1119 WindowDelegateDraw (Window &window, bool force);
1121 virtual HandleCharResult
1122 WindowDelegateHandleChar (Window &window, int key);
1127 return m_text.GetSize();
1131 GetMaxLineLength () const
1133 return m_text.GetMaxStringLength();
1138 int m_first_visible_line;
1146 Window (const char *name) :
1153 m_curr_active_window_idx (UINT32_MAX),
1154 m_prev_active_window_idx (UINT32_MAX),
1156 m_needs_update (true),
1157 m_can_activate (true),
1162 Window (const char *name, WINDOW *w, bool del = true) :
1169 m_curr_active_window_idx (UINT32_MAX),
1170 m_prev_active_window_idx (UINT32_MAX),
1172 m_needs_update (true),
1173 m_can_activate (true),
1180 Window (const char *name, const Rect &bounds) :
1186 m_curr_active_window_idx (UINT32_MAX),
1187 m_prev_active_window_idx (UINT32_MAX),
1189 m_needs_update (true),
1190 m_can_activate (true),
1193 Reset (::newwin (bounds.size.height, bounds.size.width, bounds.origin.y, bounds.origin.y));
1199 RemoveSubWindows ();
1204 Reset (WINDOW *w = NULL, bool del = true)
1211 ::del_panel (m_panel);
1214 if (m_window && m_delete)
1216 ::delwin (m_window);
1223 m_panel = ::new_panel (m_window);
1228 void AttributeOn (attr_t attr) { ::wattron (m_window, attr); }
1229 void AttributeOff (attr_t attr) { ::wattroff (m_window, attr); }
1230 void Box (chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) { ::box(m_window, v_char, h_char); }
1231 void Clear () { ::wclear (m_window); }
1232 void Erase () { ::werase (m_window); }
1233 Rect GetBounds () { return Rect (GetParentOrigin(), GetSize()); } // Get the rectangle in our parent window
1234 int GetChar () { return ::wgetch (m_window); }
1235 int GetCursorX () { return getcurx (m_window); }
1236 int GetCursorY () { return getcury (m_window); }
1237 Rect GetFrame () { return Rect (Point(), GetSize()); } // Get our rectangle in our own coordinate system
1238 Point GetParentOrigin() { return Point (GetParentX(), GetParentY()); }
1239 Size GetSize() { return Size (GetWidth(), GetHeight()); }
1240 int GetParentX () { return getparx (m_window); }
1241 int GetParentY () { return getpary (m_window); }
1242 int GetMaxX() { return getmaxx (m_window); }
1243 int GetMaxY() { return getmaxy (m_window); }
1244 int GetWidth() { return GetMaxX(); }
1245 int GetHeight() { return GetMaxY(); }
1246 void MoveCursor (int x, int y) { ::wmove (m_window, y, x); }
1247 void MoveWindow (int x, int y) { MoveWindow(Point(x,y)); }
1248 void Resize (int w, int h) { ::wresize(m_window, h, w); }
1249 void Resize (const Size &size) { ::wresize(m_window, size.height, size.width); }
1250 void PutChar (int ch) { ::waddch (m_window, ch); }
1251 void PutCString (const char *s, int len = -1) { ::waddnstr (m_window, s, len); }
1252 void Refresh () { ::wrefresh (m_window); }
1253 void DeferredRefresh ()
1255 // We are using panels, so we don't need to call this...
1256 //::wnoutrefresh(m_window);
1258 void SetBackground (int color_pair_idx) { ::wbkgd (m_window,COLOR_PAIR(color_pair_idx)); }
1259 void UnderlineOn () { AttributeOn(A_UNDERLINE); }
1260 void UnderlineOff () { AttributeOff(A_UNDERLINE); }
1262 void PutCStringTruncated (const char *s, int right_pad)
1264 int bytes_left = GetWidth() - GetCursorX();
1265 if (bytes_left > right_pad)
1267 bytes_left -= right_pad;
1268 ::waddnstr (m_window, s, bytes_left);
1273 MoveWindow (const Point &origin)
1275 const bool moving_window = origin != GetParentOrigin();
1276 if (m_is_subwin && moving_window)
1278 // Can't move subwindows, must delete and re-create
1279 Size size = GetSize();
1280 Reset (::subwin (m_parent->m_window,
1288 ::mvwin (m_window, origin.y, origin.x);
1293 SetBounds (const Rect &bounds)
1295 const bool moving_window = bounds.origin != GetParentOrigin();
1296 if (m_is_subwin && moving_window)
1298 // Can't move subwindows, must delete and re-create
1299 Reset (::subwin (m_parent->m_window,
1303 bounds.origin.x), true);
1308 MoveWindow(bounds.origin);
1309 Resize (bounds.size);
1314 Printf (const char *format, ...) __attribute__ ((format (printf, 2, 3)))
1317 va_start (args, format);
1318 vwprintw(m_window, format, args);
1325 ::touchwin (m_window);
1331 CreateSubWindow (const char *name, const Rect &bounds, bool make_active)
1333 WindowSP subwindow_sp;
1336 subwindow_sp.reset(new Window(name, ::subwin (m_window,
1340 bounds.origin.x), true));
1341 subwindow_sp->m_is_subwin = true;
1345 subwindow_sp.reset(new Window(name, ::newwin (bounds.size.height,
1348 bounds.origin.x), true));
1349 subwindow_sp->m_is_subwin = false;
1351 subwindow_sp->m_parent = this;
1354 m_prev_active_window_idx = m_curr_active_window_idx;
1355 m_curr_active_window_idx = m_subwindows.size();
1357 m_subwindows.push_back(subwindow_sp);
1358 ::top_panel (subwindow_sp->m_panel);
1359 m_needs_update = true;
1360 return subwindow_sp;
1364 RemoveSubWindow (Window *window)
1366 Windows::iterator pos, end = m_subwindows.end();
1368 for (pos = m_subwindows.begin(); pos != end; ++pos, ++i)
1370 if ((*pos).get() == window)
1372 if (m_prev_active_window_idx == i)
1373 m_prev_active_window_idx = UINT32_MAX;
1374 else if (m_prev_active_window_idx != UINT32_MAX && m_prev_active_window_idx > i)
1375 --m_prev_active_window_idx;
1377 if (m_curr_active_window_idx == i)
1378 m_curr_active_window_idx = UINT32_MAX;
1379 else if (m_curr_active_window_idx != UINT32_MAX && m_curr_active_window_idx > i)
1380 --m_curr_active_window_idx;
1382 m_subwindows.erase(pos);
1383 m_needs_update = true;
1387 ::touchwin (stdscr);
1395 FindSubWindow (const char *name)
1397 Windows::iterator pos, end = m_subwindows.end();
1399 for (pos = m_subwindows.begin(); pos != end; ++pos, ++i)
1401 if ((*pos)->m_name.compare(name) == 0)
1410 m_curr_active_window_idx = UINT32_MAX;
1411 m_prev_active_window_idx = UINT32_MAX;
1412 for (Windows::iterator pos = m_subwindows.begin();
1413 pos != m_subwindows.end();
1414 pos = m_subwindows.erase(pos))
1421 ::touchwin (stdscr);
1435 //----------------------------------------------------------------------
1436 // Window drawing utilities
1437 //----------------------------------------------------------------------
1439 DrawTitleBox (const char *title, const char *bottom_message = NULL)
1443 attr = A_BOLD | COLOR_PAIR(2);
1452 if (title && title[0])
1459 if (bottom_message && bottom_message[0])
1461 int bottom_message_length = strlen(bottom_message);
1462 int x = GetWidth() - 3 - (bottom_message_length + 2);
1466 MoveCursor (x, GetHeight() - 1);
1468 PutCString(bottom_message);
1473 MoveCursor (1, GetHeight() - 1);
1475 PutCStringTruncated (bottom_message, 1);
1486 if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw (*this, force))
1489 for (auto &subwindow_sp : m_subwindows)
1490 subwindow_sp->Draw(force);
1494 CreateHelpSubwindow ()
1498 const char *text = m_delegate_sp->WindowDelegateGetHelpText ();
1499 KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp ();
1500 if ((text && text[0]) || key_help)
1502 std::auto_ptr<HelpDialogDelegate> help_delegate_ap(new HelpDialogDelegate(text, key_help));
1503 const size_t num_lines = help_delegate_ap->GetNumLines();
1504 const size_t max_length = help_delegate_ap->GetMaxLineLength();
1505 Rect bounds = GetBounds();
1507 if (max_length + 4 < static_cast<size_t>(bounds.size.width))
1509 bounds.origin.x += (bounds.size.width - max_length + 4)/2;
1510 bounds.size.width = max_length + 4;
1514 if (bounds.size.width > 100)
1516 const int inset_w = bounds.size.width / 4;
1517 bounds.origin.x += inset_w;
1518 bounds.size.width -= 2*inset_w;
1522 if (num_lines + 2 < static_cast<size_t>(bounds.size.height))
1524 bounds.origin.y += (bounds.size.height - num_lines + 2)/2;
1525 bounds.size.height = num_lines + 2;
1529 if (bounds.size.height > 100)
1531 const int inset_h = bounds.size.height / 4;
1532 bounds.origin.y += inset_h;
1533 bounds.size.height -= 2*inset_h;
1536 WindowSP help_window_sp;
1537 Window *parent_window = GetParent();
1539 help_window_sp = parent_window->CreateSubWindow("Help", bounds, true);
1541 help_window_sp = CreateSubWindow("Help", bounds, true);
1542 help_window_sp->SetDelegate(WindowDelegateSP(help_delegate_ap.release()));
1549 virtual HandleCharResult
1550 HandleChar (int key)
1552 // Always check the active window first
1553 HandleCharResult result = eKeyNotHandled;
1554 WindowSP active_window_sp = GetActiveWindow ();
1555 if (active_window_sp)
1557 result = active_window_sp->HandleChar (key);
1558 if (result != eKeyNotHandled)
1564 result = m_delegate_sp->WindowDelegateHandleChar (*this, key);
1565 if (result != eKeyNotHandled)
1569 // Then check for any windows that want any keys
1570 // that weren't handled. This is typically only
1572 // Make a copy of the subwindows in case any HandleChar()
1573 // functions muck with the subwindows. If we don't do this,
1574 // we can crash when iterating over the subwindows.
1575 Windows subwindows (m_subwindows);
1576 for (auto subwindow_sp : subwindows)
1578 if (subwindow_sp->m_can_activate == false)
1580 HandleCharResult result = subwindow_sp->HandleChar(key);
1581 if (result != eKeyNotHandled)
1586 return eKeyNotHandled;
1590 SetActiveWindow (Window *window)
1592 const size_t num_subwindows = m_subwindows.size();
1593 for (size_t i=0; i<num_subwindows; ++i)
1595 if (m_subwindows[i].get() == window)
1597 m_prev_active_window_idx = m_curr_active_window_idx;
1598 ::top_panel (window->m_panel);
1599 m_curr_active_window_idx = i;
1609 if (!m_subwindows.empty())
1611 if (m_curr_active_window_idx >= m_subwindows.size())
1613 if (m_prev_active_window_idx < m_subwindows.size())
1615 m_curr_active_window_idx = m_prev_active_window_idx;
1616 m_prev_active_window_idx = UINT32_MAX;
1618 else if (IsActive())
1620 m_prev_active_window_idx = UINT32_MAX;
1621 m_curr_active_window_idx = UINT32_MAX;
1623 // Find first window that wants to be active if this window is active
1624 const size_t num_subwindows = m_subwindows.size();
1625 for (size_t i=0; i<num_subwindows; ++i)
1627 if (m_subwindows[i]->GetCanBeActive())
1629 m_curr_active_window_idx = i;
1636 if (m_curr_active_window_idx < m_subwindows.size())
1637 return m_subwindows[m_curr_active_window_idx];
1643 GetCanBeActive () const
1645 return m_can_activate;
1649 SetCanBeActive (bool b)
1654 const WindowDelegateSP &
1655 GetDelegate () const
1657 return m_delegate_sp;
1661 SetDelegate (const WindowDelegateSP &delegate_sp)
1663 m_delegate_sp = delegate_sp;
1676 return m_parent->GetActiveWindow().get() == this;
1678 return true; // Top level window is always active
1682 SelectNextWindowAsActive ()
1684 // Move active focus to next window
1685 const size_t num_subwindows = m_subwindows.size();
1686 if (m_curr_active_window_idx == UINT32_MAX)
1689 for (auto subwindow_sp : m_subwindows)
1691 if (subwindow_sp->GetCanBeActive())
1693 m_curr_active_window_idx = idx;
1699 else if (m_curr_active_window_idx + 1 < num_subwindows)
1701 bool handled = false;
1702 m_prev_active_window_idx = m_curr_active_window_idx;
1703 for (size_t idx=m_curr_active_window_idx + 1; idx<num_subwindows; ++idx)
1705 if (m_subwindows[idx]->GetCanBeActive())
1707 m_curr_active_window_idx = idx;
1714 for (size_t idx=0; idx<=m_prev_active_window_idx; ++idx)
1716 if (m_subwindows[idx]->GetCanBeActive())
1718 m_curr_active_window_idx = idx;
1726 m_prev_active_window_idx = m_curr_active_window_idx;
1727 for (size_t idx=0; idx<num_subwindows; ++idx)
1729 if (m_subwindows[idx]->GetCanBeActive())
1731 m_curr_active_window_idx = idx;
1741 return m_name.c_str();
1748 Windows m_subwindows;
1749 WindowDelegateSP m_delegate_sp;
1750 uint32_t m_curr_active_window_idx;
1751 uint32_t m_prev_active_window_idx;
1753 bool m_needs_update;
1754 bool m_can_activate;
1758 DISALLOW_COPY_AND_ASSIGN(Window);
1764 virtual ~MenuDelegate() {}
1766 virtual MenuActionResult
1767 MenuDelegateAction (Menu &menu) = 0;
1770 class Menu : public WindowDelegate
1781 // Menubar or separator constructor
1784 // Menuitem constructor
1785 Menu (const char *name,
1786 const char *key_name,
1788 uint64_t identifier);
1795 const MenuDelegateSP &
1796 GetDelegate () const
1798 return m_delegate_sp;
1802 SetDelegate (const MenuDelegateSP &delegate_sp)
1804 m_delegate_sp = delegate_sp;
1808 RecalculateNameLengths();
1811 AddSubmenu (const MenuSP &menu_sp);
1814 DrawAndRunMenu (Window &window);
1817 DrawMenuTitle (Window &window, bool highlight);
1820 WindowDelegateDraw (Window &window, bool force);
1822 virtual HandleCharResult
1823 WindowDelegateHandleChar (Window &window, int key);
1826 ActionPrivate (Menu &menu)
1828 MenuActionResult result = MenuActionResult::NotHandled;
1831 result = m_delegate_sp->MenuDelegateAction (menu);
1832 if (result != MenuActionResult::NotHandled)
1837 result = m_parent->ActionPrivate(menu);
1838 if (result != MenuActionResult::NotHandled)
1841 return m_canned_result;
1847 // Call the recursive action so it can try to handle it
1848 // with the menu delegate, and if not, try our parent menu
1849 return ActionPrivate (*this);
1853 SetCannedResult (MenuActionResult result)
1855 m_canned_result = result;
1871 GetSelectedSubmenuIndex () const
1877 SetSelectedSubmenuIndex (int idx)
1889 GetStartingColumn() const
1895 SetStartingColumn(int col)
1907 SetKeyValue(int key_value)
1909 m_key_value = key_value;
1925 GetDrawWidth () const
1927 return m_max_submenu_name_length + m_max_submenu_key_name_length + 8;
1932 GetIdentifier() const
1934 return m_identifier;
1938 SetIdentifier (uint64_t identifier)
1940 m_identifier = identifier;
1945 std::string m_key_name;
1946 uint64_t m_identifier;
1950 int m_max_submenu_name_length;
1951 int m_max_submenu_key_name_length;
1955 WindowSP m_menu_window_sp;
1956 MenuActionResult m_canned_result;
1957 MenuDelegateSP m_delegate_sp;
1960 // Menubar or separator constructor
1961 Menu::Menu (Type type) :
1968 m_max_submenu_name_length (0),
1969 m_max_submenu_key_name_length (0),
1973 m_canned_result (MenuActionResult::NotHandled),
1978 // Menuitem constructor
1979 Menu::Menu (const char *name,
1980 const char *key_name,
1982 uint64_t identifier) :
1985 m_identifier (identifier),
1986 m_type (Type::Invalid),
1987 m_key_value (key_value),
1989 m_max_submenu_name_length (0),
1990 m_max_submenu_key_name_length (0),
1994 m_canned_result (MenuActionResult::NotHandled),
1997 if (name && name[0])
2000 m_type = Type::Item;
2001 if (key_name && key_name[0])
2002 m_key_name = key_name;
2006 m_type = Type::Separator;
2011 Menu::RecalculateNameLengths()
2013 m_max_submenu_name_length = 0;
2014 m_max_submenu_key_name_length = 0;
2015 Menus &submenus = GetSubmenus();
2016 const size_t num_submenus = submenus.size();
2017 for (size_t i=0; i<num_submenus; ++i)
2019 Menu *submenu = submenus[i].get();
2020 if (static_cast<size_t>(m_max_submenu_name_length) < submenu->m_name.size())
2021 m_max_submenu_name_length = submenu->m_name.size();
2022 if (static_cast<size_t>(m_max_submenu_key_name_length) < submenu->m_key_name.size())
2023 m_max_submenu_key_name_length = submenu->m_key_name.size();
2028 Menu::AddSubmenu (const MenuSP &menu_sp)
2030 menu_sp->m_parent = this;
2031 if (static_cast<size_t>(m_max_submenu_name_length) < menu_sp->m_name.size())
2032 m_max_submenu_name_length = menu_sp->m_name.size();
2033 if (static_cast<size_t>(m_max_submenu_key_name_length) < menu_sp->m_key_name.size())
2034 m_max_submenu_key_name_length = menu_sp->m_key_name.size();
2035 m_submenus.push_back(menu_sp);
2039 Menu::DrawMenuTitle (Window &window, bool highlight)
2041 if (m_type == Type::Separator)
2043 window.MoveCursor(0, window.GetCursorY());
2044 window.PutChar(ACS_LTEE);
2045 int width = window.GetWidth();
2049 for (int i=0; i< width; ++i)
2050 window.PutChar(ACS_HLINE);
2052 window.PutChar(ACS_RTEE);
2056 const int shortcut_key = m_key_value;
2057 bool underlined_shortcut = false;
2058 const attr_t hilgight_attr = A_REVERSE;
2060 window.AttributeOn(hilgight_attr);
2061 if (isprint(shortcut_key))
2063 size_t lower_pos = m_name.find(tolower(shortcut_key));
2064 size_t upper_pos = m_name.find(toupper(shortcut_key));
2065 const char *name = m_name.c_str();
2066 size_t pos = std::min<size_t>(lower_pos, upper_pos);
2067 if (pos != std::string::npos)
2069 underlined_shortcut = true;
2072 window.PutCString(name, pos);
2075 const attr_t shortcut_attr = A_UNDERLINE|A_BOLD;
2076 window.AttributeOn (shortcut_attr);
2077 window.PutChar(name[0]);
2078 window.AttributeOff(shortcut_attr);
2081 window.PutCString(name);
2085 if (!underlined_shortcut)
2087 window.PutCString(m_name.c_str());
2091 window.AttributeOff(hilgight_attr);
2093 if (m_key_name.empty())
2095 if (!underlined_shortcut && isprint(m_key_value))
2097 window.AttributeOn (COLOR_PAIR(3));
2098 window.Printf (" (%c)", m_key_value);
2099 window.AttributeOff (COLOR_PAIR(3));
2104 window.AttributeOn (COLOR_PAIR(3));
2105 window.Printf (" (%s)", m_key_name.c_str());
2106 window.AttributeOff (COLOR_PAIR(3));
2112 Menu::WindowDelegateDraw (Window &window, bool force)
2114 Menus &submenus = GetSubmenus();
2115 const size_t num_submenus = submenus.size();
2116 const int selected_idx = GetSelectedSubmenuIndex();
2117 Menu::Type menu_type = GetType ();
2120 case Menu::Type::Bar:
2122 window.SetBackground(2);
2123 window.MoveCursor(0, 0);
2124 for (size_t i=0; i<num_submenus; ++i)
2126 Menu *menu = submenus[i].get();
2128 window.PutChar(' ');
2129 menu->SetStartingColumn (window.GetCursorX());
2130 window.PutCString("| ");
2131 menu->DrawMenuTitle (window, false);
2133 window.PutCString(" |");
2134 window.DeferredRefresh();
2138 case Menu::Type::Item:
2146 window.SetBackground(2);
2148 for (size_t i=0; i<num_submenus; ++i)
2150 const bool is_selected =
2151 (i == static_cast<size_t>(selected_idx));
2152 window.MoveCursor(x, y + i);
2155 // Remember where we want the cursor to be
2159 submenus[i]->DrawMenuTitle (window, is_selected);
2161 window.MoveCursor(cursor_x, cursor_y);
2162 window.DeferredRefresh();
2167 case Menu::Type::Separator:
2170 return true; // Drawing handled...
2174 Menu::WindowDelegateHandleChar (Window &window, int key)
2176 HandleCharResult result = eKeyNotHandled;
2178 Menus &submenus = GetSubmenus();
2179 const size_t num_submenus = submenus.size();
2180 const int selected_idx = GetSelectedSubmenuIndex();
2181 Menu::Type menu_type = GetType ();
2182 if (menu_type == Menu::Type::Bar)
2189 // Show last menu or first menu
2190 if (selected_idx < static_cast<int>(num_submenus))
2191 run_menu_sp = submenus[selected_idx];
2192 else if (!submenus.empty())
2193 run_menu_sp = submenus.front();
2194 result = eKeyHandled;
2200 if (m_selected >= static_cast<int>(num_submenus))
2202 if (m_selected < static_cast<int>(num_submenus))
2203 run_menu_sp = submenus[m_selected];
2204 else if (!submenus.empty())
2205 run_menu_sp = submenus.front();
2206 result = eKeyHandled;
2214 m_selected = num_submenus - 1;
2215 if (m_selected < static_cast<int>(num_submenus))
2216 run_menu_sp = submenus[m_selected];
2217 else if (!submenus.empty())
2218 run_menu_sp = submenus.front();
2219 result = eKeyHandled;
2224 for (size_t i=0; i<num_submenus; ++i)
2226 if (submenus[i]->GetKeyValue() == key)
2228 SetSelectedSubmenuIndex(i);
2229 run_menu_sp = submenus[i];
2230 result = eKeyHandled;
2239 // Run the action on this menu in case we need to populate the
2240 // menu with dynamic content and also in case check marks, and
2241 // any other menu decorations need to be caclulated
2242 if (run_menu_sp->Action() == MenuActionResult::Quit)
2243 return eQuitApplication;
2246 menu_bounds.origin.x = run_menu_sp->GetStartingColumn();
2247 menu_bounds.origin.y = 1;
2248 menu_bounds.size.width = run_menu_sp->GetDrawWidth();
2249 menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2;
2250 if (m_menu_window_sp)
2251 window.GetParent()->RemoveSubWindow(m_menu_window_sp.get());
2253 m_menu_window_sp = window.GetParent()->CreateSubWindow (run_menu_sp->GetName().c_str(),
2256 m_menu_window_sp->SetDelegate (run_menu_sp);
2259 else if (menu_type == Menu::Type::Item)
2264 if (m_submenus.size() > 1)
2266 const int start_select = m_selected;
2267 while (++m_selected != start_select)
2269 if (static_cast<size_t>(m_selected) >= num_submenus)
2271 if (m_submenus[m_selected]->GetType() == Type::Separator)
2281 if (m_submenus.size() > 1)
2283 const int start_select = m_selected;
2284 while (--m_selected != start_select)
2286 if (m_selected < static_cast<int>(0))
2287 m_selected = num_submenus - 1;
2288 if (m_submenus[m_selected]->GetType() == Type::Separator)
2298 if (static_cast<size_t>(selected_idx) < num_submenus)
2300 if (submenus[selected_idx]->Action() == MenuActionResult::Quit)
2301 return eQuitApplication;
2302 window.GetParent()->RemoveSubWindow(&window);
2307 case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in case other chars are entered for escaped sequences
2308 window.GetParent()->RemoveSubWindow(&window);
2313 for (size_t i=0; i<num_submenus; ++i)
2315 Menu *menu = submenus[i].get();
2316 if (menu->GetKeyValue() == key)
2318 SetSelectedSubmenuIndex(i);
2319 window.GetParent()->RemoveSubWindow(&window);
2320 if (menu->Action() == MenuActionResult::Quit)
2321 return eQuitApplication;
2330 else if (menu_type == Menu::Type::Separator)
2341 Application (FILE *in, FILE *out) :
2352 m_window_delegates.clear();
2353 m_window_sp.reset();
2356 ::delscreen(m_screen);
2364 ::setlocale(LC_ALL, "");
2365 ::setlocale(LC_CTYPE, "");
2369 m_screen = ::newterm(NULL, m_out, m_in);
2374 ::keypad(stdscr,TRUE);
2384 Run (Debugger &debugger)
2387 int delay_in_tenths_of_a_second = 1;
2389 // Alas the threading model in curses is a bit lame so we need to
2390 // resort to polling every 0.5 seconds. We could poll for stdin
2391 // ourselves and then pass the keys down but then we need to
2392 // translate all of the escape sequences ourselves. So we resort to
2393 // polling for input because we need to receive async process events
2394 // while in this loop.
2396 halfdelay(delay_in_tenths_of_a_second); // Poll using some number of tenths of seconds seconds when calling Window::GetChar()
2398 ListenerSP listener_sp (new Listener ("lldb.IOHandler.curses.Application"));
2399 ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass());
2400 ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass());
2401 ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass());
2402 debugger.EnableForwardEvents (listener_sp);
2405 #if defined(__APPLE__)
2406 std::deque<int> escape_chars;
2413 m_window_sp->Draw(false);
2414 // All windows should be calling Window::DeferredRefresh() instead
2415 // of Window::Refresh() so we can do a single update and avoid
2416 // any screen blinking
2419 // Cursor hiding isn't working on MacOSX, so hide it in the top left corner
2420 m_window_sp->MoveCursor(0, 0);
2426 #if defined(__APPLE__)
2427 // Terminal.app doesn't map its function keys correctly, F1-F4 default to:
2428 // \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if possible
2430 if (escape_chars.empty())
2431 ch = m_window_sp->GetChar();
2434 ch = escape_chars.front();
2435 escape_chars.pop_front();
2437 if (ch == KEY_ESCAPE)
2439 int ch2 = m_window_sp->GetChar();
2442 int ch3 = m_window_sp->GetChar();
2445 case 'P': ch = KEY_F(1); break;
2446 case 'Q': ch = KEY_F(2); break;
2447 case 'R': ch = KEY_F(3); break;
2448 case 'S': ch = KEY_F(4); break;
2450 escape_chars.push_back(ch2);
2452 escape_chars.push_back(ch3);
2457 escape_chars.push_back(ch2);
2460 int ch = m_window_sp->GetChar();
2465 if (feof(m_in) || ferror(m_in))
2471 // Just a timeout from using halfdelay(), check for events
2473 while (listener_sp->PeekAtNextEvent())
2475 listener_sp->GetNextEvent(event_sp);
2479 Broadcaster *broadcaster = event_sp->GetBroadcaster();
2482 //uint32_t event_type = event_sp->GetType();
2483 ConstString broadcaster_class (broadcaster->GetBroadcasterClass());
2484 if (broadcaster_class == broadcaster_class_process)
2486 debugger.GetCommandInterpreter().UpdateExecutionContext(NULL);
2488 continue; // Don't get any key, just update our view
2497 HandleCharResult key_result = m_window_sp->HandleChar(ch);
2501 debugger.GetCommandInterpreter().UpdateExecutionContext(NULL);
2504 case eKeyNotHandled:
2506 case eQuitApplication:
2513 debugger.CancelForwardEvents (listener_sp);
2521 m_window_sp.reset (new Window ("main", stdscr, false));
2526 GetWindowDelegates ()
2528 return m_window_delegates;
2532 WindowSP m_window_sp;
2533 WindowDelegates m_window_delegates;
2540 } // namespace curses
2543 using namespace curses;
2547 ValueObjectSP valobj;
2552 bool might_have_children;
2554 bool calculated_children;
2555 std::vector<Row> children;
2557 Row (const ValueObjectSP &v, Row *p) :
2563 might_have_children (v ? v->MightHaveChildren() : false),
2565 calculated_children (false),
2574 return 1 + parent->GetDepth();
2582 if (!calculated_children)
2584 calculated_children = true;
2587 const size_t num_children = valobj->GetNumChildren();
2588 for (size_t i=0; i<num_children; ++i)
2590 children.push_back(Row (valobj->GetChildAtIndex(i, true), this));
2603 DrawTree (Window &window)
2606 parent->DrawTreeForChild (window, this, 0);
2608 if (might_have_children)
2610 // It we can get UTF8 characters to work we should try to use the "symbol"
2611 // UTF8 string below
2612 // const char *symbol = "";
2613 // if (row.expanded)
2614 // symbol = "\xe2\x96\xbd ";
2616 // symbol = "\xe2\x96\xb7 ";
2617 // window.PutCString (symbol);
2619 // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
2620 // 'v' or '>' character...
2622 // window.PutChar (ACS_DARROW);
2624 // window.PutChar (ACS_RARROW);
2625 // Since we can't find any good looking right arrow/down arrow
2626 // symbols, just use a diamond...
2627 window.PutChar (ACS_DIAMOND);
2628 window.PutChar (ACS_HLINE);
2633 DrawTreeForChild (Window &window, Row *child, uint32_t reverse_depth)
2636 parent->DrawTreeForChild (window, this, reverse_depth + 1);
2638 if (&children.back() == child)
2641 if (reverse_depth == 0)
2643 window.PutChar (ACS_LLCORNER);
2644 window.PutChar (ACS_HLINE);
2648 window.PutChar (' ');
2649 window.PutChar (' ');
2654 if (reverse_depth == 0)
2656 window.PutChar (ACS_LTEE);
2657 window.PutChar (ACS_HLINE);
2661 window.PutChar (ACS_VLINE);
2662 window.PutChar (' ');
2668 struct DisplayOptions
2679 virtual ~TreeDelegate() {}
2680 virtual void TreeDelegateDrawTreeItem (TreeItem &item, Window &window) = 0;
2681 virtual void TreeDelegateGenerateChildren (TreeItem &item) = 0;
2682 virtual bool TreeDelegateItemSelected (TreeItem &item) = 0; // Return true if we need to update views
2684 typedef std::shared_ptr<TreeDelegate> TreeDelegateSP;
2690 TreeItem (TreeItem *parent, TreeDelegate &delegate, bool might_have_children) :
2692 m_delegate (delegate),
2697 m_might_have_children (might_have_children),
2698 m_is_expanded (false)
2703 operator=(const TreeItem &rhs)
2707 m_parent = rhs.m_parent;
2708 m_delegate = rhs.m_delegate;
2709 m_user_data = rhs.m_user_data;
2710 m_identifier = rhs.m_identifier;
2711 m_row_idx = rhs.m_row_idx;
2712 m_children = rhs.m_children;
2713 m_might_have_children = rhs.m_might_have_children;
2714 m_is_expanded = rhs.m_is_expanded;
2723 return 1 + m_parent->GetDepth();
2728 GetRowIndex () const
2740 Resize (size_t n, const TreeItem &t)
2742 m_children.resize(n, t);
2746 operator [](size_t i)
2748 return m_children[i];
2752 SetRowIndex (int row_idx)
2754 m_row_idx = row_idx;
2760 m_delegate.TreeDelegateGenerateChildren (*this);
2761 return m_children.size();
2767 m_delegate.TreeDelegateItemSelected(*this);
2770 CalculateRowIndexes (int &row_idx)
2772 SetRowIndex(row_idx);
2775 const bool expanded = IsExpanded();
2777 // The root item must calculate its children,
2778 // or we must calculate the number of children
2779 // if the item is expanded
2780 if (m_parent == NULL || expanded)
2783 for (auto &item : m_children)
2786 item.CalculateRowIndexes(row_idx);
2788 item.SetRowIndex(-1);
2801 return m_is_expanded;
2807 m_is_expanded = true;
2813 m_is_expanded = false;
2817 Draw (Window &window,
2818 const int first_visible_row,
2819 const uint32_t selected_row_idx,
2823 if (num_rows_left <= 0)
2826 if (m_row_idx >= first_visible_row)
2828 window.MoveCursor(2, row_idx + 1);
2831 m_parent->DrawTreeForChild (window, this, 0);
2833 if (m_might_have_children)
2835 // It we can get UTF8 characters to work we should try to use the "symbol"
2836 // UTF8 string below
2837 // const char *symbol = "";
2838 // if (row.expanded)
2839 // symbol = "\xe2\x96\xbd ";
2841 // symbol = "\xe2\x96\xb7 ";
2842 // window.PutCString (symbol);
2844 // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
2845 // 'v' or '>' character...
2847 // window.PutChar (ACS_DARROW);
2849 // window.PutChar (ACS_RARROW);
2850 // Since we can't find any good looking right arrow/down arrow
2851 // symbols, just use a diamond...
2852 window.PutChar (ACS_DIAMOND);
2853 window.PutChar (ACS_HLINE);
2856 (selected_row_idx == static_cast<size_t>(m_row_idx)) && window.IsActive();
2859 window.AttributeOn(A_REVERSE);
2861 m_delegate.TreeDelegateDrawTreeItem(*this, window);
2864 window.AttributeOff(A_REVERSE);
2869 if (num_rows_left <= 0)
2870 return false; // We are done drawing...
2874 for (auto &item : m_children)
2876 // If we displayed all the rows and item.Draw() returns
2877 // false we are done drawing and can exit this for loop
2878 if (item.Draw(window, first_visible_row, selected_row_idx, row_idx, num_rows_left) == false)
2882 return num_rows_left >= 0; // Return true if not done drawing yet
2886 DrawTreeForChild (Window &window, TreeItem *child, uint32_t reverse_depth)
2889 m_parent->DrawTreeForChild (window, this, reverse_depth + 1);
2891 if (&m_children.back() == child)
2894 if (reverse_depth == 0)
2896 window.PutChar (ACS_LLCORNER);
2897 window.PutChar (ACS_HLINE);
2901 window.PutChar (' ');
2902 window.PutChar (' ');
2907 if (reverse_depth == 0)
2909 window.PutChar (ACS_LTEE);
2910 window.PutChar (ACS_HLINE);
2914 window.PutChar (ACS_VLINE);
2915 window.PutChar (' ');
2921 GetItemForRowIndex (uint32_t row_idx)
2923 if (static_cast<uint32_t>(m_row_idx) == row_idx)
2925 if (m_children.empty())
2927 if (static_cast<uint32_t>(m_children.back().m_row_idx) < row_idx)
2931 for (auto &item : m_children)
2933 TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx);
2934 if (selected_item_ptr)
2935 return selected_item_ptr;
2948 SetUserData (void *user_data)
2950 m_user_data = user_data;
2954 GetIdentifier() const
2956 return m_identifier;
2960 SetIdentifier (uint64_t identifier)
2962 m_identifier = identifier;
2967 SetMightHaveChildren (bool b)
2969 m_might_have_children = b;
2974 TreeDelegate &m_delegate;
2976 uint64_t m_identifier;
2977 int m_row_idx; // Zero based visible row index, -1 if not visible or for the root item
2978 std::vector<TreeItem> m_children;
2979 bool m_might_have_children;
2984 class TreeWindowDelegate : public WindowDelegate
2987 TreeWindowDelegate (Debugger &debugger, const TreeDelegateSP &delegate_sp) :
2988 m_debugger (debugger),
2989 m_delegate_sp (delegate_sp),
2990 m_root (NULL, *delegate_sp, true),
2991 m_selected_item (NULL),
2993 m_selected_row_idx (0),
2994 m_first_visible_row (0),
3003 NumVisibleRows () const
3005 return m_max_y - m_min_y;
3009 WindowDelegateDraw (Window &window, bool force)
3011 ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext());
3012 Process *process = exe_ctx.GetProcessPtr();
3014 bool display_content = false;
3017 StateType state = process->GetState();
3018 if (StateIsStoppedState(state, true))
3020 // We are stopped, so it is ok to
3021 display_content = true;
3023 else if (StateIsRunningState(state))
3025 return true; // Don't do any updating when we are running
3031 m_max_x = window.GetWidth() - 1;
3032 m_max_y = window.GetHeight() - 1;
3035 window.DrawTitleBox (window.GetName());
3037 if (display_content)
3039 const int num_visible_rows = NumVisibleRows();
3041 m_root.CalculateRowIndexes(m_num_rows);
3043 // If we unexpanded while having something selected our
3044 // total number of rows is less than the num visible rows,
3045 // then make sure we show all the rows by setting the first
3046 // visible row accordingly.
3047 if (m_first_visible_row > 0 && m_num_rows < num_visible_rows)
3048 m_first_visible_row = 0;
3050 // Make sure the selected row is always visible
3051 if (m_selected_row_idx < m_first_visible_row)
3052 m_first_visible_row = m_selected_row_idx;
3053 else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
3054 m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
3057 int num_rows_left = num_visible_rows;
3058 m_root.Draw (window, m_first_visible_row, m_selected_row_idx, row_idx, num_rows_left);
3059 // Get the selected row
3060 m_selected_item = m_root.GetItemForRowIndex (m_selected_row_idx);
3064 m_selected_item = NULL;
3067 window.DeferredRefresh();
3070 return true; // Drawing handled
3074 virtual const char *
3075 WindowDelegateGetHelpText ()
3077 return "Thread window keyboard shortcuts:";
3081 WindowDelegateGetKeyHelp ()
3083 static curses::KeyHelp g_source_view_key_help[] = {
3084 { KEY_UP, "Select previous item" },
3085 { KEY_DOWN, "Select next item" },
3086 { KEY_RIGHT, "Expand the selected item" },
3087 { KEY_LEFT, "Unexpand the selected item or select parent if not expanded" },
3088 { KEY_PPAGE, "Page up" },
3089 { KEY_NPAGE, "Page down" },
3090 { 'h', "Show help dialog" },
3091 { ' ', "Toggle item expansion" },
3093 { '.', "Page down" },
3096 return g_source_view_key_help;
3099 virtual HandleCharResult
3100 WindowDelegateHandleChar (Window &window, int c)
3107 if (m_first_visible_row > 0)
3109 if (m_first_visible_row > m_max_y)
3110 m_first_visible_row -= m_max_y;
3112 m_first_visible_row = 0;
3113 m_selected_row_idx = m_first_visible_row;
3114 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3115 if (m_selected_item)
3116 m_selected_item->ItemWasSelected ();
3123 if (m_num_rows > m_max_y)
3125 if (m_first_visible_row + m_max_y < m_num_rows)
3127 m_first_visible_row += m_max_y;
3128 m_selected_row_idx = m_first_visible_row;
3129 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3130 if (m_selected_item)
3131 m_selected_item->ItemWasSelected ();
3137 if (m_selected_row_idx > 0)
3139 --m_selected_row_idx;
3140 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3141 if (m_selected_item)
3142 m_selected_item->ItemWasSelected ();
3146 if (m_selected_row_idx + 1 < m_num_rows)
3148 ++m_selected_row_idx;
3149 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3150 if (m_selected_item)
3151 m_selected_item->ItemWasSelected ();
3156 if (m_selected_item)
3158 if (!m_selected_item->IsExpanded())
3159 m_selected_item->Expand();
3164 if (m_selected_item)
3166 if (m_selected_item->IsExpanded())
3167 m_selected_item->Unexpand();
3168 else if (m_selected_item->GetParent())
3170 m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex();
3171 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3172 if (m_selected_item)
3173 m_selected_item->ItemWasSelected ();
3179 // Toggle expansion state when SPACE is pressed
3180 if (m_selected_item)
3182 if (m_selected_item->IsExpanded())
3183 m_selected_item->Unexpand();
3185 m_selected_item->Expand();
3190 window.CreateHelpSubwindow ();
3196 return eKeyNotHandled;
3200 Debugger &m_debugger;
3201 TreeDelegateSP m_delegate_sp;
3203 TreeItem *m_selected_item;
3205 int m_selected_row_idx;
3206 int m_first_visible_row;
3214 class FrameTreeDelegate : public TreeDelegate
3217 FrameTreeDelegate () :
3222 virtual ~FrameTreeDelegate()
3227 TreeDelegateDrawTreeItem (TreeItem &item, Window &window)
3229 Thread* thread = (Thread*)item.GetUserData();
3232 const uint64_t frame_idx = item.GetIdentifier();
3233 StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_idx);
3237 const SymbolContext &sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
3238 ExecutionContext exe_ctx (frame_sp);
3239 //const char *frame_format = "frame #${frame.index}: ${module.file.basename}{`${function.name}${function.pc-offset}}}";
3240 const char *frame_format = "frame #${frame.index}: {${function.name}${function.pc-offset}}}";
3241 if (Debugger::FormatPrompt (frame_format, &sc, &exe_ctx, NULL, strm))
3244 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
3250 TreeDelegateGenerateChildren (TreeItem &item)
3252 // No children for frames yet...
3256 TreeDelegateItemSelected (TreeItem &item)
3258 Thread* thread = (Thread*)item.GetUserData();
3261 thread->GetProcess()->GetThreadList().SetSelectedThreadByID(thread->GetID());
3262 const uint64_t frame_idx = item.GetIdentifier();
3263 thread->SetSelectedFrameByIndex(frame_idx);
3270 class ThreadTreeDelegate : public TreeDelegate
3273 ThreadTreeDelegate (Debugger &debugger) :
3275 m_debugger (debugger),
3276 m_tid (LLDB_INVALID_THREAD_ID),
3277 m_stop_id (UINT32_MAX)
3282 ~ThreadTreeDelegate()
3289 return m_debugger.GetCommandInterpreter().GetExecutionContext().GetProcessSP();
3293 GetThread (const TreeItem &item)
3295 ProcessSP process_sp = GetProcess ();
3297 return process_sp->GetThreadList().FindThreadByID(item.GetIdentifier());
3302 TreeDelegateDrawTreeItem (TreeItem &item, Window &window)
3304 ThreadSP thread_sp = GetThread (item);
3308 ExecutionContext exe_ctx (thread_sp);
3309 const char *format = "thread #${thread.index}: tid = ${thread.id}{, stop reason = ${thread.stop-reason}}";
3310 if (Debugger::FormatPrompt (format, NULL, &exe_ctx, NULL, strm))
3313 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
3318 TreeDelegateGenerateChildren (TreeItem &item)
3320 ProcessSP process_sp = GetProcess ();
3321 if (process_sp && process_sp->IsAlive())
3323 StateType state = process_sp->GetState();
3324 if (StateIsStoppedState(state, true))
3326 ThreadSP thread_sp = GetThread (item);
3329 if (m_stop_id == process_sp->GetStopID() && thread_sp->GetID() == m_tid)
3330 return; // Children are already up to date
3331 if (!m_frame_delegate_sp)
3333 // Always expand the thread item the first time we show it
3334 m_frame_delegate_sp.reset (new FrameTreeDelegate());
3337 m_stop_id = process_sp->GetStopID();
3338 m_tid = thread_sp->GetID();
3340 TreeItem t (&item, *m_frame_delegate_sp, false);
3341 size_t num_frames = thread_sp->GetStackFrameCount();
3342 item.Resize (num_frames, t);
3343 for (size_t i=0; i<num_frames; ++i)
3345 item[i].SetUserData(thread_sp.get());
3346 item[i].SetIdentifier(i);
3352 item.ClearChildren();
3356 TreeDelegateItemSelected (TreeItem &item)
3358 ProcessSP process_sp = GetProcess ();
3359 if (process_sp && process_sp->IsAlive())
3361 StateType state = process_sp->GetState();
3362 if (StateIsStoppedState(state, true))
3364 ThreadSP thread_sp = GetThread (item);
3367 ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList();
3368 Mutex::Locker locker (thread_list.GetMutex());
3369 ThreadSP selected_thread_sp = thread_list.GetSelectedThread();
3370 if (selected_thread_sp->GetID() != thread_sp->GetID())
3372 thread_list.SetSelectedThreadByID(thread_sp->GetID());
3382 Debugger &m_debugger;
3383 std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp;
3384 lldb::user_id_t m_tid;
3388 class ThreadsTreeDelegate : public TreeDelegate
3391 ThreadsTreeDelegate (Debugger &debugger) :
3393 m_thread_delegate_sp (),
3394 m_debugger (debugger),
3395 m_stop_id (UINT32_MAX)
3400 ~ThreadsTreeDelegate()
3407 return m_debugger.GetCommandInterpreter().GetExecutionContext().GetProcessSP();
3411 TreeDelegateDrawTreeItem (TreeItem &item, Window &window)
3413 ProcessSP process_sp = GetProcess ();
3414 if (process_sp && process_sp->IsAlive())
3417 ExecutionContext exe_ctx (process_sp);
3418 const char *format = "process ${process.id}{, name = ${process.name}}";
3419 if (Debugger::FormatPrompt (format, NULL, &exe_ctx, NULL, strm))
3422 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
3428 TreeDelegateGenerateChildren (TreeItem &item)
3430 ProcessSP process_sp = GetProcess ();
3431 if (process_sp && process_sp->IsAlive())
3433 StateType state = process_sp->GetState();
3434 if (StateIsStoppedState(state, true))
3436 const uint32_t stop_id = process_sp->GetStopID();
3437 if (m_stop_id == stop_id)
3438 return; // Children are already up to date
3440 m_stop_id = stop_id;
3442 if (!m_thread_delegate_sp)
3444 // Always expand the thread item the first time we show it
3446 m_thread_delegate_sp.reset (new ThreadTreeDelegate(m_debugger));
3449 TreeItem t (&item, *m_thread_delegate_sp, false);
3450 ThreadList &threads = process_sp->GetThreadList();
3451 Mutex::Locker locker (threads.GetMutex());
3452 size_t num_threads = threads.GetSize();
3453 item.Resize (num_threads, t);
3454 for (size_t i=0; i<num_threads; ++i)
3456 item[i].SetIdentifier(threads.GetThreadAtIndex(i)->GetID());
3457 item[i].SetMightHaveChildren(true);
3462 item.ClearChildren();
3466 TreeDelegateItemSelected (TreeItem &item)
3472 std::shared_ptr<ThreadTreeDelegate> m_thread_delegate_sp;
3473 Debugger &m_debugger;
3477 class ValueObjectListDelegate : public WindowDelegate
3480 ValueObjectListDelegate () :
3483 m_selected_row (NULL),
3484 m_selected_row_idx (0),
3485 m_first_visible_row (0),
3492 ValueObjectListDelegate (ValueObjectList &valobj_list) :
3493 m_valobj_list (valobj_list),
3495 m_selected_row (NULL),
3496 m_selected_row_idx (0),
3497 m_first_visible_row (0),
3502 SetValues (valobj_list);
3506 ~ValueObjectListDelegate()
3511 SetValues (ValueObjectList &valobj_list)
3513 m_selected_row = NULL;
3514 m_selected_row_idx = 0;
3515 m_first_visible_row = 0;
3518 m_valobj_list = valobj_list;
3519 const size_t num_values = m_valobj_list.GetSize();
3520 for (size_t i=0; i<num_values; ++i)
3521 m_rows.push_back(Row(m_valobj_list.GetValueObjectAtIndex(i), NULL));
3525 WindowDelegateDraw (Window &window, bool force)
3530 m_max_x = window.GetWidth() - 1;
3531 m_max_y = window.GetHeight() - 1;
3534 window.DrawTitleBox (window.GetName());
3536 const int num_visible_rows = NumVisibleRows();
3537 const int num_rows = CalculateTotalNumberRows (m_rows);
3539 // If we unexpanded while having something selected our
3540 // total number of rows is less than the num visible rows,
3541 // then make sure we show all the rows by setting the first
3542 // visible row accordingly.
3543 if (m_first_visible_row > 0 && num_rows < num_visible_rows)
3544 m_first_visible_row = 0;
3546 // Make sure the selected row is always visible
3547 if (m_selected_row_idx < m_first_visible_row)
3548 m_first_visible_row = m_selected_row_idx;
3549 else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
3550 m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
3552 DisplayRows (window, m_rows, g_options);
3554 window.DeferredRefresh();
3556 // Get the selected row
3557 m_selected_row = GetRowForRowIndex (m_selected_row_idx);
3558 // Keep the cursor on the selected row so the highlight and the cursor
3559 // are always on the same line
3561 window.MoveCursor (m_selected_row->x,
3564 return true; // Drawing handled
3568 WindowDelegateGetKeyHelp ()
3570 static curses::KeyHelp g_source_view_key_help[] = {
3571 { KEY_UP, "Select previous item" },
3572 { KEY_DOWN, "Select next item" },
3573 { KEY_RIGHT, "Expand selected item" },
3574 { KEY_LEFT, "Unexpand selected item or select parent if not expanded" },
3575 { KEY_PPAGE, "Page up" },
3576 { KEY_NPAGE, "Page down" },
3577 { 'A', "Format as annotated address" },
3578 { 'b', "Format as binary" },
3579 { 'B', "Format as hex bytes with ASCII" },
3580 { 'c', "Format as character" },
3581 { 'd', "Format as a signed integer" },
3582 { 'D', "Format selected value using the default format for the type" },
3583 { 'f', "Format as float" },
3584 { 'h', "Show help dialog" },
3585 { 'i', "Format as instructions" },
3586 { 'o', "Format as octal" },
3587 { 'p', "Format as pointer" },
3588 { 's', "Format as C string" },
3589 { 't', "Toggle showing/hiding type names" },
3590 { 'u', "Format as an unsigned integer" },
3591 { 'x', "Format as hex" },
3592 { 'X', "Format as uppercase hex" },
3593 { ' ', "Toggle item expansion" },
3595 { '.', "Page down" },
3598 return g_source_view_key_help;
3602 virtual HandleCharResult
3603 WindowDelegateHandleChar (Window &window, int c)
3621 // Change the format for the currently selected item
3623 m_selected_row->valobj->SetFormat (FormatForChar (c));
3627 // Toggle showing type names
3628 g_options.show_types = !g_options.show_types;
3634 if (m_first_visible_row > 0)
3636 if (static_cast<int>(m_first_visible_row) > m_max_y)
3637 m_first_visible_row -= m_max_y;
3639 m_first_visible_row = 0;
3640 m_selected_row_idx = m_first_visible_row;
3647 if (m_num_rows > static_cast<size_t>(m_max_y))
3649 if (m_first_visible_row + m_max_y < m_num_rows)
3651 m_first_visible_row += m_max_y;
3652 m_selected_row_idx = m_first_visible_row;
3658 if (m_selected_row_idx > 0)
3659 --m_selected_row_idx;
3662 if (m_selected_row_idx + 1 < m_num_rows)
3663 ++m_selected_row_idx;
3669 if (!m_selected_row->expanded)
3670 m_selected_row->Expand();
3677 if (m_selected_row->expanded)
3678 m_selected_row->Unexpand();
3679 else if (m_selected_row->parent)
3680 m_selected_row_idx = m_selected_row->parent->row_idx;
3685 // Toggle expansion state when SPACE is pressed
3688 if (m_selected_row->expanded)
3689 m_selected_row->Unexpand();
3691 m_selected_row->Expand();
3696 window.CreateHelpSubwindow ();
3702 return eKeyNotHandled;
3706 ValueObjectList m_valobj_list;
3707 std::vector<Row> m_rows;
3708 Row *m_selected_row;
3709 uint32_t m_selected_row_idx;
3710 uint32_t m_first_visible_row;
3711 uint32_t m_num_rows;
3718 FormatForChar (int c)
3722 case 'x': return eFormatHex;
3723 case 'X': return eFormatHexUppercase;
3724 case 'o': return eFormatOctal;
3725 case 's': return eFormatCString;
3726 case 'u': return eFormatUnsigned;
3727 case 'd': return eFormatDecimal;
3728 case 'D': return eFormatDefault;
3729 case 'i': return eFormatInstruction;
3730 case 'A': return eFormatAddressInfo;
3731 case 'p': return eFormatPointer;
3732 case 'c': return eFormatChar;
3733 case 'b': return eFormatBinary;
3734 case 'B': return eFormatBytesWithASCII;
3735 case 'f': return eFormatFloat;
3737 return eFormatDefault;
3741 DisplayRowObject (Window &window,
3743 DisplayOptions &options,
3747 ValueObject *valobj = row.valobj.get();
3752 const char *type_name = options.show_types ? valobj->GetTypeName().GetCString() : NULL;
3753 const char *name = valobj->GetName().GetCString();
3754 const char *value = valobj->GetValueAsCString ();
3755 const char *summary = valobj->GetSummaryAsCString ();
3757 window.MoveCursor (row.x, row.y);
3759 row.DrawTree (window);
3762 window.AttributeOn(A_REVERSE);
3764 if (type_name && type_name[0])
3765 window.Printf ("(%s) ", type_name);
3767 if (name && name[0])
3768 window.PutCString(name);
3770 attr_t changd_attr = 0;
3771 if (valobj->GetValueDidChange())
3772 changd_attr = COLOR_PAIR(5) | A_BOLD;
3774 if (value && value[0])
3776 window.PutCString(" = ");
3778 window.AttributeOn(changd_attr);
3779 window.PutCString (value);
3781 window.AttributeOff(changd_attr);
3784 if (summary && summary[0])
3786 window.PutChar(' ');
3788 window.AttributeOn(changd_attr);
3789 window.PutCString(summary);
3791 window.AttributeOff(changd_attr);
3795 window.AttributeOff (A_REVERSE);
3800 DisplayRows (Window &window,
3801 std::vector<Row> &rows,
3802 DisplayOptions &options)
3807 bool window_is_active = window.IsActive();
3808 for (auto &row : rows)
3810 const bool last_child = row.parent && &rows[rows.size()-1] == &row;
3811 // Save the row index in each Row structure
3812 row.row_idx = m_num_rows;
3813 if ((m_num_rows >= m_first_visible_row) &&
3814 ((m_num_rows - m_first_visible_row) < static_cast<size_t>(NumVisibleRows())))
3817 row.y = m_num_rows - m_first_visible_row + 1;
3818 if (DisplayRowObject (window,
3821 window_is_active && m_num_rows == m_selected_row_idx,
3839 if (row.expanded && !row.children.empty())
3841 DisplayRows (window,
3849 CalculateTotalNumberRows (const std::vector<Row> &rows)
3852 for (const auto &row : rows)
3856 row_count += CalculateTotalNumberRows(row.children);
3861 GetRowForRowIndexImpl (std::vector<Row> &rows, size_t &row_index)
3863 for (auto &row : rows)
3870 if (row.expanded && !row.children.empty())
3872 Row *result = GetRowForRowIndexImpl (row.children, row_index);
3882 GetRowForRowIndex (size_t row_index)
3884 return GetRowForRowIndexImpl (m_rows, row_index);
3888 NumVisibleRows () const
3890 return m_max_y - m_min_y;
3893 static DisplayOptions g_options;
3896 class FrameVariablesWindowDelegate : public ValueObjectListDelegate
3899 FrameVariablesWindowDelegate (Debugger &debugger) :
3900 ValueObjectListDelegate (),
3901 m_debugger (debugger),
3902 m_frame_block (NULL)
3907 ~FrameVariablesWindowDelegate()
3911 virtual const char *
3912 WindowDelegateGetHelpText ()
3914 return "Frame variable window keyboard shortcuts:";
3918 WindowDelegateDraw (Window &window, bool force)
3920 ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext());
3921 Process *process = exe_ctx.GetProcessPtr();
3922 Block *frame_block = NULL;
3923 StackFrame *frame = NULL;
3927 StateType state = process->GetState();
3928 if (StateIsStoppedState(state, true))
3930 frame = exe_ctx.GetFramePtr();
3932 frame_block = frame->GetFrameBlock ();
3934 else if (StateIsRunningState(state))
3936 return true; // Don't do any updating when we are running
3940 ValueObjectList local_values;
3943 // Only update the variables if they have changed
3944 if (m_frame_block != frame_block)
3946 m_frame_block = frame_block;
3948 VariableList *locals = frame->GetVariableList(true);
3951 const DynamicValueType use_dynamic = eDynamicDontRunTarget;
3952 const size_t num_locals = locals->GetSize();
3953 for (size_t i=0; i<num_locals; ++i)
3954 local_values.Append(frame->GetValueObjectForFrameVariable (locals->GetVariableAtIndex(i), use_dynamic));
3955 // Update the values
3956 SetValues(local_values);
3962 m_frame_block = NULL;
3963 // Update the values with an empty list if there is no frame
3964 SetValues(local_values);
3967 return ValueObjectListDelegate::WindowDelegateDraw (window, force);
3972 Debugger &m_debugger;
3973 Block *m_frame_block;
3977 class RegistersWindowDelegate : public ValueObjectListDelegate
3980 RegistersWindowDelegate (Debugger &debugger) :
3981 ValueObjectListDelegate (),
3982 m_debugger (debugger)
3987 ~RegistersWindowDelegate()
3991 virtual const char *
3992 WindowDelegateGetHelpText ()
3994 return "Register window keyboard shortcuts:";
3998 WindowDelegateDraw (Window &window, bool force)
4000 ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext());
4001 StackFrame *frame = exe_ctx.GetFramePtr();
4003 ValueObjectList value_list;
4006 if (frame->GetStackID() != m_stack_id)
4008 m_stack_id = frame->GetStackID();
4009 RegisterContextSP reg_ctx (frame->GetRegisterContext());
4012 const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
4013 for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx)
4015 value_list.Append(ValueObjectRegisterSet::Create (frame, reg_ctx, set_idx));
4018 SetValues(value_list);
4023 Process *process = exe_ctx.GetProcessPtr();
4024 if (process && process->IsAlive())
4025 return true; // Don't do any updating if we are running
4028 // Update the values with an empty list if there
4029 // is no process or the process isn't alive anymore
4030 SetValues(value_list);
4033 return ValueObjectListDelegate::WindowDelegateDraw (window, force);
4037 Debugger &m_debugger;
4042 CursesKeyToCString (int ch)
4044 static char g_desc[32];
4045 if (ch >= KEY_F0 && ch < KEY_F0 + 64)
4047 snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0);
4052 case KEY_DOWN: return "down";
4053 case KEY_UP: return "up";
4054 case KEY_LEFT: return "left";
4055 case KEY_RIGHT: return "right";
4056 case KEY_HOME: return "home";
4057 case KEY_BACKSPACE: return "backspace";
4058 case KEY_DL: return "delete-line";
4059 case KEY_IL: return "insert-line";
4060 case KEY_DC: return "delete-char";
4061 case KEY_IC: return "insert-char";
4062 case KEY_CLEAR: return "clear";
4063 case KEY_EOS: return "clear-to-eos";
4064 case KEY_EOL: return "clear-to-eol";
4065 case KEY_SF: return "scroll-forward";
4066 case KEY_SR: return "scroll-backward";
4067 case KEY_NPAGE: return "page-down";
4068 case KEY_PPAGE: return "page-up";
4069 case KEY_STAB: return "set-tab";
4070 case KEY_CTAB: return "clear-tab";
4071 case KEY_CATAB: return "clear-all-tabs";
4072 case KEY_ENTER: return "enter";
4073 case KEY_PRINT: return "print";
4074 case KEY_LL: return "lower-left key";
4075 case KEY_A1: return "upper left of keypad";
4076 case KEY_A3: return "upper right of keypad";
4077 case KEY_B2: return "center of keypad";
4078 case KEY_C1: return "lower left of keypad";
4079 case KEY_C3: return "lower right of keypad";
4080 case KEY_BTAB: return "back-tab key";
4081 case KEY_BEG: return "begin key";
4082 case KEY_CANCEL: return "cancel key";
4083 case KEY_CLOSE: return "close key";
4084 case KEY_COMMAND: return "command key";
4085 case KEY_COPY: return "copy key";
4086 case KEY_CREATE: return "create key";
4087 case KEY_END: return "end key";
4088 case KEY_EXIT: return "exit key";
4089 case KEY_FIND: return "find key";
4090 case KEY_HELP: return "help key";
4091 case KEY_MARK: return "mark key";
4092 case KEY_MESSAGE: return "message key";
4093 case KEY_MOVE: return "move key";
4094 case KEY_NEXT: return "next key";
4095 case KEY_OPEN: return "open key";
4096 case KEY_OPTIONS: return "options key";
4097 case KEY_PREVIOUS: return "previous key";
4098 case KEY_REDO: return "redo key";
4099 case KEY_REFERENCE: return "reference key";
4100 case KEY_REFRESH: return "refresh key";
4101 case KEY_REPLACE: return "replace key";
4102 case KEY_RESTART: return "restart key";
4103 case KEY_RESUME: return "resume key";
4104 case KEY_SAVE: return "save key";
4105 case KEY_SBEG: return "shifted begin key";
4106 case KEY_SCANCEL: return "shifted cancel key";
4107 case KEY_SCOMMAND: return "shifted command key";
4108 case KEY_SCOPY: return "shifted copy key";
4109 case KEY_SCREATE: return "shifted create key";
4110 case KEY_SDC: return "shifted delete-character key";
4111 case KEY_SDL: return "shifted delete-line key";
4112 case KEY_SELECT: return "select key";
4113 case KEY_SEND: return "shifted end key";
4114 case KEY_SEOL: return "shifted clear-to-end-of-line key";
4115 case KEY_SEXIT: return "shifted exit key";
4116 case KEY_SFIND: return "shifted find key";
4117 case KEY_SHELP: return "shifted help key";
4118 case KEY_SHOME: return "shifted home key";
4119 case KEY_SIC: return "shifted insert-character key";
4120 case KEY_SLEFT: return "shifted left-arrow key";
4121 case KEY_SMESSAGE: return "shifted message key";
4122 case KEY_SMOVE: return "shifted move key";
4123 case KEY_SNEXT: return "shifted next key";
4124 case KEY_SOPTIONS: return "shifted options key";
4125 case KEY_SPREVIOUS: return "shifted previous key";
4126 case KEY_SPRINT: return "shifted print key";
4127 case KEY_SREDO: return "shifted redo key";
4128 case KEY_SREPLACE: return "shifted replace key";
4129 case KEY_SRIGHT: return "shifted right-arrow key";
4130 case KEY_SRSUME: return "shifted resume key";
4131 case KEY_SSAVE: return "shifted save key";
4132 case KEY_SSUSPEND: return "shifted suspend key";
4133 case KEY_SUNDO: return "shifted undo key";
4134 case KEY_SUSPEND: return "suspend key";
4135 case KEY_UNDO: return "undo key";
4136 case KEY_MOUSE: return "Mouse event has occurred";
4137 case KEY_RESIZE: return "Terminal resize event";
4138 case KEY_EVENT: return "We were interrupted by an event";
4139 case KEY_RETURN: return "return";
4140 case ' ': return "space";
4141 case '\t': return "tab";
4142 case KEY_ESCAPE: return "escape";
4145 snprintf(g_desc, sizeof(g_desc), "%c", ch);
4147 snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch);
4153 HelpDialogDelegate::HelpDialogDelegate (const char *text, KeyHelp *key_help_array) :
4155 m_first_visible_line (0)
4157 if (text && text[0])
4159 m_text.SplitIntoLines(text);
4160 m_text.AppendString("");
4164 for (KeyHelp *key = key_help_array; key->ch; ++key)
4166 StreamString key_description;
4167 key_description.Printf("%10s - %s", CursesKeyToCString(key->ch), key->description);
4168 m_text.AppendString(std::move(key_description.GetString()));
4173 HelpDialogDelegate::~HelpDialogDelegate()
4178 HelpDialogDelegate::WindowDelegateDraw (Window &window, bool force)
4181 const int window_height = window.GetHeight();
4184 const int min_y = y;
4185 const int max_y = window_height - 1 - y;
4186 const size_t num_visible_lines = max_y - min_y + 1;
4187 const size_t num_lines = m_text.GetSize();
4188 const char *bottom_message;
4189 if (num_lines <= num_visible_lines)
4190 bottom_message = "Press any key to exit";
4192 bottom_message = "Use arrows to scroll, any other key to exit";
4193 window.DrawTitleBox(window.GetName(), bottom_message);
4196 window.MoveCursor(x, y);
4197 window.PutCStringTruncated(m_text.GetStringAtIndex(m_first_visible_line + y - min_y), 1);
4204 HelpDialogDelegate::WindowDelegateHandleChar (Window &window, int key)
4207 const size_t num_lines = m_text.GetSize();
4208 const size_t num_visible_lines = window.GetHeight() - 2;
4210 if (num_lines <= num_visible_lines)
4213 // If we have all lines visible and don't need scrolling, then any
4214 // key press will cause us to exit
4221 if (m_first_visible_line > 0)
4222 --m_first_visible_line;
4226 if (m_first_visible_line + num_visible_lines < num_lines)
4227 ++m_first_visible_line;
4232 if (m_first_visible_line > 0)
4234 if (static_cast<size_t>(m_first_visible_line) >= num_visible_lines)
4235 m_first_visible_line -= num_visible_lines;
4237 m_first_visible_line = 0;
4242 if (m_first_visible_line + num_visible_lines < num_lines)
4244 m_first_visible_line += num_visible_lines;
4245 if (static_cast<size_t>(m_first_visible_line) > num_lines)
4246 m_first_visible_line = num_lines - num_visible_lines;
4255 window.GetParent()->RemoveSubWindow(&window);
4259 class ApplicationDelegate :
4260 public WindowDelegate,
4270 eMenuID_TargetCreate,
4271 eMenuID_TargetDelete,
4274 eMenuID_ProcessAttach,
4275 eMenuID_ProcessDetach,
4276 eMenuID_ProcessLaunch,
4277 eMenuID_ProcessContinue,
4278 eMenuID_ProcessHalt,
4279 eMenuID_ProcessKill,
4282 eMenuID_ThreadStepIn,
4283 eMenuID_ThreadStepOver,
4284 eMenuID_ThreadStepOut,
4287 eMenuID_ViewBacktrace,
4288 eMenuID_ViewRegisters,
4290 eMenuID_ViewVariables,
4296 ApplicationDelegate (Application &app, Debugger &debugger) :
4300 m_debugger (debugger)
4305 ~ApplicationDelegate ()
4309 WindowDelegateDraw (Window &window, bool force)
4311 return false; // Drawing not handled, let standard window drawing happen
4314 virtual HandleCharResult
4315 WindowDelegateHandleChar (Window &window, int key)
4320 window.SelectNextWindowAsActive();
4324 window.CreateHelpSubwindow();
4328 return eQuitApplication;
4333 return eKeyNotHandled;
4337 virtual const char *
4338 WindowDelegateGetHelpText ()
4340 return "Welcome to the LLDB curses GUI.\n\n"
4341 "Press the TAB key to change the selected view.\n"
4342 "Each view has its own keyboard shortcuts, press 'h' to open a dialog to display them.\n\n"
4343 "Common key bindings for all views:";
4347 WindowDelegateGetKeyHelp ()
4349 static curses::KeyHelp g_source_view_key_help[] = {
4350 { '\t', "Select next view" },
4351 { 'h', "Show help dialog with view specific key bindings" },
4353 { '.', "Page down" },
4354 { KEY_UP, "Select previous" },
4355 { KEY_DOWN, "Select next" },
4356 { KEY_LEFT, "Unexpand or select parent" },
4357 { KEY_RIGHT, "Expand" },
4358 { KEY_PPAGE, "Page up" },
4359 { KEY_NPAGE, "Page down" },
4362 return g_source_view_key_help;
4365 virtual MenuActionResult
4366 MenuDelegateAction (Menu &menu)
4368 switch (menu.GetIdentifier())
4370 case eMenuID_ThreadStepIn:
4372 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4373 if (exe_ctx.HasThreadScope())
4375 Process *process = exe_ctx.GetProcessPtr();
4376 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4377 exe_ctx.GetThreadRef().StepIn(true);
4380 return MenuActionResult::Handled;
4382 case eMenuID_ThreadStepOut:
4384 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4385 if (exe_ctx.HasThreadScope())
4387 Process *process = exe_ctx.GetProcessPtr();
4388 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4389 exe_ctx.GetThreadRef().StepOut();
4392 return MenuActionResult::Handled;
4394 case eMenuID_ThreadStepOver:
4396 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4397 if (exe_ctx.HasThreadScope())
4399 Process *process = exe_ctx.GetProcessPtr();
4400 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4401 exe_ctx.GetThreadRef().StepOver(true);
4404 return MenuActionResult::Handled;
4406 case eMenuID_ProcessContinue:
4408 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4409 if (exe_ctx.HasProcessScope())
4411 Process *process = exe_ctx.GetProcessPtr();
4412 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4416 return MenuActionResult::Handled;
4418 case eMenuID_ProcessKill:
4420 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4421 if (exe_ctx.HasProcessScope())
4423 Process *process = exe_ctx.GetProcessPtr();
4424 if (process && process->IsAlive())
4428 return MenuActionResult::Handled;
4430 case eMenuID_ProcessHalt:
4432 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4433 if (exe_ctx.HasProcessScope())
4435 Process *process = exe_ctx.GetProcessPtr();
4436 if (process && process->IsAlive())
4440 return MenuActionResult::Handled;
4442 case eMenuID_ProcessDetach:
4444 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4445 if (exe_ctx.HasProcessScope())
4447 Process *process = exe_ctx.GetProcessPtr();
4448 if (process && process->IsAlive())
4449 process->Detach(false);
4452 return MenuActionResult::Handled;
4454 case eMenuID_Process:
4456 // Populate the menu with all of the threads if the process is stopped when
4457 // the Process menu gets selected and is about to display its submenu.
4458 Menus &submenus = menu.GetSubmenus();
4459 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4460 Process *process = exe_ctx.GetProcessPtr();
4461 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4463 if (submenus.size() == 7)
4464 menu.AddSubmenu (MenuSP (new Menu(Menu::Type::Separator)));
4465 else if (submenus.size() > 8)
4466 submenus.erase (submenus.begin() + 8, submenus.end());
4468 ThreadList &threads = process->GetThreadList();
4469 Mutex::Locker locker (threads.GetMutex());
4470 size_t num_threads = threads.GetSize();
4471 for (size_t i=0; i<num_threads; ++i)
4473 ThreadSP thread_sp = threads.GetThreadAtIndex(i);
4474 char menu_char = '\0';
4476 menu_char = '1' + i;
4477 StreamString thread_menu_title;
4478 thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID());
4479 const char *thread_name = thread_sp->GetName();
4480 if (thread_name && thread_name[0])
4481 thread_menu_title.Printf (" %s", thread_name);
4484 const char *queue_name = thread_sp->GetQueueName();
4485 if (queue_name && queue_name[0])
4486 thread_menu_title.Printf (" %s", queue_name);
4488 menu.AddSubmenu (MenuSP (new Menu(thread_menu_title.GetString().c_str(), NULL, menu_char, thread_sp->GetID())));
4491 else if (submenus.size() > 7)
4493 // Remove the separator and any other thread submenu items
4494 // that were previously added
4495 submenus.erase (submenus.begin() + 7, submenus.end());
4497 // Since we are adding and removing items we need to recalculate the name lengths
4498 menu.RecalculateNameLengths();
4500 return MenuActionResult::Handled;
4502 case eMenuID_ViewVariables:
4504 WindowSP main_window_sp = m_app.GetMainWindow();
4505 WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
4506 WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
4507 WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
4508 const Rect source_bounds = source_window_sp->GetBounds();
4510 if (variables_window_sp)
4512 const Rect variables_bounds = variables_window_sp->GetBounds();
4514 main_window_sp->RemoveSubWindow(variables_window_sp.get());
4516 if (registers_window_sp)
4518 // We have a registers window, so give all the area back to the registers window
4519 Rect registers_bounds = variables_bounds;
4520 registers_bounds.size.width = source_bounds.size.width;
4521 registers_window_sp->SetBounds(registers_bounds);
4525 // We have no registers window showing so give the bottom
4526 // area back to the source view
4527 source_window_sp->Resize (source_bounds.size.width,
4528 source_bounds.size.height + variables_bounds.size.height);
4533 Rect new_variables_rect;
4534 if (registers_window_sp)
4536 // We have a registers window so split the area of the registers
4537 // window into two columns where the left hand side will be the
4538 // variables and the right hand side will be the registers
4539 const Rect variables_bounds = registers_window_sp->GetBounds();
4540 Rect new_registers_rect;
4541 variables_bounds.VerticalSplitPercentage (0.50, new_variables_rect, new_registers_rect);
4542 registers_window_sp->SetBounds (new_registers_rect);
4546 // No variables window, grab the bottom part of the source window
4547 Rect new_source_rect;
4548 source_bounds.HorizontalSplitPercentage (0.70, new_source_rect, new_variables_rect);
4549 source_window_sp->SetBounds (new_source_rect);
4551 WindowSP new_window_sp = main_window_sp->CreateSubWindow ("Variables",
4554 new_window_sp->SetDelegate (WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
4558 return MenuActionResult::Handled;
4560 case eMenuID_ViewRegisters:
4562 WindowSP main_window_sp = m_app.GetMainWindow();
4563 WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
4564 WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
4565 WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
4566 const Rect source_bounds = source_window_sp->GetBounds();
4568 if (registers_window_sp)
4570 if (variables_window_sp)
4572 const Rect variables_bounds = variables_window_sp->GetBounds();
4574 // We have a variables window, so give all the area back to the variables window
4575 variables_window_sp->Resize (variables_bounds.size.width + registers_window_sp->GetWidth(),
4576 variables_bounds.size.height);
4580 // We have no variables window showing so give the bottom
4581 // area back to the source view
4582 source_window_sp->Resize (source_bounds.size.width,
4583 source_bounds.size.height + registers_window_sp->GetHeight());
4585 main_window_sp->RemoveSubWindow(registers_window_sp.get());
4590 if (variables_window_sp)
4592 // We have a variables window, split it into two columns
4593 // where the left hand side will be the variables and the
4594 // right hand side will be the registers
4595 const Rect variables_bounds = variables_window_sp->GetBounds();
4597 variables_bounds.VerticalSplitPercentage (0.50, new_vars_rect, new_regs_rect);
4598 variables_window_sp->SetBounds (new_vars_rect);
4602 // No registers window, grab the bottom part of the source window
4603 Rect new_source_rect;
4604 source_bounds.HorizontalSplitPercentage (0.70, new_source_rect, new_regs_rect);
4605 source_window_sp->SetBounds (new_source_rect);
4607 WindowSP new_window_sp = main_window_sp->CreateSubWindow ("Registers",
4610 new_window_sp->SetDelegate (WindowDelegateSP(new RegistersWindowDelegate(m_debugger)));
4614 return MenuActionResult::Handled;
4616 case eMenuID_HelpGUIHelp:
4617 m_app.GetMainWindow ()->CreateHelpSubwindow();
4618 return MenuActionResult::Handled;
4624 return MenuActionResult::NotHandled;
4628 Debugger &m_debugger;
4632 class StatusBarWindowDelegate : public WindowDelegate
4635 StatusBarWindowDelegate (Debugger &debugger) :
4636 m_debugger (debugger)
4641 ~StatusBarWindowDelegate ()
4645 WindowDelegateDraw (Window &window, bool force)
4647 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4648 Process *process = exe_ctx.GetProcessPtr();
4649 Thread *thread = exe_ctx.GetThreadPtr();
4650 StackFrame *frame = exe_ctx.GetFramePtr();
4652 window.SetBackground(2);
4653 window.MoveCursor (0, 0);
4656 const StateType state = process->GetState();
4657 window.Printf ("Process: %5" PRIu64 " %10s", process->GetID(), StateAsCString(state));
4659 if (StateIsStoppedState(state, true))
4662 const char *format = "Thread: ${thread.id%tid}";
4663 if (thread && Debugger::FormatPrompt (format, NULL, &exe_ctx, NULL, strm))
4665 window.MoveCursor (40, 0);
4666 window.PutCStringTruncated(strm.GetString().c_str(), 1);
4669 window.MoveCursor (60, 0);
4671 window.Printf ("Frame: %3u PC = 0x%16.16" PRIx64, frame->GetFrameIndex(), frame->GetFrameCodeAddress().GetOpcodeLoadAddress (exe_ctx.GetTargetPtr()));
4673 else if (state == eStateExited)
4675 const char *exit_desc = process->GetExitDescription();
4676 const int exit_status = process->GetExitStatus();
4677 if (exit_desc && exit_desc[0])
4678 window.Printf (" with status = %i (%s)", exit_status, exit_desc);
4680 window.Printf (" with status = %i", exit_status);
4683 window.DeferredRefresh();
4688 Debugger &m_debugger;
4691 class SourceFileWindowDelegate : public WindowDelegate
4694 SourceFileWindowDelegate (Debugger &debugger) :
4696 m_debugger (debugger),
4699 m_disassembly_scope (NULL),
4700 m_disassembly_sp (),
4701 m_disassembly_range (),
4704 m_selected_line (0),
4707 m_frame_idx (UINT32_MAX),
4708 m_first_visible_line (0),
4717 ~SourceFileWindowDelegate()
4722 Update (const SymbolContext &sc)
4728 NumVisibleLines () const
4730 return m_max_y - m_min_y;
4733 virtual const char *
4734 WindowDelegateGetHelpText ()
4736 return "Source/Disassembly window keyboard shortcuts:";
4740 WindowDelegateGetKeyHelp ()
4742 static curses::KeyHelp g_source_view_key_help[] = {
4743 { KEY_RETURN, "Run to selected line with one shot breakpoint" },
4744 { KEY_UP, "Select previous source line" },
4745 { KEY_DOWN, "Select next source line" },
4746 { KEY_PPAGE, "Page up" },
4747 { KEY_NPAGE, "Page down" },
4748 { 'b', "Set breakpoint on selected source/disassembly line" },
4749 { 'c', "Continue process" },
4750 { 'd', "Detach and resume process" },
4751 { 'D', "Detach with process suspended" },
4752 { 'h', "Show help dialog" },
4753 { 'k', "Kill process" },
4754 { 'n', "Step over (source line)" },
4755 { 'N', "Step over (single instruction)" },
4756 { 'o', "Step out" },
4757 { 's', "Step in (source line)" },
4758 { 'S', "Step in (single instruction)" },
4760 { '.', "Page down" },
4763 return g_source_view_key_help;
4767 WindowDelegateDraw (Window &window, bool force)
4769 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4770 Process *process = exe_ctx.GetProcessPtr();
4771 Thread *thread = NULL;
4773 bool update_location = false;
4776 StateType state = process->GetState();
4777 if (StateIsStoppedState(state, true))
4779 // We are stopped, so it is ok to
4780 update_location = true;
4786 m_max_x = window.GetMaxX()-1;
4787 m_max_y = window.GetMaxY()-1;
4789 const uint32_t num_visible_lines = NumVisibleLines();
4790 StackFrameSP frame_sp;
4791 bool set_selected_line_to_pc = false;
4793 if (update_location)
4795 const bool process_alive = process ? process->IsAlive() : false;
4796 bool thread_changed = false;
4799 thread = exe_ctx.GetThreadPtr();
4802 frame_sp = thread->GetSelectedFrame();
4803 auto tid = thread->GetID();
4804 thread_changed = tid != m_tid;
4809 if (m_tid != LLDB_INVALID_THREAD_ID)
4811 thread_changed = true;
4812 m_tid = LLDB_INVALID_THREAD_ID;
4816 const uint32_t stop_id = process ? process->GetStopID() : 0;
4817 const bool stop_id_changed = stop_id != m_stop_id;
4818 bool frame_changed = false;
4819 m_stop_id = stop_id;
4823 m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
4826 m_title.Printf("%s", m_sc.module_sp->GetFileSpec().GetFilename().GetCString());
4827 ConstString func_name = m_sc.GetFunctionName();
4829 m_title.Printf("`%s", func_name.GetCString());
4831 const uint32_t frame_idx = frame_sp->GetFrameIndex();
4832 frame_changed = frame_idx != m_frame_idx;
4833 m_frame_idx = frame_idx;
4838 frame_changed = m_frame_idx != UINT32_MAX;
4839 m_frame_idx = UINT32_MAX;
4842 const bool context_changed = thread_changed || frame_changed || stop_id_changed;
4846 if (m_sc.line_entry.IsValid())
4848 m_pc_line = m_sc.line_entry.line;
4849 if (m_pc_line != UINT32_MAX)
4850 --m_pc_line; // Convert to zero based line number...
4851 // Update the selected line if the stop ID changed...
4852 if (context_changed)
4853 m_selected_line = m_pc_line;
4855 if (m_file_sp && m_file_sp->FileSpecMatches(m_sc.line_entry.file))
4857 // Same file, nothing to do, we should either have the
4858 // lines or not (source file missing)
4859 if (m_selected_line >= static_cast<size_t>(m_first_visible_line))
4861 if (m_selected_line >= m_first_visible_line + num_visible_lines)
4862 m_first_visible_line = m_selected_line - 10;
4866 if (m_selected_line > 10)
4867 m_first_visible_line = m_selected_line - 10;
4869 m_first_visible_line = 0;
4874 // File changed, set selected line to the line with the PC
4875 m_selected_line = m_pc_line;
4876 m_file_sp = m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file);
4879 const size_t num_lines = m_file_sp->GetNumLines();
4880 int m_line_width = 1;
4881 for (size_t n = num_lines; n >= 10; n = n / 10)
4884 snprintf (m_line_format, sizeof(m_line_format), " %%%iu ", m_line_width);
4885 if (num_lines < num_visible_lines || m_selected_line < num_visible_lines)
4886 m_first_visible_line = 0;
4888 m_first_visible_line = m_selected_line - 10;
4897 if (!m_file_sp || m_file_sp->GetNumLines() == 0)
4900 bool prefer_file_cache = false;
4903 if (m_disassembly_scope != m_sc.function)
4905 m_disassembly_scope = m_sc.function;
4906 m_disassembly_sp = m_sc.function->GetInstructions (exe_ctx, NULL, prefer_file_cache);
4907 if (m_disassembly_sp)
4909 set_selected_line_to_pc = true;
4910 m_disassembly_range = m_sc.function->GetAddressRange();
4914 m_disassembly_range.Clear();
4919 set_selected_line_to_pc = context_changed;
4922 else if (m_sc.symbol)
4924 if (m_disassembly_scope != m_sc.symbol)
4926 m_disassembly_scope = m_sc.symbol;
4927 m_disassembly_sp = m_sc.symbol->GetInstructions (exe_ctx, NULL, prefer_file_cache);
4928 if (m_disassembly_sp)
4930 set_selected_line_to_pc = true;
4931 m_disassembly_range.GetBaseAddress() = m_sc.symbol->GetAddress();
4932 m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize());
4936 m_disassembly_range.Clear();
4941 set_selected_line_to_pc = context_changed;
4948 m_pc_line = UINT32_MAX;
4952 const int window_width = window.GetWidth();
4954 window.DrawTitleBox ("Sources");
4955 if (!m_title.GetString().empty())
4957 window.AttributeOn(A_REVERSE);
4958 window.MoveCursor(1, 1);
4959 window.PutChar(' ');
4960 window.PutCStringTruncated(m_title.GetString().c_str(), 1);
4961 int x = window.GetCursorX();
4962 if (x < window_width - 1)
4964 window.Printf ("%*s", window_width - x - 1, "");
4966 window.AttributeOff(A_REVERSE);
4969 Target *target = exe_ctx.GetTargetPtr();
4970 const size_t num_source_lines = GetNumSourceLines();
4971 if (num_source_lines > 0)
4974 BreakpointLines bp_lines;
4977 BreakpointList &bp_list = target->GetBreakpointList();
4978 const size_t num_bps = bp_list.GetSize();
4979 for (size_t bp_idx=0; bp_idx<num_bps; ++bp_idx)
4981 BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
4982 const size_t num_bps_locs = bp_sp->GetNumLocations();
4983 for (size_t bp_loc_idx=0; bp_loc_idx<num_bps_locs; ++bp_loc_idx)
4985 BreakpointLocationSP bp_loc_sp = bp_sp->GetLocationAtIndex(bp_loc_idx);
4986 LineEntry bp_loc_line_entry;
4987 if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry (bp_loc_line_entry))
4989 if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file)
4991 bp_lines.insert(bp_loc_line_entry.line);
4998 const attr_t selected_highlight_attr = A_REVERSE;
4999 const attr_t pc_highlight_attr = COLOR_PAIR(1);
5001 for (size_t i=0; i<num_visible_lines; ++i)
5003 const uint32_t curr_line = m_first_visible_line + i;
5004 if (curr_line < num_source_lines)
5006 const int line_y = m_min_y+i;
5007 window.MoveCursor(1, line_y);
5008 const bool is_pc_line = curr_line == m_pc_line;
5009 const bool line_is_selected = m_selected_line == curr_line;
5010 // Highlight the line as the PC line first, then if the selected line
5011 // isn't the same as the PC line, highlight it differently
5012 attr_t highlight_attr = 0;
5015 highlight_attr = pc_highlight_attr;
5016 else if (line_is_selected)
5017 highlight_attr = selected_highlight_attr;
5019 if (bp_lines.find(curr_line+1) != bp_lines.end())
5020 bp_attr = COLOR_PAIR(2);
5023 window.AttributeOn(bp_attr);
5025 window.Printf (m_line_format, curr_line + 1);
5028 window.AttributeOff(bp_attr);
5030 window.PutChar(ACS_VLINE);
5031 // Mark the line with the PC with a diamond
5033 window.PutChar(ACS_DIAMOND);
5035 window.PutChar(' ');
5038 window.AttributeOn(highlight_attr);
5039 const uint32_t line_len = m_file_sp->GetLineLength(curr_line + 1, false);
5041 window.PutCString(m_file_sp->PeekLineData(curr_line + 1), line_len);
5043 if (is_pc_line && frame_sp && frame_sp->GetConcreteFrameIndex() == 0)
5045 StopInfoSP stop_info_sp;
5047 stop_info_sp = thread->GetStopInfo();
5050 const char *stop_description = stop_info_sp->GetDescription();
5051 if (stop_description && stop_description[0])
5053 size_t stop_description_len = strlen(stop_description);
5054 int desc_x = window_width - stop_description_len - 16;
5055 window.Printf ("%*s", desc_x - window.GetCursorX(), "");
5056 //window.MoveCursor(window_width - stop_description_len - 15, line_y);
5057 window.Printf ("<<< Thread %u: %s ", thread->GetIndexID(), stop_description);
5062 window.Printf ("%*s", window_width - window.GetCursorX() - 1, "");
5066 window.AttributeOff(highlight_attr);
5077 size_t num_disassembly_lines = GetNumDisassemblyLines();
5078 if (num_disassembly_lines > 0)
5080 // Display disassembly
5081 BreakpointAddrs bp_file_addrs;
5082 Target *target = exe_ctx.GetTargetPtr();
5085 BreakpointList &bp_list = target->GetBreakpointList();
5086 const size_t num_bps = bp_list.GetSize();
5087 for (size_t bp_idx=0; bp_idx<num_bps; ++bp_idx)
5089 BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
5090 const size_t num_bps_locs = bp_sp->GetNumLocations();
5091 for (size_t bp_loc_idx=0; bp_loc_idx<num_bps_locs; ++bp_loc_idx)
5093 BreakpointLocationSP bp_loc_sp = bp_sp->GetLocationAtIndex(bp_loc_idx);
5094 LineEntry bp_loc_line_entry;
5095 const lldb::addr_t file_addr = bp_loc_sp->GetAddress().GetFileAddress();
5096 if (file_addr != LLDB_INVALID_ADDRESS)
5098 if (m_disassembly_range.ContainsFileAddress(file_addr))
5099 bp_file_addrs.insert(file_addr);
5105 const attr_t selected_highlight_attr = A_REVERSE;
5106 const attr_t pc_highlight_attr = COLOR_PAIR(1);
5110 InstructionList &insts = m_disassembly_sp->GetInstructionList();
5114 pc_address = frame_sp->GetFrameCodeAddress();
5115 const uint32_t pc_idx = pc_address.IsValid() ? insts.GetIndexOfInstructionAtAddress (pc_address) : UINT32_MAX;
5116 if (set_selected_line_to_pc)
5118 m_selected_line = pc_idx;
5121 const uint32_t non_visible_pc_offset = (num_visible_lines / 5);
5122 if (static_cast<size_t>(m_first_visible_line) >= num_disassembly_lines)
5123 m_first_visible_line = 0;
5125 if (pc_idx < num_disassembly_lines)
5127 if (pc_idx < static_cast<uint32_t>(m_first_visible_line) ||
5128 pc_idx >= m_first_visible_line + num_visible_lines)
5129 m_first_visible_line = pc_idx - non_visible_pc_offset;
5132 for (size_t i=0; i<num_visible_lines; ++i)
5134 const uint32_t inst_idx = m_first_visible_line + i;
5135 Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get();
5139 const int line_y = m_min_y+i;
5140 window.MoveCursor(1, line_y);
5141 const bool is_pc_line = frame_sp && inst_idx == pc_idx;
5142 const bool line_is_selected = m_selected_line == inst_idx;
5143 // Highlight the line as the PC line first, then if the selected line
5144 // isn't the same as the PC line, highlight it differently
5145 attr_t highlight_attr = 0;
5148 highlight_attr = pc_highlight_attr;
5149 else if (line_is_selected)
5150 highlight_attr = selected_highlight_attr;
5152 if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) != bp_file_addrs.end())
5153 bp_attr = COLOR_PAIR(2);
5156 window.AttributeOn(bp_attr);
5158 window.Printf (" 0x%16.16llx ",
5159 static_cast<unsigned long long>(inst->GetAddress().GetLoadAddress(target)));
5162 window.AttributeOff(bp_attr);
5164 window.PutChar(ACS_VLINE);
5165 // Mark the line with the PC with a diamond
5167 window.PutChar(ACS_DIAMOND);
5169 window.PutChar(' ');
5172 window.AttributeOn(highlight_attr);
5174 const char *mnemonic = inst->GetMnemonic(&exe_ctx);
5175 const char *operands = inst->GetOperands(&exe_ctx);
5176 const char *comment = inst->GetComment(&exe_ctx);
5178 if (mnemonic && mnemonic[0] == '\0')
5180 if (operands && operands[0] == '\0')
5182 if (comment && comment[0] == '\0')
5187 if (mnemonic && operands && comment)
5188 strm.Printf ("%-8s %-25s ; %s", mnemonic, operands, comment);
5189 else if (mnemonic && operands)
5190 strm.Printf ("%-8s %s", mnemonic, operands);
5192 strm.Printf ("%s", mnemonic);
5195 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
5197 if (is_pc_line && frame_sp && frame_sp->GetConcreteFrameIndex() == 0)
5199 StopInfoSP stop_info_sp;
5201 stop_info_sp = thread->GetStopInfo();
5204 const char *stop_description = stop_info_sp->GetDescription();
5205 if (stop_description && stop_description[0])
5207 size_t stop_description_len = strlen(stop_description);
5208 int desc_x = window_width - stop_description_len - 16;
5209 window.Printf ("%*s", desc_x - window.GetCursorX(), "");
5210 //window.MoveCursor(window_width - stop_description_len - 15, line_y);
5211 window.Printf ("<<< Thread %u: %s ", thread->GetIndexID(), stop_description);
5216 window.Printf ("%*s", window_width - window.GetCursorX() - 1, "");
5220 window.AttributeOff(highlight_attr);
5224 window.DeferredRefresh();
5225 return true; // Drawing handled
5231 size_t num_lines = GetNumSourceLines();
5233 num_lines = GetNumDisassemblyLines();
5238 GetNumSourceLines () const
5241 return m_file_sp->GetNumLines();
5245 GetNumDisassemblyLines () const
5247 if (m_disassembly_sp)
5248 return m_disassembly_sp->GetInstructionList().GetSize();
5252 virtual HandleCharResult
5253 WindowDelegateHandleChar (Window &window, int c)
5255 const uint32_t num_visible_lines = NumVisibleLines();
5256 const size_t num_lines = GetNumLines ();
5263 if (static_cast<uint32_t>(m_first_visible_line) > num_visible_lines)
5264 m_first_visible_line -= num_visible_lines;
5266 m_first_visible_line = 0;
5267 m_selected_line = m_first_visible_line;
5274 if (m_first_visible_line + num_visible_lines < num_lines)
5275 m_first_visible_line += num_visible_lines;
5276 else if (num_lines < num_visible_lines)
5277 m_first_visible_line = 0;
5279 m_first_visible_line = num_lines - num_visible_lines;
5280 m_selected_line = m_first_visible_line;
5285 if (m_selected_line > 0)
5288 if (static_cast<size_t>(m_first_visible_line) > m_selected_line)
5289 m_first_visible_line = m_selected_line;
5294 if (m_selected_line + 1 < num_lines)
5297 if (m_first_visible_line + num_visible_lines < m_selected_line)
5298 m_first_visible_line++;
5305 // Set a breakpoint and run to the line using a one shot breakpoint
5306 if (GetNumSourceLines() > 0)
5308 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5309 if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive())
5311 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (NULL, // Don't limit the breakpoint to certain modules
5312 m_file_sp->GetFileSpec(), // Source file
5313 m_selected_line + 1, // Source line number (m_selected_line is zero based)
5314 eLazyBoolCalculate, // Check inlines using global setting
5315 eLazyBoolCalculate, // Skip prologue using global setting,
5317 false); // request_hardware
5318 // Make breakpoint one shot
5319 bp_sp->GetOptions()->SetOneShot(true);
5320 exe_ctx.GetProcessRef().Resume();
5323 else if (m_selected_line < GetNumDisassemblyLines())
5325 const Instruction *inst = m_disassembly_sp->GetInstructionList().GetInstructionAtIndex(m_selected_line).get();
5326 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5327 if (exe_ctx.HasTargetScope())
5329 Address addr = inst->GetAddress();
5330 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (addr, // lldb_private::Address
5332 false); // request_hardware
5333 // Make breakpoint one shot
5334 bp_sp->GetOptions()->SetOneShot(true);
5335 exe_ctx.GetProcessRef().Resume();
5340 case 'b': // 'b' == toggle breakpoint on currently selected line
5341 if (m_selected_line < GetNumSourceLines())
5343 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5344 if (exe_ctx.HasTargetScope())
5346 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (NULL, // Don't limit the breakpoint to certain modules
5347 m_file_sp->GetFileSpec(), // Source file
5348 m_selected_line + 1, // Source line number (m_selected_line is zero based)
5349 eLazyBoolCalculate, // Check inlines using global setting
5350 eLazyBoolCalculate, // Skip prologue using global setting,
5352 false); // request_hardware
5355 else if (m_selected_line < GetNumDisassemblyLines())
5357 const Instruction *inst = m_disassembly_sp->GetInstructionList().GetInstructionAtIndex(m_selected_line).get();
5358 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5359 if (exe_ctx.HasTargetScope())
5361 Address addr = inst->GetAddress();
5362 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (addr, // lldb_private::Address
5364 false); // request_hardware
5369 case 'd': // 'd' == detach and let run
5370 case 'D': // 'D' == detach and keep stopped
5372 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5373 if (exe_ctx.HasProcessScope())
5374 exe_ctx.GetProcessRef().Detach(c == 'D');
5381 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5382 if (exe_ctx.HasProcessScope())
5383 exe_ctx.GetProcessRef().Destroy();
5390 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5391 if (exe_ctx.HasProcessScope())
5392 exe_ctx.GetProcessRef().Resume();
5399 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5400 if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true))
5402 exe_ctx.GetThreadRef().StepOut();
5406 case 'n': // 'n' == step over
5407 case 'N': // 'N' == step over instruction
5409 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5410 if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true))
5412 bool source_step = (c == 'n');
5413 exe_ctx.GetThreadRef().StepOver(source_step);
5417 case 's': // 's' == step into
5418 case 'S': // 'S' == step into instruction
5420 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5421 if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true))
5423 bool source_step = (c == 's');
5424 exe_ctx.GetThreadRef().StepIn(source_step);
5430 window.CreateHelpSubwindow ();
5436 return eKeyNotHandled;
5440 typedef std::set<uint32_t> BreakpointLines;
5441 typedef std::set<lldb::addr_t> BreakpointAddrs;
5443 Debugger &m_debugger;
5445 SourceManager::FileSP m_file_sp;
5446 SymbolContextScope *m_disassembly_scope;
5447 lldb::DisassemblerSP m_disassembly_sp;
5448 AddressRange m_disassembly_range;
5449 StreamString m_title;
5450 lldb::user_id_t m_tid;
5451 char m_line_format[8];
5453 uint32_t m_selected_line; // The selected line
5454 uint32_t m_pc_line; // The line with the PC
5456 uint32_t m_frame_idx;
5457 int m_first_visible_line;
5465 DisplayOptions ValueObjectListDelegate::g_options = { true };
5467 IOHandlerCursesGUI::IOHandlerCursesGUI (Debugger &debugger) :
5468 IOHandler (debugger, IOHandler::Type::Curses)
5473 IOHandlerCursesGUI::Activate ()
5475 IOHandler::Activate();
5478 m_app_ap.reset (new Application (GetInputFILE(), GetOutputFILE()));
5481 // This is both a window and a menu delegate
5482 std::shared_ptr<ApplicationDelegate> app_delegate_sp(new ApplicationDelegate(*m_app_ap, m_debugger));
5484 MenuDelegateSP app_menu_delegate_sp = std::static_pointer_cast<MenuDelegate>(app_delegate_sp);
5485 MenuSP lldb_menu_sp(new Menu("LLDB" , "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB));
5486 MenuSP exit_menuitem_sp(new Menu("Exit", NULL, 'x', ApplicationDelegate::eMenuID_LLDBExit));
5487 exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit);
5488 lldb_menu_sp->AddSubmenu (MenuSP (new Menu("About LLDB", NULL, 'a', ApplicationDelegate::eMenuID_LLDBAbout)));
5489 lldb_menu_sp->AddSubmenu (MenuSP (new Menu(Menu::Type::Separator)));
5490 lldb_menu_sp->AddSubmenu (exit_menuitem_sp);
5492 MenuSP target_menu_sp(new Menu("Target" ,"F2", KEY_F(2), ApplicationDelegate::eMenuID_Target));
5493 target_menu_sp->AddSubmenu (MenuSP (new Menu("Create", NULL, 'c', ApplicationDelegate::eMenuID_TargetCreate)));
5494 target_menu_sp->AddSubmenu (MenuSP (new Menu("Delete", NULL, 'd', ApplicationDelegate::eMenuID_TargetDelete)));
5496 MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3), ApplicationDelegate::eMenuID_Process));
5497 process_menu_sp->AddSubmenu (MenuSP (new Menu("Attach" , NULL, 'a', ApplicationDelegate::eMenuID_ProcessAttach)));
5498 process_menu_sp->AddSubmenu (MenuSP (new Menu("Detach" , NULL, 'd', ApplicationDelegate::eMenuID_ProcessDetach)));
5499 process_menu_sp->AddSubmenu (MenuSP (new Menu("Launch" , NULL, 'l', ApplicationDelegate::eMenuID_ProcessLaunch)));
5500 process_menu_sp->AddSubmenu (MenuSP (new Menu(Menu::Type::Separator)));
5501 process_menu_sp->AddSubmenu (MenuSP (new Menu("Continue", NULL, 'c', ApplicationDelegate::eMenuID_ProcessContinue)));
5502 process_menu_sp->AddSubmenu (MenuSP (new Menu("Halt" , NULL, 'h', ApplicationDelegate::eMenuID_ProcessHalt)));
5503 process_menu_sp->AddSubmenu (MenuSP (new Menu("Kill" , NULL, 'k', ApplicationDelegate::eMenuID_ProcessKill)));
5505 MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4), ApplicationDelegate::eMenuID_Thread));
5506 thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step In" , NULL, 'i', ApplicationDelegate::eMenuID_ThreadStepIn)));
5507 thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step Over", NULL, 'v', ApplicationDelegate::eMenuID_ThreadStepOver)));
5508 thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step Out" , NULL, 'o', ApplicationDelegate::eMenuID_ThreadStepOut)));
5510 MenuSP view_menu_sp(new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View));
5511 view_menu_sp->AddSubmenu (MenuSP (new Menu("Backtrace", NULL, 'b', ApplicationDelegate::eMenuID_ViewBacktrace)));
5512 view_menu_sp->AddSubmenu (MenuSP (new Menu("Registers", NULL, 'r', ApplicationDelegate::eMenuID_ViewRegisters)));
5513 view_menu_sp->AddSubmenu (MenuSP (new Menu("Source" , NULL, 's', ApplicationDelegate::eMenuID_ViewSource)));
5514 view_menu_sp->AddSubmenu (MenuSP (new Menu("Variables", NULL, 'v', ApplicationDelegate::eMenuID_ViewVariables)));
5516 MenuSP help_menu_sp(new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help));
5517 help_menu_sp->AddSubmenu (MenuSP (new Menu("GUI Help", NULL, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp)));
5519 m_app_ap->Initialize();
5520 WindowSP &main_window_sp = m_app_ap->GetMainWindow();
5522 MenuSP menubar_sp(new Menu(Menu::Type::Bar));
5523 menubar_sp->AddSubmenu (lldb_menu_sp);
5524 menubar_sp->AddSubmenu (target_menu_sp);
5525 menubar_sp->AddSubmenu (process_menu_sp);
5526 menubar_sp->AddSubmenu (thread_menu_sp);
5527 menubar_sp->AddSubmenu (view_menu_sp);
5528 menubar_sp->AddSubmenu (help_menu_sp);
5529 menubar_sp->SetDelegate(app_menu_delegate_sp);
5531 Rect content_bounds = main_window_sp->GetFrame();
5532 Rect menubar_bounds = content_bounds.MakeMenuBar();
5533 Rect status_bounds = content_bounds.MakeStatusBar();
5535 Rect variables_bounds;
5536 Rect threads_bounds;
5537 Rect source_variables_bounds;
5538 content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds, threads_bounds);
5539 source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds, variables_bounds);
5541 WindowSP menubar_window_sp = main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false);
5542 // Let the menubar get keys if the active window doesn't handle the
5543 // keys that are typed so it can respond to menubar key presses.
5544 menubar_window_sp->SetCanBeActive(false); // Don't let the menubar become the active window
5545 menubar_window_sp->SetDelegate(menubar_sp);
5547 WindowSP source_window_sp (main_window_sp->CreateSubWindow("Source",
5550 WindowSP variables_window_sp (main_window_sp->CreateSubWindow("Variables",
5553 WindowSP threads_window_sp (main_window_sp->CreateSubWindow("Threads",
5556 WindowSP status_window_sp (main_window_sp->CreateSubWindow("Status",
5559 status_window_sp->SetCanBeActive(false); // Don't let the status bar become the active window
5560 main_window_sp->SetDelegate (std::static_pointer_cast<WindowDelegate>(app_delegate_sp));
5561 source_window_sp->SetDelegate (WindowDelegateSP(new SourceFileWindowDelegate(m_debugger)));
5562 variables_window_sp->SetDelegate (WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
5563 TreeDelegateSP thread_delegate_sp (new ThreadsTreeDelegate(m_debugger));
5564 threads_window_sp->SetDelegate (WindowDelegateSP(new TreeWindowDelegate(m_debugger, thread_delegate_sp)));
5565 status_window_sp->SetDelegate (WindowDelegateSP(new StatusBarWindowDelegate(m_debugger)));
5567 // Show the main help window once the first time the curses GUI is launched
5568 static bool g_showed_help = false;
5571 g_showed_help = true;
5572 main_window_sp->CreateHelpSubwindow();
5575 init_pair (1, COLOR_WHITE , COLOR_BLUE );
5576 init_pair (2, COLOR_BLACK , COLOR_WHITE );
5577 init_pair (3, COLOR_MAGENTA , COLOR_WHITE );
5578 init_pair (4, COLOR_MAGENTA , COLOR_BLACK );
5579 init_pair (5, COLOR_RED , COLOR_BLACK );
5585 IOHandlerCursesGUI::Deactivate ()
5587 m_app_ap->Terminate();
5591 IOHandlerCursesGUI::Run ()
5593 m_app_ap->Run(m_debugger);
5598 IOHandlerCursesGUI::~IOHandlerCursesGUI ()
5604 IOHandlerCursesGUI::Hide ()
5610 IOHandlerCursesGUI::Refresh ()
5615 IOHandlerCursesGUI::Cancel ()
5620 IOHandlerCursesGUI::Interrupt ()
5627 IOHandlerCursesGUI::GotEOF()
5631 #endif // #ifndef LLDB_DISABLE_CURSES