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