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