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