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