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