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