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