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