1 //===-- IOHandler.cpp -------------------------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
11 #include "lldb/lldb-python.h"
15 #include "lldb/Breakpoint/BreakpointLocation.h"
16 #include "lldb/Core/IOHandler.h"
17 #include "lldb/Core/Debugger.h"
18 #include "lldb/Core/Module.h"
19 #include "lldb/Core/State.h"
20 #include "lldb/Core/StreamFile.h"
21 #include "lldb/Core/ValueObjectRegister.h"
22 #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) :
41 StreamFileSP(), // Adopt STDIN from top input reader
42 StreamFileSP(), // Adopt STDOUT from top input reader
43 StreamFileSP(), // Adopt STDERR from top input reader
49 IOHandler::IOHandler (Debugger &debugger,
50 const lldb::StreamFileSP &input_sp,
51 const lldb::StreamFileSP &output_sp,
52 const lldb::StreamFileSP &error_sp,
54 m_debugger (debugger),
55 m_input_sp (input_sp),
56 m_output_sp (output_sp),
57 m_error_sp (error_sp),
63 // If any files are not specified, then adopt them from the top input reader.
64 if (!m_input_sp || !m_output_sp || !m_error_sp)
65 debugger.AdoptTopIOHandlerFilesIfInvalid (m_input_sp,
70 IOHandler::~IOHandler()
76 IOHandler::GetInputFD()
79 return m_input_sp->GetFile().GetDescriptor();
84 IOHandler::GetOutputFD()
87 return m_output_sp->GetFile().GetDescriptor();
92 IOHandler::GetErrorFD()
95 return m_error_sp->GetFile().GetDescriptor();
100 IOHandler::GetInputFILE()
103 return m_input_sp->GetFile().GetStream();
108 IOHandler::GetOutputFILE()
111 return m_output_sp->GetFile().GetStream();
116 IOHandler::GetErrorFILE()
119 return m_error_sp->GetFile().GetStream();
124 IOHandler::GetInputStreamFile()
130 IOHandler::GetOutputStreamFile()
137 IOHandler::GetErrorStreamFile()
143 IOHandler::GetIsInteractive ()
145 return GetInputStreamFile()->GetFile().GetIsInteractive ();
149 IOHandler::GetIsRealTerminal ()
151 return GetInputStreamFile()->GetFile().GetIsRealTerminal();
154 IOHandlerConfirm::IOHandlerConfirm (Debugger &debugger,
156 bool default_response) :
157 IOHandlerEditline(debugger,
158 NULL, // NULL editline_name means no history loaded/saved
163 m_default_response (default_response),
164 m_user_response (default_response)
166 StreamString prompt_stream;
167 prompt_stream.PutCString(prompt);
168 if (m_default_response)
169 prompt_stream.Printf(": [Y/n] ");
171 prompt_stream.Printf(": [y/N] ");
173 SetPrompt (prompt_stream.GetString().c_str());
178 IOHandlerConfirm::~IOHandlerConfirm ()
183 IOHandlerConfirm::IOHandlerComplete (IOHandler &io_handler,
184 const char *current_line,
186 const char *last_char,
187 int skip_first_n_matches,
191 if (current_line == cursor)
193 if (m_default_response)
195 matches.AppendString("y");
199 matches.AppendString("n");
202 return matches.GetSize();
206 IOHandlerConfirm::IOHandlerInputComplete (IOHandler &io_handler, std::string &line)
210 // User just hit enter, set the response to the default
211 m_user_response = m_default_response;
212 io_handler.SetIsDone(true);
216 if (line.size() == 1)
222 m_user_response = true;
223 io_handler.SetIsDone(true);
227 m_user_response = false;
228 io_handler.SetIsDone(true);
235 if (line == "yes" || line == "YES" || line == "Yes")
237 m_user_response = true;
238 io_handler.SetIsDone(true);
240 else if (line == "no" || line == "NO" || line == "No")
242 m_user_response = false;
243 io_handler.SetIsDone(true);
248 IOHandlerDelegate::IOHandlerComplete (IOHandler &io_handler,
249 const char *current_line,
251 const char *last_char,
252 int skip_first_n_matches,
256 switch (m_completion)
258 case Completion::None:
261 case Completion::LLDBCommand:
262 return io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion (current_line,
265 skip_first_n_matches,
269 case Completion::Expression:
271 bool word_complete = false;
272 const char *word_start = cursor;
273 if (cursor > current_line)
275 while (word_start > current_line && !isspace(*word_start))
277 CommandCompletions::InvokeCommonCompletionCallbacks (io_handler.GetDebugger().GetCommandInterpreter(),
278 CommandCompletions::eVariablePathCompletion,
280 skip_first_n_matches,
286 size_t num_matches = matches.GetSize();
289 std::string common_prefix;
290 matches.LongestCommonPrefix (common_prefix);
291 const size_t partial_name_len = strlen(word_start);
293 // If we matched a unique single command, add a space...
294 // Only do this if the completer told us this was a complete word, however...
295 if (num_matches == 1 && word_complete)
297 common_prefix.push_back(' ');
299 common_prefix.erase (0, partial_name_len);
300 matches.InsertStringAtIndex(0, std::move(common_prefix));
312 IOHandlerEditline::IOHandlerEditline (Debugger &debugger,
313 const char *editline_name, // Used for saving history files
316 uint32_t line_number_start,
317 IOHandlerDelegate &delegate) :
318 IOHandlerEditline(debugger,
319 StreamFileSP(), // Inherit input from top input reader
320 StreamFileSP(), // Inherit output from top input reader
321 StreamFileSP(), // Inherit error from top input reader
323 editline_name, // Used for saving history files
331 IOHandlerEditline::IOHandlerEditline (Debugger &debugger,
332 const lldb::StreamFileSP &input_sp,
333 const lldb::StreamFileSP &output_sp,
334 const lldb::StreamFileSP &error_sp,
336 const char *editline_name, // Used for saving history files
339 uint32_t line_number_start,
340 IOHandlerDelegate &delegate) :
341 IOHandler (debugger, input_sp, output_sp, error_sp, flags),
343 m_delegate (delegate),
345 m_base_line_number (line_number_start),
346 m_multi_line (multi_line)
350 bool use_editline = false;
353 use_editline = m_input_sp->GetFile().GetIsRealTerminal();
355 // Editline is causing issues on Windows, so use the fallback.
356 use_editline = false;
361 m_editline_ap.reset(new Editline (editline_name,
362 prompt ? prompt : "",
367 if (m_base_line_number > 0)
368 m_editline_ap->ShowLineNumbers(true, m_base_line_number);
369 m_editline_ap->SetLineCompleteCallback (LineCompletedCallback, this);
370 m_editline_ap->SetAutoCompleteCallback (AutoCompleteCallback, this);
375 IOHandlerEditline::~IOHandlerEditline ()
377 m_editline_ap.reset();
382 IOHandlerEditline::GetLine (std::string &line, bool &interrupted)
386 return m_editline_ap->GetLine(line, interrupted).Success();
392 FILE *in = GetInputFILE();
395 if (GetIsInteractive())
397 const char *prompt = GetPrompt();
398 if (prompt && prompt[0])
400 FILE *out = GetOutputFILE();
403 ::fprintf(out, "%s", prompt);
410 bool got_line = false;
413 if (fgets(buffer, sizeof(buffer), in) == NULL)
415 const int saved_errno = errno;
420 if (saved_errno != EINTR)
427 size_t buffer_len = strlen(buffer);
428 assert (buffer[buffer_len] == '\0');
429 char last_char = buffer[buffer_len-1];
430 if (last_char == '\r' || last_char == '\n')
433 // Strip trailing newlines
434 while (last_char == '\r' || last_char == '\n')
439 last_char = buffer[buffer_len-1];
442 line.append(buffer, buffer_len);
445 // We might have gotten a newline on a line by itself
446 // make sure to return true in this case.
451 // No more input file, we are done...
460 IOHandlerEditline::LineCompletedCallback (Editline *editline,
466 IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton;
467 return editline_reader->m_delegate.IOHandlerLinesUpdated(*editline_reader, lines, line_idx, error);
471 IOHandlerEditline::AutoCompleteCallback (const char *current_line,
473 const char *last_char,
474 int skip_first_n_matches,
479 IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton;
481 return editline_reader->m_delegate.IOHandlerComplete (*editline_reader,
485 skip_first_n_matches,
492 IOHandlerEditline::GetPrompt ()
495 return m_editline_ap->GetPrompt ();
496 else if (m_prompt.empty())
498 return m_prompt.c_str();
502 IOHandlerEditline::SetPrompt (const char *p)
509 m_editline_ap->SetPrompt (m_prompt.empty() ? NULL : m_prompt.c_str());
514 IOHandlerEditline::SetBaseLineNumber (uint32_t line)
516 m_base_line_number = line;
518 m_editline_ap->ShowLineNumbers (true, line);
522 IOHandlerEditline::GetLines (StringList &lines, bool &interrupted)
524 bool success = false;
527 std::string end_token;
528 success = m_editline_ap->GetLines(end_token, lines, interrupted).Success();
532 LineStatus lines_status = LineStatus::Success;
535 while (lines_status == LineStatus::Success)
537 // Show line numbers if we are asked to
539 if (m_base_line_number > 0 && GetIsInteractive())
541 FILE *out = GetOutputFILE();
543 ::fprintf(out, "%u%s", m_base_line_number + (uint32_t)lines.GetSize(), GetPrompt() == NULL ? " " : "");
546 bool interrupted = false;
547 if (GetLine(line, interrupted))
551 lines_status = LineStatus::Done;
555 lines.AppendString(line);
556 lines_status = m_delegate.IOHandlerLinesUpdated(*this, lines, lines.GetSize() - 1, error);
561 lines_status = LineStatus::Done;
565 // Call the IOHandlerLinesUpdated function with UINT32_MAX as the line
566 // number to indicate all lines are complete
567 m_delegate.IOHandlerLinesUpdated(*this, lines, UINT32_MAX, error);
569 success = lines.GetSize() > 0;
574 // Each IOHandler gets to run until it is done. It should read data
575 // from the "in" and place output into "out" and "err and return
578 IOHandlerEditline::Run ()
583 bool interrupted = false;
587 if (GetLines (lines, interrupted))
595 line = lines.CopyList();
596 m_delegate.IOHandlerInputComplete(*this, line);
606 if (GetLine(line, interrupted))
609 m_delegate.IOHandlerInputComplete(*this, line);
620 IOHandlerEditline::Hide ()
623 m_editline_ap->Hide();
628 IOHandlerEditline::Refresh ()
632 m_editline_ap->Refresh();
636 const char *prompt = GetPrompt();
637 if (prompt && prompt[0])
639 FILE *out = GetOutputFILE();
642 ::fprintf(out, "%s", prompt);
650 IOHandlerEditline::Cancel ()
653 m_editline_ap->Interrupt ();
657 IOHandlerEditline::Interrupt ()
659 // Let the delgate handle it first
660 if (m_delegate.IOHandlerInterrupt(*this))
664 return m_editline_ap->Interrupt();
669 IOHandlerEditline::GotEOF()
672 m_editline_ap->Interrupt();
675 // we may want curses to be disabled for some builds
676 // for instance, windows
677 #ifndef LLDB_DISABLE_CURSES
679 #include "lldb/Core/ValueObject.h"
680 #include "lldb/Symbol/VariableList.h"
681 #include "lldb/Target/Target.h"
682 #include "lldb/Target/Process.h"
683 #include "lldb/Target/Thread.h"
684 #include "lldb/Target/StackFrame.h"
686 #define KEY_RETURN 10
687 #define KEY_ESCAPE 27
694 class WindowDelegate;
695 typedef std::shared_ptr<Menu> MenuSP;
696 typedef std::shared_ptr<MenuDelegate> MenuDelegateSP;
697 typedef std::shared_ptr<Window> WindowSP;
698 typedef std::shared_ptr<WindowDelegate> WindowDelegateSP;
699 typedef std::vector<MenuSP> Menus;
700 typedef std::vector<WindowSP> Windows;
701 typedef std::vector<WindowDelegateSP> WindowDelegates;
704 type summary add -s "x=${var.x}, y=${var.y}" curses::Point
705 type summary add -s "w=${var.width}, h=${var.height}" curses::Size
706 type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect
713 Point (int _x = 0, int _y = 0) :
727 operator += (const Point &rhs)
737 printf ("(x=%i, y=%i)\n", x, y);
742 bool operator == (const Point &lhs, const Point &rhs)
744 return lhs.x == rhs.x && lhs.y == rhs.y;
746 bool operator != (const Point &lhs, const Point &rhs)
748 return lhs.x != rhs.x || lhs.y != rhs.y;
755 Size (int w = 0, int h = 0) :
771 printf ("(w=%i, h=%i)\n", width, height);
776 bool operator == (const Size &lhs, const Size &rhs)
778 return lhs.width == rhs.width && lhs.height == rhs.height;
780 bool operator != (const Size &lhs, const Size &rhs)
782 return lhs.width != rhs.width || lhs.height != rhs.height;
796 Rect (const Point &p, const Size &s) :
812 printf ("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width, size.height);
818 if (size.width > w*2)
822 if (size.height > h*2)
826 // Return a status bar rectangle which is the last line of
827 // this rectangle. This rectangle will be modified to not
828 // include the status bar area.
835 status_bar.origin.x = origin.x;
836 status_bar.origin.y = size.height;
837 status_bar.size.width = size.width;
838 status_bar.size.height = 1;
844 // Return a menubar rectangle which is the first line of
845 // this rectangle. This rectangle will be modified to not
846 // include the menubar area.
853 menubar.origin.x = origin.x;
854 menubar.origin.y = origin.y;
855 menubar.size.width = size.width;
856 menubar.size.height = 1;
864 HorizontalSplitPercentage (float top_percentage, Rect &top, Rect &bottom) const
866 float top_height = top_percentage * size.height;
867 HorizontalSplit (top_height, top, bottom);
871 HorizontalSplit (int top_height, Rect &top, Rect &bottom) const
874 if (top_height < size.height)
876 top.size.height = top_height;
877 bottom.origin.x = origin.x;
878 bottom.origin.y = origin.y + top.size.height;
879 bottom.size.width = size.width;
880 bottom.size.height = size.height - top.size.height;
889 VerticalSplitPercentage (float left_percentage, Rect &left, Rect &right) const
891 float left_width = left_percentage * size.width;
892 VerticalSplit (left_width, left, right);
897 VerticalSplit (int left_width, Rect &left, Rect &right) const
900 if (left_width < size.width)
902 left.size.width = left_width;
903 right.origin.x = origin.x + left.size.width;
904 right.origin.y = origin.y;
905 right.size.width = size.width - left.size.width;
906 right.size.height = size.height;
915 bool operator == (const Rect &lhs, const Rect &rhs)
917 return lhs.origin == rhs.origin && lhs.size == rhs.size;
919 bool operator != (const Rect &lhs, const Rect &rhs)
921 return lhs.origin != rhs.origin || lhs.size != rhs.size;
924 enum HandleCharResult
931 enum class MenuActionResult
935 Quit // Exit all menus and quit
941 const char *description;
953 WindowDelegateDraw (Window &window, bool force)
955 return false; // Drawing not handled
958 virtual HandleCharResult
959 WindowDelegateHandleChar (Window &window, int key)
961 return eKeyNotHandled;
965 WindowDelegateGetHelpText ()
971 WindowDelegateGetKeyHelp ()
977 class HelpDialogDelegate :
978 public WindowDelegate
981 HelpDialogDelegate (const char *text, KeyHelp *key_help_array);
984 ~HelpDialogDelegate();
987 WindowDelegateDraw (Window &window, bool force);
989 virtual HandleCharResult
990 WindowDelegateHandleChar (Window &window, int key);
995 return m_text.GetSize();
999 GetMaxLineLength () const
1001 return m_text.GetMaxStringLength();
1006 int m_first_visible_line;
1014 Window (const char *name) :
1021 m_curr_active_window_idx (UINT32_MAX),
1022 m_prev_active_window_idx (UINT32_MAX),
1024 m_needs_update (true),
1025 m_can_activate (true),
1030 Window (const char *name, WINDOW *w, bool del = true) :
1037 m_curr_active_window_idx (UINT32_MAX),
1038 m_prev_active_window_idx (UINT32_MAX),
1040 m_needs_update (true),
1041 m_can_activate (true),
1048 Window (const char *name, const Rect &bounds) :
1054 m_curr_active_window_idx (UINT32_MAX),
1055 m_prev_active_window_idx (UINT32_MAX),
1057 m_needs_update (true),
1058 m_can_activate (true),
1061 Reset (::newwin (bounds.size.height, bounds.size.width, bounds.origin.y, bounds.origin.y));
1067 RemoveSubWindows ();
1072 Reset (WINDOW *w = NULL, bool del = true)
1079 ::del_panel (m_panel);
1082 if (m_window && m_delete)
1084 ::delwin (m_window);
1091 m_panel = ::new_panel (m_window);
1096 void AttributeOn (attr_t attr) { ::wattron (m_window, attr); }
1097 void AttributeOff (attr_t attr) { ::wattroff (m_window, attr); }
1098 void Box (chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) { ::box(m_window, v_char, h_char); }
1099 void Clear () { ::wclear (m_window); }
1100 void Erase () { ::werase (m_window); }
1101 Rect GetBounds () { return Rect (GetParentOrigin(), GetSize()); } // Get the rectangle in our parent window
1102 int GetChar () { return ::wgetch (m_window); }
1103 int GetCursorX () { return getcurx (m_window); }
1104 int GetCursorY () { return getcury (m_window); }
1105 Rect GetFrame () { return Rect (Point(), GetSize()); } // Get our rectangle in our own coordinate system
1106 Point GetParentOrigin() { return Point (GetParentX(), GetParentY()); }
1107 Size GetSize() { return Size (GetWidth(), GetHeight()); }
1108 int GetParentX () { return getparx (m_window); }
1109 int GetParentY () { return getpary (m_window); }
1110 int GetMaxX() { return getmaxx (m_window); }
1111 int GetMaxY() { return getmaxy (m_window); }
1112 int GetWidth() { return GetMaxX(); }
1113 int GetHeight() { return GetMaxY(); }
1114 void MoveCursor (int x, int y) { ::wmove (m_window, y, x); }
1115 void MoveWindow (int x, int y) { MoveWindow(Point(x,y)); }
1116 void Resize (int w, int h) { ::wresize(m_window, h, w); }
1117 void Resize (const Size &size) { ::wresize(m_window, size.height, size.width); }
1118 void PutChar (int ch) { ::waddch (m_window, ch); }
1119 void PutCString (const char *s, int len = -1) { ::waddnstr (m_window, s, len); }
1120 void Refresh () { ::wrefresh (m_window); }
1121 void DeferredRefresh ()
1123 // We are using panels, so we don't need to call this...
1124 //::wnoutrefresh(m_window);
1126 void SetBackground (int color_pair_idx) { ::wbkgd (m_window,COLOR_PAIR(color_pair_idx)); }
1127 void UnderlineOn () { AttributeOn(A_UNDERLINE); }
1128 void UnderlineOff () { AttributeOff(A_UNDERLINE); }
1130 void PutCStringTruncated (const char *s, int right_pad)
1132 int bytes_left = GetWidth() - GetCursorX();
1133 if (bytes_left > right_pad)
1135 bytes_left -= right_pad;
1136 ::waddnstr (m_window, s, bytes_left);
1141 MoveWindow (const Point &origin)
1143 const bool moving_window = origin != GetParentOrigin();
1144 if (m_is_subwin && moving_window)
1146 // Can't move subwindows, must delete and re-create
1147 Size size = GetSize();
1148 Reset (::subwin (m_parent->m_window,
1156 ::mvwin (m_window, origin.y, origin.x);
1161 SetBounds (const Rect &bounds)
1163 const bool moving_window = bounds.origin != GetParentOrigin();
1164 if (m_is_subwin && moving_window)
1166 // Can't move subwindows, must delete and re-create
1167 Reset (::subwin (m_parent->m_window,
1171 bounds.origin.x), true);
1176 MoveWindow(bounds.origin);
1177 Resize (bounds.size);
1182 Printf (const char *format, ...) __attribute__ ((format (printf, 2, 3)))
1185 va_start (args, format);
1186 vwprintw(m_window, format, args);
1193 ::touchwin (m_window);
1199 CreateSubWindow (const char *name, const Rect &bounds, bool make_active)
1201 WindowSP subwindow_sp;
1204 subwindow_sp.reset(new Window(name, ::subwin (m_window,
1208 bounds.origin.x), true));
1209 subwindow_sp->m_is_subwin = true;
1213 subwindow_sp.reset(new Window(name, ::newwin (bounds.size.height,
1216 bounds.origin.x), true));
1217 subwindow_sp->m_is_subwin = false;
1219 subwindow_sp->m_parent = this;
1222 m_prev_active_window_idx = m_curr_active_window_idx;
1223 m_curr_active_window_idx = m_subwindows.size();
1225 m_subwindows.push_back(subwindow_sp);
1226 ::top_panel (subwindow_sp->m_panel);
1227 m_needs_update = true;
1228 return subwindow_sp;
1232 RemoveSubWindow (Window *window)
1234 Windows::iterator pos, end = m_subwindows.end();
1236 for (pos = m_subwindows.begin(); pos != end; ++pos, ++i)
1238 if ((*pos).get() == window)
1240 if (m_prev_active_window_idx == i)
1241 m_prev_active_window_idx = UINT32_MAX;
1242 else if (m_prev_active_window_idx != UINT32_MAX && m_prev_active_window_idx > i)
1243 --m_prev_active_window_idx;
1245 if (m_curr_active_window_idx == i)
1246 m_curr_active_window_idx = UINT32_MAX;
1247 else if (m_curr_active_window_idx != UINT32_MAX && m_curr_active_window_idx > i)
1248 --m_curr_active_window_idx;
1250 m_subwindows.erase(pos);
1251 m_needs_update = true;
1255 ::touchwin (stdscr);
1263 FindSubWindow (const char *name)
1265 Windows::iterator pos, end = m_subwindows.end();
1267 for (pos = m_subwindows.begin(); pos != end; ++pos, ++i)
1269 if ((*pos)->m_name.compare(name) == 0)
1278 m_curr_active_window_idx = UINT32_MAX;
1279 m_prev_active_window_idx = UINT32_MAX;
1280 for (Windows::iterator pos = m_subwindows.begin();
1281 pos != m_subwindows.end();
1282 pos = m_subwindows.erase(pos))
1289 ::touchwin (stdscr);
1303 //----------------------------------------------------------------------
1304 // Window drawing utilities
1305 //----------------------------------------------------------------------
1307 DrawTitleBox (const char *title, const char *bottom_message = NULL)
1311 attr = A_BOLD | COLOR_PAIR(2);
1320 if (title && title[0])
1327 if (bottom_message && bottom_message[0])
1329 int bottom_message_length = strlen(bottom_message);
1330 int x = GetWidth() - 3 - (bottom_message_length + 2);
1334 MoveCursor (x, GetHeight() - 1);
1336 PutCString(bottom_message);
1341 MoveCursor (1, GetHeight() - 1);
1343 PutCStringTruncated (bottom_message, 1);
1354 if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw (*this, force))
1357 for (auto &subwindow_sp : m_subwindows)
1358 subwindow_sp->Draw(force);
1362 CreateHelpSubwindow ()
1366 const char *text = m_delegate_sp->WindowDelegateGetHelpText ();
1367 KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp ();
1368 if ((text && text[0]) || key_help)
1370 std::auto_ptr<HelpDialogDelegate> help_delegate_ap(new HelpDialogDelegate(text, key_help));
1371 const size_t num_lines = help_delegate_ap->GetNumLines();
1372 const size_t max_length = help_delegate_ap->GetMaxLineLength();
1373 Rect bounds = GetBounds();
1375 if (max_length + 4 < static_cast<size_t>(bounds.size.width))
1377 bounds.origin.x += (bounds.size.width - max_length + 4)/2;
1378 bounds.size.width = max_length + 4;
1382 if (bounds.size.width > 100)
1384 const int inset_w = bounds.size.width / 4;
1385 bounds.origin.x += inset_w;
1386 bounds.size.width -= 2*inset_w;
1390 if (num_lines + 2 < static_cast<size_t>(bounds.size.height))
1392 bounds.origin.y += (bounds.size.height - num_lines + 2)/2;
1393 bounds.size.height = num_lines + 2;
1397 if (bounds.size.height > 100)
1399 const int inset_h = bounds.size.height / 4;
1400 bounds.origin.y += inset_h;
1401 bounds.size.height -= 2*inset_h;
1404 WindowSP help_window_sp;
1405 Window *parent_window = GetParent();
1407 help_window_sp = parent_window->CreateSubWindow("Help", bounds, true);
1409 help_window_sp = CreateSubWindow("Help", bounds, true);
1410 help_window_sp->SetDelegate(WindowDelegateSP(help_delegate_ap.release()));
1417 virtual HandleCharResult
1418 HandleChar (int key)
1420 // Always check the active window first
1421 HandleCharResult result = eKeyNotHandled;
1422 WindowSP active_window_sp = GetActiveWindow ();
1423 if (active_window_sp)
1425 result = active_window_sp->HandleChar (key);
1426 if (result != eKeyNotHandled)
1432 result = m_delegate_sp->WindowDelegateHandleChar (*this, key);
1433 if (result != eKeyNotHandled)
1437 // Then check for any windows that want any keys
1438 // that weren't handled. This is typically only
1440 // Make a copy of the subwindows in case any HandleChar()
1441 // functions muck with the subwindows. If we don't do this,
1442 // we can crash when iterating over the subwindows.
1443 Windows subwindows (m_subwindows);
1444 for (auto subwindow_sp : subwindows)
1446 if (subwindow_sp->m_can_activate == false)
1448 HandleCharResult result = subwindow_sp->HandleChar(key);
1449 if (result != eKeyNotHandled)
1454 return eKeyNotHandled;
1458 SetActiveWindow (Window *window)
1460 const size_t num_subwindows = m_subwindows.size();
1461 for (size_t i=0; i<num_subwindows; ++i)
1463 if (m_subwindows[i].get() == window)
1465 m_prev_active_window_idx = m_curr_active_window_idx;
1466 ::top_panel (window->m_panel);
1467 m_curr_active_window_idx = i;
1477 if (!m_subwindows.empty())
1479 if (m_curr_active_window_idx >= m_subwindows.size())
1481 if (m_prev_active_window_idx < m_subwindows.size())
1483 m_curr_active_window_idx = m_prev_active_window_idx;
1484 m_prev_active_window_idx = UINT32_MAX;
1486 else if (IsActive())
1488 m_prev_active_window_idx = UINT32_MAX;
1489 m_curr_active_window_idx = UINT32_MAX;
1491 // Find first window that wants to be active if this window is active
1492 const size_t num_subwindows = m_subwindows.size();
1493 for (size_t i=0; i<num_subwindows; ++i)
1495 if (m_subwindows[i]->GetCanBeActive())
1497 m_curr_active_window_idx = i;
1504 if (m_curr_active_window_idx < m_subwindows.size())
1505 return m_subwindows[m_curr_active_window_idx];
1511 GetCanBeActive () const
1513 return m_can_activate;
1517 SetCanBeActive (bool b)
1522 const WindowDelegateSP &
1523 GetDelegate () const
1525 return m_delegate_sp;
1529 SetDelegate (const WindowDelegateSP &delegate_sp)
1531 m_delegate_sp = delegate_sp;
1544 return m_parent->GetActiveWindow().get() == this;
1546 return true; // Top level window is always active
1550 SelectNextWindowAsActive ()
1552 // Move active focus to next window
1553 const size_t num_subwindows = m_subwindows.size();
1554 if (m_curr_active_window_idx == UINT32_MAX)
1557 for (auto subwindow_sp : m_subwindows)
1559 if (subwindow_sp->GetCanBeActive())
1561 m_curr_active_window_idx = idx;
1567 else if (m_curr_active_window_idx + 1 < num_subwindows)
1569 bool handled = false;
1570 m_prev_active_window_idx = m_curr_active_window_idx;
1571 for (size_t idx=m_curr_active_window_idx + 1; idx<num_subwindows; ++idx)
1573 if (m_subwindows[idx]->GetCanBeActive())
1575 m_curr_active_window_idx = idx;
1582 for (size_t idx=0; idx<=m_prev_active_window_idx; ++idx)
1584 if (m_subwindows[idx]->GetCanBeActive())
1586 m_curr_active_window_idx = idx;
1594 m_prev_active_window_idx = m_curr_active_window_idx;
1595 for (size_t idx=0; idx<num_subwindows; ++idx)
1597 if (m_subwindows[idx]->GetCanBeActive())
1599 m_curr_active_window_idx = idx;
1609 return m_name.c_str();
1616 Windows m_subwindows;
1617 WindowDelegateSP m_delegate_sp;
1618 uint32_t m_curr_active_window_idx;
1619 uint32_t m_prev_active_window_idx;
1621 bool m_needs_update;
1622 bool m_can_activate;
1626 DISALLOW_COPY_AND_ASSIGN(Window);
1632 virtual ~MenuDelegate() {}
1634 virtual MenuActionResult
1635 MenuDelegateAction (Menu &menu) = 0;
1638 class Menu : public WindowDelegate
1649 // Menubar or separator constructor
1652 // Menuitem constructor
1653 Menu (const char *name,
1654 const char *key_name,
1656 uint64_t identifier);
1663 const MenuDelegateSP &
1664 GetDelegate () const
1666 return m_delegate_sp;
1670 SetDelegate (const MenuDelegateSP &delegate_sp)
1672 m_delegate_sp = delegate_sp;
1676 RecalculateNameLengths();
1679 AddSubmenu (const MenuSP &menu_sp);
1682 DrawAndRunMenu (Window &window);
1685 DrawMenuTitle (Window &window, bool highlight);
1688 WindowDelegateDraw (Window &window, bool force);
1690 virtual HandleCharResult
1691 WindowDelegateHandleChar (Window &window, int key);
1694 ActionPrivate (Menu &menu)
1696 MenuActionResult result = MenuActionResult::NotHandled;
1699 result = m_delegate_sp->MenuDelegateAction (menu);
1700 if (result != MenuActionResult::NotHandled)
1705 result = m_parent->ActionPrivate(menu);
1706 if (result != MenuActionResult::NotHandled)
1709 return m_canned_result;
1715 // Call the recursive action so it can try to handle it
1716 // with the menu delegate, and if not, try our parent menu
1717 return ActionPrivate (*this);
1721 SetCannedResult (MenuActionResult result)
1723 m_canned_result = result;
1739 GetSelectedSubmenuIndex () const
1745 SetSelectedSubmenuIndex (int idx)
1757 GetStartingColumn() const
1763 SetStartingColumn(int col)
1775 SetKeyValue(int key_value)
1777 m_key_value = key_value;
1793 GetDrawWidth () const
1795 return m_max_submenu_name_length + m_max_submenu_key_name_length + 8;
1800 GetIdentifier() const
1802 return m_identifier;
1806 SetIdentifier (uint64_t identifier)
1808 m_identifier = identifier;
1813 std::string m_key_name;
1814 uint64_t m_identifier;
1818 int m_max_submenu_name_length;
1819 int m_max_submenu_key_name_length;
1823 WindowSP m_menu_window_sp;
1824 MenuActionResult m_canned_result;
1825 MenuDelegateSP m_delegate_sp;
1828 // Menubar or separator constructor
1829 Menu::Menu (Type type) :
1836 m_max_submenu_name_length (0),
1837 m_max_submenu_key_name_length (0),
1841 m_canned_result (MenuActionResult::NotHandled),
1846 // Menuitem constructor
1847 Menu::Menu (const char *name,
1848 const char *key_name,
1850 uint64_t identifier) :
1853 m_identifier (identifier),
1854 m_type (Type::Invalid),
1855 m_key_value (key_value),
1857 m_max_submenu_name_length (0),
1858 m_max_submenu_key_name_length (0),
1862 m_canned_result (MenuActionResult::NotHandled),
1865 if (name && name[0])
1868 m_type = Type::Item;
1869 if (key_name && key_name[0])
1870 m_key_name = key_name;
1874 m_type = Type::Separator;
1879 Menu::RecalculateNameLengths()
1881 m_max_submenu_name_length = 0;
1882 m_max_submenu_key_name_length = 0;
1883 Menus &submenus = GetSubmenus();
1884 const size_t num_submenus = submenus.size();
1885 for (size_t i=0; i<num_submenus; ++i)
1887 Menu *submenu = submenus[i].get();
1888 if (static_cast<size_t>(m_max_submenu_name_length) < submenu->m_name.size())
1889 m_max_submenu_name_length = submenu->m_name.size();
1890 if (static_cast<size_t>(m_max_submenu_key_name_length) < submenu->m_key_name.size())
1891 m_max_submenu_key_name_length = submenu->m_key_name.size();
1896 Menu::AddSubmenu (const MenuSP &menu_sp)
1898 menu_sp->m_parent = this;
1899 if (static_cast<size_t>(m_max_submenu_name_length) < menu_sp->m_name.size())
1900 m_max_submenu_name_length = menu_sp->m_name.size();
1901 if (static_cast<size_t>(m_max_submenu_key_name_length) < menu_sp->m_key_name.size())
1902 m_max_submenu_key_name_length = menu_sp->m_key_name.size();
1903 m_submenus.push_back(menu_sp);
1907 Menu::DrawMenuTitle (Window &window, bool highlight)
1909 if (m_type == Type::Separator)
1911 window.MoveCursor(0, window.GetCursorY());
1912 window.PutChar(ACS_LTEE);
1913 int width = window.GetWidth();
1917 for (int i=0; i< width; ++i)
1918 window.PutChar(ACS_HLINE);
1920 window.PutChar(ACS_RTEE);
1924 const int shortcut_key = m_key_value;
1925 bool underlined_shortcut = false;
1926 const attr_t hilgight_attr = A_REVERSE;
1928 window.AttributeOn(hilgight_attr);
1929 if (isprint(shortcut_key))
1931 size_t lower_pos = m_name.find(tolower(shortcut_key));
1932 size_t upper_pos = m_name.find(toupper(shortcut_key));
1933 const char *name = m_name.c_str();
1934 size_t pos = std::min<size_t>(lower_pos, upper_pos);
1935 if (pos != std::string::npos)
1937 underlined_shortcut = true;
1940 window.PutCString(name, pos);
1943 const attr_t shortcut_attr = A_UNDERLINE|A_BOLD;
1944 window.AttributeOn (shortcut_attr);
1945 window.PutChar(name[0]);
1946 window.AttributeOff(shortcut_attr);
1949 window.PutCString(name);
1953 if (!underlined_shortcut)
1955 window.PutCString(m_name.c_str());
1959 window.AttributeOff(hilgight_attr);
1961 if (m_key_name.empty())
1963 if (!underlined_shortcut && isprint(m_key_value))
1965 window.AttributeOn (COLOR_PAIR(3));
1966 window.Printf (" (%c)", m_key_value);
1967 window.AttributeOff (COLOR_PAIR(3));
1972 window.AttributeOn (COLOR_PAIR(3));
1973 window.Printf (" (%s)", m_key_name.c_str());
1974 window.AttributeOff (COLOR_PAIR(3));
1980 Menu::WindowDelegateDraw (Window &window, bool force)
1982 Menus &submenus = GetSubmenus();
1983 const size_t num_submenus = submenus.size();
1984 const int selected_idx = GetSelectedSubmenuIndex();
1985 Menu::Type menu_type = GetType ();
1988 case Menu::Type::Bar:
1990 window.SetBackground(2);
1991 window.MoveCursor(0, 0);
1992 for (size_t i=0; i<num_submenus; ++i)
1994 Menu *menu = submenus[i].get();
1996 window.PutChar(' ');
1997 menu->SetStartingColumn (window.GetCursorX());
1998 window.PutCString("| ");
1999 menu->DrawMenuTitle (window, false);
2001 window.PutCString(" |");
2002 window.DeferredRefresh();
2006 case Menu::Type::Item:
2014 window.SetBackground(2);
2016 for (size_t i=0; i<num_submenus; ++i)
2018 const bool is_selected =
2019 (i == static_cast<size_t>(selected_idx));
2020 window.MoveCursor(x, y + i);
2023 // Remember where we want the cursor to be
2027 submenus[i]->DrawMenuTitle (window, is_selected);
2029 window.MoveCursor(cursor_x, cursor_y);
2030 window.DeferredRefresh();
2035 case Menu::Type::Separator:
2038 return true; // Drawing handled...
2042 Menu::WindowDelegateHandleChar (Window &window, int key)
2044 HandleCharResult result = eKeyNotHandled;
2046 Menus &submenus = GetSubmenus();
2047 const size_t num_submenus = submenus.size();
2048 const int selected_idx = GetSelectedSubmenuIndex();
2049 Menu::Type menu_type = GetType ();
2050 if (menu_type == Menu::Type::Bar)
2057 // Show last menu or first menu
2058 if (selected_idx < static_cast<int>(num_submenus))
2059 run_menu_sp = submenus[selected_idx];
2060 else if (!submenus.empty())
2061 run_menu_sp = submenus.front();
2062 result = eKeyHandled;
2068 if (m_selected >= static_cast<int>(num_submenus))
2070 if (m_selected < static_cast<int>(num_submenus))
2071 run_menu_sp = submenus[m_selected];
2072 else if (!submenus.empty())
2073 run_menu_sp = submenus.front();
2074 result = eKeyHandled;
2082 m_selected = num_submenus - 1;
2083 if (m_selected < static_cast<int>(num_submenus))
2084 run_menu_sp = submenus[m_selected];
2085 else if (!submenus.empty())
2086 run_menu_sp = submenus.front();
2087 result = eKeyHandled;
2092 for (size_t i=0; i<num_submenus; ++i)
2094 if (submenus[i]->GetKeyValue() == key)
2096 SetSelectedSubmenuIndex(i);
2097 run_menu_sp = submenus[i];
2098 result = eKeyHandled;
2107 // Run the action on this menu in case we need to populate the
2108 // menu with dynamic content and also in case check marks, and
2109 // any other menu decorations need to be caclulated
2110 if (run_menu_sp->Action() == MenuActionResult::Quit)
2111 return eQuitApplication;
2114 menu_bounds.origin.x = run_menu_sp->GetStartingColumn();
2115 menu_bounds.origin.y = 1;
2116 menu_bounds.size.width = run_menu_sp->GetDrawWidth();
2117 menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2;
2118 if (m_menu_window_sp)
2119 window.GetParent()->RemoveSubWindow(m_menu_window_sp.get());
2121 m_menu_window_sp = window.GetParent()->CreateSubWindow (run_menu_sp->GetName().c_str(),
2124 m_menu_window_sp->SetDelegate (run_menu_sp);
2127 else if (menu_type == Menu::Type::Item)
2132 if (m_submenus.size() > 1)
2134 const int start_select = m_selected;
2135 while (++m_selected != start_select)
2137 if (static_cast<size_t>(m_selected) >= num_submenus)
2139 if (m_submenus[m_selected]->GetType() == Type::Separator)
2149 if (m_submenus.size() > 1)
2151 const int start_select = m_selected;
2152 while (--m_selected != start_select)
2154 if (m_selected < static_cast<int>(0))
2155 m_selected = num_submenus - 1;
2156 if (m_submenus[m_selected]->GetType() == Type::Separator)
2166 if (static_cast<size_t>(selected_idx) < num_submenus)
2168 if (submenus[selected_idx]->Action() == MenuActionResult::Quit)
2169 return eQuitApplication;
2170 window.GetParent()->RemoveSubWindow(&window);
2175 case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in case other chars are entered for escaped sequences
2176 window.GetParent()->RemoveSubWindow(&window);
2181 for (size_t i=0; i<num_submenus; ++i)
2183 Menu *menu = submenus[i].get();
2184 if (menu->GetKeyValue() == key)
2186 SetSelectedSubmenuIndex(i);
2187 window.GetParent()->RemoveSubWindow(&window);
2188 if (menu->Action() == MenuActionResult::Quit)
2189 return eQuitApplication;
2198 else if (menu_type == Menu::Type::Separator)
2209 Application (FILE *in, FILE *out) :
2220 m_window_delegates.clear();
2221 m_window_sp.reset();
2224 ::delscreen(m_screen);
2232 ::setlocale(LC_ALL, "");
2233 ::setlocale(LC_CTYPE, "");
2237 m_screen = ::newterm(NULL, m_out, m_in);
2242 ::keypad(stdscr,TRUE);
2252 Run (Debugger &debugger)
2255 int delay_in_tenths_of_a_second = 1;
2257 // Alas the threading model in curses is a bit lame so we need to
2258 // resort to polling every 0.5 seconds. We could poll for stdin
2259 // ourselves and then pass the keys down but then we need to
2260 // translate all of the escape sequences ourselves. So we resort to
2261 // polling for input because we need to receive async process events
2262 // while in this loop.
2264 halfdelay(delay_in_tenths_of_a_second); // Poll using some number of tenths of seconds seconds when calling Window::GetChar()
2266 ListenerSP listener_sp (new Listener ("lldb.IOHandler.curses.Application"));
2267 ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass());
2268 ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass());
2269 ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass());
2270 debugger.EnableForwardEvents (listener_sp);
2273 #if defined(__APPLE__)
2274 std::deque<int> escape_chars;
2281 m_window_sp->Draw(false);
2282 // All windows should be calling Window::DeferredRefresh() instead
2283 // of Window::Refresh() so we can do a single update and avoid
2284 // any screen blinking
2287 // Cursor hiding isn't working on MacOSX, so hide it in the top left corner
2288 m_window_sp->MoveCursor(0, 0);
2294 #if defined(__APPLE__)
2295 // Terminal.app doesn't map its function keys correctly, F1-F4 default to:
2296 // \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if possible
2298 if (escape_chars.empty())
2299 ch = m_window_sp->GetChar();
2302 ch = escape_chars.front();
2303 escape_chars.pop_front();
2305 if (ch == KEY_ESCAPE)
2307 int ch2 = m_window_sp->GetChar();
2310 int ch3 = m_window_sp->GetChar();
2313 case 'P': ch = KEY_F(1); break;
2314 case 'Q': ch = KEY_F(2); break;
2315 case 'R': ch = KEY_F(3); break;
2316 case 'S': ch = KEY_F(4); break;
2318 escape_chars.push_back(ch2);
2320 escape_chars.push_back(ch3);
2325 escape_chars.push_back(ch2);
2328 int ch = m_window_sp->GetChar();
2333 if (feof(m_in) || ferror(m_in))
2339 // Just a timeout from using halfdelay(), check for events
2341 while (listener_sp->PeekAtNextEvent())
2343 listener_sp->GetNextEvent(event_sp);
2347 Broadcaster *broadcaster = event_sp->GetBroadcaster();
2350 //uint32_t event_type = event_sp->GetType();
2351 ConstString broadcaster_class (broadcaster->GetBroadcasterClass());
2352 if (broadcaster_class == broadcaster_class_process)
2354 debugger.GetCommandInterpreter().UpdateExecutionContext(NULL);
2356 continue; // Don't get any key, just update our view
2365 HandleCharResult key_result = m_window_sp->HandleChar(ch);
2369 debugger.GetCommandInterpreter().UpdateExecutionContext(NULL);
2372 case eKeyNotHandled:
2374 case eQuitApplication:
2381 debugger.CancelForwardEvents (listener_sp);
2389 m_window_sp.reset (new Window ("main", stdscr, false));
2394 GetWindowDelegates ()
2396 return m_window_delegates;
2400 WindowSP m_window_sp;
2401 WindowDelegates m_window_delegates;
2408 } // namespace curses
2411 using namespace curses;
2415 ValueObjectSP valobj;
2420 bool might_have_children;
2422 bool calculated_children;
2423 std::vector<Row> children;
2425 Row (const ValueObjectSP &v, Row *p) :
2431 might_have_children (v ? v->MightHaveChildren() : false),
2433 calculated_children (false),
2442 return 1 + parent->GetDepth();
2450 if (!calculated_children)
2452 calculated_children = true;
2455 const size_t num_children = valobj->GetNumChildren();
2456 for (size_t i=0; i<num_children; ++i)
2458 children.push_back(Row (valobj->GetChildAtIndex(i, true), this));
2471 DrawTree (Window &window)
2474 parent->DrawTreeForChild (window, this, 0);
2476 if (might_have_children)
2478 // It we can get UTF8 characters to work we should try to use the "symbol"
2479 // UTF8 string below
2480 // const char *symbol = "";
2481 // if (row.expanded)
2482 // symbol = "\xe2\x96\xbd ";
2484 // symbol = "\xe2\x96\xb7 ";
2485 // window.PutCString (symbol);
2487 // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
2488 // 'v' or '>' character...
2490 // window.PutChar (ACS_DARROW);
2492 // window.PutChar (ACS_RARROW);
2493 // Since we can't find any good looking right arrow/down arrow
2494 // symbols, just use a diamond...
2495 window.PutChar (ACS_DIAMOND);
2496 window.PutChar (ACS_HLINE);
2501 DrawTreeForChild (Window &window, Row *child, uint32_t reverse_depth)
2504 parent->DrawTreeForChild (window, this, reverse_depth + 1);
2506 if (&children.back() == child)
2509 if (reverse_depth == 0)
2511 window.PutChar (ACS_LLCORNER);
2512 window.PutChar (ACS_HLINE);
2516 window.PutChar (' ');
2517 window.PutChar (' ');
2522 if (reverse_depth == 0)
2524 window.PutChar (ACS_LTEE);
2525 window.PutChar (ACS_HLINE);
2529 window.PutChar (ACS_VLINE);
2530 window.PutChar (' ');
2536 struct DisplayOptions
2547 virtual ~TreeDelegate() {}
2548 virtual void TreeDelegateDrawTreeItem (TreeItem &item, Window &window) = 0;
2549 virtual void TreeDelegateGenerateChildren (TreeItem &item) = 0;
2550 virtual bool TreeDelegateItemSelected (TreeItem &item) = 0; // Return true if we need to update views
2552 typedef std::shared_ptr<TreeDelegate> TreeDelegateSP;
2558 TreeItem (TreeItem *parent, TreeDelegate &delegate, bool might_have_children) :
2560 m_delegate (delegate),
2565 m_might_have_children (might_have_children),
2566 m_is_expanded (false)
2571 operator=(const TreeItem &rhs)
2575 m_parent = rhs.m_parent;
2576 m_delegate = rhs.m_delegate;
2577 m_user_data = rhs.m_user_data;
2578 m_identifier = rhs.m_identifier;
2579 m_row_idx = rhs.m_row_idx;
2580 m_children = rhs.m_children;
2581 m_might_have_children = rhs.m_might_have_children;
2582 m_is_expanded = rhs.m_is_expanded;
2591 return 1 + m_parent->GetDepth();
2596 GetRowIndex () const
2608 Resize (size_t n, const TreeItem &t)
2610 m_children.resize(n, t);
2614 operator [](size_t i)
2616 return m_children[i];
2620 SetRowIndex (int row_idx)
2622 m_row_idx = row_idx;
2628 m_delegate.TreeDelegateGenerateChildren (*this);
2629 return m_children.size();
2635 m_delegate.TreeDelegateItemSelected(*this);
2638 CalculateRowIndexes (int &row_idx)
2640 SetRowIndex(row_idx);
2643 const bool expanded = IsExpanded();
2645 // The root item must calculate its children,
2646 // or we must calculate the number of children
2647 // if the item is expanded
2648 if (m_parent == NULL || expanded)
2651 for (auto &item : m_children)
2654 item.CalculateRowIndexes(row_idx);
2656 item.SetRowIndex(-1);
2669 return m_is_expanded;
2675 m_is_expanded = true;
2681 m_is_expanded = false;
2685 Draw (Window &window,
2686 const int first_visible_row,
2687 const uint32_t selected_row_idx,
2691 if (num_rows_left <= 0)
2694 if (m_row_idx >= first_visible_row)
2696 window.MoveCursor(2, row_idx + 1);
2699 m_parent->DrawTreeForChild (window, this, 0);
2701 if (m_might_have_children)
2703 // It we can get UTF8 characters to work we should try to use the "symbol"
2704 // UTF8 string below
2705 // const char *symbol = "";
2706 // if (row.expanded)
2707 // symbol = "\xe2\x96\xbd ";
2709 // symbol = "\xe2\x96\xb7 ";
2710 // window.PutCString (symbol);
2712 // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
2713 // 'v' or '>' character...
2715 // window.PutChar (ACS_DARROW);
2717 // window.PutChar (ACS_RARROW);
2718 // Since we can't find any good looking right arrow/down arrow
2719 // symbols, just use a diamond...
2720 window.PutChar (ACS_DIAMOND);
2721 window.PutChar (ACS_HLINE);
2724 (selected_row_idx == static_cast<size_t>(m_row_idx)) && window.IsActive();
2727 window.AttributeOn(A_REVERSE);
2729 m_delegate.TreeDelegateDrawTreeItem(*this, window);
2732 window.AttributeOff(A_REVERSE);
2737 if (num_rows_left <= 0)
2738 return false; // We are done drawing...
2742 for (auto &item : m_children)
2744 // If we displayed all the rows and item.Draw() returns
2745 // false we are done drawing and can exit this for loop
2746 if (item.Draw(window, first_visible_row, selected_row_idx, row_idx, num_rows_left) == false)
2750 return num_rows_left >= 0; // Return true if not done drawing yet
2754 DrawTreeForChild (Window &window, TreeItem *child, uint32_t reverse_depth)
2757 m_parent->DrawTreeForChild (window, this, reverse_depth + 1);
2759 if (&m_children.back() == child)
2762 if (reverse_depth == 0)
2764 window.PutChar (ACS_LLCORNER);
2765 window.PutChar (ACS_HLINE);
2769 window.PutChar (' ');
2770 window.PutChar (' ');
2775 if (reverse_depth == 0)
2777 window.PutChar (ACS_LTEE);
2778 window.PutChar (ACS_HLINE);
2782 window.PutChar (ACS_VLINE);
2783 window.PutChar (' ');
2789 GetItemForRowIndex (uint32_t row_idx)
2791 if (static_cast<uint32_t>(m_row_idx) == row_idx)
2793 if (m_children.empty())
2795 if (static_cast<uint32_t>(m_children.back().m_row_idx) < row_idx)
2799 for (auto &item : m_children)
2801 TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx);
2802 if (selected_item_ptr)
2803 return selected_item_ptr;
2816 SetUserData (void *user_data)
2818 m_user_data = user_data;
2822 GetIdentifier() const
2824 return m_identifier;
2828 SetIdentifier (uint64_t identifier)
2830 m_identifier = identifier;
2835 SetMightHaveChildren (bool b)
2837 m_might_have_children = b;
2842 TreeDelegate &m_delegate;
2844 uint64_t m_identifier;
2845 int m_row_idx; // Zero based visible row index, -1 if not visible or for the root item
2846 std::vector<TreeItem> m_children;
2847 bool m_might_have_children;
2852 class TreeWindowDelegate : public WindowDelegate
2855 TreeWindowDelegate (Debugger &debugger, const TreeDelegateSP &delegate_sp) :
2856 m_debugger (debugger),
2857 m_delegate_sp (delegate_sp),
2858 m_root (NULL, *delegate_sp, true),
2859 m_selected_item (NULL),
2861 m_selected_row_idx (0),
2862 m_first_visible_row (0),
2871 NumVisibleRows () const
2873 return m_max_y - m_min_y;
2877 WindowDelegateDraw (Window &window, bool force)
2879 ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext());
2880 Process *process = exe_ctx.GetProcessPtr();
2882 bool display_content = false;
2885 StateType state = process->GetState();
2886 if (StateIsStoppedState(state, true))
2888 // We are stopped, so it is ok to
2889 display_content = true;
2891 else if (StateIsRunningState(state))
2893 return true; // Don't do any updating when we are running
2899 m_max_x = window.GetWidth() - 1;
2900 m_max_y = window.GetHeight() - 1;
2903 window.DrawTitleBox (window.GetName());
2905 if (display_content)
2907 const int num_visible_rows = NumVisibleRows();
2909 m_root.CalculateRowIndexes(m_num_rows);
2911 // If we unexpanded while having something selected our
2912 // total number of rows is less than the num visible rows,
2913 // then make sure we show all the rows by setting the first
2914 // visible row accordingly.
2915 if (m_first_visible_row > 0 && m_num_rows < num_visible_rows)
2916 m_first_visible_row = 0;
2918 // Make sure the selected row is always visible
2919 if (m_selected_row_idx < m_first_visible_row)
2920 m_first_visible_row = m_selected_row_idx;
2921 else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
2922 m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
2925 int num_rows_left = num_visible_rows;
2926 m_root.Draw (window, m_first_visible_row, m_selected_row_idx, row_idx, num_rows_left);
2927 // Get the selected row
2928 m_selected_item = m_root.GetItemForRowIndex (m_selected_row_idx);
2932 m_selected_item = NULL;
2935 window.DeferredRefresh();
2938 return true; // Drawing handled
2942 virtual const char *
2943 WindowDelegateGetHelpText ()
2945 return "Thread window keyboard shortcuts:";
2949 WindowDelegateGetKeyHelp ()
2951 static curses::KeyHelp g_source_view_key_help[] = {
2952 { KEY_UP, "Select previous item" },
2953 { KEY_DOWN, "Select next item" },
2954 { KEY_RIGHT, "Expand the selected item" },
2955 { KEY_LEFT, "Unexpand the selected item or select parent if not expanded" },
2956 { KEY_PPAGE, "Page up" },
2957 { KEY_NPAGE, "Page down" },
2958 { 'h', "Show help dialog" },
2959 { ' ', "Toggle item expansion" },
2961 { '.', "Page down" },
2964 return g_source_view_key_help;
2967 virtual HandleCharResult
2968 WindowDelegateHandleChar (Window &window, int c)
2975 if (m_first_visible_row > 0)
2977 if (m_first_visible_row > m_max_y)
2978 m_first_visible_row -= m_max_y;
2980 m_first_visible_row = 0;
2981 m_selected_row_idx = m_first_visible_row;
2982 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
2983 if (m_selected_item)
2984 m_selected_item->ItemWasSelected ();
2991 if (m_num_rows > m_max_y)
2993 if (m_first_visible_row + m_max_y < m_num_rows)
2995 m_first_visible_row += m_max_y;
2996 m_selected_row_idx = m_first_visible_row;
2997 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
2998 if (m_selected_item)
2999 m_selected_item->ItemWasSelected ();
3005 if (m_selected_row_idx > 0)
3007 --m_selected_row_idx;
3008 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3009 if (m_selected_item)
3010 m_selected_item->ItemWasSelected ();
3014 if (m_selected_row_idx + 1 < m_num_rows)
3016 ++m_selected_row_idx;
3017 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3018 if (m_selected_item)
3019 m_selected_item->ItemWasSelected ();
3024 if (m_selected_item)
3026 if (!m_selected_item->IsExpanded())
3027 m_selected_item->Expand();
3032 if (m_selected_item)
3034 if (m_selected_item->IsExpanded())
3035 m_selected_item->Unexpand();
3036 else if (m_selected_item->GetParent())
3038 m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex();
3039 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3040 if (m_selected_item)
3041 m_selected_item->ItemWasSelected ();
3047 // Toggle expansion state when SPACE is pressed
3048 if (m_selected_item)
3050 if (m_selected_item->IsExpanded())
3051 m_selected_item->Unexpand();
3053 m_selected_item->Expand();
3058 window.CreateHelpSubwindow ();
3064 return eKeyNotHandled;
3068 Debugger &m_debugger;
3069 TreeDelegateSP m_delegate_sp;
3071 TreeItem *m_selected_item;
3073 int m_selected_row_idx;
3074 int m_first_visible_row;
3082 class FrameTreeDelegate : public TreeDelegate
3085 FrameTreeDelegate () :
3090 virtual ~FrameTreeDelegate()
3095 TreeDelegateDrawTreeItem (TreeItem &item, Window &window)
3097 Thread* thread = (Thread*)item.GetUserData();
3100 const uint64_t frame_idx = item.GetIdentifier();
3101 StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_idx);
3105 const SymbolContext &sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
3106 ExecutionContext exe_ctx (frame_sp);
3107 //const char *frame_format = "frame #${frame.index}: ${module.file.basename}{`${function.name}${function.pc-offset}}}";
3108 const char *frame_format = "frame #${frame.index}: {${function.name}${function.pc-offset}}}";
3109 if (Debugger::FormatPrompt (frame_format, &sc, &exe_ctx, NULL, strm))
3112 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
3118 TreeDelegateGenerateChildren (TreeItem &item)
3120 // No children for frames yet...
3124 TreeDelegateItemSelected (TreeItem &item)
3126 Thread* thread = (Thread*)item.GetUserData();
3129 thread->GetProcess()->GetThreadList().SetSelectedThreadByID(thread->GetID());
3130 const uint64_t frame_idx = item.GetIdentifier();
3131 thread->SetSelectedFrameByIndex(frame_idx);
3138 class ThreadTreeDelegate : public TreeDelegate
3141 ThreadTreeDelegate (Debugger &debugger) :
3143 m_debugger (debugger),
3144 m_tid (LLDB_INVALID_THREAD_ID),
3145 m_stop_id (UINT32_MAX)
3150 ~ThreadTreeDelegate()
3157 return m_debugger.GetCommandInterpreter().GetExecutionContext().GetProcessSP();
3161 GetThread (const TreeItem &item)
3163 ProcessSP process_sp = GetProcess ();
3165 return process_sp->GetThreadList().FindThreadByID(item.GetIdentifier());
3170 TreeDelegateDrawTreeItem (TreeItem &item, Window &window)
3172 ThreadSP thread_sp = GetThread (item);
3176 ExecutionContext exe_ctx (thread_sp);
3177 const char *format = "thread #${thread.index}: tid = ${thread.id}{, stop reason = ${thread.stop-reason}}";
3178 if (Debugger::FormatPrompt (format, NULL, &exe_ctx, NULL, strm))
3181 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
3186 TreeDelegateGenerateChildren (TreeItem &item)
3188 ProcessSP process_sp = GetProcess ();
3189 if (process_sp && process_sp->IsAlive())
3191 StateType state = process_sp->GetState();
3192 if (StateIsStoppedState(state, true))
3194 ThreadSP thread_sp = GetThread (item);
3197 if (m_stop_id == process_sp->GetStopID() && thread_sp->GetID() == m_tid)
3198 return; // Children are already up to date
3199 if (!m_frame_delegate_sp)
3201 // Always expand the thread item the first time we show it
3202 m_frame_delegate_sp.reset (new FrameTreeDelegate());
3205 m_stop_id = process_sp->GetStopID();
3206 m_tid = thread_sp->GetID();
3208 TreeItem t (&item, *m_frame_delegate_sp, false);
3209 size_t num_frames = thread_sp->GetStackFrameCount();
3210 item.Resize (num_frames, t);
3211 for (size_t i=0; i<num_frames; ++i)
3213 item[i].SetUserData(thread_sp.get());
3214 item[i].SetIdentifier(i);
3220 item.ClearChildren();
3224 TreeDelegateItemSelected (TreeItem &item)
3226 ProcessSP process_sp = GetProcess ();
3227 if (process_sp && process_sp->IsAlive())
3229 StateType state = process_sp->GetState();
3230 if (StateIsStoppedState(state, true))
3232 ThreadSP thread_sp = GetThread (item);
3235 ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList();
3236 Mutex::Locker locker (thread_list.GetMutex());
3237 ThreadSP selected_thread_sp = thread_list.GetSelectedThread();
3238 if (selected_thread_sp->GetID() != thread_sp->GetID())
3240 thread_list.SetSelectedThreadByID(thread_sp->GetID());
3250 Debugger &m_debugger;
3251 std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp;
3252 lldb::user_id_t m_tid;
3256 class ThreadsTreeDelegate : public TreeDelegate
3259 ThreadsTreeDelegate (Debugger &debugger) :
3261 m_thread_delegate_sp (),
3262 m_debugger (debugger),
3263 m_stop_id (UINT32_MAX)
3268 ~ThreadsTreeDelegate()
3275 return m_debugger.GetCommandInterpreter().GetExecutionContext().GetProcessSP();
3279 TreeDelegateDrawTreeItem (TreeItem &item, Window &window)
3281 ProcessSP process_sp = GetProcess ();
3282 if (process_sp && process_sp->IsAlive())
3285 ExecutionContext exe_ctx (process_sp);
3286 const char *format = "process ${process.id}{, name = ${process.name}}";
3287 if (Debugger::FormatPrompt (format, NULL, &exe_ctx, NULL, strm))
3290 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
3296 TreeDelegateGenerateChildren (TreeItem &item)
3298 ProcessSP process_sp = GetProcess ();
3299 if (process_sp && process_sp->IsAlive())
3301 StateType state = process_sp->GetState();
3302 if (StateIsStoppedState(state, true))
3304 const uint32_t stop_id = process_sp->GetStopID();
3305 if (m_stop_id == stop_id)
3306 return; // Children are already up to date
3308 m_stop_id = stop_id;
3310 if (!m_thread_delegate_sp)
3312 // Always expand the thread item the first time we show it
3314 m_thread_delegate_sp.reset (new ThreadTreeDelegate(m_debugger));
3317 TreeItem t (&item, *m_thread_delegate_sp, false);
3318 ThreadList &threads = process_sp->GetThreadList();
3319 Mutex::Locker locker (threads.GetMutex());
3320 size_t num_threads = threads.GetSize();
3321 item.Resize (num_threads, t);
3322 for (size_t i=0; i<num_threads; ++i)
3324 item[i].SetIdentifier(threads.GetThreadAtIndex(i)->GetID());
3325 item[i].SetMightHaveChildren(true);
3330 item.ClearChildren();
3334 TreeDelegateItemSelected (TreeItem &item)
3340 std::shared_ptr<ThreadTreeDelegate> m_thread_delegate_sp;
3341 Debugger &m_debugger;
3345 class ValueObjectListDelegate : public WindowDelegate
3348 ValueObjectListDelegate () :
3351 m_selected_row (NULL),
3352 m_selected_row_idx (0),
3353 m_first_visible_row (0),
3360 ValueObjectListDelegate (ValueObjectList &valobj_list) :
3361 m_valobj_list (valobj_list),
3363 m_selected_row (NULL),
3364 m_selected_row_idx (0),
3365 m_first_visible_row (0),
3370 SetValues (valobj_list);
3374 ~ValueObjectListDelegate()
3379 SetValues (ValueObjectList &valobj_list)
3381 m_selected_row = NULL;
3382 m_selected_row_idx = 0;
3383 m_first_visible_row = 0;
3386 m_valobj_list = valobj_list;
3387 const size_t num_values = m_valobj_list.GetSize();
3388 for (size_t i=0; i<num_values; ++i)
3389 m_rows.push_back(Row(m_valobj_list.GetValueObjectAtIndex(i), NULL));
3393 WindowDelegateDraw (Window &window, bool force)
3398 m_max_x = window.GetWidth() - 1;
3399 m_max_y = window.GetHeight() - 1;
3402 window.DrawTitleBox (window.GetName());
3404 const int num_visible_rows = NumVisibleRows();
3405 const int num_rows = CalculateTotalNumberRows (m_rows);
3407 // If we unexpanded while having something selected our
3408 // total number of rows is less than the num visible rows,
3409 // then make sure we show all the rows by setting the first
3410 // visible row accordingly.
3411 if (m_first_visible_row > 0 && num_rows < num_visible_rows)
3412 m_first_visible_row = 0;
3414 // Make sure the selected row is always visible
3415 if (m_selected_row_idx < m_first_visible_row)
3416 m_first_visible_row = m_selected_row_idx;
3417 else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
3418 m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
3420 DisplayRows (window, m_rows, g_options);
3422 window.DeferredRefresh();
3424 // Get the selected row
3425 m_selected_row = GetRowForRowIndex (m_selected_row_idx);
3426 // Keep the cursor on the selected row so the highlight and the cursor
3427 // are always on the same line
3429 window.MoveCursor (m_selected_row->x,
3432 return true; // Drawing handled
3436 WindowDelegateGetKeyHelp ()
3438 static curses::KeyHelp g_source_view_key_help[] = {
3439 { KEY_UP, "Select previous item" },
3440 { KEY_DOWN, "Select next item" },
3441 { KEY_RIGHT, "Expand selected item" },
3442 { KEY_LEFT, "Unexpand selected item or select parent if not expanded" },
3443 { KEY_PPAGE, "Page up" },
3444 { KEY_NPAGE, "Page down" },
3445 { 'A', "Format as annotated address" },
3446 { 'b', "Format as binary" },
3447 { 'B', "Format as hex bytes with ASCII" },
3448 { 'c', "Format as character" },
3449 { 'd', "Format as a signed integer" },
3450 { 'D', "Format selected value using the default format for the type" },
3451 { 'f', "Format as float" },
3452 { 'h', "Show help dialog" },
3453 { 'i', "Format as instructions" },
3454 { 'o', "Format as octal" },
3455 { 'p', "Format as pointer" },
3456 { 's', "Format as C string" },
3457 { 't', "Toggle showing/hiding type names" },
3458 { 'u', "Format as an unsigned integer" },
3459 { 'x', "Format as hex" },
3460 { 'X', "Format as uppercase hex" },
3461 { ' ', "Toggle item expansion" },
3463 { '.', "Page down" },
3466 return g_source_view_key_help;
3470 virtual HandleCharResult
3471 WindowDelegateHandleChar (Window &window, int c)
3489 // Change the format for the currently selected item
3491 m_selected_row->valobj->SetFormat (FormatForChar (c));
3495 // Toggle showing type names
3496 g_options.show_types = !g_options.show_types;
3502 if (m_first_visible_row > 0)
3504 if (static_cast<int>(m_first_visible_row) > m_max_y)
3505 m_first_visible_row -= m_max_y;
3507 m_first_visible_row = 0;
3508 m_selected_row_idx = m_first_visible_row;
3515 if (m_num_rows > static_cast<size_t>(m_max_y))
3517 if (m_first_visible_row + m_max_y < m_num_rows)
3519 m_first_visible_row += m_max_y;
3520 m_selected_row_idx = m_first_visible_row;
3526 if (m_selected_row_idx > 0)
3527 --m_selected_row_idx;
3530 if (m_selected_row_idx + 1 < m_num_rows)
3531 ++m_selected_row_idx;
3537 if (!m_selected_row->expanded)
3538 m_selected_row->Expand();
3545 if (m_selected_row->expanded)
3546 m_selected_row->Unexpand();
3547 else if (m_selected_row->parent)
3548 m_selected_row_idx = m_selected_row->parent->row_idx;
3553 // Toggle expansion state when SPACE is pressed
3556 if (m_selected_row->expanded)
3557 m_selected_row->Unexpand();
3559 m_selected_row->Expand();
3564 window.CreateHelpSubwindow ();
3570 return eKeyNotHandled;
3574 ValueObjectList m_valobj_list;
3575 std::vector<Row> m_rows;
3576 Row *m_selected_row;
3577 uint32_t m_selected_row_idx;
3578 uint32_t m_first_visible_row;
3579 uint32_t m_num_rows;
3586 FormatForChar (int c)
3590 case 'x': return eFormatHex;
3591 case 'X': return eFormatHexUppercase;
3592 case 'o': return eFormatOctal;
3593 case 's': return eFormatCString;
3594 case 'u': return eFormatUnsigned;
3595 case 'd': return eFormatDecimal;
3596 case 'D': return eFormatDefault;
3597 case 'i': return eFormatInstruction;
3598 case 'A': return eFormatAddressInfo;
3599 case 'p': return eFormatPointer;
3600 case 'c': return eFormatChar;
3601 case 'b': return eFormatBinary;
3602 case 'B': return eFormatBytesWithASCII;
3603 case 'f': return eFormatFloat;
3605 return eFormatDefault;
3609 DisplayRowObject (Window &window,
3611 DisplayOptions &options,
3615 ValueObject *valobj = row.valobj.get();
3620 const char *type_name = options.show_types ? valobj->GetTypeName().GetCString() : NULL;
3621 const char *name = valobj->GetName().GetCString();
3622 const char *value = valobj->GetValueAsCString ();
3623 const char *summary = valobj->GetSummaryAsCString ();
3625 window.MoveCursor (row.x, row.y);
3627 row.DrawTree (window);
3630 window.AttributeOn(A_REVERSE);
3632 if (type_name && type_name[0])
3633 window.Printf ("(%s) ", type_name);
3635 if (name && name[0])
3636 window.PutCString(name);
3638 attr_t changd_attr = 0;
3639 if (valobj->GetValueDidChange())
3640 changd_attr = COLOR_PAIR(5) | A_BOLD;
3642 if (value && value[0])
3644 window.PutCString(" = ");
3646 window.AttributeOn(changd_attr);
3647 window.PutCString (value);
3649 window.AttributeOff(changd_attr);
3652 if (summary && summary[0])
3654 window.PutChar(' ');
3656 window.AttributeOn(changd_attr);
3657 window.PutCString(summary);
3659 window.AttributeOff(changd_attr);
3663 window.AttributeOff (A_REVERSE);
3668 DisplayRows (Window &window,
3669 std::vector<Row> &rows,
3670 DisplayOptions &options)
3675 bool window_is_active = window.IsActive();
3676 for (auto &row : rows)
3678 const bool last_child = row.parent && &rows[rows.size()-1] == &row;
3679 // Save the row index in each Row structure
3680 row.row_idx = m_num_rows;
3681 if ((m_num_rows >= m_first_visible_row) &&
3682 ((m_num_rows - m_first_visible_row) < static_cast<size_t>(NumVisibleRows())))
3685 row.y = m_num_rows - m_first_visible_row + 1;
3686 if (DisplayRowObject (window,
3689 window_is_active && m_num_rows == m_selected_row_idx,
3707 if (row.expanded && !row.children.empty())
3709 DisplayRows (window,
3717 CalculateTotalNumberRows (const std::vector<Row> &rows)
3720 for (const auto &row : rows)
3724 row_count += CalculateTotalNumberRows(row.children);
3729 GetRowForRowIndexImpl (std::vector<Row> &rows, size_t &row_index)
3731 for (auto &row : rows)
3738 if (row.expanded && !row.children.empty())
3740 Row *result = GetRowForRowIndexImpl (row.children, row_index);
3750 GetRowForRowIndex (size_t row_index)
3752 return GetRowForRowIndexImpl (m_rows, row_index);
3756 NumVisibleRows () const
3758 return m_max_y - m_min_y;
3761 static DisplayOptions g_options;
3764 class FrameVariablesWindowDelegate : public ValueObjectListDelegate
3767 FrameVariablesWindowDelegate (Debugger &debugger) :
3768 ValueObjectListDelegate (),
3769 m_debugger (debugger),
3770 m_frame_block (NULL)
3775 ~FrameVariablesWindowDelegate()
3779 virtual const char *
3780 WindowDelegateGetHelpText ()
3782 return "Frame variable window keyboard shortcuts:";
3786 WindowDelegateDraw (Window &window, bool force)
3788 ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext());
3789 Process *process = exe_ctx.GetProcessPtr();
3790 Block *frame_block = NULL;
3791 StackFrame *frame = NULL;
3795 StateType state = process->GetState();
3796 if (StateIsStoppedState(state, true))
3798 frame = exe_ctx.GetFramePtr();
3800 frame_block = frame->GetFrameBlock ();
3802 else if (StateIsRunningState(state))
3804 return true; // Don't do any updating when we are running
3808 ValueObjectList local_values;
3811 // Only update the variables if they have changed
3812 if (m_frame_block != frame_block)
3814 m_frame_block = frame_block;
3816 VariableList *locals = frame->GetVariableList(true);
3819 const DynamicValueType use_dynamic = eDynamicDontRunTarget;
3820 const size_t num_locals = locals->GetSize();
3821 for (size_t i=0; i<num_locals; ++i)
3822 local_values.Append(frame->GetValueObjectForFrameVariable (locals->GetVariableAtIndex(i), use_dynamic));
3823 // Update the values
3824 SetValues(local_values);
3830 m_frame_block = NULL;
3831 // Update the values with an empty list if there is no frame
3832 SetValues(local_values);
3835 return ValueObjectListDelegate::WindowDelegateDraw (window, force);
3840 Debugger &m_debugger;
3841 Block *m_frame_block;
3845 class RegistersWindowDelegate : public ValueObjectListDelegate
3848 RegistersWindowDelegate (Debugger &debugger) :
3849 ValueObjectListDelegate (),
3850 m_debugger (debugger)
3855 ~RegistersWindowDelegate()
3859 virtual const char *
3860 WindowDelegateGetHelpText ()
3862 return "Register window keyboard shortcuts:";
3866 WindowDelegateDraw (Window &window, bool force)
3868 ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext());
3869 StackFrame *frame = exe_ctx.GetFramePtr();
3871 ValueObjectList value_list;
3874 if (frame->GetStackID() != m_stack_id)
3876 m_stack_id = frame->GetStackID();
3877 RegisterContextSP reg_ctx (frame->GetRegisterContext());
3880 const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
3881 for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx)
3883 value_list.Append(ValueObjectRegisterSet::Create (frame, reg_ctx, set_idx));
3886 SetValues(value_list);
3891 Process *process = exe_ctx.GetProcessPtr();
3892 if (process && process->IsAlive())
3893 return true; // Don't do any updating if we are running
3896 // Update the values with an empty list if there
3897 // is no process or the process isn't alive anymore
3898 SetValues(value_list);
3901 return ValueObjectListDelegate::WindowDelegateDraw (window, force);
3905 Debugger &m_debugger;
3910 CursesKeyToCString (int ch)
3912 static char g_desc[32];
3913 if (ch >= KEY_F0 && ch < KEY_F0 + 64)
3915 snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0);
3920 case KEY_DOWN: return "down";
3921 case KEY_UP: return "up";
3922 case KEY_LEFT: return "left";
3923 case KEY_RIGHT: return "right";
3924 case KEY_HOME: return "home";
3925 case KEY_BACKSPACE: return "backspace";
3926 case KEY_DL: return "delete-line";
3927 case KEY_IL: return "insert-line";
3928 case KEY_DC: return "delete-char";
3929 case KEY_IC: return "insert-char";
3930 case KEY_CLEAR: return "clear";
3931 case KEY_EOS: return "clear-to-eos";
3932 case KEY_EOL: return "clear-to-eol";
3933 case KEY_SF: return "scroll-forward";
3934 case KEY_SR: return "scroll-backward";
3935 case KEY_NPAGE: return "page-down";
3936 case KEY_PPAGE: return "page-up";
3937 case KEY_STAB: return "set-tab";
3938 case KEY_CTAB: return "clear-tab";
3939 case KEY_CATAB: return "clear-all-tabs";
3940 case KEY_ENTER: return "enter";
3941 case KEY_PRINT: return "print";
3942 case KEY_LL: return "lower-left key";
3943 case KEY_A1: return "upper left of keypad";
3944 case KEY_A3: return "upper right of keypad";
3945 case KEY_B2: return "center of keypad";
3946 case KEY_C1: return "lower left of keypad";
3947 case KEY_C3: return "lower right of keypad";
3948 case KEY_BTAB: return "back-tab key";
3949 case KEY_BEG: return "begin key";
3950 case KEY_CANCEL: return "cancel key";
3951 case KEY_CLOSE: return "close key";
3952 case KEY_COMMAND: return "command key";
3953 case KEY_COPY: return "copy key";
3954 case KEY_CREATE: return "create key";
3955 case KEY_END: return "end key";
3956 case KEY_EXIT: return "exit key";
3957 case KEY_FIND: return "find key";
3958 case KEY_HELP: return "help key";
3959 case KEY_MARK: return "mark key";
3960 case KEY_MESSAGE: return "message key";
3961 case KEY_MOVE: return "move key";
3962 case KEY_NEXT: return "next key";
3963 case KEY_OPEN: return "open key";
3964 case KEY_OPTIONS: return "options key";
3965 case KEY_PREVIOUS: return "previous key";
3966 case KEY_REDO: return "redo key";
3967 case KEY_REFERENCE: return "reference key";
3968 case KEY_REFRESH: return "refresh key";
3969 case KEY_REPLACE: return "replace key";
3970 case KEY_RESTART: return "restart key";
3971 case KEY_RESUME: return "resume key";
3972 case KEY_SAVE: return "save key";
3973 case KEY_SBEG: return "shifted begin key";
3974 case KEY_SCANCEL: return "shifted cancel key";
3975 case KEY_SCOMMAND: return "shifted command key";
3976 case KEY_SCOPY: return "shifted copy key";
3977 case KEY_SCREATE: return "shifted create key";
3978 case KEY_SDC: return "shifted delete-character key";
3979 case KEY_SDL: return "shifted delete-line key";
3980 case KEY_SELECT: return "select key";
3981 case KEY_SEND: return "shifted end key";
3982 case KEY_SEOL: return "shifted clear-to-end-of-line key";
3983 case KEY_SEXIT: return "shifted exit key";
3984 case KEY_SFIND: return "shifted find key";
3985 case KEY_SHELP: return "shifted help key";
3986 case KEY_SHOME: return "shifted home key";
3987 case KEY_SIC: return "shifted insert-character key";
3988 case KEY_SLEFT: return "shifted left-arrow key";
3989 case KEY_SMESSAGE: return "shifted message key";
3990 case KEY_SMOVE: return "shifted move key";
3991 case KEY_SNEXT: return "shifted next key";
3992 case KEY_SOPTIONS: return "shifted options key";
3993 case KEY_SPREVIOUS: return "shifted previous key";
3994 case KEY_SPRINT: return "shifted print key";
3995 case KEY_SREDO: return "shifted redo key";
3996 case KEY_SREPLACE: return "shifted replace key";
3997 case KEY_SRIGHT: return "shifted right-arrow key";
3998 case KEY_SRSUME: return "shifted resume key";
3999 case KEY_SSAVE: return "shifted save key";
4000 case KEY_SSUSPEND: return "shifted suspend key";
4001 case KEY_SUNDO: return "shifted undo key";
4002 case KEY_SUSPEND: return "suspend key";
4003 case KEY_UNDO: return "undo key";
4004 case KEY_MOUSE: return "Mouse event has occurred";
4005 case KEY_RESIZE: return "Terminal resize event";
4006 case KEY_EVENT: return "We were interrupted by an event";
4007 case KEY_RETURN: return "return";
4008 case ' ': return "space";
4009 case '\t': return "tab";
4010 case KEY_ESCAPE: return "escape";
4013 snprintf(g_desc, sizeof(g_desc), "%c", ch);
4015 snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch);
4021 HelpDialogDelegate::HelpDialogDelegate (const char *text, KeyHelp *key_help_array) :
4023 m_first_visible_line (0)
4025 if (text && text[0])
4027 m_text.SplitIntoLines(text);
4028 m_text.AppendString("");
4032 for (KeyHelp *key = key_help_array; key->ch; ++key)
4034 StreamString key_description;
4035 key_description.Printf("%10s - %s", CursesKeyToCString(key->ch), key->description);
4036 m_text.AppendString(std::move(key_description.GetString()));
4041 HelpDialogDelegate::~HelpDialogDelegate()
4046 HelpDialogDelegate::WindowDelegateDraw (Window &window, bool force)
4049 const int window_height = window.GetHeight();
4052 const int min_y = y;
4053 const int max_y = window_height - 1 - y;
4054 const size_t num_visible_lines = max_y - min_y + 1;
4055 const size_t num_lines = m_text.GetSize();
4056 const char *bottom_message;
4057 if (num_lines <= num_visible_lines)
4058 bottom_message = "Press any key to exit";
4060 bottom_message = "Use arrows to scroll, any other key to exit";
4061 window.DrawTitleBox(window.GetName(), bottom_message);
4064 window.MoveCursor(x, y);
4065 window.PutCStringTruncated(m_text.GetStringAtIndex(m_first_visible_line + y - min_y), 1);
4072 HelpDialogDelegate::WindowDelegateHandleChar (Window &window, int key)
4075 const size_t num_lines = m_text.GetSize();
4076 const size_t num_visible_lines = window.GetHeight() - 2;
4078 if (num_lines <= num_visible_lines)
4081 // If we have all lines visible and don't need scrolling, then any
4082 // key press will cause us to exit
4089 if (m_first_visible_line > 0)
4090 --m_first_visible_line;
4094 if (m_first_visible_line + num_visible_lines < num_lines)
4095 ++m_first_visible_line;
4100 if (m_first_visible_line > 0)
4102 if (static_cast<size_t>(m_first_visible_line) >= num_visible_lines)
4103 m_first_visible_line -= num_visible_lines;
4105 m_first_visible_line = 0;
4110 if (m_first_visible_line + num_visible_lines < num_lines)
4112 m_first_visible_line += num_visible_lines;
4113 if (static_cast<size_t>(m_first_visible_line) > num_lines)
4114 m_first_visible_line = num_lines - num_visible_lines;
4123 window.GetParent()->RemoveSubWindow(&window);
4127 class ApplicationDelegate :
4128 public WindowDelegate,
4138 eMenuID_TargetCreate,
4139 eMenuID_TargetDelete,
4142 eMenuID_ProcessAttach,
4143 eMenuID_ProcessDetach,
4144 eMenuID_ProcessLaunch,
4145 eMenuID_ProcessContinue,
4146 eMenuID_ProcessHalt,
4147 eMenuID_ProcessKill,
4150 eMenuID_ThreadStepIn,
4151 eMenuID_ThreadStepOver,
4152 eMenuID_ThreadStepOut,
4155 eMenuID_ViewBacktrace,
4156 eMenuID_ViewRegisters,
4158 eMenuID_ViewVariables,
4164 ApplicationDelegate (Application &app, Debugger &debugger) :
4168 m_debugger (debugger)
4173 ~ApplicationDelegate ()
4177 WindowDelegateDraw (Window &window, bool force)
4179 return false; // Drawing not handled, let standard window drawing happen
4182 virtual HandleCharResult
4183 WindowDelegateHandleChar (Window &window, int key)
4188 window.SelectNextWindowAsActive();
4192 window.CreateHelpSubwindow();
4196 return eQuitApplication;
4201 return eKeyNotHandled;
4205 virtual const char *
4206 WindowDelegateGetHelpText ()
4208 return "Welcome to the LLDB curses GUI.\n\n"
4209 "Press the TAB key to change the selected view.\n"
4210 "Each view has its own keyboard shortcuts, press 'h' to open a dialog to display them.\n\n"
4211 "Common key bindings for all views:";
4215 WindowDelegateGetKeyHelp ()
4217 static curses::KeyHelp g_source_view_key_help[] = {
4218 { '\t', "Select next view" },
4219 { 'h', "Show help dialog with view specific key bindings" },
4221 { '.', "Page down" },
4222 { KEY_UP, "Select previous" },
4223 { KEY_DOWN, "Select next" },
4224 { KEY_LEFT, "Unexpand or select parent" },
4225 { KEY_RIGHT, "Expand" },
4226 { KEY_PPAGE, "Page up" },
4227 { KEY_NPAGE, "Page down" },
4230 return g_source_view_key_help;
4233 virtual MenuActionResult
4234 MenuDelegateAction (Menu &menu)
4236 switch (menu.GetIdentifier())
4238 case eMenuID_ThreadStepIn:
4240 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4241 if (exe_ctx.HasThreadScope())
4243 Process *process = exe_ctx.GetProcessPtr();
4244 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4245 exe_ctx.GetThreadRef().StepIn(true);
4248 return MenuActionResult::Handled;
4250 case eMenuID_ThreadStepOut:
4252 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4253 if (exe_ctx.HasThreadScope())
4255 Process *process = exe_ctx.GetProcessPtr();
4256 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4257 exe_ctx.GetThreadRef().StepOut();
4260 return MenuActionResult::Handled;
4262 case eMenuID_ThreadStepOver:
4264 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4265 if (exe_ctx.HasThreadScope())
4267 Process *process = exe_ctx.GetProcessPtr();
4268 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4269 exe_ctx.GetThreadRef().StepOver(true);
4272 return MenuActionResult::Handled;
4274 case eMenuID_ProcessContinue:
4276 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4277 if (exe_ctx.HasProcessScope())
4279 Process *process = exe_ctx.GetProcessPtr();
4280 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4284 return MenuActionResult::Handled;
4286 case eMenuID_ProcessKill:
4288 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4289 if (exe_ctx.HasProcessScope())
4291 Process *process = exe_ctx.GetProcessPtr();
4292 if (process && process->IsAlive())
4296 return MenuActionResult::Handled;
4298 case eMenuID_ProcessHalt:
4300 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4301 if (exe_ctx.HasProcessScope())
4303 Process *process = exe_ctx.GetProcessPtr();
4304 if (process && process->IsAlive())
4308 return MenuActionResult::Handled;
4310 case eMenuID_ProcessDetach:
4312 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4313 if (exe_ctx.HasProcessScope())
4315 Process *process = exe_ctx.GetProcessPtr();
4316 if (process && process->IsAlive())
4317 process->Detach(false);
4320 return MenuActionResult::Handled;
4322 case eMenuID_Process:
4324 // Populate the menu with all of the threads if the process is stopped when
4325 // the Process menu gets selected and is about to display its submenu.
4326 Menus &submenus = menu.GetSubmenus();
4327 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4328 Process *process = exe_ctx.GetProcessPtr();
4329 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4331 if (submenus.size() == 7)
4332 menu.AddSubmenu (MenuSP (new Menu(Menu::Type::Separator)));
4333 else if (submenus.size() > 8)
4334 submenus.erase (submenus.begin() + 8, submenus.end());
4336 ThreadList &threads = process->GetThreadList();
4337 Mutex::Locker locker (threads.GetMutex());
4338 size_t num_threads = threads.GetSize();
4339 for (size_t i=0; i<num_threads; ++i)
4341 ThreadSP thread_sp = threads.GetThreadAtIndex(i);
4342 char menu_char = '\0';
4344 menu_char = '1' + i;
4345 StreamString thread_menu_title;
4346 thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID());
4347 const char *thread_name = thread_sp->GetName();
4348 if (thread_name && thread_name[0])
4349 thread_menu_title.Printf (" %s", thread_name);
4352 const char *queue_name = thread_sp->GetQueueName();
4353 if (queue_name && queue_name[0])
4354 thread_menu_title.Printf (" %s", queue_name);
4356 menu.AddSubmenu (MenuSP (new Menu(thread_menu_title.GetString().c_str(), NULL, menu_char, thread_sp->GetID())));
4359 else if (submenus.size() > 7)
4361 // Remove the separator and any other thread submenu items
4362 // that were previously added
4363 submenus.erase (submenus.begin() + 7, submenus.end());
4365 // Since we are adding and removing items we need to recalculate the name lengths
4366 menu.RecalculateNameLengths();
4368 return MenuActionResult::Handled;
4370 case eMenuID_ViewVariables:
4372 WindowSP main_window_sp = m_app.GetMainWindow();
4373 WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
4374 WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
4375 WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
4376 const Rect source_bounds = source_window_sp->GetBounds();
4378 if (variables_window_sp)
4380 const Rect variables_bounds = variables_window_sp->GetBounds();
4382 main_window_sp->RemoveSubWindow(variables_window_sp.get());
4384 if (registers_window_sp)
4386 // We have a registers window, so give all the area back to the registers window
4387 Rect registers_bounds = variables_bounds;
4388 registers_bounds.size.width = source_bounds.size.width;
4389 registers_window_sp->SetBounds(registers_bounds);
4393 // We have no registers window showing so give the bottom
4394 // area back to the source view
4395 source_window_sp->Resize (source_bounds.size.width,
4396 source_bounds.size.height + variables_bounds.size.height);
4401 Rect new_variables_rect;
4402 if (registers_window_sp)
4404 // We have a registers window so split the area of the registers
4405 // window into two columns where the left hand side will be the
4406 // variables and the right hand side will be the registers
4407 const Rect variables_bounds = registers_window_sp->GetBounds();
4408 Rect new_registers_rect;
4409 variables_bounds.VerticalSplitPercentage (0.50, new_variables_rect, new_registers_rect);
4410 registers_window_sp->SetBounds (new_registers_rect);
4414 // No variables window, grab the bottom part of the source window
4415 Rect new_source_rect;
4416 source_bounds.HorizontalSplitPercentage (0.70, new_source_rect, new_variables_rect);
4417 source_window_sp->SetBounds (new_source_rect);
4419 WindowSP new_window_sp = main_window_sp->CreateSubWindow ("Variables",
4422 new_window_sp->SetDelegate (WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
4426 return MenuActionResult::Handled;
4428 case eMenuID_ViewRegisters:
4430 WindowSP main_window_sp = m_app.GetMainWindow();
4431 WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
4432 WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
4433 WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
4434 const Rect source_bounds = source_window_sp->GetBounds();
4436 if (registers_window_sp)
4438 if (variables_window_sp)
4440 const Rect variables_bounds = variables_window_sp->GetBounds();
4442 // We have a variables window, so give all the area back to the variables window
4443 variables_window_sp->Resize (variables_bounds.size.width + registers_window_sp->GetWidth(),
4444 variables_bounds.size.height);
4448 // We have no variables window showing so give the bottom
4449 // area back to the source view
4450 source_window_sp->Resize (source_bounds.size.width,
4451 source_bounds.size.height + registers_window_sp->GetHeight());
4453 main_window_sp->RemoveSubWindow(registers_window_sp.get());
4458 if (variables_window_sp)
4460 // We have a variables window, split it into two columns
4461 // where the left hand side will be the variables and the
4462 // right hand side will be the registers
4463 const Rect variables_bounds = variables_window_sp->GetBounds();
4465 variables_bounds.VerticalSplitPercentage (0.50, new_vars_rect, new_regs_rect);
4466 variables_window_sp->SetBounds (new_vars_rect);
4470 // No registers window, grab the bottom part of the source window
4471 Rect new_source_rect;
4472 source_bounds.HorizontalSplitPercentage (0.70, new_source_rect, new_regs_rect);
4473 source_window_sp->SetBounds (new_source_rect);
4475 WindowSP new_window_sp = main_window_sp->CreateSubWindow ("Registers",
4478 new_window_sp->SetDelegate (WindowDelegateSP(new RegistersWindowDelegate(m_debugger)));
4482 return MenuActionResult::Handled;
4484 case eMenuID_HelpGUIHelp:
4485 m_app.GetMainWindow ()->CreateHelpSubwindow();
4486 return MenuActionResult::Handled;
4492 return MenuActionResult::NotHandled;
4496 Debugger &m_debugger;
4500 class StatusBarWindowDelegate : public WindowDelegate
4503 StatusBarWindowDelegate (Debugger &debugger) :
4504 m_debugger (debugger)
4509 ~StatusBarWindowDelegate ()
4513 WindowDelegateDraw (Window &window, bool force)
4515 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4516 Process *process = exe_ctx.GetProcessPtr();
4517 Thread *thread = exe_ctx.GetThreadPtr();
4518 StackFrame *frame = exe_ctx.GetFramePtr();
4520 window.SetBackground(2);
4521 window.MoveCursor (0, 0);
4524 const StateType state = process->GetState();
4525 window.Printf ("Process: %5" PRIu64 " %10s", process->GetID(), StateAsCString(state));
4527 if (StateIsStoppedState(state, true))
4530 const char *format = "Thread: ${thread.id%tid}";
4531 if (thread && Debugger::FormatPrompt (format, NULL, &exe_ctx, NULL, strm))
4533 window.MoveCursor (40, 0);
4534 window.PutCStringTruncated(strm.GetString().c_str(), 1);
4537 window.MoveCursor (60, 0);
4539 window.Printf ("Frame: %3u PC = 0x%16.16" PRIx64, frame->GetFrameIndex(), frame->GetFrameCodeAddress().GetOpcodeLoadAddress (exe_ctx.GetTargetPtr()));
4541 else if (state == eStateExited)
4543 const char *exit_desc = process->GetExitDescription();
4544 const int exit_status = process->GetExitStatus();
4545 if (exit_desc && exit_desc[0])
4546 window.Printf (" with status = %i (%s)", exit_status, exit_desc);
4548 window.Printf (" with status = %i", exit_status);
4551 window.DeferredRefresh();
4556 Debugger &m_debugger;
4559 class SourceFileWindowDelegate : public WindowDelegate
4562 SourceFileWindowDelegate (Debugger &debugger) :
4564 m_debugger (debugger),
4567 m_disassembly_scope (NULL),
4568 m_disassembly_sp (),
4569 m_disassembly_range (),
4572 m_selected_line (0),
4575 m_frame_idx (UINT32_MAX),
4576 m_first_visible_line (0),
4585 ~SourceFileWindowDelegate()
4590 Update (const SymbolContext &sc)
4596 NumVisibleLines () const
4598 return m_max_y - m_min_y;
4601 virtual const char *
4602 WindowDelegateGetHelpText ()
4604 return "Source/Disassembly window keyboard shortcuts:";
4608 WindowDelegateGetKeyHelp ()
4610 static curses::KeyHelp g_source_view_key_help[] = {
4611 { KEY_RETURN, "Run to selected line with one shot breakpoint" },
4612 { KEY_UP, "Select previous source line" },
4613 { KEY_DOWN, "Select next source line" },
4614 { KEY_PPAGE, "Page up" },
4615 { KEY_NPAGE, "Page down" },
4616 { 'b', "Set breakpoint on selected source/disassembly line" },
4617 { 'c', "Continue process" },
4618 { 'd', "Detach and resume process" },
4619 { 'D', "Detach with process suspended" },
4620 { 'h', "Show help dialog" },
4621 { 'k', "Kill process" },
4622 { 'n', "Step over (source line)" },
4623 { 'N', "Step over (single instruction)" },
4624 { 'o', "Step out" },
4625 { 's', "Step in (source line)" },
4626 { 'S', "Step in (single instruction)" },
4628 { '.', "Page down" },
4631 return g_source_view_key_help;
4635 WindowDelegateDraw (Window &window, bool force)
4637 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4638 Process *process = exe_ctx.GetProcessPtr();
4639 Thread *thread = NULL;
4641 bool update_location = false;
4644 StateType state = process->GetState();
4645 if (StateIsStoppedState(state, true))
4647 // We are stopped, so it is ok to
4648 update_location = true;
4654 m_max_x = window.GetMaxX()-1;
4655 m_max_y = window.GetMaxY()-1;
4657 const uint32_t num_visible_lines = NumVisibleLines();
4658 StackFrameSP frame_sp;
4659 bool set_selected_line_to_pc = false;
4661 if (update_location)
4663 const bool process_alive = process ? process->IsAlive() : false;
4664 bool thread_changed = false;
4667 thread = exe_ctx.GetThreadPtr();
4670 frame_sp = thread->GetSelectedFrame();
4671 auto tid = thread->GetID();
4672 thread_changed = tid != m_tid;
4677 if (m_tid != LLDB_INVALID_THREAD_ID)
4679 thread_changed = true;
4680 m_tid = LLDB_INVALID_THREAD_ID;
4684 const uint32_t stop_id = process ? process->GetStopID() : 0;
4685 const bool stop_id_changed = stop_id != m_stop_id;
4686 bool frame_changed = false;
4687 m_stop_id = stop_id;
4691 m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
4694 m_title.Printf("%s", m_sc.module_sp->GetFileSpec().GetFilename().GetCString());
4695 ConstString func_name = m_sc.GetFunctionName();
4697 m_title.Printf("`%s", func_name.GetCString());
4699 const uint32_t frame_idx = frame_sp->GetFrameIndex();
4700 frame_changed = frame_idx != m_frame_idx;
4701 m_frame_idx = frame_idx;
4706 frame_changed = m_frame_idx != UINT32_MAX;
4707 m_frame_idx = UINT32_MAX;
4710 const bool context_changed = thread_changed || frame_changed || stop_id_changed;
4714 if (m_sc.line_entry.IsValid())
4716 m_pc_line = m_sc.line_entry.line;
4717 if (m_pc_line != UINT32_MAX)
4718 --m_pc_line; // Convert to zero based line number...
4719 // Update the selected line if the stop ID changed...
4720 if (context_changed)
4721 m_selected_line = m_pc_line;
4723 if (m_file_sp && m_file_sp->FileSpecMatches(m_sc.line_entry.file))
4725 // Same file, nothing to do, we should either have the
4726 // lines or not (source file missing)
4727 if (m_selected_line >= static_cast<size_t>(m_first_visible_line))
4729 if (m_selected_line >= m_first_visible_line + num_visible_lines)
4730 m_first_visible_line = m_selected_line - 10;
4734 if (m_selected_line > 10)
4735 m_first_visible_line = m_selected_line - 10;
4737 m_first_visible_line = 0;
4742 // File changed, set selected line to the line with the PC
4743 m_selected_line = m_pc_line;
4744 m_file_sp = m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file);
4747 const size_t num_lines = m_file_sp->GetNumLines();
4748 int m_line_width = 1;
4749 for (size_t n = num_lines; n >= 10; n = n / 10)
4752 snprintf (m_line_format, sizeof(m_line_format), " %%%iu ", m_line_width);
4753 if (num_lines < num_visible_lines || m_selected_line < num_visible_lines)
4754 m_first_visible_line = 0;
4756 m_first_visible_line = m_selected_line - 10;
4765 if (!m_file_sp || m_file_sp->GetNumLines() == 0)
4768 bool prefer_file_cache = false;
4771 if (m_disassembly_scope != m_sc.function)
4773 m_disassembly_scope = m_sc.function;
4774 m_disassembly_sp = m_sc.function->GetInstructions (exe_ctx, NULL, prefer_file_cache);
4775 if (m_disassembly_sp)
4777 set_selected_line_to_pc = true;
4778 m_disassembly_range = m_sc.function->GetAddressRange();
4782 m_disassembly_range.Clear();
4787 set_selected_line_to_pc = context_changed;
4790 else if (m_sc.symbol)
4792 if (m_disassembly_scope != m_sc.symbol)
4794 m_disassembly_scope = m_sc.symbol;
4795 m_disassembly_sp = m_sc.symbol->GetInstructions (exe_ctx, NULL, prefer_file_cache);
4796 if (m_disassembly_sp)
4798 set_selected_line_to_pc = true;
4799 m_disassembly_range.GetBaseAddress() = m_sc.symbol->GetAddress();
4800 m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize());
4804 m_disassembly_range.Clear();
4809 set_selected_line_to_pc = context_changed;
4816 m_pc_line = UINT32_MAX;
4820 const int window_width = window.GetWidth();
4822 window.DrawTitleBox ("Sources");
4823 if (!m_title.GetString().empty())
4825 window.AttributeOn(A_REVERSE);
4826 window.MoveCursor(1, 1);
4827 window.PutChar(' ');
4828 window.PutCStringTruncated(m_title.GetString().c_str(), 1);
4829 int x = window.GetCursorX();
4830 if (x < window_width - 1)
4832 window.Printf ("%*s", window_width - x - 1, "");
4834 window.AttributeOff(A_REVERSE);
4837 Target *target = exe_ctx.GetTargetPtr();
4838 const size_t num_source_lines = GetNumSourceLines();
4839 if (num_source_lines > 0)
4842 BreakpointLines bp_lines;
4845 BreakpointList &bp_list = target->GetBreakpointList();
4846 const size_t num_bps = bp_list.GetSize();
4847 for (size_t bp_idx=0; bp_idx<num_bps; ++bp_idx)
4849 BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
4850 const size_t num_bps_locs = bp_sp->GetNumLocations();
4851 for (size_t bp_loc_idx=0; bp_loc_idx<num_bps_locs; ++bp_loc_idx)
4853 BreakpointLocationSP bp_loc_sp = bp_sp->GetLocationAtIndex(bp_loc_idx);
4854 LineEntry bp_loc_line_entry;
4855 if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry (bp_loc_line_entry))
4857 if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file)
4859 bp_lines.insert(bp_loc_line_entry.line);
4866 const attr_t selected_highlight_attr = A_REVERSE;
4867 const attr_t pc_highlight_attr = COLOR_PAIR(1);
4869 for (size_t i=0; i<num_visible_lines; ++i)
4871 const uint32_t curr_line = m_first_visible_line + i;
4872 if (curr_line < num_source_lines)
4874 const int line_y = m_min_y+i;
4875 window.MoveCursor(1, line_y);
4876 const bool is_pc_line = curr_line == m_pc_line;
4877 const bool line_is_selected = m_selected_line == curr_line;
4878 // Highlight the line as the PC line first, then if the selected line
4879 // isn't the same as the PC line, highlight it differently
4880 attr_t highlight_attr = 0;
4883 highlight_attr = pc_highlight_attr;
4884 else if (line_is_selected)
4885 highlight_attr = selected_highlight_attr;
4887 if (bp_lines.find(curr_line+1) != bp_lines.end())
4888 bp_attr = COLOR_PAIR(2);
4891 window.AttributeOn(bp_attr);
4893 window.Printf (m_line_format, curr_line + 1);
4896 window.AttributeOff(bp_attr);
4898 window.PutChar(ACS_VLINE);
4899 // Mark the line with the PC with a diamond
4901 window.PutChar(ACS_DIAMOND);
4903 window.PutChar(' ');
4906 window.AttributeOn(highlight_attr);
4907 const uint32_t line_len = m_file_sp->GetLineLength(curr_line + 1, false);
4909 window.PutCString(m_file_sp->PeekLineData(curr_line + 1), line_len);
4911 if (is_pc_line && frame_sp && frame_sp->GetConcreteFrameIndex() == 0)
4913 StopInfoSP stop_info_sp;
4915 stop_info_sp = thread->GetStopInfo();
4918 const char *stop_description = stop_info_sp->GetDescription();
4919 if (stop_description && stop_description[0])
4921 size_t stop_description_len = strlen(stop_description);
4922 int desc_x = window_width - stop_description_len - 16;
4923 window.Printf ("%*s", desc_x - window.GetCursorX(), "");
4924 //window.MoveCursor(window_width - stop_description_len - 15, line_y);
4925 window.Printf ("<<< Thread %u: %s ", thread->GetIndexID(), stop_description);
4930 window.Printf ("%*s", window_width - window.GetCursorX() - 1, "");
4934 window.AttributeOff(highlight_attr);
4945 size_t num_disassembly_lines = GetNumDisassemblyLines();
4946 if (num_disassembly_lines > 0)
4948 // Display disassembly
4949 BreakpointAddrs bp_file_addrs;
4950 Target *target = exe_ctx.GetTargetPtr();
4953 BreakpointList &bp_list = target->GetBreakpointList();
4954 const size_t num_bps = bp_list.GetSize();
4955 for (size_t bp_idx=0; bp_idx<num_bps; ++bp_idx)
4957 BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
4958 const size_t num_bps_locs = bp_sp->GetNumLocations();
4959 for (size_t bp_loc_idx=0; bp_loc_idx<num_bps_locs; ++bp_loc_idx)
4961 BreakpointLocationSP bp_loc_sp = bp_sp->GetLocationAtIndex(bp_loc_idx);
4962 LineEntry bp_loc_line_entry;
4963 const lldb::addr_t file_addr = bp_loc_sp->GetAddress().GetFileAddress();
4964 if (file_addr != LLDB_INVALID_ADDRESS)
4966 if (m_disassembly_range.ContainsFileAddress(file_addr))
4967 bp_file_addrs.insert(file_addr);
4973 const attr_t selected_highlight_attr = A_REVERSE;
4974 const attr_t pc_highlight_attr = COLOR_PAIR(1);
4978 InstructionList &insts = m_disassembly_sp->GetInstructionList();
4982 pc_address = frame_sp->GetFrameCodeAddress();
4983 const uint32_t pc_idx = pc_address.IsValid() ? insts.GetIndexOfInstructionAtAddress (pc_address) : UINT32_MAX;
4984 if (set_selected_line_to_pc)
4986 m_selected_line = pc_idx;
4989 const uint32_t non_visible_pc_offset = (num_visible_lines / 5);
4990 if (static_cast<size_t>(m_first_visible_line) >= num_disassembly_lines)
4991 m_first_visible_line = 0;
4993 if (pc_idx < num_disassembly_lines)
4995 if (pc_idx < static_cast<uint32_t>(m_first_visible_line) ||
4996 pc_idx >= m_first_visible_line + num_visible_lines)
4997 m_first_visible_line = pc_idx - non_visible_pc_offset;
5000 for (size_t i=0; i<num_visible_lines; ++i)
5002 const uint32_t inst_idx = m_first_visible_line + i;
5003 Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get();
5007 const int line_y = m_min_y+i;
5008 window.MoveCursor(1, line_y);
5009 const bool is_pc_line = frame_sp && inst_idx == pc_idx;
5010 const bool line_is_selected = m_selected_line == inst_idx;
5011 // Highlight the line as the PC line first, then if the selected line
5012 // isn't the same as the PC line, highlight it differently
5013 attr_t highlight_attr = 0;
5016 highlight_attr = pc_highlight_attr;
5017 else if (line_is_selected)
5018 highlight_attr = selected_highlight_attr;
5020 if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) != bp_file_addrs.end())
5021 bp_attr = COLOR_PAIR(2);
5024 window.AttributeOn(bp_attr);
5026 window.Printf (" 0x%16.16llx ",
5027 static_cast<unsigned long long>(inst->GetAddress().GetLoadAddress(target)));
5030 window.AttributeOff(bp_attr);
5032 window.PutChar(ACS_VLINE);
5033 // Mark the line with the PC with a diamond
5035 window.PutChar(ACS_DIAMOND);
5037 window.PutChar(' ');
5040 window.AttributeOn(highlight_attr);
5042 const char *mnemonic = inst->GetMnemonic(&exe_ctx);
5043 const char *operands = inst->GetOperands(&exe_ctx);
5044 const char *comment = inst->GetComment(&exe_ctx);
5046 if (mnemonic && mnemonic[0] == '\0')
5048 if (operands && operands[0] == '\0')
5050 if (comment && comment[0] == '\0')
5055 if (mnemonic && operands && comment)
5056 strm.Printf ("%-8s %-25s ; %s", mnemonic, operands, comment);
5057 else if (mnemonic && operands)
5058 strm.Printf ("%-8s %s", mnemonic, operands);
5060 strm.Printf ("%s", mnemonic);
5063 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
5065 if (is_pc_line && frame_sp && frame_sp->GetConcreteFrameIndex() == 0)
5067 StopInfoSP stop_info_sp;
5069 stop_info_sp = thread->GetStopInfo();
5072 const char *stop_description = stop_info_sp->GetDescription();
5073 if (stop_description && stop_description[0])
5075 size_t stop_description_len = strlen(stop_description);
5076 int desc_x = window_width - stop_description_len - 16;
5077 window.Printf ("%*s", desc_x - window.GetCursorX(), "");
5078 //window.MoveCursor(window_width - stop_description_len - 15, line_y);
5079 window.Printf ("<<< Thread %u: %s ", thread->GetIndexID(), stop_description);
5084 window.Printf ("%*s", window_width - window.GetCursorX() - 1, "");
5088 window.AttributeOff(highlight_attr);
5092 window.DeferredRefresh();
5093 return true; // Drawing handled
5099 size_t num_lines = GetNumSourceLines();
5101 num_lines = GetNumDisassemblyLines();
5106 GetNumSourceLines () const
5109 return m_file_sp->GetNumLines();
5113 GetNumDisassemblyLines () const
5115 if (m_disassembly_sp)
5116 return m_disassembly_sp->GetInstructionList().GetSize();
5120 virtual HandleCharResult
5121 WindowDelegateHandleChar (Window &window, int c)
5123 const uint32_t num_visible_lines = NumVisibleLines();
5124 const size_t num_lines = GetNumLines ();
5131 if (static_cast<uint32_t>(m_first_visible_line) > num_visible_lines)
5132 m_first_visible_line -= num_visible_lines;
5134 m_first_visible_line = 0;
5135 m_selected_line = m_first_visible_line;
5142 if (m_first_visible_line + num_visible_lines < num_lines)
5143 m_first_visible_line += num_visible_lines;
5144 else if (num_lines < num_visible_lines)
5145 m_first_visible_line = 0;
5147 m_first_visible_line = num_lines - num_visible_lines;
5148 m_selected_line = m_first_visible_line;
5153 if (m_selected_line > 0)
5156 if (static_cast<size_t>(m_first_visible_line) > m_selected_line)
5157 m_first_visible_line = m_selected_line;
5162 if (m_selected_line + 1 < num_lines)
5165 if (m_first_visible_line + num_visible_lines < m_selected_line)
5166 m_first_visible_line++;
5173 // Set a breakpoint and run to the line using a one shot breakpoint
5174 if (GetNumSourceLines() > 0)
5176 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5177 if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive())
5179 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (NULL, // Don't limit the breakpoint to certain modules
5180 m_file_sp->GetFileSpec(), // Source file
5181 m_selected_line + 1, // Source line number (m_selected_line is zero based)
5182 eLazyBoolCalculate, // Check inlines using global setting
5183 eLazyBoolCalculate, // Skip prologue using global setting,
5185 false); // request_hardware
5186 // Make breakpoint one shot
5187 bp_sp->GetOptions()->SetOneShot(true);
5188 exe_ctx.GetProcessRef().Resume();
5191 else if (m_selected_line < GetNumDisassemblyLines())
5193 const Instruction *inst = m_disassembly_sp->GetInstructionList().GetInstructionAtIndex(m_selected_line).get();
5194 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5195 if (exe_ctx.HasTargetScope())
5197 Address addr = inst->GetAddress();
5198 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (addr, // lldb_private::Address
5200 false); // request_hardware
5201 // Make breakpoint one shot
5202 bp_sp->GetOptions()->SetOneShot(true);
5203 exe_ctx.GetProcessRef().Resume();
5208 case 'b': // 'b' == toggle breakpoint on currently selected line
5209 if (m_selected_line < GetNumSourceLines())
5211 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5212 if (exe_ctx.HasTargetScope())
5214 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (NULL, // Don't limit the breakpoint to certain modules
5215 m_file_sp->GetFileSpec(), // Source file
5216 m_selected_line + 1, // Source line number (m_selected_line is zero based)
5217 eLazyBoolCalculate, // Check inlines using global setting
5218 eLazyBoolCalculate, // Skip prologue using global setting,
5220 false); // request_hardware
5223 else if (m_selected_line < GetNumDisassemblyLines())
5225 const Instruction *inst = m_disassembly_sp->GetInstructionList().GetInstructionAtIndex(m_selected_line).get();
5226 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5227 if (exe_ctx.HasTargetScope())
5229 Address addr = inst->GetAddress();
5230 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (addr, // lldb_private::Address
5232 false); // request_hardware
5237 case 'd': // 'd' == detach and let run
5238 case 'D': // 'D' == detach and keep stopped
5240 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5241 if (exe_ctx.HasProcessScope())
5242 exe_ctx.GetProcessRef().Detach(c == 'D');
5249 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5250 if (exe_ctx.HasProcessScope())
5251 exe_ctx.GetProcessRef().Destroy();
5258 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5259 if (exe_ctx.HasProcessScope())
5260 exe_ctx.GetProcessRef().Resume();
5267 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5268 if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true))
5270 exe_ctx.GetThreadRef().StepOut();
5274 case 'n': // 'n' == step over
5275 case 'N': // 'N' == step over instruction
5277 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5278 if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true))
5280 bool source_step = (c == 'n');
5281 exe_ctx.GetThreadRef().StepOver(source_step);
5285 case 's': // 's' == step into
5286 case 'S': // 'S' == step into instruction
5288 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5289 if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true))
5291 bool source_step = (c == 's');
5292 exe_ctx.GetThreadRef().StepIn(source_step);
5298 window.CreateHelpSubwindow ();
5304 return eKeyNotHandled;
5308 typedef std::set<uint32_t> BreakpointLines;
5309 typedef std::set<lldb::addr_t> BreakpointAddrs;
5311 Debugger &m_debugger;
5313 SourceManager::FileSP m_file_sp;
5314 SymbolContextScope *m_disassembly_scope;
5315 lldb::DisassemblerSP m_disassembly_sp;
5316 AddressRange m_disassembly_range;
5317 StreamString m_title;
5318 lldb::user_id_t m_tid;
5319 char m_line_format[8];
5321 uint32_t m_selected_line; // The selected line
5322 uint32_t m_pc_line; // The line with the PC
5324 uint32_t m_frame_idx;
5325 int m_first_visible_line;
5333 DisplayOptions ValueObjectListDelegate::g_options = { true };
5335 IOHandlerCursesGUI::IOHandlerCursesGUI (Debugger &debugger) :
5336 IOHandler (debugger)
5341 IOHandlerCursesGUI::Activate ()
5343 IOHandler::Activate();
5346 m_app_ap.reset (new Application (GetInputFILE(), GetOutputFILE()));
5349 // This is both a window and a menu delegate
5350 std::shared_ptr<ApplicationDelegate> app_delegate_sp(new ApplicationDelegate(*m_app_ap, m_debugger));
5352 MenuDelegateSP app_menu_delegate_sp = std::static_pointer_cast<MenuDelegate>(app_delegate_sp);
5353 MenuSP lldb_menu_sp(new Menu("LLDB" , "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB));
5354 MenuSP exit_menuitem_sp(new Menu("Exit", NULL, 'x', ApplicationDelegate::eMenuID_LLDBExit));
5355 exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit);
5356 lldb_menu_sp->AddSubmenu (MenuSP (new Menu("About LLDB", NULL, 'a', ApplicationDelegate::eMenuID_LLDBAbout)));
5357 lldb_menu_sp->AddSubmenu (MenuSP (new Menu(Menu::Type::Separator)));
5358 lldb_menu_sp->AddSubmenu (exit_menuitem_sp);
5360 MenuSP target_menu_sp(new Menu("Target" ,"F2", KEY_F(2), ApplicationDelegate::eMenuID_Target));
5361 target_menu_sp->AddSubmenu (MenuSP (new Menu("Create", NULL, 'c', ApplicationDelegate::eMenuID_TargetCreate)));
5362 target_menu_sp->AddSubmenu (MenuSP (new Menu("Delete", NULL, 'd', ApplicationDelegate::eMenuID_TargetDelete)));
5364 MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3), ApplicationDelegate::eMenuID_Process));
5365 process_menu_sp->AddSubmenu (MenuSP (new Menu("Attach" , NULL, 'a', ApplicationDelegate::eMenuID_ProcessAttach)));
5366 process_menu_sp->AddSubmenu (MenuSP (new Menu("Detach" , NULL, 'd', ApplicationDelegate::eMenuID_ProcessDetach)));
5367 process_menu_sp->AddSubmenu (MenuSP (new Menu("Launch" , NULL, 'l', ApplicationDelegate::eMenuID_ProcessLaunch)));
5368 process_menu_sp->AddSubmenu (MenuSP (new Menu(Menu::Type::Separator)));
5369 process_menu_sp->AddSubmenu (MenuSP (new Menu("Continue", NULL, 'c', ApplicationDelegate::eMenuID_ProcessContinue)));
5370 process_menu_sp->AddSubmenu (MenuSP (new Menu("Halt" , NULL, 'h', ApplicationDelegate::eMenuID_ProcessHalt)));
5371 process_menu_sp->AddSubmenu (MenuSP (new Menu("Kill" , NULL, 'k', ApplicationDelegate::eMenuID_ProcessKill)));
5373 MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4), ApplicationDelegate::eMenuID_Thread));
5374 thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step In" , NULL, 'i', ApplicationDelegate::eMenuID_ThreadStepIn)));
5375 thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step Over", NULL, 'v', ApplicationDelegate::eMenuID_ThreadStepOver)));
5376 thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step Out" , NULL, 'o', ApplicationDelegate::eMenuID_ThreadStepOut)));
5378 MenuSP view_menu_sp(new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View));
5379 view_menu_sp->AddSubmenu (MenuSP (new Menu("Backtrace", NULL, 'b', ApplicationDelegate::eMenuID_ViewBacktrace)));
5380 view_menu_sp->AddSubmenu (MenuSP (new Menu("Registers", NULL, 'r', ApplicationDelegate::eMenuID_ViewRegisters)));
5381 view_menu_sp->AddSubmenu (MenuSP (new Menu("Source" , NULL, 's', ApplicationDelegate::eMenuID_ViewSource)));
5382 view_menu_sp->AddSubmenu (MenuSP (new Menu("Variables", NULL, 'v', ApplicationDelegate::eMenuID_ViewVariables)));
5384 MenuSP help_menu_sp(new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help));
5385 help_menu_sp->AddSubmenu (MenuSP (new Menu("GUI Help", NULL, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp)));
5387 m_app_ap->Initialize();
5388 WindowSP &main_window_sp = m_app_ap->GetMainWindow();
5390 MenuSP menubar_sp(new Menu(Menu::Type::Bar));
5391 menubar_sp->AddSubmenu (lldb_menu_sp);
5392 menubar_sp->AddSubmenu (target_menu_sp);
5393 menubar_sp->AddSubmenu (process_menu_sp);
5394 menubar_sp->AddSubmenu (thread_menu_sp);
5395 menubar_sp->AddSubmenu (view_menu_sp);
5396 menubar_sp->AddSubmenu (help_menu_sp);
5397 menubar_sp->SetDelegate(app_menu_delegate_sp);
5399 Rect content_bounds = main_window_sp->GetFrame();
5400 Rect menubar_bounds = content_bounds.MakeMenuBar();
5401 Rect status_bounds = content_bounds.MakeStatusBar();
5403 Rect variables_bounds;
5404 Rect threads_bounds;
5405 Rect source_variables_bounds;
5406 content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds, threads_bounds);
5407 source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds, variables_bounds);
5409 WindowSP menubar_window_sp = main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false);
5410 // Let the menubar get keys if the active window doesn't handle the
5411 // keys that are typed so it can respond to menubar key presses.
5412 menubar_window_sp->SetCanBeActive(false); // Don't let the menubar become the active window
5413 menubar_window_sp->SetDelegate(menubar_sp);
5415 WindowSP source_window_sp (main_window_sp->CreateSubWindow("Source",
5418 WindowSP variables_window_sp (main_window_sp->CreateSubWindow("Variables",
5421 WindowSP threads_window_sp (main_window_sp->CreateSubWindow("Threads",
5424 WindowSP status_window_sp (main_window_sp->CreateSubWindow("Status",
5427 status_window_sp->SetCanBeActive(false); // Don't let the status bar become the active window
5428 main_window_sp->SetDelegate (std::static_pointer_cast<WindowDelegate>(app_delegate_sp));
5429 source_window_sp->SetDelegate (WindowDelegateSP(new SourceFileWindowDelegate(m_debugger)));
5430 variables_window_sp->SetDelegate (WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
5431 TreeDelegateSP thread_delegate_sp (new ThreadsTreeDelegate(m_debugger));
5432 threads_window_sp->SetDelegate (WindowDelegateSP(new TreeWindowDelegate(m_debugger, thread_delegate_sp)));
5433 status_window_sp->SetDelegate (WindowDelegateSP(new StatusBarWindowDelegate(m_debugger)));
5435 // Show the main help window once the first time the curses GUI is launched
5436 static bool g_showed_help = false;
5439 g_showed_help = true;
5440 main_window_sp->CreateHelpSubwindow();
5443 init_pair (1, COLOR_WHITE , COLOR_BLUE );
5444 init_pair (2, COLOR_BLACK , COLOR_WHITE );
5445 init_pair (3, COLOR_MAGENTA , COLOR_WHITE );
5446 init_pair (4, COLOR_MAGENTA , COLOR_BLACK );
5447 init_pair (5, COLOR_RED , COLOR_BLACK );
5453 IOHandlerCursesGUI::Deactivate ()
5455 m_app_ap->Terminate();
5459 IOHandlerCursesGUI::Run ()
5461 m_app_ap->Run(m_debugger);
5466 IOHandlerCursesGUI::~IOHandlerCursesGUI ()
5472 IOHandlerCursesGUI::Hide ()
5478 IOHandlerCursesGUI::Refresh ()
5483 IOHandlerCursesGUI::Cancel ()
5488 IOHandlerCursesGUI::Interrupt ()
5495 IOHandlerCursesGUI::GotEOF()
5499 #endif // #ifndef LLDB_DISABLE_CURSES