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