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