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