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