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