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