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