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