blob: 2976beeeb40bd79da443980f3a82e69079ffdb45 [file] [log] [blame]
Greg Clayton44d93782014-01-27 23:43:24 +00001//===-- IOHandler.cpp -------------------------------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10
11#include "lldb/lldb-python.h"
12
13#include <stdio.h> /* ioctl, TIOCGWINSZ */
14#include <sys/ioctl.h> /* ioctl, TIOCGWINSZ */
15
16
17#include <string>
18
19#include "lldb/Breakpoint/BreakpointLocation.h"
20#include "lldb/Core/IOHandler.h"
21#include "lldb/Core/Debugger.h"
22#include "lldb/Core/State.h"
23#include "lldb/Core/StreamFile.h"
24#include "lldb/Core/ValueObjectRegister.h"
25#include "lldb/Host/Editline.h"
26#include "lldb/Interpreter/CommandCompletions.h"
27#include "lldb/Interpreter/CommandInterpreter.h"
28#include "lldb/Symbol/Block.h"
29#include "lldb/Symbol/Function.h"
30#include "lldb/Symbol/Symbol.h"
31#include "lldb/Target/RegisterContext.h"
32#include "lldb/Target/ThreadPlan.h"
33
34#include <ncurses.h>
35#include <panel.h>
36
37using namespace lldb;
38using namespace lldb_private;
39
40IOHandler::IOHandler (Debugger &debugger) :
41 IOHandler (debugger,
42 StreamFileSP(), // Adopt STDIN from top input reader
43 StreamFileSP(), // Adopt STDOUT from top input reader
44 StreamFileSP()) // Adopt STDERR from top input reader
45{
46}
47
48
49IOHandler::IOHandler (Debugger &debugger,
50 const lldb::StreamFileSP &input_sp,
51 const lldb::StreamFileSP &output_sp,
52 const lldb::StreamFileSP &error_sp) :
53 m_debugger (debugger),
54 m_input_sp (input_sp),
55 m_output_sp (output_sp),
56 m_error_sp (error_sp),
57 m_user_data (NULL),
58 m_done (false),
59 m_active (false)
60{
61 // If any files are not specified, then adopt them from the top input reader.
62 if (!m_input_sp || !m_output_sp || !m_error_sp)
63 debugger.AdoptTopIOHandlerFilesIfInvalid (m_input_sp,
64 m_output_sp,
65 m_error_sp);
66}
67
68IOHandler::~IOHandler()
69{
70}
71
72
73int
74IOHandler::GetInputFD()
75{
76 if (m_input_sp)
77 return m_input_sp->GetFile().GetDescriptor();
78 return -1;
79}
80
81int
82IOHandler::GetOutputFD()
83{
84 if (m_output_sp)
85 return m_output_sp->GetFile().GetDescriptor();
86 return -1;
87}
88
89int
90IOHandler::GetErrorFD()
91{
92 if (m_error_sp)
93 return m_error_sp->GetFile().GetDescriptor();
94 return -1;
95}
96
97FILE *
98IOHandler::GetInputFILE()
99{
100 if (m_input_sp)
101 return m_input_sp->GetFile().GetStream();
102 return NULL;
103}
104
105FILE *
106IOHandler::GetOutputFILE()
107{
108 if (m_output_sp)
109 return m_output_sp->GetFile().GetStream();
110 return NULL;
111}
112
113FILE *
114IOHandler::GetErrorFILE()
115{
116 if (m_error_sp)
117 return m_error_sp->GetFile().GetStream();
118 return NULL;
119}
120
121StreamFileSP &
122IOHandler::GetInputStreamFile()
123{
124 return m_input_sp;
125}
126
127StreamFileSP &
128IOHandler::GetOutputStreamFile()
129{
130 return m_output_sp;
131}
132
133
134StreamFileSP &
135IOHandler::GetErrorStreamFile()
136{
137 return m_error_sp;
138}
139
140
141IOHandlerConfirm::IOHandlerConfirm (Debugger &debugger,
142 const char *prompt,
143 bool default_response) :
144 IOHandlerEditline(debugger,
145 NULL, // NULL editline_name means no history loaded/saved
146 NULL,
147 false, // Multi-line
148 *this),
149 m_default_response (default_response),
150 m_user_response (default_response)
151{
152 StreamString prompt_stream;
153 prompt_stream.PutCString(prompt);
154 if (m_default_response)
155 prompt_stream.Printf(": [Y/n] ");
156 else
157 prompt_stream.Printf(": [y/N] ");
158
159 SetPrompt (prompt_stream.GetString().c_str());
160
161}
162
163
164IOHandlerConfirm::~IOHandlerConfirm ()
165{
166}
167
168int
169IOHandlerConfirm::IOHandlerComplete (IOHandler &io_handler,
170 const char *current_line,
171 const char *cursor,
172 const char *last_char,
173 int skip_first_n_matches,
174 int max_matches,
175 StringList &matches)
176{
177 if (current_line == cursor)
178 {
179 if (m_default_response)
180 {
181 matches.AppendString("y");
182 }
183 else
184 {
185 matches.AppendString("n");
186 }
187 }
188 return matches.GetSize();
189}
190
191void
192IOHandlerConfirm::IOHandlerInputComplete (IOHandler &io_handler, std::string &line)
193{
194 if (line.empty())
195 {
196 // User just hit enter, set the response to the default
197 m_user_response = m_default_response;
198 io_handler.SetIsDone(true);
199 return;
200 }
201
202 if (line.size() == 1)
203 {
204 switch (line[0])
205 {
206 case 'y':
207 case 'Y':
208 m_user_response = true;
209 io_handler.SetIsDone(true);
210 return;
211 case 'n':
212 case 'N':
213 m_user_response = false;
214 io_handler.SetIsDone(true);
215 return;
216 default:
217 break;
218 }
219 }
220
221 if (line == "yes" || line == "YES" || line == "Yes")
222 {
223 m_user_response = true;
224 io_handler.SetIsDone(true);
225 }
226 else if (line == "no" || line == "NO" || line == "No")
227 {
228 m_user_response = false;
229 io_handler.SetIsDone(true);
230 }
231}
232
233int
234IOHandlerDelegate::IOHandlerComplete (IOHandler &io_handler,
235 const char *current_line,
236 const char *cursor,
237 const char *last_char,
238 int skip_first_n_matches,
239 int max_matches,
240 StringList &matches)
241{
242 switch (m_completion)
243 {
244 case Completion::None:
245 break;
246
247 case Completion::LLDBCommand:
248 return io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion (current_line,
249 cursor,
250 last_char,
251 skip_first_n_matches,
252 max_matches,
253 matches);
254
255 case Completion::Expression:
256 {
257 bool word_complete = false;
258 const char *word_start = cursor;
259 if (cursor > current_line)
260 --word_start;
261 while (word_start > current_line && !isspace(*word_start))
262 --word_start;
263 CommandCompletions::InvokeCommonCompletionCallbacks (io_handler.GetDebugger().GetCommandInterpreter(),
264 CommandCompletions::eVariablePathCompletion,
265 word_start,
266 skip_first_n_matches,
267 max_matches,
268 NULL,
269 word_complete,
270 matches);
271
272 size_t num_matches = matches.GetSize();
273 if (num_matches > 0)
274 {
275 std::string common_prefix;
276 matches.LongestCommonPrefix (common_prefix);
277 const size_t partial_name_len = strlen(word_start);
278
279 // If we matched a unique single command, add a space...
280 // Only do this if the completer told us this was a complete word, however...
281 if (num_matches == 1 && word_complete)
282 {
283 common_prefix.push_back(' ');
284 }
285 common_prefix.erase (0, partial_name_len);
286 matches.InsertStringAtIndex(0, std::move(common_prefix));
287 }
288 return num_matches;
289 }
290 break;
291 }
292
293
294 return 0;
295}
296
297
298IOHandlerEditline::IOHandlerEditline (Debugger &debugger,
299 const char *editline_name, // Used for saving history files
300 const char *prompt,
301 bool multi_line,
302 IOHandlerDelegate &delegate) :
303 IOHandlerEditline(debugger,
304 StreamFileSP(), // Inherit input from top input reader
305 StreamFileSP(), // Inherit output from top input reader
306 StreamFileSP(), // Inherit error from top input reader
307 editline_name, // Used for saving history files
308 prompt,
309 multi_line,
310 delegate)
311{
312}
313
314IOHandlerEditline::IOHandlerEditline (Debugger &debugger,
315 const lldb::StreamFileSP &input_sp,
316 const lldb::StreamFileSP &output_sp,
317 const lldb::StreamFileSP &error_sp,
318 const char *editline_name, // Used for saving history files
319 const char *prompt,
320 bool multi_line,
321 IOHandlerDelegate &delegate) :
322 IOHandler (debugger, input_sp, output_sp, error_sp),
323 m_editline_ap (),
324 m_delegate (delegate),
325 m_prompt (),
326 m_multi_line (multi_line),
327 m_interactive (false)
328{
329 SetPrompt(prompt);
330
331 const int in_fd = GetInputFD();
332 struct winsize window_size;
333 bool use_editline = false;
334 if (isatty (in_fd))
335 {
336 m_interactive = true;
337 if (::ioctl (in_fd, TIOCGWINSZ, &window_size) == 0)
338 {
339 if (window_size.ws_col > 0)
340 use_editline = true;
341 }
342 }
343
344 if (use_editline)
345 {
346 m_editline_ap.reset(new Editline (editline_name,
347 prompt ? prompt : "",
348 GetInputFILE (),
349 GetOutputFILE (),
350 GetErrorFILE ()));
351 m_editline_ap->SetLineCompleteCallback (LineCompletedCallback, this);
352 m_editline_ap->SetAutoCompleteCallback (AutoCompleteCallback, this);
353 }
354
355}
356
357IOHandlerEditline::~IOHandlerEditline ()
358{
359 m_editline_ap.reset();
360}
361
362
363bool
364IOHandlerEditline::GetLine (std::string &line)
365{
366 if (m_editline_ap)
367 {
368 return m_editline_ap->GetLine(line).Success();
369 }
370 else
371 {
372 line.clear();
373
374 FILE *in = GetInputFILE();
375 if (in)
376 {
377 if (m_interactive)
378 {
379 const char *prompt = GetPrompt();
380 if (prompt && prompt[0])
381 {
382 FILE *out = GetOutputFILE();
383 if (out)
384 {
385 ::fprintf(out, "%s", prompt);
386 ::fflush(out);
387 }
388 }
389 }
390 char buffer[256];
391 bool done = false;
392 while (!done)
393 {
394 if (fgets(buffer, sizeof(buffer), in) == NULL)
395 done = true;
396 else
397 {
398 size_t buffer_len = strlen(buffer);
399 assert (buffer[buffer_len] == '\0');
400 char last_char = buffer[buffer_len-1];
401 if (last_char == '\r' || last_char == '\n')
402 {
403 done = true;
404 // Strip trailing newlines
405 while (last_char == '\r' || last_char == '\n')
406 {
407 --buffer_len;
408 if (buffer_len == 0)
409 break;
410 last_char = buffer[buffer_len-1];
411 }
412 }
413 line.append(buffer, buffer_len);
414 }
415 }
416 }
417 else
418 {
419 // No more input file, we are done...
420 SetIsDone(true);
421 }
422 return !line.empty();
423 }
424}
425
426
427LineStatus
428IOHandlerEditline::LineCompletedCallback (Editline *editline,
429 StringList &lines,
430 uint32_t line_idx,
431 Error &error,
432 void *baton)
433{
434 IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton;
435 return editline_reader->m_delegate.IOHandlerLinesUpdated(*editline_reader, lines, line_idx, error);
436}
437
438int
439IOHandlerEditline::AutoCompleteCallback (const char *current_line,
440 const char *cursor,
441 const char *last_char,
442 int skip_first_n_matches,
443 int max_matches,
444 StringList &matches,
445 void *baton)
446{
447 IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton;
448 if (editline_reader)
449 return editline_reader->m_delegate.IOHandlerComplete (*editline_reader,
450 current_line,
451 cursor,
452 last_char,
453 skip_first_n_matches,
454 max_matches,
455 matches);
456 return 0;
457}
458
459const char *
460IOHandlerEditline::GetPrompt ()
461{
462 if (m_editline_ap)
463 return m_editline_ap->GetPrompt ();
464 else if (m_prompt.empty())
465 return NULL;
466 return m_prompt.c_str();
467}
468
469bool
470IOHandlerEditline::SetPrompt (const char *p)
471{
472 if (p && p[0])
473 m_prompt = p;
474 else
475 m_prompt.clear();
476 if (m_editline_ap)
477 m_editline_ap->SetPrompt (m_prompt.empty() ? NULL : m_prompt.c_str());
478 return true;
479}
480
481bool
482IOHandlerEditline::GetLines (StringList &lines)
483{
484 bool success = false;
485 if (m_editline_ap)
486 {
487 std::string end_token;
488 success = m_editline_ap->GetLines(end_token, lines).Success();
489 }
490 else
491 {
492 LineStatus lines_status = LineStatus::Success;
493
494 while (lines_status == LineStatus::Success)
495 {
496 std::string line;
497 if (GetLine(line))
498 {
499 lines.AppendString(line);
500 Error error;
501 lines_status = m_delegate.IOHandlerLinesUpdated(*this, lines, lines.GetSize() - 1, error);
502 }
503 else
504 {
505 lines_status = LineStatus::Done;
506 }
507 }
508 success = lines.GetSize() > 0;
509 }
510 return success;
511}
512
513// Each IOHandler gets to run until it is done. It should read data
514// from the "in" and place output into "out" and "err and return
515// when done.
516void
517IOHandlerEditline::Run ()
518{
519 std::string line;
520 while (IsActive())
521 {
522 if (m_multi_line)
523 {
524 StringList lines;
525 if (GetLines (lines))
526 {
527 line = lines.CopyList();
528 m_delegate.IOHandlerInputComplete(*this, line);
529 }
530 else
531 {
532 m_done = true;
533 }
534 }
535 else
536 {
537 if (GetLine(line))
538 {
539 m_delegate.IOHandlerInputComplete(*this, line);
540 }
541 else
542 {
543 m_done = true;
544 }
545 }
546 }
547}
548
549void
550IOHandlerEditline::Hide ()
551{
552 if (m_editline_ap && m_editline_ap->GettingLine())
553 m_editline_ap->Hide();
554}
555
556
557void
558IOHandlerEditline::Refresh ()
559{
560 if (m_editline_ap && m_editline_ap->GettingLine())
561 m_editline_ap->Refresh();
562 else
563 {
564 const char *prompt = GetPrompt();
565 if (prompt && prompt[0])
566 {
567 FILE *out = GetOutputFILE();
568 if (out)
569 {
570 ::fprintf(out, "%s", prompt);
571 ::fflush(out);
572 }
573 }
574 }
575}
576
577void
578IOHandlerEditline::Interrupt ()
579{
580 if (m_editline_ap)
581 m_editline_ap->Interrupt();
582}
583
584void
585IOHandlerEditline::GotEOF()
586{
587 if (m_editline_ap)
588 m_editline_ap->Interrupt();
589}
590
591#include "lldb/Core/ValueObject.h"
592#include "lldb/Symbol/VariableList.h"
593#include "lldb/Target/Target.h"
594#include "lldb/Target/Process.h"
595#include "lldb/Target/Thread.h"
596#include "lldb/Target/StackFrame.h"
597
598#define KEY_RETURN 10
599#define KEY_ESCAPE 27
600
601namespace curses
602{
603 class Menu;
604 class MenuDelegate;
605 class Window;
606 class WindowDelegate;
607 typedef std::shared_ptr<Menu> MenuSP;
608 typedef std::shared_ptr<MenuDelegate> MenuDelegateSP;
609 typedef std::shared_ptr<Window> WindowSP;
610 typedef std::shared_ptr<WindowDelegate> WindowDelegateSP;
611 typedef std::vector<MenuSP> Menus;
612 typedef std::vector<WindowSP> Windows;
613 typedef std::vector<WindowDelegateSP> WindowDelegates;
614
615#if 0
616type summary add -s "x=${var.x}, y=${var.y}" curses::Point
617type summary add -s "w=${var.width}, h=${var.height}" curses::Size
618type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect
619#endif
620 struct Point
621 {
622 int x;
623 int y;
624
625 Point (int _x = 0, int _y = 0) :
626 x(_x),
627 y(_y)
628 {
629 }
630
631 void
632 Clear ()
633 {
634 x = 0;
635 y = 0;
636 }
637
638 Point &
639 operator += (const Point &rhs)
640 {
641 x += rhs.x;
642 y += rhs.y;
643 return *this;
644 }
645
646 void
647 Dump ()
648 {
649 printf ("(x=%i, y=%i)\n", x, y);
650 }
651
652 };
653
654 bool operator == (const Point &lhs, const Point &rhs)
655 {
656 return lhs.x == rhs.x && lhs.y == rhs.y;
657 }
658 bool operator != (const Point &lhs, const Point &rhs)
659 {
660 return lhs.x != rhs.x || lhs.y != rhs.y;
661 }
662
663 struct Size
664 {
665 int width;
666 int height;
667 Size (int w = 0, int h = 0) :
668 width (w),
669 height (h)
670 {
671 }
672
673 void
674 Clear ()
675 {
676 width = 0;
677 height = 0;
678 }
679
680 void
681 Dump ()
682 {
683 printf ("(w=%i, h=%i)\n", width, height);
684 }
685
686 };
687
688 bool operator == (const Size &lhs, const Size &rhs)
689 {
690 return lhs.width == rhs.width && lhs.height == rhs.height;
691 }
692 bool operator != (const Size &lhs, const Size &rhs)
693 {
694 return lhs.width != rhs.width || lhs.height != rhs.height;
695 }
696
697 struct Rect
698 {
699 Point origin;
700 Size size;
701
702 Rect () :
703 origin(),
704 size()
705 {
706 }
707
708 Rect (const Point &p, const Size &s) :
709 origin (p),
710 size (s)
711 {
712 }
713
714 void
715 Clear ()
716 {
717 origin.Clear();
718 size.Clear();
719 }
720
721 void
722 Dump ()
723 {
724 printf ("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width, size.height);
725 }
726
727 void
728 Inset (int w, int h)
729 {
730 if (size.width > w*2)
731 size.width -= w*2;
732 origin.x += w;
733
734 if (size.height > h*2)
735 size.height -= h*2;
736 origin.y += h;
737 }
738 // Return a status bar rectangle which is the last line of
739 // this rectangle. This rectangle will be modified to not
740 // include the status bar area.
741 Rect
742 MakeStatusBar ()
743 {
744 Rect status_bar;
745 if (size.height > 1)
746 {
747 status_bar.origin.x = origin.x;
748 status_bar.origin.y = size.height;
749 status_bar.size.width = size.width;
750 status_bar.size.height = 1;
751 --size.height;
752 }
753 return status_bar;
754 }
755
756 // Return a menubar rectangle which is the first line of
757 // this rectangle. This rectangle will be modified to not
758 // include the menubar area.
759 Rect
760 MakeMenuBar ()
761 {
762 Rect menubar;
763 if (size.height > 1)
764 {
765 menubar.origin.x = origin.x;
766 menubar.origin.y = origin.y;
767 menubar.size.width = size.width;
768 menubar.size.height = 1;
769 ++origin.y;
770 --size.height;
771 }
772 return menubar;
773 }
774
775 void
776 HorizontalSplitPercentage (float top_percentage, Rect &top, Rect &bottom) const
777 {
778 float top_height = top_percentage * size.height;
779 HorizontalSplit (top_height, top, bottom);
780 }
781
782 void
783 HorizontalSplit (int top_height, Rect &top, Rect &bottom) const
784 {
785 top = *this;
786 if (top_height < size.height)
787 {
788 top.size.height = top_height;
789 bottom.origin.x = origin.x;
790 bottom.origin.y = origin.y + top.size.height;
791 bottom.size.width = size.width;
792 bottom.size.height = size.height - top.size.height;
793 }
794 else
795 {
796 bottom.Clear();
797 }
798 }
799
800 void
801 VerticalSplitPercentage (float left_percentage, Rect &left, Rect &right) const
802 {
803 float left_width = left_percentage * size.width;
804 VerticalSplit (left_width, left, right);
805 }
806
807
808 void
809 VerticalSplit (int left_width, Rect &left, Rect &right) const
810 {
811 left = *this;
812 if (left_width < size.width)
813 {
814 left.size.width = left_width;
815 right.origin.x = origin.x + left.size.width;
816 right.origin.y = origin.y;
817 right.size.width = size.width - left.size.width;
818 right.size.height = size.height;
819 }
820 else
821 {
822 right.Clear();
823 }
824 }
825 };
826
827 bool operator == (const Rect &lhs, const Rect &rhs)
828 {
829 return lhs.origin == rhs.origin && lhs.size == rhs.size;
830 }
831 bool operator != (const Rect &lhs, const Rect &rhs)
832 {
833 return lhs.origin != rhs.origin || lhs.size != rhs.size;
834 }
835
836 enum HandleCharResult
837 {
838 eKeyNotHandled = 0,
839 eKeyHandled = 1,
840 eQuitApplication = 2
841 };
842
843 enum class MenuActionResult
844 {
845 Handled,
846 NotHandled,
847 Quit // Exit all menus and quit
848 };
849
850 struct KeyHelp
851 {
852 int ch;
853 const char *description;
854 };
855
856 class WindowDelegate
857 {
858 public:
859 virtual
860 ~WindowDelegate()
861 {
862 }
863
864 virtual bool
865 WindowDelegateDraw (Window &window, bool force)
866 {
867 return false; // Drawing not handled
868 }
869
870 virtual HandleCharResult
871 WindowDelegateHandleChar (Window &window, int key)
872 {
873 return eKeyNotHandled;
874 }
875
876 virtual const char *
877 WindowDelegateGetHelpText ()
878 {
879 return NULL;
880 }
881
882 virtual KeyHelp *
883 WindowDelegateGetKeyHelp ()
884 {
885 return NULL;
886 }
887 };
888
889 class HelpDialogDelegate :
890 public WindowDelegate
891 {
892 public:
893 HelpDialogDelegate (const char *text, KeyHelp *key_help_array);
894
895 virtual
896 ~HelpDialogDelegate();
897
898 virtual bool
899 WindowDelegateDraw (Window &window, bool force);
900
901 virtual HandleCharResult
902 WindowDelegateHandleChar (Window &window, int key);
903
904 size_t
905 GetNumLines() const
906 {
907 return m_text.GetSize();
908 }
909
910 size_t
911 GetMaxLineLength () const
912 {
913 return m_text.GetMaxStringLength();
914 }
915
916 protected:
917 StringList m_text;
918 int m_first_visible_line;
919 };
920
921
922 class Window
923 {
924 public:
925
926 Window (const char *name) :
927 m_name (name),
928 m_window (NULL),
929 m_panel (NULL),
930 m_parent (NULL),
931 m_subwindows (),
932 m_delegate_sp (),
933 m_curr_active_window_idx (UINT32_MAX),
934 m_prev_active_window_idx (UINT32_MAX),
935 m_delete (false),
936 m_needs_update (true),
937 m_can_activate (true),
938 m_is_subwin (false)
939 {
940 }
941
942 Window (const char *name, WINDOW *w, bool del = true) :
943 m_name (name),
944 m_window (NULL),
945 m_panel (NULL),
946 m_parent (NULL),
947 m_subwindows (),
948 m_delegate_sp (),
949 m_curr_active_window_idx (UINT32_MAX),
950 m_prev_active_window_idx (UINT32_MAX),
951 m_delete (del),
952 m_needs_update (true),
953 m_can_activate (true),
954 m_is_subwin (false)
955 {
956 if (w)
957 Reset(w);
958 }
959
960 Window (const char *name, const Rect &bounds) :
961 m_name (name),
962 m_window (NULL),
963 m_parent (NULL),
964 m_subwindows (),
965 m_delegate_sp (),
966 m_curr_active_window_idx (UINT32_MAX),
967 m_prev_active_window_idx (UINT32_MAX),
968 m_delete (true),
969 m_needs_update (true),
970 m_can_activate (true),
971 m_is_subwin (false)
972 {
973 Reset (::newwin (bounds.size.height, bounds.size.width, bounds.origin.y, bounds.origin.y));
974 }
975
976 virtual
977 ~Window ()
978 {
979 RemoveSubWindows ();
980 Reset ();
981 }
982
983 void
984 Reset (WINDOW *w = NULL, bool del = true)
985 {
986 if (m_window == w)
987 return;
988
989 if (m_panel)
990 {
991 ::del_panel (m_panel);
992 m_panel = NULL;
993 }
994 if (m_window && m_delete)
995 {
996 ::delwin (m_window);
997 m_window = NULL;
998 m_delete = false;
999 }
1000 if (w)
1001 {
1002 m_window = w;
1003 m_panel = ::new_panel (m_window);
1004 m_delete = del;
1005 }
1006 }
1007
1008 void AttributeOn (attr_t attr) { ::wattron (m_window, attr); }
1009 void AttributeOff (attr_t attr) { ::wattroff (m_window, attr); }
1010 void Box (chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) { ::box(m_window, v_char, h_char); }
1011 void Clear () { ::wclear (m_window); }
1012 void Erase () { ::werase (m_window); }
1013 Rect GetBounds () { return Rect (GetParentOrigin(), GetSize()); } // Get the rectangle in our parent window
1014 int GetChar () { return ::wgetch (m_window); }
1015 int GetCursorX () { return getcurx (m_window); }
1016 int GetCursorY () { return getcury (m_window); }
1017 Rect GetFrame () { return Rect (Point(), GetSize()); } // Get our rectangle in our own coordinate system
1018 Point GetParentOrigin() { return Point (GetParentX(), GetParentY()); }
1019 Size GetSize() { return Size (GetWidth(), GetHeight()); }
1020 int GetParentX () { return getparx (m_window); }
1021 int GetParentY () { return getpary (m_window); }
1022 int GetMaxX() { return getmaxx (m_window); }
1023 int GetMaxY() { return getmaxy (m_window); }
1024 int GetWidth() { return GetMaxX(); }
1025 int GetHeight() { return GetMaxY(); }
1026 void MoveCursor (int x, int y) { ::wmove (m_window, y, x); }
1027 void MoveWindow (int x, int y) { MoveWindow(Point(x,y)); }
1028 void Resize (int w, int h) { ::wresize(m_window, h, w); }
1029 void Resize (const Size &size) { ::wresize(m_window, size.height, size.width); }
1030 void PutChar (int ch) { ::waddch (m_window, ch); }
1031 void PutCString (const char *s, int len = -1) { ::waddnstr (m_window, s, len); }
1032 void Refresh () { ::wrefresh (m_window); }
1033 void DeferredRefresh ()
1034 {
1035 // We are using panels, so we don't need to call this...
1036 //::wnoutrefresh(m_window);
1037 }
1038 void SetBackground (int color_pair_idx) { ::wbkgd (m_window,COLOR_PAIR(color_pair_idx)); }
1039 void UnderlineOn () { AttributeOn(A_UNDERLINE); }
1040 void UnderlineOff () { AttributeOff(A_UNDERLINE); }
1041
1042 void PutCStringTruncated (const char *s, int right_pad)
1043 {
1044 int bytes_left = GetWidth() - GetCursorX();
1045 if (bytes_left > right_pad)
1046 {
1047 bytes_left -= right_pad;
1048 ::waddnstr (m_window, s, bytes_left);
1049 }
1050 }
1051
1052 void
1053 MoveWindow (const Point &origin)
1054 {
1055 const bool moving_window = origin != GetParentOrigin();
1056 if (m_is_subwin && moving_window)
1057 {
1058 // Can't move subwindows, must delete and re-create
1059 Size size = GetSize();
1060 Reset (::subwin (m_parent->m_window,
1061 size.height,
1062 size.width,
1063 origin.y,
1064 origin.x), true);
1065 }
1066 else
1067 {
1068 ::mvwin (m_window, origin.y, origin.x);
1069 }
1070 }
1071
1072 void
1073 SetBounds (const Rect &bounds)
1074 {
1075 const bool moving_window = bounds.origin != GetParentOrigin();
1076 if (m_is_subwin && moving_window)
1077 {
1078 // Can't move subwindows, must delete and re-create
1079 Reset (::subwin (m_parent->m_window,
1080 bounds.size.height,
1081 bounds.size.width,
1082 bounds.origin.y,
1083 bounds.origin.x), true);
1084 }
1085 else
1086 {
1087 if (moving_window)
1088 MoveWindow(bounds.origin);
1089 Resize (bounds.size);
1090 }
1091 }
1092
1093 void
1094 Printf (const char *format, ...) __attribute__ ((format (printf, 2, 3)))
1095 {
1096 va_list args;
1097 va_start (args, format);
1098 vwprintw(m_window, format, args);
1099 va_end (args);
1100 }
1101
1102 void
1103 Touch ()
1104 {
1105 ::touchwin (m_window);
1106 if (m_parent)
1107 m_parent->Touch();
1108 }
1109
1110 WindowSP
1111 CreateSubWindow (const char *name, const Rect &bounds, bool make_active)
1112 {
1113 WindowSP subwindow_sp;
1114 if (m_window)
1115 {
1116 subwindow_sp.reset(new Window(name, ::subwin (m_window,
1117 bounds.size.height,
1118 bounds.size.width,
1119 bounds.origin.y,
1120 bounds.origin.x), true));
1121 subwindow_sp->m_is_subwin = true;
1122 }
1123 else
1124 {
1125 subwindow_sp.reset(new Window(name, ::newwin (bounds.size.height,
1126 bounds.size.width,
1127 bounds.origin.y,
1128 bounds.origin.x), true));
1129 subwindow_sp->m_is_subwin = false;
1130 }
1131 subwindow_sp->m_parent = this;
1132 if (make_active)
1133 {
1134 m_prev_active_window_idx = m_curr_active_window_idx;
1135 m_curr_active_window_idx = m_subwindows.size();
1136 }
1137 m_subwindows.push_back(subwindow_sp);
1138 ::top_panel (subwindow_sp->m_panel);
1139 m_needs_update = true;
1140 return subwindow_sp;
1141 }
1142
1143 bool
1144 RemoveSubWindow (Window *window)
1145 {
1146 Windows::iterator pos, end = m_subwindows.end();
1147 size_t i = 0;
1148 for (pos = m_subwindows.begin(); pos != end; ++pos, ++i)
1149 {
1150 if ((*pos).get() == window)
1151 {
1152 if (m_prev_active_window_idx == i)
1153 m_prev_active_window_idx = UINT32_MAX;
1154 else if (m_prev_active_window_idx != UINT32_MAX && m_prev_active_window_idx > i)
1155 --m_prev_active_window_idx;
1156
1157 if (m_curr_active_window_idx == i)
1158 m_curr_active_window_idx = UINT32_MAX;
1159 else if (m_curr_active_window_idx != UINT32_MAX && m_curr_active_window_idx > i)
1160 --m_curr_active_window_idx;
1161 window->Erase();
1162 m_subwindows.erase(pos);
1163 m_needs_update = true;
1164 if (m_parent)
1165 m_parent->Touch();
1166 else
1167 ::touchwin (stdscr);
1168 return true;
1169 }
1170 }
1171 return false;
1172 }
1173
1174 WindowSP
1175 FindSubWindow (const char *name)
1176 {
1177 Windows::iterator pos, end = m_subwindows.end();
1178 size_t i = 0;
1179 for (pos = m_subwindows.begin(); pos != end; ++pos, ++i)
1180 {
1181 if ((*pos)->m_name.compare(name) == 0)
1182 return *pos;
1183 }
1184 return WindowSP();
1185 }
1186
1187 void
1188 RemoveSubWindows ()
1189 {
1190 m_curr_active_window_idx = UINT32_MAX;
1191 m_prev_active_window_idx = UINT32_MAX;
1192 for (Windows::iterator pos = m_subwindows.begin();
1193 pos != m_subwindows.end();
1194 pos = m_subwindows.erase(pos))
1195 {
1196 (*pos)->Erase();
1197 }
1198 if (m_parent)
1199 m_parent->Touch();
1200 else
1201 ::touchwin (stdscr);
1202 }
1203
1204 WINDOW *
1205 get()
1206 {
1207 return m_window;
1208 }
1209
1210 operator WINDOW *()
1211 {
1212 return m_window;
1213 }
1214
1215 //----------------------------------------------------------------------
1216 // Window drawing utilities
1217 //----------------------------------------------------------------------
1218 void
1219 DrawTitleBox (const char *title, const char *bottom_message = NULL)
1220 {
1221 attr_t attr = 0;
1222 if (IsActive())
1223 attr = A_BOLD | COLOR_PAIR(2);
1224 else
1225 attr = 0;
1226 if (attr)
1227 AttributeOn(attr);
1228
1229 Box();
1230 MoveCursor(3, 0);
1231
1232 if (title && title[0])
1233 {
1234 PutChar ('<');
1235 PutCString (title);
1236 PutChar ('>');
1237 }
1238
1239 if (bottom_message && bottom_message[0])
1240 {
1241 int bottom_message_length = strlen(bottom_message);
1242 int x = GetWidth() - 3 - (bottom_message_length + 2);
1243
1244 if (x > 0)
1245 {
1246 MoveCursor (x, GetHeight() - 1);
1247 PutChar ('[');
1248 PutCString(bottom_message);
1249 PutChar (']');
1250 }
1251 else
1252 {
1253 MoveCursor (1, GetHeight() - 1);
1254 PutChar ('[');
1255 PutCStringTruncated (bottom_message, 1);
1256 }
1257 }
1258 if (attr)
1259 AttributeOff(attr);
1260
1261 }
1262
1263 virtual void
1264 Draw (bool force)
1265 {
1266 if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw (*this, force))
1267 return;
1268
1269 for (auto &subwindow_sp : m_subwindows)
1270 subwindow_sp->Draw(force);
1271 }
1272
1273 bool
1274 CreateHelpSubwindow ()
1275 {
1276 if (m_delegate_sp)
1277 {
1278 const char *text = m_delegate_sp->WindowDelegateGetHelpText ();
1279 KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp ();
1280 if ((text && text[0]) || key_help)
1281 {
1282 std::auto_ptr<HelpDialogDelegate> help_delegate_ap(new HelpDialogDelegate(text, key_help));
1283 const size_t num_lines = help_delegate_ap->GetNumLines();
1284 const size_t max_length = help_delegate_ap->GetMaxLineLength();
1285 Rect bounds = GetBounds();
1286 bounds.Inset(1, 1);
1287 if (max_length + 4 < bounds.size.width)
1288 {
1289 bounds.origin.x += (bounds.size.width - max_length + 4)/2;
1290 bounds.size.width = max_length + 4;
1291 }
1292 else
1293 {
1294 if (bounds.size.width > 100)
1295 {
1296 const int inset_w = bounds.size.width / 4;
1297 bounds.origin.x += inset_w;
1298 bounds.size.width -= 2*inset_w;
1299 }
1300 }
1301
1302 if (num_lines + 2 < bounds.size.height)
1303 {
1304 bounds.origin.y += (bounds.size.height - num_lines + 2)/2;
1305 bounds.size.height = num_lines + 2;
1306 }
1307 else
1308 {
1309 if (bounds.size.height > 100)
1310 {
1311 const int inset_h = bounds.size.height / 4;
1312 bounds.origin.y += inset_h;
1313 bounds.size.height -= 2*inset_h;
1314 }
1315 }
1316 WindowSP help_window_sp = GetParent()->CreateSubWindow("Help", bounds, true);
1317 help_window_sp->SetDelegate(WindowDelegateSP(help_delegate_ap.release()));
1318 return true;
1319 }
1320 }
1321 return false;
1322 }
1323
1324 virtual HandleCharResult
1325 HandleChar (int key)
1326 {
1327 // Always check the active window first
1328 HandleCharResult result = eKeyNotHandled;
1329 WindowSP active_window_sp = GetActiveWindow ();
1330 if (active_window_sp)
1331 {
1332 result = active_window_sp->HandleChar (key);
1333 if (result != eKeyNotHandled)
1334 return result;
1335 }
1336
1337 if (m_delegate_sp)
1338 {
1339 result = m_delegate_sp->WindowDelegateHandleChar (*this, key);
1340 if (result != eKeyNotHandled)
1341 return result;
1342 }
1343
1344 // Then check for any windows that want any keys
1345 // that weren't handled. This is typically only
1346 // for a menubar.
1347 // Make a copy of the subwindows in case any HandleChar()
1348 // functions muck with the subwindows. If we don't do this,
1349 // we can crash when iterating over the subwindows.
1350 Windows subwindows (m_subwindows);
1351 for (auto subwindow_sp : subwindows)
1352 {
1353 if (subwindow_sp->m_can_activate == false)
1354 {
1355 HandleCharResult result = subwindow_sp->HandleChar(key);
1356 if (result != eKeyNotHandled)
1357 return result;
1358 }
1359 }
1360
1361 return eKeyNotHandled;
1362 }
1363
1364 bool
1365 SetActiveWindow (Window *window)
1366 {
1367 const size_t num_subwindows = m_subwindows.size();
1368 for (size_t i=0; i<num_subwindows; ++i)
1369 {
1370 if (m_subwindows[i].get() == window)
1371 {
1372 m_prev_active_window_idx = m_curr_active_window_idx;
1373 ::top_panel (window->m_panel);
1374 m_curr_active_window_idx = i;
1375 return true;
1376 }
1377 }
1378 return false;
1379 }
1380
1381 WindowSP
1382 GetActiveWindow ()
1383 {
1384 if (!m_subwindows.empty())
1385 {
1386 if (m_curr_active_window_idx >= m_subwindows.size())
1387 {
1388 if (m_prev_active_window_idx < m_subwindows.size())
1389 {
1390 m_curr_active_window_idx = m_prev_active_window_idx;
1391 m_prev_active_window_idx = UINT32_MAX;
1392 }
1393 else if (IsActive())
1394 {
1395 m_prev_active_window_idx = UINT32_MAX;
1396 m_curr_active_window_idx = UINT32_MAX;
1397
1398 // Find first window that wants to be active if this window is active
1399 const size_t num_subwindows = m_subwindows.size();
1400 for (size_t i=0; i<num_subwindows; ++i)
1401 {
1402 if (m_subwindows[i]->GetCanBeActive())
1403 {
1404 m_curr_active_window_idx = i;
1405 break;
1406 }
1407 }
1408 }
1409 }
1410
1411 if (m_curr_active_window_idx < m_subwindows.size())
1412 return m_subwindows[m_curr_active_window_idx];
1413 }
1414 return WindowSP();
1415 }
1416
1417 bool
1418 GetCanBeActive () const
1419 {
1420 return m_can_activate;
1421 }
1422
1423 void
1424 SetCanBeActive (bool b)
1425 {
1426 m_can_activate = b;
1427 }
1428
1429 const WindowDelegateSP &
1430 GetDelegate () const
1431 {
1432 return m_delegate_sp;
1433 }
1434
1435 void
1436 SetDelegate (const WindowDelegateSP &delegate_sp)
1437 {
1438 m_delegate_sp = delegate_sp;
1439 }
1440
1441 Window *
1442 GetParent () const
1443 {
1444 return m_parent;
1445 }
1446
1447 bool
1448 IsActive () const
1449 {
1450 if (m_parent)
1451 return m_parent->GetActiveWindow().get() == this;
1452 else
1453 return true; // Top level window is always active
1454 }
1455
1456 void
1457 SelectNextWindowAsActive ()
1458 {
1459 // Move active focus to next window
1460 const size_t num_subwindows = m_subwindows.size();
1461 if (m_curr_active_window_idx == UINT32_MAX)
1462 {
1463 uint32_t idx = 0;
1464 for (auto subwindow_sp : m_subwindows)
1465 {
1466 if (subwindow_sp->GetCanBeActive())
1467 {
1468 m_curr_active_window_idx = idx;
1469 break;
1470 }
1471 ++idx;
1472 }
1473 }
1474 else if (m_curr_active_window_idx + 1 < num_subwindows)
1475 {
1476 bool handled = false;
1477 m_prev_active_window_idx = m_curr_active_window_idx;
1478 for (size_t idx=m_curr_active_window_idx + 1; idx<num_subwindows; ++idx)
1479 {
1480 if (m_subwindows[idx]->GetCanBeActive())
1481 {
1482 m_curr_active_window_idx = idx;
1483 handled = true;
1484 break;
1485 }
1486 }
1487 if (!handled)
1488 {
1489 for (size_t idx=0; idx<=m_prev_active_window_idx; ++idx)
1490 {
1491 if (m_subwindows[idx]->GetCanBeActive())
1492 {
1493 m_curr_active_window_idx = idx;
1494 break;
1495 }
1496 }
1497 }
1498 }
1499 else
1500 {
1501 m_prev_active_window_idx = m_curr_active_window_idx;
1502 for (size_t idx=0; idx<num_subwindows; ++idx)
1503 {
1504 if (m_subwindows[idx]->GetCanBeActive())
1505 {
1506 m_curr_active_window_idx = idx;
1507 break;
1508 }
1509 }
1510 }
1511 }
1512
1513 const char *
1514 GetName () const
1515 {
1516 return m_name.c_str();
1517 }
1518 protected:
1519 std::string m_name;
1520 WINDOW *m_window;
1521 PANEL *m_panel;
1522 Window *m_parent;
1523 Windows m_subwindows;
1524 WindowDelegateSP m_delegate_sp;
1525 uint32_t m_curr_active_window_idx;
1526 uint32_t m_prev_active_window_idx;
1527 bool m_delete;
1528 bool m_needs_update;
1529 bool m_can_activate;
1530 bool m_is_subwin;
1531
1532 private:
1533 DISALLOW_COPY_AND_ASSIGN(Window);
1534 };
1535
1536 class MenuDelegate
1537 {
1538 public:
1539 virtual ~MenuDelegate() {}
1540
1541 virtual MenuActionResult
1542 MenuDelegateAction (Menu &menu) = 0;
1543 };
1544
1545 class Menu : public WindowDelegate
1546 {
1547 public:
1548 enum class Type
1549 {
1550 Invalid,
1551 Bar,
1552 Item,
1553 Separator
1554 };
1555
1556 // Menubar or separator constructor
1557 Menu (Type type);
1558
1559 // Menuitem constructor
1560 Menu (const char *name,
1561 const char *key_name,
1562 int key_value,
1563 uint64_t identifier);
1564
1565 virtual ~
1566 Menu ()
1567 {
1568 }
1569
1570 const MenuDelegateSP &
1571 GetDelegate () const
1572 {
1573 return m_delegate_sp;
1574 }
1575
1576 void
1577 SetDelegate (const MenuDelegateSP &delegate_sp)
1578 {
1579 m_delegate_sp = delegate_sp;
1580 }
1581
1582 void
1583 RecalculateNameLengths();
1584
1585 void
1586 AddSubmenu (const MenuSP &menu_sp);
1587
1588 int
1589 DrawAndRunMenu (Window &window);
1590
1591 void
1592 DrawMenuTitle (Window &window, bool highlight);
1593
1594 virtual bool
1595 WindowDelegateDraw (Window &window, bool force);
1596
1597 virtual HandleCharResult
1598 WindowDelegateHandleChar (Window &window, int key);
1599
1600 MenuActionResult
1601 ActionPrivate (Menu &menu)
1602 {
1603 MenuActionResult result = MenuActionResult::NotHandled;
1604 if (m_delegate_sp)
1605 {
1606 result = m_delegate_sp->MenuDelegateAction (menu);
1607 if (result != MenuActionResult::NotHandled)
1608 return result;
1609 }
1610 else if (m_parent)
1611 {
1612 result = m_parent->ActionPrivate(menu);
1613 if (result != MenuActionResult::NotHandled)
1614 return result;
1615 }
1616 return m_canned_result;
1617 }
1618
1619 MenuActionResult
1620 Action ()
1621 {
1622 // Call the recursive action so it can try to handle it
1623 // with the menu delegate, and if not, try our parent menu
1624 return ActionPrivate (*this);
1625 }
1626
1627 void
1628 SetCannedResult (MenuActionResult result)
1629 {
1630 m_canned_result = result;
1631 }
1632
1633 Menus &
1634 GetSubmenus()
1635 {
1636 return m_submenus;
1637 }
1638
1639 const Menus &
1640 GetSubmenus() const
1641 {
1642 return m_submenus;
1643 }
1644
1645 int
1646 GetSelectedSubmenuIndex () const
1647 {
1648 return m_selected;
1649 }
1650
1651 void
1652 SetSelectedSubmenuIndex (int idx)
1653 {
1654 m_selected = idx;
1655 }
1656
1657 Type
1658 GetType () const
1659 {
1660 return m_type;
1661 }
1662
1663 int
1664 GetStartingColumn() const
1665 {
1666 return m_start_col;
1667 }
1668
1669 void
1670 SetStartingColumn(int col)
1671 {
1672 m_start_col = col;
1673 }
1674
1675 int
1676 GetKeyValue() const
1677 {
1678 return m_key_value;
1679 }
1680
1681 void
1682 SetKeyValue(int key_value)
1683 {
1684 m_key_value = key_value;
1685 }
1686
1687 std::string &
1688 GetName()
1689 {
1690 return m_name;
1691 }
1692
1693 std::string &
1694 GetKeyName()
1695 {
1696 return m_key_name;
1697 }
1698
1699 int
1700 GetDrawWidth () const
1701 {
1702 return m_max_submenu_name_length + m_max_submenu_key_name_length + 8;
1703 }
1704
1705
1706 uint64_t
1707 GetIdentifier() const
1708 {
1709 return m_identifier;
1710 }
1711
1712 void
1713 SetIdentifier (uint64_t identifier)
1714 {
1715 m_identifier = identifier;
1716 }
1717
1718 protected:
1719 std::string m_name;
1720 std::string m_key_name;
1721 uint64_t m_identifier;
1722 Type m_type;
1723 int m_key_value;
1724 int m_start_col;
1725 int m_max_submenu_name_length;
1726 int m_max_submenu_key_name_length;
1727 int m_selected;
1728 Menu *m_parent;
1729 Menus m_submenus;
1730 WindowSP m_menu_window_sp;
1731 MenuActionResult m_canned_result;
1732 MenuDelegateSP m_delegate_sp;
1733 };
1734
1735 // Menubar or separator constructor
1736 Menu::Menu (Type type) :
1737 m_name (),
1738 m_key_name (),
1739 m_identifier (0),
1740 m_type (type),
1741 m_key_value (0),
1742 m_start_col (0),
1743 m_max_submenu_name_length (0),
1744 m_max_submenu_key_name_length (0),
1745 m_selected (0),
1746 m_parent (NULL),
1747 m_submenus (),
1748 m_canned_result (MenuActionResult::NotHandled),
1749 m_delegate_sp()
1750 {
1751 }
1752
1753 // Menuitem constructor
1754 Menu::Menu (const char *name,
1755 const char *key_name,
1756 int key_value,
1757 uint64_t identifier) :
1758 m_name (),
1759 m_key_name (),
1760 m_identifier (identifier),
1761 m_type (Type::Invalid),
1762 m_key_value (key_value),
1763 m_start_col (0),
1764 m_max_submenu_name_length (0),
1765 m_max_submenu_key_name_length (0),
1766 m_selected (0),
1767 m_parent (NULL),
1768 m_submenus (),
1769 m_canned_result (MenuActionResult::NotHandled),
1770 m_delegate_sp()
1771 {
1772 if (name && name[0])
1773 {
1774 m_name = name;
1775 m_type = Type::Item;
1776 if (key_name && key_name[0])
1777 m_key_name = key_name;
1778 }
1779 else
1780 {
1781 m_type = Type::Separator;
1782 }
1783 }
1784
1785 void
1786 Menu::RecalculateNameLengths()
1787 {
1788 m_max_submenu_name_length = 0;
1789 m_max_submenu_key_name_length = 0;
1790 Menus &submenus = GetSubmenus();
1791 const size_t num_submenus = submenus.size();
1792 for (size_t i=0; i<num_submenus; ++i)
1793 {
1794 Menu *submenu = submenus[i].get();
1795 if (m_max_submenu_name_length < submenu->m_name.size())
1796 m_max_submenu_name_length = submenu->m_name.size();
1797 if (m_max_submenu_key_name_length < submenu->m_key_name.size())
1798 m_max_submenu_key_name_length = submenu->m_key_name.size();
1799 }
1800 }
1801
1802 void
1803 Menu::AddSubmenu (const MenuSP &menu_sp)
1804 {
1805 menu_sp->m_parent = this;
1806 if (m_max_submenu_name_length < menu_sp->m_name.size())
1807 m_max_submenu_name_length = menu_sp->m_name.size();
1808 if (m_max_submenu_key_name_length < menu_sp->m_key_name.size())
1809 m_max_submenu_key_name_length = menu_sp->m_key_name.size();
1810 m_submenus.push_back(menu_sp);
1811 }
1812
1813 void
1814 Menu::DrawMenuTitle (Window &window, bool highlight)
1815 {
1816 if (m_type == Type::Separator)
1817 {
1818 window.MoveCursor(0, window.GetCursorY());
1819 window.PutChar(ACS_LTEE);
1820 int width = window.GetWidth();
1821 if (width > 2)
1822 {
1823 width -= 2;
1824 for (size_t i=0; i< width; ++i)
1825 window.PutChar(ACS_HLINE);
1826 }
1827 window.PutChar(ACS_RTEE);
1828 }
1829 else
1830 {
1831 const int shortcut_key = m_key_value;
1832 bool underlined_shortcut = false;
1833 const attr_t hilgight_attr = A_REVERSE;
1834 if (highlight)
1835 window.AttributeOn(hilgight_attr);
1836 if (isprint(shortcut_key))
1837 {
1838 size_t lower_pos = m_name.find(tolower(shortcut_key));
1839 size_t upper_pos = m_name.find(toupper(shortcut_key));
1840 const char *name = m_name.c_str();
1841 size_t pos = std::min<size_t>(lower_pos, upper_pos);
1842 if (pos != std::string::npos)
1843 {
1844 underlined_shortcut = true;
1845 if (pos > 0)
1846 {
1847 window.PutCString(name, pos);
1848 name += pos;
1849 }
1850 const attr_t shortcut_attr = A_UNDERLINE|A_BOLD;
1851 window.AttributeOn (shortcut_attr);
1852 window.PutChar(name[0]);
1853 window.AttributeOff(shortcut_attr);
1854 name++;
1855 if (name[0])
1856 window.PutCString(name);
1857 }
1858 }
1859
1860 if (!underlined_shortcut)
1861 {
1862 window.PutCString(m_name.c_str());
1863 }
1864
1865 if (highlight)
1866 window.AttributeOff(hilgight_attr);
1867
1868 if (m_key_name.empty())
1869 {
1870 if (!underlined_shortcut && isprint(m_key_value))
1871 {
1872 window.AttributeOn (COLOR_PAIR(3));
1873 window.Printf (" (%c)", m_key_value);
1874 window.AttributeOff (COLOR_PAIR(3));
1875 }
1876 }
1877 else
1878 {
1879 window.AttributeOn (COLOR_PAIR(3));
1880 window.Printf (" (%s)", m_key_name.c_str());
1881 window.AttributeOff (COLOR_PAIR(3));
1882 }
1883 }
1884 }
1885
1886 bool
1887 Menu::WindowDelegateDraw (Window &window, bool force)
1888 {
1889 Menus &submenus = GetSubmenus();
1890 const size_t num_submenus = submenus.size();
1891 const int selected_idx = GetSelectedSubmenuIndex();
1892 Menu::Type menu_type = GetType ();
1893 switch (menu_type)
1894 {
1895 case Menu::Type::Bar:
1896 {
1897 window.SetBackground(2);
1898 window.MoveCursor(0, 0);
1899 for (size_t i=0; i<num_submenus; ++i)
1900 {
1901 Menu *menu = submenus[i].get();
1902 if (i > 0)
1903 window.PutChar(' ');
1904 menu->SetStartingColumn (window.GetCursorX());
1905 window.PutCString("| ");
1906 menu->DrawMenuTitle (window, false);
1907 }
1908 window.PutCString(" |");
1909 window.DeferredRefresh();
1910 }
1911 break;
1912
1913 case Menu::Type::Item:
1914 {
1915 int y = 1;
1916 int x = 3;
1917 // Draw the menu
1918 int cursor_x = 0;
1919 int cursor_y = 0;
1920 window.Erase();
1921 window.SetBackground(2);
1922 window.Box();
1923 for (size_t i=0; i<num_submenus; ++i)
1924 {
1925 const bool is_selected = i == selected_idx;
1926 window.MoveCursor(x, y + i);
1927 if (is_selected)
1928 {
1929 // Remember where we want the cursor to be
1930 cursor_x = x-1;
1931 cursor_y = y+i;
1932 }
1933 submenus[i]->DrawMenuTitle (window, is_selected);
1934 }
1935 window.MoveCursor(cursor_x, cursor_y);
1936 window.DeferredRefresh();
1937 }
1938 break;
1939
1940 default:
1941 case Menu::Type::Separator:
1942 break;
1943 }
1944 return true; // Drawing handled...
1945 }
1946
1947 HandleCharResult
1948 Menu::WindowDelegateHandleChar (Window &window, int key)
1949 {
1950 HandleCharResult result = eKeyNotHandled;
1951
1952 Menus &submenus = GetSubmenus();
1953 const size_t num_submenus = submenus.size();
1954 const int selected_idx = GetSelectedSubmenuIndex();
1955 Menu::Type menu_type = GetType ();
1956 if (menu_type == Menu::Type::Bar)
1957 {
1958 MenuSP run_menu_sp;
1959 switch (key)
1960 {
1961 case KEY_DOWN:
1962 case KEY_UP:
1963 // Show last menu or first menu
1964 if (selected_idx < num_submenus)
1965 run_menu_sp = submenus[selected_idx];
1966 else if (!submenus.empty())
1967 run_menu_sp = submenus.front();
1968 result = eKeyHandled;
1969 break;
1970
1971 case KEY_RIGHT:
1972 {
1973 ++m_selected;
1974 if (m_selected >= num_submenus)
1975 m_selected = 0;
1976 if (m_selected < num_submenus)
1977 run_menu_sp = submenus[m_selected];
1978 else if (!submenus.empty())
1979 run_menu_sp = submenus.front();
1980 result = eKeyHandled;
1981 }
1982 break;
1983
1984 case KEY_LEFT:
1985 {
1986 --m_selected;
1987 if (m_selected < 0)
1988 m_selected = num_submenus - 1;
1989 if (m_selected < num_submenus)
1990 run_menu_sp = submenus[m_selected];
1991 else if (!submenus.empty())
1992 run_menu_sp = submenus.front();
1993 result = eKeyHandled;
1994 }
1995 break;
1996
1997 default:
1998 for (size_t i=0; i<num_submenus; ++i)
1999 {
2000 if (submenus[i]->GetKeyValue() == key)
2001 {
2002 SetSelectedSubmenuIndex(i);
2003 run_menu_sp = submenus[i];
2004 result = eKeyHandled;
2005 break;
2006 }
2007 }
2008 break;
2009 }
2010
2011 if (run_menu_sp)
2012 {
2013 // Run the action on this menu in case we need to populate the
2014 // menu with dynamic content and also in case check marks, and
2015 // any other menu decorations need to be caclulated
2016 if (run_menu_sp->Action() == MenuActionResult::Quit)
2017 return eQuitApplication;
2018
2019 Rect menu_bounds;
2020 menu_bounds.origin.x = run_menu_sp->GetStartingColumn();
2021 menu_bounds.origin.y = 1;
2022 menu_bounds.size.width = run_menu_sp->GetDrawWidth();
2023 menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2;
2024 if (m_menu_window_sp)
2025 window.GetParent()->RemoveSubWindow(m_menu_window_sp.get());
2026
2027 m_menu_window_sp = window.GetParent()->CreateSubWindow (run_menu_sp->GetName().c_str(),
2028 menu_bounds,
2029 true);
2030 m_menu_window_sp->SetDelegate (run_menu_sp);
2031 }
2032 }
2033 else if (menu_type == Menu::Type::Item)
2034 {
2035 switch (key)
2036 {
2037 case KEY_DOWN:
2038 if (m_submenus.size() > 1)
2039 {
2040 const int start_select = m_selected;
2041 while (++m_selected != start_select)
2042 {
2043 if (m_selected >= num_submenus)
2044 m_selected = 0;
2045 if (m_submenus[m_selected]->GetType() == Type::Separator)
2046 continue;
2047 else
2048 break;
2049 }
2050 return eKeyHandled;
2051 }
2052 break;
2053
2054 case KEY_UP:
2055 if (m_submenus.size() > 1)
2056 {
2057 const int start_select = m_selected;
2058 while (--m_selected != start_select)
2059 {
2060 if (m_selected < 0)
2061 m_selected = num_submenus - 1;
2062 if (m_submenus[m_selected]->GetType() == Type::Separator)
2063 continue;
2064 else
2065 break;
2066 }
2067 return eKeyHandled;
2068 }
2069 break;
2070
2071 case KEY_RETURN:
2072 if (selected_idx < num_submenus)
2073 {
2074 if (submenus[selected_idx]->Action() == MenuActionResult::Quit)
2075 return eQuitApplication;
2076 window.GetParent()->RemoveSubWindow(&window);
2077 return eKeyHandled;
2078 }
2079 break;
2080
2081 case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in case other chars are entered for escaped sequences
2082 window.GetParent()->RemoveSubWindow(&window);
2083 return eKeyHandled;
2084
2085 default:
2086 {
2087 bool handled = false;
2088 for (size_t i=0; i<num_submenus; ++i)
2089 {
2090 Menu *menu = submenus[i].get();
2091 if (menu->GetKeyValue() == key)
2092 {
2093 handled = true;
2094 SetSelectedSubmenuIndex(i);
2095 window.GetParent()->RemoveSubWindow(&window);
2096 if (menu->Action() == MenuActionResult::Quit)
2097 return eQuitApplication;
2098 return eKeyHandled;
2099 }
2100 }
2101 }
2102 break;
2103
2104 }
2105 }
2106 else if (menu_type == Menu::Type::Separator)
2107 {
2108
2109 }
2110 return result;
2111 }
2112
2113
2114 class Application
2115 {
2116 public:
2117 Application (FILE *in, FILE *out) :
2118 m_window_sp(),
2119 m_screen (NULL),
2120 m_in (in),
2121 m_out (out)
2122 {
2123
2124 }
2125
2126 ~Application ()
2127 {
2128 m_window_delegates.clear();
2129 m_window_sp.reset();
2130 if (m_screen)
2131 {
2132 ::delscreen(m_screen);
2133 m_screen = NULL;
2134 }
2135 }
2136
2137 void
2138 Initialize ()
2139 {
2140 ::setlocale(LC_ALL, "");
2141 ::setlocale(LC_CTYPE, "");
2142#if 0
2143 ::initscr();
2144#else
2145 m_screen = ::newterm(NULL, m_out, m_in);
2146#endif
2147 ::start_color();
2148 ::curs_set(0);
2149 ::noecho();
2150 ::keypad(stdscr,TRUE);
2151 }
2152
2153 void
2154 Terminate ()
2155 {
2156 ::endwin();
2157 }
2158
2159 void
2160 Run (Debugger &debugger)
2161 {
2162 bool done = false;
2163 int delay_in_tenths_of_a_second = 1;
2164
2165 // Alas the threading model in curses is a bit lame so we need to
2166 // resort to polling every 0.5 seconds. We could poll for stdin
2167 // ourselves and then pass the keys down but then we need to
2168 // translate all of the escape sequences ourselves. So we resort to
2169 // polling for input because we need to receive async process events
2170 // while in this loop.
2171
2172 halfdelay(delay_in_tenths_of_a_second); // Poll using some number of tenths of seconds seconds when calling Window::GetChar()
2173
2174 ListenerSP listener_sp (new Listener ("lldb.IOHandler.curses.Application"));
2175 ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass());
2176 ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass());
2177 ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass());
2178 debugger.EnableForwardEvents (listener_sp);
2179
2180 bool update = true;
2181#if defined(__APPLE__)
2182 std::deque<int> escape_chars;
2183#endif
2184
2185 while (!done)
2186 {
2187 if (update)
2188 {
2189 m_window_sp->Draw(false);
2190 // All windows should be calling Window::DeferredRefresh() instead
2191 // of Window::Refresh() so we can do a single update and avoid
2192 // any screen blinking
2193 update_panels();
2194
2195 // Cursor hiding isn't working on MacOSX, so hide it in the top left corner
2196 m_window_sp->MoveCursor(0, 0);
2197
2198 doupdate();
2199 update = false;
2200 }
2201
2202#if defined(__APPLE__)
2203 // Terminal.app doesn't map its function keys correctly, F1-F4 default to:
2204 // \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if possible
2205 int ch;
2206 if (escape_chars.empty())
2207 ch = m_window_sp->GetChar();
2208 else
2209 {
2210 ch = escape_chars.front();
2211 escape_chars.pop_front();
2212 }
2213 if (ch == KEY_ESCAPE)
2214 {
2215 int ch2 = m_window_sp->GetChar();
2216 if (ch2 == 'O')
2217 {
2218 int ch3 = m_window_sp->GetChar();
2219 switch (ch3)
2220 {
2221 case 'P': ch = KEY_F(1); break;
2222 case 'Q': ch = KEY_F(2); break;
2223 case 'R': ch = KEY_F(3); break;
2224 case 'S': ch = KEY_F(4); break;
2225 default:
2226 escape_chars.push_back(ch2);
2227 if (ch3 != -1)
2228 escape_chars.push_back(ch3);
2229 break;
2230 }
2231 }
2232 else if (ch2 != -1)
2233 escape_chars.push_back(ch2);
2234 }
2235#else
2236 int ch = m_window_sp->GetChar();
2237
2238#endif
2239 if (ch == -1)
2240 {
2241 if (feof(m_in) || ferror(m_in))
2242 {
2243 done = true;
2244 }
2245 else
2246 {
2247 // Just a timeout from using halfdelay(), check for events
2248 EventSP event_sp;
2249 while (listener_sp->PeekAtNextEvent())
2250 {
2251 listener_sp->GetNextEvent(event_sp);
2252
2253 if (event_sp)
2254 {
2255 Broadcaster *broadcaster = event_sp->GetBroadcaster();
2256 if (broadcaster)
2257 {
2258 //uint32_t event_type = event_sp->GetType();
2259 ConstString broadcaster_class (broadcaster->GetBroadcasterClass());
2260 if (broadcaster_class == broadcaster_class_process)
2261 {
2262 update = true;
2263 continue; // Don't get any key, just update our view
2264 }
2265 }
2266 }
2267 }
2268 }
2269 }
2270 else
2271 {
2272 HandleCharResult key_result = m_window_sp->HandleChar(ch);
2273 switch (key_result)
2274 {
2275 case eKeyHandled:
2276 update = true;
2277 break;
2278 case eKeyNotHandled:
2279 break;
2280 case eQuitApplication:
2281 done = true;
2282 break;
2283 }
2284 }
2285 }
2286
2287 debugger.CancelForwardEvents (listener_sp);
2288
2289 }
2290
2291 WindowSP &
2292 GetMainWindow ()
2293 {
2294 if (!m_window_sp)
2295 m_window_sp.reset (new Window ("main", stdscr, false));
2296 return m_window_sp;
2297 }
2298
2299 WindowDelegates &
2300 GetWindowDelegates ()
2301 {
2302 return m_window_delegates;
2303 }
2304
2305 protected:
2306 WindowSP m_window_sp;
2307 WindowDelegates m_window_delegates;
2308 SCREEN *m_screen;
2309 FILE *m_in;
2310 FILE *m_out;
2311 };
2312
2313
2314} // namespace curses
2315
2316
2317using namespace curses;
2318
2319struct Row
2320{
2321 ValueObjectSP valobj;
2322 Row *parent;
2323 int row_idx;
2324 int x;
2325 int y;
2326 bool might_have_children;
2327 bool expanded;
2328 bool calculated_children;
2329 std::vector<Row> children;
2330
2331 Row (const ValueObjectSP &v, Row *p) :
2332 valobj (v),
2333 parent (p),
2334 row_idx(0),
2335 x(1),
2336 y(1),
2337 might_have_children (v ? v->MightHaveChildren() : false),
2338 expanded (false),
2339 calculated_children (false),
2340 children()
2341 {
2342 }
2343
2344 size_t
2345 GetDepth () const
2346 {
2347 if (parent)
2348 return 1 + parent->GetDepth();
2349 return 0;
2350 }
2351
2352 void
2353 Expand()
2354 {
2355 expanded = true;
2356 if (!calculated_children)
2357 {
2358 calculated_children = true;
2359 if (valobj)
2360 {
2361 const size_t num_children = valobj->GetNumChildren();
2362 for (size_t i=0; i<num_children; ++i)
2363 {
2364 children.push_back(Row (valobj->GetChildAtIndex(i, true), this));
2365 }
2366 }
2367 }
2368 }
2369
2370 void
2371 Unexpand ()
2372 {
2373 expanded = false;
2374 }
2375
2376 void
2377 DrawTree (Window &window)
2378 {
2379 if (parent)
2380 parent->DrawTreeForChild (window, this, 0);
2381
2382 if (might_have_children)
2383 {
2384 // It we can get UTF8 characters to work we should try to use the "symbol"
2385 // UTF8 string below
2386// const char *symbol = "";
2387// if (row.expanded)
2388// symbol = "\xe2\x96\xbd ";
2389// else
2390// symbol = "\xe2\x96\xb7 ";
2391// window.PutCString (symbol);
2392
2393 // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
2394 // 'v' or '>' character...
2395// if (expanded)
2396// window.PutChar (ACS_DARROW);
2397// else
2398// window.PutChar (ACS_RARROW);
2399 // Since we can't find any good looking right arrow/down arrow
2400 // symbols, just use a diamond...
2401 window.PutChar (ACS_DIAMOND);
2402 window.PutChar (ACS_HLINE);
2403 }
2404 }
2405
2406 void
2407 DrawTreeForChild (Window &window, Row *child, uint32_t reverse_depth)
2408 {
2409 if (parent)
2410 parent->DrawTreeForChild (window, this, reverse_depth + 1);
2411
2412 if (&children.back() == child)
2413 {
2414 // Last child
2415 if (reverse_depth == 0)
2416 {
2417 window.PutChar (ACS_LLCORNER);
2418 window.PutChar (ACS_HLINE);
2419 }
2420 else
2421 {
2422 window.PutChar (' ');
2423 window.PutChar (' ');
2424 }
2425 }
2426 else
2427 {
2428 if (reverse_depth == 0)
2429 {
2430 window.PutChar (ACS_LTEE);
2431 window.PutChar (ACS_HLINE);
2432 }
2433 else
2434 {
2435 window.PutChar (ACS_VLINE);
2436 window.PutChar (' ');
2437 }
2438 }
2439 }
2440};
2441
2442struct DisplayOptions
2443{
2444 bool show_types;
2445};
2446
2447class TreeItem;
2448
2449class TreeDelegate
2450{
2451public:
2452 TreeDelegate() {}
2453 virtual ~TreeDelegate() {}
2454 virtual void TreeDelegateDrawTreeItem (TreeItem &item, Window &window) = 0;
2455 virtual void TreeDelegateGenerateChildren (TreeItem &item) = 0;
2456 virtual bool TreeDelegateItemSelected (TreeItem &item) = 0; // Return true if we need to update views
2457};
2458typedef std::shared_ptr<TreeDelegate> TreeDelegateSP;
2459
2460class TreeItem
2461{
2462public:
2463
2464 TreeItem (TreeItem *parent, TreeDelegate &delegate, bool might_have_children) :
2465 m_parent (parent),
2466 m_delegate (delegate),
2467 m_identifier (0),
2468 m_row_idx (-1),
2469 m_children (),
2470 m_might_have_children (might_have_children),
2471 m_is_expanded (false)
2472 {
2473 }
2474
2475 TreeItem &
2476 operator=(const TreeItem &rhs)
2477 {
2478 if (this != &rhs)
2479 {
2480 m_parent = rhs.m_parent;
2481 m_delegate = rhs.m_delegate;
2482 m_identifier = rhs.m_identifier;
2483 m_row_idx = rhs.m_row_idx;
2484 m_children = rhs.m_children;
2485 m_might_have_children = rhs.m_might_have_children;
2486 m_is_expanded = rhs.m_is_expanded;
2487 }
2488 return *this;
2489 }
2490
2491 size_t
2492 GetDepth () const
2493 {
2494 if (m_parent)
2495 return 1 + m_parent->GetDepth();
2496 return 0;
2497 }
2498
2499 int
2500 GetRowIndex () const
2501 {
2502 return m_row_idx;
2503 }
2504
2505 void
2506 ClearChildren ()
2507 {
2508 m_children.clear();
2509 }
2510
2511 void
2512 Resize (size_t n, const TreeItem &t)
2513 {
2514 m_children.resize(n, t);
2515 }
2516
2517 TreeItem &
2518 operator [](size_t i)
2519 {
2520 return m_children[i];
2521 }
2522
2523 void
2524 SetRowIndex (int row_idx)
2525 {
2526 m_row_idx = row_idx;
2527 }
2528
2529 size_t
2530 GetNumChildren ()
2531 {
2532 m_delegate.TreeDelegateGenerateChildren (*this);
2533 return m_children.size();
2534 }
2535
2536 void
2537 ItemWasSelected ()
2538 {
2539 m_delegate.TreeDelegateItemSelected(*this);
2540 }
2541 void
2542 CalculateRowIndexes (int &row_idx)
2543 {
2544 SetRowIndex(row_idx);
2545 ++row_idx;
2546
2547 // The root item must calculate its children
2548 if (m_parent == NULL)
2549 GetNumChildren();
2550
2551 const bool expanded = IsExpanded();
2552 for (auto &item : m_children)
2553 {
2554 if (expanded)
2555 item.CalculateRowIndexes(row_idx);
2556 else
2557 item.SetRowIndex(-1);
2558 }
2559 }
2560
2561 TreeItem *
2562 GetParent ()
2563 {
2564 return m_parent;
2565 }
2566
2567 bool
2568 IsExpanded () const
2569 {
2570 return m_is_expanded;
2571 }
2572
2573 void
2574 Expand()
2575 {
2576 m_is_expanded = true;
2577 }
2578
2579 void
2580 Unexpand ()
2581 {
2582 m_is_expanded = false;
2583 }
2584
2585 bool
2586 Draw (Window &window,
2587 const int first_visible_row,
2588 const uint32_t selected_row_idx,
2589 int &row_idx,
2590 int &num_rows_left)
2591 {
2592 if (num_rows_left <= 0)
2593 return false;
2594
2595 if (m_row_idx >= first_visible_row)
2596 {
2597 window.MoveCursor(2, row_idx + 1);
2598
2599 if (m_parent)
2600 m_parent->DrawTreeForChild (window, this, 0);
2601
2602 if (m_might_have_children)
2603 {
2604 // It we can get UTF8 characters to work we should try to use the "symbol"
2605 // UTF8 string below
2606 // const char *symbol = "";
2607 // if (row.expanded)
2608 // symbol = "\xe2\x96\xbd ";
2609 // else
2610 // symbol = "\xe2\x96\xb7 ";
2611 // window.PutCString (symbol);
2612
2613 // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
2614 // 'v' or '>' character...
2615 // if (expanded)
2616 // window.PutChar (ACS_DARROW);
2617 // else
2618 // window.PutChar (ACS_RARROW);
2619 // Since we can't find any good looking right arrow/down arrow
2620 // symbols, just use a diamond...
2621 window.PutChar (ACS_DIAMOND);
2622 window.PutChar (ACS_HLINE);
2623 }
2624 bool highlight = (selected_row_idx == m_row_idx) && window.IsActive();
2625
2626 if (highlight)
2627 window.AttributeOn(A_REVERSE);
2628
2629 m_delegate.TreeDelegateDrawTreeItem(*this, window);
2630
2631 if (highlight)
2632 window.AttributeOff(A_REVERSE);
2633 ++row_idx;
2634 --num_rows_left;
2635 }
2636
2637 if (num_rows_left <= 0)
2638 return false; // We are done drawing...
2639
2640 if (IsExpanded())
2641 {
2642 for (auto &item : m_children)
2643 {
2644 // If we displayed all the rows and item.Draw() returns
2645 // false we are done drawing and can exit this for loop
2646 if (item.Draw(window, first_visible_row, selected_row_idx, row_idx, num_rows_left) == false)
2647 break;
2648 }
2649 }
2650 return num_rows_left >= 0; // Return true if not done drawing yet
2651 }
2652
2653 void
2654 DrawTreeForChild (Window &window, TreeItem *child, uint32_t reverse_depth)
2655 {
2656 if (m_parent)
2657 m_parent->DrawTreeForChild (window, this, reverse_depth + 1);
2658
2659 if (&m_children.back() == child)
2660 {
2661 // Last child
2662 if (reverse_depth == 0)
2663 {
2664 window.PutChar (ACS_LLCORNER);
2665 window.PutChar (ACS_HLINE);
2666 }
2667 else
2668 {
2669 window.PutChar (' ');
2670 window.PutChar (' ');
2671 }
2672 }
2673 else
2674 {
2675 if (reverse_depth == 0)
2676 {
2677 window.PutChar (ACS_LTEE);
2678 window.PutChar (ACS_HLINE);
2679 }
2680 else
2681 {
2682 window.PutChar (ACS_VLINE);
2683 window.PutChar (' ');
2684 }
2685 }
2686 }
2687
2688 TreeItem *
2689 GetItemForRowIndex (uint32_t row_idx)
2690 {
2691 if (m_row_idx == row_idx)
2692 return this;
2693 if (m_children.empty())
2694 return NULL;
2695 if (m_children.back().m_row_idx < row_idx)
2696 return NULL;
2697 if (IsExpanded())
2698 {
2699 for (auto &item : m_children)
2700 {
2701 TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx);
2702 if (selected_item_ptr)
2703 return selected_item_ptr;
2704 }
2705 }
2706 return NULL;
2707 }
2708
2709// void *
2710// GetUserData() const
2711// {
2712// return m_user_data;
2713// }
2714//
2715// void
2716// SetUserData (void *user_data)
2717// {
2718// m_user_data = user_data;
2719// }
2720 uint64_t
2721 GetIdentifier() const
2722 {
2723 return m_identifier;
2724 }
2725
2726 void
2727 SetIdentifier (uint64_t identifier)
2728 {
2729 m_identifier = identifier;
2730 }
2731
2732
2733protected:
2734 TreeItem *m_parent;
2735 TreeDelegate &m_delegate;
2736 //void *m_user_data;
2737 uint64_t m_identifier;
2738 int m_row_idx; // Zero based visible row index, -1 if not visible or for the root item
2739 std::vector<TreeItem> m_children;
2740 bool m_might_have_children;
2741 bool m_is_expanded;
2742
2743};
2744
2745class TreeWindowDelegate : public WindowDelegate
2746{
2747public:
2748 TreeWindowDelegate (Debugger &debugger, const TreeDelegateSP &delegate_sp) :
2749 m_debugger (debugger),
2750 m_delegate_sp (delegate_sp),
2751 m_root (NULL, *delegate_sp, true),
2752 m_selected_item (NULL),
2753 m_num_rows (0),
2754 m_selected_row_idx (0),
2755 m_first_visible_row (0),
2756 m_min_x (0),
2757 m_min_y (0),
2758 m_max_x (0),
2759 m_max_y (0)
2760 {
2761 }
2762
2763 int
2764 NumVisibleRows () const
2765 {
2766 return m_max_y - m_min_y;
2767 }
2768
2769 virtual bool
2770 WindowDelegateDraw (Window &window, bool force)
2771 {
2772 ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext());
2773 Process *process = exe_ctx.GetProcessPtr();
2774
2775 bool display_content = false;
2776 if (process)
2777 {
2778 StateType state = process->GetState();
2779 if (StateIsStoppedState(state, true))
2780 {
2781 // We are stopped, so it is ok to
2782 display_content = true;
2783 }
2784 else if (StateIsRunningState(state))
2785 {
2786 return true; // Don't do any updating when we are running
2787 }
2788 }
2789
2790 m_min_x = 2;
2791 m_min_y = 1;
2792 m_max_x = window.GetWidth() - 1;
2793 m_max_y = window.GetHeight() - 1;
2794
2795 window.Erase();
2796 window.DrawTitleBox (window.GetName());
2797
2798 if (display_content)
2799 {
2800 const int num_visible_rows = NumVisibleRows();
2801 m_num_rows = 0;
2802 m_root.CalculateRowIndexes(m_num_rows);
2803
2804 // If we unexpanded while having something selected our
2805 // total number of rows is less than the num visible rows,
2806 // then make sure we show all the rows by setting the first
2807 // visible row accordingly.
2808 if (m_first_visible_row > 0 && m_num_rows < num_visible_rows)
2809 m_first_visible_row = 0;
2810
2811 // Make sure the selected row is always visible
2812 if (m_selected_row_idx < m_first_visible_row)
2813 m_first_visible_row = m_selected_row_idx;
2814 else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
2815 m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
2816
2817 int row_idx = 0;
2818 int num_rows_left = num_visible_rows;
2819 m_root.Draw (window, m_first_visible_row, m_selected_row_idx, row_idx, num_rows_left);
2820 // Get the selected row
2821 m_selected_item = m_root.GetItemForRowIndex (m_selected_row_idx);
2822 }
2823 else
2824 {
2825 m_selected_item = NULL;
2826 }
2827
2828 window.DeferredRefresh();
2829
2830
2831 return true; // Drawing handled
2832 }
2833
2834
2835 virtual const char *
2836 WindowDelegateGetHelpText ()
2837 {
2838 return "Thread window keyboard shortcuts:";
2839 }
2840
2841 virtual KeyHelp *
2842 WindowDelegateGetKeyHelp ()
2843 {
2844 static curses::KeyHelp g_source_view_key_help[] = {
2845 { KEY_UP, "Select previous item" },
2846 { KEY_DOWN, "Select next item" },
2847 { KEY_RIGHT, "Expand the selected item" },
2848 { KEY_LEFT, "Unexpand the selected item or select parent if not expanded" },
2849 { KEY_PPAGE, "Page up" },
2850 { KEY_NPAGE, "Page down" },
2851 { 'h', "Show help dialog" },
2852 { ' ', "Toggle item expansion" },
2853 { ',', "Page up" },
2854 { '.', "Page down" },
2855 { '\0', NULL }
2856 };
2857 return g_source_view_key_help;
2858 }
2859
2860 virtual HandleCharResult
2861 WindowDelegateHandleChar (Window &window, int c)
2862 {
2863 switch(c)
2864 {
2865 case ',':
2866 case KEY_PPAGE:
2867 // Page up key
2868 if (m_first_visible_row > 0)
2869 {
2870 if (m_first_visible_row > m_max_y)
2871 m_first_visible_row -= m_max_y;
2872 else
2873 m_first_visible_row = 0;
2874 m_selected_row_idx = m_first_visible_row;
2875 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
2876 if (m_selected_item)
2877 m_selected_item->ItemWasSelected ();
2878 }
2879 return eKeyHandled;
2880
2881 case '.':
2882 case KEY_NPAGE:
2883 // Page down key
2884 if (m_num_rows > m_max_y)
2885 {
2886 if (m_first_visible_row + m_max_y < m_num_rows)
2887 {
2888 m_first_visible_row += m_max_y;
2889 m_selected_row_idx = m_first_visible_row;
2890 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
2891 if (m_selected_item)
2892 m_selected_item->ItemWasSelected ();
2893 }
2894 }
2895 return eKeyHandled;
2896
2897 case KEY_UP:
2898 if (m_selected_row_idx > 0)
2899 {
2900 --m_selected_row_idx;
2901 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
2902 if (m_selected_item)
2903 m_selected_item->ItemWasSelected ();
2904 }
2905 return eKeyHandled;
2906 case KEY_DOWN:
2907 if (m_selected_row_idx + 1 < m_num_rows)
2908 {
2909 ++m_selected_row_idx;
2910 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
2911 if (m_selected_item)
2912 m_selected_item->ItemWasSelected ();
2913 }
2914 return eKeyHandled;
2915
2916 case KEY_RIGHT:
2917 if (m_selected_item)
2918 {
2919 if (!m_selected_item->IsExpanded())
2920 m_selected_item->Expand();
2921 }
2922 return eKeyHandled;
2923
2924 case KEY_LEFT:
2925 if (m_selected_item)
2926 {
2927 if (m_selected_item->IsExpanded())
2928 m_selected_item->Unexpand();
2929 else if (m_selected_item->GetParent())
2930 {
2931 m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex();
2932 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
2933 if (m_selected_item)
2934 m_selected_item->ItemWasSelected ();
2935 }
2936 }
2937 return eKeyHandled;
2938
2939 case ' ':
2940 // Toggle expansion state when SPACE is pressed
2941 if (m_selected_item)
2942 {
2943 if (m_selected_item->IsExpanded())
2944 m_selected_item->Unexpand();
2945 else
2946 m_selected_item->Expand();
2947 }
2948 return eKeyHandled;
2949
2950 case 'h':
2951 window.CreateHelpSubwindow ();
2952 return eKeyHandled;
2953
2954 default:
2955 break;
2956 }
2957 return eKeyNotHandled;
2958 }
2959
2960protected:
2961 Debugger &m_debugger;
2962 TreeDelegateSP m_delegate_sp;
2963 TreeItem m_root;
2964 TreeItem *m_selected_item;
2965 int m_num_rows;
2966 int m_selected_row_idx;
2967 int m_first_visible_row;
2968 int m_min_x;
2969 int m_min_y;
2970 int m_max_x;
2971 int m_max_y;
2972
2973};
2974
2975class FrameTreeDelegate : public TreeDelegate
2976{
2977public:
2978 FrameTreeDelegate (const ThreadSP &thread_sp) :
2979 TreeDelegate(),
2980 m_thread_wp()
2981 {
2982 if (thread_sp)
2983 m_thread_wp = thread_sp;
2984 }
2985
2986 virtual ~FrameTreeDelegate()
2987 {
2988 }
2989
2990 virtual void
2991 TreeDelegateDrawTreeItem (TreeItem &item, Window &window)
2992 {
2993 ThreadSP thread_sp = m_thread_wp.lock();
2994 if (thread_sp)
2995 {
2996 const uint64_t frame_idx = item.GetIdentifier();
2997 StackFrameSP frame_sp = thread_sp->GetStackFrameAtIndex(frame_idx);
2998 if (frame_sp)
2999 {
3000 StreamString strm;
3001 const SymbolContext &sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
3002 ExecutionContext exe_ctx (frame_sp);
3003 //const char *frame_format = "frame #${frame.index}: ${module.file.basename}{`${function.name}${function.pc-offset}}}";
3004 const char *frame_format = "frame #${frame.index}: {${function.name}${function.pc-offset}}}";
3005 if (Debugger::FormatPrompt (frame_format, &sc, &exe_ctx, NULL, strm))
3006 {
3007 int right_pad = 1;
3008 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
3009 }
3010 }
3011 }
3012 }
3013 virtual void
3014 TreeDelegateGenerateChildren (TreeItem &item)
3015 {
3016 // No children for frames yet...
3017 }
3018
3019 virtual bool
3020 TreeDelegateItemSelected (TreeItem &item)
3021 {
3022 ThreadSP thread_sp = m_thread_wp.lock();
3023 if (thread_sp)
3024 {
3025 const uint64_t frame_idx = item.GetIdentifier();
3026 thread_sp->SetSelectedFrameByIndex(frame_idx);
3027 return true;
3028 }
3029 return false;
3030 }
3031 void
3032 SetThread (ThreadSP thread_sp)
3033 {
3034 m_thread_wp = thread_sp;
3035 }
3036
3037protected:
3038 ThreadWP m_thread_wp;
3039};
3040
3041class ThreadTreeDelegate : public TreeDelegate
3042{
3043public:
3044 ThreadTreeDelegate (Debugger &debugger) :
3045 TreeDelegate(),
3046 m_debugger (debugger),
3047 m_thread_wp (),
3048 m_tid (LLDB_INVALID_THREAD_ID),
3049 m_stop_id (UINT32_MAX)
3050 {
3051 }
3052
3053 virtual
3054 ~ThreadTreeDelegate()
3055 {
3056 }
3057
3058 virtual void
3059 TreeDelegateDrawTreeItem (TreeItem &item, Window &window)
3060 {
3061 ThreadSP thread_sp = m_thread_wp.lock();
3062 if (thread_sp)
3063 {
3064 StreamString strm;
3065 ExecutionContext exe_ctx (thread_sp);
3066 const char *format = "thread #${thread.index}: tid = ${thread.id}{, stop reason = ${thread.stop-reason}}";
3067 if (Debugger::FormatPrompt (format, NULL, &exe_ctx, NULL, strm))
3068 {
3069 int right_pad = 1;
3070 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
3071 }
3072 }
3073 }
3074 virtual void
3075 TreeDelegateGenerateChildren (TreeItem &item)
3076 {
3077 TargetSP target_sp (m_debugger.GetSelectedTarget());
3078 if (target_sp)
3079 {
3080 ProcessSP process_sp = target_sp->GetProcessSP();
3081 if (process_sp && process_sp->IsAlive())
3082 {
3083 StateType state = process_sp->GetState();
3084 if (StateIsStoppedState(state, true))
3085 {
3086 ThreadSP thread_sp = process_sp->GetThreadList().GetSelectedThread();
3087 if (thread_sp)
3088 {
3089 if (m_stop_id == process_sp->GetStopID() && thread_sp->GetID() == m_tid)
3090 return; // Children are already up to date
3091 if (m_frame_delegate_sp)
3092 m_frame_delegate_sp->SetThread(thread_sp);
3093 else
3094 {
3095 // Always expand the thread item the first time we show it
3096 item.Expand();
3097 m_frame_delegate_sp.reset (new FrameTreeDelegate(thread_sp));
3098 }
3099
3100 m_stop_id = process_sp->GetStopID();
3101 m_thread_wp = thread_sp;
3102 m_tid = thread_sp->GetID();
3103
3104 TreeItem t (&item, *m_frame_delegate_sp, false);
3105 size_t num_frames = thread_sp->GetStackFrameCount();
3106 item.Resize (num_frames, t);
3107 for (size_t i=0; i<num_frames; ++i)
3108 {
3109 item[i].SetIdentifier(i);
3110 }
3111 }
3112 return;
3113 }
3114 }
3115 }
3116 item.ClearChildren();
3117 }
3118
3119 virtual bool
3120 TreeDelegateItemSelected (TreeItem &item)
3121 {
3122 ThreadSP thread_sp = m_thread_wp.lock();
3123 if (thread_sp)
3124 {
3125 ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList();
3126 Mutex::Locker locker (thread_list.GetMutex());
3127 ThreadSP selected_thread_sp = thread_list.GetSelectedThread();
3128 if (selected_thread_sp->GetID() != thread_sp->GetID())
3129 {
3130 thread_list.SetSelectedThreadByID(thread_sp->GetID());
3131 return true;
3132 }
3133 }
3134 return false;
3135 }
3136
3137protected:
3138 Debugger &m_debugger;
3139 ThreadWP m_thread_wp;
3140 std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp;
3141 lldb::user_id_t m_tid;
3142 uint32_t m_stop_id;
3143};
3144
3145class ValueObjectListDelegate : public WindowDelegate
3146{
3147public:
3148 ValueObjectListDelegate () :
3149 m_valobj_list (),
3150 m_rows (),
3151 m_selected_row (NULL),
3152 m_selected_row_idx (0),
3153 m_first_visible_row (0),
3154 m_num_rows (0),
3155 m_max_x (0),
3156 m_max_y (0)
3157 {
3158 }
3159
3160 ValueObjectListDelegate (ValueObjectList &valobj_list) :
3161 m_valobj_list (valobj_list),
3162 m_rows (),
3163 m_selected_row (NULL),
3164 m_selected_row_idx (0),
3165 m_first_visible_row (0),
3166 m_num_rows (0),
3167 m_max_x (0),
3168 m_max_y (0)
3169 {
3170 SetValues (valobj_list);
3171 }
3172
3173 virtual
3174 ~ValueObjectListDelegate()
3175 {
3176 }
3177
3178 void
3179 SetValues (ValueObjectList &valobj_list)
3180 {
3181 m_selected_row = NULL;
3182 m_selected_row_idx = 0;
3183 m_first_visible_row = 0;
3184 m_num_rows = 0;
3185 m_rows.clear();
3186 m_valobj_list = valobj_list;
3187 const size_t num_values = m_valobj_list.GetSize();
3188 for (size_t i=0; i<num_values; ++i)
3189 m_rows.push_back(Row(m_valobj_list.GetValueObjectAtIndex(i), NULL));
3190 }
3191
3192 virtual bool
3193 WindowDelegateDraw (Window &window, bool force)
3194 {
3195 m_num_rows = 0;
3196 m_min_x = 2;
3197 m_min_y = 1;
3198 m_max_x = window.GetWidth() - 1;
3199 m_max_y = window.GetHeight() - 1;
3200
3201 window.Erase();
3202 window.DrawTitleBox (window.GetName());
3203
3204 const int num_visible_rows = NumVisibleRows();
3205 const int num_rows = CalculateTotalNumberRows (m_rows);
3206
3207 // If we unexpanded while having something selected our
3208 // total number of rows is less than the num visible rows,
3209 // then make sure we show all the rows by setting the first
3210 // visible row accordingly.
3211 if (m_first_visible_row > 0 && num_rows < num_visible_rows)
3212 m_first_visible_row = 0;
3213
3214 // Make sure the selected row is always visible
3215 if (m_selected_row_idx < m_first_visible_row)
3216 m_first_visible_row = m_selected_row_idx;
3217 else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
3218 m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
3219
3220 DisplayRows (window, m_rows, g_options);
3221
3222 window.DeferredRefresh();
3223
3224 // Get the selected row
3225 m_selected_row = GetRowForRowIndex (m_selected_row_idx);
3226 // Keep the cursor on the selected row so the highlight and the cursor
3227 // are always on the same line
3228 if (m_selected_row)
3229 window.MoveCursor (m_selected_row->x,
3230 m_selected_row->y);
3231
3232 return true; // Drawing handled
3233 }
3234
3235 virtual KeyHelp *
3236 WindowDelegateGetKeyHelp ()
3237 {
3238 static curses::KeyHelp g_source_view_key_help[] = {
3239 { KEY_UP, "Select previous item" },
3240 { KEY_DOWN, "Select next item" },
3241 { KEY_RIGHT, "Expand selected item" },
3242 { KEY_LEFT, "Unexpand selected item or select parent if not expanded" },
3243 { KEY_PPAGE, "Page up" },
3244 { KEY_NPAGE, "Page down" },
3245 { 'A', "Format as annotated address" },
3246 { 'b', "Format as binary" },
3247 { 'B', "Format as hex bytes with ASCII" },
3248 { 'c', "Format as character" },
3249 { 'd', "Format as a signed integer" },
3250 { 'D', "Format selected value using the default format for the type" },
3251 { 'f', "Format as float" },
3252 { 'h', "Show help dialog" },
3253 { 'i', "Format as instructions" },
3254 { 'o', "Format as octal" },
3255 { 'p', "Format as pointer" },
3256 { 's', "Format as C string" },
3257 { 't', "Toggle showing/hiding type names" },
3258 { 'u', "Format as an unsigned integer" },
3259 { 'x', "Format as hex" },
3260 { 'X', "Format as uppercase hex" },
3261 { ' ', "Toggle item expansion" },
3262 { ',', "Page up" },
3263 { '.', "Page down" },
3264 { '\0', NULL }
3265 };
3266 return g_source_view_key_help;
3267 }
3268
3269
3270 virtual HandleCharResult
3271 WindowDelegateHandleChar (Window &window, int c)
3272 {
3273 switch(c)
3274 {
3275 case 'x':
3276 case 'X':
3277 case 'o':
3278 case 's':
3279 case 'u':
3280 case 'd':
3281 case 'D':
3282 case 'i':
3283 case 'A':
3284 case 'p':
3285 case 'c':
3286 case 'b':
3287 case 'B':
3288 case 'f':
3289 // Change the format for the currently selected item
3290 if (m_selected_row)
3291 m_selected_row->valobj->SetFormat (FormatForChar (c));
3292 return eKeyHandled;
3293
3294 case 't':
3295 // Toggle showing type names
3296 g_options.show_types = !g_options.show_types;
3297 return eKeyHandled;
3298
3299 case ',':
3300 case KEY_PPAGE:
3301 // Page up key
3302 if (m_first_visible_row > 0)
3303 {
3304 if (m_first_visible_row > m_max_y)
3305 m_first_visible_row -= m_max_y;
3306 else
3307 m_first_visible_row = 0;
3308 m_selected_row_idx = m_first_visible_row;
3309 }
3310 return eKeyHandled;
3311
3312 case '.':
3313 case KEY_NPAGE:
3314 // Page down key
3315 if (m_num_rows > m_max_y)
3316 {
3317 if (m_first_visible_row + m_max_y < m_num_rows)
3318 {
3319 m_first_visible_row += m_max_y;
3320 m_selected_row_idx = m_first_visible_row;
3321 }
3322 }
3323 return eKeyHandled;
3324
3325 case KEY_UP:
3326 if (m_selected_row_idx > 0)
3327 --m_selected_row_idx;
3328 return eKeyHandled;
3329 case KEY_DOWN:
3330 if (m_selected_row_idx + 1 < m_num_rows)
3331 ++m_selected_row_idx;
3332 return eKeyHandled;
3333
3334 case KEY_RIGHT:
3335 if (m_selected_row)
3336 {
3337 if (!m_selected_row->expanded)
3338 m_selected_row->Expand();
3339 }
3340 return eKeyHandled;
3341
3342 case KEY_LEFT:
3343 if (m_selected_row)
3344 {
3345 if (m_selected_row->expanded)
3346 m_selected_row->Unexpand();
3347 else if (m_selected_row->parent)
3348 m_selected_row_idx = m_selected_row->parent->row_idx;
3349 }
3350 return eKeyHandled;
3351
3352 case ' ':
3353 // Toggle expansion state when SPACE is pressed
3354 if (m_selected_row)
3355 {
3356 if (m_selected_row->expanded)
3357 m_selected_row->Unexpand();
3358 else
3359 m_selected_row->Expand();
3360 }
3361 return eKeyHandled;
3362
3363 case 'h':
3364 window.CreateHelpSubwindow ();
3365 return eKeyHandled;
3366
3367 default:
3368 break;
3369 }
3370 return eKeyNotHandled;
3371 }
3372
3373protected:
3374 ValueObjectList m_valobj_list;
3375 std::vector<Row> m_rows;
3376 Row *m_selected_row;
3377 uint32_t m_selected_row_idx;
3378 uint32_t m_first_visible_row;
3379 uint32_t m_num_rows;
3380 int m_min_x;
3381 int m_min_y;
3382 int m_max_x;
3383 int m_max_y;
3384
3385 static Format
3386 FormatForChar (int c)
3387 {
3388 switch (c)
3389 {
3390 case 'x': return eFormatHex;
3391 case 'X': return eFormatHexUppercase;
3392 case 'o': return eFormatOctal;
3393 case 's': return eFormatCString;
3394 case 'u': return eFormatUnsigned;
3395 case 'd': return eFormatDecimal;
3396 case 'D': return eFormatDefault;
3397 case 'i': return eFormatInstruction;
3398 case 'A': return eFormatAddressInfo;
3399 case 'p': return eFormatPointer;
3400 case 'c': return eFormatChar;
3401 case 'b': return eFormatBinary;
3402 case 'B': return eFormatBytesWithASCII;
3403 case 'f': return eFormatFloat;
3404 }
3405 return eFormatDefault;
3406 }
3407
3408 bool
3409 DisplayRowObject (Window &window,
3410 Row &row,
3411 DisplayOptions &options,
3412 bool highlight,
3413 bool last_child)
3414 {
3415 ValueObject *valobj = row.valobj.get();
3416
3417 if (valobj == NULL)
3418 return false;
3419
3420 const char *type_name = options.show_types ? valobj->GetTypeName().GetCString() : NULL;
3421 const char *name = valobj->GetName().GetCString();
3422 const char *value = valobj->GetValueAsCString ();
3423 const char *summary = valobj->GetSummaryAsCString ();
3424
3425 window.MoveCursor (row.x, row.y);
3426
3427 row.DrawTree (window);
3428
3429 if (highlight)
3430 window.AttributeOn(A_REVERSE);
3431
3432 if (type_name && type_name[0])
3433 window.Printf ("(%s) ", type_name);
3434
3435 if (name && name[0])
3436 window.PutCString(name);
3437
3438 attr_t changd_attr = 0;
3439 if (valobj->GetValueDidChange())
3440 changd_attr = COLOR_PAIR(5) | A_BOLD;
3441
3442 if (value && value[0])
3443 {
3444 window.PutCString(" = ");
3445 if (changd_attr)
3446 window.AttributeOn(changd_attr);
3447 window.PutCString (value);
3448 if (changd_attr)
3449 window.AttributeOff(changd_attr);
3450 }
3451
3452 if (summary && summary[0])
3453 {
3454 window.PutChar(' ');
3455 if (changd_attr)
3456 window.AttributeOn(changd_attr);
3457 window.PutCString(summary);
3458 if (changd_attr)
3459 window.AttributeOff(changd_attr);
3460 }
3461
3462 if (highlight)
3463 window.AttributeOff (A_REVERSE);
3464
3465 return true;
3466 }
3467 void
3468 DisplayRows (Window &window,
3469 std::vector<Row> &rows,
3470 DisplayOptions &options)
3471 {
3472 // > 0x25B7
3473 // \/ 0x25BD
3474
3475 bool window_is_active = window.IsActive();
3476 for (auto &row : rows)
3477 {
3478 const bool last_child = row.parent && &rows[rows.size()-1] == &row;
3479 // Save the row index in each Row structure
3480 row.row_idx = m_num_rows;
3481 if ((m_num_rows >= m_first_visible_row) &&
3482 ((m_num_rows - m_first_visible_row) < NumVisibleRows()))
3483 {
3484 row.x = m_min_x;
3485 row.y = m_num_rows - m_first_visible_row + 1;
3486 if (DisplayRowObject (window,
3487 row,
3488 options,
3489 window_is_active && m_num_rows == m_selected_row_idx,
3490 last_child))
3491 {
3492 ++m_num_rows;
3493 }
3494 else
3495 {
3496 row.x = 0;
3497 row.y = 0;
3498 }
3499 }
3500 else
3501 {
3502 row.x = 0;
3503 row.y = 0;
3504 ++m_num_rows;
3505 }
3506
3507 if (row.expanded && !row.children.empty())
3508 {
3509 DisplayRows (window,
3510 row.children,
3511 options);
3512 }
3513 }
3514 }
3515
3516 int
3517 CalculateTotalNumberRows (const std::vector<Row> &rows)
3518 {
3519 int row_count = 0;
3520 for (const auto &row : rows)
3521 {
3522 ++row_count;
3523 if (row.expanded)
3524 row_count += CalculateTotalNumberRows(row.children);
3525 }
3526 return row_count;
3527 }
3528 static Row *
3529 GetRowForRowIndexImpl (std::vector<Row> &rows, size_t &row_index)
3530 {
3531 for (auto &row : rows)
3532 {
3533 if (row_index == 0)
3534 return &row;
3535 else
3536 {
3537 --row_index;
3538 if (row.expanded && !row.children.empty())
3539 {
3540 Row *result = GetRowForRowIndexImpl (row.children, row_index);
3541 if (result)
3542 return result;
3543 }
3544 }
3545 }
3546 return NULL;
3547 }
3548
3549 Row *
3550 GetRowForRowIndex (size_t row_index)
3551 {
3552 return GetRowForRowIndexImpl (m_rows, row_index);
3553 }
3554
3555 int
3556 NumVisibleRows () const
3557 {
3558 return m_max_y - m_min_y;
3559 }
3560
3561 static DisplayOptions g_options;
3562};
3563
3564class FrameVariablesWindowDelegate : public ValueObjectListDelegate
3565{
3566public:
3567 FrameVariablesWindowDelegate (Debugger &debugger) :
3568 ValueObjectListDelegate (),
3569 m_debugger (debugger),
3570 m_frame_block (NULL)
3571 {
3572 }
3573
3574 virtual
3575 ~FrameVariablesWindowDelegate()
3576 {
3577 }
3578
3579 virtual const char *
3580 WindowDelegateGetHelpText ()
3581 {
3582 return "Frame variable window keyboard shortcuts:";
3583 }
3584
3585 virtual bool
3586 WindowDelegateDraw (Window &window, bool force)
3587 {
3588 ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext());
3589 Process *process = exe_ctx.GetProcessPtr();
3590 Block *frame_block = NULL;
3591 StackFrame *frame = NULL;
3592
3593 if (process)
3594 {
3595 StateType state = process->GetState();
3596 if (StateIsStoppedState(state, true))
3597 {
3598 frame = exe_ctx.GetFramePtr();
3599 if (frame)
3600 frame_block = frame->GetFrameBlock ();
3601 }
3602 else if (StateIsRunningState(state))
3603 {
3604 return true; // Don't do any updating when we are running
3605 }
3606 }
3607
3608 ValueObjectList local_values;
3609 if (frame_block)
3610 {
3611 // Only update the variables if they have changed
3612 if (m_frame_block != frame_block)
3613 {
3614 m_frame_block = frame_block;
3615
3616 VariableList *locals = frame->GetVariableList(true);
3617 if (locals)
3618 {
3619 const DynamicValueType use_dynamic = eDynamicDontRunTarget;
3620 const size_t num_locals = locals->GetSize();
3621 for (size_t i=0; i<num_locals; ++i)
3622 local_values.Append(frame->GetValueObjectForFrameVariable (locals->GetVariableAtIndex(i), use_dynamic));
3623 // Update the values
3624 SetValues(local_values);
3625 }
3626 }
3627 }
3628 else
3629 {
3630 m_frame_block = NULL;
3631 // Update the values with an empty list if there is no frame
3632 SetValues(local_values);
3633 }
3634
3635 return ValueObjectListDelegate::WindowDelegateDraw (window, force);
3636
3637 }
3638
3639protected:
3640 Debugger &m_debugger;
3641 Block *m_frame_block;
3642};
3643
3644
3645class RegistersWindowDelegate : public ValueObjectListDelegate
3646{
3647public:
3648 RegistersWindowDelegate (Debugger &debugger) :
3649 ValueObjectListDelegate (),
3650 m_debugger (debugger)
3651 {
3652 }
3653
3654 virtual
3655 ~RegistersWindowDelegate()
3656 {
3657 }
3658
3659 virtual const char *
3660 WindowDelegateGetHelpText ()
3661 {
3662 return "Register window keyboard shortcuts:";
3663 }
3664
3665 virtual bool
3666 WindowDelegateDraw (Window &window, bool force)
3667 {
3668 ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext());
3669 StackFrame *frame = exe_ctx.GetFramePtr();
3670
3671 ValueObjectList value_list;
3672 if (frame)
3673 {
3674 if (frame->GetStackID() != m_stack_id)
3675 {
3676 m_stack_id = frame->GetStackID();
3677 RegisterContextSP reg_ctx (frame->GetRegisterContext());
3678 if (reg_ctx)
3679 {
3680 const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
3681 for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx)
3682 {
3683 value_list.Append(ValueObjectRegisterSet::Create (frame, reg_ctx, set_idx));
3684 }
3685 }
3686 SetValues(value_list);
3687 }
3688 }
3689 else
3690 {
3691 Process *process = exe_ctx.GetProcessPtr();
3692 if (process && process->IsAlive())
3693 return true; // Don't do any updating if we are running
3694 else
3695 {
3696 // Update the values with an empty list if there
3697 // is no process or the process isn't alive anymore
3698 SetValues(value_list);
3699 }
3700 }
3701 return ValueObjectListDelegate::WindowDelegateDraw (window, force);
3702 }
3703
3704protected:
3705 Debugger &m_debugger;
3706 StackID m_stack_id;
3707};
3708
3709static const char *
3710CursesKeyToCString (int ch)
3711{
3712 static char g_desc[32];
3713 if (ch >= KEY_F0 && ch < KEY_F0 + 64)
3714 {
3715 snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0);
3716 return g_desc;
3717 }
3718 switch (ch)
3719 {
3720 case KEY_DOWN: return "down";
3721 case KEY_UP: return "up";
3722 case KEY_LEFT: return "left";
3723 case KEY_RIGHT: return "right";
3724 case KEY_HOME: return "home";
3725 case KEY_BACKSPACE: return "backspace";
3726 case KEY_DL: return "delete-line";
3727 case KEY_IL: return "insert-line";
3728 case KEY_DC: return "delete-char";
3729 case KEY_IC: return "insert-char";
3730 case KEY_CLEAR: return "clear";
3731 case KEY_EOS: return "clear-to-eos";
3732 case KEY_EOL: return "clear-to-eol";
3733 case KEY_SF: return "scroll-forward";
3734 case KEY_SR: return "scroll-backward";
3735 case KEY_NPAGE: return "page-down";
3736 case KEY_PPAGE: return "page-up";
3737 case KEY_STAB: return "set-tab";
3738 case KEY_CTAB: return "clear-tab";
3739 case KEY_CATAB: return "clear-all-tabs";
3740 case KEY_ENTER: return "enter";
3741 case KEY_PRINT: return "print";
3742 case KEY_LL: return "lower-left key";
3743 case KEY_A1: return "upper left of keypad";
3744 case KEY_A3: return "upper right of keypad";
3745 case KEY_B2: return "center of keypad";
3746 case KEY_C1: return "lower left of keypad";
3747 case KEY_C3: return "lower right of keypad";
3748 case KEY_BTAB: return "back-tab key";
3749 case KEY_BEG: return "begin key";
3750 case KEY_CANCEL: return "cancel key";
3751 case KEY_CLOSE: return "close key";
3752 case KEY_COMMAND: return "command key";
3753 case KEY_COPY: return "copy key";
3754 case KEY_CREATE: return "create key";
3755 case KEY_END: return "end key";
3756 case KEY_EXIT: return "exit key";
3757 case KEY_FIND: return "find key";
3758 case KEY_HELP: return "help key";
3759 case KEY_MARK: return "mark key";
3760 case KEY_MESSAGE: return "message key";
3761 case KEY_MOVE: return "move key";
3762 case KEY_NEXT: return "next key";
3763 case KEY_OPEN: return "open key";
3764 case KEY_OPTIONS: return "options key";
3765 case KEY_PREVIOUS: return "previous key";
3766 case KEY_REDO: return "redo key";
3767 case KEY_REFERENCE: return "reference key";
3768 case KEY_REFRESH: return "refresh key";
3769 case KEY_REPLACE: return "replace key";
3770 case KEY_RESTART: return "restart key";
3771 case KEY_RESUME: return "resume key";
3772 case KEY_SAVE: return "save key";
3773 case KEY_SBEG: return "shifted begin key";
3774 case KEY_SCANCEL: return "shifted cancel key";
3775 case KEY_SCOMMAND: return "shifted command key";
3776 case KEY_SCOPY: return "shifted copy key";
3777 case KEY_SCREATE: return "shifted create key";
3778 case KEY_SDC: return "shifted delete-character key";
3779 case KEY_SDL: return "shifted delete-line key";
3780 case KEY_SELECT: return "select key";
3781 case KEY_SEND: return "shifted end key";
3782 case KEY_SEOL: return "shifted clear-to-end-of-line key";
3783 case KEY_SEXIT: return "shifted exit key";
3784 case KEY_SFIND: return "shifted find key";
3785 case KEY_SHELP: return "shifted help key";
3786 case KEY_SHOME: return "shifted home key";
3787 case KEY_SIC: return "shifted insert-character key";
3788 case KEY_SLEFT: return "shifted left-arrow key";
3789 case KEY_SMESSAGE: return "shifted message key";
3790 case KEY_SMOVE: return "shifted move key";
3791 case KEY_SNEXT: return "shifted next key";
3792 case KEY_SOPTIONS: return "shifted options key";
3793 case KEY_SPREVIOUS: return "shifted previous key";
3794 case KEY_SPRINT: return "shifted print key";
3795 case KEY_SREDO: return "shifted redo key";
3796 case KEY_SREPLACE: return "shifted replace key";
3797 case KEY_SRIGHT: return "shifted right-arrow key";
3798 case KEY_SRSUME: return "shifted resume key";
3799 case KEY_SSAVE: return "shifted save key";
3800 case KEY_SSUSPEND: return "shifted suspend key";
3801 case KEY_SUNDO: return "shifted undo key";
3802 case KEY_SUSPEND: return "suspend key";
3803 case KEY_UNDO: return "undo key";
3804 case KEY_MOUSE: return "Mouse event has occurred";
3805 case KEY_RESIZE: return "Terminal resize event";
3806 case KEY_EVENT: return "We were interrupted by an event";
3807 case KEY_RETURN: return "return";
3808 case ' ': return "space";
3809 case KEY_ESCAPE: return "escape";
3810 default:
3811 if (isprint(ch))
3812 snprintf(g_desc, sizeof(g_desc), "%c", ch);
3813 else
3814 snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch);
3815 return g_desc;
3816 }
3817 return NULL;
3818}
3819
3820HelpDialogDelegate::HelpDialogDelegate (const char *text, KeyHelp *key_help_array) :
3821 m_text (),
3822 m_first_visible_line (0)
3823{
3824 if (text && text[0])
3825 {
3826 m_text.SplitIntoLines(text);
3827 m_text.AppendString("");
3828 }
3829 if (key_help_array)
3830 {
3831 for (KeyHelp *key = key_help_array; key->ch; ++key)
3832 {
3833 StreamString key_description;
3834 key_description.Printf("%10s - %s", CursesKeyToCString(key->ch), key->description);
3835 m_text.AppendString(std::move(key_description.GetString()));
3836 }
3837 }
3838}
3839
3840HelpDialogDelegate::~HelpDialogDelegate()
3841{
3842}
3843
3844bool
3845HelpDialogDelegate::WindowDelegateDraw (Window &window, bool force)
3846{
3847 window.Erase();
3848 const int window_height = window.GetHeight();
3849 int x = 2;
3850 int y = 1;
3851 const int min_y = y;
3852 const int max_y = window_height - 1 - y;
3853 const int num_visible_lines = max_y - min_y + 1;
3854 const size_t num_lines = m_text.GetSize();
3855 const char *bottom_message;
3856 if (num_lines <= num_visible_lines)
3857 bottom_message = "Press any key to exit";
3858 else
3859 bottom_message = "Use arrows to scroll, any other key to exit";
3860 window.DrawTitleBox(window.GetName(), bottom_message);
3861 while (y <= max_y)
3862 {
3863 window.MoveCursor(x, y);
3864 window.PutCStringTruncated(m_text.GetStringAtIndex(m_first_visible_line + y - min_y), 1);
3865 ++y;
3866 }
3867 return true;
3868}
3869
3870HandleCharResult
3871HelpDialogDelegate::WindowDelegateHandleChar (Window &window, int key)
3872{
3873 bool done = false;
3874 const size_t num_lines = m_text.GetSize();
3875 const size_t num_visible_lines = window.GetHeight() - 2;
3876
3877 if (num_lines <= num_visible_lines)
3878 {
3879 done = true;
3880 // If we have all lines visible and don't need scrolling, then any
3881 // key press will cause us to exit
3882 }
3883 else
3884 {
3885 switch (key)
3886 {
3887 case KEY_UP:
3888 if (m_first_visible_line > 0)
3889 --m_first_visible_line;
3890 break;
3891
3892 case KEY_DOWN:
3893 if (m_first_visible_line + num_visible_lines < num_lines)
3894 ++m_first_visible_line;
3895 break;
3896
3897 case KEY_PPAGE:
3898 case ',':
3899 if (m_first_visible_line > 0)
3900 {
3901 if (m_first_visible_line >= num_visible_lines)
3902 m_first_visible_line -= num_visible_lines;
3903 else
3904 m_first_visible_line = 0;
3905 }
3906 break;
3907 case KEY_NPAGE:
3908 case '.':
3909 if (m_first_visible_line + num_visible_lines < num_lines)
3910 {
3911 m_first_visible_line += num_visible_lines;
3912 if (m_first_visible_line > num_lines)
3913 m_first_visible_line = num_lines - num_visible_lines;
3914 }
3915 break;
3916 default:
3917 done = true;
3918 break;
3919 }
3920 }
3921 if (done)
3922 window.GetParent()->RemoveSubWindow(&window);
3923 return eKeyHandled;
3924}
3925
3926class ApplicationDelegate :
3927 public WindowDelegate,
3928 public MenuDelegate
3929{
3930public:
3931 enum {
3932 eMenuID_LLDB = 1,
3933 eMenuID_LLDBAbout,
3934 eMenuID_LLDBExit,
3935
3936 eMenuID_Target,
3937 eMenuID_TargetCreate,
3938 eMenuID_TargetDelete,
3939
3940 eMenuID_Process,
3941 eMenuID_ProcessAttach,
3942 eMenuID_ProcessDetach,
3943 eMenuID_ProcessLaunch,
3944 eMenuID_ProcessContinue,
3945 eMenuID_ProcessHalt,
3946 eMenuID_ProcessKill,
3947
3948 eMenuID_Thread,
3949 eMenuID_ThreadStepIn,
3950 eMenuID_ThreadStepOver,
3951 eMenuID_ThreadStepOut,
3952
3953 eMenuID_View,
3954 eMenuID_ViewBacktrace,
3955 eMenuID_ViewRegisters,
3956 eMenuID_ViewSource,
3957 eMenuID_ViewVariables,
3958
3959 eMenuID_Help,
3960 eMenuID_HelpGUIHelp
3961 };
3962
3963 ApplicationDelegate (Application &app, Debugger &debugger) :
3964 WindowDelegate (),
3965 MenuDelegate (),
3966 m_app (app),
3967 m_debugger (debugger)
3968 {
3969 }
3970
3971 virtual
3972 ~ApplicationDelegate ()
3973 {
3974 }
3975 virtual bool
3976 WindowDelegateDraw (Window &window, bool force)
3977 {
3978 return false; // Drawing not handled, let standard window drawing happen
3979 }
3980
3981 virtual HandleCharResult
3982 WindowDelegateHandleChar (Window &window, int key)
3983 {
3984 if (key == '\t')
3985 {
3986 window.SelectNextWindowAsActive();
3987 return eKeyHandled;
3988 }
3989 return eKeyNotHandled;
3990 }
3991
3992 virtual MenuActionResult
3993 MenuDelegateAction (Menu &menu)
3994 {
3995 switch (menu.GetIdentifier())
3996 {
3997 case eMenuID_ThreadStepIn:
3998 {
3999 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4000 if (exe_ctx.HasThreadScope())
4001 {
4002 Process *process = exe_ctx.GetProcessPtr();
4003 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4004 exe_ctx.GetThreadRef().StepIn(true, true);
4005 }
4006 }
4007 return MenuActionResult::Handled;
4008
4009 case eMenuID_ThreadStepOut:
4010 {
4011 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4012 if (exe_ctx.HasThreadScope())
4013 {
4014 Process *process = exe_ctx.GetProcessPtr();
4015 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4016 exe_ctx.GetThreadRef().StepOut();
4017 }
4018 }
4019 return MenuActionResult::Handled;
4020
4021 case eMenuID_ThreadStepOver:
4022 {
4023 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4024 if (exe_ctx.HasThreadScope())
4025 {
4026 Process *process = exe_ctx.GetProcessPtr();
4027 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4028 exe_ctx.GetThreadRef().StepOver(true);
4029 }
4030 }
4031 return MenuActionResult::Handled;
4032
4033 case eMenuID_ProcessContinue:
4034 {
4035 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4036 if (exe_ctx.HasProcessScope())
4037 {
4038 Process *process = exe_ctx.GetProcessPtr();
4039 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4040 process->Resume();
4041 }
4042 }
4043 return MenuActionResult::Handled;
4044
4045 case eMenuID_ProcessKill:
4046 {
4047 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4048 if (exe_ctx.HasProcessScope())
4049 {
4050 Process *process = exe_ctx.GetProcessPtr();
4051 if (process && process->IsAlive())
4052 process->Destroy();
4053 }
4054 }
4055 return MenuActionResult::Handled;
4056
4057 case eMenuID_ProcessHalt:
4058 {
4059 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4060 if (exe_ctx.HasProcessScope())
4061 {
4062 Process *process = exe_ctx.GetProcessPtr();
4063 if (process && process->IsAlive())
4064 process->Halt();
4065 }
4066 }
4067 return MenuActionResult::Handled;
4068
4069 case eMenuID_ProcessDetach:
4070 {
4071 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4072 if (exe_ctx.HasProcessScope())
4073 {
4074 Process *process = exe_ctx.GetProcessPtr();
4075 if (process && process->IsAlive())
4076 process->Detach(false);
4077 }
4078 }
4079 return MenuActionResult::Handled;
4080
4081 case eMenuID_Process:
4082 {
4083 // Populate the menu with all of the threads if the process is stopped when
4084 // the Process menu gets selected and is about to display its submenu.
4085 Menus &submenus = menu.GetSubmenus();
4086 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4087 Process *process = exe_ctx.GetProcessPtr();
4088 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4089 {
4090 if (submenus.size() == 7)
4091 menu.AddSubmenu (MenuSP (new Menu(Menu::Type::Separator)));
4092 else if (submenus.size() > 8)
4093 submenus.erase (submenus.begin() + 8, submenus.end());
4094
4095 ThreadList &threads = process->GetThreadList();
4096 Mutex::Locker locker (threads.GetMutex());
4097 size_t num_threads = threads.GetSize();
4098 for (size_t i=0; i<num_threads; ++i)
4099 {
4100 ThreadSP thread_sp = threads.GetThreadAtIndex(i);
4101 char menu_char = '\0';
4102 if (i < 9)
4103 menu_char = '1' + i;
4104 StreamString thread_menu_title;
4105 thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID());
4106 const char *thread_name = thread_sp->GetName();
4107 if (thread_name && thread_name[0])
4108 thread_menu_title.Printf (" %s", thread_name);
4109 else
4110 {
4111 const char *queue_name = thread_sp->GetQueueName();
4112 if (queue_name && queue_name[0])
4113 thread_menu_title.Printf (" %s", queue_name);
4114 }
4115 menu.AddSubmenu (MenuSP (new Menu(thread_menu_title.GetString().c_str(), NULL, menu_char, thread_sp->GetID())));
4116 }
4117 }
4118 else if (submenus.size() > 7)
4119 {
4120 // Remove the separator and any other thread submenu items
4121 // that were previously added
4122 submenus.erase (submenus.begin() + 7, submenus.end());
4123 }
4124 // Since we are adding and removing items we need to recalculate the name lengths
4125 menu.RecalculateNameLengths();
4126 }
4127 return MenuActionResult::Handled;
4128
4129 case eMenuID_ViewVariables:
4130 {
4131 WindowSP main_window_sp = m_app.GetMainWindow();
4132 WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
4133 WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
4134 WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
4135 const Rect source_bounds = source_window_sp->GetBounds();
4136
4137 if (variables_window_sp)
4138 {
4139 const Rect variables_bounds = variables_window_sp->GetBounds();
4140
4141 main_window_sp->RemoveSubWindow(variables_window_sp.get());
4142
4143 if (registers_window_sp)
4144 {
4145 // We have a registers window, so give all the area back to the registers window
4146 Rect registers_bounds = variables_bounds;
4147 registers_bounds.size.width = source_bounds.size.width;
4148 registers_window_sp->SetBounds(registers_bounds);
4149 }
4150 else
4151 {
4152 // We have no registers window showing so give the bottom
4153 // area back to the source view
4154 source_window_sp->Resize (source_bounds.size.width,
4155 source_bounds.size.height + variables_bounds.size.height);
4156 }
4157 }
4158 else
4159 {
4160 Rect new_variables_rect;
4161 if (registers_window_sp)
4162 {
4163 // We have a registers window so split the area of the registers
4164 // window into two columns where the left hand side will be the
4165 // variables and the right hand side will be the registers
4166 const Rect variables_bounds = registers_window_sp->GetBounds();
4167 Rect new_registers_rect;
4168 variables_bounds.VerticalSplitPercentage (0.50, new_variables_rect, new_registers_rect);
4169 registers_window_sp->SetBounds (new_registers_rect);
4170 }
4171 else
4172 {
4173 // No variables window, grab the bottom part of the source window
4174 Rect new_source_rect;
4175 source_bounds.HorizontalSplitPercentage (0.70, new_source_rect, new_variables_rect);
4176 source_window_sp->SetBounds (new_source_rect);
4177 }
4178 WindowSP new_window_sp = main_window_sp->CreateSubWindow ("Variables",
4179 new_variables_rect,
4180 false);
4181 new_window_sp->SetDelegate (WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
4182 }
4183 touchwin(stdscr);
4184 }
4185 return MenuActionResult::Handled;
4186
4187 case eMenuID_ViewRegisters:
4188 {
4189 WindowSP main_window_sp = m_app.GetMainWindow();
4190 WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
4191 WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
4192 WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
4193 const Rect source_bounds = source_window_sp->GetBounds();
4194
4195 if (registers_window_sp)
4196 {
4197 if (variables_window_sp)
4198 {
4199 const Rect variables_bounds = variables_window_sp->GetBounds();
4200
4201 // We have a variables window, so give all the area back to the variables window
4202 variables_window_sp->Resize (variables_bounds.size.width + registers_window_sp->GetWidth(),
4203 variables_bounds.size.height);
4204 }
4205 else
4206 {
4207 // We have no variables window showing so give the bottom
4208 // area back to the source view
4209 source_window_sp->Resize (source_bounds.size.width,
4210 source_bounds.size.height + registers_window_sp->GetHeight());
4211 }
4212 main_window_sp->RemoveSubWindow(registers_window_sp.get());
4213 }
4214 else
4215 {
4216 Rect new_regs_rect;
4217 if (variables_window_sp)
4218 {
4219 // We have a variables window, split it into two columns
4220 // where the left hand side will be the variables and the
4221 // right hand side will be the registers
4222 const Rect variables_bounds = variables_window_sp->GetBounds();
4223 Rect new_vars_rect;
4224 variables_bounds.VerticalSplitPercentage (0.50, new_vars_rect, new_regs_rect);
4225 variables_window_sp->SetBounds (new_vars_rect);
4226 }
4227 else
4228 {
4229 // No registers window, grab the bottom part of the source window
4230 Rect new_source_rect;
4231 source_bounds.HorizontalSplitPercentage (0.70, new_source_rect, new_regs_rect);
4232 source_window_sp->SetBounds (new_source_rect);
4233 }
4234 WindowSP new_window_sp = main_window_sp->CreateSubWindow ("Registers",
4235 new_regs_rect,
4236 false);
4237 new_window_sp->SetDelegate (WindowDelegateSP(new RegistersWindowDelegate(m_debugger)));
4238 }
4239 touchwin(stdscr);
4240 }
4241 return MenuActionResult::Handled;
4242
4243 case eMenuID_HelpGUIHelp:
4244 return MenuActionResult::Handled;
4245
4246 default:
4247 break;
4248 }
4249
4250 return MenuActionResult::NotHandled;
4251 }
4252protected:
4253 Application &m_app;
4254 Debugger &m_debugger;
4255};
4256
4257
4258class StatusBarWindowDelegate : public WindowDelegate
4259{
4260public:
4261 StatusBarWindowDelegate (Debugger &debugger) :
4262 m_debugger (debugger)
4263 {
4264 }
4265
4266 virtual
4267 ~StatusBarWindowDelegate ()
4268 {
4269 }
4270 virtual bool
4271 WindowDelegateDraw (Window &window, bool force)
4272 {
4273 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4274 Process *process = exe_ctx.GetProcessPtr();
4275 Thread *thread = exe_ctx.GetThreadPtr();
4276 StackFrame *frame = exe_ctx.GetFramePtr();
4277 window.Erase();
4278 window.SetBackground(2);
4279 window.MoveCursor (0, 0);
4280 if (process)
4281 {
4282 const StateType state = process->GetState();
4283 window.Printf ("Process: %5" PRIu64 " %10s", process->GetID(), StateAsCString(state));
4284
4285 if (StateIsStoppedState(state, true))
4286 {
4287 window.MoveCursor (40, 0);
4288 if (thread)
4289 window.Printf ("Thread: 0x%4.4" PRIx64, thread->GetID());
4290
4291 window.MoveCursor (60, 0);
4292 if (frame)
4293 window.Printf ("Frame: %3u PC = 0x%16.16" PRIx64, frame->GetFrameIndex(), frame->GetFrameCodeAddress().GetOpcodeLoadAddress (exe_ctx.GetTargetPtr()));
4294 }
4295 else if (state == eStateExited)
4296 {
4297 const char *exit_desc = process->GetExitDescription();
4298 const int exit_status = process->GetExitStatus();
4299 if (exit_desc && exit_desc[0])
4300 window.Printf (" with status = %i (%s)", exit_status, exit_desc);
4301 else
4302 window.Printf (" with status = %i", exit_status);
4303 }
4304 }
4305 window.DeferredRefresh();
4306 return true;
4307 }
4308
4309protected:
4310 Debugger &m_debugger;
4311};
4312
4313class SourceFileWindowDelegate : public WindowDelegate
4314{
4315public:
4316 SourceFileWindowDelegate (Debugger &debugger) :
4317 WindowDelegate (),
4318 m_debugger (debugger),
4319 m_sc (),
4320 m_file_sp (),
4321 m_disassembly_scope (NULL),
4322 m_disassembly_sp (),
4323 m_disassembly_range (),
4324 m_line_width (4),
4325 m_selected_line (0),
4326 m_pc_line (0),
4327 m_stop_id (0),
4328 m_frame_idx (UINT32_MAX),
4329 m_first_visible_line (0),
4330 m_min_x (0),
4331 m_min_y (0),
4332 m_max_x (0),
4333 m_max_y (0)
4334 {
4335 }
4336
4337
4338 virtual
4339 ~SourceFileWindowDelegate()
4340 {
4341 }
4342
4343 void
4344 Update (const SymbolContext &sc)
4345 {
4346 m_sc = sc;
4347 }
4348
4349 uint32_t
4350 NumVisibleLines () const
4351 {
4352 return m_max_y - m_min_y;
4353 }
4354
4355 virtual const char *
4356 WindowDelegateGetHelpText ()
4357 {
4358 return "Source/Disassembly window keyboard shortcuts:";
4359 }
4360
4361 virtual KeyHelp *
4362 WindowDelegateGetKeyHelp ()
4363 {
4364 static curses::KeyHelp g_source_view_key_help[] = {
4365 { KEY_RETURN, "Run to selected line with one shot breakpoint" },
4366 { KEY_UP, "Select previous source line" },
4367 { KEY_DOWN, "Select next source line" },
4368 { KEY_PPAGE, "Page up" },
4369 { KEY_NPAGE, "Page down" },
4370 { 'b', "Set breakpoint on selected source/disassembly line" },
4371 { 'c', "Continue process" },
4372 { 'd', "Detach and resume process" },
4373 { 'D', "Detach with process suspended" },
4374 { 'h', "Show help dialog" },
4375 { 'k', "Kill process" },
4376 { 'n', "Step over (source line)" },
4377 { 'N', "Step over (single instruction)" },
4378 { 'o', "Step out" },
4379 { 's', "Step in (source line)" },
4380 { 'S', "Step in (single instruction)" },
4381 { ',', "Page up" },
4382 { '.', "Page down" },
4383 { '\0', NULL }
4384 };
4385 return g_source_view_key_help;
4386 }
4387
4388 virtual bool
4389 WindowDelegateDraw (Window &window, bool force)
4390 {
4391 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4392 Process *process = exe_ctx.GetProcessPtr();
4393 Thread *thread = NULL;
4394
4395 bool update_location = false;
4396 if (process)
4397 {
4398 StateType state = process->GetState();
4399 if (StateIsStoppedState(state, true))
4400 {
4401 // We are stopped, so it is ok to
4402 update_location = true;
4403 }
4404 }
4405
4406 m_min_x = 1;
4407 m_min_y = 1;
4408 m_max_x = window.GetMaxX()-1;
4409 m_max_y = window.GetMaxY()-1;
4410
4411 const uint32_t num_visible_lines = NumVisibleLines();
4412 StackFrameSP frame_sp;
4413 bool set_selected_line_to_pc = false;
4414
4415
4416 if (update_location)
4417 {
4418
4419 const bool process_alive = process ? process->IsAlive() : false;
4420 bool thread_changed = false;
4421 if (process_alive)
4422 {
4423 thread = exe_ctx.GetThreadPtr();
4424 if (thread)
4425 {
4426 frame_sp = thread->GetSelectedFrame();
4427 auto tid = thread->GetID();
4428 thread_changed = tid != m_tid;
4429 m_tid = tid;
4430 }
4431 else
4432 {
4433 if (m_tid != LLDB_INVALID_THREAD_ID)
4434 {
4435 thread_changed = true;
4436 m_tid = LLDB_INVALID_THREAD_ID;
4437 }
4438 }
4439 }
4440 const uint32_t stop_id = process ? process->GetStopID() : 0;
4441 const bool stop_id_changed = stop_id != m_stop_id;
4442 bool frame_changed = false;
4443 m_stop_id = stop_id;
4444 if (frame_sp)
4445 {
4446 m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
4447 const uint32_t frame_idx = frame_sp->GetFrameIndex();
4448 frame_changed = frame_idx != m_frame_idx;
4449 m_frame_idx = frame_idx;
4450 }
4451 else
4452 {
4453 m_sc.Clear(true);
4454 frame_changed = m_frame_idx != UINT32_MAX;
4455 m_frame_idx = UINT32_MAX;
4456 }
4457
4458 const bool context_changed = thread_changed || frame_changed || stop_id_changed;
4459
4460 if (process_alive)
4461 {
4462 if (m_sc.line_entry.IsValid())
4463 {
4464 m_pc_line = m_sc.line_entry.line;
4465 if (m_pc_line != UINT32_MAX)
4466 --m_pc_line; // Convert to zero based line number...
4467 // Update the selected line if the stop ID changed...
4468 if (context_changed)
4469 m_selected_line = m_pc_line;
4470
4471 if (m_file_sp && m_file_sp->FileSpecMatches(m_sc.line_entry.file))
4472 {
4473 // Same file, nothing to do, we should either have the
4474 // lines or not (source file missing)
4475 if (m_selected_line >= m_first_visible_line)
4476 {
4477 if (m_selected_line >= m_first_visible_line + num_visible_lines)
4478 m_first_visible_line = m_selected_line - 10;
4479 }
4480 else
4481 {
4482 if (m_selected_line > 10)
4483 m_first_visible_line = m_selected_line - 10;
4484 else
4485 m_first_visible_line = 0;
4486 }
4487 }
4488 else
4489 {
4490 // File changed, set selected line to the line with the PC
4491 m_selected_line = m_pc_line;
4492 m_file_sp = m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file);
4493 if (m_file_sp)
4494 {
4495 const size_t num_lines = m_file_sp->GetNumLines();
4496 int m_line_width = 1;
4497 for (size_t n = num_lines; n >= 10; n = n / 10)
4498 ++m_line_width;
4499
4500 snprintf (m_line_format, sizeof(m_line_format), " %%%iu ", m_line_width);
4501 if (num_lines < num_visible_lines || m_selected_line < num_visible_lines)
4502 m_first_visible_line = 0;
4503 else
4504 m_first_visible_line = m_selected_line - 10;
4505 }
4506 }
4507 }
4508 else
4509 {
4510 m_file_sp.reset();
4511 }
4512
4513 if (!m_file_sp || m_file_sp->GetNumLines() == 0)
4514 {
4515 // Show disassembly
4516 bool prefer_file_cache = false;
4517 if (m_sc.function)
4518 {
4519 if (m_disassembly_scope != m_sc.function)
4520 {
4521 m_disassembly_scope = m_sc.function;
4522 m_disassembly_sp = m_sc.function->GetInstructions (exe_ctx, NULL, prefer_file_cache);
4523 if (m_disassembly_sp)
4524 {
4525 set_selected_line_to_pc = true;
4526 m_disassembly_range = m_sc.function->GetAddressRange();
4527 }
4528 else
4529 {
4530 m_disassembly_range.Clear();
4531 }
4532 }
4533 else
4534 {
4535 set_selected_line_to_pc = context_changed;
4536 }
4537 }
4538 else if (m_sc.symbol)
4539 {
4540 if (m_disassembly_scope != m_sc.symbol)
4541 {
4542 m_disassembly_scope = m_sc.symbol;
4543 m_disassembly_sp = m_sc.symbol->GetInstructions (exe_ctx, NULL, prefer_file_cache);
4544 if (m_disassembly_sp)
4545 {
4546 set_selected_line_to_pc = true;
4547 m_disassembly_range.GetBaseAddress() = m_sc.symbol->GetAddress();
4548 m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize());
4549 }
4550 else
4551 {
4552 m_disassembly_range.Clear();
4553 }
4554 }
4555 else
4556 {
4557 set_selected_line_to_pc = context_changed;
4558 }
4559 }
4560 }
4561 }
4562 else
4563 {
4564 m_pc_line = UINT32_MAX;
4565 }
4566 }
4567
4568
4569 window.Erase();
4570 window.DrawTitleBox ("Sources");
4571
4572
4573 Target *target = exe_ctx.GetTargetPtr();
4574 const size_t num_source_lines = GetNumSourceLines();
4575 if (num_source_lines > 0)
4576 {
4577 // Display source
4578 BreakpointLines bp_lines;
4579 if (target)
4580 {
4581 BreakpointList &bp_list = target->GetBreakpointList();
4582 const size_t num_bps = bp_list.GetSize();
4583 for (size_t bp_idx=0; bp_idx<num_bps; ++bp_idx)
4584 {
4585 BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
4586 const size_t num_bps_locs = bp_sp->GetNumLocations();
4587 for (size_t bp_loc_idx=0; bp_loc_idx<num_bps_locs; ++bp_loc_idx)
4588 {
4589 BreakpointLocationSP bp_loc_sp = bp_sp->GetLocationAtIndex(bp_loc_idx);
4590 LineEntry bp_loc_line_entry;
4591 if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry (bp_loc_line_entry))
4592 {
4593 if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file)
4594 {
4595 bp_lines.insert(bp_loc_line_entry.line);
4596 }
4597 }
4598 }
4599 }
4600 }
4601
4602
4603 const attr_t selected_highlight_attr = A_REVERSE;
4604 const attr_t pc_highlight_attr = COLOR_PAIR(1);
4605
4606 for (int i=0; i<num_visible_lines; ++i)
4607 {
4608 const uint32_t curr_line = m_first_visible_line + i;
4609 if (curr_line < num_source_lines)
4610 {
4611 const int line_y = 1+i;
4612 window.MoveCursor(1, line_y);
4613 const bool is_pc_line = curr_line == m_pc_line;
4614 const bool line_is_selected = m_selected_line == curr_line;
4615 // Highlight the line as the PC line first, then if the selected line
4616 // isn't the same as the PC line, highlight it differently
4617 attr_t highlight_attr = 0;
4618 attr_t bp_attr = 0;
4619 if (is_pc_line)
4620 highlight_attr = pc_highlight_attr;
4621 else if (line_is_selected)
4622 highlight_attr = selected_highlight_attr;
4623
4624 if (bp_lines.find(curr_line+1) != bp_lines.end())
4625 bp_attr = COLOR_PAIR(2);
4626
4627 if (bp_attr)
4628 window.AttributeOn(bp_attr);
4629
4630 window.Printf (m_line_format, curr_line + 1);
4631
4632 if (bp_attr)
4633 window.AttributeOff(bp_attr);
4634
4635 window.PutChar(ACS_VLINE);
4636 // Mark the line with the PC with a diamond
4637 if (is_pc_line)
4638 window.PutChar(ACS_DIAMOND);
4639 else
4640 window.PutChar(' ');
4641
4642 if (highlight_attr)
4643 window.AttributeOn(highlight_attr);
4644 const uint32_t line_len = m_file_sp->GetLineLength(curr_line + 1, false);
4645 if (line_len > 0)
4646 window.PutCString(m_file_sp->PeekLineData(curr_line + 1), line_len);
4647
4648 if (is_pc_line && frame_sp && frame_sp->GetConcreteFrameIndex() == 0)
4649 {
4650 StopInfoSP stop_info_sp;
4651 if (thread)
4652 stop_info_sp = thread->GetStopInfo();
4653 if (stop_info_sp)
4654 {
4655 const char *stop_description = stop_info_sp->GetDescription();
4656 if (stop_description && stop_description[0])
4657 {
4658 size_t stop_description_len = strlen(stop_description);
4659 int desc_x = window.GetWidth() - stop_description_len - 16;
4660 window.Printf ("%*s", desc_x - window.GetCursorX(), "");
4661 //window.MoveCursor(window.GetWidth() - stop_description_len - 15, line_y);
4662 window.Printf ("<<< Thread %u: %s ", thread->GetIndexID(), stop_description);
4663 }
4664 }
4665 else
4666 {
4667 window.Printf ("%*s", window.GetWidth() - window.GetCursorX() - 1, "");
4668 }
4669 }
4670 if (highlight_attr)
4671 window.AttributeOff(highlight_attr);
4672
4673 }
4674 else
4675 {
4676 break;
4677 }
4678 }
4679 }
4680 else
4681 {
4682 size_t num_disassembly_lines = GetNumDisassemblyLines();
4683 if (num_disassembly_lines > 0)
4684 {
4685 // Display disassembly
4686 BreakpointAddrs bp_file_addrs;
4687 Target *target = exe_ctx.GetTargetPtr();
4688 if (target)
4689 {
4690 BreakpointList &bp_list = target->GetBreakpointList();
4691 const size_t num_bps = bp_list.GetSize();
4692 for (size_t bp_idx=0; bp_idx<num_bps; ++bp_idx)
4693 {
4694 BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
4695 const size_t num_bps_locs = bp_sp->GetNumLocations();
4696 for (size_t bp_loc_idx=0; bp_loc_idx<num_bps_locs; ++bp_loc_idx)
4697 {
4698 BreakpointLocationSP bp_loc_sp = bp_sp->GetLocationAtIndex(bp_loc_idx);
4699 LineEntry bp_loc_line_entry;
4700 const lldb::addr_t file_addr = bp_loc_sp->GetAddress().GetFileAddress();
4701 if (file_addr != LLDB_INVALID_ADDRESS)
4702 {
4703 if (m_disassembly_range.ContainsFileAddress(file_addr))
4704 bp_file_addrs.insert(file_addr);
4705 }
4706 }
4707 }
4708 }
4709
4710
4711 const attr_t selected_highlight_attr = A_REVERSE;
4712 const attr_t pc_highlight_attr = COLOR_PAIR(1);
4713
4714 StreamString strm;
4715
4716 InstructionList &insts = m_disassembly_sp->GetInstructionList();
4717 Address pc_address;
4718
4719 if (frame_sp)
4720 pc_address = frame_sp->GetFrameCodeAddress();
4721 const uint32_t pc_idx = pc_address.IsValid() ? insts.GetIndexOfInstructionAtAddress (pc_address) : UINT32_MAX;
4722 if (set_selected_line_to_pc)
4723 {
4724 m_selected_line = pc_idx;
4725 }
4726
4727 const uint32_t non_visible_pc_offset = (num_visible_lines / 5);
4728 if (m_first_visible_line >= num_disassembly_lines)
4729 m_first_visible_line = 0;
4730
4731 if (pc_idx < num_disassembly_lines)
4732 {
4733 if (pc_idx < m_first_visible_line ||
4734 pc_idx >= m_first_visible_line + num_visible_lines)
4735 m_first_visible_line = pc_idx - non_visible_pc_offset;
4736 }
4737
4738 for (size_t i=0; i<num_visible_lines; ++i)
4739 {
4740 const uint32_t inst_idx = m_first_visible_line + i;
4741 Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get();
4742 if (!inst)
4743 break;
4744
4745 window.MoveCursor(1, i+1);
4746 const bool is_pc_line = frame_sp && inst_idx == pc_idx;
4747 const bool line_is_selected = m_selected_line == inst_idx;
4748 // Highlight the line as the PC line first, then if the selected line
4749 // isn't the same as the PC line, highlight it differently
4750 attr_t highlight_attr = 0;
4751 attr_t bp_attr = 0;
4752 if (is_pc_line)
4753 highlight_attr = pc_highlight_attr;
4754 else if (line_is_selected)
4755 highlight_attr = selected_highlight_attr;
4756
4757 if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) != bp_file_addrs.end())
4758 bp_attr = COLOR_PAIR(2);
4759
4760 if (bp_attr)
4761 window.AttributeOn(bp_attr);
4762
4763 window.Printf (" 0x%16.16llx ", inst->GetAddress().GetLoadAddress(target));
4764
4765 if (bp_attr)
4766 window.AttributeOff(bp_attr);
4767
4768 window.PutChar(ACS_VLINE);
4769 // Mark the line with the PC with a diamond
4770 if (is_pc_line)
4771 window.PutChar(ACS_DIAMOND);
4772 else
4773 window.PutChar(' ');
4774
4775 if (highlight_attr)
4776 window.AttributeOn(highlight_attr);
4777
4778 const char *mnemonic = inst->GetMnemonic(&exe_ctx);
4779 const char *operands = inst->GetOperands(&exe_ctx);
4780 const char *comment = inst->GetComment(&exe_ctx);
4781
4782 if (mnemonic && mnemonic[0] == '\0')
4783 mnemonic = NULL;
4784 if (operands && operands[0] == '\0')
4785 operands = NULL;
4786 if (comment && comment[0] == '\0')
4787 comment = NULL;
4788
4789 strm.Clear();
4790
4791 if (mnemonic && operands && comment)
4792 strm.Printf ("%-8s %-25s ; %s", mnemonic, operands, comment);
4793 else if (mnemonic && operands)
4794 strm.Printf ("%-8s %s", mnemonic, operands);
4795 else if (mnemonic)
4796 strm.Printf ("%s", mnemonic);
4797
4798 int right_pad = 1;
4799 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
4800
4801 if (is_pc_line && frame_sp && frame_sp->GetConcreteFrameIndex() == 0)
4802 {
4803 StopInfoSP stop_info_sp;
4804 if (thread)
4805 stop_info_sp = thread->GetStopInfo();
4806 if (stop_info_sp)
4807 {
4808 const char *stop_description = stop_info_sp->GetDescription();
4809 if (stop_description && stop_description[0])
4810 {
4811 size_t stop_description_len = strlen(stop_description);
4812 int desc_x = window.GetWidth() - stop_description_len - 16;
4813 window.Printf ("%*s", desc_x - window.GetCursorX(), "");
4814 //window.MoveCursor(window.GetWidth() - stop_description_len - 15, line_y);
4815 window.Printf ("<<< Thread %u: %s ", thread->GetIndexID(), stop_description);
4816 }
4817 }
4818 else
4819 {
4820 window.Printf ("%*s", window.GetWidth() - window.GetCursorX() - 1, "");
4821 }
4822 }
4823 if (highlight_attr)
4824 window.AttributeOff(highlight_attr);
4825 }
4826 }
4827 }
4828 window.DeferredRefresh();
4829 return true; // Drawing handled
4830 }
4831
4832 size_t
4833 GetNumLines ()
4834 {
4835 size_t num_lines = GetNumSourceLines();
4836 if (num_lines == 0)
4837 num_lines = GetNumDisassemblyLines();
4838 return num_lines;
4839 }
4840
4841 size_t
4842 GetNumSourceLines () const
4843 {
4844 if (m_file_sp)
4845 return m_file_sp->GetNumLines();
4846 return 0;
4847 }
4848 size_t
4849 GetNumDisassemblyLines () const
4850 {
4851 if (m_disassembly_sp)
4852 return m_disassembly_sp->GetInstructionList().GetSize();
4853 return 0;
4854 }
4855
4856 virtual HandleCharResult
4857 WindowDelegateHandleChar (Window &window, int c)
4858 {
4859 const uint32_t num_visible_lines = NumVisibleLines();
4860 const size_t num_lines = GetNumLines ();
4861
4862 switch (c)
4863 {
4864 case ',':
4865 case KEY_PPAGE:
4866 // Page up key
4867 if (m_first_visible_line > num_visible_lines)
4868 m_first_visible_line -= num_visible_lines;
4869 else
4870 m_first_visible_line = 0;
4871 m_selected_line = m_first_visible_line;
4872 return eKeyHandled;
4873
4874 case '.':
4875 case KEY_NPAGE:
4876 // Page down key
4877 {
4878 if (m_first_visible_line + num_visible_lines < num_lines)
4879 m_first_visible_line += num_visible_lines;
4880 else if (num_lines < num_visible_lines)
4881 m_first_visible_line = 0;
4882 else
4883 m_first_visible_line = num_lines - num_visible_lines;
4884 m_selected_line = m_first_visible_line;
4885 }
4886 return eKeyHandled;
4887
4888 case KEY_UP:
4889 if (m_selected_line > 0)
4890 {
4891 m_selected_line--;
4892 if (m_first_visible_line > m_selected_line)
4893 m_first_visible_line = m_selected_line;
4894 }
4895 return eKeyHandled;
4896
4897 case KEY_DOWN:
4898 if (m_selected_line + 1 < num_lines)
4899 {
4900 m_selected_line++;
4901 if (m_first_visible_line + num_visible_lines < m_selected_line)
4902 m_first_visible_line++;
4903 }
4904 return eKeyHandled;
4905
4906 case '\r':
4907 case '\n':
4908 case KEY_ENTER:
4909 // Set a breakpoint and run to the line using a one shot breakpoint
4910 if (GetNumSourceLines() > 0)
4911 {
4912 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4913 if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive())
4914 {
4915 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (NULL, // Don't limit the breakpoint to certain modules
4916 m_file_sp->GetFileSpec(), // Source file
4917 m_selected_line + 1, // Source line number (m_selected_line is zero based)
4918 eLazyBoolCalculate, // Check inlines using global setting
4919 eLazyBoolCalculate, // Skip prologue using global setting,
4920 false, // internal
4921 false); // request_hardware
4922 // Make breakpoint one shot
4923 bp_sp->GetOptions()->SetOneShot(true);
4924 exe_ctx.GetProcessRef().Resume();
4925 }
4926 }
4927 else if (m_selected_line < GetNumDisassemblyLines())
4928 {
4929 const Instruction *inst = m_disassembly_sp->GetInstructionList().GetInstructionAtIndex(m_selected_line).get();
4930 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4931 if (exe_ctx.HasTargetScope())
4932 {
4933 Address addr = inst->GetAddress();
4934 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (addr, // lldb_private::Address
4935 false, // internal
4936 false); // request_hardware
4937 // Make breakpoint one shot
4938 bp_sp->GetOptions()->SetOneShot(true);
4939 exe_ctx.GetProcessRef().Resume();
4940 }
4941 }
4942 return eKeyHandled;
4943
4944 case 'b': // 'b' == toggle breakpoint on currently selected line
4945 if (m_selected_line < GetNumSourceLines())
4946 {
4947 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4948 if (exe_ctx.HasTargetScope())
4949 {
4950 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (NULL, // Don't limit the breakpoint to certain modules
4951 m_file_sp->GetFileSpec(), // Source file
4952 m_selected_line + 1, // Source line number (m_selected_line is zero based)
4953 eLazyBoolCalculate, // Check inlines using global setting
4954 eLazyBoolCalculate, // Skip prologue using global setting,
4955 false, // internal
4956 false); // request_hardware
4957 }
4958 }
4959 else if (m_selected_line < GetNumDisassemblyLines())
4960 {
4961 const Instruction *inst = m_disassembly_sp->GetInstructionList().GetInstructionAtIndex(m_selected_line).get();
4962 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4963 if (exe_ctx.HasTargetScope())
4964 {
4965 Address addr = inst->GetAddress();
4966 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (addr, // lldb_private::Address
4967 false, // internal
4968 false); // request_hardware
4969 }
4970 }
4971 return eKeyHandled;
4972
4973 case 'd': // 'd' == detach and let run
4974 case 'D': // 'D' == detach and keep stopped
4975 {
4976 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4977 if (exe_ctx.HasProcessScope())
4978 exe_ctx.GetProcessRef().Detach(c == 'D');
4979 }
4980 return eKeyHandled;
4981
4982 case 'k':
4983 // 'k' == kill
4984 {
4985 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4986 if (exe_ctx.HasProcessScope())
4987 exe_ctx.GetProcessRef().Destroy();
4988 }
4989 return eKeyHandled;
4990
4991 case 'c':
4992 // 'c' == continue
4993 {
4994 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4995 if (exe_ctx.HasProcessScope())
4996 exe_ctx.GetProcessRef().Resume();
4997 }
4998 return eKeyHandled;
4999
5000 case 'o':
5001 // 'o' == step out
5002 {
5003 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5004 if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true))
5005 {
5006 exe_ctx.GetThreadRef().StepOut();
5007 }
5008 }
5009 return eKeyHandled;
5010 case 'n': // 'n' == step over
5011 case 'N': // 'N' == step over instruction
5012 {
5013 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5014 if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true))
5015 {
5016 bool source_step = (c == 'n');
5017 exe_ctx.GetThreadRef().StepOver(source_step);
5018 }
5019 }
5020 return eKeyHandled;
5021 case 's': // 's' == step into
5022 case 'S': // 'S' == step into instruction
5023 {
5024 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5025 if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true))
5026 {
5027 bool source_step = (c == 's');
5028 bool avoid_code_without_debug_info = true;
5029 exe_ctx.GetThreadRef().StepIn(source_step, avoid_code_without_debug_info);
5030 }
5031 }
5032 return eKeyHandled;
5033
5034 case 'h':
5035 window.CreateHelpSubwindow ();
5036 return eKeyHandled;
5037
5038 default:
5039 break;
5040 }
5041 return eKeyNotHandled;
5042 }
5043
5044protected:
5045 typedef std::set<uint32_t> BreakpointLines;
5046 typedef std::set<lldb::addr_t> BreakpointAddrs;
5047
5048 Debugger &m_debugger;
5049 SymbolContext m_sc;
5050 SourceManager::FileSP m_file_sp;
5051 SymbolContextScope *m_disassembly_scope;
5052 lldb::DisassemblerSP m_disassembly_sp;
5053 AddressRange m_disassembly_range;
5054 lldb::user_id_t m_tid;
5055 char m_line_format[8];
5056 int m_line_width;
5057 uint32_t m_selected_line; // The selected line
5058 uint32_t m_pc_line; // The line with the PC
5059 uint32_t m_stop_id;
5060 uint32_t m_frame_idx;
5061 int m_first_visible_line;
5062 int m_min_x;
5063 int m_min_y;
5064 int m_max_x;
5065 int m_max_y;
5066
5067};
5068
5069DisplayOptions ValueObjectListDelegate::g_options = { true };
5070
5071IOHandlerCursesGUI::IOHandlerCursesGUI (Debugger &debugger) :
5072 IOHandler (debugger)
5073{
5074}
5075
5076void
5077IOHandlerCursesGUI::Activate ()
5078{
5079 IOHandler::Activate();
5080 if (!m_app_ap)
5081 {
5082 m_app_ap.reset (new Application (GetInputFILE(), GetOutputFILE()));
5083
5084
5085 // This is both a window and a menu delegate
5086 std::shared_ptr<ApplicationDelegate> app_delegate_sp(new ApplicationDelegate(*m_app_ap, m_debugger));
5087
5088 MenuDelegateSP app_menu_delegate_sp = std::static_pointer_cast<MenuDelegate>(app_delegate_sp);
5089 MenuSP lldb_menu_sp(new Menu("LLDB" , "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB));
5090 MenuSP exit_menuitem_sp(new Menu("Exit", NULL, 'x', ApplicationDelegate::eMenuID_LLDBExit));
5091 exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit);
5092 lldb_menu_sp->AddSubmenu (MenuSP (new Menu("About LLDB", NULL, 'a', ApplicationDelegate::eMenuID_LLDBAbout)));
5093 lldb_menu_sp->AddSubmenu (MenuSP (new Menu(Menu::Type::Separator)));
5094 lldb_menu_sp->AddSubmenu (exit_menuitem_sp);
5095
5096 MenuSP target_menu_sp(new Menu("Target" ,"F2", KEY_F(2), ApplicationDelegate::eMenuID_Target));
5097 target_menu_sp->AddSubmenu (MenuSP (new Menu("Create", NULL, 'c', ApplicationDelegate::eMenuID_TargetCreate)));
5098 target_menu_sp->AddSubmenu (MenuSP (new Menu("Delete", NULL, 'd', ApplicationDelegate::eMenuID_TargetDelete)));
5099
5100 MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3), ApplicationDelegate::eMenuID_Process));
5101 process_menu_sp->AddSubmenu (MenuSP (new Menu("Attach" , NULL, 'a', ApplicationDelegate::eMenuID_ProcessAttach)));
5102 process_menu_sp->AddSubmenu (MenuSP (new Menu("Detach" , NULL, 'd', ApplicationDelegate::eMenuID_ProcessDetach)));
5103 process_menu_sp->AddSubmenu (MenuSP (new Menu("Launch" , NULL, 'l', ApplicationDelegate::eMenuID_ProcessLaunch)));
5104 process_menu_sp->AddSubmenu (MenuSP (new Menu(Menu::Type::Separator)));
5105 process_menu_sp->AddSubmenu (MenuSP (new Menu("Continue", NULL, 'c', ApplicationDelegate::eMenuID_ProcessContinue)));
5106 process_menu_sp->AddSubmenu (MenuSP (new Menu("Halt" , NULL, 'h', ApplicationDelegate::eMenuID_ProcessHalt)));
5107 process_menu_sp->AddSubmenu (MenuSP (new Menu("Kill" , NULL, 'k', ApplicationDelegate::eMenuID_ProcessKill)));
5108
5109 MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4), ApplicationDelegate::eMenuID_Thread));
5110 thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step In" , NULL, 'i', ApplicationDelegate::eMenuID_ThreadStepIn)));
5111 thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step Over", NULL, 'v', ApplicationDelegate::eMenuID_ThreadStepOver)));
5112 thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step Out" , NULL, 'o', ApplicationDelegate::eMenuID_ThreadStepOut)));
5113
5114 MenuSP view_menu_sp(new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View));
5115 view_menu_sp->AddSubmenu (MenuSP (new Menu("Backtrace", NULL, 'b', ApplicationDelegate::eMenuID_ViewBacktrace)));
5116 view_menu_sp->AddSubmenu (MenuSP (new Menu("Registers", NULL, 'r', ApplicationDelegate::eMenuID_ViewRegisters)));
5117 view_menu_sp->AddSubmenu (MenuSP (new Menu("Source" , NULL, 's', ApplicationDelegate::eMenuID_ViewSource)));
5118 view_menu_sp->AddSubmenu (MenuSP (new Menu("Variables", NULL, 'v', ApplicationDelegate::eMenuID_ViewVariables)));
5119
5120 MenuSP help_menu_sp(new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help));
5121 help_menu_sp->AddSubmenu (MenuSP (new Menu("GUI Help", NULL, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp)));
5122
5123 m_app_ap->Initialize();
5124 WindowSP &main_window_sp = m_app_ap->GetMainWindow();
5125
5126 MenuSP menubar_sp(new Menu(Menu::Type::Bar));
5127 menubar_sp->AddSubmenu (lldb_menu_sp);
5128 menubar_sp->AddSubmenu (target_menu_sp);
5129 menubar_sp->AddSubmenu (process_menu_sp);
5130 menubar_sp->AddSubmenu (thread_menu_sp);
5131 menubar_sp->AddSubmenu (view_menu_sp);
5132 menubar_sp->AddSubmenu (help_menu_sp);
5133 menubar_sp->SetDelegate(app_menu_delegate_sp);
5134
5135 Rect content_bounds = main_window_sp->GetFrame();
5136 Rect menubar_bounds = content_bounds.MakeMenuBar();
5137 Rect status_bounds = content_bounds.MakeStatusBar();
5138 Rect source_bounds;
5139 Rect variables_bounds;
5140 Rect threads_bounds;
5141 Rect source_variables_bounds;
5142 content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds, threads_bounds);
5143 source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds, variables_bounds);
5144
5145 WindowSP menubar_window_sp = main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false);
5146 // Let the menubar get keys if the active window doesn't handle the
5147 // keys that are typed so it can respond to menubar key presses.
5148 menubar_window_sp->SetCanBeActive(false); // Don't let the menubar become the active window
5149 menubar_window_sp->SetDelegate(menubar_sp);
5150
5151 WindowSP source_window_sp (main_window_sp->CreateSubWindow("Source",
5152 source_bounds,
5153 true));
5154 WindowSP variables_window_sp (main_window_sp->CreateSubWindow("Variables",
5155 variables_bounds,
5156 false));
5157 WindowSP threads_window_sp (main_window_sp->CreateSubWindow("Threads",
5158 threads_bounds,
5159 false));
5160 WindowSP status_window_sp (main_window_sp->CreateSubWindow("Status",
5161 status_bounds,
5162 false));
5163 status_window_sp->SetCanBeActive(false); // Don't let the status bar become the active window
5164 main_window_sp->SetDelegate (std::static_pointer_cast<WindowDelegate>(app_delegate_sp));
5165 source_window_sp->SetDelegate (WindowDelegateSP(new SourceFileWindowDelegate(m_debugger)));
5166 variables_window_sp->SetDelegate (WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
5167 TreeDelegateSP thread_delegate_sp (new ThreadTreeDelegate(m_debugger));
5168 threads_window_sp->SetDelegate (WindowDelegateSP(new TreeWindowDelegate(m_debugger, thread_delegate_sp)));
5169 status_window_sp->SetDelegate (WindowDelegateSP(new StatusBarWindowDelegate(m_debugger)));
5170
5171 init_pair (1, COLOR_WHITE , COLOR_BLUE );
5172 init_pair (2, COLOR_BLACK , COLOR_WHITE );
5173 init_pair (3, COLOR_MAGENTA , COLOR_WHITE );
5174 init_pair (4, COLOR_MAGENTA , COLOR_BLACK );
5175 init_pair (5, COLOR_RED , COLOR_BLACK );
5176
5177 }
5178}
5179
5180void
5181IOHandlerCursesGUI::Deactivate ()
5182{
5183 m_app_ap->Terminate();
5184}
5185
5186void
5187IOHandlerCursesGUI::Run ()
5188{
5189 m_app_ap->Run(m_debugger);
5190 SetIsDone(true);
5191}
5192
5193
5194IOHandlerCursesGUI::~IOHandlerCursesGUI ()
5195{
5196
5197}
5198
5199void
5200IOHandlerCursesGUI::Hide ()
5201{
5202}
5203
5204
5205void
5206IOHandlerCursesGUI::Refresh ()
5207{
5208}
5209
5210
5211void
5212IOHandlerCursesGUI::Interrupt ()
5213{
5214}
5215
5216
5217void
5218IOHandlerCursesGUI::GotEOF()
5219{
5220}
5221