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 //===----------------------------------------------------------------------===//
13 #include "lldb/Breakpoint/BreakpointLocation.h"
14 #include "lldb/Core/IOHandler.h"
15 #include "lldb/Core/Debugger.h"
16 #include "lldb/Core/Module.h"
17 #include "lldb/Core/State.h"
18 #include "lldb/Core/StreamFile.h"
19 #include "lldb/Core/ValueObjectRegister.h"
20 #ifndef LLDB_DISABLE_LIBEDIT
21 #include "lldb/Host/Editline.h"
23 #include "lldb/Interpreter/CommandCompletions.h"
24 #include "lldb/Interpreter/CommandInterpreter.h"
25 #include "lldb/Symbol/Block.h"
26 #include "lldb/Symbol/Function.h"
27 #include "lldb/Symbol/Symbol.h"
28 #include "lldb/Target/RegisterContext.h"
29 #include "lldb/Target/ThreadPlan.h"
31 #ifndef LLDB_DISABLE_CURSES
37 using namespace lldb_private;
39 IOHandler::IOHandler (Debugger &debugger, IOHandler::Type type) :
42 StreamFileSP(), // Adopt STDIN from top input reader
43 StreamFileSP(), // Adopt STDOUT from top input reader
44 StreamFileSP(), // Adopt STDERR from top input reader
50 IOHandler::IOHandler (Debugger &debugger,
52 const lldb::StreamFileSP &input_sp,
53 const lldb::StreamFileSP &output_sp,
54 const lldb::StreamFileSP &error_sp,
56 m_debugger (debugger),
57 m_input_sp (input_sp),
58 m_output_sp (output_sp),
59 m_error_sp (error_sp),
67 // If any files are not specified, then adopt them from the top input reader.
68 if (!m_input_sp || !m_output_sp || !m_error_sp)
69 debugger.AdoptTopIOHandlerFilesIfInvalid (m_input_sp,
74 IOHandler::~IOHandler()
80 IOHandler::GetInputFD()
83 return m_input_sp->GetFile().GetDescriptor();
88 IOHandler::GetOutputFD()
91 return m_output_sp->GetFile().GetDescriptor();
96 IOHandler::GetErrorFD()
99 return m_error_sp->GetFile().GetDescriptor();
104 IOHandler::GetInputFILE()
107 return m_input_sp->GetFile().GetStream();
112 IOHandler::GetOutputFILE()
115 return m_output_sp->GetFile().GetStream();
120 IOHandler::GetErrorFILE()
123 return m_error_sp->GetFile().GetStream();
128 IOHandler::GetInputStreamFile()
134 IOHandler::GetOutputStreamFile()
141 IOHandler::GetErrorStreamFile()
147 IOHandler::GetIsInteractive ()
149 return GetInputStreamFile()->GetFile().GetIsInteractive ();
153 IOHandler::GetIsRealTerminal ()
155 return GetInputStreamFile()->GetFile().GetIsRealTerminal();
159 IOHandler::SetPopped (bool b)
161 m_popped.SetValue(b, eBroadcastOnChange);
165 IOHandler::WaitForPop ()
167 m_popped.WaitForValueEqualTo(true);
171 IOHandlerStack::PrintAsync (Stream *stream, const char *s, size_t len)
175 Mutex::Locker locker (m_mutex);
177 m_top->PrintAsync (stream, s, len);
181 IOHandlerConfirm::IOHandlerConfirm (Debugger &debugger,
183 bool default_response) :
184 IOHandlerEditline(debugger,
185 IOHandler::Type::Confirm,
186 NULL, // NULL editline_name means no history loaded/saved
188 NULL, // No continuation prompt
190 false, // Don't colorize the prompt (i.e. the confirm message.)
193 m_default_response (default_response),
194 m_user_response (default_response)
196 StreamString prompt_stream;
197 prompt_stream.PutCString(prompt);
198 if (m_default_response)
199 prompt_stream.Printf(": [Y/n] ");
201 prompt_stream.Printf(": [y/N] ");
203 SetPrompt (prompt_stream.GetString().c_str());
208 IOHandlerConfirm::~IOHandlerConfirm ()
213 IOHandlerConfirm::IOHandlerComplete (IOHandler &io_handler,
214 const char *current_line,
216 const char *last_char,
217 int skip_first_n_matches,
221 if (current_line == cursor)
223 if (m_default_response)
225 matches.AppendString("y");
229 matches.AppendString("n");
232 return matches.GetSize();
236 IOHandlerConfirm::IOHandlerInputComplete (IOHandler &io_handler, std::string &line)
240 // User just hit enter, set the response to the default
241 m_user_response = m_default_response;
242 io_handler.SetIsDone(true);
246 if (line.size() == 1)
252 m_user_response = true;
253 io_handler.SetIsDone(true);
257 m_user_response = false;
258 io_handler.SetIsDone(true);
265 if (line == "yes" || line == "YES" || line == "Yes")
267 m_user_response = true;
268 io_handler.SetIsDone(true);
270 else if (line == "no" || line == "NO" || line == "No")
272 m_user_response = false;
273 io_handler.SetIsDone(true);
278 IOHandlerDelegate::IOHandlerComplete (IOHandler &io_handler,
279 const char *current_line,
281 const char *last_char,
282 int skip_first_n_matches,
286 switch (m_completion)
288 case Completion::None:
291 case Completion::LLDBCommand:
292 return io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion (current_line,
295 skip_first_n_matches,
299 case Completion::Expression:
301 bool word_complete = false;
302 const char *word_start = cursor;
303 if (cursor > current_line)
305 while (word_start > current_line && !isspace(*word_start))
307 CommandCompletions::InvokeCommonCompletionCallbacks (io_handler.GetDebugger().GetCommandInterpreter(),
308 CommandCompletions::eVariablePathCompletion,
310 skip_first_n_matches,
316 size_t num_matches = matches.GetSize();
319 std::string common_prefix;
320 matches.LongestCommonPrefix (common_prefix);
321 const size_t partial_name_len = strlen(word_start);
323 // If we matched a unique single command, add a space...
324 // Only do this if the completer told us this was a complete word, however...
325 if (num_matches == 1 && word_complete)
327 common_prefix.push_back(' ');
329 common_prefix.erase (0, partial_name_len);
330 matches.InsertStringAtIndex(0, std::move(common_prefix));
342 IOHandlerEditline::IOHandlerEditline (Debugger &debugger,
343 IOHandler::Type type,
344 const char *editline_name, // Used for saving history files
346 const char *continuation_prompt,
349 uint32_t line_number_start,
350 IOHandlerDelegate &delegate) :
351 IOHandlerEditline(debugger,
353 StreamFileSP(), // Inherit input from top input reader
354 StreamFileSP(), // Inherit output from top input reader
355 StreamFileSP(), // Inherit error from top input reader
357 editline_name, // Used for saving history files
367 IOHandlerEditline::IOHandlerEditline (Debugger &debugger,
368 IOHandler::Type type,
369 const lldb::StreamFileSP &input_sp,
370 const lldb::StreamFileSP &output_sp,
371 const lldb::StreamFileSP &error_sp,
373 const char *editline_name, // Used for saving history files
375 const char *continuation_prompt,
378 uint32_t line_number_start,
379 IOHandlerDelegate &delegate) :
380 IOHandler (debugger, type, input_sp, output_sp, error_sp, flags),
381 #ifndef LLDB_DISABLE_LIBEDIT
384 m_delegate (delegate),
386 m_continuation_prompt(),
387 m_current_lines_ptr (NULL),
388 m_base_line_number (line_number_start),
389 m_curr_line_idx (UINT32_MAX),
390 m_multi_line (multi_line),
391 m_color_prompts (color_prompts),
392 m_interrupt_exits (true),
397 #ifndef LLDB_DISABLE_LIBEDIT
398 bool use_editline = false;
400 use_editline = m_input_sp->GetFile().GetIsRealTerminal();
404 m_editline_ap.reset(new Editline (editline_name,
409 m_editline_ap->SetIsInputCompleteCallback (IsInputCompleteCallback, this);
410 m_editline_ap->SetAutoCompleteCallback (AutoCompleteCallback, this);
411 // See if the delegate supports fixing indentation
412 const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters();
415 // The delegate does support indentation, hook it up so when any indentation
416 // character is typed, the delegate gets a chance to fix it
417 m_editline_ap->SetFixIndentationCallback (FixIndentationCallback, this, indent_chars);
421 SetBaseLineNumber (m_base_line_number);
422 SetPrompt(prompt ? prompt : "");
423 SetContinuationPrompt(continuation_prompt);
426 IOHandlerEditline::~IOHandlerEditline ()
428 #ifndef LLDB_DISABLE_LIBEDIT
429 m_editline_ap.reset();
434 IOHandlerEditline::Activate ()
436 IOHandler::Activate();
437 m_delegate.IOHandlerActivated(*this);
441 IOHandlerEditline::Deactivate ()
443 IOHandler::Deactivate();
444 m_delegate.IOHandlerDeactivated(*this);
449 IOHandlerEditline::GetLine (std::string &line, bool &interrupted)
451 #ifndef LLDB_DISABLE_LIBEDIT
454 return m_editline_ap->GetLine (line, interrupted);
461 FILE *in = GetInputFILE();
464 if (GetIsInteractive())
466 const char *prompt = NULL;
468 if (m_multi_line && m_curr_line_idx > 0)
469 prompt = GetContinuationPrompt();
472 prompt = GetPrompt();
474 if (prompt && prompt[0])
476 FILE *out = GetOutputFILE();
479 ::fprintf(out, "%s", prompt);
486 bool got_line = false;
490 if (fgets(buffer, sizeof(buffer), in) == NULL)
492 const int saved_errno = errno;
497 if (saved_errno != EINTR)
504 size_t buffer_len = strlen(buffer);
505 assert (buffer[buffer_len] == '\0');
506 char last_char = buffer[buffer_len-1];
507 if (last_char == '\r' || last_char == '\n')
510 // Strip trailing newlines
511 while (last_char == '\r' || last_char == '\n')
516 last_char = buffer[buffer_len-1];
519 line.append(buffer, buffer_len);
523 // We might have gotten a newline on a line by itself
524 // make sure to return true in this case.
529 // No more input file, we are done...
533 #ifndef LLDB_DISABLE_LIBEDIT
539 #ifndef LLDB_DISABLE_LIBEDIT
541 IOHandlerEditline::IsInputCompleteCallback (Editline *editline,
545 IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton;
546 return editline_reader->m_delegate.IOHandlerIsInputComplete(*editline_reader, lines);
550 IOHandlerEditline::FixIndentationCallback (Editline *editline,
551 const StringList &lines,
555 IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton;
556 return editline_reader->m_delegate.IOHandlerFixIndentation(*editline_reader, lines, cursor_position);
560 IOHandlerEditline::AutoCompleteCallback (const char *current_line,
562 const char *last_char,
563 int skip_first_n_matches,
568 IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton;
570 return editline_reader->m_delegate.IOHandlerComplete (*editline_reader,
574 skip_first_n_matches,
582 IOHandlerEditline::GetPrompt ()
584 #ifndef LLDB_DISABLE_LIBEDIT
587 return m_editline_ap->GetPrompt ();
592 if (m_prompt.empty())
594 #ifndef LLDB_DISABLE_LIBEDIT
597 return m_prompt.c_str();
601 IOHandlerEditline::SetPrompt (const char *p)
607 #ifndef LLDB_DISABLE_LIBEDIT
609 m_editline_ap->SetPrompt (m_prompt.empty() ? NULL : m_prompt.c_str());
615 IOHandlerEditline::GetContinuationPrompt ()
617 if (m_continuation_prompt.empty())
619 return m_continuation_prompt.c_str();
624 IOHandlerEditline::SetContinuationPrompt (const char *p)
627 m_continuation_prompt = p;
629 m_continuation_prompt.clear();
631 #ifndef LLDB_DISABLE_LIBEDIT
633 m_editline_ap->SetContinuationPrompt (m_continuation_prompt.empty() ? NULL : m_continuation_prompt.c_str());
639 IOHandlerEditline::SetBaseLineNumber (uint32_t line)
641 m_base_line_number = line;
645 IOHandlerEditline::GetCurrentLineIndex () const
647 #ifndef LLDB_DISABLE_LIBEDIT
649 return m_editline_ap->GetCurrentLine();
651 return m_curr_line_idx;
655 IOHandlerEditline::GetLines (StringList &lines, bool &interrupted)
657 m_current_lines_ptr = &lines;
659 bool success = false;
660 #ifndef LLDB_DISABLE_LIBEDIT
663 return m_editline_ap->GetLines (m_base_line_number, lines, interrupted);
673 // Show line numbers if we are asked to
675 if (m_base_line_number > 0 && GetIsInteractive())
677 FILE *out = GetOutputFILE();
679 ::fprintf(out, "%u%s", m_base_line_number + (uint32_t)lines.GetSize(), GetPrompt() == NULL ? " " : "");
682 m_curr_line_idx = lines.GetSize();
684 bool interrupted = false;
685 if (GetLine(line, interrupted) && !interrupted)
687 lines.AppendString(line);
688 done = m_delegate.IOHandlerIsInputComplete(*this, lines);
695 success = lines.GetSize() > 0;
696 #ifndef LLDB_DISABLE_LIBEDIT
702 // Each IOHandler gets to run until it is done. It should read data
703 // from the "in" and place output into "out" and "err and return
706 IOHandlerEditline::Run ()
711 bool interrupted = false;
715 if (GetLines (lines, interrupted))
719 m_done = m_interrupt_exits;
720 m_delegate.IOHandlerInputInterrupted (*this, line);
725 line = lines.CopyList();
726 m_delegate.IOHandlerInputComplete (*this, line);
736 if (GetLine(line, interrupted))
739 m_delegate.IOHandlerInputInterrupted (*this, line);
741 m_delegate.IOHandlerInputComplete (*this, line);
752 IOHandlerEditline::Cancel ()
754 #ifndef LLDB_DISABLE_LIBEDIT
756 m_editline_ap->Cancel ();
761 IOHandlerEditline::Interrupt ()
763 // Let the delgate handle it first
764 if (m_delegate.IOHandlerInterrupt(*this))
767 #ifndef LLDB_DISABLE_LIBEDIT
769 return m_editline_ap->Interrupt();
775 IOHandlerEditline::GotEOF()
777 #ifndef LLDB_DISABLE_LIBEDIT
779 m_editline_ap->Interrupt();
784 IOHandlerEditline::PrintAsync (Stream *stream, const char *s, size_t len)
786 #ifndef LLDB_DISABLE_LIBEDIT
788 m_editline_ap->PrintAsync(stream, s, len);
791 IOHandler::PrintAsync(stream, s, len);
794 // we may want curses to be disabled for some builds
795 // for instance, windows
796 #ifndef LLDB_DISABLE_CURSES
798 #include "lldb/Core/ValueObject.h"
799 #include "lldb/Symbol/VariableList.h"
800 #include "lldb/Target/Target.h"
801 #include "lldb/Target/Process.h"
802 #include "lldb/Target/Thread.h"
803 #include "lldb/Target/StackFrame.h"
805 #define KEY_RETURN 10
806 #define KEY_ESCAPE 27
813 class WindowDelegate;
814 typedef std::shared_ptr<Menu> MenuSP;
815 typedef std::shared_ptr<MenuDelegate> MenuDelegateSP;
816 typedef std::shared_ptr<Window> WindowSP;
817 typedef std::shared_ptr<WindowDelegate> WindowDelegateSP;
818 typedef std::vector<MenuSP> Menus;
819 typedef std::vector<WindowSP> Windows;
820 typedef std::vector<WindowDelegateSP> WindowDelegates;
823 type summary add -s "x=${var.x}, y=${var.y}" curses::Point
824 type summary add -s "w=${var.width}, h=${var.height}" curses::Size
825 type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect
832 Point (int _x = 0, int _y = 0) :
846 operator += (const Point &rhs)
856 printf ("(x=%i, y=%i)\n", x, y);
861 bool operator == (const Point &lhs, const Point &rhs)
863 return lhs.x == rhs.x && lhs.y == rhs.y;
865 bool operator != (const Point &lhs, const Point &rhs)
867 return lhs.x != rhs.x || lhs.y != rhs.y;
874 Size (int w = 0, int h = 0) :
890 printf ("(w=%i, h=%i)\n", width, height);
895 bool operator == (const Size &lhs, const Size &rhs)
897 return lhs.width == rhs.width && lhs.height == rhs.height;
899 bool operator != (const Size &lhs, const Size &rhs)
901 return lhs.width != rhs.width || lhs.height != rhs.height;
915 Rect (const Point &p, const Size &s) :
931 printf ("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width, size.height);
937 if (size.width > w*2)
941 if (size.height > h*2)
945 // Return a status bar rectangle which is the last line of
946 // this rectangle. This rectangle will be modified to not
947 // include the status bar area.
954 status_bar.origin.x = origin.x;
955 status_bar.origin.y = size.height;
956 status_bar.size.width = size.width;
957 status_bar.size.height = 1;
963 // Return a menubar rectangle which is the first line of
964 // this rectangle. This rectangle will be modified to not
965 // include the menubar area.
972 menubar.origin.x = origin.x;
973 menubar.origin.y = origin.y;
974 menubar.size.width = size.width;
975 menubar.size.height = 1;
983 HorizontalSplitPercentage (float top_percentage, Rect &top, Rect &bottom) const
985 float top_height = top_percentage * size.height;
986 HorizontalSplit (top_height, top, bottom);
990 HorizontalSplit (int top_height, Rect &top, Rect &bottom) const
993 if (top_height < size.height)
995 top.size.height = top_height;
996 bottom.origin.x = origin.x;
997 bottom.origin.y = origin.y + top.size.height;
998 bottom.size.width = size.width;
999 bottom.size.height = size.height - top.size.height;
1008 VerticalSplitPercentage (float left_percentage, Rect &left, Rect &right) const
1010 float left_width = left_percentage * size.width;
1011 VerticalSplit (left_width, left, right);
1016 VerticalSplit (int left_width, Rect &left, Rect &right) const
1019 if (left_width < size.width)
1021 left.size.width = left_width;
1022 right.origin.x = origin.x + left.size.width;
1023 right.origin.y = origin.y;
1024 right.size.width = size.width - left.size.width;
1025 right.size.height = size.height;
1034 bool operator == (const Rect &lhs, const Rect &rhs)
1036 return lhs.origin == rhs.origin && lhs.size == rhs.size;
1038 bool operator != (const Rect &lhs, const Rect &rhs)
1040 return lhs.origin != rhs.origin || lhs.size != rhs.size;
1043 enum HandleCharResult
1047 eQuitApplication = 2
1050 enum class MenuActionResult
1054 Quit // Exit all menus and quit
1060 const char *description;
1063 class WindowDelegate
1072 WindowDelegateDraw (Window &window, bool force)
1074 return false; // Drawing not handled
1077 virtual HandleCharResult
1078 WindowDelegateHandleChar (Window &window, int key)
1080 return eKeyNotHandled;
1083 virtual const char *
1084 WindowDelegateGetHelpText ()
1090 WindowDelegateGetKeyHelp ()
1096 class HelpDialogDelegate :
1097 public WindowDelegate
1100 HelpDialogDelegate (const char *text, KeyHelp *key_help_array);
1102 ~HelpDialogDelegate() override;
1105 WindowDelegateDraw (Window &window, bool force) override;
1108 WindowDelegateHandleChar (Window &window, int key) override;
1113 return m_text.GetSize();
1117 GetMaxLineLength () const
1119 return m_text.GetMaxStringLength();
1124 int m_first_visible_line;
1132 Window (const char *name) :
1139 m_curr_active_window_idx (UINT32_MAX),
1140 m_prev_active_window_idx (UINT32_MAX),
1142 m_needs_update (true),
1143 m_can_activate (true),
1148 Window (const char *name, WINDOW *w, bool del = true) :
1155 m_curr_active_window_idx (UINT32_MAX),
1156 m_prev_active_window_idx (UINT32_MAX),
1158 m_needs_update (true),
1159 m_can_activate (true),
1166 Window (const char *name, const Rect &bounds) :
1172 m_curr_active_window_idx (UINT32_MAX),
1173 m_prev_active_window_idx (UINT32_MAX),
1175 m_needs_update (true),
1176 m_can_activate (true),
1179 Reset (::newwin (bounds.size.height, bounds.size.width, bounds.origin.y, bounds.origin.y));
1185 RemoveSubWindows ();
1190 Reset (WINDOW *w = NULL, bool del = true)
1197 ::del_panel (m_panel);
1200 if (m_window && m_delete)
1202 ::delwin (m_window);
1209 m_panel = ::new_panel (m_window);
1214 void AttributeOn (attr_t attr) { ::wattron (m_window, attr); }
1215 void AttributeOff (attr_t attr) { ::wattroff (m_window, attr); }
1216 void Box (chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) { ::box(m_window, v_char, h_char); }
1217 void Clear () { ::wclear (m_window); }
1218 void Erase () { ::werase (m_window); }
1219 Rect GetBounds () { return Rect (GetParentOrigin(), GetSize()); } // Get the rectangle in our parent window
1220 int GetChar () { return ::wgetch (m_window); }
1221 int GetCursorX () { return getcurx (m_window); }
1222 int GetCursorY () { return getcury (m_window); }
1223 Rect GetFrame () { return Rect (Point(), GetSize()); } // Get our rectangle in our own coordinate system
1224 Point GetParentOrigin() { return Point (GetParentX(), GetParentY()); }
1225 Size GetSize() { return Size (GetWidth(), GetHeight()); }
1226 int GetParentX () { return getparx (m_window); }
1227 int GetParentY () { return getpary (m_window); }
1228 int GetMaxX() { return getmaxx (m_window); }
1229 int GetMaxY() { return getmaxy (m_window); }
1230 int GetWidth() { return GetMaxX(); }
1231 int GetHeight() { return GetMaxY(); }
1232 void MoveCursor (int x, int y) { ::wmove (m_window, y, x); }
1233 void MoveWindow (int x, int y) { MoveWindow(Point(x,y)); }
1234 void Resize (int w, int h) { ::wresize(m_window, h, w); }
1235 void Resize (const Size &size) { ::wresize(m_window, size.height, size.width); }
1236 void PutChar (int ch) { ::waddch (m_window, ch); }
1237 void PutCString (const char *s, int len = -1) { ::waddnstr (m_window, s, len); }
1238 void Refresh () { ::wrefresh (m_window); }
1239 void DeferredRefresh ()
1241 // We are using panels, so we don't need to call this...
1242 //::wnoutrefresh(m_window);
1244 void SetBackground (int color_pair_idx) { ::wbkgd (m_window,COLOR_PAIR(color_pair_idx)); }
1245 void UnderlineOn () { AttributeOn(A_UNDERLINE); }
1246 void UnderlineOff () { AttributeOff(A_UNDERLINE); }
1248 void PutCStringTruncated (const char *s, int right_pad)
1250 int bytes_left = GetWidth() - GetCursorX();
1251 if (bytes_left > right_pad)
1253 bytes_left -= right_pad;
1254 ::waddnstr (m_window, s, bytes_left);
1259 MoveWindow (const Point &origin)
1261 const bool moving_window = origin != GetParentOrigin();
1262 if (m_is_subwin && moving_window)
1264 // Can't move subwindows, must delete and re-create
1265 Size size = GetSize();
1266 Reset (::subwin (m_parent->m_window,
1274 ::mvwin (m_window, origin.y, origin.x);
1279 SetBounds (const Rect &bounds)
1281 const bool moving_window = bounds.origin != GetParentOrigin();
1282 if (m_is_subwin && moving_window)
1284 // Can't move subwindows, must delete and re-create
1285 Reset (::subwin (m_parent->m_window,
1289 bounds.origin.x), true);
1294 MoveWindow(bounds.origin);
1295 Resize (bounds.size);
1300 Printf (const char *format, ...) __attribute__ ((format (printf, 2, 3)))
1303 va_start (args, format);
1304 vwprintw(m_window, format, args);
1311 ::touchwin (m_window);
1317 CreateSubWindow (const char *name, const Rect &bounds, bool make_active)
1319 WindowSP subwindow_sp;
1322 subwindow_sp.reset(new Window(name, ::subwin (m_window,
1326 bounds.origin.x), true));
1327 subwindow_sp->m_is_subwin = true;
1331 subwindow_sp.reset(new Window(name, ::newwin (bounds.size.height,
1334 bounds.origin.x), true));
1335 subwindow_sp->m_is_subwin = false;
1337 subwindow_sp->m_parent = this;
1340 m_prev_active_window_idx = m_curr_active_window_idx;
1341 m_curr_active_window_idx = m_subwindows.size();
1343 m_subwindows.push_back(subwindow_sp);
1344 ::top_panel (subwindow_sp->m_panel);
1345 m_needs_update = true;
1346 return subwindow_sp;
1350 RemoveSubWindow (Window *window)
1352 Windows::iterator pos, end = m_subwindows.end();
1354 for (pos = m_subwindows.begin(); pos != end; ++pos, ++i)
1356 if ((*pos).get() == window)
1358 if (m_prev_active_window_idx == i)
1359 m_prev_active_window_idx = UINT32_MAX;
1360 else if (m_prev_active_window_idx != UINT32_MAX && m_prev_active_window_idx > i)
1361 --m_prev_active_window_idx;
1363 if (m_curr_active_window_idx == i)
1364 m_curr_active_window_idx = UINT32_MAX;
1365 else if (m_curr_active_window_idx != UINT32_MAX && m_curr_active_window_idx > i)
1366 --m_curr_active_window_idx;
1368 m_subwindows.erase(pos);
1369 m_needs_update = true;
1373 ::touchwin (stdscr);
1381 FindSubWindow (const char *name)
1383 Windows::iterator pos, end = m_subwindows.end();
1385 for (pos = m_subwindows.begin(); pos != end; ++pos, ++i)
1387 if ((*pos)->m_name.compare(name) == 0)
1396 m_curr_active_window_idx = UINT32_MAX;
1397 m_prev_active_window_idx = UINT32_MAX;
1398 for (Windows::iterator pos = m_subwindows.begin();
1399 pos != m_subwindows.end();
1400 pos = m_subwindows.erase(pos))
1407 ::touchwin (stdscr);
1421 //----------------------------------------------------------------------
1422 // Window drawing utilities
1423 //----------------------------------------------------------------------
1425 DrawTitleBox (const char *title, const char *bottom_message = NULL)
1429 attr = A_BOLD | COLOR_PAIR(2);
1438 if (title && title[0])
1445 if (bottom_message && bottom_message[0])
1447 int bottom_message_length = strlen(bottom_message);
1448 int x = GetWidth() - 3 - (bottom_message_length + 2);
1452 MoveCursor (x, GetHeight() - 1);
1454 PutCString(bottom_message);
1459 MoveCursor (1, GetHeight() - 1);
1461 PutCStringTruncated (bottom_message, 1);
1472 if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw (*this, force))
1475 for (auto &subwindow_sp : m_subwindows)
1476 subwindow_sp->Draw(force);
1480 CreateHelpSubwindow ()
1484 const char *text = m_delegate_sp->WindowDelegateGetHelpText ();
1485 KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp ();
1486 if ((text && text[0]) || key_help)
1488 std::auto_ptr<HelpDialogDelegate> help_delegate_ap(new HelpDialogDelegate(text, key_help));
1489 const size_t num_lines = help_delegate_ap->GetNumLines();
1490 const size_t max_length = help_delegate_ap->GetMaxLineLength();
1491 Rect bounds = GetBounds();
1493 if (max_length + 4 < static_cast<size_t>(bounds.size.width))
1495 bounds.origin.x += (bounds.size.width - max_length + 4)/2;
1496 bounds.size.width = max_length + 4;
1500 if (bounds.size.width > 100)
1502 const int inset_w = bounds.size.width / 4;
1503 bounds.origin.x += inset_w;
1504 bounds.size.width -= 2*inset_w;
1508 if (num_lines + 2 < static_cast<size_t>(bounds.size.height))
1510 bounds.origin.y += (bounds.size.height - num_lines + 2)/2;
1511 bounds.size.height = num_lines + 2;
1515 if (bounds.size.height > 100)
1517 const int inset_h = bounds.size.height / 4;
1518 bounds.origin.y += inset_h;
1519 bounds.size.height -= 2*inset_h;
1522 WindowSP help_window_sp;
1523 Window *parent_window = GetParent();
1525 help_window_sp = parent_window->CreateSubWindow("Help", bounds, true);
1527 help_window_sp = CreateSubWindow("Help", bounds, true);
1528 help_window_sp->SetDelegate(WindowDelegateSP(help_delegate_ap.release()));
1535 virtual HandleCharResult
1536 HandleChar (int key)
1538 // Always check the active window first
1539 HandleCharResult result = eKeyNotHandled;
1540 WindowSP active_window_sp = GetActiveWindow ();
1541 if (active_window_sp)
1543 result = active_window_sp->HandleChar (key);
1544 if (result != eKeyNotHandled)
1550 result = m_delegate_sp->WindowDelegateHandleChar (*this, key);
1551 if (result != eKeyNotHandled)
1555 // Then check for any windows that want any keys
1556 // that weren't handled. This is typically only
1558 // Make a copy of the subwindows in case any HandleChar()
1559 // functions muck with the subwindows. If we don't do this,
1560 // we can crash when iterating over the subwindows.
1561 Windows subwindows (m_subwindows);
1562 for (auto subwindow_sp : subwindows)
1564 if (subwindow_sp->m_can_activate == false)
1566 HandleCharResult result = subwindow_sp->HandleChar(key);
1567 if (result != eKeyNotHandled)
1572 return eKeyNotHandled;
1576 SetActiveWindow (Window *window)
1578 const size_t num_subwindows = m_subwindows.size();
1579 for (size_t i=0; i<num_subwindows; ++i)
1581 if (m_subwindows[i].get() == window)
1583 m_prev_active_window_idx = m_curr_active_window_idx;
1584 ::top_panel (window->m_panel);
1585 m_curr_active_window_idx = i;
1595 if (!m_subwindows.empty())
1597 if (m_curr_active_window_idx >= m_subwindows.size())
1599 if (m_prev_active_window_idx < m_subwindows.size())
1601 m_curr_active_window_idx = m_prev_active_window_idx;
1602 m_prev_active_window_idx = UINT32_MAX;
1604 else if (IsActive())
1606 m_prev_active_window_idx = UINT32_MAX;
1607 m_curr_active_window_idx = UINT32_MAX;
1609 // Find first window that wants to be active if this window is active
1610 const size_t num_subwindows = m_subwindows.size();
1611 for (size_t i=0; i<num_subwindows; ++i)
1613 if (m_subwindows[i]->GetCanBeActive())
1615 m_curr_active_window_idx = i;
1622 if (m_curr_active_window_idx < m_subwindows.size())
1623 return m_subwindows[m_curr_active_window_idx];
1629 GetCanBeActive () const
1631 return m_can_activate;
1635 SetCanBeActive (bool b)
1640 const WindowDelegateSP &
1641 GetDelegate () const
1643 return m_delegate_sp;
1647 SetDelegate (const WindowDelegateSP &delegate_sp)
1649 m_delegate_sp = delegate_sp;
1662 return m_parent->GetActiveWindow().get() == this;
1664 return true; // Top level window is always active
1668 SelectNextWindowAsActive ()
1670 // Move active focus to next window
1671 const size_t num_subwindows = m_subwindows.size();
1672 if (m_curr_active_window_idx == UINT32_MAX)
1675 for (auto subwindow_sp : m_subwindows)
1677 if (subwindow_sp->GetCanBeActive())
1679 m_curr_active_window_idx = idx;
1685 else if (m_curr_active_window_idx + 1 < num_subwindows)
1687 bool handled = false;
1688 m_prev_active_window_idx = m_curr_active_window_idx;
1689 for (size_t idx=m_curr_active_window_idx + 1; idx<num_subwindows; ++idx)
1691 if (m_subwindows[idx]->GetCanBeActive())
1693 m_curr_active_window_idx = idx;
1700 for (size_t idx=0; idx<=m_prev_active_window_idx; ++idx)
1702 if (m_subwindows[idx]->GetCanBeActive())
1704 m_curr_active_window_idx = idx;
1712 m_prev_active_window_idx = m_curr_active_window_idx;
1713 for (size_t idx=0; idx<num_subwindows; ++idx)
1715 if (m_subwindows[idx]->GetCanBeActive())
1717 m_curr_active_window_idx = idx;
1727 return m_name.c_str();
1734 Windows m_subwindows;
1735 WindowDelegateSP m_delegate_sp;
1736 uint32_t m_curr_active_window_idx;
1737 uint32_t m_prev_active_window_idx;
1739 bool m_needs_update;
1740 bool m_can_activate;
1744 DISALLOW_COPY_AND_ASSIGN(Window);
1750 virtual ~MenuDelegate() {}
1752 virtual MenuActionResult
1753 MenuDelegateAction (Menu &menu) = 0;
1756 class Menu : public WindowDelegate
1767 // Menubar or separator constructor
1770 // Menuitem constructor
1771 Menu (const char *name,
1772 const char *key_name,
1774 uint64_t identifier);
1780 const MenuDelegateSP &
1781 GetDelegate () const
1783 return m_delegate_sp;
1787 SetDelegate (const MenuDelegateSP &delegate_sp)
1789 m_delegate_sp = delegate_sp;
1793 RecalculateNameLengths();
1796 AddSubmenu (const MenuSP &menu_sp);
1799 DrawAndRunMenu (Window &window);
1802 DrawMenuTitle (Window &window, bool highlight);
1805 WindowDelegateDraw (Window &window, bool force) override;
1808 WindowDelegateHandleChar (Window &window, int key) override;
1811 ActionPrivate (Menu &menu)
1813 MenuActionResult result = MenuActionResult::NotHandled;
1816 result = m_delegate_sp->MenuDelegateAction (menu);
1817 if (result != MenuActionResult::NotHandled)
1822 result = m_parent->ActionPrivate(menu);
1823 if (result != MenuActionResult::NotHandled)
1826 return m_canned_result;
1832 // Call the recursive action so it can try to handle it
1833 // with the menu delegate, and if not, try our parent menu
1834 return ActionPrivate (*this);
1838 SetCannedResult (MenuActionResult result)
1840 m_canned_result = result;
1856 GetSelectedSubmenuIndex () const
1862 SetSelectedSubmenuIndex (int idx)
1874 GetStartingColumn() const
1880 SetStartingColumn(int col)
1892 SetKeyValue(int key_value)
1894 m_key_value = key_value;
1910 GetDrawWidth () const
1912 return m_max_submenu_name_length + m_max_submenu_key_name_length + 8;
1917 GetIdentifier() const
1919 return m_identifier;
1923 SetIdentifier (uint64_t identifier)
1925 m_identifier = identifier;
1930 std::string m_key_name;
1931 uint64_t m_identifier;
1935 int m_max_submenu_name_length;
1936 int m_max_submenu_key_name_length;
1940 WindowSP m_menu_window_sp;
1941 MenuActionResult m_canned_result;
1942 MenuDelegateSP m_delegate_sp;
1945 // Menubar or separator constructor
1946 Menu::Menu (Type type) :
1953 m_max_submenu_name_length (0),
1954 m_max_submenu_key_name_length (0),
1958 m_canned_result (MenuActionResult::NotHandled),
1963 // Menuitem constructor
1964 Menu::Menu (const char *name,
1965 const char *key_name,
1967 uint64_t identifier) :
1970 m_identifier (identifier),
1971 m_type (Type::Invalid),
1972 m_key_value (key_value),
1974 m_max_submenu_name_length (0),
1975 m_max_submenu_key_name_length (0),
1979 m_canned_result (MenuActionResult::NotHandled),
1982 if (name && name[0])
1985 m_type = Type::Item;
1986 if (key_name && key_name[0])
1987 m_key_name = key_name;
1991 m_type = Type::Separator;
1996 Menu::RecalculateNameLengths()
1998 m_max_submenu_name_length = 0;
1999 m_max_submenu_key_name_length = 0;
2000 Menus &submenus = GetSubmenus();
2001 const size_t num_submenus = submenus.size();
2002 for (size_t i=0; i<num_submenus; ++i)
2004 Menu *submenu = submenus[i].get();
2005 if (static_cast<size_t>(m_max_submenu_name_length) < submenu->m_name.size())
2006 m_max_submenu_name_length = submenu->m_name.size();
2007 if (static_cast<size_t>(m_max_submenu_key_name_length) < submenu->m_key_name.size())
2008 m_max_submenu_key_name_length = submenu->m_key_name.size();
2013 Menu::AddSubmenu (const MenuSP &menu_sp)
2015 menu_sp->m_parent = this;
2016 if (static_cast<size_t>(m_max_submenu_name_length) < menu_sp->m_name.size())
2017 m_max_submenu_name_length = menu_sp->m_name.size();
2018 if (static_cast<size_t>(m_max_submenu_key_name_length) < menu_sp->m_key_name.size())
2019 m_max_submenu_key_name_length = menu_sp->m_key_name.size();
2020 m_submenus.push_back(menu_sp);
2024 Menu::DrawMenuTitle (Window &window, bool highlight)
2026 if (m_type == Type::Separator)
2028 window.MoveCursor(0, window.GetCursorY());
2029 window.PutChar(ACS_LTEE);
2030 int width = window.GetWidth();
2034 for (int i=0; i< width; ++i)
2035 window.PutChar(ACS_HLINE);
2037 window.PutChar(ACS_RTEE);
2041 const int shortcut_key = m_key_value;
2042 bool underlined_shortcut = false;
2043 const attr_t hilgight_attr = A_REVERSE;
2045 window.AttributeOn(hilgight_attr);
2046 if (isprint(shortcut_key))
2048 size_t lower_pos = m_name.find(tolower(shortcut_key));
2049 size_t upper_pos = m_name.find(toupper(shortcut_key));
2050 const char *name = m_name.c_str();
2051 size_t pos = std::min<size_t>(lower_pos, upper_pos);
2052 if (pos != std::string::npos)
2054 underlined_shortcut = true;
2057 window.PutCString(name, pos);
2060 const attr_t shortcut_attr = A_UNDERLINE|A_BOLD;
2061 window.AttributeOn (shortcut_attr);
2062 window.PutChar(name[0]);
2063 window.AttributeOff(shortcut_attr);
2066 window.PutCString(name);
2070 if (!underlined_shortcut)
2072 window.PutCString(m_name.c_str());
2076 window.AttributeOff(hilgight_attr);
2078 if (m_key_name.empty())
2080 if (!underlined_shortcut && isprint(m_key_value))
2082 window.AttributeOn (COLOR_PAIR(3));
2083 window.Printf (" (%c)", m_key_value);
2084 window.AttributeOff (COLOR_PAIR(3));
2089 window.AttributeOn (COLOR_PAIR(3));
2090 window.Printf (" (%s)", m_key_name.c_str());
2091 window.AttributeOff (COLOR_PAIR(3));
2097 Menu::WindowDelegateDraw (Window &window, bool force)
2099 Menus &submenus = GetSubmenus();
2100 const size_t num_submenus = submenus.size();
2101 const int selected_idx = GetSelectedSubmenuIndex();
2102 Menu::Type menu_type = GetType ();
2105 case Menu::Type::Bar:
2107 window.SetBackground(2);
2108 window.MoveCursor(0, 0);
2109 for (size_t i=0; i<num_submenus; ++i)
2111 Menu *menu = submenus[i].get();
2113 window.PutChar(' ');
2114 menu->SetStartingColumn (window.GetCursorX());
2115 window.PutCString("| ");
2116 menu->DrawMenuTitle (window, false);
2118 window.PutCString(" |");
2119 window.DeferredRefresh();
2123 case Menu::Type::Item:
2131 window.SetBackground(2);
2133 for (size_t i=0; i<num_submenus; ++i)
2135 const bool is_selected =
2136 (i == static_cast<size_t>(selected_idx));
2137 window.MoveCursor(x, y + i);
2140 // Remember where we want the cursor to be
2144 submenus[i]->DrawMenuTitle (window, is_selected);
2146 window.MoveCursor(cursor_x, cursor_y);
2147 window.DeferredRefresh();
2152 case Menu::Type::Separator:
2155 return true; // Drawing handled...
2159 Menu::WindowDelegateHandleChar (Window &window, int key)
2161 HandleCharResult result = eKeyNotHandled;
2163 Menus &submenus = GetSubmenus();
2164 const size_t num_submenus = submenus.size();
2165 const int selected_idx = GetSelectedSubmenuIndex();
2166 Menu::Type menu_type = GetType ();
2167 if (menu_type == Menu::Type::Bar)
2174 // Show last menu or first menu
2175 if (selected_idx < static_cast<int>(num_submenus))
2176 run_menu_sp = submenus[selected_idx];
2177 else if (!submenus.empty())
2178 run_menu_sp = submenus.front();
2179 result = eKeyHandled;
2185 if (m_selected >= static_cast<int>(num_submenus))
2187 if (m_selected < static_cast<int>(num_submenus))
2188 run_menu_sp = submenus[m_selected];
2189 else if (!submenus.empty())
2190 run_menu_sp = submenus.front();
2191 result = eKeyHandled;
2199 m_selected = num_submenus - 1;
2200 if (m_selected < static_cast<int>(num_submenus))
2201 run_menu_sp = submenus[m_selected];
2202 else if (!submenus.empty())
2203 run_menu_sp = submenus.front();
2204 result = eKeyHandled;
2209 for (size_t i=0; i<num_submenus; ++i)
2211 if (submenus[i]->GetKeyValue() == key)
2213 SetSelectedSubmenuIndex(i);
2214 run_menu_sp = submenus[i];
2215 result = eKeyHandled;
2224 // Run the action on this menu in case we need to populate the
2225 // menu with dynamic content and also in case check marks, and
2226 // any other menu decorations need to be caclulated
2227 if (run_menu_sp->Action() == MenuActionResult::Quit)
2228 return eQuitApplication;
2231 menu_bounds.origin.x = run_menu_sp->GetStartingColumn();
2232 menu_bounds.origin.y = 1;
2233 menu_bounds.size.width = run_menu_sp->GetDrawWidth();
2234 menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2;
2235 if (m_menu_window_sp)
2236 window.GetParent()->RemoveSubWindow(m_menu_window_sp.get());
2238 m_menu_window_sp = window.GetParent()->CreateSubWindow (run_menu_sp->GetName().c_str(),
2241 m_menu_window_sp->SetDelegate (run_menu_sp);
2244 else if (menu_type == Menu::Type::Item)
2249 if (m_submenus.size() > 1)
2251 const int start_select = m_selected;
2252 while (++m_selected != start_select)
2254 if (static_cast<size_t>(m_selected) >= num_submenus)
2256 if (m_submenus[m_selected]->GetType() == Type::Separator)
2266 if (m_submenus.size() > 1)
2268 const int start_select = m_selected;
2269 while (--m_selected != start_select)
2271 if (m_selected < static_cast<int>(0))
2272 m_selected = num_submenus - 1;
2273 if (m_submenus[m_selected]->GetType() == Type::Separator)
2283 if (static_cast<size_t>(selected_idx) < num_submenus)
2285 if (submenus[selected_idx]->Action() == MenuActionResult::Quit)
2286 return eQuitApplication;
2287 window.GetParent()->RemoveSubWindow(&window);
2292 case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in case other chars are entered for escaped sequences
2293 window.GetParent()->RemoveSubWindow(&window);
2298 for (size_t i=0; i<num_submenus; ++i)
2300 Menu *menu = submenus[i].get();
2301 if (menu->GetKeyValue() == key)
2303 SetSelectedSubmenuIndex(i);
2304 window.GetParent()->RemoveSubWindow(&window);
2305 if (menu->Action() == MenuActionResult::Quit)
2306 return eQuitApplication;
2315 else if (menu_type == Menu::Type::Separator)
2326 Application (FILE *in, FILE *out) :
2337 m_window_delegates.clear();
2338 m_window_sp.reset();
2341 ::delscreen(m_screen);
2349 ::setlocale(LC_ALL, "");
2350 ::setlocale(LC_CTYPE, "");
2354 m_screen = ::newterm(NULL, m_out, m_in);
2359 ::keypad(stdscr,TRUE);
2369 Run (Debugger &debugger)
2372 int delay_in_tenths_of_a_second = 1;
2374 // Alas the threading model in curses is a bit lame so we need to
2375 // resort to polling every 0.5 seconds. We could poll for stdin
2376 // ourselves and then pass the keys down but then we need to
2377 // translate all of the escape sequences ourselves. So we resort to
2378 // polling for input because we need to receive async process events
2379 // while in this loop.
2381 halfdelay(delay_in_tenths_of_a_second); // Poll using some number of tenths of seconds seconds when calling Window::GetChar()
2383 ListenerSP listener_sp (new Listener ("lldb.IOHandler.curses.Application"));
2384 ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass());
2385 ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass());
2386 ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass());
2387 debugger.EnableForwardEvents (listener_sp);
2390 #if defined(__APPLE__)
2391 std::deque<int> escape_chars;
2398 m_window_sp->Draw(false);
2399 // All windows should be calling Window::DeferredRefresh() instead
2400 // of Window::Refresh() so we can do a single update and avoid
2401 // any screen blinking
2404 // Cursor hiding isn't working on MacOSX, so hide it in the top left corner
2405 m_window_sp->MoveCursor(0, 0);
2411 #if defined(__APPLE__)
2412 // Terminal.app doesn't map its function keys correctly, F1-F4 default to:
2413 // \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if possible
2415 if (escape_chars.empty())
2416 ch = m_window_sp->GetChar();
2419 ch = escape_chars.front();
2420 escape_chars.pop_front();
2422 if (ch == KEY_ESCAPE)
2424 int ch2 = m_window_sp->GetChar();
2427 int ch3 = m_window_sp->GetChar();
2430 case 'P': ch = KEY_F(1); break;
2431 case 'Q': ch = KEY_F(2); break;
2432 case 'R': ch = KEY_F(3); break;
2433 case 'S': ch = KEY_F(4); break;
2435 escape_chars.push_back(ch2);
2437 escape_chars.push_back(ch3);
2442 escape_chars.push_back(ch2);
2445 int ch = m_window_sp->GetChar();
2450 if (feof(m_in) || ferror(m_in))
2456 // Just a timeout from using halfdelay(), check for events
2458 while (listener_sp->PeekAtNextEvent())
2460 listener_sp->GetNextEvent(event_sp);
2464 Broadcaster *broadcaster = event_sp->GetBroadcaster();
2467 //uint32_t event_type = event_sp->GetType();
2468 ConstString broadcaster_class (broadcaster->GetBroadcasterClass());
2469 if (broadcaster_class == broadcaster_class_process)
2471 debugger.GetCommandInterpreter().UpdateExecutionContext(NULL);
2473 continue; // Don't get any key, just update our view
2482 HandleCharResult key_result = m_window_sp->HandleChar(ch);
2486 debugger.GetCommandInterpreter().UpdateExecutionContext(NULL);
2489 case eKeyNotHandled:
2491 case eQuitApplication:
2498 debugger.CancelForwardEvents (listener_sp);
2506 m_window_sp.reset (new Window ("main", stdscr, false));
2511 GetWindowDelegates ()
2513 return m_window_delegates;
2517 WindowSP m_window_sp;
2518 WindowDelegates m_window_delegates;
2525 } // namespace curses
2528 using namespace curses;
2532 ValueObjectSP valobj;
2537 bool might_have_children;
2539 bool calculated_children;
2540 std::vector<Row> children;
2542 Row (const ValueObjectSP &v, Row *p) :
2548 might_have_children (v ? v->MightHaveChildren() : false),
2550 calculated_children (false),
2559 return 1 + parent->GetDepth();
2567 if (!calculated_children)
2569 calculated_children = true;
2572 const size_t num_children = valobj->GetNumChildren();
2573 for (size_t i=0; i<num_children; ++i)
2575 children.push_back(Row (valobj->GetChildAtIndex(i, true), this));
2588 DrawTree (Window &window)
2591 parent->DrawTreeForChild (window, this, 0);
2593 if (might_have_children)
2595 // It we can get UTF8 characters to work we should try to use the "symbol"
2596 // UTF8 string below
2597 // const char *symbol = "";
2598 // if (row.expanded)
2599 // symbol = "\xe2\x96\xbd ";
2601 // symbol = "\xe2\x96\xb7 ";
2602 // window.PutCString (symbol);
2604 // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
2605 // 'v' or '>' character...
2607 // window.PutChar (ACS_DARROW);
2609 // window.PutChar (ACS_RARROW);
2610 // Since we can't find any good looking right arrow/down arrow
2611 // symbols, just use a diamond...
2612 window.PutChar (ACS_DIAMOND);
2613 window.PutChar (ACS_HLINE);
2618 DrawTreeForChild (Window &window, Row *child, uint32_t reverse_depth)
2621 parent->DrawTreeForChild (window, this, reverse_depth + 1);
2623 if (&children.back() == child)
2626 if (reverse_depth == 0)
2628 window.PutChar (ACS_LLCORNER);
2629 window.PutChar (ACS_HLINE);
2633 window.PutChar (' ');
2634 window.PutChar (' ');
2639 if (reverse_depth == 0)
2641 window.PutChar (ACS_LTEE);
2642 window.PutChar (ACS_HLINE);
2646 window.PutChar (ACS_VLINE);
2647 window.PutChar (' ');
2653 struct DisplayOptions
2664 virtual ~TreeDelegate() {}
2665 virtual void TreeDelegateDrawTreeItem (TreeItem &item, Window &window) = 0;
2666 virtual void TreeDelegateGenerateChildren (TreeItem &item) = 0;
2667 virtual bool TreeDelegateItemSelected (TreeItem &item) = 0; // Return true if we need to update views
2669 typedef std::shared_ptr<TreeDelegate> TreeDelegateSP;
2675 TreeItem (TreeItem *parent, TreeDelegate &delegate, bool might_have_children) :
2677 m_delegate (delegate),
2682 m_might_have_children (might_have_children),
2683 m_is_expanded (false)
2688 operator=(const TreeItem &rhs)
2692 m_parent = rhs.m_parent;
2693 m_delegate = rhs.m_delegate;
2694 m_user_data = rhs.m_user_data;
2695 m_identifier = rhs.m_identifier;
2696 m_row_idx = rhs.m_row_idx;
2697 m_children = rhs.m_children;
2698 m_might_have_children = rhs.m_might_have_children;
2699 m_is_expanded = rhs.m_is_expanded;
2708 return 1 + m_parent->GetDepth();
2713 GetRowIndex () const
2725 Resize (size_t n, const TreeItem &t)
2727 m_children.resize(n, t);
2731 operator [](size_t i)
2733 return m_children[i];
2737 SetRowIndex (int row_idx)
2739 m_row_idx = row_idx;
2745 m_delegate.TreeDelegateGenerateChildren (*this);
2746 return m_children.size();
2752 m_delegate.TreeDelegateItemSelected(*this);
2755 CalculateRowIndexes (int &row_idx)
2757 SetRowIndex(row_idx);
2760 const bool expanded = IsExpanded();
2762 // The root item must calculate its children,
2763 // or we must calculate the number of children
2764 // if the item is expanded
2765 if (m_parent == NULL || expanded)
2768 for (auto &item : m_children)
2771 item.CalculateRowIndexes(row_idx);
2773 item.SetRowIndex(-1);
2786 return m_is_expanded;
2792 m_is_expanded = true;
2798 m_is_expanded = false;
2802 Draw (Window &window,
2803 const int first_visible_row,
2804 const uint32_t selected_row_idx,
2808 if (num_rows_left <= 0)
2811 if (m_row_idx >= first_visible_row)
2813 window.MoveCursor(2, row_idx + 1);
2816 m_parent->DrawTreeForChild (window, this, 0);
2818 if (m_might_have_children)
2820 // It we can get UTF8 characters to work we should try to use the "symbol"
2821 // UTF8 string below
2822 // const char *symbol = "";
2823 // if (row.expanded)
2824 // symbol = "\xe2\x96\xbd ";
2826 // symbol = "\xe2\x96\xb7 ";
2827 // window.PutCString (symbol);
2829 // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
2830 // 'v' or '>' character...
2832 // window.PutChar (ACS_DARROW);
2834 // window.PutChar (ACS_RARROW);
2835 // Since we can't find any good looking right arrow/down arrow
2836 // symbols, just use a diamond...
2837 window.PutChar (ACS_DIAMOND);
2838 window.PutChar (ACS_HLINE);
2841 (selected_row_idx == static_cast<size_t>(m_row_idx)) && window.IsActive();
2844 window.AttributeOn(A_REVERSE);
2846 m_delegate.TreeDelegateDrawTreeItem(*this, window);
2849 window.AttributeOff(A_REVERSE);
2854 if (num_rows_left <= 0)
2855 return false; // We are done drawing...
2859 for (auto &item : m_children)
2861 // If we displayed all the rows and item.Draw() returns
2862 // false we are done drawing and can exit this for loop
2863 if (item.Draw(window, first_visible_row, selected_row_idx, row_idx, num_rows_left) == false)
2867 return num_rows_left >= 0; // Return true if not done drawing yet
2871 DrawTreeForChild (Window &window, TreeItem *child, uint32_t reverse_depth)
2874 m_parent->DrawTreeForChild (window, this, reverse_depth + 1);
2876 if (&m_children.back() == child)
2879 if (reverse_depth == 0)
2881 window.PutChar (ACS_LLCORNER);
2882 window.PutChar (ACS_HLINE);
2886 window.PutChar (' ');
2887 window.PutChar (' ');
2892 if (reverse_depth == 0)
2894 window.PutChar (ACS_LTEE);
2895 window.PutChar (ACS_HLINE);
2899 window.PutChar (ACS_VLINE);
2900 window.PutChar (' ');
2906 GetItemForRowIndex (uint32_t row_idx)
2908 if (static_cast<uint32_t>(m_row_idx) == row_idx)
2910 if (m_children.empty())
2914 for (auto &item : m_children)
2916 TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx);
2917 if (selected_item_ptr)
2918 return selected_item_ptr;
2931 SetUserData (void *user_data)
2933 m_user_data = user_data;
2937 GetIdentifier() const
2939 return m_identifier;
2943 SetIdentifier (uint64_t identifier)
2945 m_identifier = identifier;
2950 SetMightHaveChildren (bool b)
2952 m_might_have_children = b;
2957 TreeDelegate &m_delegate;
2959 uint64_t m_identifier;
2960 int m_row_idx; // Zero based visible row index, -1 if not visible or for the root item
2961 std::vector<TreeItem> m_children;
2962 bool m_might_have_children;
2967 class TreeWindowDelegate : public WindowDelegate
2970 TreeWindowDelegate (Debugger &debugger, const TreeDelegateSP &delegate_sp) :
2971 m_debugger (debugger),
2972 m_delegate_sp (delegate_sp),
2973 m_root (NULL, *delegate_sp, true),
2974 m_selected_item (NULL),
2976 m_selected_row_idx (0),
2977 m_first_visible_row (0),
2986 NumVisibleRows () const
2988 return m_max_y - m_min_y;
2992 WindowDelegateDraw (Window &window, bool force) override
2994 ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext());
2995 Process *process = exe_ctx.GetProcessPtr();
2997 bool display_content = false;
3000 StateType state = process->GetState();
3001 if (StateIsStoppedState(state, true))
3003 // We are stopped, so it is ok to
3004 display_content = true;
3006 else if (StateIsRunningState(state))
3008 return true; // Don't do any updating when we are running
3014 m_max_x = window.GetWidth() - 1;
3015 m_max_y = window.GetHeight() - 1;
3018 window.DrawTitleBox (window.GetName());
3020 if (display_content)
3022 const int num_visible_rows = NumVisibleRows();
3024 m_root.CalculateRowIndexes(m_num_rows);
3026 // If we unexpanded while having something selected our
3027 // total number of rows is less than the num visible rows,
3028 // then make sure we show all the rows by setting the first
3029 // visible row accordingly.
3030 if (m_first_visible_row > 0 && m_num_rows < num_visible_rows)
3031 m_first_visible_row = 0;
3033 // Make sure the selected row is always visible
3034 if (m_selected_row_idx < m_first_visible_row)
3035 m_first_visible_row = m_selected_row_idx;
3036 else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
3037 m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
3040 int num_rows_left = num_visible_rows;
3041 m_root.Draw (window, m_first_visible_row, m_selected_row_idx, row_idx, num_rows_left);
3042 // Get the selected row
3043 m_selected_item = m_root.GetItemForRowIndex (m_selected_row_idx);
3047 m_selected_item = NULL;
3050 window.DeferredRefresh();
3053 return true; // Drawing handled
3058 WindowDelegateGetHelpText () override
3060 return "Thread window keyboard shortcuts:";
3064 WindowDelegateGetKeyHelp () override
3066 static curses::KeyHelp g_source_view_key_help[] = {
3067 { KEY_UP, "Select previous item" },
3068 { KEY_DOWN, "Select next item" },
3069 { KEY_RIGHT, "Expand the selected item" },
3070 { KEY_LEFT, "Unexpand the selected item or select parent if not expanded" },
3071 { KEY_PPAGE, "Page up" },
3072 { KEY_NPAGE, "Page down" },
3073 { 'h', "Show help dialog" },
3074 { ' ', "Toggle item expansion" },
3076 { '.', "Page down" },
3079 return g_source_view_key_help;
3083 WindowDelegateHandleChar (Window &window, int c) override
3090 if (m_first_visible_row > 0)
3092 if (m_first_visible_row > m_max_y)
3093 m_first_visible_row -= m_max_y;
3095 m_first_visible_row = 0;
3096 m_selected_row_idx = m_first_visible_row;
3097 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3098 if (m_selected_item)
3099 m_selected_item->ItemWasSelected ();
3106 if (m_num_rows > m_max_y)
3108 if (m_first_visible_row + m_max_y < m_num_rows)
3110 m_first_visible_row += m_max_y;
3111 m_selected_row_idx = m_first_visible_row;
3112 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3113 if (m_selected_item)
3114 m_selected_item->ItemWasSelected ();
3120 if (m_selected_row_idx > 0)
3122 --m_selected_row_idx;
3123 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3124 if (m_selected_item)
3125 m_selected_item->ItemWasSelected ();
3129 if (m_selected_row_idx + 1 < m_num_rows)
3131 ++m_selected_row_idx;
3132 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3133 if (m_selected_item)
3134 m_selected_item->ItemWasSelected ();
3139 if (m_selected_item)
3141 if (!m_selected_item->IsExpanded())
3142 m_selected_item->Expand();
3147 if (m_selected_item)
3149 if (m_selected_item->IsExpanded())
3150 m_selected_item->Unexpand();
3151 else if (m_selected_item->GetParent())
3153 m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex();
3154 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3155 if (m_selected_item)
3156 m_selected_item->ItemWasSelected ();
3162 // Toggle expansion state when SPACE is pressed
3163 if (m_selected_item)
3165 if (m_selected_item->IsExpanded())
3166 m_selected_item->Unexpand();
3168 m_selected_item->Expand();
3173 window.CreateHelpSubwindow ();
3179 return eKeyNotHandled;
3183 Debugger &m_debugger;
3184 TreeDelegateSP m_delegate_sp;
3186 TreeItem *m_selected_item;
3188 int m_selected_row_idx;
3189 int m_first_visible_row;
3197 class FrameTreeDelegate : public TreeDelegate
3200 FrameTreeDelegate () :
3203 FormatEntity::Parse ("frame #${frame.index}: {${function.name}${function.pc-offset}}}",
3207 ~FrameTreeDelegate() override
3212 TreeDelegateDrawTreeItem (TreeItem &item, Window &window) override
3214 Thread* thread = (Thread*)item.GetUserData();
3217 const uint64_t frame_idx = item.GetIdentifier();
3218 StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_idx);
3222 const SymbolContext &sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
3223 ExecutionContext exe_ctx (frame_sp);
3224 if (FormatEntity::Format(m_format, strm, &sc, &exe_ctx, NULL, NULL, false, false))
3227 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
3233 TreeDelegateGenerateChildren (TreeItem &item) override
3235 // No children for frames yet...
3239 TreeDelegateItemSelected (TreeItem &item) override
3241 Thread* thread = (Thread*)item.GetUserData();
3244 thread->GetProcess()->GetThreadList().SetSelectedThreadByID(thread->GetID());
3245 const uint64_t frame_idx = item.GetIdentifier();
3246 thread->SetSelectedFrameByIndex(frame_idx);
3252 FormatEntity::Entry m_format;
3255 class ThreadTreeDelegate : public TreeDelegate
3258 ThreadTreeDelegate (Debugger &debugger) :
3260 m_debugger (debugger),
3261 m_tid (LLDB_INVALID_THREAD_ID),
3262 m_stop_id (UINT32_MAX)
3264 FormatEntity::Parse ("thread #${thread.index}: tid = ${thread.id}{, stop reason = ${thread.stop-reason}}",
3268 ~ThreadTreeDelegate() override
3275 return m_debugger.GetCommandInterpreter().GetExecutionContext().GetProcessSP();
3279 GetThread (const TreeItem &item)
3281 ProcessSP process_sp = GetProcess ();
3283 return process_sp->GetThreadList().FindThreadByID(item.GetIdentifier());
3288 TreeDelegateDrawTreeItem (TreeItem &item, Window &window) override
3290 ThreadSP thread_sp = GetThread (item);
3294 ExecutionContext exe_ctx (thread_sp);
3295 if (FormatEntity::Format (m_format, strm, NULL, &exe_ctx, NULL, NULL, false, false))
3298 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
3303 TreeDelegateGenerateChildren (TreeItem &item) override
3305 ProcessSP process_sp = GetProcess ();
3306 if (process_sp && process_sp->IsAlive())
3308 StateType state = process_sp->GetState();
3309 if (StateIsStoppedState(state, true))
3311 ThreadSP thread_sp = GetThread (item);
3314 if (m_stop_id == process_sp->GetStopID() && thread_sp->GetID() == m_tid)
3315 return; // Children are already up to date
3316 if (!m_frame_delegate_sp)
3318 // Always expand the thread item the first time we show it
3319 m_frame_delegate_sp.reset (new FrameTreeDelegate());
3322 m_stop_id = process_sp->GetStopID();
3323 m_tid = thread_sp->GetID();
3325 TreeItem t (&item, *m_frame_delegate_sp, false);
3326 size_t num_frames = thread_sp->GetStackFrameCount();
3327 item.Resize (num_frames, t);
3328 for (size_t i=0; i<num_frames; ++i)
3330 item[i].SetUserData(thread_sp.get());
3331 item[i].SetIdentifier(i);
3337 item.ClearChildren();
3341 TreeDelegateItemSelected (TreeItem &item) override
3343 ProcessSP process_sp = GetProcess ();
3344 if (process_sp && process_sp->IsAlive())
3346 StateType state = process_sp->GetState();
3347 if (StateIsStoppedState(state, true))
3349 ThreadSP thread_sp = GetThread (item);
3352 ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList();
3353 Mutex::Locker locker (thread_list.GetMutex());
3354 ThreadSP selected_thread_sp = thread_list.GetSelectedThread();
3355 if (selected_thread_sp->GetID() != thread_sp->GetID())
3357 thread_list.SetSelectedThreadByID(thread_sp->GetID());
3367 Debugger &m_debugger;
3368 std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp;
3369 lldb::user_id_t m_tid;
3371 FormatEntity::Entry m_format;
3375 class ThreadsTreeDelegate : public TreeDelegate
3378 ThreadsTreeDelegate (Debugger &debugger) :
3380 m_thread_delegate_sp (),
3381 m_debugger (debugger),
3382 m_stop_id (UINT32_MAX)
3384 FormatEntity::Parse("process ${process.id}{, name = ${process.name}}",
3388 ~ThreadsTreeDelegate() override
3395 return m_debugger.GetCommandInterpreter().GetExecutionContext().GetProcessSP();
3399 TreeDelegateDrawTreeItem (TreeItem &item, Window &window) override
3401 ProcessSP process_sp = GetProcess ();
3402 if (process_sp && process_sp->IsAlive())
3405 ExecutionContext exe_ctx (process_sp);
3406 if (FormatEntity::Format (m_format, strm, NULL, &exe_ctx, NULL, NULL, false, false))
3409 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
3415 TreeDelegateGenerateChildren (TreeItem &item) override
3417 ProcessSP process_sp = GetProcess ();
3418 if (process_sp && process_sp->IsAlive())
3420 StateType state = process_sp->GetState();
3421 if (StateIsStoppedState(state, true))
3423 const uint32_t stop_id = process_sp->GetStopID();
3424 if (m_stop_id == stop_id)
3425 return; // Children are already up to date
3427 m_stop_id = stop_id;
3429 if (!m_thread_delegate_sp)
3431 // Always expand the thread item the first time we show it
3433 m_thread_delegate_sp.reset (new ThreadTreeDelegate(m_debugger));
3436 TreeItem t (&item, *m_thread_delegate_sp, false);
3437 ThreadList &threads = process_sp->GetThreadList();
3438 Mutex::Locker locker (threads.GetMutex());
3439 size_t num_threads = threads.GetSize();
3440 item.Resize (num_threads, t);
3441 for (size_t i=0; i<num_threads; ++i)
3443 item[i].SetIdentifier(threads.GetThreadAtIndex(i)->GetID());
3444 item[i].SetMightHaveChildren(true);
3449 item.ClearChildren();
3453 TreeDelegateItemSelected (TreeItem &item) override
3459 std::shared_ptr<ThreadTreeDelegate> m_thread_delegate_sp;
3460 Debugger &m_debugger;
3462 FormatEntity::Entry m_format;
3466 class ValueObjectListDelegate : public WindowDelegate
3469 ValueObjectListDelegate () :
3472 m_selected_row (NULL),
3473 m_selected_row_idx (0),
3474 m_first_visible_row (0),
3481 ValueObjectListDelegate (ValueObjectList &valobj_list) :
3482 m_valobj_list (valobj_list),
3484 m_selected_row (NULL),
3485 m_selected_row_idx (0),
3486 m_first_visible_row (0),
3491 SetValues (valobj_list);
3494 ~ValueObjectListDelegate() override
3499 SetValues (ValueObjectList &valobj_list)
3501 m_selected_row = NULL;
3502 m_selected_row_idx = 0;
3503 m_first_visible_row = 0;
3506 m_valobj_list = valobj_list;
3507 const size_t num_values = m_valobj_list.GetSize();
3508 for (size_t i=0; i<num_values; ++i)
3509 m_rows.push_back(Row(m_valobj_list.GetValueObjectAtIndex(i), NULL));
3513 WindowDelegateDraw (Window &window, bool force) override
3518 m_max_x = window.GetWidth() - 1;
3519 m_max_y = window.GetHeight() - 1;
3522 window.DrawTitleBox (window.GetName());
3524 const int num_visible_rows = NumVisibleRows();
3525 const int num_rows = CalculateTotalNumberRows (m_rows);
3527 // If we unexpanded while having something selected our
3528 // total number of rows is less than the num visible rows,
3529 // then make sure we show all the rows by setting the first
3530 // visible row accordingly.
3531 if (m_first_visible_row > 0 && num_rows < num_visible_rows)
3532 m_first_visible_row = 0;
3534 // Make sure the selected row is always visible
3535 if (m_selected_row_idx < m_first_visible_row)
3536 m_first_visible_row = m_selected_row_idx;
3537 else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
3538 m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
3540 DisplayRows (window, m_rows, g_options);
3542 window.DeferredRefresh();
3544 // Get the selected row
3545 m_selected_row = GetRowForRowIndex (m_selected_row_idx);
3546 // Keep the cursor on the selected row so the highlight and the cursor
3547 // are always on the same line
3549 window.MoveCursor (m_selected_row->x,
3552 return true; // Drawing handled
3556 WindowDelegateGetKeyHelp () override
3558 static curses::KeyHelp g_source_view_key_help[] = {
3559 { KEY_UP, "Select previous item" },
3560 { KEY_DOWN, "Select next item" },
3561 { KEY_RIGHT, "Expand selected item" },
3562 { KEY_LEFT, "Unexpand selected item or select parent if not expanded" },
3563 { KEY_PPAGE, "Page up" },
3564 { KEY_NPAGE, "Page down" },
3565 { 'A', "Format as annotated address" },
3566 { 'b', "Format as binary" },
3567 { 'B', "Format as hex bytes with ASCII" },
3568 { 'c', "Format as character" },
3569 { 'd', "Format as a signed integer" },
3570 { 'D', "Format selected value using the default format for the type" },
3571 { 'f', "Format as float" },
3572 { 'h', "Show help dialog" },
3573 { 'i', "Format as instructions" },
3574 { 'o', "Format as octal" },
3575 { 'p', "Format as pointer" },
3576 { 's', "Format as C string" },
3577 { 't', "Toggle showing/hiding type names" },
3578 { 'u', "Format as an unsigned integer" },
3579 { 'x', "Format as hex" },
3580 { 'X', "Format as uppercase hex" },
3581 { ' ', "Toggle item expansion" },
3583 { '.', "Page down" },
3586 return g_source_view_key_help;
3591 WindowDelegateHandleChar (Window &window, int c) override
3609 // Change the format for the currently selected item
3611 m_selected_row->valobj->SetFormat (FormatForChar (c));
3615 // Toggle showing type names
3616 g_options.show_types = !g_options.show_types;
3622 if (m_first_visible_row > 0)
3624 if (static_cast<int>(m_first_visible_row) > m_max_y)
3625 m_first_visible_row -= m_max_y;
3627 m_first_visible_row = 0;
3628 m_selected_row_idx = m_first_visible_row;
3635 if (m_num_rows > static_cast<size_t>(m_max_y))
3637 if (m_first_visible_row + m_max_y < m_num_rows)
3639 m_first_visible_row += m_max_y;
3640 m_selected_row_idx = m_first_visible_row;
3646 if (m_selected_row_idx > 0)
3647 --m_selected_row_idx;
3650 if (m_selected_row_idx + 1 < m_num_rows)
3651 ++m_selected_row_idx;
3657 if (!m_selected_row->expanded)
3658 m_selected_row->Expand();
3665 if (m_selected_row->expanded)
3666 m_selected_row->Unexpand();
3667 else if (m_selected_row->parent)
3668 m_selected_row_idx = m_selected_row->parent->row_idx;
3673 // Toggle expansion state when SPACE is pressed
3676 if (m_selected_row->expanded)
3677 m_selected_row->Unexpand();
3679 m_selected_row->Expand();
3684 window.CreateHelpSubwindow ();
3690 return eKeyNotHandled;
3694 ValueObjectList m_valobj_list;
3695 std::vector<Row> m_rows;
3696 Row *m_selected_row;
3697 uint32_t m_selected_row_idx;
3698 uint32_t m_first_visible_row;
3699 uint32_t m_num_rows;
3706 FormatForChar (int c)
3710 case 'x': return eFormatHex;
3711 case 'X': return eFormatHexUppercase;
3712 case 'o': return eFormatOctal;
3713 case 's': return eFormatCString;
3714 case 'u': return eFormatUnsigned;
3715 case 'd': return eFormatDecimal;
3716 case 'D': return eFormatDefault;
3717 case 'i': return eFormatInstruction;
3718 case 'A': return eFormatAddressInfo;
3719 case 'p': return eFormatPointer;
3720 case 'c': return eFormatChar;
3721 case 'b': return eFormatBinary;
3722 case 'B': return eFormatBytesWithASCII;
3723 case 'f': return eFormatFloat;
3725 return eFormatDefault;
3729 DisplayRowObject (Window &window,
3731 DisplayOptions &options,
3735 ValueObject *valobj = row.valobj.get();
3740 const char *type_name = options.show_types ? valobj->GetTypeName().GetCString() : NULL;
3741 const char *name = valobj->GetName().GetCString();
3742 const char *value = valobj->GetValueAsCString ();
3743 const char *summary = valobj->GetSummaryAsCString ();
3745 window.MoveCursor (row.x, row.y);
3747 row.DrawTree (window);
3750 window.AttributeOn(A_REVERSE);
3752 if (type_name && type_name[0])
3753 window.Printf ("(%s) ", type_name);
3755 if (name && name[0])
3756 window.PutCString(name);
3758 attr_t changd_attr = 0;
3759 if (valobj->GetValueDidChange())
3760 changd_attr = COLOR_PAIR(5) | A_BOLD;
3762 if (value && value[0])
3764 window.PutCString(" = ");
3766 window.AttributeOn(changd_attr);
3767 window.PutCString (value);
3769 window.AttributeOff(changd_attr);
3772 if (summary && summary[0])
3774 window.PutChar(' ');
3776 window.AttributeOn(changd_attr);
3777 window.PutCString(summary);
3779 window.AttributeOff(changd_attr);
3783 window.AttributeOff (A_REVERSE);
3788 DisplayRows (Window &window,
3789 std::vector<Row> &rows,
3790 DisplayOptions &options)
3795 bool window_is_active = window.IsActive();
3796 for (auto &row : rows)
3798 const bool last_child = row.parent && &rows[rows.size()-1] == &row;
3799 // Save the row index in each Row structure
3800 row.row_idx = m_num_rows;
3801 if ((m_num_rows >= m_first_visible_row) &&
3802 ((m_num_rows - m_first_visible_row) < static_cast<size_t>(NumVisibleRows())))
3805 row.y = m_num_rows - m_first_visible_row + 1;
3806 if (DisplayRowObject (window,
3809 window_is_active && m_num_rows == m_selected_row_idx,
3827 if (row.expanded && !row.children.empty())
3829 DisplayRows (window,
3837 CalculateTotalNumberRows (const std::vector<Row> &rows)
3840 for (const auto &row : rows)
3844 row_count += CalculateTotalNumberRows(row.children);
3849 GetRowForRowIndexImpl (std::vector<Row> &rows, size_t &row_index)
3851 for (auto &row : rows)
3858 if (row.expanded && !row.children.empty())
3860 Row *result = GetRowForRowIndexImpl (row.children, row_index);
3870 GetRowForRowIndex (size_t row_index)
3872 return GetRowForRowIndexImpl (m_rows, row_index);
3876 NumVisibleRows () const
3878 return m_max_y - m_min_y;
3881 static DisplayOptions g_options;
3884 class FrameVariablesWindowDelegate : public ValueObjectListDelegate
3887 FrameVariablesWindowDelegate (Debugger &debugger) :
3888 ValueObjectListDelegate (),
3889 m_debugger (debugger),
3890 m_frame_block (NULL)
3894 ~FrameVariablesWindowDelegate() override
3899 WindowDelegateGetHelpText () override
3901 return "Frame variable window keyboard shortcuts:";
3905 WindowDelegateDraw (Window &window, bool force) override
3907 ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext());
3908 Process *process = exe_ctx.GetProcessPtr();
3909 Block *frame_block = NULL;
3910 StackFrame *frame = NULL;
3914 StateType state = process->GetState();
3915 if (StateIsStoppedState(state, true))
3917 frame = exe_ctx.GetFramePtr();
3919 frame_block = frame->GetFrameBlock ();
3921 else if (StateIsRunningState(state))
3923 return true; // Don't do any updating when we are running
3928 ValueObjectList local_values;
3931 // Only update the variables if they have changed
3932 if (m_frame_block != frame_block)
3934 m_frame_block = frame_block;
3936 VariableList *locals = frame->GetVariableList(true);
3939 const DynamicValueType use_dynamic = eDynamicDontRunTarget;
3940 const size_t num_locals = locals->GetSize();
3941 for (size_t i=0; i<num_locals; ++i)
3943 ValueObjectSP value_sp = frame->GetValueObjectForFrameVariable (locals->GetVariableAtIndex(i), use_dynamic);
3946 ValueObjectSP synthetic_value_sp = value_sp->GetSyntheticValue();
3947 if (synthetic_value_sp)
3948 local_values.Append(synthetic_value_sp);
3950 local_values.Append(value_sp);
3954 // Update the values
3955 SetValues(local_values);
3961 m_frame_block = NULL;
3962 // Update the values with an empty list if there is no frame
3963 SetValues(local_values);
3966 return ValueObjectListDelegate::WindowDelegateDraw (window, force);
3971 Debugger &m_debugger;
3972 Block *m_frame_block;
3976 class RegistersWindowDelegate : public ValueObjectListDelegate
3979 RegistersWindowDelegate (Debugger &debugger) :
3980 ValueObjectListDelegate (),
3981 m_debugger (debugger)
3985 ~RegistersWindowDelegate()
3990 WindowDelegateGetHelpText () override
3992 return "Register window keyboard shortcuts:";
3996 WindowDelegateDraw (Window &window, bool force) override
3998 ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext());
3999 StackFrame *frame = exe_ctx.GetFramePtr();
4001 ValueObjectList value_list;
4004 if (frame->GetStackID() != m_stack_id)
4006 m_stack_id = frame->GetStackID();
4007 RegisterContextSP reg_ctx (frame->GetRegisterContext());
4010 const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
4011 for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx)
4013 value_list.Append(ValueObjectRegisterSet::Create (frame, reg_ctx, set_idx));
4016 SetValues(value_list);
4021 Process *process = exe_ctx.GetProcessPtr();
4022 if (process && process->IsAlive())
4023 return true; // Don't do any updating if we are running
4026 // Update the values with an empty list if there
4027 // is no process or the process isn't alive anymore
4028 SetValues(value_list);
4031 return ValueObjectListDelegate::WindowDelegateDraw (window, force);
4035 Debugger &m_debugger;
4040 CursesKeyToCString (int ch)
4042 static char g_desc[32];
4043 if (ch >= KEY_F0 && ch < KEY_F0 + 64)
4045 snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0);
4050 case KEY_DOWN: return "down";
4051 case KEY_UP: return "up";
4052 case KEY_LEFT: return "left";
4053 case KEY_RIGHT: return "right";
4054 case KEY_HOME: return "home";
4055 case KEY_BACKSPACE: return "backspace";
4056 case KEY_DL: return "delete-line";
4057 case KEY_IL: return "insert-line";
4058 case KEY_DC: return "delete-char";
4059 case KEY_IC: return "insert-char";
4060 case KEY_CLEAR: return "clear";
4061 case KEY_EOS: return "clear-to-eos";
4062 case KEY_EOL: return "clear-to-eol";
4063 case KEY_SF: return "scroll-forward";
4064 case KEY_SR: return "scroll-backward";
4065 case KEY_NPAGE: return "page-down";
4066 case KEY_PPAGE: return "page-up";
4067 case KEY_STAB: return "set-tab";
4068 case KEY_CTAB: return "clear-tab";
4069 case KEY_CATAB: return "clear-all-tabs";
4070 case KEY_ENTER: return "enter";
4071 case KEY_PRINT: return "print";
4072 case KEY_LL: return "lower-left key";
4073 case KEY_A1: return "upper left of keypad";
4074 case KEY_A3: return "upper right of keypad";
4075 case KEY_B2: return "center of keypad";
4076 case KEY_C1: return "lower left of keypad";
4077 case KEY_C3: return "lower right of keypad";
4078 case KEY_BTAB: return "back-tab key";
4079 case KEY_BEG: return "begin key";
4080 case KEY_CANCEL: return "cancel key";
4081 case KEY_CLOSE: return "close key";
4082 case KEY_COMMAND: return "command key";
4083 case KEY_COPY: return "copy key";
4084 case KEY_CREATE: return "create key";
4085 case KEY_END: return "end key";
4086 case KEY_EXIT: return "exit key";
4087 case KEY_FIND: return "find key";
4088 case KEY_HELP: return "help key";
4089 case KEY_MARK: return "mark key";
4090 case KEY_MESSAGE: return "message key";
4091 case KEY_MOVE: return "move key";
4092 case KEY_NEXT: return "next key";
4093 case KEY_OPEN: return "open key";
4094 case KEY_OPTIONS: return "options key";
4095 case KEY_PREVIOUS: return "previous key";
4096 case KEY_REDO: return "redo key";
4097 case KEY_REFERENCE: return "reference key";
4098 case KEY_REFRESH: return "refresh key";
4099 case KEY_REPLACE: return "replace key";
4100 case KEY_RESTART: return "restart key";
4101 case KEY_RESUME: return "resume key";
4102 case KEY_SAVE: return "save key";
4103 case KEY_SBEG: return "shifted begin key";
4104 case KEY_SCANCEL: return "shifted cancel key";
4105 case KEY_SCOMMAND: return "shifted command key";
4106 case KEY_SCOPY: return "shifted copy key";
4107 case KEY_SCREATE: return "shifted create key";
4108 case KEY_SDC: return "shifted delete-character key";
4109 case KEY_SDL: return "shifted delete-line key";
4110 case KEY_SELECT: return "select key";
4111 case KEY_SEND: return "shifted end key";
4112 case KEY_SEOL: return "shifted clear-to-end-of-line key";
4113 case KEY_SEXIT: return "shifted exit key";
4114 case KEY_SFIND: return "shifted find key";
4115 case KEY_SHELP: return "shifted help key";
4116 case KEY_SHOME: return "shifted home key";
4117 case KEY_SIC: return "shifted insert-character key";
4118 case KEY_SLEFT: return "shifted left-arrow key";
4119 case KEY_SMESSAGE: return "shifted message key";
4120 case KEY_SMOVE: return "shifted move key";
4121 case KEY_SNEXT: return "shifted next key";
4122 case KEY_SOPTIONS: return "shifted options key";
4123 case KEY_SPREVIOUS: return "shifted previous key";
4124 case KEY_SPRINT: return "shifted print key";
4125 case KEY_SREDO: return "shifted redo key";
4126 case KEY_SREPLACE: return "shifted replace key";
4127 case KEY_SRIGHT: return "shifted right-arrow key";
4128 case KEY_SRSUME: return "shifted resume key";
4129 case KEY_SSAVE: return "shifted save key";
4130 case KEY_SSUSPEND: return "shifted suspend key";
4131 case KEY_SUNDO: return "shifted undo key";
4132 case KEY_SUSPEND: return "suspend key";
4133 case KEY_UNDO: return "undo key";
4134 case KEY_MOUSE: return "Mouse event has occurred";
4135 case KEY_RESIZE: return "Terminal resize event";
4136 case KEY_EVENT: return "We were interrupted by an event";
4137 case KEY_RETURN: return "return";
4138 case ' ': return "space";
4139 case '\t': return "tab";
4140 case KEY_ESCAPE: return "escape";
4143 snprintf(g_desc, sizeof(g_desc), "%c", ch);
4145 snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch);
4151 HelpDialogDelegate::HelpDialogDelegate (const char *text, KeyHelp *key_help_array) :
4153 m_first_visible_line (0)
4155 if (text && text[0])
4157 m_text.SplitIntoLines(text);
4158 m_text.AppendString("");
4162 for (KeyHelp *key = key_help_array; key->ch; ++key)
4164 StreamString key_description;
4165 key_description.Printf("%10s - %s", CursesKeyToCString(key->ch), key->description);
4166 m_text.AppendString(std::move(key_description.GetString()));
4171 HelpDialogDelegate::~HelpDialogDelegate()
4176 HelpDialogDelegate::WindowDelegateDraw (Window &window, bool force)
4179 const int window_height = window.GetHeight();
4182 const int min_y = y;
4183 const int max_y = window_height - 1 - y;
4184 const size_t num_visible_lines = max_y - min_y + 1;
4185 const size_t num_lines = m_text.GetSize();
4186 const char *bottom_message;
4187 if (num_lines <= num_visible_lines)
4188 bottom_message = "Press any key to exit";
4190 bottom_message = "Use arrows to scroll, any other key to exit";
4191 window.DrawTitleBox(window.GetName(), bottom_message);
4194 window.MoveCursor(x, y);
4195 window.PutCStringTruncated(m_text.GetStringAtIndex(m_first_visible_line + y - min_y), 1);
4202 HelpDialogDelegate::WindowDelegateHandleChar (Window &window, int key)
4205 const size_t num_lines = m_text.GetSize();
4206 const size_t num_visible_lines = window.GetHeight() - 2;
4208 if (num_lines <= num_visible_lines)
4211 // If we have all lines visible and don't need scrolling, then any
4212 // key press will cause us to exit
4219 if (m_first_visible_line > 0)
4220 --m_first_visible_line;
4224 if (m_first_visible_line + num_visible_lines < num_lines)
4225 ++m_first_visible_line;
4230 if (m_first_visible_line > 0)
4232 if (static_cast<size_t>(m_first_visible_line) >= num_visible_lines)
4233 m_first_visible_line -= num_visible_lines;
4235 m_first_visible_line = 0;
4240 if (m_first_visible_line + num_visible_lines < num_lines)
4242 m_first_visible_line += num_visible_lines;
4243 if (static_cast<size_t>(m_first_visible_line) > num_lines)
4244 m_first_visible_line = num_lines - num_visible_lines;
4253 window.GetParent()->RemoveSubWindow(&window);
4257 class ApplicationDelegate :
4258 public WindowDelegate,
4268 eMenuID_TargetCreate,
4269 eMenuID_TargetDelete,
4272 eMenuID_ProcessAttach,
4273 eMenuID_ProcessDetach,
4274 eMenuID_ProcessLaunch,
4275 eMenuID_ProcessContinue,
4276 eMenuID_ProcessHalt,
4277 eMenuID_ProcessKill,
4280 eMenuID_ThreadStepIn,
4281 eMenuID_ThreadStepOver,
4282 eMenuID_ThreadStepOut,
4285 eMenuID_ViewBacktrace,
4286 eMenuID_ViewRegisters,
4288 eMenuID_ViewVariables,
4294 ApplicationDelegate (Application &app, Debugger &debugger) :
4298 m_debugger (debugger)
4302 ~ApplicationDelegate ()
4307 WindowDelegateDraw (Window &window, bool force) override
4309 return false; // Drawing not handled, let standard window drawing happen
4313 WindowDelegateHandleChar (Window &window, int key) override
4318 window.SelectNextWindowAsActive();
4322 window.CreateHelpSubwindow();
4326 return eQuitApplication;
4331 return eKeyNotHandled;
4336 WindowDelegateGetHelpText () override
4338 return "Welcome to the LLDB curses GUI.\n\n"
4339 "Press the TAB key to change the selected view.\n"
4340 "Each view has its own keyboard shortcuts, press 'h' to open a dialog to display them.\n\n"
4341 "Common key bindings for all views:";
4345 WindowDelegateGetKeyHelp () override
4347 static curses::KeyHelp g_source_view_key_help[] = {
4348 { '\t', "Select next view" },
4349 { 'h', "Show help dialog with view specific key bindings" },
4351 { '.', "Page down" },
4352 { KEY_UP, "Select previous" },
4353 { KEY_DOWN, "Select next" },
4354 { KEY_LEFT, "Unexpand or select parent" },
4355 { KEY_RIGHT, "Expand" },
4356 { KEY_PPAGE, "Page up" },
4357 { KEY_NPAGE, "Page down" },
4360 return g_source_view_key_help;
4364 MenuDelegateAction (Menu &menu) override
4366 switch (menu.GetIdentifier())
4368 case eMenuID_ThreadStepIn:
4370 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4371 if (exe_ctx.HasThreadScope())
4373 Process *process = exe_ctx.GetProcessPtr();
4374 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4375 exe_ctx.GetThreadRef().StepIn(true);
4378 return MenuActionResult::Handled;
4380 case eMenuID_ThreadStepOut:
4382 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4383 if (exe_ctx.HasThreadScope())
4385 Process *process = exe_ctx.GetProcessPtr();
4386 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4387 exe_ctx.GetThreadRef().StepOut();
4390 return MenuActionResult::Handled;
4392 case eMenuID_ThreadStepOver:
4394 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4395 if (exe_ctx.HasThreadScope())
4397 Process *process = exe_ctx.GetProcessPtr();
4398 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4399 exe_ctx.GetThreadRef().StepOver(true);
4402 return MenuActionResult::Handled;
4404 case eMenuID_ProcessContinue:
4406 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4407 if (exe_ctx.HasProcessScope())
4409 Process *process = exe_ctx.GetProcessPtr();
4410 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4414 return MenuActionResult::Handled;
4416 case eMenuID_ProcessKill:
4418 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4419 if (exe_ctx.HasProcessScope())
4421 Process *process = exe_ctx.GetProcessPtr();
4422 if (process && process->IsAlive())
4423 process->Destroy(false);
4426 return MenuActionResult::Handled;
4428 case eMenuID_ProcessHalt:
4430 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4431 if (exe_ctx.HasProcessScope())
4433 Process *process = exe_ctx.GetProcessPtr();
4434 if (process && process->IsAlive())
4438 return MenuActionResult::Handled;
4440 case eMenuID_ProcessDetach:
4442 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4443 if (exe_ctx.HasProcessScope())
4445 Process *process = exe_ctx.GetProcessPtr();
4446 if (process && process->IsAlive())
4447 process->Detach(false);
4450 return MenuActionResult::Handled;
4452 case eMenuID_Process:
4454 // Populate the menu with all of the threads if the process is stopped when
4455 // the Process menu gets selected and is about to display its submenu.
4456 Menus &submenus = menu.GetSubmenus();
4457 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4458 Process *process = exe_ctx.GetProcessPtr();
4459 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4461 if (submenus.size() == 7)
4462 menu.AddSubmenu (MenuSP (new Menu(Menu::Type::Separator)));
4463 else if (submenus.size() > 8)
4464 submenus.erase (submenus.begin() + 8, submenus.end());
4466 ThreadList &threads = process->GetThreadList();
4467 Mutex::Locker locker (threads.GetMutex());
4468 size_t num_threads = threads.GetSize();
4469 for (size_t i=0; i<num_threads; ++i)
4471 ThreadSP thread_sp = threads.GetThreadAtIndex(i);
4472 char menu_char = '\0';
4474 menu_char = '1' + i;
4475 StreamString thread_menu_title;
4476 thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID());
4477 const char *thread_name = thread_sp->GetName();
4478 if (thread_name && thread_name[0])
4479 thread_menu_title.Printf (" %s", thread_name);
4482 const char *queue_name = thread_sp->GetQueueName();
4483 if (queue_name && queue_name[0])
4484 thread_menu_title.Printf (" %s", queue_name);
4486 menu.AddSubmenu (MenuSP (new Menu(thread_menu_title.GetString().c_str(), NULL, menu_char, thread_sp->GetID())));
4489 else if (submenus.size() > 7)
4491 // Remove the separator and any other thread submenu items
4492 // that were previously added
4493 submenus.erase (submenus.begin() + 7, submenus.end());
4495 // Since we are adding and removing items we need to recalculate the name lengths
4496 menu.RecalculateNameLengths();
4498 return MenuActionResult::Handled;
4500 case eMenuID_ViewVariables:
4502 WindowSP main_window_sp = m_app.GetMainWindow();
4503 WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
4504 WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
4505 WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
4506 const Rect source_bounds = source_window_sp->GetBounds();
4508 if (variables_window_sp)
4510 const Rect variables_bounds = variables_window_sp->GetBounds();
4512 main_window_sp->RemoveSubWindow(variables_window_sp.get());
4514 if (registers_window_sp)
4516 // We have a registers window, so give all the area back to the registers window
4517 Rect registers_bounds = variables_bounds;
4518 registers_bounds.size.width = source_bounds.size.width;
4519 registers_window_sp->SetBounds(registers_bounds);
4523 // We have no registers window showing so give the bottom
4524 // area back to the source view
4525 source_window_sp->Resize (source_bounds.size.width,
4526 source_bounds.size.height + variables_bounds.size.height);
4531 Rect new_variables_rect;
4532 if (registers_window_sp)
4534 // We have a registers window so split the area of the registers
4535 // window into two columns where the left hand side will be the
4536 // variables and the right hand side will be the registers
4537 const Rect variables_bounds = registers_window_sp->GetBounds();
4538 Rect new_registers_rect;
4539 variables_bounds.VerticalSplitPercentage (0.50, new_variables_rect, new_registers_rect);
4540 registers_window_sp->SetBounds (new_registers_rect);
4544 // No variables window, grab the bottom part of the source window
4545 Rect new_source_rect;
4546 source_bounds.HorizontalSplitPercentage (0.70, new_source_rect, new_variables_rect);
4547 source_window_sp->SetBounds (new_source_rect);
4549 WindowSP new_window_sp = main_window_sp->CreateSubWindow ("Variables",
4552 new_window_sp->SetDelegate (WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
4556 return MenuActionResult::Handled;
4558 case eMenuID_ViewRegisters:
4560 WindowSP main_window_sp = m_app.GetMainWindow();
4561 WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
4562 WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
4563 WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
4564 const Rect source_bounds = source_window_sp->GetBounds();
4566 if (registers_window_sp)
4568 if (variables_window_sp)
4570 const Rect variables_bounds = variables_window_sp->GetBounds();
4572 // We have a variables window, so give all the area back to the variables window
4573 variables_window_sp->Resize (variables_bounds.size.width + registers_window_sp->GetWidth(),
4574 variables_bounds.size.height);
4578 // We have no variables window showing so give the bottom
4579 // area back to the source view
4580 source_window_sp->Resize (source_bounds.size.width,
4581 source_bounds.size.height + registers_window_sp->GetHeight());
4583 main_window_sp->RemoveSubWindow(registers_window_sp.get());
4588 if (variables_window_sp)
4590 // We have a variables window, split it into two columns
4591 // where the left hand side will be the variables and the
4592 // right hand side will be the registers
4593 const Rect variables_bounds = variables_window_sp->GetBounds();
4595 variables_bounds.VerticalSplitPercentage (0.50, new_vars_rect, new_regs_rect);
4596 variables_window_sp->SetBounds (new_vars_rect);
4600 // No registers window, grab the bottom part of the source window
4601 Rect new_source_rect;
4602 source_bounds.HorizontalSplitPercentage (0.70, new_source_rect, new_regs_rect);
4603 source_window_sp->SetBounds (new_source_rect);
4605 WindowSP new_window_sp = main_window_sp->CreateSubWindow ("Registers",
4608 new_window_sp->SetDelegate (WindowDelegateSP(new RegistersWindowDelegate(m_debugger)));
4612 return MenuActionResult::Handled;
4614 case eMenuID_HelpGUIHelp:
4615 m_app.GetMainWindow ()->CreateHelpSubwindow();
4616 return MenuActionResult::Handled;
4622 return MenuActionResult::NotHandled;
4626 Debugger &m_debugger;
4630 class StatusBarWindowDelegate : public WindowDelegate
4633 StatusBarWindowDelegate (Debugger &debugger) :
4634 m_debugger (debugger)
4636 FormatEntity::Parse("Thread: ${thread.id%tid}",
4640 ~StatusBarWindowDelegate ()
4645 WindowDelegateDraw (Window &window, bool force) override
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 if (thread && FormatEntity::Format (m_format, strm, NULL, &exe_ctx, NULL, NULL, false, false))
4664 window.MoveCursor (40, 0);
4665 window.PutCStringTruncated(strm.GetString().c_str(), 1);
4668 window.MoveCursor (60, 0);
4670 window.Printf ("Frame: %3u PC = 0x%16.16" PRIx64, frame->GetFrameIndex(), frame->GetFrameCodeAddress().GetOpcodeLoadAddress (exe_ctx.GetTargetPtr()));
4672 else if (state == eStateExited)
4674 const char *exit_desc = process->GetExitDescription();
4675 const int exit_status = process->GetExitStatus();
4676 if (exit_desc && exit_desc[0])
4677 window.Printf (" with status = %i (%s)", exit_status, exit_desc);
4679 window.Printf (" with status = %i", exit_status);
4682 window.DeferredRefresh();
4687 Debugger &m_debugger;
4688 FormatEntity::Entry m_format;
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),
4716 ~SourceFileWindowDelegate() override
4721 Update (const SymbolContext &sc)
4727 NumVisibleLines () const
4729 return m_max_y - m_min_y;
4733 WindowDelegateGetHelpText () override
4735 return "Source/Disassembly window keyboard shortcuts:";
4739 WindowDelegateGetKeyHelp () override
4741 static curses::KeyHelp g_source_view_key_help[] = {
4742 { KEY_RETURN, "Run to selected line with one shot breakpoint" },
4743 { KEY_UP, "Select previous source line" },
4744 { KEY_DOWN, "Select next source line" },
4745 { KEY_PPAGE, "Page up" },
4746 { KEY_NPAGE, "Page down" },
4747 { 'b', "Set breakpoint on selected source/disassembly line" },
4748 { 'c', "Continue process" },
4749 { 'd', "Detach and resume process" },
4750 { 'D', "Detach with process suspended" },
4751 { 'h', "Show help dialog" },
4752 { 'k', "Kill process" },
4753 { 'n', "Step over (source line)" },
4754 { 'N', "Step over (single instruction)" },
4755 { 'o', "Step out" },
4756 { 's', "Step in (source line)" },
4757 { 'S', "Step in (single instruction)" },
4759 { '.', "Page down" },
4762 return g_source_view_key_help;
4766 WindowDelegateDraw (Window &window, bool force) override
4768 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4769 Process *process = exe_ctx.GetProcessPtr();
4770 Thread *thread = NULL;
4772 bool update_location = false;
4775 StateType state = process->GetState();
4776 if (StateIsStoppedState(state, true))
4778 // We are stopped, so it is ok to
4779 update_location = true;
4785 m_max_x = window.GetMaxX()-1;
4786 m_max_y = window.GetMaxY()-1;
4788 const uint32_t num_visible_lines = NumVisibleLines();
4789 StackFrameSP frame_sp;
4790 bool set_selected_line_to_pc = false;
4792 if (update_location)
4794 const bool process_alive = process ? process->IsAlive() : false;
4795 bool thread_changed = false;
4798 thread = exe_ctx.GetThreadPtr();
4801 frame_sp = thread->GetSelectedFrame();
4802 auto tid = thread->GetID();
4803 thread_changed = tid != m_tid;
4808 if (m_tid != LLDB_INVALID_THREAD_ID)
4810 thread_changed = true;
4811 m_tid = LLDB_INVALID_THREAD_ID;
4815 const uint32_t stop_id = process ? process->GetStopID() : 0;
4816 const bool stop_id_changed = stop_id != m_stop_id;
4817 bool frame_changed = false;
4818 m_stop_id = stop_id;
4822 m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
4825 m_title.Printf("%s", m_sc.module_sp->GetFileSpec().GetFilename().GetCString());
4826 ConstString func_name = m_sc.GetFunctionName();
4828 m_title.Printf("`%s", func_name.GetCString());
4830 const uint32_t frame_idx = frame_sp->GetFrameIndex();
4831 frame_changed = frame_idx != m_frame_idx;
4832 m_frame_idx = frame_idx;
4837 frame_changed = m_frame_idx != UINT32_MAX;
4838 m_frame_idx = UINT32_MAX;
4841 const bool context_changed = thread_changed || frame_changed || stop_id_changed;
4845 if (m_sc.line_entry.IsValid())
4847 m_pc_line = m_sc.line_entry.line;
4848 if (m_pc_line != UINT32_MAX)
4849 --m_pc_line; // Convert to zero based line number...
4850 // Update the selected line if the stop ID changed...
4851 if (context_changed)
4852 m_selected_line = m_pc_line;
4854 if (m_file_sp && m_file_sp->FileSpecMatches(m_sc.line_entry.file))
4856 // Same file, nothing to do, we should either have the
4857 // lines or not (source file missing)
4858 if (m_selected_line >= static_cast<size_t>(m_first_visible_line))
4860 if (m_selected_line >= m_first_visible_line + num_visible_lines)
4861 m_first_visible_line = m_selected_line - 10;
4865 if (m_selected_line > 10)
4866 m_first_visible_line = m_selected_line - 10;
4868 m_first_visible_line = 0;
4873 // File changed, set selected line to the line with the PC
4874 m_selected_line = m_pc_line;
4875 m_file_sp = m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file);
4878 const size_t num_lines = m_file_sp->GetNumLines();
4879 int m_line_width = 1;
4880 for (size_t n = num_lines; n >= 10; n = n / 10)
4883 snprintf (m_line_format, sizeof(m_line_format), " %%%iu ", m_line_width);
4884 if (num_lines < num_visible_lines || m_selected_line < num_visible_lines)
4885 m_first_visible_line = 0;
4887 m_first_visible_line = m_selected_line - 10;
4896 if (!m_file_sp || m_file_sp->GetNumLines() == 0)
4899 bool prefer_file_cache = false;
4902 if (m_disassembly_scope != m_sc.function)
4904 m_disassembly_scope = m_sc.function;
4905 m_disassembly_sp = m_sc.function->GetInstructions (exe_ctx, NULL, prefer_file_cache);
4906 if (m_disassembly_sp)
4908 set_selected_line_to_pc = true;
4909 m_disassembly_range = m_sc.function->GetAddressRange();
4913 m_disassembly_range.Clear();
4918 set_selected_line_to_pc = context_changed;
4921 else if (m_sc.symbol)
4923 if (m_disassembly_scope != m_sc.symbol)
4925 m_disassembly_scope = m_sc.symbol;
4926 m_disassembly_sp = m_sc.symbol->GetInstructions (exe_ctx, NULL, prefer_file_cache);
4927 if (m_disassembly_sp)
4929 set_selected_line_to_pc = true;
4930 m_disassembly_range.GetBaseAddress() = m_sc.symbol->GetAddress();
4931 m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize());
4935 m_disassembly_range.Clear();
4940 set_selected_line_to_pc = context_changed;
4947 m_pc_line = UINT32_MAX;
4951 const int window_width = window.GetWidth();
4953 window.DrawTitleBox ("Sources");
4954 if (!m_title.GetString().empty())
4956 window.AttributeOn(A_REVERSE);
4957 window.MoveCursor(1, 1);
4958 window.PutChar(' ');
4959 window.PutCStringTruncated(m_title.GetString().c_str(), 1);
4960 int x = window.GetCursorX();
4961 if (x < window_width - 1)
4963 window.Printf ("%*s", window_width - x - 1, "");
4965 window.AttributeOff(A_REVERSE);
4968 Target *target = exe_ctx.GetTargetPtr();
4969 const size_t num_source_lines = GetNumSourceLines();
4970 if (num_source_lines > 0)
4973 BreakpointLines bp_lines;
4976 BreakpointList &bp_list = target->GetBreakpointList();
4977 const size_t num_bps = bp_list.GetSize();
4978 for (size_t bp_idx=0; bp_idx<num_bps; ++bp_idx)
4980 BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
4981 const size_t num_bps_locs = bp_sp->GetNumLocations();
4982 for (size_t bp_loc_idx=0; bp_loc_idx<num_bps_locs; ++bp_loc_idx)
4984 BreakpointLocationSP bp_loc_sp = bp_sp->GetLocationAtIndex(bp_loc_idx);
4985 LineEntry bp_loc_line_entry;
4986 if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry (bp_loc_line_entry))
4988 if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file)
4990 bp_lines.insert(bp_loc_line_entry.line);
4997 const attr_t selected_highlight_attr = A_REVERSE;
4998 const attr_t pc_highlight_attr = COLOR_PAIR(1);
5000 for (size_t i=0; i<num_visible_lines; ++i)
5002 const uint32_t curr_line = m_first_visible_line + i;
5003 if (curr_line < num_source_lines)
5005 const int line_y = m_min_y+i;
5006 window.MoveCursor(1, line_y);
5007 const bool is_pc_line = curr_line == m_pc_line;
5008 const bool line_is_selected = m_selected_line == curr_line;
5009 // Highlight the line as the PC line first, then if the selected line
5010 // isn't the same as the PC line, highlight it differently
5011 attr_t highlight_attr = 0;
5014 highlight_attr = pc_highlight_attr;
5015 else if (line_is_selected)
5016 highlight_attr = selected_highlight_attr;
5018 if (bp_lines.find(curr_line+1) != bp_lines.end())
5019 bp_attr = COLOR_PAIR(2);
5022 window.AttributeOn(bp_attr);
5024 window.Printf (m_line_format, curr_line + 1);
5027 window.AttributeOff(bp_attr);
5029 window.PutChar(ACS_VLINE);
5030 // Mark the line with the PC with a diamond
5032 window.PutChar(ACS_DIAMOND);
5034 window.PutChar(' ');
5037 window.AttributeOn(highlight_attr);
5038 const uint32_t line_len = m_file_sp->GetLineLength(curr_line + 1, false);
5040 window.PutCString(m_file_sp->PeekLineData(curr_line + 1), line_len);
5042 if (is_pc_line && frame_sp && frame_sp->GetConcreteFrameIndex() == 0)
5044 StopInfoSP stop_info_sp;
5046 stop_info_sp = thread->GetStopInfo();
5049 const char *stop_description = stop_info_sp->GetDescription();
5050 if (stop_description && stop_description[0])
5052 size_t stop_description_len = strlen(stop_description);
5053 int desc_x = window_width - stop_description_len - 16;
5054 window.Printf ("%*s", desc_x - window.GetCursorX(), "");
5055 //window.MoveCursor(window_width - stop_description_len - 15, line_y);
5056 window.Printf ("<<< Thread %u: %s ", thread->GetIndexID(), stop_description);
5061 window.Printf ("%*s", window_width - window.GetCursorX() - 1, "");
5065 window.AttributeOff(highlight_attr);
5076 size_t num_disassembly_lines = GetNumDisassemblyLines();
5077 if (num_disassembly_lines > 0)
5079 // Display disassembly
5080 BreakpointAddrs bp_file_addrs;
5081 Target *target = exe_ctx.GetTargetPtr();
5084 BreakpointList &bp_list = target->GetBreakpointList();
5085 const size_t num_bps = bp_list.GetSize();
5086 for (size_t bp_idx=0; bp_idx<num_bps; ++bp_idx)
5088 BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
5089 const size_t num_bps_locs = bp_sp->GetNumLocations();
5090 for (size_t bp_loc_idx=0; bp_loc_idx<num_bps_locs; ++bp_loc_idx)
5092 BreakpointLocationSP bp_loc_sp = bp_sp->GetLocationAtIndex(bp_loc_idx);
5093 LineEntry bp_loc_line_entry;
5094 const lldb::addr_t file_addr = bp_loc_sp->GetAddress().GetFileAddress();
5095 if (file_addr != LLDB_INVALID_ADDRESS)
5097 if (m_disassembly_range.ContainsFileAddress(file_addr))
5098 bp_file_addrs.insert(file_addr);
5104 const attr_t selected_highlight_attr = A_REVERSE;
5105 const attr_t pc_highlight_attr = COLOR_PAIR(1);
5109 InstructionList &insts = m_disassembly_sp->GetInstructionList();
5113 pc_address = frame_sp->GetFrameCodeAddress();
5114 const uint32_t pc_idx = pc_address.IsValid() ? insts.GetIndexOfInstructionAtAddress (pc_address) : UINT32_MAX;
5115 if (set_selected_line_to_pc)
5117 m_selected_line = pc_idx;
5120 const uint32_t non_visible_pc_offset = (num_visible_lines / 5);
5121 if (static_cast<size_t>(m_first_visible_line) >= num_disassembly_lines)
5122 m_first_visible_line = 0;
5124 if (pc_idx < num_disassembly_lines)
5126 if (pc_idx < static_cast<uint32_t>(m_first_visible_line) ||
5127 pc_idx >= m_first_visible_line + num_visible_lines)
5128 m_first_visible_line = pc_idx - non_visible_pc_offset;
5131 for (size_t i=0; i<num_visible_lines; ++i)
5133 const uint32_t inst_idx = m_first_visible_line + i;
5134 Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get();
5138 const int line_y = m_min_y+i;
5139 window.MoveCursor(1, line_y);
5140 const bool is_pc_line = frame_sp && inst_idx == pc_idx;
5141 const bool line_is_selected = m_selected_line == inst_idx;
5142 // Highlight the line as the PC line first, then if the selected line
5143 // isn't the same as the PC line, highlight it differently
5144 attr_t highlight_attr = 0;
5147 highlight_attr = pc_highlight_attr;
5148 else if (line_is_selected)
5149 highlight_attr = selected_highlight_attr;
5151 if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) != bp_file_addrs.end())
5152 bp_attr = COLOR_PAIR(2);
5155 window.AttributeOn(bp_attr);
5157 window.Printf (" 0x%16.16llx ",
5158 static_cast<unsigned long long>(inst->GetAddress().GetLoadAddress(target)));
5161 window.AttributeOff(bp_attr);
5163 window.PutChar(ACS_VLINE);
5164 // Mark the line with the PC with a diamond
5166 window.PutChar(ACS_DIAMOND);
5168 window.PutChar(' ');
5171 window.AttributeOn(highlight_attr);
5173 const char *mnemonic = inst->GetMnemonic(&exe_ctx);
5174 const char *operands = inst->GetOperands(&exe_ctx);
5175 const char *comment = inst->GetComment(&exe_ctx);
5177 if (mnemonic && mnemonic[0] == '\0')
5179 if (operands && operands[0] == '\0')
5181 if (comment && comment[0] == '\0')
5186 if (mnemonic && operands && comment)
5187 strm.Printf ("%-8s %-25s ; %s", mnemonic, operands, comment);
5188 else if (mnemonic && operands)
5189 strm.Printf ("%-8s %s", mnemonic, operands);
5191 strm.Printf ("%s", mnemonic);
5194 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
5196 if (is_pc_line && frame_sp && frame_sp->GetConcreteFrameIndex() == 0)
5198 StopInfoSP stop_info_sp;
5200 stop_info_sp = thread->GetStopInfo();
5203 const char *stop_description = stop_info_sp->GetDescription();
5204 if (stop_description && stop_description[0])
5206 size_t stop_description_len = strlen(stop_description);
5207 int desc_x = window_width - stop_description_len - 16;
5208 window.Printf ("%*s", desc_x - window.GetCursorX(), "");
5209 //window.MoveCursor(window_width - stop_description_len - 15, line_y);
5210 window.Printf ("<<< Thread %u: %s ", thread->GetIndexID(), stop_description);
5215 window.Printf ("%*s", window_width - window.GetCursorX() - 1, "");
5219 window.AttributeOff(highlight_attr);
5223 window.DeferredRefresh();
5224 return true; // Drawing handled
5230 size_t num_lines = GetNumSourceLines();
5232 num_lines = GetNumDisassemblyLines();
5237 GetNumSourceLines () const
5240 return m_file_sp->GetNumLines();
5244 GetNumDisassemblyLines () const
5246 if (m_disassembly_sp)
5247 return m_disassembly_sp->GetInstructionList().GetSize();
5252 WindowDelegateHandleChar (Window &window, int c) override
5254 const uint32_t num_visible_lines = NumVisibleLines();
5255 const size_t num_lines = GetNumLines ();
5262 if (static_cast<uint32_t>(m_first_visible_line) > num_visible_lines)
5263 m_first_visible_line -= num_visible_lines;
5265 m_first_visible_line = 0;
5266 m_selected_line = m_first_visible_line;
5273 if (m_first_visible_line + num_visible_lines < num_lines)
5274 m_first_visible_line += num_visible_lines;
5275 else if (num_lines < num_visible_lines)
5276 m_first_visible_line = 0;
5278 m_first_visible_line = num_lines - num_visible_lines;
5279 m_selected_line = m_first_visible_line;
5284 if (m_selected_line > 0)
5287 if (static_cast<size_t>(m_first_visible_line) > m_selected_line)
5288 m_first_visible_line = m_selected_line;
5293 if (m_selected_line + 1 < num_lines)
5296 if (m_first_visible_line + num_visible_lines < m_selected_line)
5297 m_first_visible_line++;
5304 // Set a breakpoint and run to the line using a one shot breakpoint
5305 if (GetNumSourceLines() > 0)
5307 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5308 if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive())
5310 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (NULL, // Don't limit the breakpoint to certain modules
5311 m_file_sp->GetFileSpec(), // Source file
5312 m_selected_line + 1, // Source line number (m_selected_line is zero based)
5313 eLazyBoolCalculate, // Check inlines using global setting
5314 eLazyBoolCalculate, // Skip prologue using global setting,
5316 false, // request_hardware
5317 eLazyBoolCalculate); // move_to_nearest_code
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
5353 eLazyBoolCalculate); // move_to_nearest_code
5356 else if (m_selected_line < GetNumDisassemblyLines())
5358 const Instruction *inst = m_disassembly_sp->GetInstructionList().GetInstructionAtIndex(m_selected_line).get();
5359 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5360 if (exe_ctx.HasTargetScope())
5362 Address addr = inst->GetAddress();
5363 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (addr, // lldb_private::Address
5365 false); // request_hardware
5370 case 'd': // 'd' == detach and let run
5371 case 'D': // 'D' == detach and keep stopped
5373 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5374 if (exe_ctx.HasProcessScope())
5375 exe_ctx.GetProcessRef().Detach(c == 'D');
5382 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5383 if (exe_ctx.HasProcessScope())
5384 exe_ctx.GetProcessRef().Destroy(false);
5391 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5392 if (exe_ctx.HasProcessScope())
5393 exe_ctx.GetProcessRef().Resume();
5400 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5401 if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true))
5403 exe_ctx.GetThreadRef().StepOut();
5407 case 'n': // 'n' == step over
5408 case 'N': // 'N' == step over instruction
5410 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5411 if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true))
5413 bool source_step = (c == 'n');
5414 exe_ctx.GetThreadRef().StepOver(source_step);
5418 case 's': // 's' == step into
5419 case 'S': // 'S' == step into instruction
5421 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5422 if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true))
5424 bool source_step = (c == 's');
5425 exe_ctx.GetThreadRef().StepIn(source_step);
5431 window.CreateHelpSubwindow ();
5437 return eKeyNotHandled;
5441 typedef std::set<uint32_t> BreakpointLines;
5442 typedef std::set<lldb::addr_t> BreakpointAddrs;
5444 Debugger &m_debugger;
5446 SourceManager::FileSP m_file_sp;
5447 SymbolContextScope *m_disassembly_scope;
5448 lldb::DisassemblerSP m_disassembly_sp;
5449 AddressRange m_disassembly_range;
5450 StreamString m_title;
5451 lldb::user_id_t m_tid;
5452 char m_line_format[8];
5454 uint32_t m_selected_line; // The selected line
5455 uint32_t m_pc_line; // The line with the PC
5457 uint32_t m_frame_idx;
5458 int m_first_visible_line;
5466 DisplayOptions ValueObjectListDelegate::g_options = { true };
5468 IOHandlerCursesGUI::IOHandlerCursesGUI (Debugger &debugger) :
5469 IOHandler (debugger, IOHandler::Type::Curses)
5474 IOHandlerCursesGUI::Activate ()
5476 IOHandler::Activate();
5479 m_app_ap.reset (new Application (GetInputFILE(), GetOutputFILE()));
5482 // This is both a window and a menu delegate
5483 std::shared_ptr<ApplicationDelegate> app_delegate_sp(new ApplicationDelegate(*m_app_ap, m_debugger));
5485 MenuDelegateSP app_menu_delegate_sp = std::static_pointer_cast<MenuDelegate>(app_delegate_sp);
5486 MenuSP lldb_menu_sp(new Menu("LLDB" , "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB));
5487 MenuSP exit_menuitem_sp(new Menu("Exit", NULL, 'x', ApplicationDelegate::eMenuID_LLDBExit));
5488 exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit);
5489 lldb_menu_sp->AddSubmenu (MenuSP (new Menu("About LLDB", NULL, 'a', ApplicationDelegate::eMenuID_LLDBAbout)));
5490 lldb_menu_sp->AddSubmenu (MenuSP (new Menu(Menu::Type::Separator)));
5491 lldb_menu_sp->AddSubmenu (exit_menuitem_sp);
5493 MenuSP target_menu_sp(new Menu("Target" ,"F2", KEY_F(2), ApplicationDelegate::eMenuID_Target));
5494 target_menu_sp->AddSubmenu (MenuSP (new Menu("Create", NULL, 'c', ApplicationDelegate::eMenuID_TargetCreate)));
5495 target_menu_sp->AddSubmenu (MenuSP (new Menu("Delete", NULL, 'd', ApplicationDelegate::eMenuID_TargetDelete)));
5497 MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3), ApplicationDelegate::eMenuID_Process));
5498 process_menu_sp->AddSubmenu (MenuSP (new Menu("Attach" , NULL, 'a', ApplicationDelegate::eMenuID_ProcessAttach)));
5499 process_menu_sp->AddSubmenu (MenuSP (new Menu("Detach" , NULL, 'd', ApplicationDelegate::eMenuID_ProcessDetach)));
5500 process_menu_sp->AddSubmenu (MenuSP (new Menu("Launch" , NULL, 'l', ApplicationDelegate::eMenuID_ProcessLaunch)));
5501 process_menu_sp->AddSubmenu (MenuSP (new Menu(Menu::Type::Separator)));
5502 process_menu_sp->AddSubmenu (MenuSP (new Menu("Continue", NULL, 'c', ApplicationDelegate::eMenuID_ProcessContinue)));
5503 process_menu_sp->AddSubmenu (MenuSP (new Menu("Halt" , NULL, 'h', ApplicationDelegate::eMenuID_ProcessHalt)));
5504 process_menu_sp->AddSubmenu (MenuSP (new Menu("Kill" , NULL, 'k', ApplicationDelegate::eMenuID_ProcessKill)));
5506 MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4), ApplicationDelegate::eMenuID_Thread));
5507 thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step In" , NULL, 'i', ApplicationDelegate::eMenuID_ThreadStepIn)));
5508 thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step Over", NULL, 'v', ApplicationDelegate::eMenuID_ThreadStepOver)));
5509 thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step Out" , NULL, 'o', ApplicationDelegate::eMenuID_ThreadStepOut)));
5511 MenuSP view_menu_sp(new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View));
5512 view_menu_sp->AddSubmenu (MenuSP (new Menu("Backtrace", NULL, 'b', ApplicationDelegate::eMenuID_ViewBacktrace)));
5513 view_menu_sp->AddSubmenu (MenuSP (new Menu("Registers", NULL, 'r', ApplicationDelegate::eMenuID_ViewRegisters)));
5514 view_menu_sp->AddSubmenu (MenuSP (new Menu("Source" , NULL, 's', ApplicationDelegate::eMenuID_ViewSource)));
5515 view_menu_sp->AddSubmenu (MenuSP (new Menu("Variables", NULL, 'v', ApplicationDelegate::eMenuID_ViewVariables)));
5517 MenuSP help_menu_sp(new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help));
5518 help_menu_sp->AddSubmenu (MenuSP (new Menu("GUI Help", NULL, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp)));
5520 m_app_ap->Initialize();
5521 WindowSP &main_window_sp = m_app_ap->GetMainWindow();
5523 MenuSP menubar_sp(new Menu(Menu::Type::Bar));
5524 menubar_sp->AddSubmenu (lldb_menu_sp);
5525 menubar_sp->AddSubmenu (target_menu_sp);
5526 menubar_sp->AddSubmenu (process_menu_sp);
5527 menubar_sp->AddSubmenu (thread_menu_sp);
5528 menubar_sp->AddSubmenu (view_menu_sp);
5529 menubar_sp->AddSubmenu (help_menu_sp);
5530 menubar_sp->SetDelegate(app_menu_delegate_sp);
5532 Rect content_bounds = main_window_sp->GetFrame();
5533 Rect menubar_bounds = content_bounds.MakeMenuBar();
5534 Rect status_bounds = content_bounds.MakeStatusBar();
5536 Rect variables_bounds;
5537 Rect threads_bounds;
5538 Rect source_variables_bounds;
5539 content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds, threads_bounds);
5540 source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds, variables_bounds);
5542 WindowSP menubar_window_sp = main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false);
5543 // Let the menubar get keys if the active window doesn't handle the
5544 // keys that are typed so it can respond to menubar key presses.
5545 menubar_window_sp->SetCanBeActive(false); // Don't let the menubar become the active window
5546 menubar_window_sp->SetDelegate(menubar_sp);
5548 WindowSP source_window_sp (main_window_sp->CreateSubWindow("Source",
5551 WindowSP variables_window_sp (main_window_sp->CreateSubWindow("Variables",
5554 WindowSP threads_window_sp (main_window_sp->CreateSubWindow("Threads",
5557 WindowSP status_window_sp (main_window_sp->CreateSubWindow("Status",
5560 status_window_sp->SetCanBeActive(false); // Don't let the status bar become the active window
5561 main_window_sp->SetDelegate (std::static_pointer_cast<WindowDelegate>(app_delegate_sp));
5562 source_window_sp->SetDelegate (WindowDelegateSP(new SourceFileWindowDelegate(m_debugger)));
5563 variables_window_sp->SetDelegate (WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
5564 TreeDelegateSP thread_delegate_sp (new ThreadsTreeDelegate(m_debugger));
5565 threads_window_sp->SetDelegate (WindowDelegateSP(new TreeWindowDelegate(m_debugger, thread_delegate_sp)));
5566 status_window_sp->SetDelegate (WindowDelegateSP(new StatusBarWindowDelegate(m_debugger)));
5568 // Show the main help window once the first time the curses GUI is launched
5569 static bool g_showed_help = false;
5572 g_showed_help = true;
5573 main_window_sp->CreateHelpSubwindow();
5576 init_pair (1, COLOR_WHITE , COLOR_BLUE );
5577 init_pair (2, COLOR_BLACK , COLOR_WHITE );
5578 init_pair (3, COLOR_MAGENTA , COLOR_WHITE );
5579 init_pair (4, COLOR_MAGENTA , COLOR_BLACK );
5580 init_pair (5, COLOR_RED , COLOR_BLACK );
5586 IOHandlerCursesGUI::Deactivate ()
5588 m_app_ap->Terminate();
5592 IOHandlerCursesGUI::Run ()
5594 m_app_ap->Run(m_debugger);
5599 IOHandlerCursesGUI::~IOHandlerCursesGUI ()
5605 IOHandlerCursesGUI::Cancel ()
5610 IOHandlerCursesGUI::Interrupt ()
5617 IOHandlerCursesGUI::GotEOF()
5621 #endif // #ifndef LLDB_DISABLE_CURSES