112b93ac6SEd Maste //===-- IOHandler.cpp -------------------------------------------*- C++ -*-===//
212b93ac6SEd Maste //
312b93ac6SEd Maste // The LLVM Compiler Infrastructure
412b93ac6SEd Maste //
512b93ac6SEd Maste // This file is distributed under the University of Illinois Open Source
612b93ac6SEd Maste // License. See LICENSE.TXT for details.
712b93ac6SEd Maste //
812b93ac6SEd Maste //===----------------------------------------------------------------------===//
912b93ac6SEd Maste
10f678e45dSDimitry Andric #include "lldb/Core/IOHandler.h"
11f678e45dSDimitry Andric
129f2f44ceSEd Maste #ifndef LLDB_DISABLE_CURSES
139f2f44ceSEd Maste #include <curses.h>
149f2f44ceSEd Maste #include <panel.h>
159f2f44ceSEd Maste #endif
1612b93ac6SEd Maste
179f2f44ceSEd Maste #if defined(__APPLE__)
189f2f44ceSEd Maste #include <deque>
199f2f44ceSEd Maste #endif
2012b93ac6SEd Maste #include <string>
2112b93ac6SEd Maste
2212b93ac6SEd Maste #include "lldb/Core/Debugger.h"
2312b93ac6SEd Maste #include "lldb/Core/StreamFile.h"
24*b5893f02SDimitry Andric #include "lldb/Host/File.h"
25*b5893f02SDimitry Andric #include "lldb/Utility/Predicate.h"
26*b5893f02SDimitry Andric #include "lldb/Utility/Status.h"
27*b5893f02SDimitry Andric #include "lldb/Utility/StreamString.h"
28*b5893f02SDimitry Andric #include "lldb/Utility/StringList.h"
29*b5893f02SDimitry Andric #include "lldb/lldb-forward.h"
30f678e45dSDimitry Andric
317aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
3212b93ac6SEd Maste #include "lldb/Host/Editline.h"
337aa51b79SEd Maste #endif
3412b93ac6SEd Maste #include "lldb/Interpreter/CommandCompletions.h"
3512b93ac6SEd Maste #include "lldb/Interpreter/CommandInterpreter.h"
36f678e45dSDimitry Andric #ifndef LLDB_DISABLE_CURSES
37f678e45dSDimitry Andric #include "lldb/Breakpoint/BreakpointLocation.h"
38f678e45dSDimitry Andric #include "lldb/Core/Module.h"
39f678e45dSDimitry Andric #include "lldb/Core/ValueObject.h"
40f678e45dSDimitry Andric #include "lldb/Core/ValueObjectRegister.h"
4112b93ac6SEd Maste #include "lldb/Symbol/Block.h"
4212b93ac6SEd Maste #include "lldb/Symbol/Function.h"
4312b93ac6SEd Maste #include "lldb/Symbol/Symbol.h"
444bb0738eSEd Maste #include "lldb/Symbol/VariableList.h"
454bb0738eSEd Maste #include "lldb/Target/Process.h"
46f678e45dSDimitry Andric #include "lldb/Target/RegisterContext.h"
474bb0738eSEd Maste #include "lldb/Target/StackFrame.h"
48f678e45dSDimitry Andric #include "lldb/Target/StopInfo.h"
49435933ddSDimitry Andric #include "lldb/Target/Target.h"
50435933ddSDimitry Andric #include "lldb/Target/Thread.h"
51*b5893f02SDimitry Andric #include "lldb/Utility/State.h"
524bb0738eSEd Maste #endif
5312b93ac6SEd Maste
54*b5893f02SDimitry Andric #include "llvm/ADT/StringRef.h"
55f678e45dSDimitry Andric
564bb0738eSEd Maste #ifdef _MSC_VER
57edd7eaddSDimitry Andric #include "lldb/Host/windows/windows.h"
584bb0738eSEd Maste #endif
5912b93ac6SEd Maste
60*b5893f02SDimitry Andric #include <memory>
61*b5893f02SDimitry Andric #include <mutex>
62f678e45dSDimitry Andric
63*b5893f02SDimitry Andric #include <assert.h>
64*b5893f02SDimitry Andric #include <ctype.h>
65*b5893f02SDimitry Andric #include <errno.h>
66*b5893f02SDimitry Andric #include <locale.h>
67*b5893f02SDimitry Andric #include <stdint.h>
68*b5893f02SDimitry Andric #include <stdio.h>
69*b5893f02SDimitry Andric #include <string.h>
70*b5893f02SDimitry Andric #include <type_traits>
71f678e45dSDimitry Andric
7212b93ac6SEd Maste using namespace lldb;
7312b93ac6SEd Maste using namespace lldb_private;
7412b93ac6SEd Maste
IOHandler(Debugger & debugger,IOHandler::Type type)75435933ddSDimitry Andric IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type)
76435933ddSDimitry Andric : IOHandler(debugger, type,
7712b93ac6SEd Maste StreamFileSP(), // Adopt STDIN from top input reader
7812b93ac6SEd Maste StreamFileSP(), // Adopt STDOUT from top input reader
7912b93ac6SEd Maste StreamFileSP(), // Adopt STDERR from top input reader
8012b93ac6SEd Maste 0) // Flags
81435933ddSDimitry Andric {}
8212b93ac6SEd Maste
IOHandler(Debugger & debugger,IOHandler::Type type,const lldb::StreamFileSP & input_sp,const lldb::StreamFileSP & output_sp,const lldb::StreamFileSP & error_sp,uint32_t flags)83435933ddSDimitry Andric IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type,
8412b93ac6SEd Maste const lldb::StreamFileSP &input_sp,
8512b93ac6SEd Maste const lldb::StreamFileSP &output_sp,
86435933ddSDimitry Andric const lldb::StreamFileSP &error_sp, uint32_t flags)
87435933ddSDimitry Andric : m_debugger(debugger), m_input_sp(input_sp), m_output_sp(output_sp),
88435933ddSDimitry Andric m_error_sp(error_sp), m_popped(false), m_flags(flags), m_type(type),
89435933ddSDimitry Andric m_user_data(nullptr), m_done(false), m_active(false) {
9012b93ac6SEd Maste // If any files are not specified, then adopt them from the top input reader.
9112b93ac6SEd Maste if (!m_input_sp || !m_output_sp || !m_error_sp)
92435933ddSDimitry Andric debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_sp, m_output_sp,
9312b93ac6SEd Maste m_error_sp);
9412b93ac6SEd Maste }
9512b93ac6SEd Maste
969f2f44ceSEd Maste IOHandler::~IOHandler() = default;
9712b93ac6SEd Maste
GetInputFD()98435933ddSDimitry Andric int IOHandler::GetInputFD() {
994bb0738eSEd Maste return (m_input_sp ? m_input_sp->GetFile().GetDescriptor() : -1);
10012b93ac6SEd Maste }
10112b93ac6SEd Maste
GetOutputFD()102435933ddSDimitry Andric int IOHandler::GetOutputFD() {
1034bb0738eSEd Maste return (m_output_sp ? m_output_sp->GetFile().GetDescriptor() : -1);
10412b93ac6SEd Maste }
10512b93ac6SEd Maste
GetErrorFD()106435933ddSDimitry Andric int IOHandler::GetErrorFD() {
1074bb0738eSEd Maste return (m_error_sp ? m_error_sp->GetFile().GetDescriptor() : -1);
10812b93ac6SEd Maste }
10912b93ac6SEd Maste
GetInputFILE()110435933ddSDimitry Andric FILE *IOHandler::GetInputFILE() {
1114bb0738eSEd Maste return (m_input_sp ? m_input_sp->GetFile().GetStream() : nullptr);
11212b93ac6SEd Maste }
11312b93ac6SEd Maste
GetOutputFILE()114435933ddSDimitry Andric FILE *IOHandler::GetOutputFILE() {
1154bb0738eSEd Maste return (m_output_sp ? m_output_sp->GetFile().GetStream() : nullptr);
11612b93ac6SEd Maste }
11712b93ac6SEd Maste
GetErrorFILE()118435933ddSDimitry Andric FILE *IOHandler::GetErrorFILE() {
1194bb0738eSEd Maste return (m_error_sp ? m_error_sp->GetFile().GetStream() : nullptr);
12012b93ac6SEd Maste }
12112b93ac6SEd Maste
GetInputStreamFile()122435933ddSDimitry Andric StreamFileSP &IOHandler::GetInputStreamFile() { return m_input_sp; }
12312b93ac6SEd Maste
GetOutputStreamFile()124435933ddSDimitry Andric StreamFileSP &IOHandler::GetOutputStreamFile() { return m_output_sp; }
12512b93ac6SEd Maste
GetErrorStreamFile()126435933ddSDimitry Andric StreamFileSP &IOHandler::GetErrorStreamFile() { return m_error_sp; }
12712b93ac6SEd Maste
GetIsInteractive()128435933ddSDimitry Andric bool IOHandler::GetIsInteractive() {
12912b93ac6SEd Maste return GetInputStreamFile()->GetFile().GetIsInteractive();
13012b93ac6SEd Maste }
13112b93ac6SEd Maste
GetIsRealTerminal()132435933ddSDimitry Andric bool IOHandler::GetIsRealTerminal() {
13312b93ac6SEd Maste return GetInputStreamFile()->GetFile().GetIsRealTerminal();
13412b93ac6SEd Maste }
13512b93ac6SEd Maste
SetPopped(bool b)136435933ddSDimitry Andric void IOHandler::SetPopped(bool b) { m_popped.SetValue(b, eBroadcastOnChange); }
1377aa51b79SEd Maste
WaitForPop()138435933ddSDimitry Andric void IOHandler::WaitForPop() { m_popped.WaitForValueEqualTo(true); }
1397aa51b79SEd Maste
PrintAsync(Stream * stream,const char * s,size_t len)140435933ddSDimitry Andric void IOHandlerStack::PrintAsync(Stream *stream, const char *s, size_t len) {
141435933ddSDimitry Andric if (stream) {
1424bb0738eSEd Maste std::lock_guard<std::recursive_mutex> guard(m_mutex);
1431c3bbb01SEd Maste if (m_top)
1441c3bbb01SEd Maste m_top->PrintAsync(stream, s, len);
1451c3bbb01SEd Maste }
1461c3bbb01SEd Maste }
1471c3bbb01SEd Maste
IOHandlerConfirm(Debugger & debugger,llvm::StringRef prompt,bool default_response)148435933ddSDimitry Andric IOHandlerConfirm::IOHandlerConfirm(Debugger &debugger, llvm::StringRef prompt,
149435933ddSDimitry Andric bool default_response)
150435933ddSDimitry Andric : IOHandlerEditline(
151435933ddSDimitry Andric debugger, IOHandler::Type::Confirm,
1524bb0738eSEd Maste nullptr, // nullptr editline_name means no history loaded/saved
153435933ddSDimitry Andric llvm::StringRef(), // No prompt
154435933ddSDimitry Andric llvm::StringRef(), // No continuation prompt
15512b93ac6SEd Maste false, // Multi-line
1567aa51b79SEd Maste false, // Don't colorize the prompt (i.e. the confirm message.)
157435933ddSDimitry Andric 0, *this),
158435933ddSDimitry Andric m_default_response(default_response), m_user_response(default_response) {
15912b93ac6SEd Maste StreamString prompt_stream;
16012b93ac6SEd Maste prompt_stream.PutCString(prompt);
16112b93ac6SEd Maste if (m_default_response)
16212b93ac6SEd Maste prompt_stream.Printf(": [Y/n] ");
16312b93ac6SEd Maste else
16412b93ac6SEd Maste prompt_stream.Printf(": [y/N] ");
16512b93ac6SEd Maste
166435933ddSDimitry Andric SetPrompt(prompt_stream.GetString());
16712b93ac6SEd Maste }
16812b93ac6SEd Maste
1699f2f44ceSEd Maste IOHandlerConfirm::~IOHandlerConfirm() = default;
17012b93ac6SEd Maste
IOHandlerComplete(IOHandler & io_handler,const char * current_line,const char * cursor,const char * last_char,int skip_first_n_matches,int max_matches,StringList & matches,StringList & descriptions)171*b5893f02SDimitry Andric int IOHandlerConfirm::IOHandlerComplete(
172*b5893f02SDimitry Andric IOHandler &io_handler, const char *current_line, const char *cursor,
173*b5893f02SDimitry Andric const char *last_char, int skip_first_n_matches, int max_matches,
174*b5893f02SDimitry Andric StringList &matches, StringList &descriptions) {
175435933ddSDimitry Andric if (current_line == cursor) {
176435933ddSDimitry Andric if (m_default_response) {
17712b93ac6SEd Maste matches.AppendString("y");
178435933ddSDimitry Andric } else {
17912b93ac6SEd Maste matches.AppendString("n");
18012b93ac6SEd Maste }
18112b93ac6SEd Maste }
18212b93ac6SEd Maste return matches.GetSize();
18312b93ac6SEd Maste }
18412b93ac6SEd Maste
IOHandlerInputComplete(IOHandler & io_handler,std::string & line)185435933ddSDimitry Andric void IOHandlerConfirm::IOHandlerInputComplete(IOHandler &io_handler,
186435933ddSDimitry Andric std::string &line) {
187435933ddSDimitry Andric if (line.empty()) {
18812b93ac6SEd Maste // User just hit enter, set the response to the default
18912b93ac6SEd Maste m_user_response = m_default_response;
19012b93ac6SEd Maste io_handler.SetIsDone(true);
19112b93ac6SEd Maste return;
19212b93ac6SEd Maste }
19312b93ac6SEd Maste
194435933ddSDimitry Andric if (line.size() == 1) {
195435933ddSDimitry Andric switch (line[0]) {
19612b93ac6SEd Maste case 'y':
19712b93ac6SEd Maste case 'Y':
19812b93ac6SEd Maste m_user_response = true;
19912b93ac6SEd Maste io_handler.SetIsDone(true);
20012b93ac6SEd Maste return;
20112b93ac6SEd Maste case 'n':
20212b93ac6SEd Maste case 'N':
20312b93ac6SEd Maste m_user_response = false;
20412b93ac6SEd Maste io_handler.SetIsDone(true);
20512b93ac6SEd Maste return;
20612b93ac6SEd Maste default:
20712b93ac6SEd Maste break;
20812b93ac6SEd Maste }
20912b93ac6SEd Maste }
21012b93ac6SEd Maste
211435933ddSDimitry Andric if (line == "yes" || line == "YES" || line == "Yes") {
21212b93ac6SEd Maste m_user_response = true;
21312b93ac6SEd Maste io_handler.SetIsDone(true);
214435933ddSDimitry Andric } else if (line == "no" || line == "NO" || line == "No") {
21512b93ac6SEd Maste m_user_response = false;
21612b93ac6SEd Maste io_handler.SetIsDone(true);
21712b93ac6SEd Maste }
21812b93ac6SEd Maste }
21912b93ac6SEd Maste
IOHandlerComplete(IOHandler & io_handler,const char * current_line,const char * cursor,const char * last_char,int skip_first_n_matches,int max_matches,StringList & matches,StringList & descriptions)220*b5893f02SDimitry Andric int IOHandlerDelegate::IOHandlerComplete(
221*b5893f02SDimitry Andric IOHandler &io_handler, const char *current_line, const char *cursor,
222*b5893f02SDimitry Andric const char *last_char, int skip_first_n_matches, int max_matches,
223*b5893f02SDimitry Andric StringList &matches, StringList &descriptions) {
224435933ddSDimitry Andric switch (m_completion) {
22512b93ac6SEd Maste case Completion::None:
22612b93ac6SEd Maste break;
22712b93ac6SEd Maste
22812b93ac6SEd Maste case Completion::LLDBCommand:
229435933ddSDimitry Andric return io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion(
230435933ddSDimitry Andric current_line, cursor, last_char, skip_first_n_matches, max_matches,
231*b5893f02SDimitry Andric matches, descriptions);
232435933ddSDimitry Andric case Completion::Expression: {
233*b5893f02SDimitry Andric CompletionResult result;
2344ba319b5SDimitry Andric CompletionRequest request(current_line, current_line - cursor,
235*b5893f02SDimitry Andric skip_first_n_matches, max_matches, result);
236435933ddSDimitry Andric CommandCompletions::InvokeCommonCompletionCallbacks(
237435933ddSDimitry Andric io_handler.GetDebugger().GetCommandInterpreter(),
2384ba319b5SDimitry Andric CommandCompletions::eVariablePathCompletion, request, nullptr);
239*b5893f02SDimitry Andric result.GetMatches(matches);
240*b5893f02SDimitry Andric result.GetDescriptions(descriptions);
24112b93ac6SEd Maste
2424ba319b5SDimitry Andric size_t num_matches = request.GetNumberOfMatches();
243435933ddSDimitry Andric if (num_matches > 0) {
24412b93ac6SEd Maste std::string common_prefix;
24512b93ac6SEd Maste matches.LongestCommonPrefix(common_prefix);
2464ba319b5SDimitry Andric const size_t partial_name_len = request.GetCursorArgumentPrefix().size();
24712b93ac6SEd Maste
2484ba319b5SDimitry Andric // If we matched a unique single command, add a space... Only do this if
2494ba319b5SDimitry Andric // the completer told us this was a complete word, however...
2504ba319b5SDimitry Andric if (num_matches == 1 && request.GetWordComplete()) {
25112b93ac6SEd Maste common_prefix.push_back(' ');
25212b93ac6SEd Maste }
25312b93ac6SEd Maste common_prefix.erase(0, partial_name_len);
25412b93ac6SEd Maste matches.InsertStringAtIndex(0, std::move(common_prefix));
25512b93ac6SEd Maste }
25612b93ac6SEd Maste return num_matches;
257435933ddSDimitry Andric } break;
25812b93ac6SEd Maste }
25912b93ac6SEd Maste
26012b93ac6SEd Maste return 0;
26112b93ac6SEd Maste }
26212b93ac6SEd Maste
IOHandlerEditline(Debugger & debugger,IOHandler::Type type,const char * editline_name,llvm::StringRef prompt,llvm::StringRef continuation_prompt,bool multi_line,bool color_prompts,uint32_t line_number_start,IOHandlerDelegate & delegate)263435933ddSDimitry Andric IOHandlerEditline::IOHandlerEditline(
264435933ddSDimitry Andric Debugger &debugger, IOHandler::Type type,
26512b93ac6SEd Maste const char *editline_name, // Used for saving history files
266435933ddSDimitry Andric llvm::StringRef prompt, llvm::StringRef continuation_prompt,
267435933ddSDimitry Andric bool multi_line, bool color_prompts, uint32_t line_number_start,
268435933ddSDimitry Andric IOHandlerDelegate &delegate)
269435933ddSDimitry Andric : IOHandlerEditline(debugger, type,
27012b93ac6SEd Maste StreamFileSP(), // Inherit input from top input reader
27112b93ac6SEd Maste StreamFileSP(), // Inherit output from top input reader
27212b93ac6SEd Maste StreamFileSP(), // Inherit error from top input reader
27312b93ac6SEd Maste 0, // Flags
27412b93ac6SEd Maste editline_name, // Used for saving history files
275435933ddSDimitry Andric prompt, continuation_prompt, multi_line, color_prompts,
276435933ddSDimitry Andric line_number_start, delegate) {}
27712b93ac6SEd Maste
IOHandlerEditline(Debugger & debugger,IOHandler::Type type,const lldb::StreamFileSP & input_sp,const lldb::StreamFileSP & output_sp,const lldb::StreamFileSP & error_sp,uint32_t flags,const char * editline_name,llvm::StringRef prompt,llvm::StringRef continuation_prompt,bool multi_line,bool color_prompts,uint32_t line_number_start,IOHandlerDelegate & delegate)278435933ddSDimitry Andric IOHandlerEditline::IOHandlerEditline(
279435933ddSDimitry Andric Debugger &debugger, IOHandler::Type type,
280435933ddSDimitry Andric const lldb::StreamFileSP &input_sp, const lldb::StreamFileSP &output_sp,
281435933ddSDimitry Andric const lldb::StreamFileSP &error_sp, uint32_t flags,
28212b93ac6SEd Maste const char *editline_name, // Used for saving history files
283435933ddSDimitry Andric llvm::StringRef prompt, llvm::StringRef continuation_prompt,
284435933ddSDimitry Andric bool multi_line, bool color_prompts, uint32_t line_number_start,
285435933ddSDimitry Andric IOHandlerDelegate &delegate)
286435933ddSDimitry Andric : IOHandler(debugger, type, input_sp, output_sp, error_sp, flags),
2877aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
28812b93ac6SEd Maste m_editline_ap(),
2897aa51b79SEd Maste #endif
290435933ddSDimitry Andric m_delegate(delegate), m_prompt(), m_continuation_prompt(),
291435933ddSDimitry Andric m_current_lines_ptr(nullptr), m_base_line_number(line_number_start),
292435933ddSDimitry Andric m_curr_line_idx(UINT32_MAX), m_multi_line(multi_line),
293435933ddSDimitry Andric m_color_prompts(color_prompts), m_interrupt_exits(true),
294435933ddSDimitry Andric m_editing(false) {
29512b93ac6SEd Maste SetPrompt(prompt);
29612b93ac6SEd Maste
2977aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
29812b93ac6SEd Maste bool use_editline = false;
29912b93ac6SEd Maste
30012b93ac6SEd Maste use_editline = m_input_sp->GetFile().GetIsRealTerminal();
30112b93ac6SEd Maste
302435933ddSDimitry Andric if (use_editline) {
303435933ddSDimitry Andric m_editline_ap.reset(new Editline(editline_name, GetInputFILE(),
304435933ddSDimitry Andric GetOutputFILE(), GetErrorFILE(),
3057aa51b79SEd Maste m_color_prompts));
3067aa51b79SEd Maste m_editline_ap->SetIsInputCompleteCallback(IsInputCompleteCallback, this);
30712b93ac6SEd Maste m_editline_ap->SetAutoCompleteCallback(AutoCompleteCallback, this);
3087aa51b79SEd Maste // See if the delegate supports fixing indentation
3097aa51b79SEd Maste const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters();
310435933ddSDimitry Andric if (indent_chars) {
311435933ddSDimitry Andric // The delegate does support indentation, hook it up so when any
3124ba319b5SDimitry Andric // indentation character is typed, the delegate gets a chance to fix it
313435933ddSDimitry Andric m_editline_ap->SetFixIndentationCallback(FixIndentationCallback, this,
314435933ddSDimitry Andric indent_chars);
31512b93ac6SEd Maste }
3167aa51b79SEd Maste }
3177aa51b79SEd Maste #endif
3187aa51b79SEd Maste SetBaseLineNumber(m_base_line_number);
319435933ddSDimitry Andric SetPrompt(prompt);
3207aa51b79SEd Maste SetContinuationPrompt(continuation_prompt);
32112b93ac6SEd Maste }
32212b93ac6SEd Maste
~IOHandlerEditline()323435933ddSDimitry Andric IOHandlerEditline::~IOHandlerEditline() {
3247aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
32512b93ac6SEd Maste m_editline_ap.reset();
3267aa51b79SEd Maste #endif
3277aa51b79SEd Maste }
3287aa51b79SEd Maste
Activate()329435933ddSDimitry Andric void IOHandlerEditline::Activate() {
3307aa51b79SEd Maste IOHandler::Activate();
3317aa51b79SEd Maste m_delegate.IOHandlerActivated(*this);
3327aa51b79SEd Maste }
3337aa51b79SEd Maste
Deactivate()334435933ddSDimitry Andric void IOHandlerEditline::Deactivate() {
3357aa51b79SEd Maste IOHandler::Deactivate();
3367aa51b79SEd Maste m_delegate.IOHandlerDeactivated(*this);
33712b93ac6SEd Maste }
33812b93ac6SEd Maste
GetLine(std::string & line,bool & interrupted)339435933ddSDimitry Andric bool IOHandlerEditline::GetLine(std::string &line, bool &interrupted) {
3407aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
341435933ddSDimitry Andric if (m_editline_ap) {
3427aa51b79SEd Maste return m_editline_ap->GetLine(line, interrupted);
343435933ddSDimitry Andric } else {
3447aa51b79SEd Maste #endif
34512b93ac6SEd Maste line.clear();
34612b93ac6SEd Maste
34712b93ac6SEd Maste FILE *in = GetInputFILE();
348435933ddSDimitry Andric if (in) {
349435933ddSDimitry Andric if (GetIsInteractive()) {
3504bb0738eSEd Maste const char *prompt = nullptr;
3517aa51b79SEd Maste
3527aa51b79SEd Maste if (m_multi_line && m_curr_line_idx > 0)
3537aa51b79SEd Maste prompt = GetContinuationPrompt();
3547aa51b79SEd Maste
3554bb0738eSEd Maste if (prompt == nullptr)
3567aa51b79SEd Maste prompt = GetPrompt();
3577aa51b79SEd Maste
358435933ddSDimitry Andric if (prompt && prompt[0]) {
35912b93ac6SEd Maste FILE *out = GetOutputFILE();
360435933ddSDimitry Andric if (out) {
36112b93ac6SEd Maste ::fprintf(out, "%s", prompt);
36212b93ac6SEd Maste ::fflush(out);
36312b93ac6SEd Maste }
36412b93ac6SEd Maste }
36512b93ac6SEd Maste }
36612b93ac6SEd Maste char buffer[256];
36712b93ac6SEd Maste bool done = false;
36812b93ac6SEd Maste bool got_line = false;
3691c3bbb01SEd Maste m_editing = true;
370435933ddSDimitry Andric while (!done) {
371435933ddSDimitry Andric if (fgets(buffer, sizeof(buffer), in) == nullptr) {
3720127ef0fSEd Maste const int saved_errno = errno;
3730127ef0fSEd Maste if (feof(in))
37412b93ac6SEd Maste done = true;
375435933ddSDimitry Andric else if (ferror(in)) {
3760127ef0fSEd Maste if (saved_errno != EINTR)
3770127ef0fSEd Maste done = true;
3780127ef0fSEd Maste }
379435933ddSDimitry Andric } else {
38012b93ac6SEd Maste got_line = true;
38112b93ac6SEd Maste size_t buffer_len = strlen(buffer);
38212b93ac6SEd Maste assert(buffer[buffer_len] == '\0');
38312b93ac6SEd Maste char last_char = buffer[buffer_len - 1];
384435933ddSDimitry Andric if (last_char == '\r' || last_char == '\n') {
38512b93ac6SEd Maste done = true;
38612b93ac6SEd Maste // Strip trailing newlines
387435933ddSDimitry Andric while (last_char == '\r' || last_char == '\n') {
38812b93ac6SEd Maste --buffer_len;
38912b93ac6SEd Maste if (buffer_len == 0)
39012b93ac6SEd Maste break;
39112b93ac6SEd Maste last_char = buffer[buffer_len - 1];
39212b93ac6SEd Maste }
39312b93ac6SEd Maste }
39412b93ac6SEd Maste line.append(buffer, buffer_len);
39512b93ac6SEd Maste }
39612b93ac6SEd Maste }
3971c3bbb01SEd Maste m_editing = false;
3984ba319b5SDimitry Andric // We might have gotten a newline on a line by itself make sure to return
3994ba319b5SDimitry Andric // true in this case.
40012b93ac6SEd Maste return got_line;
401435933ddSDimitry Andric } else {
40212b93ac6SEd Maste // No more input file, we are done...
40312b93ac6SEd Maste SetIsDone(true);
40412b93ac6SEd Maste }
40512b93ac6SEd Maste return false;
4067aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
40712b93ac6SEd Maste }
4087aa51b79SEd Maste #endif
40912b93ac6SEd Maste }
41012b93ac6SEd Maste
4117aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
IsInputCompleteCallback(Editline * editline,StringList & lines,void * baton)412435933ddSDimitry Andric bool IOHandlerEditline::IsInputCompleteCallback(Editline *editline,
41312b93ac6SEd Maste StringList &lines,
414435933ddSDimitry Andric void *baton) {
41512b93ac6SEd Maste IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton;
416435933ddSDimitry Andric return editline_reader->m_delegate.IOHandlerIsInputComplete(*editline_reader,
417435933ddSDimitry Andric lines);
4187aa51b79SEd Maste }
4197aa51b79SEd Maste
FixIndentationCallback(Editline * editline,const StringList & lines,int cursor_position,void * baton)420435933ddSDimitry Andric int IOHandlerEditline::FixIndentationCallback(Editline *editline,
4217aa51b79SEd Maste const StringList &lines,
4227aa51b79SEd Maste int cursor_position,
423435933ddSDimitry Andric void *baton) {
4247aa51b79SEd Maste IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton;
425435933ddSDimitry Andric return editline_reader->m_delegate.IOHandlerFixIndentation(
426435933ddSDimitry Andric *editline_reader, lines, cursor_position);
42712b93ac6SEd Maste }
42812b93ac6SEd Maste
AutoCompleteCallback(const char * current_line,const char * cursor,const char * last_char,int skip_first_n_matches,int max_matches,StringList & matches,StringList & descriptions,void * baton)429*b5893f02SDimitry Andric int IOHandlerEditline::AutoCompleteCallback(
430*b5893f02SDimitry Andric const char *current_line, const char *cursor, const char *last_char,
431*b5893f02SDimitry Andric int skip_first_n_matches, int max_matches, StringList &matches,
432*b5893f02SDimitry Andric StringList &descriptions, void *baton) {
43312b93ac6SEd Maste IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton;
43412b93ac6SEd Maste if (editline_reader)
435435933ddSDimitry Andric return editline_reader->m_delegate.IOHandlerComplete(
436435933ddSDimitry Andric *editline_reader, current_line, cursor, last_char, skip_first_n_matches,
437*b5893f02SDimitry Andric max_matches, matches, descriptions);
43812b93ac6SEd Maste return 0;
43912b93ac6SEd Maste }
4407aa51b79SEd Maste #endif
44112b93ac6SEd Maste
GetPrompt()442435933ddSDimitry Andric const char *IOHandlerEditline::GetPrompt() {
4437aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
444435933ddSDimitry Andric if (m_editline_ap) {
44512b93ac6SEd Maste return m_editline_ap->GetPrompt();
446435933ddSDimitry Andric } else {
4477aa51b79SEd Maste #endif
4487aa51b79SEd Maste if (m_prompt.empty())
4494bb0738eSEd Maste return nullptr;
4507aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
4517aa51b79SEd Maste }
4527aa51b79SEd Maste #endif
45312b93ac6SEd Maste return m_prompt.c_str();
45412b93ac6SEd Maste }
45512b93ac6SEd Maste
SetPrompt(llvm::StringRef prompt)456435933ddSDimitry Andric bool IOHandlerEditline::SetPrompt(llvm::StringRef prompt) {
457435933ddSDimitry Andric m_prompt = prompt;
458435933ddSDimitry Andric
4597aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
46012b93ac6SEd Maste if (m_editline_ap)
4614bb0738eSEd Maste m_editline_ap->SetPrompt(m_prompt.empty() ? nullptr : m_prompt.c_str());
4627aa51b79SEd Maste #endif
46312b93ac6SEd Maste return true;
46412b93ac6SEd Maste }
46512b93ac6SEd Maste
GetContinuationPrompt()466435933ddSDimitry Andric const char *IOHandlerEditline::GetContinuationPrompt() {
467435933ddSDimitry Andric return (m_continuation_prompt.empty() ? nullptr
468435933ddSDimitry Andric : m_continuation_prompt.c_str());
4697aa51b79SEd Maste }
4707aa51b79SEd Maste
SetContinuationPrompt(llvm::StringRef prompt)471435933ddSDimitry Andric void IOHandlerEditline::SetContinuationPrompt(llvm::StringRef prompt) {
472435933ddSDimitry Andric m_continuation_prompt = prompt;
4737aa51b79SEd Maste
4747aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
4757aa51b79SEd Maste if (m_editline_ap)
476435933ddSDimitry Andric m_editline_ap->SetContinuationPrompt(m_continuation_prompt.empty()
477435933ddSDimitry Andric ? nullptr
478435933ddSDimitry Andric : m_continuation_prompt.c_str());
4797aa51b79SEd Maste #endif
4807aa51b79SEd Maste }
4817aa51b79SEd Maste
SetBaseLineNumber(uint32_t line)482435933ddSDimitry Andric void IOHandlerEditline::SetBaseLineNumber(uint32_t line) {
4830127ef0fSEd Maste m_base_line_number = line;
4840127ef0fSEd Maste }
4857aa51b79SEd Maste
GetCurrentLineIndex() const486435933ddSDimitry Andric uint32_t IOHandlerEditline::GetCurrentLineIndex() const {
4877aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
4887aa51b79SEd Maste if (m_editline_ap)
4897aa51b79SEd Maste return m_editline_ap->GetCurrentLine();
4907aa51b79SEd Maste #endif
4917aa51b79SEd Maste return m_curr_line_idx;
4927aa51b79SEd Maste }
4937aa51b79SEd Maste
GetLines(StringList & lines,bool & interrupted)494435933ddSDimitry Andric bool IOHandlerEditline::GetLines(StringList &lines, bool &interrupted) {
4957aa51b79SEd Maste m_current_lines_ptr = &lines;
4967aa51b79SEd Maste
49712b93ac6SEd Maste bool success = false;
4987aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
499435933ddSDimitry Andric if (m_editline_ap) {
5007aa51b79SEd Maste return m_editline_ap->GetLines(m_base_line_number, lines, interrupted);
501435933ddSDimitry Andric } else {
5027aa51b79SEd Maste #endif
5037aa51b79SEd Maste bool done = false;
5045517e702SDimitry Andric Status error;
50512b93ac6SEd Maste
506435933ddSDimitry Andric while (!done) {
5070127ef0fSEd Maste // Show line numbers if we are asked to
50812b93ac6SEd Maste std::string line;
509435933ddSDimitry Andric if (m_base_line_number > 0 && GetIsInteractive()) {
5100127ef0fSEd Maste FILE *out = GetOutputFILE();
5110127ef0fSEd Maste if (out)
512435933ddSDimitry Andric ::fprintf(out, "%u%s", m_base_line_number + (uint32_t)lines.GetSize(),
513435933ddSDimitry Andric GetPrompt() == nullptr ? " " : "");
5140127ef0fSEd Maste }
5150127ef0fSEd Maste
5167aa51b79SEd Maste m_curr_line_idx = lines.GetSize();
5177aa51b79SEd Maste
5180127ef0fSEd Maste bool interrupted = false;
519435933ddSDimitry Andric if (GetLine(line, interrupted) && !interrupted) {
52012b93ac6SEd Maste lines.AppendString(line);
5217aa51b79SEd Maste done = m_delegate.IOHandlerIsInputComplete(*this, lines);
522435933ddSDimitry Andric } else {
5237aa51b79SEd Maste done = true;
52412b93ac6SEd Maste }
52512b93ac6SEd Maste }
52612b93ac6SEd Maste success = lines.GetSize() > 0;
5277aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
52812b93ac6SEd Maste }
5297aa51b79SEd Maste #endif
53012b93ac6SEd Maste return success;
53112b93ac6SEd Maste }
53212b93ac6SEd Maste
5334ba319b5SDimitry Andric // Each IOHandler gets to run until it is done. It should read data from the
5344ba319b5SDimitry Andric // "in" and place output into "out" and "err and return when done.
Run()535435933ddSDimitry Andric void IOHandlerEditline::Run() {
53612b93ac6SEd Maste std::string line;
537435933ddSDimitry Andric while (IsActive()) {
5380127ef0fSEd Maste bool interrupted = false;
539435933ddSDimitry Andric if (m_multi_line) {
54012b93ac6SEd Maste StringList lines;
541435933ddSDimitry Andric if (GetLines(lines, interrupted)) {
542435933ddSDimitry Andric if (interrupted) {
5437aa51b79SEd Maste m_done = m_interrupt_exits;
5447aa51b79SEd Maste m_delegate.IOHandlerInputInterrupted(*this, line);
5457aa51b79SEd Maste
546435933ddSDimitry Andric } else {
54712b93ac6SEd Maste line = lines.CopyList();
54812b93ac6SEd Maste m_delegate.IOHandlerInputComplete(*this, line);
54912b93ac6SEd Maste }
550435933ddSDimitry Andric } else {
55112b93ac6SEd Maste m_done = true;
55212b93ac6SEd Maste }
553435933ddSDimitry Andric } else {
554435933ddSDimitry Andric if (GetLine(line, interrupted)) {
5557aa51b79SEd Maste if (interrupted)
5567aa51b79SEd Maste m_delegate.IOHandlerInputInterrupted(*this, line);
5577aa51b79SEd Maste else
55812b93ac6SEd Maste m_delegate.IOHandlerInputComplete(*this, line);
559435933ddSDimitry Andric } else {
56012b93ac6SEd Maste m_done = true;
56112b93ac6SEd Maste }
56212b93ac6SEd Maste }
56312b93ac6SEd Maste }
56412b93ac6SEd Maste }
56512b93ac6SEd Maste
Cancel()566435933ddSDimitry Andric void IOHandlerEditline::Cancel() {
5677aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
5686fcb8242SEd Maste if (m_editline_ap)
5691c3bbb01SEd Maste m_editline_ap->Cancel();
5707aa51b79SEd Maste #endif
5716fcb8242SEd Maste }
5726fcb8242SEd Maste
Interrupt()573435933ddSDimitry Andric bool IOHandlerEditline::Interrupt() {
5740127ef0fSEd Maste // Let the delgate handle it first
5750127ef0fSEd Maste if (m_delegate.IOHandlerInterrupt(*this))
5760127ef0fSEd Maste return true;
5770127ef0fSEd Maste
5787aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
57912b93ac6SEd Maste if (m_editline_ap)
5800127ef0fSEd Maste return m_editline_ap->Interrupt();
5817aa51b79SEd Maste #endif
5820127ef0fSEd Maste return false;
58312b93ac6SEd Maste }
58412b93ac6SEd Maste
GotEOF()585435933ddSDimitry Andric void IOHandlerEditline::GotEOF() {
5867aa51b79SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
58712b93ac6SEd Maste if (m_editline_ap)
58812b93ac6SEd Maste m_editline_ap->Interrupt();
5897aa51b79SEd Maste #endif
59012b93ac6SEd Maste }
59112b93ac6SEd Maste
PrintAsync(Stream * stream,const char * s,size_t len)592435933ddSDimitry Andric void IOHandlerEditline::PrintAsync(Stream *stream, const char *s, size_t len) {
5931c3bbb01SEd Maste #ifndef LLDB_DISABLE_LIBEDIT
5941c3bbb01SEd Maste if (m_editline_ap)
5951c3bbb01SEd Maste m_editline_ap->PrintAsync(stream, s, len);
5961c3bbb01SEd Maste else
5971c3bbb01SEd Maste #endif
5984bb0738eSEd Maste {
5994bb0738eSEd Maste #ifdef _MSC_VER
600435933ddSDimitry Andric const char *prompt = GetPrompt();
601435933ddSDimitry Andric if (prompt) {
6024bb0738eSEd Maste // Back up over previous prompt using Windows API
6034bb0738eSEd Maste CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info;
6044bb0738eSEd Maste HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
6054bb0738eSEd Maste GetConsoleScreenBufferInfo(console_handle, &screen_buffer_info);
6064bb0738eSEd Maste COORD coord = screen_buffer_info.dwCursorPosition;
6074bb0738eSEd Maste coord.X -= strlen(prompt);
6084bb0738eSEd Maste if (coord.X < 0)
6094bb0738eSEd Maste coord.X = 0;
6104bb0738eSEd Maste SetConsoleCursorPosition(console_handle, coord);
6114bb0738eSEd Maste }
6124bb0738eSEd Maste #endif
6131c3bbb01SEd Maste IOHandler::PrintAsync(stream, s, len);
614435933ddSDimitry Andric #ifdef _MSC_VER
6154bb0738eSEd Maste if (prompt)
616435933ddSDimitry Andric IOHandler::PrintAsync(GetOutputStreamFile().get(), prompt,
617435933ddSDimitry Andric strlen(prompt));
618435933ddSDimitry Andric #endif
6194bb0738eSEd Maste }
6201c3bbb01SEd Maste }
6211c3bbb01SEd Maste
6224ba319b5SDimitry Andric // we may want curses to be disabled for some builds for instance, windows
62312b93ac6SEd Maste #ifndef LLDB_DISABLE_CURSES
62412b93ac6SEd Maste
62512b93ac6SEd Maste #define KEY_RETURN 10
62612b93ac6SEd Maste #define KEY_ESCAPE 27
62712b93ac6SEd Maste
628435933ddSDimitry Andric namespace curses {
62912b93ac6SEd Maste class Menu;
63012b93ac6SEd Maste class MenuDelegate;
63112b93ac6SEd Maste class Window;
63212b93ac6SEd Maste class WindowDelegate;
63312b93ac6SEd Maste typedef std::shared_ptr<Menu> MenuSP;
63412b93ac6SEd Maste typedef std::shared_ptr<MenuDelegate> MenuDelegateSP;
63512b93ac6SEd Maste typedef std::shared_ptr<Window> WindowSP;
63612b93ac6SEd Maste typedef std::shared_ptr<WindowDelegate> WindowDelegateSP;
63712b93ac6SEd Maste typedef std::vector<MenuSP> Menus;
63812b93ac6SEd Maste typedef std::vector<WindowSP> Windows;
63912b93ac6SEd Maste typedef std::vector<WindowDelegateSP> WindowDelegates;
64012b93ac6SEd Maste
64112b93ac6SEd Maste #if 0
64212b93ac6SEd Maste type summary add -s "x=${var.x}, y=${var.y}" curses::Point
64312b93ac6SEd Maste type summary add -s "w=${var.width}, h=${var.height}" curses::Size
64412b93ac6SEd Maste type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect
64512b93ac6SEd Maste #endif
6469f2f44ceSEd Maste
647435933ddSDimitry Andric struct Point {
64812b93ac6SEd Maste int x;
64912b93ac6SEd Maste int y;
65012b93ac6SEd Maste
Pointcurses::Point651435933ddSDimitry Andric Point(int _x = 0, int _y = 0) : x(_x), y(_y) {}
65212b93ac6SEd Maste
Clearcurses::Point653435933ddSDimitry Andric void Clear() {
65412b93ac6SEd Maste x = 0;
65512b93ac6SEd Maste y = 0;
65612b93ac6SEd Maste }
65712b93ac6SEd Maste
operator +=curses::Point658435933ddSDimitry Andric Point &operator+=(const Point &rhs) {
65912b93ac6SEd Maste x += rhs.x;
66012b93ac6SEd Maste y += rhs.y;
66112b93ac6SEd Maste return *this;
66212b93ac6SEd Maste }
66312b93ac6SEd Maste
Dumpcurses::Point664435933ddSDimitry Andric void Dump() { printf("(x=%i, y=%i)\n", x, y); }
66512b93ac6SEd Maste };
66612b93ac6SEd Maste
operator ==(const Point & lhs,const Point & rhs)667435933ddSDimitry Andric bool operator==(const Point &lhs, const Point &rhs) {
66812b93ac6SEd Maste return lhs.x == rhs.x && lhs.y == rhs.y;
66912b93ac6SEd Maste }
6709f2f44ceSEd Maste
operator !=(const Point & lhs,const Point & rhs)671435933ddSDimitry Andric bool operator!=(const Point &lhs, const Point &rhs) {
67212b93ac6SEd Maste return lhs.x != rhs.x || lhs.y != rhs.y;
67312b93ac6SEd Maste }
67412b93ac6SEd Maste
675435933ddSDimitry Andric struct Size {
67612b93ac6SEd Maste int width;
67712b93ac6SEd Maste int height;
Sizecurses::Size678435933ddSDimitry Andric Size(int w = 0, int h = 0) : width(w), height(h) {}
67912b93ac6SEd Maste
Clearcurses::Size680435933ddSDimitry Andric void Clear() {
68112b93ac6SEd Maste width = 0;
68212b93ac6SEd Maste height = 0;
68312b93ac6SEd Maste }
68412b93ac6SEd Maste
Dumpcurses::Size685435933ddSDimitry Andric void Dump() { printf("(w=%i, h=%i)\n", width, height); }
68612b93ac6SEd Maste };
68712b93ac6SEd Maste
operator ==(const Size & lhs,const Size & rhs)688435933ddSDimitry Andric bool operator==(const Size &lhs, const Size &rhs) {
68912b93ac6SEd Maste return lhs.width == rhs.width && lhs.height == rhs.height;
69012b93ac6SEd Maste }
6919f2f44ceSEd Maste
operator !=(const Size & lhs,const Size & rhs)692435933ddSDimitry Andric bool operator!=(const Size &lhs, const Size &rhs) {
69312b93ac6SEd Maste return lhs.width != rhs.width || lhs.height != rhs.height;
69412b93ac6SEd Maste }
69512b93ac6SEd Maste
696435933ddSDimitry Andric struct Rect {
69712b93ac6SEd Maste Point origin;
69812b93ac6SEd Maste Size size;
69912b93ac6SEd Maste
Rectcurses::Rect700435933ddSDimitry Andric Rect() : origin(), size() {}
70112b93ac6SEd Maste
Rectcurses::Rect702435933ddSDimitry Andric Rect(const Point &p, const Size &s) : origin(p), size(s) {}
70312b93ac6SEd Maste
Clearcurses::Rect704435933ddSDimitry Andric void Clear() {
70512b93ac6SEd Maste origin.Clear();
70612b93ac6SEd Maste size.Clear();
70712b93ac6SEd Maste }
70812b93ac6SEd Maste
Dumpcurses::Rect709435933ddSDimitry Andric void Dump() {
710435933ddSDimitry Andric printf("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width,
711435933ddSDimitry Andric size.height);
71212b93ac6SEd Maste }
71312b93ac6SEd Maste
Insetcurses::Rect714435933ddSDimitry Andric void Inset(int w, int h) {
71512b93ac6SEd Maste if (size.width > w * 2)
71612b93ac6SEd Maste size.width -= w * 2;
71712b93ac6SEd Maste origin.x += w;
71812b93ac6SEd Maste
71912b93ac6SEd Maste if (size.height > h * 2)
72012b93ac6SEd Maste size.height -= h * 2;
72112b93ac6SEd Maste origin.y += h;
72212b93ac6SEd Maste }
7239f2f44ceSEd Maste
7244ba319b5SDimitry Andric // Return a status bar rectangle which is the last line of this rectangle.
7254ba319b5SDimitry Andric // This rectangle will be modified to not include the status bar area.
MakeStatusBarcurses::Rect726435933ddSDimitry Andric Rect MakeStatusBar() {
72712b93ac6SEd Maste Rect status_bar;
728435933ddSDimitry Andric if (size.height > 1) {
72912b93ac6SEd Maste status_bar.origin.x = origin.x;
73012b93ac6SEd Maste status_bar.origin.y = size.height;
73112b93ac6SEd Maste status_bar.size.width = size.width;
73212b93ac6SEd Maste status_bar.size.height = 1;
73312b93ac6SEd Maste --size.height;
73412b93ac6SEd Maste }
73512b93ac6SEd Maste return status_bar;
73612b93ac6SEd Maste }
73712b93ac6SEd Maste
7384ba319b5SDimitry Andric // Return a menubar rectangle which is the first line of this rectangle. This
7394ba319b5SDimitry Andric // rectangle will be modified to not include the menubar area.
MakeMenuBarcurses::Rect740435933ddSDimitry Andric Rect MakeMenuBar() {
74112b93ac6SEd Maste Rect menubar;
742435933ddSDimitry Andric if (size.height > 1) {
74312b93ac6SEd Maste menubar.origin.x = origin.x;
74412b93ac6SEd Maste menubar.origin.y = origin.y;
74512b93ac6SEd Maste menubar.size.width = size.width;
74612b93ac6SEd Maste menubar.size.height = 1;
74712b93ac6SEd Maste ++origin.y;
74812b93ac6SEd Maste --size.height;
74912b93ac6SEd Maste }
75012b93ac6SEd Maste return menubar;
75112b93ac6SEd Maste }
75212b93ac6SEd Maste
HorizontalSplitPercentagecurses::Rect753435933ddSDimitry Andric void HorizontalSplitPercentage(float top_percentage, Rect &top,
754435933ddSDimitry Andric Rect &bottom) const {
75512b93ac6SEd Maste float top_height = top_percentage * size.height;
75612b93ac6SEd Maste HorizontalSplit(top_height, top, bottom);
75712b93ac6SEd Maste }
75812b93ac6SEd Maste
HorizontalSplitcurses::Rect759435933ddSDimitry Andric void HorizontalSplit(int top_height, Rect &top, Rect &bottom) const {
76012b93ac6SEd Maste top = *this;
761435933ddSDimitry Andric if (top_height < size.height) {
76212b93ac6SEd Maste top.size.height = top_height;
76312b93ac6SEd Maste bottom.origin.x = origin.x;
76412b93ac6SEd Maste bottom.origin.y = origin.y + top.size.height;
76512b93ac6SEd Maste bottom.size.width = size.width;
76612b93ac6SEd Maste bottom.size.height = size.height - top.size.height;
767435933ddSDimitry Andric } else {
76812b93ac6SEd Maste bottom.Clear();
76912b93ac6SEd Maste }
77012b93ac6SEd Maste }
77112b93ac6SEd Maste
VerticalSplitPercentagecurses::Rect772435933ddSDimitry Andric void VerticalSplitPercentage(float left_percentage, Rect &left,
773435933ddSDimitry Andric Rect &right) const {
77412b93ac6SEd Maste float left_width = left_percentage * size.width;
77512b93ac6SEd Maste VerticalSplit(left_width, left, right);
77612b93ac6SEd Maste }
77712b93ac6SEd Maste
VerticalSplitcurses::Rect778435933ddSDimitry Andric void VerticalSplit(int left_width, Rect &left, Rect &right) const {
77912b93ac6SEd Maste left = *this;
780435933ddSDimitry Andric if (left_width < size.width) {
78112b93ac6SEd Maste left.size.width = left_width;
78212b93ac6SEd Maste right.origin.x = origin.x + left.size.width;
78312b93ac6SEd Maste right.origin.y = origin.y;
78412b93ac6SEd Maste right.size.width = size.width - left.size.width;
78512b93ac6SEd Maste right.size.height = size.height;
786435933ddSDimitry Andric } else {
78712b93ac6SEd Maste right.Clear();
78812b93ac6SEd Maste }
78912b93ac6SEd Maste }
79012b93ac6SEd Maste };
79112b93ac6SEd Maste
operator ==(const Rect & lhs,const Rect & rhs)792435933ddSDimitry Andric bool operator==(const Rect &lhs, const Rect &rhs) {
79312b93ac6SEd Maste return lhs.origin == rhs.origin && lhs.size == rhs.size;
79412b93ac6SEd Maste }
7959f2f44ceSEd Maste
operator !=(const Rect & lhs,const Rect & rhs)796435933ddSDimitry Andric bool operator!=(const Rect &lhs, const Rect &rhs) {
79712b93ac6SEd Maste return lhs.origin != rhs.origin || lhs.size != rhs.size;
79812b93ac6SEd Maste }
79912b93ac6SEd Maste
800435933ddSDimitry Andric enum HandleCharResult {
80112b93ac6SEd Maste eKeyNotHandled = 0,
80212b93ac6SEd Maste eKeyHandled = 1,
80312b93ac6SEd Maste eQuitApplication = 2
80412b93ac6SEd Maste };
80512b93ac6SEd Maste
806435933ddSDimitry Andric enum class MenuActionResult {
80712b93ac6SEd Maste Handled,
80812b93ac6SEd Maste NotHandled,
80912b93ac6SEd Maste Quit // Exit all menus and quit
81012b93ac6SEd Maste };
81112b93ac6SEd Maste
812435933ddSDimitry Andric struct KeyHelp {
81312b93ac6SEd Maste int ch;
81412b93ac6SEd Maste const char *description;
81512b93ac6SEd Maste };
81612b93ac6SEd Maste
817435933ddSDimitry Andric class WindowDelegate {
81812b93ac6SEd Maste public:
819435933ddSDimitry Andric virtual ~WindowDelegate() = default;
82012b93ac6SEd Maste
WindowDelegateDraw(Window & window,bool force)821435933ddSDimitry Andric virtual bool WindowDelegateDraw(Window &window, bool force) {
82212b93ac6SEd Maste return false; // Drawing not handled
82312b93ac6SEd Maste }
82412b93ac6SEd Maste
WindowDelegateHandleChar(Window & window,int key)825435933ddSDimitry Andric virtual HandleCharResult WindowDelegateHandleChar(Window &window, int key) {
82612b93ac6SEd Maste return eKeyNotHandled;
82712b93ac6SEd Maste }
82812b93ac6SEd Maste
WindowDelegateGetHelpText()829435933ddSDimitry Andric virtual const char *WindowDelegateGetHelpText() { return nullptr; }
83012b93ac6SEd Maste
WindowDelegateGetKeyHelp()831435933ddSDimitry Andric virtual KeyHelp *WindowDelegateGetKeyHelp() { return nullptr; }
83212b93ac6SEd Maste };
83312b93ac6SEd Maste
834435933ddSDimitry Andric class HelpDialogDelegate : public WindowDelegate {
83512b93ac6SEd Maste public:
83612b93ac6SEd Maste HelpDialogDelegate(const char *text, KeyHelp *key_help_array);
83712b93ac6SEd Maste
8381c3bbb01SEd Maste ~HelpDialogDelegate() override;
83912b93ac6SEd Maste
840435933ddSDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override;
84112b93ac6SEd Maste
842435933ddSDimitry Andric HandleCharResult WindowDelegateHandleChar(Window &window, int key) override;
84312b93ac6SEd Maste
GetNumLines() const844435933ddSDimitry Andric size_t GetNumLines() const { return m_text.GetSize(); }
84512b93ac6SEd Maste
GetMaxLineLength() const846435933ddSDimitry Andric size_t GetMaxLineLength() const { return m_text.GetMaxStringLength(); }
84712b93ac6SEd Maste
84812b93ac6SEd Maste protected:
84912b93ac6SEd Maste StringList m_text;
85012b93ac6SEd Maste int m_first_visible_line;
85112b93ac6SEd Maste };
85212b93ac6SEd Maste
853435933ddSDimitry Andric class Window {
85412b93ac6SEd Maste public:
Window(const char * name)855435933ddSDimitry Andric Window(const char *name)
856435933ddSDimitry Andric : m_name(name), m_window(nullptr), m_panel(nullptr), m_parent(nullptr),
857435933ddSDimitry Andric m_subwindows(), m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX),
858435933ddSDimitry Andric m_prev_active_window_idx(UINT32_MAX), m_delete(false),
859435933ddSDimitry Andric m_needs_update(true), m_can_activate(true), m_is_subwin(false) {}
86012b93ac6SEd Maste
Window(const char * name,WINDOW * w,bool del=true)861435933ddSDimitry Andric Window(const char *name, WINDOW *w, bool del = true)
862435933ddSDimitry Andric : m_name(name), m_window(nullptr), m_panel(nullptr), m_parent(nullptr),
863435933ddSDimitry Andric m_subwindows(), m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX),
864435933ddSDimitry Andric m_prev_active_window_idx(UINT32_MAX), m_delete(del),
865435933ddSDimitry Andric m_needs_update(true), m_can_activate(true), m_is_subwin(false) {
86612b93ac6SEd Maste if (w)
86712b93ac6SEd Maste Reset(w);
86812b93ac6SEd Maste }
86912b93ac6SEd Maste
Window(const char * name,const Rect & bounds)870435933ddSDimitry Andric Window(const char *name, const Rect &bounds)
871435933ddSDimitry Andric : m_name(name), m_window(nullptr), m_parent(nullptr), m_subwindows(),
872435933ddSDimitry Andric m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX),
873435933ddSDimitry Andric m_prev_active_window_idx(UINT32_MAX), m_delete(true),
874435933ddSDimitry Andric m_needs_update(true), m_can_activate(true), m_is_subwin(false) {
875435933ddSDimitry Andric Reset(::newwin(bounds.size.height, bounds.size.width, bounds.origin.y,
876435933ddSDimitry Andric bounds.origin.y));
87712b93ac6SEd Maste }
87812b93ac6SEd Maste
~Window()879435933ddSDimitry Andric virtual ~Window() {
88012b93ac6SEd Maste RemoveSubWindows();
88112b93ac6SEd Maste Reset();
88212b93ac6SEd Maste }
88312b93ac6SEd Maste
Reset(WINDOW * w=nullptr,bool del=true)884435933ddSDimitry Andric void Reset(WINDOW *w = nullptr, bool del = true) {
88512b93ac6SEd Maste if (m_window == w)
88612b93ac6SEd Maste return;
88712b93ac6SEd Maste
888435933ddSDimitry Andric if (m_panel) {
88912b93ac6SEd Maste ::del_panel(m_panel);
8904bb0738eSEd Maste m_panel = nullptr;
89112b93ac6SEd Maste }
892435933ddSDimitry Andric if (m_window && m_delete) {
89312b93ac6SEd Maste ::delwin(m_window);
8944bb0738eSEd Maste m_window = nullptr;
89512b93ac6SEd Maste m_delete = false;
89612b93ac6SEd Maste }
897435933ddSDimitry Andric if (w) {
89812b93ac6SEd Maste m_window = w;
89912b93ac6SEd Maste m_panel = ::new_panel(m_window);
90012b93ac6SEd Maste m_delete = del;
90112b93ac6SEd Maste }
90212b93ac6SEd Maste }
90312b93ac6SEd Maste
AttributeOn(attr_t attr)90412b93ac6SEd Maste void AttributeOn(attr_t attr) { ::wattron(m_window, attr); }
AttributeOff(attr_t attr)90512b93ac6SEd Maste void AttributeOff(attr_t attr) { ::wattroff(m_window, attr); }
Box(chtype v_char=ACS_VLINE,chtype h_char=ACS_HLINE)906435933ddSDimitry Andric void Box(chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) {
907435933ddSDimitry Andric ::box(m_window, v_char, h_char);
908435933ddSDimitry Andric }
Clear()90912b93ac6SEd Maste void Clear() { ::wclear(m_window); }
Erase()91012b93ac6SEd Maste void Erase() { ::werase(m_window); }
GetBounds()911435933ddSDimitry Andric Rect GetBounds() {
912435933ddSDimitry Andric return Rect(GetParentOrigin(), GetSize());
913435933ddSDimitry Andric } // Get the rectangle in our parent window
GetChar()91412b93ac6SEd Maste int GetChar() { return ::wgetch(m_window); }
GetCursorX()91512b93ac6SEd Maste int GetCursorX() { return getcurx(m_window); }
GetCursorY()91612b93ac6SEd Maste int GetCursorY() { return getcury(m_window); }
GetFrame()917435933ddSDimitry Andric Rect GetFrame() {
918435933ddSDimitry Andric return Rect(Point(), GetSize());
919435933ddSDimitry Andric } // Get our rectangle in our own coordinate system
GetParentOrigin()92012b93ac6SEd Maste Point GetParentOrigin() { return Point(GetParentX(), GetParentY()); }
GetSize()92112b93ac6SEd Maste Size GetSize() { return Size(GetWidth(), GetHeight()); }
GetParentX()92212b93ac6SEd Maste int GetParentX() { return getparx(m_window); }
GetParentY()92312b93ac6SEd Maste int GetParentY() { return getpary(m_window); }
GetMaxX()92412b93ac6SEd Maste int GetMaxX() { return getmaxx(m_window); }
GetMaxY()92512b93ac6SEd Maste int GetMaxY() { return getmaxy(m_window); }
GetWidth()92612b93ac6SEd Maste int GetWidth() { return GetMaxX(); }
GetHeight()92712b93ac6SEd Maste int GetHeight() { return GetMaxY(); }
MoveCursor(int x,int y)92812b93ac6SEd Maste void MoveCursor(int x, int y) { ::wmove(m_window, y, x); }
MoveWindow(int x,int y)92912b93ac6SEd Maste void MoveWindow(int x, int y) { MoveWindow(Point(x, y)); }
Resize(int w,int h)93012b93ac6SEd Maste void Resize(int w, int h) { ::wresize(m_window, h, w); }
Resize(const Size & size)931435933ddSDimitry Andric void Resize(const Size &size) {
932435933ddSDimitry Andric ::wresize(m_window, size.height, size.width);
933435933ddSDimitry Andric }
PutChar(int ch)93412b93ac6SEd Maste void PutChar(int ch) { ::waddch(m_window, ch); }
PutCString(const char * s,int len=-1)93512b93ac6SEd Maste void PutCString(const char *s, int len = -1) { ::waddnstr(m_window, s, len); }
Refresh()93612b93ac6SEd Maste void Refresh() { ::wrefresh(m_window); }
DeferredRefresh()937435933ddSDimitry Andric void DeferredRefresh() {
93812b93ac6SEd Maste // We are using panels, so we don't need to call this...
93912b93ac6SEd Maste //::wnoutrefresh(m_window);
94012b93ac6SEd Maste }
SetBackground(int color_pair_idx)941435933ddSDimitry Andric void SetBackground(int color_pair_idx) {
942435933ddSDimitry Andric ::wbkgd(m_window, COLOR_PAIR(color_pair_idx));
943435933ddSDimitry Andric }
UnderlineOn()94412b93ac6SEd Maste void UnderlineOn() { AttributeOn(A_UNDERLINE); }
UnderlineOff()94512b93ac6SEd Maste void UnderlineOff() { AttributeOff(A_UNDERLINE); }
94612b93ac6SEd Maste
PutCStringTruncated(const char * s,int right_pad)947435933ddSDimitry Andric void PutCStringTruncated(const char *s, int right_pad) {
94812b93ac6SEd Maste int bytes_left = GetWidth() - GetCursorX();
949435933ddSDimitry Andric if (bytes_left > right_pad) {
95012b93ac6SEd Maste bytes_left -= right_pad;
95112b93ac6SEd Maste ::waddnstr(m_window, s, bytes_left);
95212b93ac6SEd Maste }
95312b93ac6SEd Maste }
95412b93ac6SEd Maste
MoveWindow(const Point & origin)955435933ddSDimitry Andric void MoveWindow(const Point &origin) {
95612b93ac6SEd Maste const bool moving_window = origin != GetParentOrigin();
957435933ddSDimitry Andric if (m_is_subwin && moving_window) {
95812b93ac6SEd Maste // Can't move subwindows, must delete and re-create
95912b93ac6SEd Maste Size size = GetSize();
960435933ddSDimitry Andric Reset(::subwin(m_parent->m_window, size.height, size.width, origin.y,
961435933ddSDimitry Andric origin.x),
962435933ddSDimitry Andric true);
963435933ddSDimitry Andric } else {
96412b93ac6SEd Maste ::mvwin(m_window, origin.y, origin.x);
96512b93ac6SEd Maste }
96612b93ac6SEd Maste }
96712b93ac6SEd Maste
SetBounds(const Rect & bounds)968435933ddSDimitry Andric void SetBounds(const Rect &bounds) {
96912b93ac6SEd Maste const bool moving_window = bounds.origin != GetParentOrigin();
970435933ddSDimitry Andric if (m_is_subwin && moving_window) {
97112b93ac6SEd Maste // Can't move subwindows, must delete and re-create
972435933ddSDimitry Andric Reset(::subwin(m_parent->m_window, bounds.size.height, bounds.size.width,
973435933ddSDimitry Andric bounds.origin.y, bounds.origin.x),
974435933ddSDimitry Andric true);
975435933ddSDimitry Andric } else {
97612b93ac6SEd Maste if (moving_window)
97712b93ac6SEd Maste MoveWindow(bounds.origin);
97812b93ac6SEd Maste Resize(bounds.size);
97912b93ac6SEd Maste }
98012b93ac6SEd Maste }
98112b93ac6SEd Maste
Printf(const char * format,...)982435933ddSDimitry Andric void Printf(const char *format, ...) __attribute__((format(printf, 2, 3))) {
98312b93ac6SEd Maste va_list args;
98412b93ac6SEd Maste va_start(args, format);
98512b93ac6SEd Maste vwprintw(m_window, format, args);
98612b93ac6SEd Maste va_end(args);
98712b93ac6SEd Maste }
98812b93ac6SEd Maste
Touch()989435933ddSDimitry Andric void Touch() {
99012b93ac6SEd Maste ::touchwin(m_window);
99112b93ac6SEd Maste if (m_parent)
99212b93ac6SEd Maste m_parent->Touch();
99312b93ac6SEd Maste }
99412b93ac6SEd Maste
CreateSubWindow(const char * name,const Rect & bounds,bool make_active)995435933ddSDimitry Andric WindowSP CreateSubWindow(const char *name, const Rect &bounds,
996435933ddSDimitry Andric bool make_active) {
99712b93ac6SEd Maste WindowSP subwindow_sp;
998435933ddSDimitry Andric if (m_window) {
999435933ddSDimitry Andric subwindow_sp.reset(new Window(
1000435933ddSDimitry Andric name, ::subwin(m_window, bounds.size.height, bounds.size.width,
1001435933ddSDimitry Andric bounds.origin.y, bounds.origin.x),
1002435933ddSDimitry Andric true));
100312b93ac6SEd Maste subwindow_sp->m_is_subwin = true;
1004435933ddSDimitry Andric } else {
1005435933ddSDimitry Andric subwindow_sp.reset(
1006435933ddSDimitry Andric new Window(name, ::newwin(bounds.size.height, bounds.size.width,
1007435933ddSDimitry Andric bounds.origin.y, bounds.origin.x),
1008435933ddSDimitry Andric true));
100912b93ac6SEd Maste subwindow_sp->m_is_subwin = false;
101012b93ac6SEd Maste }
101112b93ac6SEd Maste subwindow_sp->m_parent = this;
1012435933ddSDimitry Andric if (make_active) {
101312b93ac6SEd Maste m_prev_active_window_idx = m_curr_active_window_idx;
101412b93ac6SEd Maste m_curr_active_window_idx = m_subwindows.size();
101512b93ac6SEd Maste }
101612b93ac6SEd Maste m_subwindows.push_back(subwindow_sp);
101712b93ac6SEd Maste ::top_panel(subwindow_sp->m_panel);
101812b93ac6SEd Maste m_needs_update = true;
101912b93ac6SEd Maste return subwindow_sp;
102012b93ac6SEd Maste }
102112b93ac6SEd Maste
RemoveSubWindow(Window * window)1022435933ddSDimitry Andric bool RemoveSubWindow(Window *window) {
102312b93ac6SEd Maste Windows::iterator pos, end = m_subwindows.end();
102412b93ac6SEd Maste size_t i = 0;
1025435933ddSDimitry Andric for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) {
1026435933ddSDimitry Andric if ((*pos).get() == window) {
102712b93ac6SEd Maste if (m_prev_active_window_idx == i)
102812b93ac6SEd Maste m_prev_active_window_idx = UINT32_MAX;
1029435933ddSDimitry Andric else if (m_prev_active_window_idx != UINT32_MAX &&
1030435933ddSDimitry Andric m_prev_active_window_idx > i)
103112b93ac6SEd Maste --m_prev_active_window_idx;
103212b93ac6SEd Maste
103312b93ac6SEd Maste if (m_curr_active_window_idx == i)
103412b93ac6SEd Maste m_curr_active_window_idx = UINT32_MAX;
1035435933ddSDimitry Andric else if (m_curr_active_window_idx != UINT32_MAX &&
1036435933ddSDimitry Andric m_curr_active_window_idx > i)
103712b93ac6SEd Maste --m_curr_active_window_idx;
103812b93ac6SEd Maste window->Erase();
103912b93ac6SEd Maste m_subwindows.erase(pos);
104012b93ac6SEd Maste m_needs_update = true;
104112b93ac6SEd Maste if (m_parent)
104212b93ac6SEd Maste m_parent->Touch();
104312b93ac6SEd Maste else
104412b93ac6SEd Maste ::touchwin(stdscr);
104512b93ac6SEd Maste return true;
104612b93ac6SEd Maste }
104712b93ac6SEd Maste }
104812b93ac6SEd Maste return false;
104912b93ac6SEd Maste }
105012b93ac6SEd Maste
FindSubWindow(const char * name)1051435933ddSDimitry Andric WindowSP FindSubWindow(const char *name) {
105212b93ac6SEd Maste Windows::iterator pos, end = m_subwindows.end();
105312b93ac6SEd Maste size_t i = 0;
1054435933ddSDimitry Andric for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) {
1055*b5893f02SDimitry Andric if ((*pos)->m_name == name)
105612b93ac6SEd Maste return *pos;
105712b93ac6SEd Maste }
105812b93ac6SEd Maste return WindowSP();
105912b93ac6SEd Maste }
106012b93ac6SEd Maste
RemoveSubWindows()1061435933ddSDimitry Andric void RemoveSubWindows() {
106212b93ac6SEd Maste m_curr_active_window_idx = UINT32_MAX;
106312b93ac6SEd Maste m_prev_active_window_idx = UINT32_MAX;
106412b93ac6SEd Maste for (Windows::iterator pos = m_subwindows.begin();
1065435933ddSDimitry Andric pos != m_subwindows.end(); pos = m_subwindows.erase(pos)) {
106612b93ac6SEd Maste (*pos)->Erase();
106712b93ac6SEd Maste }
106812b93ac6SEd Maste if (m_parent)
106912b93ac6SEd Maste m_parent->Touch();
107012b93ac6SEd Maste else
107112b93ac6SEd Maste ::touchwin(stdscr);
107212b93ac6SEd Maste }
107312b93ac6SEd Maste
get()1074435933ddSDimitry Andric WINDOW *get() { return m_window; }
107512b93ac6SEd Maste
operator WINDOW*()1076435933ddSDimitry Andric operator WINDOW *() { return m_window; }
107712b93ac6SEd Maste
107812b93ac6SEd Maste //----------------------------------------------------------------------
107912b93ac6SEd Maste // Window drawing utilities
108012b93ac6SEd Maste //----------------------------------------------------------------------
DrawTitleBox(const char * title,const char * bottom_message=nullptr)1081435933ddSDimitry Andric void DrawTitleBox(const char *title, const char *bottom_message = nullptr) {
108212b93ac6SEd Maste attr_t attr = 0;
108312b93ac6SEd Maste if (IsActive())
108412b93ac6SEd Maste attr = A_BOLD | COLOR_PAIR(2);
108512b93ac6SEd Maste else
108612b93ac6SEd Maste attr = 0;
108712b93ac6SEd Maste if (attr)
108812b93ac6SEd Maste AttributeOn(attr);
108912b93ac6SEd Maste
109012b93ac6SEd Maste Box();
109112b93ac6SEd Maste MoveCursor(3, 0);
109212b93ac6SEd Maste
1093435933ddSDimitry Andric if (title && title[0]) {
109412b93ac6SEd Maste PutChar('<');
109512b93ac6SEd Maste PutCString(title);
109612b93ac6SEd Maste PutChar('>');
109712b93ac6SEd Maste }
109812b93ac6SEd Maste
1099435933ddSDimitry Andric if (bottom_message && bottom_message[0]) {
110012b93ac6SEd Maste int bottom_message_length = strlen(bottom_message);
110112b93ac6SEd Maste int x = GetWidth() - 3 - (bottom_message_length + 2);
110212b93ac6SEd Maste
1103435933ddSDimitry Andric if (x > 0) {
110412b93ac6SEd Maste MoveCursor(x, GetHeight() - 1);
110512b93ac6SEd Maste PutChar('[');
110612b93ac6SEd Maste PutCString(bottom_message);
110712b93ac6SEd Maste PutChar(']');
1108435933ddSDimitry Andric } else {
110912b93ac6SEd Maste MoveCursor(1, GetHeight() - 1);
111012b93ac6SEd Maste PutChar('[');
111112b93ac6SEd Maste PutCStringTruncated(bottom_message, 1);
111212b93ac6SEd Maste }
111312b93ac6SEd Maste }
111412b93ac6SEd Maste if (attr)
111512b93ac6SEd Maste AttributeOff(attr);
111612b93ac6SEd Maste }
111712b93ac6SEd Maste
Draw(bool force)1118435933ddSDimitry Andric virtual void Draw(bool force) {
111912b93ac6SEd Maste if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw(*this, force))
112012b93ac6SEd Maste return;
112112b93ac6SEd Maste
112212b93ac6SEd Maste for (auto &subwindow_sp : m_subwindows)
112312b93ac6SEd Maste subwindow_sp->Draw(force);
112412b93ac6SEd Maste }
112512b93ac6SEd Maste
CreateHelpSubwindow()1126435933ddSDimitry Andric bool CreateHelpSubwindow() {
1127435933ddSDimitry Andric if (m_delegate_sp) {
112812b93ac6SEd Maste const char *text = m_delegate_sp->WindowDelegateGetHelpText();
112912b93ac6SEd Maste KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp();
1130435933ddSDimitry Andric if ((text && text[0]) || key_help) {
1131acac075bSDimitry Andric std::unique_ptr<HelpDialogDelegate> help_delegate_ap(
1132435933ddSDimitry Andric new HelpDialogDelegate(text, key_help));
113312b93ac6SEd Maste const size_t num_lines = help_delegate_ap->GetNumLines();
113412b93ac6SEd Maste const size_t max_length = help_delegate_ap->GetMaxLineLength();
113512b93ac6SEd Maste Rect bounds = GetBounds();
113612b93ac6SEd Maste bounds.Inset(1, 1);
1137435933ddSDimitry Andric if (max_length + 4 < static_cast<size_t>(bounds.size.width)) {
113812b93ac6SEd Maste bounds.origin.x += (bounds.size.width - max_length + 4) / 2;
113912b93ac6SEd Maste bounds.size.width = max_length + 4;
1140435933ddSDimitry Andric } else {
1141435933ddSDimitry Andric if (bounds.size.width > 100) {
114212b93ac6SEd Maste const int inset_w = bounds.size.width / 4;
114312b93ac6SEd Maste bounds.origin.x += inset_w;
114412b93ac6SEd Maste bounds.size.width -= 2 * inset_w;
114512b93ac6SEd Maste }
114612b93ac6SEd Maste }
114712b93ac6SEd Maste
1148435933ddSDimitry Andric if (num_lines + 2 < static_cast<size_t>(bounds.size.height)) {
114912b93ac6SEd Maste bounds.origin.y += (bounds.size.height - num_lines + 2) / 2;
115012b93ac6SEd Maste bounds.size.height = num_lines + 2;
1151435933ddSDimitry Andric } else {
1152435933ddSDimitry Andric if (bounds.size.height > 100) {
115312b93ac6SEd Maste const int inset_h = bounds.size.height / 4;
115412b93ac6SEd Maste bounds.origin.y += inset_h;
115512b93ac6SEd Maste bounds.size.height -= 2 * inset_h;
115612b93ac6SEd Maste }
115712b93ac6SEd Maste }
115812b93ac6SEd Maste WindowSP help_window_sp;
115912b93ac6SEd Maste Window *parent_window = GetParent();
116012b93ac6SEd Maste if (parent_window)
116112b93ac6SEd Maste help_window_sp = parent_window->CreateSubWindow("Help", bounds, true);
116212b93ac6SEd Maste else
116312b93ac6SEd Maste help_window_sp = CreateSubWindow("Help", bounds, true);
1164435933ddSDimitry Andric help_window_sp->SetDelegate(
1165435933ddSDimitry Andric WindowDelegateSP(help_delegate_ap.release()));
116612b93ac6SEd Maste return true;
116712b93ac6SEd Maste }
116812b93ac6SEd Maste }
116912b93ac6SEd Maste return false;
117012b93ac6SEd Maste }
117112b93ac6SEd Maste
HandleChar(int key)1172435933ddSDimitry Andric virtual HandleCharResult HandleChar(int key) {
117312b93ac6SEd Maste // Always check the active window first
117412b93ac6SEd Maste HandleCharResult result = eKeyNotHandled;
117512b93ac6SEd Maste WindowSP active_window_sp = GetActiveWindow();
1176435933ddSDimitry Andric if (active_window_sp) {
117712b93ac6SEd Maste result = active_window_sp->HandleChar(key);
117812b93ac6SEd Maste if (result != eKeyNotHandled)
117912b93ac6SEd Maste return result;
118012b93ac6SEd Maste }
118112b93ac6SEd Maste
1182435933ddSDimitry Andric if (m_delegate_sp) {
118312b93ac6SEd Maste result = m_delegate_sp->WindowDelegateHandleChar(*this, key);
118412b93ac6SEd Maste if (result != eKeyNotHandled)
118512b93ac6SEd Maste return result;
118612b93ac6SEd Maste }
118712b93ac6SEd Maste
11884ba319b5SDimitry Andric // Then check for any windows that want any keys that weren't handled. This
11894ba319b5SDimitry Andric // is typically only for a menubar. Make a copy of the subwindows in case
11904ba319b5SDimitry Andric // any HandleChar() functions muck with the subwindows. If we don't do
11914ba319b5SDimitry Andric // this, we can crash when iterating over the subwindows.
119212b93ac6SEd Maste Windows subwindows(m_subwindows);
1193435933ddSDimitry Andric for (auto subwindow_sp : subwindows) {
1194435933ddSDimitry Andric if (!subwindow_sp->m_can_activate) {
119512b93ac6SEd Maste HandleCharResult result = subwindow_sp->HandleChar(key);
119612b93ac6SEd Maste if (result != eKeyNotHandled)
119712b93ac6SEd Maste return result;
119812b93ac6SEd Maste }
119912b93ac6SEd Maste }
120012b93ac6SEd Maste
120112b93ac6SEd Maste return eKeyNotHandled;
120212b93ac6SEd Maste }
120312b93ac6SEd Maste
SetActiveWindow(Window * window)1204435933ddSDimitry Andric bool SetActiveWindow(Window *window) {
120512b93ac6SEd Maste const size_t num_subwindows = m_subwindows.size();
1206435933ddSDimitry Andric for (size_t i = 0; i < num_subwindows; ++i) {
1207435933ddSDimitry Andric if (m_subwindows[i].get() == window) {
120812b93ac6SEd Maste m_prev_active_window_idx = m_curr_active_window_idx;
120912b93ac6SEd Maste ::top_panel(window->m_panel);
121012b93ac6SEd Maste m_curr_active_window_idx = i;
121112b93ac6SEd Maste return true;
121212b93ac6SEd Maste }
121312b93ac6SEd Maste }
121412b93ac6SEd Maste return false;
121512b93ac6SEd Maste }
121612b93ac6SEd Maste
GetActiveWindow()1217435933ddSDimitry Andric WindowSP GetActiveWindow() {
1218435933ddSDimitry Andric if (!m_subwindows.empty()) {
1219435933ddSDimitry Andric if (m_curr_active_window_idx >= m_subwindows.size()) {
1220435933ddSDimitry Andric if (m_prev_active_window_idx < m_subwindows.size()) {
122112b93ac6SEd Maste m_curr_active_window_idx = m_prev_active_window_idx;
122212b93ac6SEd Maste m_prev_active_window_idx = UINT32_MAX;
1223435933ddSDimitry Andric } else if (IsActive()) {
122412b93ac6SEd Maste m_prev_active_window_idx = UINT32_MAX;
122512b93ac6SEd Maste m_curr_active_window_idx = UINT32_MAX;
122612b93ac6SEd Maste
122712b93ac6SEd Maste // Find first window that wants to be active if this window is active
122812b93ac6SEd Maste const size_t num_subwindows = m_subwindows.size();
1229435933ddSDimitry Andric for (size_t i = 0; i < num_subwindows; ++i) {
1230435933ddSDimitry Andric if (m_subwindows[i]->GetCanBeActive()) {
123112b93ac6SEd Maste m_curr_active_window_idx = i;
123212b93ac6SEd Maste break;
123312b93ac6SEd Maste }
123412b93ac6SEd Maste }
123512b93ac6SEd Maste }
123612b93ac6SEd Maste }
123712b93ac6SEd Maste
123812b93ac6SEd Maste if (m_curr_active_window_idx < m_subwindows.size())
123912b93ac6SEd Maste return m_subwindows[m_curr_active_window_idx];
124012b93ac6SEd Maste }
124112b93ac6SEd Maste return WindowSP();
124212b93ac6SEd Maste }
124312b93ac6SEd Maste
GetCanBeActive() const1244435933ddSDimitry Andric bool GetCanBeActive() const { return m_can_activate; }
124512b93ac6SEd Maste
SetCanBeActive(bool b)1246435933ddSDimitry Andric void SetCanBeActive(bool b) { m_can_activate = b; }
124712b93ac6SEd Maste
GetDelegate() const1248435933ddSDimitry Andric const WindowDelegateSP &GetDelegate() const { return m_delegate_sp; }
124912b93ac6SEd Maste
SetDelegate(const WindowDelegateSP & delegate_sp)1250435933ddSDimitry Andric void SetDelegate(const WindowDelegateSP &delegate_sp) {
125112b93ac6SEd Maste m_delegate_sp = delegate_sp;
125212b93ac6SEd Maste }
125312b93ac6SEd Maste
GetParent() const1254435933ddSDimitry Andric Window *GetParent() const { return m_parent; }
125512b93ac6SEd Maste
IsActive() const1256435933ddSDimitry Andric bool IsActive() const {
125712b93ac6SEd Maste if (m_parent)
125812b93ac6SEd Maste return m_parent->GetActiveWindow().get() == this;
125912b93ac6SEd Maste else
126012b93ac6SEd Maste return true; // Top level window is always active
126112b93ac6SEd Maste }
126212b93ac6SEd Maste
SelectNextWindowAsActive()1263435933ddSDimitry Andric void SelectNextWindowAsActive() {
126412b93ac6SEd Maste // Move active focus to next window
126512b93ac6SEd Maste const size_t num_subwindows = m_subwindows.size();
1266435933ddSDimitry Andric if (m_curr_active_window_idx == UINT32_MAX) {
126712b93ac6SEd Maste uint32_t idx = 0;
1268435933ddSDimitry Andric for (auto subwindow_sp : m_subwindows) {
1269435933ddSDimitry Andric if (subwindow_sp->GetCanBeActive()) {
127012b93ac6SEd Maste m_curr_active_window_idx = idx;
127112b93ac6SEd Maste break;
127212b93ac6SEd Maste }
127312b93ac6SEd Maste ++idx;
127412b93ac6SEd Maste }
1275435933ddSDimitry Andric } else if (m_curr_active_window_idx + 1 < num_subwindows) {
127612b93ac6SEd Maste bool handled = false;
127712b93ac6SEd Maste m_prev_active_window_idx = m_curr_active_window_idx;
1278435933ddSDimitry Andric for (size_t idx = m_curr_active_window_idx + 1; idx < num_subwindows;
1279435933ddSDimitry Andric ++idx) {
1280435933ddSDimitry Andric if (m_subwindows[idx]->GetCanBeActive()) {
128112b93ac6SEd Maste m_curr_active_window_idx = idx;
128212b93ac6SEd Maste handled = true;
128312b93ac6SEd Maste break;
128412b93ac6SEd Maste }
128512b93ac6SEd Maste }
1286435933ddSDimitry Andric if (!handled) {
1287435933ddSDimitry Andric for (size_t idx = 0; idx <= m_prev_active_window_idx; ++idx) {
1288435933ddSDimitry Andric if (m_subwindows[idx]->GetCanBeActive()) {
128912b93ac6SEd Maste m_curr_active_window_idx = idx;
129012b93ac6SEd Maste break;
129112b93ac6SEd Maste }
129212b93ac6SEd Maste }
129312b93ac6SEd Maste }
1294435933ddSDimitry Andric } else {
129512b93ac6SEd Maste m_prev_active_window_idx = m_curr_active_window_idx;
1296435933ddSDimitry Andric for (size_t idx = 0; idx < num_subwindows; ++idx) {
1297435933ddSDimitry Andric if (m_subwindows[idx]->GetCanBeActive()) {
129812b93ac6SEd Maste m_curr_active_window_idx = idx;
129912b93ac6SEd Maste break;
130012b93ac6SEd Maste }
130112b93ac6SEd Maste }
130212b93ac6SEd Maste }
130312b93ac6SEd Maste }
130412b93ac6SEd Maste
GetName() const1305435933ddSDimitry Andric const char *GetName() const { return m_name.c_str(); }
13069f2f44ceSEd Maste
130712b93ac6SEd Maste protected:
130812b93ac6SEd Maste std::string m_name;
130912b93ac6SEd Maste WINDOW *m_window;
131012b93ac6SEd Maste PANEL *m_panel;
131112b93ac6SEd Maste Window *m_parent;
131212b93ac6SEd Maste Windows m_subwindows;
131312b93ac6SEd Maste WindowDelegateSP m_delegate_sp;
131412b93ac6SEd Maste uint32_t m_curr_active_window_idx;
131512b93ac6SEd Maste uint32_t m_prev_active_window_idx;
131612b93ac6SEd Maste bool m_delete;
131712b93ac6SEd Maste bool m_needs_update;
131812b93ac6SEd Maste bool m_can_activate;
131912b93ac6SEd Maste bool m_is_subwin;
132012b93ac6SEd Maste
132112b93ac6SEd Maste private:
132212b93ac6SEd Maste DISALLOW_COPY_AND_ASSIGN(Window);
132312b93ac6SEd Maste };
132412b93ac6SEd Maste
1325435933ddSDimitry Andric class MenuDelegate {
132612b93ac6SEd Maste public:
13279f2f44ceSEd Maste virtual ~MenuDelegate() = default;
132812b93ac6SEd Maste
1329435933ddSDimitry Andric virtual MenuActionResult MenuDelegateAction(Menu &menu) = 0;
133012b93ac6SEd Maste };
133112b93ac6SEd Maste
1332435933ddSDimitry Andric class Menu : public WindowDelegate {
133312b93ac6SEd Maste public:
1334435933ddSDimitry Andric enum class Type { Invalid, Bar, Item, Separator };
133512b93ac6SEd Maste
133612b93ac6SEd Maste // Menubar or separator constructor
133712b93ac6SEd Maste Menu(Type type);
133812b93ac6SEd Maste
133912b93ac6SEd Maste // Menuitem constructor
1340435933ddSDimitry Andric Menu(const char *name, const char *key_name, int key_value,
134112b93ac6SEd Maste uint64_t identifier);
134212b93ac6SEd Maste
13439f2f44ceSEd Maste ~Menu() override = default;
134412b93ac6SEd Maste
GetDelegate() const1345435933ddSDimitry Andric const MenuDelegateSP &GetDelegate() const { return m_delegate_sp; }
134612b93ac6SEd Maste
SetDelegate(const MenuDelegateSP & delegate_sp)1347435933ddSDimitry Andric void SetDelegate(const MenuDelegateSP &delegate_sp) {
134812b93ac6SEd Maste m_delegate_sp = delegate_sp;
134912b93ac6SEd Maste }
135012b93ac6SEd Maste
1351435933ddSDimitry Andric void RecalculateNameLengths();
135212b93ac6SEd Maste
1353435933ddSDimitry Andric void AddSubmenu(const MenuSP &menu_sp);
135412b93ac6SEd Maste
1355435933ddSDimitry Andric int DrawAndRunMenu(Window &window);
135612b93ac6SEd Maste
1357435933ddSDimitry Andric void DrawMenuTitle(Window &window, bool highlight);
135812b93ac6SEd Maste
1359435933ddSDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override;
136012b93ac6SEd Maste
1361435933ddSDimitry Andric HandleCharResult WindowDelegateHandleChar(Window &window, int key) override;
136212b93ac6SEd Maste
ActionPrivate(Menu & menu)1363435933ddSDimitry Andric MenuActionResult ActionPrivate(Menu &menu) {
136412b93ac6SEd Maste MenuActionResult result = MenuActionResult::NotHandled;
1365435933ddSDimitry Andric if (m_delegate_sp) {
136612b93ac6SEd Maste result = m_delegate_sp->MenuDelegateAction(menu);
136712b93ac6SEd Maste if (result != MenuActionResult::NotHandled)
136812b93ac6SEd Maste return result;
1369435933ddSDimitry Andric } else if (m_parent) {
137012b93ac6SEd Maste result = m_parent->ActionPrivate(menu);
137112b93ac6SEd Maste if (result != MenuActionResult::NotHandled)
137212b93ac6SEd Maste return result;
137312b93ac6SEd Maste }
137412b93ac6SEd Maste return m_canned_result;
137512b93ac6SEd Maste }
137612b93ac6SEd Maste
Action()1377435933ddSDimitry Andric MenuActionResult Action() {
13784ba319b5SDimitry Andric // Call the recursive action so it can try to handle it with the menu
13794ba319b5SDimitry Andric // delegate, and if not, try our parent menu
138012b93ac6SEd Maste return ActionPrivate(*this);
138112b93ac6SEd Maste }
138212b93ac6SEd Maste
SetCannedResult(MenuActionResult result)1383435933ddSDimitry Andric void SetCannedResult(MenuActionResult result) { m_canned_result = result; }
138412b93ac6SEd Maste
GetSubmenus()1385435933ddSDimitry Andric Menus &GetSubmenus() { return m_submenus; }
138612b93ac6SEd Maste
GetSubmenus() const1387435933ddSDimitry Andric const Menus &GetSubmenus() const { return m_submenus; }
138812b93ac6SEd Maste
GetSelectedSubmenuIndex() const1389435933ddSDimitry Andric int GetSelectedSubmenuIndex() const { return m_selected; }
139012b93ac6SEd Maste
SetSelectedSubmenuIndex(int idx)1391435933ddSDimitry Andric void SetSelectedSubmenuIndex(int idx) { m_selected = idx; }
139212b93ac6SEd Maste
GetType() const1393435933ddSDimitry Andric Type GetType() const { return m_type; }
139412b93ac6SEd Maste
GetStartingColumn() const1395435933ddSDimitry Andric int GetStartingColumn() const { return m_start_col; }
139612b93ac6SEd Maste
SetStartingColumn(int col)1397435933ddSDimitry Andric void SetStartingColumn(int col) { m_start_col = col; }
139812b93ac6SEd Maste
GetKeyValue() const1399435933ddSDimitry Andric int GetKeyValue() const { return m_key_value; }
140012b93ac6SEd Maste
SetKeyValue(int key_value)1401435933ddSDimitry Andric void SetKeyValue(int key_value) { m_key_value = key_value; }
140212b93ac6SEd Maste
GetName()1403435933ddSDimitry Andric std::string &GetName() { return m_name; }
140412b93ac6SEd Maste
GetKeyName()1405435933ddSDimitry Andric std::string &GetKeyName() { return m_key_name; }
140612b93ac6SEd Maste
GetDrawWidth() const1407435933ddSDimitry Andric int GetDrawWidth() const {
140812b93ac6SEd Maste return m_max_submenu_name_length + m_max_submenu_key_name_length + 8;
140912b93ac6SEd Maste }
141012b93ac6SEd Maste
GetIdentifier() const1411435933ddSDimitry Andric uint64_t GetIdentifier() const { return m_identifier; }
141212b93ac6SEd Maste
SetIdentifier(uint64_t identifier)1413435933ddSDimitry Andric void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
141412b93ac6SEd Maste
141512b93ac6SEd Maste protected:
141612b93ac6SEd Maste std::string m_name;
141712b93ac6SEd Maste std::string m_key_name;
141812b93ac6SEd Maste uint64_t m_identifier;
141912b93ac6SEd Maste Type m_type;
142012b93ac6SEd Maste int m_key_value;
142112b93ac6SEd Maste int m_start_col;
142212b93ac6SEd Maste int m_max_submenu_name_length;
142312b93ac6SEd Maste int m_max_submenu_key_name_length;
142412b93ac6SEd Maste int m_selected;
142512b93ac6SEd Maste Menu *m_parent;
142612b93ac6SEd Maste Menus m_submenus;
142712b93ac6SEd Maste WindowSP m_menu_window_sp;
142812b93ac6SEd Maste MenuActionResult m_canned_result;
142912b93ac6SEd Maste MenuDelegateSP m_delegate_sp;
143012b93ac6SEd Maste };
143112b93ac6SEd Maste
143212b93ac6SEd Maste // Menubar or separator constructor
Menu(Type type)1433435933ddSDimitry Andric Menu::Menu(Type type)
1434435933ddSDimitry Andric : m_name(), m_key_name(), m_identifier(0), m_type(type), m_key_value(0),
1435435933ddSDimitry Andric m_start_col(0), m_max_submenu_name_length(0),
1436435933ddSDimitry Andric m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr),
1437435933ddSDimitry Andric m_submenus(), m_canned_result(MenuActionResult::NotHandled),
1438435933ddSDimitry Andric m_delegate_sp() {}
143912b93ac6SEd Maste
144012b93ac6SEd Maste // Menuitem constructor
Menu(const char * name,const char * key_name,int key_value,uint64_t identifier)1441435933ddSDimitry Andric Menu::Menu(const char *name, const char *key_name, int key_value,
1442435933ddSDimitry Andric uint64_t identifier)
1443435933ddSDimitry Andric : m_name(), m_key_name(), m_identifier(identifier), m_type(Type::Invalid),
1444435933ddSDimitry Andric m_key_value(key_value), m_start_col(0), m_max_submenu_name_length(0),
1445435933ddSDimitry Andric m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr),
1446435933ddSDimitry Andric m_submenus(), m_canned_result(MenuActionResult::NotHandled),
1447435933ddSDimitry Andric m_delegate_sp() {
1448435933ddSDimitry Andric if (name && name[0]) {
144912b93ac6SEd Maste m_name = name;
145012b93ac6SEd Maste m_type = Type::Item;
145112b93ac6SEd Maste if (key_name && key_name[0])
145212b93ac6SEd Maste m_key_name = key_name;
1453435933ddSDimitry Andric } else {
145412b93ac6SEd Maste m_type = Type::Separator;
145512b93ac6SEd Maste }
145612b93ac6SEd Maste }
145712b93ac6SEd Maste
RecalculateNameLengths()1458435933ddSDimitry Andric void Menu::RecalculateNameLengths() {
145912b93ac6SEd Maste m_max_submenu_name_length = 0;
146012b93ac6SEd Maste m_max_submenu_key_name_length = 0;
146112b93ac6SEd Maste Menus &submenus = GetSubmenus();
146212b93ac6SEd Maste const size_t num_submenus = submenus.size();
1463435933ddSDimitry Andric for (size_t i = 0; i < num_submenus; ++i) {
146412b93ac6SEd Maste Menu *submenu = submenus[i].get();
14650127ef0fSEd Maste if (static_cast<size_t>(m_max_submenu_name_length) < submenu->m_name.size())
146612b93ac6SEd Maste m_max_submenu_name_length = submenu->m_name.size();
1467435933ddSDimitry Andric if (static_cast<size_t>(m_max_submenu_key_name_length) <
1468435933ddSDimitry Andric submenu->m_key_name.size())
146912b93ac6SEd Maste m_max_submenu_key_name_length = submenu->m_key_name.size();
147012b93ac6SEd Maste }
147112b93ac6SEd Maste }
147212b93ac6SEd Maste
AddSubmenu(const MenuSP & menu_sp)1473435933ddSDimitry Andric void Menu::AddSubmenu(const MenuSP &menu_sp) {
147412b93ac6SEd Maste menu_sp->m_parent = this;
14750127ef0fSEd Maste if (static_cast<size_t>(m_max_submenu_name_length) < menu_sp->m_name.size())
147612b93ac6SEd Maste m_max_submenu_name_length = menu_sp->m_name.size();
1477435933ddSDimitry Andric if (static_cast<size_t>(m_max_submenu_key_name_length) <
1478435933ddSDimitry Andric menu_sp->m_key_name.size())
147912b93ac6SEd Maste m_max_submenu_key_name_length = menu_sp->m_key_name.size();
148012b93ac6SEd Maste m_submenus.push_back(menu_sp);
148112b93ac6SEd Maste }
148212b93ac6SEd Maste
DrawMenuTitle(Window & window,bool highlight)1483435933ddSDimitry Andric void Menu::DrawMenuTitle(Window &window, bool highlight) {
1484435933ddSDimitry Andric if (m_type == Type::Separator) {
148512b93ac6SEd Maste window.MoveCursor(0, window.GetCursorY());
148612b93ac6SEd Maste window.PutChar(ACS_LTEE);
148712b93ac6SEd Maste int width = window.GetWidth();
1488435933ddSDimitry Andric if (width > 2) {
148912b93ac6SEd Maste width -= 2;
14900127ef0fSEd Maste for (int i = 0; i < width; ++i)
149112b93ac6SEd Maste window.PutChar(ACS_HLINE);
149212b93ac6SEd Maste }
149312b93ac6SEd Maste window.PutChar(ACS_RTEE);
1494435933ddSDimitry Andric } else {
149512b93ac6SEd Maste const int shortcut_key = m_key_value;
149612b93ac6SEd Maste bool underlined_shortcut = false;
149712b93ac6SEd Maste const attr_t hilgight_attr = A_REVERSE;
149812b93ac6SEd Maste if (highlight)
149912b93ac6SEd Maste window.AttributeOn(hilgight_attr);
1500435933ddSDimitry Andric if (isprint(shortcut_key)) {
150112b93ac6SEd Maste size_t lower_pos = m_name.find(tolower(shortcut_key));
150212b93ac6SEd Maste size_t upper_pos = m_name.find(toupper(shortcut_key));
150312b93ac6SEd Maste const char *name = m_name.c_str();
150412b93ac6SEd Maste size_t pos = std::min<size_t>(lower_pos, upper_pos);
1505435933ddSDimitry Andric if (pos != std::string::npos) {
150612b93ac6SEd Maste underlined_shortcut = true;
1507435933ddSDimitry Andric if (pos > 0) {
150812b93ac6SEd Maste window.PutCString(name, pos);
150912b93ac6SEd Maste name += pos;
151012b93ac6SEd Maste }
151112b93ac6SEd Maste const attr_t shortcut_attr = A_UNDERLINE | A_BOLD;
151212b93ac6SEd Maste window.AttributeOn(shortcut_attr);
151312b93ac6SEd Maste window.PutChar(name[0]);
151412b93ac6SEd Maste window.AttributeOff(shortcut_attr);
151512b93ac6SEd Maste name++;
151612b93ac6SEd Maste if (name[0])
151712b93ac6SEd Maste window.PutCString(name);
151812b93ac6SEd Maste }
151912b93ac6SEd Maste }
152012b93ac6SEd Maste
1521435933ddSDimitry Andric if (!underlined_shortcut) {
152212b93ac6SEd Maste window.PutCString(m_name.c_str());
152312b93ac6SEd Maste }
152412b93ac6SEd Maste
152512b93ac6SEd Maste if (highlight)
152612b93ac6SEd Maste window.AttributeOff(hilgight_attr);
152712b93ac6SEd Maste
1528435933ddSDimitry Andric if (m_key_name.empty()) {
1529435933ddSDimitry Andric if (!underlined_shortcut && isprint(m_key_value)) {
153012b93ac6SEd Maste window.AttributeOn(COLOR_PAIR(3));
153112b93ac6SEd Maste window.Printf(" (%c)", m_key_value);
153212b93ac6SEd Maste window.AttributeOff(COLOR_PAIR(3));
153312b93ac6SEd Maste }
1534435933ddSDimitry Andric } else {
153512b93ac6SEd Maste window.AttributeOn(COLOR_PAIR(3));
153612b93ac6SEd Maste window.Printf(" (%s)", m_key_name.c_str());
153712b93ac6SEd Maste window.AttributeOff(COLOR_PAIR(3));
153812b93ac6SEd Maste }
153912b93ac6SEd Maste }
154012b93ac6SEd Maste }
154112b93ac6SEd Maste
WindowDelegateDraw(Window & window,bool force)1542435933ddSDimitry Andric bool Menu::WindowDelegateDraw(Window &window, bool force) {
154312b93ac6SEd Maste Menus &submenus = GetSubmenus();
154412b93ac6SEd Maste const size_t num_submenus = submenus.size();
154512b93ac6SEd Maste const int selected_idx = GetSelectedSubmenuIndex();
154612b93ac6SEd Maste Menu::Type menu_type = GetType();
1547435933ddSDimitry Andric switch (menu_type) {
1548435933ddSDimitry Andric case Menu::Type::Bar: {
154912b93ac6SEd Maste window.SetBackground(2);
155012b93ac6SEd Maste window.MoveCursor(0, 0);
1551435933ddSDimitry Andric for (size_t i = 0; i < num_submenus; ++i) {
155212b93ac6SEd Maste Menu *menu = submenus[i].get();
155312b93ac6SEd Maste if (i > 0)
155412b93ac6SEd Maste window.PutChar(' ');
155512b93ac6SEd Maste menu->SetStartingColumn(window.GetCursorX());
155612b93ac6SEd Maste window.PutCString("| ");
155712b93ac6SEd Maste menu->DrawMenuTitle(window, false);
155812b93ac6SEd Maste }
155912b93ac6SEd Maste window.PutCString(" |");
156012b93ac6SEd Maste window.DeferredRefresh();
1561435933ddSDimitry Andric } break;
156212b93ac6SEd Maste
1563435933ddSDimitry Andric case Menu::Type::Item: {
156412b93ac6SEd Maste int y = 1;
156512b93ac6SEd Maste int x = 3;
156612b93ac6SEd Maste // Draw the menu
156712b93ac6SEd Maste int cursor_x = 0;
156812b93ac6SEd Maste int cursor_y = 0;
156912b93ac6SEd Maste window.Erase();
157012b93ac6SEd Maste window.SetBackground(2);
157112b93ac6SEd Maste window.Box();
1572435933ddSDimitry Andric for (size_t i = 0; i < num_submenus; ++i) {
1573435933ddSDimitry Andric const bool is_selected = (i == static_cast<size_t>(selected_idx));
157412b93ac6SEd Maste window.MoveCursor(x, y + i);
1575435933ddSDimitry Andric if (is_selected) {
157612b93ac6SEd Maste // Remember where we want the cursor to be
157712b93ac6SEd Maste cursor_x = x - 1;
157812b93ac6SEd Maste cursor_y = y + i;
157912b93ac6SEd Maste }
158012b93ac6SEd Maste submenus[i]->DrawMenuTitle(window, is_selected);
158112b93ac6SEd Maste }
158212b93ac6SEd Maste window.MoveCursor(cursor_x, cursor_y);
158312b93ac6SEd Maste window.DeferredRefresh();
1584435933ddSDimitry Andric } break;
158512b93ac6SEd Maste
158612b93ac6SEd Maste default:
158712b93ac6SEd Maste case Menu::Type::Separator:
158812b93ac6SEd Maste break;
158912b93ac6SEd Maste }
159012b93ac6SEd Maste return true; // Drawing handled...
159112b93ac6SEd Maste }
159212b93ac6SEd Maste
WindowDelegateHandleChar(Window & window,int key)1593435933ddSDimitry Andric HandleCharResult Menu::WindowDelegateHandleChar(Window &window, int key) {
159412b93ac6SEd Maste HandleCharResult result = eKeyNotHandled;
159512b93ac6SEd Maste
159612b93ac6SEd Maste Menus &submenus = GetSubmenus();
159712b93ac6SEd Maste const size_t num_submenus = submenus.size();
159812b93ac6SEd Maste const int selected_idx = GetSelectedSubmenuIndex();
159912b93ac6SEd Maste Menu::Type menu_type = GetType();
1600435933ddSDimitry Andric if (menu_type == Menu::Type::Bar) {
160112b93ac6SEd Maste MenuSP run_menu_sp;
1602435933ddSDimitry Andric switch (key) {
160312b93ac6SEd Maste case KEY_DOWN:
160412b93ac6SEd Maste case KEY_UP:
160512b93ac6SEd Maste // Show last menu or first menu
16060127ef0fSEd Maste if (selected_idx < static_cast<int>(num_submenus))
160712b93ac6SEd Maste run_menu_sp = submenus[selected_idx];
160812b93ac6SEd Maste else if (!submenus.empty())
160912b93ac6SEd Maste run_menu_sp = submenus.front();
161012b93ac6SEd Maste result = eKeyHandled;
161112b93ac6SEd Maste break;
161212b93ac6SEd Maste
161312b93ac6SEd Maste case KEY_RIGHT:
161412b93ac6SEd Maste ++m_selected;
16150127ef0fSEd Maste if (m_selected >= static_cast<int>(num_submenus))
161612b93ac6SEd Maste m_selected = 0;
16170127ef0fSEd Maste if (m_selected < static_cast<int>(num_submenus))
161812b93ac6SEd Maste run_menu_sp = submenus[m_selected];
161912b93ac6SEd Maste else if (!submenus.empty())
162012b93ac6SEd Maste run_menu_sp = submenus.front();
162112b93ac6SEd Maste result = eKeyHandled;
162212b93ac6SEd Maste break;
162312b93ac6SEd Maste
162412b93ac6SEd Maste case KEY_LEFT:
162512b93ac6SEd Maste --m_selected;
162612b93ac6SEd Maste if (m_selected < 0)
162712b93ac6SEd Maste m_selected = num_submenus - 1;
16280127ef0fSEd Maste if (m_selected < static_cast<int>(num_submenus))
162912b93ac6SEd Maste run_menu_sp = submenus[m_selected];
163012b93ac6SEd Maste else if (!submenus.empty())
163112b93ac6SEd Maste run_menu_sp = submenus.front();
163212b93ac6SEd Maste result = eKeyHandled;
163312b93ac6SEd Maste break;
163412b93ac6SEd Maste
163512b93ac6SEd Maste default:
1636435933ddSDimitry Andric for (size_t i = 0; i < num_submenus; ++i) {
1637435933ddSDimitry Andric if (submenus[i]->GetKeyValue() == key) {
163812b93ac6SEd Maste SetSelectedSubmenuIndex(i);
163912b93ac6SEd Maste run_menu_sp = submenus[i];
164012b93ac6SEd Maste result = eKeyHandled;
164112b93ac6SEd Maste break;
164212b93ac6SEd Maste }
164312b93ac6SEd Maste }
164412b93ac6SEd Maste break;
164512b93ac6SEd Maste }
164612b93ac6SEd Maste
1647435933ddSDimitry Andric if (run_menu_sp) {
16484ba319b5SDimitry Andric // Run the action on this menu in case we need to populate the menu with
16494ba319b5SDimitry Andric // dynamic content and also in case check marks, and any other menu
16504ba319b5SDimitry Andric // decorations need to be calculated
165112b93ac6SEd Maste if (run_menu_sp->Action() == MenuActionResult::Quit)
165212b93ac6SEd Maste return eQuitApplication;
165312b93ac6SEd Maste
165412b93ac6SEd Maste Rect menu_bounds;
165512b93ac6SEd Maste menu_bounds.origin.x = run_menu_sp->GetStartingColumn();
165612b93ac6SEd Maste menu_bounds.origin.y = 1;
165712b93ac6SEd Maste menu_bounds.size.width = run_menu_sp->GetDrawWidth();
165812b93ac6SEd Maste menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2;
165912b93ac6SEd Maste if (m_menu_window_sp)
166012b93ac6SEd Maste window.GetParent()->RemoveSubWindow(m_menu_window_sp.get());
166112b93ac6SEd Maste
1662435933ddSDimitry Andric m_menu_window_sp = window.GetParent()->CreateSubWindow(
1663435933ddSDimitry Andric run_menu_sp->GetName().c_str(), menu_bounds, true);
166412b93ac6SEd Maste m_menu_window_sp->SetDelegate(run_menu_sp);
166512b93ac6SEd Maste }
1666435933ddSDimitry Andric } else if (menu_type == Menu::Type::Item) {
1667435933ddSDimitry Andric switch (key) {
166812b93ac6SEd Maste case KEY_DOWN:
1669435933ddSDimitry Andric if (m_submenus.size() > 1) {
167012b93ac6SEd Maste const int start_select = m_selected;
1671435933ddSDimitry Andric while (++m_selected != start_select) {
16720127ef0fSEd Maste if (static_cast<size_t>(m_selected) >= num_submenus)
167312b93ac6SEd Maste m_selected = 0;
167412b93ac6SEd Maste if (m_submenus[m_selected]->GetType() == Type::Separator)
167512b93ac6SEd Maste continue;
167612b93ac6SEd Maste else
167712b93ac6SEd Maste break;
167812b93ac6SEd Maste }
167912b93ac6SEd Maste return eKeyHandled;
168012b93ac6SEd Maste }
168112b93ac6SEd Maste break;
168212b93ac6SEd Maste
168312b93ac6SEd Maste case KEY_UP:
1684435933ddSDimitry Andric if (m_submenus.size() > 1) {
168512b93ac6SEd Maste const int start_select = m_selected;
1686435933ddSDimitry Andric while (--m_selected != start_select) {
16870127ef0fSEd Maste if (m_selected < static_cast<int>(0))
168812b93ac6SEd Maste m_selected = num_submenus - 1;
168912b93ac6SEd Maste if (m_submenus[m_selected]->GetType() == Type::Separator)
169012b93ac6SEd Maste continue;
169112b93ac6SEd Maste else
169212b93ac6SEd Maste break;
169312b93ac6SEd Maste }
169412b93ac6SEd Maste return eKeyHandled;
169512b93ac6SEd Maste }
169612b93ac6SEd Maste break;
169712b93ac6SEd Maste
169812b93ac6SEd Maste case KEY_RETURN:
1699435933ddSDimitry Andric if (static_cast<size_t>(selected_idx) < num_submenus) {
170012b93ac6SEd Maste if (submenus[selected_idx]->Action() == MenuActionResult::Quit)
170112b93ac6SEd Maste return eQuitApplication;
170212b93ac6SEd Maste window.GetParent()->RemoveSubWindow(&window);
170312b93ac6SEd Maste return eKeyHandled;
170412b93ac6SEd Maste }
170512b93ac6SEd Maste break;
170612b93ac6SEd Maste
1707435933ddSDimitry Andric case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in
1708435933ddSDimitry Andric // case other chars are entered for escaped sequences
170912b93ac6SEd Maste window.GetParent()->RemoveSubWindow(&window);
171012b93ac6SEd Maste return eKeyHandled;
171112b93ac6SEd Maste
171212b93ac6SEd Maste default:
1713435933ddSDimitry Andric for (size_t i = 0; i < num_submenus; ++i) {
171412b93ac6SEd Maste Menu *menu = submenus[i].get();
1715435933ddSDimitry Andric if (menu->GetKeyValue() == key) {
171612b93ac6SEd Maste SetSelectedSubmenuIndex(i);
171712b93ac6SEd Maste window.GetParent()->RemoveSubWindow(&window);
171812b93ac6SEd Maste if (menu->Action() == MenuActionResult::Quit)
171912b93ac6SEd Maste return eQuitApplication;
172012b93ac6SEd Maste return eKeyHandled;
172112b93ac6SEd Maste }
172212b93ac6SEd Maste }
172312b93ac6SEd Maste break;
172412b93ac6SEd Maste }
1725435933ddSDimitry Andric } else if (menu_type == Menu::Type::Separator) {
172612b93ac6SEd Maste }
172712b93ac6SEd Maste return result;
172812b93ac6SEd Maste }
172912b93ac6SEd Maste
1730435933ddSDimitry Andric class Application {
173112b93ac6SEd Maste public:
Application(FILE * in,FILE * out)1732435933ddSDimitry Andric Application(FILE *in, FILE *out)
1733435933ddSDimitry Andric : m_window_sp(), m_screen(nullptr), m_in(in), m_out(out) {}
173412b93ac6SEd Maste
~Application()1735435933ddSDimitry Andric ~Application() {
173612b93ac6SEd Maste m_window_delegates.clear();
173712b93ac6SEd Maste m_window_sp.reset();
1738435933ddSDimitry Andric if (m_screen) {
173912b93ac6SEd Maste ::delscreen(m_screen);
17404bb0738eSEd Maste m_screen = nullptr;
174112b93ac6SEd Maste }
174212b93ac6SEd Maste }
174312b93ac6SEd Maste
Initialize()1744435933ddSDimitry Andric void Initialize() {
174512b93ac6SEd Maste ::setlocale(LC_ALL, "");
174612b93ac6SEd Maste ::setlocale(LC_CTYPE, "");
17474bb0738eSEd Maste m_screen = ::newterm(nullptr, m_out, m_in);
174812b93ac6SEd Maste ::start_color();
174912b93ac6SEd Maste ::curs_set(0);
175012b93ac6SEd Maste ::noecho();
175112b93ac6SEd Maste ::keypad(stdscr, TRUE);
175212b93ac6SEd Maste }
175312b93ac6SEd Maste
Terminate()1754435933ddSDimitry Andric void Terminate() { ::endwin(); }
175512b93ac6SEd Maste
Run(Debugger & debugger)1756435933ddSDimitry Andric void Run(Debugger &debugger) {
175712b93ac6SEd Maste bool done = false;
175812b93ac6SEd Maste int delay_in_tenths_of_a_second = 1;
175912b93ac6SEd Maste
17604ba319b5SDimitry Andric // Alas the threading model in curses is a bit lame so we need to resort to
17614ba319b5SDimitry Andric // polling every 0.5 seconds. We could poll for stdin ourselves and then
17624ba319b5SDimitry Andric // pass the keys down but then we need to translate all of the escape
17634ba319b5SDimitry Andric // sequences ourselves. So we resort to polling for input because we need
17644ba319b5SDimitry Andric // to receive async process events while in this loop.
176512b93ac6SEd Maste
1766435933ddSDimitry Andric halfdelay(delay_in_tenths_of_a_second); // Poll using some number of tenths
1767435933ddSDimitry Andric // of seconds seconds when calling
1768435933ddSDimitry Andric // Window::GetChar()
176912b93ac6SEd Maste
1770435933ddSDimitry Andric ListenerSP listener_sp(
1771435933ddSDimitry Andric Listener::MakeListener("lldb.IOHandler.curses.Application"));
177212b93ac6SEd Maste ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass());
177312b93ac6SEd Maste ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass());
177412b93ac6SEd Maste ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass());
177512b93ac6SEd Maste debugger.EnableForwardEvents(listener_sp);
177612b93ac6SEd Maste
177712b93ac6SEd Maste bool update = true;
177812b93ac6SEd Maste #if defined(__APPLE__)
177912b93ac6SEd Maste std::deque<int> escape_chars;
178012b93ac6SEd Maste #endif
178112b93ac6SEd Maste
1782435933ddSDimitry Andric while (!done) {
1783435933ddSDimitry Andric if (update) {
178412b93ac6SEd Maste m_window_sp->Draw(false);
17854ba319b5SDimitry Andric // All windows should be calling Window::DeferredRefresh() instead of
17864ba319b5SDimitry Andric // Window::Refresh() so we can do a single update and avoid any screen
17874ba319b5SDimitry Andric // blinking
178812b93ac6SEd Maste update_panels();
178912b93ac6SEd Maste
1790435933ddSDimitry Andric // Cursor hiding isn't working on MacOSX, so hide it in the top left
1791435933ddSDimitry Andric // corner
179212b93ac6SEd Maste m_window_sp->MoveCursor(0, 0);
179312b93ac6SEd Maste
179412b93ac6SEd Maste doupdate();
179512b93ac6SEd Maste update = false;
179612b93ac6SEd Maste }
179712b93ac6SEd Maste
179812b93ac6SEd Maste #if defined(__APPLE__)
17994ba319b5SDimitry Andric // Terminal.app doesn't map its function keys correctly, F1-F4 default
18004ba319b5SDimitry Andric // to: \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if
1801435933ddSDimitry Andric // possible
180212b93ac6SEd Maste int ch;
180312b93ac6SEd Maste if (escape_chars.empty())
180412b93ac6SEd Maste ch = m_window_sp->GetChar();
1805435933ddSDimitry Andric else {
180612b93ac6SEd Maste ch = escape_chars.front();
180712b93ac6SEd Maste escape_chars.pop_front();
180812b93ac6SEd Maste }
1809435933ddSDimitry Andric if (ch == KEY_ESCAPE) {
181012b93ac6SEd Maste int ch2 = m_window_sp->GetChar();
1811435933ddSDimitry Andric if (ch2 == 'O') {
181212b93ac6SEd Maste int ch3 = m_window_sp->GetChar();
1813435933ddSDimitry Andric switch (ch3) {
1814435933ddSDimitry Andric case 'P':
1815435933ddSDimitry Andric ch = KEY_F(1);
1816435933ddSDimitry Andric break;
1817435933ddSDimitry Andric case 'Q':
1818435933ddSDimitry Andric ch = KEY_F(2);
1819435933ddSDimitry Andric break;
1820435933ddSDimitry Andric case 'R':
1821435933ddSDimitry Andric ch = KEY_F(3);
1822435933ddSDimitry Andric break;
1823435933ddSDimitry Andric case 'S':
1824435933ddSDimitry Andric ch = KEY_F(4);
1825435933ddSDimitry Andric break;
182612b93ac6SEd Maste default:
182712b93ac6SEd Maste escape_chars.push_back(ch2);
182812b93ac6SEd Maste if (ch3 != -1)
182912b93ac6SEd Maste escape_chars.push_back(ch3);
183012b93ac6SEd Maste break;
183112b93ac6SEd Maste }
1832435933ddSDimitry Andric } else if (ch2 != -1)
183312b93ac6SEd Maste escape_chars.push_back(ch2);
183412b93ac6SEd Maste }
183512b93ac6SEd Maste #else
183612b93ac6SEd Maste int ch = m_window_sp->GetChar();
183712b93ac6SEd Maste
183812b93ac6SEd Maste #endif
1839435933ddSDimitry Andric if (ch == -1) {
1840435933ddSDimitry Andric if (feof(m_in) || ferror(m_in)) {
184112b93ac6SEd Maste done = true;
1842435933ddSDimitry Andric } else {
184312b93ac6SEd Maste // Just a timeout from using halfdelay(), check for events
184412b93ac6SEd Maste EventSP event_sp;
1845435933ddSDimitry Andric while (listener_sp->PeekAtNextEvent()) {
1846435933ddSDimitry Andric listener_sp->GetEvent(event_sp, std::chrono::seconds(0));
184712b93ac6SEd Maste
1848435933ddSDimitry Andric if (event_sp) {
184912b93ac6SEd Maste Broadcaster *broadcaster = event_sp->GetBroadcaster();
1850435933ddSDimitry Andric if (broadcaster) {
185112b93ac6SEd Maste // uint32_t event_type = event_sp->GetType();
1852435933ddSDimitry Andric ConstString broadcaster_class(
1853435933ddSDimitry Andric broadcaster->GetBroadcasterClass());
1854435933ddSDimitry Andric if (broadcaster_class == broadcaster_class_process) {
1855435933ddSDimitry Andric debugger.GetCommandInterpreter().UpdateExecutionContext(
1856435933ddSDimitry Andric nullptr);
185712b93ac6SEd Maste update = true;
185812b93ac6SEd Maste continue; // Don't get any key, just update our view
185912b93ac6SEd Maste }
186012b93ac6SEd Maste }
186112b93ac6SEd Maste }
186212b93ac6SEd Maste }
186312b93ac6SEd Maste }
1864435933ddSDimitry Andric } else {
186512b93ac6SEd Maste HandleCharResult key_result = m_window_sp->HandleChar(ch);
1866435933ddSDimitry Andric switch (key_result) {
186712b93ac6SEd Maste case eKeyHandled:
18684bb0738eSEd Maste debugger.GetCommandInterpreter().UpdateExecutionContext(nullptr);
186912b93ac6SEd Maste update = true;
187012b93ac6SEd Maste break;
187112b93ac6SEd Maste case eKeyNotHandled:
187212b93ac6SEd Maste break;
187312b93ac6SEd Maste case eQuitApplication:
187412b93ac6SEd Maste done = true;
187512b93ac6SEd Maste break;
187612b93ac6SEd Maste }
187712b93ac6SEd Maste }
187812b93ac6SEd Maste }
187912b93ac6SEd Maste
188012b93ac6SEd Maste debugger.CancelForwardEvents(listener_sp);
188112b93ac6SEd Maste }
188212b93ac6SEd Maste
GetMainWindow()1883435933ddSDimitry Andric WindowSP &GetMainWindow() {
188412b93ac6SEd Maste if (!m_window_sp)
188512b93ac6SEd Maste m_window_sp.reset(new Window("main", stdscr, false));
188612b93ac6SEd Maste return m_window_sp;
188712b93ac6SEd Maste }
188812b93ac6SEd Maste
GetWindowDelegates()1889435933ddSDimitry Andric WindowDelegates &GetWindowDelegates() { return m_window_delegates; }
189012b93ac6SEd Maste
189112b93ac6SEd Maste protected:
189212b93ac6SEd Maste WindowSP m_window_sp;
189312b93ac6SEd Maste WindowDelegates m_window_delegates;
189412b93ac6SEd Maste SCREEN *m_screen;
189512b93ac6SEd Maste FILE *m_in;
189612b93ac6SEd Maste FILE *m_out;
189712b93ac6SEd Maste };
189812b93ac6SEd Maste
189912b93ac6SEd Maste } // namespace curses
190012b93ac6SEd Maste
190112b93ac6SEd Maste using namespace curses;
190212b93ac6SEd Maste
1903435933ddSDimitry Andric struct Row {
1904435933ddSDimitry Andric ValueObjectManager value;
190512b93ac6SEd Maste Row *parent;
1906435933ddSDimitry Andric // The process stop ID when the children were calculated.
1907435933ddSDimitry Andric uint32_t children_stop_id;
190812b93ac6SEd Maste int row_idx;
190912b93ac6SEd Maste int x;
191012b93ac6SEd Maste int y;
191112b93ac6SEd Maste bool might_have_children;
191212b93ac6SEd Maste bool expanded;
191312b93ac6SEd Maste bool calculated_children;
191412b93ac6SEd Maste std::vector<Row> children;
191512b93ac6SEd Maste
RowRow1916435933ddSDimitry Andric Row(const ValueObjectSP &v, Row *p)
1917435933ddSDimitry Andric : value(v, lldb::eDynamicDontRunTarget, true), parent(p), row_idx(0),
1918435933ddSDimitry Andric x(1), y(1), might_have_children(v ? v->MightHaveChildren() : false),
1919435933ddSDimitry Andric expanded(false), calculated_children(false), children() {}
192012b93ac6SEd Maste
GetDepthRow1921435933ddSDimitry Andric size_t GetDepth() const {
192212b93ac6SEd Maste if (parent)
192312b93ac6SEd Maste return 1 + parent->GetDepth();
192412b93ac6SEd Maste return 0;
192512b93ac6SEd Maste }
192612b93ac6SEd Maste
ExpandRow1927435933ddSDimitry Andric void Expand() {
192812b93ac6SEd Maste expanded = true;
1929435933ddSDimitry Andric }
1930435933ddSDimitry Andric
GetChildrenRow1931435933ddSDimitry Andric std::vector<Row> &GetChildren() {
1932435933ddSDimitry Andric ProcessSP process_sp = value.GetProcessSP();
1933435933ddSDimitry Andric auto stop_id = process_sp->GetStopID();
1934435933ddSDimitry Andric if (process_sp && stop_id != children_stop_id) {
1935435933ddSDimitry Andric children_stop_id = stop_id;
1936435933ddSDimitry Andric calculated_children = false;
1937435933ddSDimitry Andric }
1938435933ddSDimitry Andric if (!calculated_children) {
1939435933ddSDimitry Andric children.clear();
194012b93ac6SEd Maste calculated_children = true;
1941435933ddSDimitry Andric ValueObjectSP valobj = value.GetSP();
1942435933ddSDimitry Andric if (valobj) {
194312b93ac6SEd Maste const size_t num_children = valobj->GetNumChildren();
1944435933ddSDimitry Andric for (size_t i = 0; i < num_children; ++i) {
194512b93ac6SEd Maste children.push_back(Row(valobj->GetChildAtIndex(i, true), this));
194612b93ac6SEd Maste }
194712b93ac6SEd Maste }
194812b93ac6SEd Maste }
1949435933ddSDimitry Andric return children;
195012b93ac6SEd Maste }
195112b93ac6SEd Maste
UnexpandRow1952435933ddSDimitry Andric void Unexpand() {
195312b93ac6SEd Maste expanded = false;
1954435933ddSDimitry Andric calculated_children = false;
1955435933ddSDimitry Andric children.clear();
195612b93ac6SEd Maste }
195712b93ac6SEd Maste
DrawTreeRow1958435933ddSDimitry Andric void DrawTree(Window &window) {
195912b93ac6SEd Maste if (parent)
196012b93ac6SEd Maste parent->DrawTreeForChild(window, this, 0);
196112b93ac6SEd Maste
1962435933ddSDimitry Andric if (might_have_children) {
19634ba319b5SDimitry Andric // It we can get UTF8 characters to work we should try to use the
19644ba319b5SDimitry Andric // "symbol" UTF8 string below
196512b93ac6SEd Maste // const char *symbol = "";
196612b93ac6SEd Maste // if (row.expanded)
196712b93ac6SEd Maste // symbol = "\xe2\x96\xbd ";
196812b93ac6SEd Maste // else
196912b93ac6SEd Maste // symbol = "\xe2\x96\xb7 ";
197012b93ac6SEd Maste // window.PutCString (symbol);
197112b93ac6SEd Maste
19724ba319b5SDimitry Andric // The ACS_DARROW and ACS_RARROW don't look very nice they are just a 'v'
19734ba319b5SDimitry Andric // or '>' character...
197412b93ac6SEd Maste // if (expanded)
197512b93ac6SEd Maste // window.PutChar (ACS_DARROW);
197612b93ac6SEd Maste // else
197712b93ac6SEd Maste // window.PutChar (ACS_RARROW);
19784ba319b5SDimitry Andric // Since we can't find any good looking right arrow/down arrow symbols,
19794ba319b5SDimitry Andric // just use a diamond...
198012b93ac6SEd Maste window.PutChar(ACS_DIAMOND);
198112b93ac6SEd Maste window.PutChar(ACS_HLINE);
198212b93ac6SEd Maste }
198312b93ac6SEd Maste }
198412b93ac6SEd Maste
DrawTreeForChildRow1985435933ddSDimitry Andric void DrawTreeForChild(Window &window, Row *child, uint32_t reverse_depth) {
198612b93ac6SEd Maste if (parent)
198712b93ac6SEd Maste parent->DrawTreeForChild(window, this, reverse_depth + 1);
198812b93ac6SEd Maste
1989435933ddSDimitry Andric if (&GetChildren().back() == child) {
199012b93ac6SEd Maste // Last child
1991435933ddSDimitry Andric if (reverse_depth == 0) {
199212b93ac6SEd Maste window.PutChar(ACS_LLCORNER);
199312b93ac6SEd Maste window.PutChar(ACS_HLINE);
1994435933ddSDimitry Andric } else {
199512b93ac6SEd Maste window.PutChar(' ');
199612b93ac6SEd Maste window.PutChar(' ');
199712b93ac6SEd Maste }
1998435933ddSDimitry Andric } else {
1999435933ddSDimitry Andric if (reverse_depth == 0) {
200012b93ac6SEd Maste window.PutChar(ACS_LTEE);
200112b93ac6SEd Maste window.PutChar(ACS_HLINE);
2002435933ddSDimitry Andric } else {
200312b93ac6SEd Maste window.PutChar(ACS_VLINE);
200412b93ac6SEd Maste window.PutChar(' ');
200512b93ac6SEd Maste }
200612b93ac6SEd Maste }
200712b93ac6SEd Maste }
200812b93ac6SEd Maste };
200912b93ac6SEd Maste
2010435933ddSDimitry Andric struct DisplayOptions {
201112b93ac6SEd Maste bool show_types;
201212b93ac6SEd Maste };
201312b93ac6SEd Maste
201412b93ac6SEd Maste class TreeItem;
201512b93ac6SEd Maste
2016435933ddSDimitry Andric class TreeDelegate {
201712b93ac6SEd Maste public:
20184bb0738eSEd Maste TreeDelegate() = default;
20199f2f44ceSEd Maste virtual ~TreeDelegate() = default;
20209f2f44ceSEd Maste
202112b93ac6SEd Maste virtual void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) = 0;
202212b93ac6SEd Maste virtual void TreeDelegateGenerateChildren(TreeItem &item) = 0;
2023435933ddSDimitry Andric virtual bool TreeDelegateItemSelected(
2024435933ddSDimitry Andric TreeItem &item) = 0; // Return true if we need to update views
202512b93ac6SEd Maste };
20269f2f44ceSEd Maste
202712b93ac6SEd Maste typedef std::shared_ptr<TreeDelegate> TreeDelegateSP;
202812b93ac6SEd Maste
2029435933ddSDimitry Andric class TreeItem {
203012b93ac6SEd Maste public:
TreeItem(TreeItem * parent,TreeDelegate & delegate,bool might_have_children)2031435933ddSDimitry Andric TreeItem(TreeItem *parent, TreeDelegate &delegate, bool might_have_children)
2032435933ddSDimitry Andric : m_parent(parent), m_delegate(delegate), m_user_data(nullptr),
2033435933ddSDimitry Andric m_identifier(0), m_row_idx(-1), m_children(),
2034435933ddSDimitry Andric m_might_have_children(might_have_children), m_is_expanded(false) {}
203512b93ac6SEd Maste
operator =(const TreeItem & rhs)2036435933ddSDimitry Andric TreeItem &operator=(const TreeItem &rhs) {
2037435933ddSDimitry Andric if (this != &rhs) {
203812b93ac6SEd Maste m_parent = rhs.m_parent;
203912b93ac6SEd Maste m_delegate = rhs.m_delegate;
20400127ef0fSEd Maste m_user_data = rhs.m_user_data;
204112b93ac6SEd Maste m_identifier = rhs.m_identifier;
204212b93ac6SEd Maste m_row_idx = rhs.m_row_idx;
204312b93ac6SEd Maste m_children = rhs.m_children;
204412b93ac6SEd Maste m_might_have_children = rhs.m_might_have_children;
204512b93ac6SEd Maste m_is_expanded = rhs.m_is_expanded;
204612b93ac6SEd Maste }
204712b93ac6SEd Maste return *this;
204812b93ac6SEd Maste }
204912b93ac6SEd Maste
GetDepth() const2050435933ddSDimitry Andric size_t GetDepth() const {
205112b93ac6SEd Maste if (m_parent)
205212b93ac6SEd Maste return 1 + m_parent->GetDepth();
205312b93ac6SEd Maste return 0;
205412b93ac6SEd Maste }
205512b93ac6SEd Maste
GetRowIndex() const2056435933ddSDimitry Andric int GetRowIndex() const { return m_row_idx; }
205712b93ac6SEd Maste
ClearChildren()2058435933ddSDimitry Andric void ClearChildren() { m_children.clear(); }
205912b93ac6SEd Maste
Resize(size_t n,const TreeItem & t)2060435933ddSDimitry Andric void Resize(size_t n, const TreeItem &t) { m_children.resize(n, t); }
206112b93ac6SEd Maste
operator [](size_t i)2062435933ddSDimitry Andric TreeItem &operator[](size_t i) { return m_children[i]; }
206312b93ac6SEd Maste
SetRowIndex(int row_idx)2064435933ddSDimitry Andric void SetRowIndex(int row_idx) { m_row_idx = row_idx; }
206512b93ac6SEd Maste
GetNumChildren()2066435933ddSDimitry Andric size_t GetNumChildren() {
206712b93ac6SEd Maste m_delegate.TreeDelegateGenerateChildren(*this);
206812b93ac6SEd Maste return m_children.size();
206912b93ac6SEd Maste }
207012b93ac6SEd Maste
ItemWasSelected()2071435933ddSDimitry Andric void ItemWasSelected() { m_delegate.TreeDelegateItemSelected(*this); }
20729f2f44ceSEd Maste
CalculateRowIndexes(int & row_idx)2073435933ddSDimitry Andric void CalculateRowIndexes(int &row_idx) {
207412b93ac6SEd Maste SetRowIndex(row_idx);
207512b93ac6SEd Maste ++row_idx;
207612b93ac6SEd Maste
20770127ef0fSEd Maste const bool expanded = IsExpanded();
20780127ef0fSEd Maste
20794ba319b5SDimitry Andric // The root item must calculate its children, or we must calculate the
20804ba319b5SDimitry Andric // number of children if the item is expanded
20814bb0738eSEd Maste if (m_parent == nullptr || expanded)
208212b93ac6SEd Maste GetNumChildren();
208312b93ac6SEd Maste
2084435933ddSDimitry Andric for (auto &item : m_children) {
208512b93ac6SEd Maste if (expanded)
208612b93ac6SEd Maste item.CalculateRowIndexes(row_idx);
208712b93ac6SEd Maste else
208812b93ac6SEd Maste item.SetRowIndex(-1);
208912b93ac6SEd Maste }
209012b93ac6SEd Maste }
209112b93ac6SEd Maste
GetParent()2092435933ddSDimitry Andric TreeItem *GetParent() { return m_parent; }
209312b93ac6SEd Maste
IsExpanded() const2094435933ddSDimitry Andric bool IsExpanded() const { return m_is_expanded; }
209512b93ac6SEd Maste
Expand()2096435933ddSDimitry Andric void Expand() { m_is_expanded = true; }
209712b93ac6SEd Maste
Unexpand()2098435933ddSDimitry Andric void Unexpand() { m_is_expanded = false; }
209912b93ac6SEd Maste
Draw(Window & window,const int first_visible_row,const uint32_t selected_row_idx,int & row_idx,int & num_rows_left)2100435933ddSDimitry Andric bool Draw(Window &window, const int first_visible_row,
2101435933ddSDimitry Andric const uint32_t selected_row_idx, int &row_idx, int &num_rows_left) {
210212b93ac6SEd Maste if (num_rows_left <= 0)
210312b93ac6SEd Maste return false;
210412b93ac6SEd Maste
2105435933ddSDimitry Andric if (m_row_idx >= first_visible_row) {
210612b93ac6SEd Maste window.MoveCursor(2, row_idx + 1);
210712b93ac6SEd Maste
210812b93ac6SEd Maste if (m_parent)
210912b93ac6SEd Maste m_parent->DrawTreeForChild(window, this, 0);
211012b93ac6SEd Maste
2111435933ddSDimitry Andric if (m_might_have_children) {
2112435933ddSDimitry Andric // It we can get UTF8 characters to work we should try to use the
21134ba319b5SDimitry Andric // "symbol" UTF8 string below
211412b93ac6SEd Maste // const char *symbol = "";
211512b93ac6SEd Maste // if (row.expanded)
211612b93ac6SEd Maste // symbol = "\xe2\x96\xbd ";
211712b93ac6SEd Maste // else
211812b93ac6SEd Maste // symbol = "\xe2\x96\xb7 ";
211912b93ac6SEd Maste // window.PutCString (symbol);
212012b93ac6SEd Maste
212112b93ac6SEd Maste // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
212212b93ac6SEd Maste // 'v' or '>' character...
212312b93ac6SEd Maste // if (expanded)
212412b93ac6SEd Maste // window.PutChar (ACS_DARROW);
212512b93ac6SEd Maste // else
212612b93ac6SEd Maste // window.PutChar (ACS_RARROW);
21274ba319b5SDimitry Andric // Since we can't find any good looking right arrow/down arrow symbols,
21284ba319b5SDimitry Andric // just use a diamond...
212912b93ac6SEd Maste window.PutChar(ACS_DIAMOND);
213012b93ac6SEd Maste window.PutChar(ACS_HLINE);
213112b93ac6SEd Maste }
2132435933ddSDimitry Andric bool highlight = (selected_row_idx == static_cast<size_t>(m_row_idx)) &&
2133435933ddSDimitry Andric window.IsActive();
213412b93ac6SEd Maste
213512b93ac6SEd Maste if (highlight)
213612b93ac6SEd Maste window.AttributeOn(A_REVERSE);
213712b93ac6SEd Maste
213812b93ac6SEd Maste m_delegate.TreeDelegateDrawTreeItem(*this, window);
213912b93ac6SEd Maste
214012b93ac6SEd Maste if (highlight)
214112b93ac6SEd Maste window.AttributeOff(A_REVERSE);
214212b93ac6SEd Maste ++row_idx;
214312b93ac6SEd Maste --num_rows_left;
214412b93ac6SEd Maste }
214512b93ac6SEd Maste
214612b93ac6SEd Maste if (num_rows_left <= 0)
214712b93ac6SEd Maste return false; // We are done drawing...
214812b93ac6SEd Maste
2149435933ddSDimitry Andric if (IsExpanded()) {
2150435933ddSDimitry Andric for (auto &item : m_children) {
21514ba319b5SDimitry Andric // If we displayed all the rows and item.Draw() returns false we are
21524ba319b5SDimitry Andric // done drawing and can exit this for loop
2153435933ddSDimitry Andric if (!item.Draw(window, first_visible_row, selected_row_idx, row_idx,
2154435933ddSDimitry Andric num_rows_left))
215512b93ac6SEd Maste break;
215612b93ac6SEd Maste }
215712b93ac6SEd Maste }
215812b93ac6SEd Maste return num_rows_left >= 0; // Return true if not done drawing yet
215912b93ac6SEd Maste }
216012b93ac6SEd Maste
DrawTreeForChild(Window & window,TreeItem * child,uint32_t reverse_depth)2161435933ddSDimitry Andric void DrawTreeForChild(Window &window, TreeItem *child,
2162435933ddSDimitry Andric uint32_t reverse_depth) {
216312b93ac6SEd Maste if (m_parent)
216412b93ac6SEd Maste m_parent->DrawTreeForChild(window, this, reverse_depth + 1);
216512b93ac6SEd Maste
2166435933ddSDimitry Andric if (&m_children.back() == child) {
216712b93ac6SEd Maste // Last child
2168435933ddSDimitry Andric if (reverse_depth == 0) {
216912b93ac6SEd Maste window.PutChar(ACS_LLCORNER);
217012b93ac6SEd Maste window.PutChar(ACS_HLINE);
2171435933ddSDimitry Andric } else {
217212b93ac6SEd Maste window.PutChar(' ');
217312b93ac6SEd Maste window.PutChar(' ');
217412b93ac6SEd Maste }
2175435933ddSDimitry Andric } else {
2176435933ddSDimitry Andric if (reverse_depth == 0) {
217712b93ac6SEd Maste window.PutChar(ACS_LTEE);
217812b93ac6SEd Maste window.PutChar(ACS_HLINE);
2179435933ddSDimitry Andric } else {
218012b93ac6SEd Maste window.PutChar(ACS_VLINE);
218112b93ac6SEd Maste window.PutChar(' ');
218212b93ac6SEd Maste }
218312b93ac6SEd Maste }
218412b93ac6SEd Maste }
218512b93ac6SEd Maste
GetItemForRowIndex(uint32_t row_idx)2186435933ddSDimitry Andric TreeItem *GetItemForRowIndex(uint32_t row_idx) {
21870127ef0fSEd Maste if (static_cast<uint32_t>(m_row_idx) == row_idx)
218812b93ac6SEd Maste return this;
218912b93ac6SEd Maste if (m_children.empty())
21904bb0738eSEd Maste return nullptr;
2191435933ddSDimitry Andric if (IsExpanded()) {
2192435933ddSDimitry Andric for (auto &item : m_children) {
219312b93ac6SEd Maste TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx);
219412b93ac6SEd Maste if (selected_item_ptr)
219512b93ac6SEd Maste return selected_item_ptr;
219612b93ac6SEd Maste }
219712b93ac6SEd Maste }
21984bb0738eSEd Maste return nullptr;
219912b93ac6SEd Maste }
220012b93ac6SEd Maste
GetUserData() const2201435933ddSDimitry Andric void *GetUserData() const { return m_user_data; }
22020127ef0fSEd Maste
SetUserData(void * user_data)2203435933ddSDimitry Andric void SetUserData(void *user_data) { m_user_data = user_data; }
22040127ef0fSEd Maste
GetIdentifier() const2205435933ddSDimitry Andric uint64_t GetIdentifier() const { return m_identifier; }
220612b93ac6SEd Maste
SetIdentifier(uint64_t identifier)2207435933ddSDimitry Andric void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
220812b93ac6SEd Maste
SetMightHaveChildren(bool b)2209435933ddSDimitry Andric void SetMightHaveChildren(bool b) { m_might_have_children = b; }
22100127ef0fSEd Maste
221112b93ac6SEd Maste protected:
221212b93ac6SEd Maste TreeItem *m_parent;
221312b93ac6SEd Maste TreeDelegate &m_delegate;
22140127ef0fSEd Maste void *m_user_data;
221512b93ac6SEd Maste uint64_t m_identifier;
2216435933ddSDimitry Andric int m_row_idx; // Zero based visible row index, -1 if not visible or for the
2217435933ddSDimitry Andric // root item
221812b93ac6SEd Maste std::vector<TreeItem> m_children;
221912b93ac6SEd Maste bool m_might_have_children;
222012b93ac6SEd Maste bool m_is_expanded;
222112b93ac6SEd Maste };
222212b93ac6SEd Maste
2223435933ddSDimitry Andric class TreeWindowDelegate : public WindowDelegate {
222412b93ac6SEd Maste public:
TreeWindowDelegate(Debugger & debugger,const TreeDelegateSP & delegate_sp)2225435933ddSDimitry Andric TreeWindowDelegate(Debugger &debugger, const TreeDelegateSP &delegate_sp)
2226435933ddSDimitry Andric : m_debugger(debugger), m_delegate_sp(delegate_sp),
2227435933ddSDimitry Andric m_root(nullptr, *delegate_sp, true), m_selected_item(nullptr),
2228435933ddSDimitry Andric m_num_rows(0), m_selected_row_idx(0), m_first_visible_row(0),
2229435933ddSDimitry Andric m_min_x(0), m_min_y(0), m_max_x(0), m_max_y(0) {}
223012b93ac6SEd Maste
NumVisibleRows() const2231435933ddSDimitry Andric int NumVisibleRows() const { return m_max_y - m_min_y; }
223212b93ac6SEd Maste
WindowDelegateDraw(Window & window,bool force)2233435933ddSDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override {
2234435933ddSDimitry Andric ExecutionContext exe_ctx(
2235435933ddSDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext());
223612b93ac6SEd Maste Process *process = exe_ctx.GetProcessPtr();
223712b93ac6SEd Maste
223812b93ac6SEd Maste bool display_content = false;
2239435933ddSDimitry Andric if (process) {
224012b93ac6SEd Maste StateType state = process->GetState();
2241435933ddSDimitry Andric if (StateIsStoppedState(state, true)) {
224212b93ac6SEd Maste // We are stopped, so it is ok to
224312b93ac6SEd Maste display_content = true;
2244435933ddSDimitry Andric } else if (StateIsRunningState(state)) {
224512b93ac6SEd Maste return true; // Don't do any updating when we are running
224612b93ac6SEd Maste }
224712b93ac6SEd Maste }
224812b93ac6SEd Maste
224912b93ac6SEd Maste m_min_x = 2;
225012b93ac6SEd Maste m_min_y = 1;
225112b93ac6SEd Maste m_max_x = window.GetWidth() - 1;
225212b93ac6SEd Maste m_max_y = window.GetHeight() - 1;
225312b93ac6SEd Maste
225412b93ac6SEd Maste window.Erase();
225512b93ac6SEd Maste window.DrawTitleBox(window.GetName());
225612b93ac6SEd Maste
2257435933ddSDimitry Andric if (display_content) {
225812b93ac6SEd Maste const int num_visible_rows = NumVisibleRows();
225912b93ac6SEd Maste m_num_rows = 0;
226012b93ac6SEd Maste m_root.CalculateRowIndexes(m_num_rows);
226112b93ac6SEd Maste
22624ba319b5SDimitry Andric // If we unexpanded while having something selected our total number of
22634ba319b5SDimitry Andric // rows is less than the num visible rows, then make sure we show all the
22644ba319b5SDimitry Andric // rows by setting the first visible row accordingly.
226512b93ac6SEd Maste if (m_first_visible_row > 0 && m_num_rows < num_visible_rows)
226612b93ac6SEd Maste m_first_visible_row = 0;
226712b93ac6SEd Maste
226812b93ac6SEd Maste // Make sure the selected row is always visible
226912b93ac6SEd Maste if (m_selected_row_idx < m_first_visible_row)
227012b93ac6SEd Maste m_first_visible_row = m_selected_row_idx;
227112b93ac6SEd Maste else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
227212b93ac6SEd Maste m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
227312b93ac6SEd Maste
227412b93ac6SEd Maste int row_idx = 0;
227512b93ac6SEd Maste int num_rows_left = num_visible_rows;
2276435933ddSDimitry Andric m_root.Draw(window, m_first_visible_row, m_selected_row_idx, row_idx,
2277435933ddSDimitry Andric num_rows_left);
227812b93ac6SEd Maste // Get the selected row
227912b93ac6SEd Maste m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
2280435933ddSDimitry Andric } else {
22814bb0738eSEd Maste m_selected_item = nullptr;
228212b93ac6SEd Maste }
228312b93ac6SEd Maste
228412b93ac6SEd Maste window.DeferredRefresh();
228512b93ac6SEd Maste
228612b93ac6SEd Maste return true; // Drawing handled
228712b93ac6SEd Maste }
228812b93ac6SEd Maste
WindowDelegateGetHelpText()2289435933ddSDimitry Andric const char *WindowDelegateGetHelpText() override {
229012b93ac6SEd Maste return "Thread window keyboard shortcuts:";
229112b93ac6SEd Maste }
229212b93ac6SEd Maste
WindowDelegateGetKeyHelp()2293435933ddSDimitry Andric KeyHelp *WindowDelegateGetKeyHelp() override {
229412b93ac6SEd Maste static curses::KeyHelp g_source_view_key_help[] = {
229512b93ac6SEd Maste {KEY_UP, "Select previous item"},
229612b93ac6SEd Maste {KEY_DOWN, "Select next item"},
229712b93ac6SEd Maste {KEY_RIGHT, "Expand the selected item"},
2298435933ddSDimitry Andric {KEY_LEFT,
2299435933ddSDimitry Andric "Unexpand the selected item or select parent if not expanded"},
230012b93ac6SEd Maste {KEY_PPAGE, "Page up"},
230112b93ac6SEd Maste {KEY_NPAGE, "Page down"},
230212b93ac6SEd Maste {'h', "Show help dialog"},
230312b93ac6SEd Maste {' ', "Toggle item expansion"},
230412b93ac6SEd Maste {',', "Page up"},
230512b93ac6SEd Maste {'.', "Page down"},
2306435933ddSDimitry Andric {'\0', nullptr}};
230712b93ac6SEd Maste return g_source_view_key_help;
230812b93ac6SEd Maste }
230912b93ac6SEd Maste
WindowDelegateHandleChar(Window & window,int c)2310435933ddSDimitry Andric HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
2311435933ddSDimitry Andric switch (c) {
231212b93ac6SEd Maste case ',':
231312b93ac6SEd Maste case KEY_PPAGE:
231412b93ac6SEd Maste // Page up key
2315435933ddSDimitry Andric if (m_first_visible_row > 0) {
231612b93ac6SEd Maste if (m_first_visible_row > m_max_y)
231712b93ac6SEd Maste m_first_visible_row -= m_max_y;
231812b93ac6SEd Maste else
231912b93ac6SEd Maste m_first_visible_row = 0;
232012b93ac6SEd Maste m_selected_row_idx = m_first_visible_row;
232112b93ac6SEd Maste m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
232212b93ac6SEd Maste if (m_selected_item)
232312b93ac6SEd Maste m_selected_item->ItemWasSelected();
232412b93ac6SEd Maste }
232512b93ac6SEd Maste return eKeyHandled;
232612b93ac6SEd Maste
232712b93ac6SEd Maste case '.':
232812b93ac6SEd Maste case KEY_NPAGE:
232912b93ac6SEd Maste // Page down key
2330435933ddSDimitry Andric if (m_num_rows > m_max_y) {
2331435933ddSDimitry Andric if (m_first_visible_row + m_max_y < m_num_rows) {
233212b93ac6SEd Maste m_first_visible_row += m_max_y;
233312b93ac6SEd Maste m_selected_row_idx = m_first_visible_row;
233412b93ac6SEd Maste m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
233512b93ac6SEd Maste if (m_selected_item)
233612b93ac6SEd Maste m_selected_item->ItemWasSelected();
233712b93ac6SEd Maste }
233812b93ac6SEd Maste }
233912b93ac6SEd Maste return eKeyHandled;
234012b93ac6SEd Maste
234112b93ac6SEd Maste case KEY_UP:
2342435933ddSDimitry Andric if (m_selected_row_idx > 0) {
234312b93ac6SEd Maste --m_selected_row_idx;
234412b93ac6SEd Maste m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
234512b93ac6SEd Maste if (m_selected_item)
234612b93ac6SEd Maste m_selected_item->ItemWasSelected();
234712b93ac6SEd Maste }
234812b93ac6SEd Maste return eKeyHandled;
23499f2f44ceSEd Maste
235012b93ac6SEd Maste case KEY_DOWN:
2351435933ddSDimitry Andric if (m_selected_row_idx + 1 < m_num_rows) {
235212b93ac6SEd Maste ++m_selected_row_idx;
235312b93ac6SEd Maste m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
235412b93ac6SEd Maste if (m_selected_item)
235512b93ac6SEd Maste m_selected_item->ItemWasSelected();
235612b93ac6SEd Maste }
235712b93ac6SEd Maste return eKeyHandled;
235812b93ac6SEd Maste
235912b93ac6SEd Maste case KEY_RIGHT:
2360435933ddSDimitry Andric if (m_selected_item) {
236112b93ac6SEd Maste if (!m_selected_item->IsExpanded())
236212b93ac6SEd Maste m_selected_item->Expand();
236312b93ac6SEd Maste }
236412b93ac6SEd Maste return eKeyHandled;
236512b93ac6SEd Maste
236612b93ac6SEd Maste case KEY_LEFT:
2367435933ddSDimitry Andric if (m_selected_item) {
236812b93ac6SEd Maste if (m_selected_item->IsExpanded())
236912b93ac6SEd Maste m_selected_item->Unexpand();
2370435933ddSDimitry Andric else if (m_selected_item->GetParent()) {
237112b93ac6SEd Maste m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex();
237212b93ac6SEd Maste m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
237312b93ac6SEd Maste if (m_selected_item)
237412b93ac6SEd Maste m_selected_item->ItemWasSelected();
237512b93ac6SEd Maste }
237612b93ac6SEd Maste }
237712b93ac6SEd Maste return eKeyHandled;
237812b93ac6SEd Maste
237912b93ac6SEd Maste case ' ':
238012b93ac6SEd Maste // Toggle expansion state when SPACE is pressed
2381435933ddSDimitry Andric if (m_selected_item) {
238212b93ac6SEd Maste if (m_selected_item->IsExpanded())
238312b93ac6SEd Maste m_selected_item->Unexpand();
238412b93ac6SEd Maste else
238512b93ac6SEd Maste m_selected_item->Expand();
238612b93ac6SEd Maste }
238712b93ac6SEd Maste return eKeyHandled;
238812b93ac6SEd Maste
238912b93ac6SEd Maste case 'h':
239012b93ac6SEd Maste window.CreateHelpSubwindow();
239112b93ac6SEd Maste return eKeyHandled;
239212b93ac6SEd Maste
239312b93ac6SEd Maste default:
239412b93ac6SEd Maste break;
239512b93ac6SEd Maste }
239612b93ac6SEd Maste return eKeyNotHandled;
239712b93ac6SEd Maste }
239812b93ac6SEd Maste
239912b93ac6SEd Maste protected:
240012b93ac6SEd Maste Debugger &m_debugger;
240112b93ac6SEd Maste TreeDelegateSP m_delegate_sp;
240212b93ac6SEd Maste TreeItem m_root;
240312b93ac6SEd Maste TreeItem *m_selected_item;
240412b93ac6SEd Maste int m_num_rows;
240512b93ac6SEd Maste int m_selected_row_idx;
240612b93ac6SEd Maste int m_first_visible_row;
240712b93ac6SEd Maste int m_min_x;
240812b93ac6SEd Maste int m_min_y;
240912b93ac6SEd Maste int m_max_x;
241012b93ac6SEd Maste int m_max_y;
241112b93ac6SEd Maste };
241212b93ac6SEd Maste
2413435933ddSDimitry Andric class FrameTreeDelegate : public TreeDelegate {
241412b93ac6SEd Maste public:
FrameTreeDelegate()2415435933ddSDimitry Andric FrameTreeDelegate() : TreeDelegate() {
2416435933ddSDimitry Andric FormatEntity::Parse(
2417435933ddSDimitry Andric "frame #${frame.index}: {${function.name}${function.pc-offset}}}",
24181c3bbb01SEd Maste m_format);
241912b93ac6SEd Maste }
242012b93ac6SEd Maste
24219f2f44ceSEd Maste ~FrameTreeDelegate() override = default;
242212b93ac6SEd Maste
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)2423435933ddSDimitry Andric void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
24240127ef0fSEd Maste Thread *thread = (Thread *)item.GetUserData();
2425435933ddSDimitry Andric if (thread) {
242612b93ac6SEd Maste const uint64_t frame_idx = item.GetIdentifier();
24270127ef0fSEd Maste StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_idx);
2428435933ddSDimitry Andric if (frame_sp) {
242912b93ac6SEd Maste StreamString strm;
2430435933ddSDimitry Andric const SymbolContext &sc =
2431435933ddSDimitry Andric frame_sp->GetSymbolContext(eSymbolContextEverything);
243212b93ac6SEd Maste ExecutionContext exe_ctx(frame_sp);
2433435933ddSDimitry Andric if (FormatEntity::Format(m_format, strm, &sc, &exe_ctx, nullptr,
2434435933ddSDimitry Andric nullptr, false, false)) {
243512b93ac6SEd Maste int right_pad = 1;
2436435933ddSDimitry Andric window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad);
243712b93ac6SEd Maste }
243812b93ac6SEd Maste }
243912b93ac6SEd Maste }
244012b93ac6SEd Maste }
24419f2f44ceSEd Maste
TreeDelegateGenerateChildren(TreeItem & item)2442435933ddSDimitry Andric void TreeDelegateGenerateChildren(TreeItem &item) override {
244312b93ac6SEd Maste // No children for frames yet...
244412b93ac6SEd Maste }
244512b93ac6SEd Maste
TreeDelegateItemSelected(TreeItem & item)2446435933ddSDimitry Andric bool TreeDelegateItemSelected(TreeItem &item) override {
24470127ef0fSEd Maste Thread *thread = (Thread *)item.GetUserData();
2448435933ddSDimitry Andric if (thread) {
2449435933ddSDimitry Andric thread->GetProcess()->GetThreadList().SetSelectedThreadByID(
2450435933ddSDimitry Andric thread->GetID());
245112b93ac6SEd Maste const uint64_t frame_idx = item.GetIdentifier();
24520127ef0fSEd Maste thread->SetSelectedFrameByIndex(frame_idx);
245312b93ac6SEd Maste return true;
245412b93ac6SEd Maste }
245512b93ac6SEd Maste return false;
245612b93ac6SEd Maste }
24579f2f44ceSEd Maste
24581c3bbb01SEd Maste protected:
24591c3bbb01SEd Maste FormatEntity::Entry m_format;
246012b93ac6SEd Maste };
246112b93ac6SEd Maste
2462435933ddSDimitry Andric class ThreadTreeDelegate : public TreeDelegate {
246312b93ac6SEd Maste public:
ThreadTreeDelegate(Debugger & debugger)2464435933ddSDimitry Andric ThreadTreeDelegate(Debugger &debugger)
2465435933ddSDimitry Andric : TreeDelegate(), m_debugger(debugger), m_tid(LLDB_INVALID_THREAD_ID),
2466435933ddSDimitry Andric m_stop_id(UINT32_MAX) {
2467435933ddSDimitry Andric FormatEntity::Parse("thread #${thread.index}: tid = ${thread.id}{, stop "
2468435933ddSDimitry Andric "reason = ${thread.stop-reason}}",
24691c3bbb01SEd Maste m_format);
247012b93ac6SEd Maste }
247112b93ac6SEd Maste
24729f2f44ceSEd Maste ~ThreadTreeDelegate() override = default;
247312b93ac6SEd Maste
GetProcess()2474435933ddSDimitry Andric ProcessSP GetProcess() {
2475435933ddSDimitry Andric return m_debugger.GetCommandInterpreter()
2476435933ddSDimitry Andric .GetExecutionContext()
2477435933ddSDimitry Andric .GetProcessSP();
24780127ef0fSEd Maste }
24790127ef0fSEd Maste
GetThread(const TreeItem & item)2480435933ddSDimitry Andric ThreadSP GetThread(const TreeItem &item) {
24810127ef0fSEd Maste ProcessSP process_sp = GetProcess();
24820127ef0fSEd Maste if (process_sp)
24830127ef0fSEd Maste return process_sp->GetThreadList().FindThreadByID(item.GetIdentifier());
24840127ef0fSEd Maste return ThreadSP();
24850127ef0fSEd Maste }
24860127ef0fSEd Maste
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)2487435933ddSDimitry Andric void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
24880127ef0fSEd Maste ThreadSP thread_sp = GetThread(item);
2489435933ddSDimitry Andric if (thread_sp) {
249012b93ac6SEd Maste StreamString strm;
249112b93ac6SEd Maste ExecutionContext exe_ctx(thread_sp);
2492435933ddSDimitry Andric if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
2493435933ddSDimitry Andric nullptr, false, false)) {
249412b93ac6SEd Maste int right_pad = 1;
2495435933ddSDimitry Andric window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad);
249612b93ac6SEd Maste }
249712b93ac6SEd Maste }
249812b93ac6SEd Maste }
24999f2f44ceSEd Maste
TreeDelegateGenerateChildren(TreeItem & item)2500435933ddSDimitry Andric void TreeDelegateGenerateChildren(TreeItem &item) override {
25010127ef0fSEd Maste ProcessSP process_sp = GetProcess();
2502435933ddSDimitry Andric if (process_sp && process_sp->IsAlive()) {
250312b93ac6SEd Maste StateType state = process_sp->GetState();
2504435933ddSDimitry Andric if (StateIsStoppedState(state, true)) {
25050127ef0fSEd Maste ThreadSP thread_sp = GetThread(item);
2506435933ddSDimitry Andric if (thread_sp) {
2507435933ddSDimitry Andric if (m_stop_id == process_sp->GetStopID() &&
2508435933ddSDimitry Andric thread_sp->GetID() == m_tid)
250912b93ac6SEd Maste return; // Children are already up to date
2510435933ddSDimitry Andric if (!m_frame_delegate_sp) {
251112b93ac6SEd Maste // Always expand the thread item the first time we show it
25120127ef0fSEd Maste m_frame_delegate_sp.reset(new FrameTreeDelegate());
251312b93ac6SEd Maste }
251412b93ac6SEd Maste
251512b93ac6SEd Maste m_stop_id = process_sp->GetStopID();
251612b93ac6SEd Maste m_tid = thread_sp->GetID();
251712b93ac6SEd Maste
251812b93ac6SEd Maste TreeItem t(&item, *m_frame_delegate_sp, false);
251912b93ac6SEd Maste size_t num_frames = thread_sp->GetStackFrameCount();
252012b93ac6SEd Maste item.Resize(num_frames, t);
2521435933ddSDimitry Andric for (size_t i = 0; i < num_frames; ++i) {
25220127ef0fSEd Maste item[i].SetUserData(thread_sp.get());
252312b93ac6SEd Maste item[i].SetIdentifier(i);
252412b93ac6SEd Maste }
252512b93ac6SEd Maste }
252612b93ac6SEd Maste return;
252712b93ac6SEd Maste }
252812b93ac6SEd Maste }
252912b93ac6SEd Maste item.ClearChildren();
253012b93ac6SEd Maste }
253112b93ac6SEd Maste
TreeDelegateItemSelected(TreeItem & item)2532435933ddSDimitry Andric bool TreeDelegateItemSelected(TreeItem &item) override {
25330127ef0fSEd Maste ProcessSP process_sp = GetProcess();
2534435933ddSDimitry Andric if (process_sp && process_sp->IsAlive()) {
25350127ef0fSEd Maste StateType state = process_sp->GetState();
2536435933ddSDimitry Andric if (StateIsStoppedState(state, true)) {
25370127ef0fSEd Maste ThreadSP thread_sp = GetThread(item);
2538435933ddSDimitry Andric if (thread_sp) {
253912b93ac6SEd Maste ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList();
25404bb0738eSEd Maste std::lock_guard<std::recursive_mutex> guard(thread_list.GetMutex());
254112b93ac6SEd Maste ThreadSP selected_thread_sp = thread_list.GetSelectedThread();
2542435933ddSDimitry Andric if (selected_thread_sp->GetID() != thread_sp->GetID()) {
254312b93ac6SEd Maste thread_list.SetSelectedThreadByID(thread_sp->GetID());
254412b93ac6SEd Maste return true;
254512b93ac6SEd Maste }
254612b93ac6SEd Maste }
25470127ef0fSEd Maste }
25480127ef0fSEd Maste }
254912b93ac6SEd Maste return false;
255012b93ac6SEd Maste }
255112b93ac6SEd Maste
255212b93ac6SEd Maste protected:
255312b93ac6SEd Maste Debugger &m_debugger;
255412b93ac6SEd Maste std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp;
255512b93ac6SEd Maste lldb::user_id_t m_tid;
255612b93ac6SEd Maste uint32_t m_stop_id;
25571c3bbb01SEd Maste FormatEntity::Entry m_format;
255812b93ac6SEd Maste };
255912b93ac6SEd Maste
2560435933ddSDimitry Andric class ThreadsTreeDelegate : public TreeDelegate {
25610127ef0fSEd Maste public:
ThreadsTreeDelegate(Debugger & debugger)2562435933ddSDimitry Andric ThreadsTreeDelegate(Debugger &debugger)
2563435933ddSDimitry Andric : TreeDelegate(), m_thread_delegate_sp(), m_debugger(debugger),
2564435933ddSDimitry Andric m_stop_id(UINT32_MAX) {
25651c3bbb01SEd Maste FormatEntity::Parse("process ${process.id}{, name = ${process.name}}",
25661c3bbb01SEd Maste m_format);
25670127ef0fSEd Maste }
25680127ef0fSEd Maste
25699f2f44ceSEd Maste ~ThreadsTreeDelegate() override = default;
25700127ef0fSEd Maste
GetProcess()2571435933ddSDimitry Andric ProcessSP GetProcess() {
2572435933ddSDimitry Andric return m_debugger.GetCommandInterpreter()
2573435933ddSDimitry Andric .GetExecutionContext()
2574435933ddSDimitry Andric .GetProcessSP();
25750127ef0fSEd Maste }
25760127ef0fSEd Maste
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)2577435933ddSDimitry Andric void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
25780127ef0fSEd Maste ProcessSP process_sp = GetProcess();
2579435933ddSDimitry Andric if (process_sp && process_sp->IsAlive()) {
25800127ef0fSEd Maste StreamString strm;
25810127ef0fSEd Maste ExecutionContext exe_ctx(process_sp);
2582435933ddSDimitry Andric if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
2583435933ddSDimitry Andric nullptr, false, false)) {
25840127ef0fSEd Maste int right_pad = 1;
2585435933ddSDimitry Andric window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad);
25860127ef0fSEd Maste }
25870127ef0fSEd Maste }
25880127ef0fSEd Maste }
25890127ef0fSEd Maste
TreeDelegateGenerateChildren(TreeItem & item)2590435933ddSDimitry Andric void TreeDelegateGenerateChildren(TreeItem &item) override {
25910127ef0fSEd Maste ProcessSP process_sp = GetProcess();
2592435933ddSDimitry Andric if (process_sp && process_sp->IsAlive()) {
25930127ef0fSEd Maste StateType state = process_sp->GetState();
2594435933ddSDimitry Andric if (StateIsStoppedState(state, true)) {
25950127ef0fSEd Maste const uint32_t stop_id = process_sp->GetStopID();
25960127ef0fSEd Maste if (m_stop_id == stop_id)
25970127ef0fSEd Maste return; // Children are already up to date
25980127ef0fSEd Maste
25990127ef0fSEd Maste m_stop_id = stop_id;
26000127ef0fSEd Maste
2601435933ddSDimitry Andric if (!m_thread_delegate_sp) {
26020127ef0fSEd Maste // Always expand the thread item the first time we show it
26030127ef0fSEd Maste // item.Expand();
26040127ef0fSEd Maste m_thread_delegate_sp.reset(new ThreadTreeDelegate(m_debugger));
26050127ef0fSEd Maste }
26060127ef0fSEd Maste
26070127ef0fSEd Maste TreeItem t(&item, *m_thread_delegate_sp, false);
26080127ef0fSEd Maste ThreadList &threads = process_sp->GetThreadList();
26094bb0738eSEd Maste std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
26100127ef0fSEd Maste size_t num_threads = threads.GetSize();
26110127ef0fSEd Maste item.Resize(num_threads, t);
2612435933ddSDimitry Andric for (size_t i = 0; i < num_threads; ++i) {
26130127ef0fSEd Maste item[i].SetIdentifier(threads.GetThreadAtIndex(i)->GetID());
26140127ef0fSEd Maste item[i].SetMightHaveChildren(true);
26150127ef0fSEd Maste }
26160127ef0fSEd Maste return;
26170127ef0fSEd Maste }
26180127ef0fSEd Maste }
26190127ef0fSEd Maste item.ClearChildren();
26200127ef0fSEd Maste }
26210127ef0fSEd Maste
TreeDelegateItemSelected(TreeItem & item)2622435933ddSDimitry Andric bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
26230127ef0fSEd Maste
26240127ef0fSEd Maste protected:
26250127ef0fSEd Maste std::shared_ptr<ThreadTreeDelegate> m_thread_delegate_sp;
26260127ef0fSEd Maste Debugger &m_debugger;
26270127ef0fSEd Maste uint32_t m_stop_id;
26281c3bbb01SEd Maste FormatEntity::Entry m_format;
26290127ef0fSEd Maste };
26300127ef0fSEd Maste
2631435933ddSDimitry Andric class ValueObjectListDelegate : public WindowDelegate {
263212b93ac6SEd Maste public:
ValueObjectListDelegate()2633435933ddSDimitry Andric ValueObjectListDelegate()
2634435933ddSDimitry Andric : m_rows(), m_selected_row(nullptr),
2635435933ddSDimitry Andric m_selected_row_idx(0), m_first_visible_row(0), m_num_rows(0),
2636435933ddSDimitry Andric m_max_x(0), m_max_y(0) {}
263712b93ac6SEd Maste
ValueObjectListDelegate(ValueObjectList & valobj_list)2638435933ddSDimitry Andric ValueObjectListDelegate(ValueObjectList &valobj_list)
2639435933ddSDimitry Andric : m_rows(), m_selected_row(nullptr),
2640435933ddSDimitry Andric m_selected_row_idx(0), m_first_visible_row(0), m_num_rows(0),
2641435933ddSDimitry Andric m_max_x(0), m_max_y(0) {
264212b93ac6SEd Maste SetValues(valobj_list);
264312b93ac6SEd Maste }
264412b93ac6SEd Maste
26459f2f44ceSEd Maste ~ValueObjectListDelegate() override = default;
264612b93ac6SEd Maste
SetValues(ValueObjectList & valobj_list)2647435933ddSDimitry Andric void SetValues(ValueObjectList &valobj_list) {
26484bb0738eSEd Maste m_selected_row = nullptr;
264912b93ac6SEd Maste m_selected_row_idx = 0;
265012b93ac6SEd Maste m_first_visible_row = 0;
265112b93ac6SEd Maste m_num_rows = 0;
265212b93ac6SEd Maste m_rows.clear();
2653435933ddSDimitry Andric for (auto &valobj_sp : valobj_list.GetObjects())
2654435933ddSDimitry Andric m_rows.push_back(Row(valobj_sp, nullptr));
265512b93ac6SEd Maste }
265612b93ac6SEd Maste
WindowDelegateDraw(Window & window,bool force)2657435933ddSDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override {
265812b93ac6SEd Maste m_num_rows = 0;
265912b93ac6SEd Maste m_min_x = 2;
266012b93ac6SEd Maste m_min_y = 1;
266112b93ac6SEd Maste m_max_x = window.GetWidth() - 1;
266212b93ac6SEd Maste m_max_y = window.GetHeight() - 1;
266312b93ac6SEd Maste
266412b93ac6SEd Maste window.Erase();
266512b93ac6SEd Maste window.DrawTitleBox(window.GetName());
266612b93ac6SEd Maste
266712b93ac6SEd Maste const int num_visible_rows = NumVisibleRows();
266812b93ac6SEd Maste const int num_rows = CalculateTotalNumberRows(m_rows);
266912b93ac6SEd Maste
26704ba319b5SDimitry Andric // If we unexpanded while having something selected our total number of
26714ba319b5SDimitry Andric // rows is less than the num visible rows, then make sure we show all the
26724ba319b5SDimitry Andric // rows by setting the first visible row accordingly.
267312b93ac6SEd Maste if (m_first_visible_row > 0 && num_rows < num_visible_rows)
267412b93ac6SEd Maste m_first_visible_row = 0;
267512b93ac6SEd Maste
267612b93ac6SEd Maste // Make sure the selected row is always visible
267712b93ac6SEd Maste if (m_selected_row_idx < m_first_visible_row)
267812b93ac6SEd Maste m_first_visible_row = m_selected_row_idx;
267912b93ac6SEd Maste else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
268012b93ac6SEd Maste m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
268112b93ac6SEd Maste
268212b93ac6SEd Maste DisplayRows(window, m_rows, g_options);
268312b93ac6SEd Maste
268412b93ac6SEd Maste window.DeferredRefresh();
268512b93ac6SEd Maste
268612b93ac6SEd Maste // Get the selected row
268712b93ac6SEd Maste m_selected_row = GetRowForRowIndex(m_selected_row_idx);
26884ba319b5SDimitry Andric // Keep the cursor on the selected row so the highlight and the cursor are
26894ba319b5SDimitry Andric // always on the same line
269012b93ac6SEd Maste if (m_selected_row)
2691435933ddSDimitry Andric window.MoveCursor(m_selected_row->x, m_selected_row->y);
269212b93ac6SEd Maste
269312b93ac6SEd Maste return true; // Drawing handled
269412b93ac6SEd Maste }
269512b93ac6SEd Maste
WindowDelegateGetKeyHelp()2696435933ddSDimitry Andric KeyHelp *WindowDelegateGetKeyHelp() override {
269712b93ac6SEd Maste static curses::KeyHelp g_source_view_key_help[] = {
269812b93ac6SEd Maste {KEY_UP, "Select previous item"},
269912b93ac6SEd Maste {KEY_DOWN, "Select next item"},
270012b93ac6SEd Maste {KEY_RIGHT, "Expand selected item"},
270112b93ac6SEd Maste {KEY_LEFT, "Unexpand selected item or select parent if not expanded"},
270212b93ac6SEd Maste {KEY_PPAGE, "Page up"},
270312b93ac6SEd Maste {KEY_NPAGE, "Page down"},
270412b93ac6SEd Maste {'A', "Format as annotated address"},
270512b93ac6SEd Maste {'b', "Format as binary"},
270612b93ac6SEd Maste {'B', "Format as hex bytes with ASCII"},
270712b93ac6SEd Maste {'c', "Format as character"},
270812b93ac6SEd Maste {'d', "Format as a signed integer"},
270912b93ac6SEd Maste {'D', "Format selected value using the default format for the type"},
271012b93ac6SEd Maste {'f', "Format as float"},
271112b93ac6SEd Maste {'h', "Show help dialog"},
271212b93ac6SEd Maste {'i', "Format as instructions"},
271312b93ac6SEd Maste {'o', "Format as octal"},
271412b93ac6SEd Maste {'p', "Format as pointer"},
271512b93ac6SEd Maste {'s', "Format as C string"},
271612b93ac6SEd Maste {'t', "Toggle showing/hiding type names"},
271712b93ac6SEd Maste {'u', "Format as an unsigned integer"},
271812b93ac6SEd Maste {'x', "Format as hex"},
271912b93ac6SEd Maste {'X', "Format as uppercase hex"},
272012b93ac6SEd Maste {' ', "Toggle item expansion"},
272112b93ac6SEd Maste {',', "Page up"},
272212b93ac6SEd Maste {'.', "Page down"},
2723435933ddSDimitry Andric {'\0', nullptr}};
272412b93ac6SEd Maste return g_source_view_key_help;
272512b93ac6SEd Maste }
272612b93ac6SEd Maste
WindowDelegateHandleChar(Window & window,int c)2727435933ddSDimitry Andric HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
2728435933ddSDimitry Andric switch (c) {
272912b93ac6SEd Maste case 'x':
273012b93ac6SEd Maste case 'X':
273112b93ac6SEd Maste case 'o':
273212b93ac6SEd Maste case 's':
273312b93ac6SEd Maste case 'u':
273412b93ac6SEd Maste case 'd':
273512b93ac6SEd Maste case 'D':
273612b93ac6SEd Maste case 'i':
273712b93ac6SEd Maste case 'A':
273812b93ac6SEd Maste case 'p':
273912b93ac6SEd Maste case 'c':
274012b93ac6SEd Maste case 'b':
274112b93ac6SEd Maste case 'B':
274212b93ac6SEd Maste case 'f':
274312b93ac6SEd Maste // Change the format for the currently selected item
2744435933ddSDimitry Andric if (m_selected_row) {
2745435933ddSDimitry Andric auto valobj_sp = m_selected_row->value.GetSP();
2746435933ddSDimitry Andric if (valobj_sp)
2747435933ddSDimitry Andric valobj_sp->SetFormat(FormatForChar(c));
2748435933ddSDimitry Andric }
274912b93ac6SEd Maste return eKeyHandled;
275012b93ac6SEd Maste
275112b93ac6SEd Maste case 't':
275212b93ac6SEd Maste // Toggle showing type names
275312b93ac6SEd Maste g_options.show_types = !g_options.show_types;
275412b93ac6SEd Maste return eKeyHandled;
275512b93ac6SEd Maste
275612b93ac6SEd Maste case ',':
275712b93ac6SEd Maste case KEY_PPAGE:
275812b93ac6SEd Maste // Page up key
2759435933ddSDimitry Andric if (m_first_visible_row > 0) {
27600127ef0fSEd Maste if (static_cast<int>(m_first_visible_row) > m_max_y)
276112b93ac6SEd Maste m_first_visible_row -= m_max_y;
276212b93ac6SEd Maste else
276312b93ac6SEd Maste m_first_visible_row = 0;
276412b93ac6SEd Maste m_selected_row_idx = m_first_visible_row;
276512b93ac6SEd Maste }
276612b93ac6SEd Maste return eKeyHandled;
276712b93ac6SEd Maste
276812b93ac6SEd Maste case '.':
276912b93ac6SEd Maste case KEY_NPAGE:
277012b93ac6SEd Maste // Page down key
2771435933ddSDimitry Andric if (m_num_rows > static_cast<size_t>(m_max_y)) {
2772435933ddSDimitry Andric if (m_first_visible_row + m_max_y < m_num_rows) {
277312b93ac6SEd Maste m_first_visible_row += m_max_y;
277412b93ac6SEd Maste m_selected_row_idx = m_first_visible_row;
277512b93ac6SEd Maste }
277612b93ac6SEd Maste }
277712b93ac6SEd Maste return eKeyHandled;
277812b93ac6SEd Maste
277912b93ac6SEd Maste case KEY_UP:
278012b93ac6SEd Maste if (m_selected_row_idx > 0)
278112b93ac6SEd Maste --m_selected_row_idx;
278212b93ac6SEd Maste return eKeyHandled;
27839f2f44ceSEd Maste
278412b93ac6SEd Maste case KEY_DOWN:
278512b93ac6SEd Maste if (m_selected_row_idx + 1 < m_num_rows)
278612b93ac6SEd Maste ++m_selected_row_idx;
278712b93ac6SEd Maste return eKeyHandled;
278812b93ac6SEd Maste
278912b93ac6SEd Maste case KEY_RIGHT:
2790435933ddSDimitry Andric if (m_selected_row) {
279112b93ac6SEd Maste if (!m_selected_row->expanded)
279212b93ac6SEd Maste m_selected_row->Expand();
279312b93ac6SEd Maste }
279412b93ac6SEd Maste return eKeyHandled;
279512b93ac6SEd Maste
279612b93ac6SEd Maste case KEY_LEFT:
2797435933ddSDimitry Andric if (m_selected_row) {
279812b93ac6SEd Maste if (m_selected_row->expanded)
279912b93ac6SEd Maste m_selected_row->Unexpand();
280012b93ac6SEd Maste else if (m_selected_row->parent)
280112b93ac6SEd Maste m_selected_row_idx = m_selected_row->parent->row_idx;
280212b93ac6SEd Maste }
280312b93ac6SEd Maste return eKeyHandled;
280412b93ac6SEd Maste
280512b93ac6SEd Maste case ' ':
280612b93ac6SEd Maste // Toggle expansion state when SPACE is pressed
2807435933ddSDimitry Andric if (m_selected_row) {
280812b93ac6SEd Maste if (m_selected_row->expanded)
280912b93ac6SEd Maste m_selected_row->Unexpand();
281012b93ac6SEd Maste else
281112b93ac6SEd Maste m_selected_row->Expand();
281212b93ac6SEd Maste }
281312b93ac6SEd Maste return eKeyHandled;
281412b93ac6SEd Maste
281512b93ac6SEd Maste case 'h':
281612b93ac6SEd Maste window.CreateHelpSubwindow();
281712b93ac6SEd Maste return eKeyHandled;
281812b93ac6SEd Maste
281912b93ac6SEd Maste default:
282012b93ac6SEd Maste break;
282112b93ac6SEd Maste }
282212b93ac6SEd Maste return eKeyNotHandled;
282312b93ac6SEd Maste }
282412b93ac6SEd Maste
282512b93ac6SEd Maste protected:
282612b93ac6SEd Maste std::vector<Row> m_rows;
282712b93ac6SEd Maste Row *m_selected_row;
282812b93ac6SEd Maste uint32_t m_selected_row_idx;
282912b93ac6SEd Maste uint32_t m_first_visible_row;
283012b93ac6SEd Maste uint32_t m_num_rows;
283112b93ac6SEd Maste int m_min_x;
283212b93ac6SEd Maste int m_min_y;
283312b93ac6SEd Maste int m_max_x;
283412b93ac6SEd Maste int m_max_y;
283512b93ac6SEd Maste
FormatForChar(int c)2836435933ddSDimitry Andric static Format FormatForChar(int c) {
2837435933ddSDimitry Andric switch (c) {
2838435933ddSDimitry Andric case 'x':
2839435933ddSDimitry Andric return eFormatHex;
2840435933ddSDimitry Andric case 'X':
2841435933ddSDimitry Andric return eFormatHexUppercase;
2842435933ddSDimitry Andric case 'o':
2843435933ddSDimitry Andric return eFormatOctal;
2844435933ddSDimitry Andric case 's':
2845435933ddSDimitry Andric return eFormatCString;
2846435933ddSDimitry Andric case 'u':
2847435933ddSDimitry Andric return eFormatUnsigned;
2848435933ddSDimitry Andric case 'd':
2849435933ddSDimitry Andric return eFormatDecimal;
2850435933ddSDimitry Andric case 'D':
2851435933ddSDimitry Andric return eFormatDefault;
2852435933ddSDimitry Andric case 'i':
2853435933ddSDimitry Andric return eFormatInstruction;
2854435933ddSDimitry Andric case 'A':
2855435933ddSDimitry Andric return eFormatAddressInfo;
2856435933ddSDimitry Andric case 'p':
2857435933ddSDimitry Andric return eFormatPointer;
2858435933ddSDimitry Andric case 'c':
2859435933ddSDimitry Andric return eFormatChar;
2860435933ddSDimitry Andric case 'b':
2861435933ddSDimitry Andric return eFormatBinary;
2862435933ddSDimitry Andric case 'B':
2863435933ddSDimitry Andric return eFormatBytesWithASCII;
2864435933ddSDimitry Andric case 'f':
2865435933ddSDimitry Andric return eFormatFloat;
286612b93ac6SEd Maste }
286712b93ac6SEd Maste return eFormatDefault;
286812b93ac6SEd Maste }
286912b93ac6SEd Maste
DisplayRowObject(Window & window,Row & row,DisplayOptions & options,bool highlight,bool last_child)2870435933ddSDimitry Andric bool DisplayRowObject(Window &window, Row &row, DisplayOptions &options,
2871435933ddSDimitry Andric bool highlight, bool last_child) {
2872435933ddSDimitry Andric ValueObject *valobj = row.value.GetSP().get();
287312b93ac6SEd Maste
28744bb0738eSEd Maste if (valobj == nullptr)
287512b93ac6SEd Maste return false;
287612b93ac6SEd Maste
2877435933ddSDimitry Andric const char *type_name =
2878435933ddSDimitry Andric options.show_types ? valobj->GetTypeName().GetCString() : nullptr;
287912b93ac6SEd Maste const char *name = valobj->GetName().GetCString();
288012b93ac6SEd Maste const char *value = valobj->GetValueAsCString();
288112b93ac6SEd Maste const char *summary = valobj->GetSummaryAsCString();
288212b93ac6SEd Maste
288312b93ac6SEd Maste window.MoveCursor(row.x, row.y);
288412b93ac6SEd Maste
288512b93ac6SEd Maste row.DrawTree(window);
288612b93ac6SEd Maste
288712b93ac6SEd Maste if (highlight)
288812b93ac6SEd Maste window.AttributeOn(A_REVERSE);
288912b93ac6SEd Maste
289012b93ac6SEd Maste if (type_name && type_name[0])
289112b93ac6SEd Maste window.Printf("(%s) ", type_name);
289212b93ac6SEd Maste
289312b93ac6SEd Maste if (name && name[0])
289412b93ac6SEd Maste window.PutCString(name);
289512b93ac6SEd Maste
289612b93ac6SEd Maste attr_t changd_attr = 0;
289712b93ac6SEd Maste if (valobj->GetValueDidChange())
289812b93ac6SEd Maste changd_attr = COLOR_PAIR(5) | A_BOLD;
289912b93ac6SEd Maste
2900435933ddSDimitry Andric if (value && value[0]) {
290112b93ac6SEd Maste window.PutCString(" = ");
290212b93ac6SEd Maste if (changd_attr)
290312b93ac6SEd Maste window.AttributeOn(changd_attr);
290412b93ac6SEd Maste window.PutCString(value);
290512b93ac6SEd Maste if (changd_attr)
290612b93ac6SEd Maste window.AttributeOff(changd_attr);
290712b93ac6SEd Maste }
290812b93ac6SEd Maste
2909435933ddSDimitry Andric if (summary && summary[0]) {
291012b93ac6SEd Maste window.PutChar(' ');
291112b93ac6SEd Maste if (changd_attr)
291212b93ac6SEd Maste window.AttributeOn(changd_attr);
291312b93ac6SEd Maste window.PutCString(summary);
291412b93ac6SEd Maste if (changd_attr)
291512b93ac6SEd Maste window.AttributeOff(changd_attr);
291612b93ac6SEd Maste }
291712b93ac6SEd Maste
291812b93ac6SEd Maste if (highlight)
291912b93ac6SEd Maste window.AttributeOff(A_REVERSE);
292012b93ac6SEd Maste
292112b93ac6SEd Maste return true;
292212b93ac6SEd Maste }
29239f2f44ceSEd Maste
DisplayRows(Window & window,std::vector<Row> & rows,DisplayOptions & options)2924435933ddSDimitry Andric void DisplayRows(Window &window, std::vector<Row> &rows,
2925435933ddSDimitry Andric DisplayOptions &options) {
292612b93ac6SEd Maste // > 0x25B7
292712b93ac6SEd Maste // \/ 0x25BD
292812b93ac6SEd Maste
292912b93ac6SEd Maste bool window_is_active = window.IsActive();
2930435933ddSDimitry Andric for (auto &row : rows) {
293112b93ac6SEd Maste const bool last_child = row.parent && &rows[rows.size() - 1] == &row;
293212b93ac6SEd Maste // Save the row index in each Row structure
293312b93ac6SEd Maste row.row_idx = m_num_rows;
293412b93ac6SEd Maste if ((m_num_rows >= m_first_visible_row) &&
2935435933ddSDimitry Andric ((m_num_rows - m_first_visible_row) <
2936435933ddSDimitry Andric static_cast<size_t>(NumVisibleRows()))) {
293712b93ac6SEd Maste row.x = m_min_x;
293812b93ac6SEd Maste row.y = m_num_rows - m_first_visible_row + 1;
2939435933ddSDimitry Andric if (DisplayRowObject(window, row, options,
2940435933ddSDimitry Andric window_is_active &&
2941435933ddSDimitry Andric m_num_rows == m_selected_row_idx,
2942435933ddSDimitry Andric last_child)) {
294312b93ac6SEd Maste ++m_num_rows;
2944435933ddSDimitry Andric } else {
294512b93ac6SEd Maste row.x = 0;
294612b93ac6SEd Maste row.y = 0;
294712b93ac6SEd Maste }
2948435933ddSDimitry Andric } else {
294912b93ac6SEd Maste row.x = 0;
295012b93ac6SEd Maste row.y = 0;
295112b93ac6SEd Maste ++m_num_rows;
295212b93ac6SEd Maste }
295312b93ac6SEd Maste
2954435933ddSDimitry Andric auto &children = row.GetChildren();
2955435933ddSDimitry Andric if (row.expanded && !children.empty()) {
2956435933ddSDimitry Andric DisplayRows(window, children, options);
295712b93ac6SEd Maste }
295812b93ac6SEd Maste }
295912b93ac6SEd Maste }
296012b93ac6SEd Maste
CalculateTotalNumberRows(std::vector<Row> & rows)2961435933ddSDimitry Andric int CalculateTotalNumberRows(std::vector<Row> &rows) {
296212b93ac6SEd Maste int row_count = 0;
2963435933ddSDimitry Andric for (auto &row : rows) {
296412b93ac6SEd Maste ++row_count;
296512b93ac6SEd Maste if (row.expanded)
2966435933ddSDimitry Andric row_count += CalculateTotalNumberRows(row.GetChildren());
296712b93ac6SEd Maste }
296812b93ac6SEd Maste return row_count;
296912b93ac6SEd Maste }
29709f2f44ceSEd Maste
GetRowForRowIndexImpl(std::vector<Row> & rows,size_t & row_index)2971435933ddSDimitry Andric static Row *GetRowForRowIndexImpl(std::vector<Row> &rows, size_t &row_index) {
2972435933ddSDimitry Andric for (auto &row : rows) {
297312b93ac6SEd Maste if (row_index == 0)
297412b93ac6SEd Maste return &row;
2975435933ddSDimitry Andric else {
297612b93ac6SEd Maste --row_index;
2977435933ddSDimitry Andric auto &children = row.GetChildren();
2978435933ddSDimitry Andric if (row.expanded && !children.empty()) {
2979435933ddSDimitry Andric Row *result = GetRowForRowIndexImpl(children, row_index);
298012b93ac6SEd Maste if (result)
298112b93ac6SEd Maste return result;
298212b93ac6SEd Maste }
298312b93ac6SEd Maste }
298412b93ac6SEd Maste }
29854bb0738eSEd Maste return nullptr;
298612b93ac6SEd Maste }
298712b93ac6SEd Maste
GetRowForRowIndex(size_t row_index)2988435933ddSDimitry Andric Row *GetRowForRowIndex(size_t row_index) {
298912b93ac6SEd Maste return GetRowForRowIndexImpl(m_rows, row_index);
299012b93ac6SEd Maste }
299112b93ac6SEd Maste
NumVisibleRows() const2992435933ddSDimitry Andric int NumVisibleRows() const { return m_max_y - m_min_y; }
299312b93ac6SEd Maste
299412b93ac6SEd Maste static DisplayOptions g_options;
299512b93ac6SEd Maste };
299612b93ac6SEd Maste
2997435933ddSDimitry Andric class FrameVariablesWindowDelegate : public ValueObjectListDelegate {
299812b93ac6SEd Maste public:
FrameVariablesWindowDelegate(Debugger & debugger)2999435933ddSDimitry Andric FrameVariablesWindowDelegate(Debugger &debugger)
3000435933ddSDimitry Andric : ValueObjectListDelegate(), m_debugger(debugger),
3001435933ddSDimitry Andric m_frame_block(nullptr) {}
300212b93ac6SEd Maste
30039f2f44ceSEd Maste ~FrameVariablesWindowDelegate() override = default;
300412b93ac6SEd Maste
WindowDelegateGetHelpText()3005435933ddSDimitry Andric const char *WindowDelegateGetHelpText() override {
300612b93ac6SEd Maste return "Frame variable window keyboard shortcuts:";
300712b93ac6SEd Maste }
300812b93ac6SEd Maste
WindowDelegateDraw(Window & window,bool force)3009435933ddSDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override {
3010435933ddSDimitry Andric ExecutionContext exe_ctx(
3011435933ddSDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext());
301212b93ac6SEd Maste Process *process = exe_ctx.GetProcessPtr();
30134bb0738eSEd Maste Block *frame_block = nullptr;
30144bb0738eSEd Maste StackFrame *frame = nullptr;
301512b93ac6SEd Maste
3016435933ddSDimitry Andric if (process) {
301712b93ac6SEd Maste StateType state = process->GetState();
3018435933ddSDimitry Andric if (StateIsStoppedState(state, true)) {
301912b93ac6SEd Maste frame = exe_ctx.GetFramePtr();
302012b93ac6SEd Maste if (frame)
302112b93ac6SEd Maste frame_block = frame->GetFrameBlock();
3022435933ddSDimitry Andric } else if (StateIsRunningState(state)) {
302312b93ac6SEd Maste return true; // Don't do any updating when we are running
302412b93ac6SEd Maste }
302512b93ac6SEd Maste }
302612b93ac6SEd Maste
302712b93ac6SEd Maste ValueObjectList local_values;
3028435933ddSDimitry Andric if (frame_block) {
302912b93ac6SEd Maste // Only update the variables if they have changed
3030435933ddSDimitry Andric if (m_frame_block != frame_block) {
303112b93ac6SEd Maste m_frame_block = frame_block;
303212b93ac6SEd Maste
303312b93ac6SEd Maste VariableList *locals = frame->GetVariableList(true);
3034435933ddSDimitry Andric if (locals) {
303512b93ac6SEd Maste const DynamicValueType use_dynamic = eDynamicDontRunTarget;
303612b93ac6SEd Maste const size_t num_locals = locals->GetSize();
3037435933ddSDimitry Andric for (size_t i = 0; i < num_locals; ++i) {
3038435933ddSDimitry Andric ValueObjectSP value_sp = frame->GetValueObjectForFrameVariable(
3039435933ddSDimitry Andric locals->GetVariableAtIndex(i), use_dynamic);
3040435933ddSDimitry Andric if (value_sp) {
30411c3bbb01SEd Maste ValueObjectSP synthetic_value_sp = value_sp->GetSyntheticValue();
30421c3bbb01SEd Maste if (synthetic_value_sp)
30431c3bbb01SEd Maste local_values.Append(synthetic_value_sp);
30441c3bbb01SEd Maste else
30451c3bbb01SEd Maste local_values.Append(value_sp);
30461c3bbb01SEd Maste }
30471c3bbb01SEd Maste }
304812b93ac6SEd Maste // Update the values
304912b93ac6SEd Maste SetValues(local_values);
305012b93ac6SEd Maste }
305112b93ac6SEd Maste }
3052435933ddSDimitry Andric } else {
30534bb0738eSEd Maste m_frame_block = nullptr;
305412b93ac6SEd Maste // Update the values with an empty list if there is no frame
305512b93ac6SEd Maste SetValues(local_values);
305612b93ac6SEd Maste }
305712b93ac6SEd Maste
305812b93ac6SEd Maste return ValueObjectListDelegate::WindowDelegateDraw(window, force);
305912b93ac6SEd Maste }
306012b93ac6SEd Maste
306112b93ac6SEd Maste protected:
306212b93ac6SEd Maste Debugger &m_debugger;
306312b93ac6SEd Maste Block *m_frame_block;
306412b93ac6SEd Maste };
306512b93ac6SEd Maste
3066435933ddSDimitry Andric class RegistersWindowDelegate : public ValueObjectListDelegate {
306712b93ac6SEd Maste public:
RegistersWindowDelegate(Debugger & debugger)3068435933ddSDimitry Andric RegistersWindowDelegate(Debugger &debugger)
3069435933ddSDimitry Andric : ValueObjectListDelegate(), m_debugger(debugger) {}
307012b93ac6SEd Maste
30719f2f44ceSEd Maste ~RegistersWindowDelegate() override = default;
307212b93ac6SEd Maste
WindowDelegateGetHelpText()3073435933ddSDimitry Andric const char *WindowDelegateGetHelpText() override {
307412b93ac6SEd Maste return "Register window keyboard shortcuts:";
307512b93ac6SEd Maste }
307612b93ac6SEd Maste
WindowDelegateDraw(Window & window,bool force)3077435933ddSDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override {
3078435933ddSDimitry Andric ExecutionContext exe_ctx(
3079435933ddSDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext());
308012b93ac6SEd Maste StackFrame *frame = exe_ctx.GetFramePtr();
308112b93ac6SEd Maste
308212b93ac6SEd Maste ValueObjectList value_list;
3083435933ddSDimitry Andric if (frame) {
3084435933ddSDimitry Andric if (frame->GetStackID() != m_stack_id) {
308512b93ac6SEd Maste m_stack_id = frame->GetStackID();
308612b93ac6SEd Maste RegisterContextSP reg_ctx(frame->GetRegisterContext());
3087435933ddSDimitry Andric if (reg_ctx) {
308812b93ac6SEd Maste const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
3089435933ddSDimitry Andric for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) {
3090435933ddSDimitry Andric value_list.Append(
3091435933ddSDimitry Andric ValueObjectRegisterSet::Create(frame, reg_ctx, set_idx));
309212b93ac6SEd Maste }
309312b93ac6SEd Maste }
309412b93ac6SEd Maste SetValues(value_list);
309512b93ac6SEd Maste }
3096435933ddSDimitry Andric } else {
309712b93ac6SEd Maste Process *process = exe_ctx.GetProcessPtr();
309812b93ac6SEd Maste if (process && process->IsAlive())
309912b93ac6SEd Maste return true; // Don't do any updating if we are running
3100435933ddSDimitry Andric else {
31014ba319b5SDimitry Andric // Update the values with an empty list if there is no process or the
31024ba319b5SDimitry Andric // process isn't alive anymore
310312b93ac6SEd Maste SetValues(value_list);
310412b93ac6SEd Maste }
310512b93ac6SEd Maste }
310612b93ac6SEd Maste return ValueObjectListDelegate::WindowDelegateDraw(window, force);
310712b93ac6SEd Maste }
310812b93ac6SEd Maste
310912b93ac6SEd Maste protected:
311012b93ac6SEd Maste Debugger &m_debugger;
311112b93ac6SEd Maste StackID m_stack_id;
311212b93ac6SEd Maste };
311312b93ac6SEd Maste
CursesKeyToCString(int ch)3114435933ddSDimitry Andric static const char *CursesKeyToCString(int ch) {
311512b93ac6SEd Maste static char g_desc[32];
3116435933ddSDimitry Andric if (ch >= KEY_F0 && ch < KEY_F0 + 64) {
311712b93ac6SEd Maste snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0);
311812b93ac6SEd Maste return g_desc;
311912b93ac6SEd Maste }
3120435933ddSDimitry Andric switch (ch) {
3121435933ddSDimitry Andric case KEY_DOWN:
3122435933ddSDimitry Andric return "down";
3123435933ddSDimitry Andric case KEY_UP:
3124435933ddSDimitry Andric return "up";
3125435933ddSDimitry Andric case KEY_LEFT:
3126435933ddSDimitry Andric return "left";
3127435933ddSDimitry Andric case KEY_RIGHT:
3128435933ddSDimitry Andric return "right";
3129435933ddSDimitry Andric case KEY_HOME:
3130435933ddSDimitry Andric return "home";
3131435933ddSDimitry Andric case KEY_BACKSPACE:
3132435933ddSDimitry Andric return "backspace";
3133435933ddSDimitry Andric case KEY_DL:
3134435933ddSDimitry Andric return "delete-line";
3135435933ddSDimitry Andric case KEY_IL:
3136435933ddSDimitry Andric return "insert-line";
3137435933ddSDimitry Andric case KEY_DC:
3138435933ddSDimitry Andric return "delete-char";
3139435933ddSDimitry Andric case KEY_IC:
3140435933ddSDimitry Andric return "insert-char";
3141435933ddSDimitry Andric case KEY_CLEAR:
3142435933ddSDimitry Andric return "clear";
3143435933ddSDimitry Andric case KEY_EOS:
3144435933ddSDimitry Andric return "clear-to-eos";
3145435933ddSDimitry Andric case KEY_EOL:
3146435933ddSDimitry Andric return "clear-to-eol";
3147435933ddSDimitry Andric case KEY_SF:
3148435933ddSDimitry Andric return "scroll-forward";
3149435933ddSDimitry Andric case KEY_SR:
3150435933ddSDimitry Andric return "scroll-backward";
3151435933ddSDimitry Andric case KEY_NPAGE:
3152435933ddSDimitry Andric return "page-down";
3153435933ddSDimitry Andric case KEY_PPAGE:
3154435933ddSDimitry Andric return "page-up";
3155435933ddSDimitry Andric case KEY_STAB:
3156435933ddSDimitry Andric return "set-tab";
3157435933ddSDimitry Andric case KEY_CTAB:
3158435933ddSDimitry Andric return "clear-tab";
3159435933ddSDimitry Andric case KEY_CATAB:
3160435933ddSDimitry Andric return "clear-all-tabs";
3161435933ddSDimitry Andric case KEY_ENTER:
3162435933ddSDimitry Andric return "enter";
3163435933ddSDimitry Andric case KEY_PRINT:
3164435933ddSDimitry Andric return "print";
3165435933ddSDimitry Andric case KEY_LL:
3166435933ddSDimitry Andric return "lower-left key";
3167435933ddSDimitry Andric case KEY_A1:
3168435933ddSDimitry Andric return "upper left of keypad";
3169435933ddSDimitry Andric case KEY_A3:
3170435933ddSDimitry Andric return "upper right of keypad";
3171435933ddSDimitry Andric case KEY_B2:
3172435933ddSDimitry Andric return "center of keypad";
3173435933ddSDimitry Andric case KEY_C1:
3174435933ddSDimitry Andric return "lower left of keypad";
3175435933ddSDimitry Andric case KEY_C3:
3176435933ddSDimitry Andric return "lower right of keypad";
3177435933ddSDimitry Andric case KEY_BTAB:
3178435933ddSDimitry Andric return "back-tab key";
3179435933ddSDimitry Andric case KEY_BEG:
3180435933ddSDimitry Andric return "begin key";
3181435933ddSDimitry Andric case KEY_CANCEL:
3182435933ddSDimitry Andric return "cancel key";
3183435933ddSDimitry Andric case KEY_CLOSE:
3184435933ddSDimitry Andric return "close key";
3185435933ddSDimitry Andric case KEY_COMMAND:
3186435933ddSDimitry Andric return "command key";
3187435933ddSDimitry Andric case KEY_COPY:
3188435933ddSDimitry Andric return "copy key";
3189435933ddSDimitry Andric case KEY_CREATE:
3190435933ddSDimitry Andric return "create key";
3191435933ddSDimitry Andric case KEY_END:
3192435933ddSDimitry Andric return "end key";
3193435933ddSDimitry Andric case KEY_EXIT:
3194435933ddSDimitry Andric return "exit key";
3195435933ddSDimitry Andric case KEY_FIND:
3196435933ddSDimitry Andric return "find key";
3197435933ddSDimitry Andric case KEY_HELP:
3198435933ddSDimitry Andric return "help key";
3199435933ddSDimitry Andric case KEY_MARK:
3200435933ddSDimitry Andric return "mark key";
3201435933ddSDimitry Andric case KEY_MESSAGE:
3202435933ddSDimitry Andric return "message key";
3203435933ddSDimitry Andric case KEY_MOVE:
3204435933ddSDimitry Andric return "move key";
3205435933ddSDimitry Andric case KEY_NEXT:
3206435933ddSDimitry Andric return "next key";
3207435933ddSDimitry Andric case KEY_OPEN:
3208435933ddSDimitry Andric return "open key";
3209435933ddSDimitry Andric case KEY_OPTIONS:
3210435933ddSDimitry Andric return "options key";
3211435933ddSDimitry Andric case KEY_PREVIOUS:
3212435933ddSDimitry Andric return "previous key";
3213435933ddSDimitry Andric case KEY_REDO:
3214435933ddSDimitry Andric return "redo key";
3215435933ddSDimitry Andric case KEY_REFERENCE:
3216435933ddSDimitry Andric return "reference key";
3217435933ddSDimitry Andric case KEY_REFRESH:
3218435933ddSDimitry Andric return "refresh key";
3219435933ddSDimitry Andric case KEY_REPLACE:
3220435933ddSDimitry Andric return "replace key";
3221435933ddSDimitry Andric case KEY_RESTART:
3222435933ddSDimitry Andric return "restart key";
3223435933ddSDimitry Andric case KEY_RESUME:
3224435933ddSDimitry Andric return "resume key";
3225435933ddSDimitry Andric case KEY_SAVE:
3226435933ddSDimitry Andric return "save key";
3227435933ddSDimitry Andric case KEY_SBEG:
3228435933ddSDimitry Andric return "shifted begin key";
3229435933ddSDimitry Andric case KEY_SCANCEL:
3230435933ddSDimitry Andric return "shifted cancel key";
3231435933ddSDimitry Andric case KEY_SCOMMAND:
3232435933ddSDimitry Andric return "shifted command key";
3233435933ddSDimitry Andric case KEY_SCOPY:
3234435933ddSDimitry Andric return "shifted copy key";
3235435933ddSDimitry Andric case KEY_SCREATE:
3236435933ddSDimitry Andric return "shifted create key";
3237435933ddSDimitry Andric case KEY_SDC:
3238435933ddSDimitry Andric return "shifted delete-character key";
3239435933ddSDimitry Andric case KEY_SDL:
3240435933ddSDimitry Andric return "shifted delete-line key";
3241435933ddSDimitry Andric case KEY_SELECT:
3242435933ddSDimitry Andric return "select key";
3243435933ddSDimitry Andric case KEY_SEND:
3244435933ddSDimitry Andric return "shifted end key";
3245435933ddSDimitry Andric case KEY_SEOL:
3246435933ddSDimitry Andric return "shifted clear-to-end-of-line key";
3247435933ddSDimitry Andric case KEY_SEXIT:
3248435933ddSDimitry Andric return "shifted exit key";
3249435933ddSDimitry Andric case KEY_SFIND:
3250435933ddSDimitry Andric return "shifted find key";
3251435933ddSDimitry Andric case KEY_SHELP:
3252435933ddSDimitry Andric return "shifted help key";
3253435933ddSDimitry Andric case KEY_SHOME:
3254435933ddSDimitry Andric return "shifted home key";
3255435933ddSDimitry Andric case KEY_SIC:
3256435933ddSDimitry Andric return "shifted insert-character key";
3257435933ddSDimitry Andric case KEY_SLEFT:
3258435933ddSDimitry Andric return "shifted left-arrow key";
3259435933ddSDimitry Andric case KEY_SMESSAGE:
3260435933ddSDimitry Andric return "shifted message key";
3261435933ddSDimitry Andric case KEY_SMOVE:
3262435933ddSDimitry Andric return "shifted move key";
3263435933ddSDimitry Andric case KEY_SNEXT:
3264435933ddSDimitry Andric return "shifted next key";
3265435933ddSDimitry Andric case KEY_SOPTIONS:
3266435933ddSDimitry Andric return "shifted options key";
3267435933ddSDimitry Andric case KEY_SPREVIOUS:
3268435933ddSDimitry Andric return "shifted previous key";
3269435933ddSDimitry Andric case KEY_SPRINT:
3270435933ddSDimitry Andric return "shifted print key";
3271435933ddSDimitry Andric case KEY_SREDO:
3272435933ddSDimitry Andric return "shifted redo key";
3273435933ddSDimitry Andric case KEY_SREPLACE:
3274435933ddSDimitry Andric return "shifted replace key";
3275435933ddSDimitry Andric case KEY_SRIGHT:
3276435933ddSDimitry Andric return "shifted right-arrow key";
3277435933ddSDimitry Andric case KEY_SRSUME:
3278435933ddSDimitry Andric return "shifted resume key";
3279435933ddSDimitry Andric case KEY_SSAVE:
3280435933ddSDimitry Andric return "shifted save key";
3281435933ddSDimitry Andric case KEY_SSUSPEND:
3282435933ddSDimitry Andric return "shifted suspend key";
3283435933ddSDimitry Andric case KEY_SUNDO:
3284435933ddSDimitry Andric return "shifted undo key";
3285435933ddSDimitry Andric case KEY_SUSPEND:
3286435933ddSDimitry Andric return "suspend key";
3287435933ddSDimitry Andric case KEY_UNDO:
3288435933ddSDimitry Andric return "undo key";
3289435933ddSDimitry Andric case KEY_MOUSE:
3290435933ddSDimitry Andric return "Mouse event has occurred";
3291435933ddSDimitry Andric case KEY_RESIZE:
3292435933ddSDimitry Andric return "Terminal resize event";
32939f2f44ceSEd Maste #ifdef KEY_EVENT
3294435933ddSDimitry Andric case KEY_EVENT:
3295435933ddSDimitry Andric return "We were interrupted by an event";
32969f2f44ceSEd Maste #endif
3297435933ddSDimitry Andric case KEY_RETURN:
3298435933ddSDimitry Andric return "return";
3299435933ddSDimitry Andric case ' ':
3300435933ddSDimitry Andric return "space";
3301435933ddSDimitry Andric case '\t':
3302435933ddSDimitry Andric return "tab";
3303435933ddSDimitry Andric case KEY_ESCAPE:
3304435933ddSDimitry Andric return "escape";
330512b93ac6SEd Maste default:
330612b93ac6SEd Maste if (isprint(ch))
330712b93ac6SEd Maste snprintf(g_desc, sizeof(g_desc), "%c", ch);
330812b93ac6SEd Maste else
330912b93ac6SEd Maste snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch);
331012b93ac6SEd Maste return g_desc;
331112b93ac6SEd Maste }
33124bb0738eSEd Maste return nullptr;
331312b93ac6SEd Maste }
331412b93ac6SEd Maste
HelpDialogDelegate(const char * text,KeyHelp * key_help_array)3315435933ddSDimitry Andric HelpDialogDelegate::HelpDialogDelegate(const char *text,
3316435933ddSDimitry Andric KeyHelp *key_help_array)
3317435933ddSDimitry Andric : m_text(), m_first_visible_line(0) {
3318435933ddSDimitry Andric if (text && text[0]) {
331912b93ac6SEd Maste m_text.SplitIntoLines(text);
332012b93ac6SEd Maste m_text.AppendString("");
332112b93ac6SEd Maste }
3322435933ddSDimitry Andric if (key_help_array) {
3323435933ddSDimitry Andric for (KeyHelp *key = key_help_array; key->ch; ++key) {
332412b93ac6SEd Maste StreamString key_description;
3325435933ddSDimitry Andric key_description.Printf("%10s - %s", CursesKeyToCString(key->ch),
3326435933ddSDimitry Andric key->description);
3327435933ddSDimitry Andric m_text.AppendString(key_description.GetString());
332812b93ac6SEd Maste }
332912b93ac6SEd Maste }
333012b93ac6SEd Maste }
333112b93ac6SEd Maste
33329f2f44ceSEd Maste HelpDialogDelegate::~HelpDialogDelegate() = default;
333312b93ac6SEd Maste
WindowDelegateDraw(Window & window,bool force)3334435933ddSDimitry Andric bool HelpDialogDelegate::WindowDelegateDraw(Window &window, bool force) {
333512b93ac6SEd Maste window.Erase();
333612b93ac6SEd Maste const int window_height = window.GetHeight();
333712b93ac6SEd Maste int x = 2;
333812b93ac6SEd Maste int y = 1;
333912b93ac6SEd Maste const int min_y = y;
334012b93ac6SEd Maste const int max_y = window_height - 1 - y;
33410127ef0fSEd Maste const size_t num_visible_lines = max_y - min_y + 1;
334212b93ac6SEd Maste const size_t num_lines = m_text.GetSize();
334312b93ac6SEd Maste const char *bottom_message;
334412b93ac6SEd Maste if (num_lines <= num_visible_lines)
334512b93ac6SEd Maste bottom_message = "Press any key to exit";
334612b93ac6SEd Maste else
334712b93ac6SEd Maste bottom_message = "Use arrows to scroll, any other key to exit";
334812b93ac6SEd Maste window.DrawTitleBox(window.GetName(), bottom_message);
3349435933ddSDimitry Andric while (y <= max_y) {
335012b93ac6SEd Maste window.MoveCursor(x, y);
3351435933ddSDimitry Andric window.PutCStringTruncated(
3352435933ddSDimitry Andric m_text.GetStringAtIndex(m_first_visible_line + y - min_y), 1);
335312b93ac6SEd Maste ++y;
335412b93ac6SEd Maste }
335512b93ac6SEd Maste return true;
335612b93ac6SEd Maste }
335712b93ac6SEd Maste
WindowDelegateHandleChar(Window & window,int key)3358435933ddSDimitry Andric HandleCharResult HelpDialogDelegate::WindowDelegateHandleChar(Window &window,
3359435933ddSDimitry Andric int key) {
336012b93ac6SEd Maste bool done = false;
336112b93ac6SEd Maste const size_t num_lines = m_text.GetSize();
336212b93ac6SEd Maste const size_t num_visible_lines = window.GetHeight() - 2;
336312b93ac6SEd Maste
3364435933ddSDimitry Andric if (num_lines <= num_visible_lines) {
336512b93ac6SEd Maste done = true;
33664ba319b5SDimitry Andric // If we have all lines visible and don't need scrolling, then any key
33674ba319b5SDimitry Andric // press will cause us to exit
3368435933ddSDimitry Andric } else {
3369435933ddSDimitry Andric switch (key) {
337012b93ac6SEd Maste case KEY_UP:
337112b93ac6SEd Maste if (m_first_visible_line > 0)
337212b93ac6SEd Maste --m_first_visible_line;
337312b93ac6SEd Maste break;
337412b93ac6SEd Maste
337512b93ac6SEd Maste case KEY_DOWN:
337612b93ac6SEd Maste if (m_first_visible_line + num_visible_lines < num_lines)
337712b93ac6SEd Maste ++m_first_visible_line;
337812b93ac6SEd Maste break;
337912b93ac6SEd Maste
338012b93ac6SEd Maste case KEY_PPAGE:
338112b93ac6SEd Maste case ',':
3382435933ddSDimitry Andric if (m_first_visible_line > 0) {
33830127ef0fSEd Maste if (static_cast<size_t>(m_first_visible_line) >= num_visible_lines)
338412b93ac6SEd Maste m_first_visible_line -= num_visible_lines;
338512b93ac6SEd Maste else
338612b93ac6SEd Maste m_first_visible_line = 0;
338712b93ac6SEd Maste }
338812b93ac6SEd Maste break;
33899f2f44ceSEd Maste
339012b93ac6SEd Maste case KEY_NPAGE:
339112b93ac6SEd Maste case '.':
3392435933ddSDimitry Andric if (m_first_visible_line + num_visible_lines < num_lines) {
339312b93ac6SEd Maste m_first_visible_line += num_visible_lines;
33940127ef0fSEd Maste if (static_cast<size_t>(m_first_visible_line) > num_lines)
339512b93ac6SEd Maste m_first_visible_line = num_lines - num_visible_lines;
339612b93ac6SEd Maste }
339712b93ac6SEd Maste break;
33989f2f44ceSEd Maste
339912b93ac6SEd Maste default:
340012b93ac6SEd Maste done = true;
340112b93ac6SEd Maste break;
340212b93ac6SEd Maste }
340312b93ac6SEd Maste }
340412b93ac6SEd Maste if (done)
340512b93ac6SEd Maste window.GetParent()->RemoveSubWindow(&window);
340612b93ac6SEd Maste return eKeyHandled;
340712b93ac6SEd Maste }
340812b93ac6SEd Maste
3409435933ddSDimitry Andric class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
341012b93ac6SEd Maste public:
341112b93ac6SEd Maste enum {
341212b93ac6SEd Maste eMenuID_LLDB = 1,
341312b93ac6SEd Maste eMenuID_LLDBAbout,
341412b93ac6SEd Maste eMenuID_LLDBExit,
341512b93ac6SEd Maste
341612b93ac6SEd Maste eMenuID_Target,
341712b93ac6SEd Maste eMenuID_TargetCreate,
341812b93ac6SEd Maste eMenuID_TargetDelete,
341912b93ac6SEd Maste
342012b93ac6SEd Maste eMenuID_Process,
342112b93ac6SEd Maste eMenuID_ProcessAttach,
342212b93ac6SEd Maste eMenuID_ProcessDetach,
342312b93ac6SEd Maste eMenuID_ProcessLaunch,
342412b93ac6SEd Maste eMenuID_ProcessContinue,
342512b93ac6SEd Maste eMenuID_ProcessHalt,
342612b93ac6SEd Maste eMenuID_ProcessKill,
342712b93ac6SEd Maste
342812b93ac6SEd Maste eMenuID_Thread,
342912b93ac6SEd Maste eMenuID_ThreadStepIn,
343012b93ac6SEd Maste eMenuID_ThreadStepOver,
343112b93ac6SEd Maste eMenuID_ThreadStepOut,
343212b93ac6SEd Maste
343312b93ac6SEd Maste eMenuID_View,
343412b93ac6SEd Maste eMenuID_ViewBacktrace,
343512b93ac6SEd Maste eMenuID_ViewRegisters,
343612b93ac6SEd Maste eMenuID_ViewSource,
343712b93ac6SEd Maste eMenuID_ViewVariables,
343812b93ac6SEd Maste
343912b93ac6SEd Maste eMenuID_Help,
344012b93ac6SEd Maste eMenuID_HelpGUIHelp
344112b93ac6SEd Maste };
344212b93ac6SEd Maste
ApplicationDelegate(Application & app,Debugger & debugger)3443435933ddSDimitry Andric ApplicationDelegate(Application &app, Debugger &debugger)
3444435933ddSDimitry Andric : WindowDelegate(), MenuDelegate(), m_app(app), m_debugger(debugger) {}
344512b93ac6SEd Maste
34469f2f44ceSEd Maste ~ApplicationDelegate() override = default;
34471c3bbb01SEd Maste
WindowDelegateDraw(Window & window,bool force)3448435933ddSDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override {
344912b93ac6SEd Maste return false; // Drawing not handled, let standard window drawing happen
345012b93ac6SEd Maste }
345112b93ac6SEd Maste
WindowDelegateHandleChar(Window & window,int key)3452435933ddSDimitry Andric HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
3453435933ddSDimitry Andric switch (key) {
345412b93ac6SEd Maste case '\t':
345512b93ac6SEd Maste window.SelectNextWindowAsActive();
345612b93ac6SEd Maste return eKeyHandled;
345712b93ac6SEd Maste
345812b93ac6SEd Maste case 'h':
345912b93ac6SEd Maste window.CreateHelpSubwindow();
346012b93ac6SEd Maste return eKeyHandled;
346112b93ac6SEd Maste
346212b93ac6SEd Maste case KEY_ESCAPE:
346312b93ac6SEd Maste return eQuitApplication;
346412b93ac6SEd Maste
346512b93ac6SEd Maste default:
346612b93ac6SEd Maste break;
346712b93ac6SEd Maste }
346812b93ac6SEd Maste return eKeyNotHandled;
346912b93ac6SEd Maste }
347012b93ac6SEd Maste
WindowDelegateGetHelpText()3471435933ddSDimitry Andric const char *WindowDelegateGetHelpText() override {
347212b93ac6SEd Maste return "Welcome to the LLDB curses GUI.\n\n"
347312b93ac6SEd Maste "Press the TAB key to change the selected view.\n"
3474435933ddSDimitry Andric "Each view has its own keyboard shortcuts, press 'h' to open a "
3475435933ddSDimitry Andric "dialog to display them.\n\n"
347612b93ac6SEd Maste "Common key bindings for all views:";
347712b93ac6SEd Maste }
347812b93ac6SEd Maste
WindowDelegateGetKeyHelp()3479435933ddSDimitry Andric KeyHelp *WindowDelegateGetKeyHelp() override {
348012b93ac6SEd Maste static curses::KeyHelp g_source_view_key_help[] = {
348112b93ac6SEd Maste {'\t', "Select next view"},
348212b93ac6SEd Maste {'h', "Show help dialog with view specific key bindings"},
348312b93ac6SEd Maste {',', "Page up"},
348412b93ac6SEd Maste {'.', "Page down"},
348512b93ac6SEd Maste {KEY_UP, "Select previous"},
348612b93ac6SEd Maste {KEY_DOWN, "Select next"},
348712b93ac6SEd Maste {KEY_LEFT, "Unexpand or select parent"},
348812b93ac6SEd Maste {KEY_RIGHT, "Expand"},
348912b93ac6SEd Maste {KEY_PPAGE, "Page up"},
349012b93ac6SEd Maste {KEY_NPAGE, "Page down"},
3491435933ddSDimitry Andric {'\0', nullptr}};
349212b93ac6SEd Maste return g_source_view_key_help;
349312b93ac6SEd Maste }
349412b93ac6SEd Maste
MenuDelegateAction(Menu & menu)3495435933ddSDimitry Andric MenuActionResult MenuDelegateAction(Menu &menu) override {
3496435933ddSDimitry Andric switch (menu.GetIdentifier()) {
3497435933ddSDimitry Andric case eMenuID_ThreadStepIn: {
3498435933ddSDimitry Andric ExecutionContext exe_ctx =
3499435933ddSDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
3500435933ddSDimitry Andric if (exe_ctx.HasThreadScope()) {
350112b93ac6SEd Maste Process *process = exe_ctx.GetProcessPtr();
3502435933ddSDimitry Andric if (process && process->IsAlive() &&
3503435933ddSDimitry Andric StateIsStoppedState(process->GetState(), true))
35040127ef0fSEd Maste exe_ctx.GetThreadRef().StepIn(true);
350512b93ac6SEd Maste }
350612b93ac6SEd Maste }
350712b93ac6SEd Maste return MenuActionResult::Handled;
350812b93ac6SEd Maste
3509435933ddSDimitry Andric case eMenuID_ThreadStepOut: {
3510435933ddSDimitry Andric ExecutionContext exe_ctx =
3511435933ddSDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
3512435933ddSDimitry Andric if (exe_ctx.HasThreadScope()) {
351312b93ac6SEd Maste Process *process = exe_ctx.GetProcessPtr();
3514435933ddSDimitry Andric if (process && process->IsAlive() &&
3515435933ddSDimitry Andric StateIsStoppedState(process->GetState(), true))
351612b93ac6SEd Maste exe_ctx.GetThreadRef().StepOut();
351712b93ac6SEd Maste }
351812b93ac6SEd Maste }
351912b93ac6SEd Maste return MenuActionResult::Handled;
352012b93ac6SEd Maste
3521435933ddSDimitry Andric case eMenuID_ThreadStepOver: {
3522435933ddSDimitry Andric ExecutionContext exe_ctx =
3523435933ddSDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
3524435933ddSDimitry Andric if (exe_ctx.HasThreadScope()) {
352512b93ac6SEd Maste Process *process = exe_ctx.GetProcessPtr();
3526435933ddSDimitry Andric if (process && process->IsAlive() &&
3527435933ddSDimitry Andric StateIsStoppedState(process->GetState(), true))
352812b93ac6SEd Maste exe_ctx.GetThreadRef().StepOver(true);
352912b93ac6SEd Maste }
353012b93ac6SEd Maste }
353112b93ac6SEd Maste return MenuActionResult::Handled;
353212b93ac6SEd Maste
3533435933ddSDimitry Andric case eMenuID_ProcessContinue: {
3534435933ddSDimitry Andric ExecutionContext exe_ctx =
3535435933ddSDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
3536435933ddSDimitry Andric if (exe_ctx.HasProcessScope()) {
353712b93ac6SEd Maste Process *process = exe_ctx.GetProcessPtr();
3538435933ddSDimitry Andric if (process && process->IsAlive() &&
3539435933ddSDimitry Andric StateIsStoppedState(process->GetState(), true))
354012b93ac6SEd Maste process->Resume();
354112b93ac6SEd Maste }
354212b93ac6SEd Maste }
354312b93ac6SEd Maste return MenuActionResult::Handled;
354412b93ac6SEd Maste
3545435933ddSDimitry Andric case eMenuID_ProcessKill: {
3546435933ddSDimitry Andric ExecutionContext exe_ctx =
3547435933ddSDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
3548435933ddSDimitry Andric if (exe_ctx.HasProcessScope()) {
354912b93ac6SEd Maste Process *process = exe_ctx.GetProcessPtr();
355012b93ac6SEd Maste if (process && process->IsAlive())
35511c3bbb01SEd Maste process->Destroy(false);
355212b93ac6SEd Maste }
355312b93ac6SEd Maste }
355412b93ac6SEd Maste return MenuActionResult::Handled;
355512b93ac6SEd Maste
3556435933ddSDimitry Andric case eMenuID_ProcessHalt: {
3557435933ddSDimitry Andric ExecutionContext exe_ctx =
3558435933ddSDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
3559435933ddSDimitry Andric if (exe_ctx.HasProcessScope()) {
356012b93ac6SEd Maste Process *process = exe_ctx.GetProcessPtr();
356112b93ac6SEd Maste if (process && process->IsAlive())
356212b93ac6SEd Maste process->Halt();
356312b93ac6SEd Maste }
356412b93ac6SEd Maste }
356512b93ac6SEd Maste return MenuActionResult::Handled;
356612b93ac6SEd Maste
3567435933ddSDimitry Andric case eMenuID_ProcessDetach: {
3568435933ddSDimitry Andric ExecutionContext exe_ctx =
3569435933ddSDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
3570435933ddSDimitry Andric if (exe_ctx.HasProcessScope()) {
357112b93ac6SEd Maste Process *process = exe_ctx.GetProcessPtr();
357212b93ac6SEd Maste if (process && process->IsAlive())
357312b93ac6SEd Maste process->Detach(false);
357412b93ac6SEd Maste }
357512b93ac6SEd Maste }
357612b93ac6SEd Maste return MenuActionResult::Handled;
357712b93ac6SEd Maste
3578435933ddSDimitry Andric case eMenuID_Process: {
3579435933ddSDimitry Andric // Populate the menu with all of the threads if the process is stopped
35804ba319b5SDimitry Andric // when the Process menu gets selected and is about to display its
35814ba319b5SDimitry Andric // submenu.
358212b93ac6SEd Maste Menus &submenus = menu.GetSubmenus();
3583435933ddSDimitry Andric ExecutionContext exe_ctx =
3584435933ddSDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
358512b93ac6SEd Maste Process *process = exe_ctx.GetProcessPtr();
3586435933ddSDimitry Andric if (process && process->IsAlive() &&
3587435933ddSDimitry Andric StateIsStoppedState(process->GetState(), true)) {
358812b93ac6SEd Maste if (submenus.size() == 7)
358912b93ac6SEd Maste menu.AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
359012b93ac6SEd Maste else if (submenus.size() > 8)
359112b93ac6SEd Maste submenus.erase(submenus.begin() + 8, submenus.end());
359212b93ac6SEd Maste
359312b93ac6SEd Maste ThreadList &threads = process->GetThreadList();
35944bb0738eSEd Maste std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
359512b93ac6SEd Maste size_t num_threads = threads.GetSize();
3596435933ddSDimitry Andric for (size_t i = 0; i < num_threads; ++i) {
359712b93ac6SEd Maste ThreadSP thread_sp = threads.GetThreadAtIndex(i);
359812b93ac6SEd Maste char menu_char = '\0';
359912b93ac6SEd Maste if (i < 9)
360012b93ac6SEd Maste menu_char = '1' + i;
360112b93ac6SEd Maste StreamString thread_menu_title;
360212b93ac6SEd Maste thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID());
360312b93ac6SEd Maste const char *thread_name = thread_sp->GetName();
360412b93ac6SEd Maste if (thread_name && thread_name[0])
360512b93ac6SEd Maste thread_menu_title.Printf(" %s", thread_name);
3606435933ddSDimitry Andric else {
360712b93ac6SEd Maste const char *queue_name = thread_sp->GetQueueName();
360812b93ac6SEd Maste if (queue_name && queue_name[0])
360912b93ac6SEd Maste thread_menu_title.Printf(" %s", queue_name);
361012b93ac6SEd Maste }
3611435933ddSDimitry Andric menu.AddSubmenu(
3612435933ddSDimitry Andric MenuSP(new Menu(thread_menu_title.GetString().str().c_str(),
3613435933ddSDimitry Andric nullptr, menu_char, thread_sp->GetID())));
361412b93ac6SEd Maste }
3615435933ddSDimitry Andric } else if (submenus.size() > 7) {
36164ba319b5SDimitry Andric // Remove the separator and any other thread submenu items that were
36174ba319b5SDimitry Andric // previously added
361812b93ac6SEd Maste submenus.erase(submenus.begin() + 7, submenus.end());
361912b93ac6SEd Maste }
3620435933ddSDimitry Andric // Since we are adding and removing items we need to recalculate the name
3621435933ddSDimitry Andric // lengths
362212b93ac6SEd Maste menu.RecalculateNameLengths();
362312b93ac6SEd Maste }
362412b93ac6SEd Maste return MenuActionResult::Handled;
362512b93ac6SEd Maste
3626435933ddSDimitry Andric case eMenuID_ViewVariables: {
362712b93ac6SEd Maste WindowSP main_window_sp = m_app.GetMainWindow();
362812b93ac6SEd Maste WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
362912b93ac6SEd Maste WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
363012b93ac6SEd Maste WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
363112b93ac6SEd Maste const Rect source_bounds = source_window_sp->GetBounds();
363212b93ac6SEd Maste
3633435933ddSDimitry Andric if (variables_window_sp) {
363412b93ac6SEd Maste const Rect variables_bounds = variables_window_sp->GetBounds();
363512b93ac6SEd Maste
363612b93ac6SEd Maste main_window_sp->RemoveSubWindow(variables_window_sp.get());
363712b93ac6SEd Maste
3638435933ddSDimitry Andric if (registers_window_sp) {
3639435933ddSDimitry Andric // We have a registers window, so give all the area back to the
3640435933ddSDimitry Andric // registers window
364112b93ac6SEd Maste Rect registers_bounds = variables_bounds;
364212b93ac6SEd Maste registers_bounds.size.width = source_bounds.size.width;
364312b93ac6SEd Maste registers_window_sp->SetBounds(registers_bounds);
3644435933ddSDimitry Andric } else {
36454ba319b5SDimitry Andric // We have no registers window showing so give the bottom area back
36464ba319b5SDimitry Andric // to the source view
364712b93ac6SEd Maste source_window_sp->Resize(source_bounds.size.width,
3648435933ddSDimitry Andric source_bounds.size.height +
3649435933ddSDimitry Andric variables_bounds.size.height);
365012b93ac6SEd Maste }
3651435933ddSDimitry Andric } else {
365212b93ac6SEd Maste Rect new_variables_rect;
3653435933ddSDimitry Andric if (registers_window_sp) {
365412b93ac6SEd Maste // We have a registers window so split the area of the registers
365512b93ac6SEd Maste // window into two columns where the left hand side will be the
365612b93ac6SEd Maste // variables and the right hand side will be the registers
365712b93ac6SEd Maste const Rect variables_bounds = registers_window_sp->GetBounds();
365812b93ac6SEd Maste Rect new_registers_rect;
3659435933ddSDimitry Andric variables_bounds.VerticalSplitPercentage(0.50, new_variables_rect,
3660435933ddSDimitry Andric new_registers_rect);
366112b93ac6SEd Maste registers_window_sp->SetBounds(new_registers_rect);
3662435933ddSDimitry Andric } else {
366312b93ac6SEd Maste // No variables window, grab the bottom part of the source window
366412b93ac6SEd Maste Rect new_source_rect;
3665435933ddSDimitry Andric source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
3666435933ddSDimitry Andric new_variables_rect);
366712b93ac6SEd Maste source_window_sp->SetBounds(new_source_rect);
366812b93ac6SEd Maste }
3669435933ddSDimitry Andric WindowSP new_window_sp = main_window_sp->CreateSubWindow(
3670435933ddSDimitry Andric "Variables", new_variables_rect, false);
3671435933ddSDimitry Andric new_window_sp->SetDelegate(
3672435933ddSDimitry Andric WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
367312b93ac6SEd Maste }
367412b93ac6SEd Maste touchwin(stdscr);
367512b93ac6SEd Maste }
367612b93ac6SEd Maste return MenuActionResult::Handled;
367712b93ac6SEd Maste
3678435933ddSDimitry Andric case eMenuID_ViewRegisters: {
367912b93ac6SEd Maste WindowSP main_window_sp = m_app.GetMainWindow();
368012b93ac6SEd Maste WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
368112b93ac6SEd Maste WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
368212b93ac6SEd Maste WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
368312b93ac6SEd Maste const Rect source_bounds = source_window_sp->GetBounds();
368412b93ac6SEd Maste
3685435933ddSDimitry Andric if (registers_window_sp) {
3686435933ddSDimitry Andric if (variables_window_sp) {
368712b93ac6SEd Maste const Rect variables_bounds = variables_window_sp->GetBounds();
368812b93ac6SEd Maste
3689435933ddSDimitry Andric // We have a variables window, so give all the area back to the
3690435933ddSDimitry Andric // variables window
3691435933ddSDimitry Andric variables_window_sp->Resize(variables_bounds.size.width +
3692435933ddSDimitry Andric registers_window_sp->GetWidth(),
369312b93ac6SEd Maste variables_bounds.size.height);
3694435933ddSDimitry Andric } else {
36954ba319b5SDimitry Andric // We have no variables window showing so give the bottom area back
36964ba319b5SDimitry Andric // to the source view
369712b93ac6SEd Maste source_window_sp->Resize(source_bounds.size.width,
3698435933ddSDimitry Andric source_bounds.size.height +
3699435933ddSDimitry Andric registers_window_sp->GetHeight());
370012b93ac6SEd Maste }
370112b93ac6SEd Maste main_window_sp->RemoveSubWindow(registers_window_sp.get());
3702435933ddSDimitry Andric } else {
370312b93ac6SEd Maste Rect new_regs_rect;
3704435933ddSDimitry Andric if (variables_window_sp) {
37054ba319b5SDimitry Andric // We have a variables window, split it into two columns where the
37064ba319b5SDimitry Andric // left hand side will be the variables and the right hand side will
37074ba319b5SDimitry Andric // be the registers
370812b93ac6SEd Maste const Rect variables_bounds = variables_window_sp->GetBounds();
370912b93ac6SEd Maste Rect new_vars_rect;
3710435933ddSDimitry Andric variables_bounds.VerticalSplitPercentage(0.50, new_vars_rect,
3711435933ddSDimitry Andric new_regs_rect);
371212b93ac6SEd Maste variables_window_sp->SetBounds(new_vars_rect);
3713435933ddSDimitry Andric } else {
371412b93ac6SEd Maste // No registers window, grab the bottom part of the source window
371512b93ac6SEd Maste Rect new_source_rect;
3716435933ddSDimitry Andric source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
3717435933ddSDimitry Andric new_regs_rect);
371812b93ac6SEd Maste source_window_sp->SetBounds(new_source_rect);
371912b93ac6SEd Maste }
3720435933ddSDimitry Andric WindowSP new_window_sp =
3721435933ddSDimitry Andric main_window_sp->CreateSubWindow("Registers", new_regs_rect, false);
3722435933ddSDimitry Andric new_window_sp->SetDelegate(
3723435933ddSDimitry Andric WindowDelegateSP(new RegistersWindowDelegate(m_debugger)));
372412b93ac6SEd Maste }
372512b93ac6SEd Maste touchwin(stdscr);
372612b93ac6SEd Maste }
372712b93ac6SEd Maste return MenuActionResult::Handled;
372812b93ac6SEd Maste
372912b93ac6SEd Maste case eMenuID_HelpGUIHelp:
373012b93ac6SEd Maste m_app.GetMainWindow()->CreateHelpSubwindow();
373112b93ac6SEd Maste return MenuActionResult::Handled;
373212b93ac6SEd Maste
373312b93ac6SEd Maste default:
373412b93ac6SEd Maste break;
373512b93ac6SEd Maste }
373612b93ac6SEd Maste
373712b93ac6SEd Maste return MenuActionResult::NotHandled;
373812b93ac6SEd Maste }
3739435933ddSDimitry Andric
374012b93ac6SEd Maste protected:
374112b93ac6SEd Maste Application &m_app;
374212b93ac6SEd Maste Debugger &m_debugger;
374312b93ac6SEd Maste };
374412b93ac6SEd Maste
3745435933ddSDimitry Andric class StatusBarWindowDelegate : public WindowDelegate {
374612b93ac6SEd Maste public:
StatusBarWindowDelegate(Debugger & debugger)3747435933ddSDimitry Andric StatusBarWindowDelegate(Debugger &debugger) : m_debugger(debugger) {
3748435933ddSDimitry Andric FormatEntity::Parse("Thread: ${thread.id%tid}", m_format);
374912b93ac6SEd Maste }
375012b93ac6SEd Maste
37519f2f44ceSEd Maste ~StatusBarWindowDelegate() override = default;
37521c3bbb01SEd Maste
WindowDelegateDraw(Window & window,bool force)3753435933ddSDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override {
3754435933ddSDimitry Andric ExecutionContext exe_ctx =
3755435933ddSDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
375612b93ac6SEd Maste Process *process = exe_ctx.GetProcessPtr();
375712b93ac6SEd Maste Thread *thread = exe_ctx.GetThreadPtr();
375812b93ac6SEd Maste StackFrame *frame = exe_ctx.GetFramePtr();
375912b93ac6SEd Maste window.Erase();
376012b93ac6SEd Maste window.SetBackground(2);
376112b93ac6SEd Maste window.MoveCursor(0, 0);
3762435933ddSDimitry Andric if (process) {
376312b93ac6SEd Maste const StateType state = process->GetState();
3764435933ddSDimitry Andric window.Printf("Process: %5" PRIu64 " %10s", process->GetID(),
3765435933ddSDimitry Andric StateAsCString(state));
376612b93ac6SEd Maste
3767435933ddSDimitry Andric if (StateIsStoppedState(state, true)) {
37680127ef0fSEd Maste StreamString strm;
3769435933ddSDimitry Andric if (thread && FormatEntity::Format(m_format, strm, nullptr, &exe_ctx,
3770435933ddSDimitry Andric nullptr, nullptr, false, false)) {
377112b93ac6SEd Maste window.MoveCursor(40, 0);
3772435933ddSDimitry Andric window.PutCStringTruncated(strm.GetString().str().c_str(), 1);
37730127ef0fSEd Maste }
377412b93ac6SEd Maste
377512b93ac6SEd Maste window.MoveCursor(60, 0);
377612b93ac6SEd Maste if (frame)
3777435933ddSDimitry Andric window.Printf("Frame: %3u PC = 0x%16.16" PRIx64,
3778435933ddSDimitry Andric frame->GetFrameIndex(),
3779435933ddSDimitry Andric frame->GetFrameCodeAddress().GetOpcodeLoadAddress(
3780435933ddSDimitry Andric exe_ctx.GetTargetPtr()));
3781435933ddSDimitry Andric } else if (state == eStateExited) {
378212b93ac6SEd Maste const char *exit_desc = process->GetExitDescription();
378312b93ac6SEd Maste const int exit_status = process->GetExitStatus();
378412b93ac6SEd Maste if (exit_desc && exit_desc[0])
378512b93ac6SEd Maste window.Printf(" with status = %i (%s)", exit_status, exit_desc);
378612b93ac6SEd Maste else
378712b93ac6SEd Maste window.Printf(" with status = %i", exit_status);
378812b93ac6SEd Maste }
378912b93ac6SEd Maste }
379012b93ac6SEd Maste window.DeferredRefresh();
379112b93ac6SEd Maste return true;
379212b93ac6SEd Maste }
379312b93ac6SEd Maste
379412b93ac6SEd Maste protected:
379512b93ac6SEd Maste Debugger &m_debugger;
37961c3bbb01SEd Maste FormatEntity::Entry m_format;
379712b93ac6SEd Maste };
379812b93ac6SEd Maste
3799435933ddSDimitry Andric class SourceFileWindowDelegate : public WindowDelegate {
380012b93ac6SEd Maste public:
SourceFileWindowDelegate(Debugger & debugger)3801435933ddSDimitry Andric SourceFileWindowDelegate(Debugger &debugger)
3802435933ddSDimitry Andric : WindowDelegate(), m_debugger(debugger), m_sc(), m_file_sp(),
3803435933ddSDimitry Andric m_disassembly_scope(nullptr), m_disassembly_sp(), m_disassembly_range(),
3804435933ddSDimitry Andric m_title(), m_line_width(4), m_selected_line(0), m_pc_line(0),
3805435933ddSDimitry Andric m_stop_id(0), m_frame_idx(UINT32_MAX), m_first_visible_line(0),
3806435933ddSDimitry Andric m_min_x(0), m_min_y(0), m_max_x(0), m_max_y(0) {}
380712b93ac6SEd Maste
38089f2f44ceSEd Maste ~SourceFileWindowDelegate() override = default;
380912b93ac6SEd Maste
Update(const SymbolContext & sc)3810435933ddSDimitry Andric void Update(const SymbolContext &sc) { m_sc = sc; }
381112b93ac6SEd Maste
NumVisibleLines() const3812435933ddSDimitry Andric uint32_t NumVisibleLines() const { return m_max_y - m_min_y; }
381312b93ac6SEd Maste
WindowDelegateGetHelpText()3814435933ddSDimitry Andric const char *WindowDelegateGetHelpText() override {
381512b93ac6SEd Maste return "Source/Disassembly window keyboard shortcuts:";
381612b93ac6SEd Maste }
381712b93ac6SEd Maste
WindowDelegateGetKeyHelp()3818435933ddSDimitry Andric KeyHelp *WindowDelegateGetKeyHelp() override {
381912b93ac6SEd Maste static curses::KeyHelp g_source_view_key_help[] = {
382012b93ac6SEd Maste {KEY_RETURN, "Run to selected line with one shot breakpoint"},
382112b93ac6SEd Maste {KEY_UP, "Select previous source line"},
382212b93ac6SEd Maste {KEY_DOWN, "Select next source line"},
382312b93ac6SEd Maste {KEY_PPAGE, "Page up"},
382412b93ac6SEd Maste {KEY_NPAGE, "Page down"},
382512b93ac6SEd Maste {'b', "Set breakpoint on selected source/disassembly line"},
382612b93ac6SEd Maste {'c', "Continue process"},
382712b93ac6SEd Maste {'d', "Detach and resume process"},
382812b93ac6SEd Maste {'D', "Detach with process suspended"},
382912b93ac6SEd Maste {'h', "Show help dialog"},
383012b93ac6SEd Maste {'k', "Kill process"},
383112b93ac6SEd Maste {'n', "Step over (source line)"},
383212b93ac6SEd Maste {'N', "Step over (single instruction)"},
383312b93ac6SEd Maste {'o', "Step out"},
383412b93ac6SEd Maste {'s', "Step in (source line)"},
383512b93ac6SEd Maste {'S', "Step in (single instruction)"},
383612b93ac6SEd Maste {',', "Page up"},
383712b93ac6SEd Maste {'.', "Page down"},
3838435933ddSDimitry Andric {'\0', nullptr}};
383912b93ac6SEd Maste return g_source_view_key_help;
384012b93ac6SEd Maste }
384112b93ac6SEd Maste
WindowDelegateDraw(Window & window,bool force)3842435933ddSDimitry Andric bool WindowDelegateDraw(Window &window, bool force) override {
3843435933ddSDimitry Andric ExecutionContext exe_ctx =
3844435933ddSDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
384512b93ac6SEd Maste Process *process = exe_ctx.GetProcessPtr();
38464bb0738eSEd Maste Thread *thread = nullptr;
384712b93ac6SEd Maste
384812b93ac6SEd Maste bool update_location = false;
3849435933ddSDimitry Andric if (process) {
385012b93ac6SEd Maste StateType state = process->GetState();
3851435933ddSDimitry Andric if (StateIsStoppedState(state, true)) {
385212b93ac6SEd Maste // We are stopped, so it is ok to
385312b93ac6SEd Maste update_location = true;
385412b93ac6SEd Maste }
385512b93ac6SEd Maste }
385612b93ac6SEd Maste
385712b93ac6SEd Maste m_min_x = 1;
38580127ef0fSEd Maste m_min_y = 2;
385912b93ac6SEd Maste m_max_x = window.GetMaxX() - 1;
386012b93ac6SEd Maste m_max_y = window.GetMaxY() - 1;
386112b93ac6SEd Maste
386212b93ac6SEd Maste const uint32_t num_visible_lines = NumVisibleLines();
386312b93ac6SEd Maste StackFrameSP frame_sp;
386412b93ac6SEd Maste bool set_selected_line_to_pc = false;
386512b93ac6SEd Maste
3866435933ddSDimitry Andric if (update_location) {
386712b93ac6SEd Maste const bool process_alive = process ? process->IsAlive() : false;
386812b93ac6SEd Maste bool thread_changed = false;
3869435933ddSDimitry Andric if (process_alive) {
387012b93ac6SEd Maste thread = exe_ctx.GetThreadPtr();
3871435933ddSDimitry Andric if (thread) {
387212b93ac6SEd Maste frame_sp = thread->GetSelectedFrame();
387312b93ac6SEd Maste auto tid = thread->GetID();
387412b93ac6SEd Maste thread_changed = tid != m_tid;
387512b93ac6SEd Maste m_tid = tid;
3876435933ddSDimitry Andric } else {
3877435933ddSDimitry Andric if (m_tid != LLDB_INVALID_THREAD_ID) {
387812b93ac6SEd Maste thread_changed = true;
387912b93ac6SEd Maste m_tid = LLDB_INVALID_THREAD_ID;
388012b93ac6SEd Maste }
388112b93ac6SEd Maste }
388212b93ac6SEd Maste }
388312b93ac6SEd Maste const uint32_t stop_id = process ? process->GetStopID() : 0;
388412b93ac6SEd Maste const bool stop_id_changed = stop_id != m_stop_id;
388512b93ac6SEd Maste bool frame_changed = false;
388612b93ac6SEd Maste m_stop_id = stop_id;
38870127ef0fSEd Maste m_title.Clear();
3888435933ddSDimitry Andric if (frame_sp) {
388912b93ac6SEd Maste m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
3890435933ddSDimitry Andric if (m_sc.module_sp) {
3891435933ddSDimitry Andric m_title.Printf(
3892435933ddSDimitry Andric "%s", m_sc.module_sp->GetFileSpec().GetFilename().GetCString());
38930127ef0fSEd Maste ConstString func_name = m_sc.GetFunctionName();
38940127ef0fSEd Maste if (func_name)
38950127ef0fSEd Maste m_title.Printf("`%s", func_name.GetCString());
38960127ef0fSEd Maste }
389712b93ac6SEd Maste const uint32_t frame_idx = frame_sp->GetFrameIndex();
389812b93ac6SEd Maste frame_changed = frame_idx != m_frame_idx;
389912b93ac6SEd Maste m_frame_idx = frame_idx;
3900435933ddSDimitry Andric } else {
390112b93ac6SEd Maste m_sc.Clear(true);
390212b93ac6SEd Maste frame_changed = m_frame_idx != UINT32_MAX;
390312b93ac6SEd Maste m_frame_idx = UINT32_MAX;
390412b93ac6SEd Maste }
390512b93ac6SEd Maste
3906435933ddSDimitry Andric const bool context_changed =
3907435933ddSDimitry Andric thread_changed || frame_changed || stop_id_changed;
390812b93ac6SEd Maste
3909435933ddSDimitry Andric if (process_alive) {
3910435933ddSDimitry Andric if (m_sc.line_entry.IsValid()) {
391112b93ac6SEd Maste m_pc_line = m_sc.line_entry.line;
391212b93ac6SEd Maste if (m_pc_line != UINT32_MAX)
391312b93ac6SEd Maste --m_pc_line; // Convert to zero based line number...
391412b93ac6SEd Maste // Update the selected line if the stop ID changed...
391512b93ac6SEd Maste if (context_changed)
391612b93ac6SEd Maste m_selected_line = m_pc_line;
391712b93ac6SEd Maste
3918435933ddSDimitry Andric if (m_file_sp && m_file_sp->FileSpecMatches(m_sc.line_entry.file)) {
39194ba319b5SDimitry Andric // Same file, nothing to do, we should either have the lines or not
39204ba319b5SDimitry Andric // (source file missing)
3921435933ddSDimitry Andric if (m_selected_line >= static_cast<size_t>(m_first_visible_line)) {
392212b93ac6SEd Maste if (m_selected_line >= m_first_visible_line + num_visible_lines)
392312b93ac6SEd Maste m_first_visible_line = m_selected_line - 10;
3924435933ddSDimitry Andric } else {
392512b93ac6SEd Maste if (m_selected_line > 10)
392612b93ac6SEd Maste m_first_visible_line = m_selected_line - 10;
392712b93ac6SEd Maste else
392812b93ac6SEd Maste m_first_visible_line = 0;
392912b93ac6SEd Maste }
3930435933ddSDimitry Andric } else {
393112b93ac6SEd Maste // File changed, set selected line to the line with the PC
393212b93ac6SEd Maste m_selected_line = m_pc_line;
3933435933ddSDimitry Andric m_file_sp =
3934435933ddSDimitry Andric m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file);
3935435933ddSDimitry Andric if (m_file_sp) {
393612b93ac6SEd Maste const size_t num_lines = m_file_sp->GetNumLines();
393712b93ac6SEd Maste int m_line_width = 1;
393812b93ac6SEd Maste for (size_t n = num_lines; n >= 10; n = n / 10)
393912b93ac6SEd Maste ++m_line_width;
394012b93ac6SEd Maste
3941435933ddSDimitry Andric snprintf(m_line_format, sizeof(m_line_format), " %%%iu ",
3942435933ddSDimitry Andric m_line_width);
3943435933ddSDimitry Andric if (num_lines < num_visible_lines ||
3944435933ddSDimitry Andric m_selected_line < num_visible_lines)
394512b93ac6SEd Maste m_first_visible_line = 0;
394612b93ac6SEd Maste else
394712b93ac6SEd Maste m_first_visible_line = m_selected_line - 10;
394812b93ac6SEd Maste }
394912b93ac6SEd Maste }
3950435933ddSDimitry Andric } else {
395112b93ac6SEd Maste m_file_sp.reset();
395212b93ac6SEd Maste }
395312b93ac6SEd Maste
3954435933ddSDimitry Andric if (!m_file_sp || m_file_sp->GetNumLines() == 0) {
395512b93ac6SEd Maste // Show disassembly
395612b93ac6SEd Maste bool prefer_file_cache = false;
3957435933ddSDimitry Andric if (m_sc.function) {
3958435933ddSDimitry Andric if (m_disassembly_scope != m_sc.function) {
395912b93ac6SEd Maste m_disassembly_scope = m_sc.function;
3960435933ddSDimitry Andric m_disassembly_sp = m_sc.function->GetInstructions(
3961435933ddSDimitry Andric exe_ctx, nullptr, prefer_file_cache);
3962435933ddSDimitry Andric if (m_disassembly_sp) {
396312b93ac6SEd Maste set_selected_line_to_pc = true;
396412b93ac6SEd Maste m_disassembly_range = m_sc.function->GetAddressRange();
3965435933ddSDimitry Andric } else {
396612b93ac6SEd Maste m_disassembly_range.Clear();
396712b93ac6SEd Maste }
3968435933ddSDimitry Andric } else {
396912b93ac6SEd Maste set_selected_line_to_pc = context_changed;
397012b93ac6SEd Maste }
3971435933ddSDimitry Andric } else if (m_sc.symbol) {
3972435933ddSDimitry Andric if (m_disassembly_scope != m_sc.symbol) {
397312b93ac6SEd Maste m_disassembly_scope = m_sc.symbol;
3974435933ddSDimitry Andric m_disassembly_sp = m_sc.symbol->GetInstructions(
3975435933ddSDimitry Andric exe_ctx, nullptr, prefer_file_cache);
3976435933ddSDimitry Andric if (m_disassembly_sp) {
397712b93ac6SEd Maste set_selected_line_to_pc = true;
3978435933ddSDimitry Andric m_disassembly_range.GetBaseAddress() =
3979435933ddSDimitry Andric m_sc.symbol->GetAddress();
398012b93ac6SEd Maste m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize());
3981435933ddSDimitry Andric } else {
398212b93ac6SEd Maste m_disassembly_range.Clear();
398312b93ac6SEd Maste }
3984435933ddSDimitry Andric } else {
398512b93ac6SEd Maste set_selected_line_to_pc = context_changed;
398612b93ac6SEd Maste }
398712b93ac6SEd Maste }
398812b93ac6SEd Maste }
3989435933ddSDimitry Andric } else {
399012b93ac6SEd Maste m_pc_line = UINT32_MAX;
399112b93ac6SEd Maste }
399212b93ac6SEd Maste }
399312b93ac6SEd Maste
39940127ef0fSEd Maste const int window_width = window.GetWidth();
399512b93ac6SEd Maste window.Erase();
399612b93ac6SEd Maste window.DrawTitleBox("Sources");
3997435933ddSDimitry Andric if (!m_title.GetString().empty()) {
39980127ef0fSEd Maste window.AttributeOn(A_REVERSE);
39990127ef0fSEd Maste window.MoveCursor(1, 1);
40000127ef0fSEd Maste window.PutChar(' ');
4001435933ddSDimitry Andric window.PutCStringTruncated(m_title.GetString().str().c_str(), 1);
40020127ef0fSEd Maste int x = window.GetCursorX();
4003435933ddSDimitry Andric if (x < window_width - 1) {
40040127ef0fSEd Maste window.Printf("%*s", window_width - x - 1, "");
40050127ef0fSEd Maste }
40060127ef0fSEd Maste window.AttributeOff(A_REVERSE);
40070127ef0fSEd Maste }
400812b93ac6SEd Maste
400912b93ac6SEd Maste Target *target = exe_ctx.GetTargetPtr();
401012b93ac6SEd Maste const size_t num_source_lines = GetNumSourceLines();
4011435933ddSDimitry Andric if (num_source_lines > 0) {
401212b93ac6SEd Maste // Display source
401312b93ac6SEd Maste BreakpointLines bp_lines;
4014435933ddSDimitry Andric if (target) {
401512b93ac6SEd Maste BreakpointList &bp_list = target->GetBreakpointList();
401612b93ac6SEd Maste const size_t num_bps = bp_list.GetSize();
4017435933ddSDimitry Andric for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
401812b93ac6SEd Maste BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
401912b93ac6SEd Maste const size_t num_bps_locs = bp_sp->GetNumLocations();
4020435933ddSDimitry Andric for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
4021435933ddSDimitry Andric BreakpointLocationSP bp_loc_sp =
4022435933ddSDimitry Andric bp_sp->GetLocationAtIndex(bp_loc_idx);
402312b93ac6SEd Maste LineEntry bp_loc_line_entry;
4024435933ddSDimitry Andric if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry(
4025435933ddSDimitry Andric bp_loc_line_entry)) {
4026435933ddSDimitry Andric if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file) {
402712b93ac6SEd Maste bp_lines.insert(bp_loc_line_entry.line);
402812b93ac6SEd Maste }
402912b93ac6SEd Maste }
403012b93ac6SEd Maste }
403112b93ac6SEd Maste }
403212b93ac6SEd Maste }
403312b93ac6SEd Maste
403412b93ac6SEd Maste const attr_t selected_highlight_attr = A_REVERSE;
403512b93ac6SEd Maste const attr_t pc_highlight_attr = COLOR_PAIR(1);
403612b93ac6SEd Maste
4037435933ddSDimitry Andric for (size_t i = 0; i < num_visible_lines; ++i) {
403812b93ac6SEd Maste const uint32_t curr_line = m_first_visible_line + i;
4039435933ddSDimitry Andric if (curr_line < num_source_lines) {
40400127ef0fSEd Maste const int line_y = m_min_y + i;
404112b93ac6SEd Maste window.MoveCursor(1, line_y);
404212b93ac6SEd Maste const bool is_pc_line = curr_line == m_pc_line;
404312b93ac6SEd Maste const bool line_is_selected = m_selected_line == curr_line;
404412b93ac6SEd Maste // Highlight the line as the PC line first, then if the selected line
404512b93ac6SEd Maste // isn't the same as the PC line, highlight it differently
404612b93ac6SEd Maste attr_t highlight_attr = 0;
404712b93ac6SEd Maste attr_t bp_attr = 0;
404812b93ac6SEd Maste if (is_pc_line)
404912b93ac6SEd Maste highlight_attr = pc_highlight_attr;
405012b93ac6SEd Maste else if (line_is_selected)
405112b93ac6SEd Maste highlight_attr = selected_highlight_attr;
405212b93ac6SEd Maste
405312b93ac6SEd Maste if (bp_lines.find(curr_line + 1) != bp_lines.end())
405412b93ac6SEd Maste bp_attr = COLOR_PAIR(2);
405512b93ac6SEd Maste
405612b93ac6SEd Maste if (bp_attr)
405712b93ac6SEd Maste window.AttributeOn(bp_attr);
405812b93ac6SEd Maste
405912b93ac6SEd Maste window.Printf(m_line_format, curr_line + 1);
406012b93ac6SEd Maste
406112b93ac6SEd Maste if (bp_attr)
406212b93ac6SEd Maste window.AttributeOff(bp_attr);
406312b93ac6SEd Maste
406412b93ac6SEd Maste window.PutChar(ACS_VLINE);
406512b93ac6SEd Maste // Mark the line with the PC with a diamond
406612b93ac6SEd Maste if (is_pc_line)
406712b93ac6SEd Maste window.PutChar(ACS_DIAMOND);
406812b93ac6SEd Maste else
406912b93ac6SEd Maste window.PutChar(' ');
407012b93ac6SEd Maste
407112b93ac6SEd Maste if (highlight_attr)
407212b93ac6SEd Maste window.AttributeOn(highlight_attr);
4073435933ddSDimitry Andric const uint32_t line_len =
4074435933ddSDimitry Andric m_file_sp->GetLineLength(curr_line + 1, false);
407512b93ac6SEd Maste if (line_len > 0)
407612b93ac6SEd Maste window.PutCString(m_file_sp->PeekLineData(curr_line + 1), line_len);
407712b93ac6SEd Maste
4078435933ddSDimitry Andric if (is_pc_line && frame_sp &&
4079435933ddSDimitry Andric frame_sp->GetConcreteFrameIndex() == 0) {
408012b93ac6SEd Maste StopInfoSP stop_info_sp;
408112b93ac6SEd Maste if (thread)
408212b93ac6SEd Maste stop_info_sp = thread->GetStopInfo();
4083435933ddSDimitry Andric if (stop_info_sp) {
408412b93ac6SEd Maste const char *stop_description = stop_info_sp->GetDescription();
4085435933ddSDimitry Andric if (stop_description && stop_description[0]) {
408612b93ac6SEd Maste size_t stop_description_len = strlen(stop_description);
40870127ef0fSEd Maste int desc_x = window_width - stop_description_len - 16;
408812b93ac6SEd Maste window.Printf("%*s", desc_x - window.GetCursorX(), "");
4089435933ddSDimitry Andric // window.MoveCursor(window_width - stop_description_len - 15,
4090435933ddSDimitry Andric // line_y);
4091435933ddSDimitry Andric window.Printf("<<< Thread %u: %s ", thread->GetIndexID(),
4092435933ddSDimitry Andric stop_description);
409312b93ac6SEd Maste }
4094435933ddSDimitry Andric } else {
40950127ef0fSEd Maste window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
409612b93ac6SEd Maste }
409712b93ac6SEd Maste }
409812b93ac6SEd Maste if (highlight_attr)
409912b93ac6SEd Maste window.AttributeOff(highlight_attr);
4100435933ddSDimitry Andric } else {
410112b93ac6SEd Maste break;
410212b93ac6SEd Maste }
410312b93ac6SEd Maste }
4104435933ddSDimitry Andric } else {
410512b93ac6SEd Maste size_t num_disassembly_lines = GetNumDisassemblyLines();
4106435933ddSDimitry Andric if (num_disassembly_lines > 0) {
410712b93ac6SEd Maste // Display disassembly
410812b93ac6SEd Maste BreakpointAddrs bp_file_addrs;
410912b93ac6SEd Maste Target *target = exe_ctx.GetTargetPtr();
4110435933ddSDimitry Andric if (target) {
411112b93ac6SEd Maste BreakpointList &bp_list = target->GetBreakpointList();
411212b93ac6SEd Maste const size_t num_bps = bp_list.GetSize();
4113435933ddSDimitry Andric for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
411412b93ac6SEd Maste BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
411512b93ac6SEd Maste const size_t num_bps_locs = bp_sp->GetNumLocations();
4116435933ddSDimitry Andric for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs;
4117435933ddSDimitry Andric ++bp_loc_idx) {
4118435933ddSDimitry Andric BreakpointLocationSP bp_loc_sp =
4119435933ddSDimitry Andric bp_sp->GetLocationAtIndex(bp_loc_idx);
412012b93ac6SEd Maste LineEntry bp_loc_line_entry;
4121435933ddSDimitry Andric const lldb::addr_t file_addr =
4122435933ddSDimitry Andric bp_loc_sp->GetAddress().GetFileAddress();
4123435933ddSDimitry Andric if (file_addr != LLDB_INVALID_ADDRESS) {
412412b93ac6SEd Maste if (m_disassembly_range.ContainsFileAddress(file_addr))
412512b93ac6SEd Maste bp_file_addrs.insert(file_addr);
412612b93ac6SEd Maste }
412712b93ac6SEd Maste }
412812b93ac6SEd Maste }
412912b93ac6SEd Maste }
413012b93ac6SEd Maste
413112b93ac6SEd Maste const attr_t selected_highlight_attr = A_REVERSE;
413212b93ac6SEd Maste const attr_t pc_highlight_attr = COLOR_PAIR(1);
413312b93ac6SEd Maste
413412b93ac6SEd Maste StreamString strm;
413512b93ac6SEd Maste
413612b93ac6SEd Maste InstructionList &insts = m_disassembly_sp->GetInstructionList();
413712b93ac6SEd Maste Address pc_address;
413812b93ac6SEd Maste
413912b93ac6SEd Maste if (frame_sp)
414012b93ac6SEd Maste pc_address = frame_sp->GetFrameCodeAddress();
4141435933ddSDimitry Andric const uint32_t pc_idx =
4142435933ddSDimitry Andric pc_address.IsValid()
4143435933ddSDimitry Andric ? insts.GetIndexOfInstructionAtAddress(pc_address)
4144435933ddSDimitry Andric : UINT32_MAX;
4145435933ddSDimitry Andric if (set_selected_line_to_pc) {
414612b93ac6SEd Maste m_selected_line = pc_idx;
414712b93ac6SEd Maste }
414812b93ac6SEd Maste
414912b93ac6SEd Maste const uint32_t non_visible_pc_offset = (num_visible_lines / 5);
41500127ef0fSEd Maste if (static_cast<size_t>(m_first_visible_line) >= num_disassembly_lines)
415112b93ac6SEd Maste m_first_visible_line = 0;
415212b93ac6SEd Maste
4153435933ddSDimitry Andric if (pc_idx < num_disassembly_lines) {
41540127ef0fSEd Maste if (pc_idx < static_cast<uint32_t>(m_first_visible_line) ||
415512b93ac6SEd Maste pc_idx >= m_first_visible_line + num_visible_lines)
415612b93ac6SEd Maste m_first_visible_line = pc_idx - non_visible_pc_offset;
415712b93ac6SEd Maste }
415812b93ac6SEd Maste
4159435933ddSDimitry Andric for (size_t i = 0; i < num_visible_lines; ++i) {
416012b93ac6SEd Maste const uint32_t inst_idx = m_first_visible_line + i;
416112b93ac6SEd Maste Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get();
416212b93ac6SEd Maste if (!inst)
416312b93ac6SEd Maste break;
416412b93ac6SEd Maste
41650127ef0fSEd Maste const int line_y = m_min_y + i;
41660127ef0fSEd Maste window.MoveCursor(1, line_y);
416712b93ac6SEd Maste const bool is_pc_line = frame_sp && inst_idx == pc_idx;
416812b93ac6SEd Maste const bool line_is_selected = m_selected_line == inst_idx;
416912b93ac6SEd Maste // Highlight the line as the PC line first, then if the selected line
417012b93ac6SEd Maste // isn't the same as the PC line, highlight it differently
417112b93ac6SEd Maste attr_t highlight_attr = 0;
417212b93ac6SEd Maste attr_t bp_attr = 0;
417312b93ac6SEd Maste if (is_pc_line)
417412b93ac6SEd Maste highlight_attr = pc_highlight_attr;
417512b93ac6SEd Maste else if (line_is_selected)
417612b93ac6SEd Maste highlight_attr = selected_highlight_attr;
417712b93ac6SEd Maste
4178435933ddSDimitry Andric if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) !=
4179435933ddSDimitry Andric bp_file_addrs.end())
418012b93ac6SEd Maste bp_attr = COLOR_PAIR(2);
418112b93ac6SEd Maste
418212b93ac6SEd Maste if (bp_attr)
418312b93ac6SEd Maste window.AttributeOn(bp_attr);
418412b93ac6SEd Maste
41850127ef0fSEd Maste window.Printf(" 0x%16.16llx ",
4186435933ddSDimitry Andric static_cast<unsigned long long>(
4187435933ddSDimitry Andric inst->GetAddress().GetLoadAddress(target)));
418812b93ac6SEd Maste
418912b93ac6SEd Maste if (bp_attr)
419012b93ac6SEd Maste window.AttributeOff(bp_attr);
419112b93ac6SEd Maste
419212b93ac6SEd Maste window.PutChar(ACS_VLINE);
419312b93ac6SEd Maste // Mark the line with the PC with a diamond
419412b93ac6SEd Maste if (is_pc_line)
419512b93ac6SEd Maste window.PutChar(ACS_DIAMOND);
419612b93ac6SEd Maste else
419712b93ac6SEd Maste window.PutChar(' ');
419812b93ac6SEd Maste
419912b93ac6SEd Maste if (highlight_attr)
420012b93ac6SEd Maste window.AttributeOn(highlight_attr);
420112b93ac6SEd Maste
420212b93ac6SEd Maste const char *mnemonic = inst->GetMnemonic(&exe_ctx);
420312b93ac6SEd Maste const char *operands = inst->GetOperands(&exe_ctx);
420412b93ac6SEd Maste const char *comment = inst->GetComment(&exe_ctx);
420512b93ac6SEd Maste
42064bb0738eSEd Maste if (mnemonic != nullptr && mnemonic[0] == '\0')
42074bb0738eSEd Maste mnemonic = nullptr;
42084bb0738eSEd Maste if (operands != nullptr && operands[0] == '\0')
42094bb0738eSEd Maste operands = nullptr;
42104bb0738eSEd Maste if (comment != nullptr && comment[0] == '\0')
42114bb0738eSEd Maste comment = nullptr;
421212b93ac6SEd Maste
421312b93ac6SEd Maste strm.Clear();
421412b93ac6SEd Maste
42154bb0738eSEd Maste if (mnemonic != nullptr && operands != nullptr && comment != nullptr)
421612b93ac6SEd Maste strm.Printf("%-8s %-25s ; %s", mnemonic, operands, comment);
42174bb0738eSEd Maste else if (mnemonic != nullptr && operands != nullptr)
421812b93ac6SEd Maste strm.Printf("%-8s %s", mnemonic, operands);
42194bb0738eSEd Maste else if (mnemonic != nullptr)
422012b93ac6SEd Maste strm.Printf("%s", mnemonic);
422112b93ac6SEd Maste
422212b93ac6SEd Maste int right_pad = 1;
4223435933ddSDimitry Andric window.PutCStringTruncated(strm.GetData(), right_pad);
422412b93ac6SEd Maste
4225435933ddSDimitry Andric if (is_pc_line && frame_sp &&
4226435933ddSDimitry Andric frame_sp->GetConcreteFrameIndex() == 0) {
422712b93ac6SEd Maste StopInfoSP stop_info_sp;
422812b93ac6SEd Maste if (thread)
422912b93ac6SEd Maste stop_info_sp = thread->GetStopInfo();
4230435933ddSDimitry Andric if (stop_info_sp) {
423112b93ac6SEd Maste const char *stop_description = stop_info_sp->GetDescription();
4232435933ddSDimitry Andric if (stop_description && stop_description[0]) {
423312b93ac6SEd Maste size_t stop_description_len = strlen(stop_description);
42340127ef0fSEd Maste int desc_x = window_width - stop_description_len - 16;
423512b93ac6SEd Maste window.Printf("%*s", desc_x - window.GetCursorX(), "");
4236435933ddSDimitry Andric // window.MoveCursor(window_width - stop_description_len - 15,
4237435933ddSDimitry Andric // line_y);
4238435933ddSDimitry Andric window.Printf("<<< Thread %u: %s ", thread->GetIndexID(),
4239435933ddSDimitry Andric stop_description);
424012b93ac6SEd Maste }
4241435933ddSDimitry Andric } else {
42420127ef0fSEd Maste window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
424312b93ac6SEd Maste }
424412b93ac6SEd Maste }
424512b93ac6SEd Maste if (highlight_attr)
424612b93ac6SEd Maste window.AttributeOff(highlight_attr);
424712b93ac6SEd Maste }
424812b93ac6SEd Maste }
424912b93ac6SEd Maste }
425012b93ac6SEd Maste window.DeferredRefresh();
425112b93ac6SEd Maste return true; // Drawing handled
425212b93ac6SEd Maste }
425312b93ac6SEd Maste
GetNumLines()4254435933ddSDimitry Andric size_t GetNumLines() {
425512b93ac6SEd Maste size_t num_lines = GetNumSourceLines();
425612b93ac6SEd Maste if (num_lines == 0)
425712b93ac6SEd Maste num_lines = GetNumDisassemblyLines();
425812b93ac6SEd Maste return num_lines;
425912b93ac6SEd Maste }
426012b93ac6SEd Maste
GetNumSourceLines() const4261435933ddSDimitry Andric size_t GetNumSourceLines() const {
426212b93ac6SEd Maste if (m_file_sp)
426312b93ac6SEd Maste return m_file_sp->GetNumLines();
426412b93ac6SEd Maste return 0;
426512b93ac6SEd Maste }
42669f2f44ceSEd Maste
GetNumDisassemblyLines() const4267435933ddSDimitry Andric size_t GetNumDisassemblyLines() const {
426812b93ac6SEd Maste if (m_disassembly_sp)
426912b93ac6SEd Maste return m_disassembly_sp->GetInstructionList().GetSize();
427012b93ac6SEd Maste return 0;
427112b93ac6SEd Maste }
427212b93ac6SEd Maste
WindowDelegateHandleChar(Window & window,int c)4273435933ddSDimitry Andric HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
427412b93ac6SEd Maste const uint32_t num_visible_lines = NumVisibleLines();
427512b93ac6SEd Maste const size_t num_lines = GetNumLines();
427612b93ac6SEd Maste
4277435933ddSDimitry Andric switch (c) {
427812b93ac6SEd Maste case ',':
427912b93ac6SEd Maste case KEY_PPAGE:
428012b93ac6SEd Maste // Page up key
42810127ef0fSEd Maste if (static_cast<uint32_t>(m_first_visible_line) > num_visible_lines)
428212b93ac6SEd Maste m_first_visible_line -= num_visible_lines;
428312b93ac6SEd Maste else
428412b93ac6SEd Maste m_first_visible_line = 0;
428512b93ac6SEd Maste m_selected_line = m_first_visible_line;
428612b93ac6SEd Maste return eKeyHandled;
428712b93ac6SEd Maste
428812b93ac6SEd Maste case '.':
428912b93ac6SEd Maste case KEY_NPAGE:
429012b93ac6SEd Maste // Page down key
429112b93ac6SEd Maste {
429212b93ac6SEd Maste if (m_first_visible_line + num_visible_lines < num_lines)
429312b93ac6SEd Maste m_first_visible_line += num_visible_lines;
429412b93ac6SEd Maste else if (num_lines < num_visible_lines)
429512b93ac6SEd Maste m_first_visible_line = 0;
429612b93ac6SEd Maste else
429712b93ac6SEd Maste m_first_visible_line = num_lines - num_visible_lines;
429812b93ac6SEd Maste m_selected_line = m_first_visible_line;
429912b93ac6SEd Maste }
430012b93ac6SEd Maste return eKeyHandled;
430112b93ac6SEd Maste
430212b93ac6SEd Maste case KEY_UP:
4303435933ddSDimitry Andric if (m_selected_line > 0) {
430412b93ac6SEd Maste m_selected_line--;
43050127ef0fSEd Maste if (static_cast<size_t>(m_first_visible_line) > m_selected_line)
430612b93ac6SEd Maste m_first_visible_line = m_selected_line;
430712b93ac6SEd Maste }
430812b93ac6SEd Maste return eKeyHandled;
430912b93ac6SEd Maste
431012b93ac6SEd Maste case KEY_DOWN:
4311435933ddSDimitry Andric if (m_selected_line + 1 < num_lines) {
431212b93ac6SEd Maste m_selected_line++;
431312b93ac6SEd Maste if (m_first_visible_line + num_visible_lines < m_selected_line)
431412b93ac6SEd Maste m_first_visible_line++;
431512b93ac6SEd Maste }
431612b93ac6SEd Maste return eKeyHandled;
431712b93ac6SEd Maste
431812b93ac6SEd Maste case '\r':
431912b93ac6SEd Maste case '\n':
432012b93ac6SEd Maste case KEY_ENTER:
432112b93ac6SEd Maste // Set a breakpoint and run to the line using a one shot breakpoint
4322435933ddSDimitry Andric if (GetNumSourceLines() > 0) {
4323435933ddSDimitry Andric ExecutionContext exe_ctx =
4324435933ddSDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
4325435933ddSDimitry Andric if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive()) {
4326435933ddSDimitry Andric BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
4327435933ddSDimitry Andric nullptr, // Don't limit the breakpoint to certain modules
432812b93ac6SEd Maste m_file_sp->GetFileSpec(), // Source file
4329435933ddSDimitry Andric m_selected_line +
4330435933ddSDimitry Andric 1, // Source line number (m_selected_line is zero based)
4331*b5893f02SDimitry Andric 0, // Unspecified column.
43324bb0738eSEd Maste 0, // No offset
433312b93ac6SEd Maste eLazyBoolCalculate, // Check inlines using global setting
433412b93ac6SEd Maste eLazyBoolCalculate, // Skip prologue using global setting,
433512b93ac6SEd Maste false, // internal
43361c3bbb01SEd Maste false, // request_hardware
43371c3bbb01SEd Maste eLazyBoolCalculate); // move_to_nearest_code
433812b93ac6SEd Maste // Make breakpoint one shot
433912b93ac6SEd Maste bp_sp->GetOptions()->SetOneShot(true);
434012b93ac6SEd Maste exe_ctx.GetProcessRef().Resume();
434112b93ac6SEd Maste }
4342435933ddSDimitry Andric } else if (m_selected_line < GetNumDisassemblyLines()) {
4343435933ddSDimitry Andric const Instruction *inst = m_disassembly_sp->GetInstructionList()
4344435933ddSDimitry Andric .GetInstructionAtIndex(m_selected_line)
4345435933ddSDimitry Andric .get();
4346435933ddSDimitry Andric ExecutionContext exe_ctx =
4347435933ddSDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
4348435933ddSDimitry Andric if (exe_ctx.HasTargetScope()) {
434912b93ac6SEd Maste Address addr = inst->GetAddress();
4350435933ddSDimitry Andric BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
4351435933ddSDimitry Andric addr, // lldb_private::Address
435212b93ac6SEd Maste false, // internal
435312b93ac6SEd Maste false); // request_hardware
435412b93ac6SEd Maste // Make breakpoint one shot
435512b93ac6SEd Maste bp_sp->GetOptions()->SetOneShot(true);
435612b93ac6SEd Maste exe_ctx.GetProcessRef().Resume();
435712b93ac6SEd Maste }
435812b93ac6SEd Maste }
435912b93ac6SEd Maste return eKeyHandled;
436012b93ac6SEd Maste
436112b93ac6SEd Maste case 'b': // 'b' == toggle breakpoint on currently selected line
4362435933ddSDimitry Andric if (m_selected_line < GetNumSourceLines()) {
4363435933ddSDimitry Andric ExecutionContext exe_ctx =
4364435933ddSDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
4365435933ddSDimitry Andric if (exe_ctx.HasTargetScope()) {
4366435933ddSDimitry Andric BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
4367435933ddSDimitry Andric nullptr, // Don't limit the breakpoint to certain modules
436812b93ac6SEd Maste m_file_sp->GetFileSpec(), // Source file
4369435933ddSDimitry Andric m_selected_line +
4370435933ddSDimitry Andric 1, // Source line number (m_selected_line is zero based)
4371*b5893f02SDimitry Andric 0, // No column specified.
43724bb0738eSEd Maste 0, // No offset
437312b93ac6SEd Maste eLazyBoolCalculate, // Check inlines using global setting
437412b93ac6SEd Maste eLazyBoolCalculate, // Skip prologue using global setting,
437512b93ac6SEd Maste false, // internal
43761c3bbb01SEd Maste false, // request_hardware
43771c3bbb01SEd Maste eLazyBoolCalculate); // move_to_nearest_code
437812b93ac6SEd Maste }
4379435933ddSDimitry Andric } else if (m_selected_line < GetNumDisassemblyLines()) {
4380435933ddSDimitry Andric const Instruction *inst = m_disassembly_sp->GetInstructionList()
4381435933ddSDimitry Andric .GetInstructionAtIndex(m_selected_line)
4382435933ddSDimitry Andric .get();
4383435933ddSDimitry Andric ExecutionContext exe_ctx =
4384435933ddSDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
4385435933ddSDimitry Andric if (exe_ctx.HasTargetScope()) {
438612b93ac6SEd Maste Address addr = inst->GetAddress();
4387435933ddSDimitry Andric BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
4388435933ddSDimitry Andric addr, // lldb_private::Address
438912b93ac6SEd Maste false, // internal
439012b93ac6SEd Maste false); // request_hardware
439112b93ac6SEd Maste }
439212b93ac6SEd Maste }
439312b93ac6SEd Maste return eKeyHandled;
439412b93ac6SEd Maste
439512b93ac6SEd Maste case 'd': // 'd' == detach and let run
439612b93ac6SEd Maste case 'D': // 'D' == detach and keep stopped
439712b93ac6SEd Maste {
4398435933ddSDimitry Andric ExecutionContext exe_ctx =
4399435933ddSDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
440012b93ac6SEd Maste if (exe_ctx.HasProcessScope())
440112b93ac6SEd Maste exe_ctx.GetProcessRef().Detach(c == 'D');
440212b93ac6SEd Maste }
440312b93ac6SEd Maste return eKeyHandled;
440412b93ac6SEd Maste
440512b93ac6SEd Maste case 'k':
440612b93ac6SEd Maste // 'k' == kill
440712b93ac6SEd Maste {
4408435933ddSDimitry Andric ExecutionContext exe_ctx =
4409435933ddSDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
441012b93ac6SEd Maste if (exe_ctx.HasProcessScope())
44111c3bbb01SEd Maste exe_ctx.GetProcessRef().Destroy(false);
441212b93ac6SEd Maste }
441312b93ac6SEd Maste return eKeyHandled;
441412b93ac6SEd Maste
441512b93ac6SEd Maste case 'c':
441612b93ac6SEd Maste // 'c' == continue
441712b93ac6SEd Maste {
4418435933ddSDimitry Andric ExecutionContext exe_ctx =
4419435933ddSDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
442012b93ac6SEd Maste if (exe_ctx.HasProcessScope())
442112b93ac6SEd Maste exe_ctx.GetProcessRef().Resume();
442212b93ac6SEd Maste }
442312b93ac6SEd Maste return eKeyHandled;
442412b93ac6SEd Maste
442512b93ac6SEd Maste case 'o':
442612b93ac6SEd Maste // 'o' == step out
442712b93ac6SEd Maste {
4428435933ddSDimitry Andric ExecutionContext exe_ctx =
4429435933ddSDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
4430435933ddSDimitry Andric if (exe_ctx.HasThreadScope() &&
4431435933ddSDimitry Andric StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
443212b93ac6SEd Maste exe_ctx.GetThreadRef().StepOut();
443312b93ac6SEd Maste }
443412b93ac6SEd Maste }
443512b93ac6SEd Maste return eKeyHandled;
44369f2f44ceSEd Maste
443712b93ac6SEd Maste case 'n': // 'n' == step over
443812b93ac6SEd Maste case 'N': // 'N' == step over instruction
443912b93ac6SEd Maste {
4440435933ddSDimitry Andric ExecutionContext exe_ctx =
4441435933ddSDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
4442435933ddSDimitry Andric if (exe_ctx.HasThreadScope() &&
4443435933ddSDimitry Andric StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
444412b93ac6SEd Maste bool source_step = (c == 'n');
444512b93ac6SEd Maste exe_ctx.GetThreadRef().StepOver(source_step);
444612b93ac6SEd Maste }
444712b93ac6SEd Maste }
444812b93ac6SEd Maste return eKeyHandled;
44499f2f44ceSEd Maste
445012b93ac6SEd Maste case 's': // 's' == step into
445112b93ac6SEd Maste case 'S': // 'S' == step into instruction
445212b93ac6SEd Maste {
4453435933ddSDimitry Andric ExecutionContext exe_ctx =
4454435933ddSDimitry Andric m_debugger.GetCommandInterpreter().GetExecutionContext();
4455435933ddSDimitry Andric if (exe_ctx.HasThreadScope() &&
4456435933ddSDimitry Andric StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
445712b93ac6SEd Maste bool source_step = (c == 's');
44580127ef0fSEd Maste exe_ctx.GetThreadRef().StepIn(source_step);
445912b93ac6SEd Maste }
446012b93ac6SEd Maste }
446112b93ac6SEd Maste return eKeyHandled;
446212b93ac6SEd Maste
446312b93ac6SEd Maste case 'h':
446412b93ac6SEd Maste window.CreateHelpSubwindow();
446512b93ac6SEd Maste return eKeyHandled;
446612b93ac6SEd Maste
446712b93ac6SEd Maste default:
446812b93ac6SEd Maste break;
446912b93ac6SEd Maste }
447012b93ac6SEd Maste return eKeyNotHandled;
447112b93ac6SEd Maste }
447212b93ac6SEd Maste
447312b93ac6SEd Maste protected:
447412b93ac6SEd Maste typedef std::set<uint32_t> BreakpointLines;
447512b93ac6SEd Maste typedef std::set<lldb::addr_t> BreakpointAddrs;
447612b93ac6SEd Maste
447712b93ac6SEd Maste Debugger &m_debugger;
447812b93ac6SEd Maste SymbolContext m_sc;
447912b93ac6SEd Maste SourceManager::FileSP m_file_sp;
448012b93ac6SEd Maste SymbolContextScope *m_disassembly_scope;
448112b93ac6SEd Maste lldb::DisassemblerSP m_disassembly_sp;
448212b93ac6SEd Maste AddressRange m_disassembly_range;
44830127ef0fSEd Maste StreamString m_title;
448412b93ac6SEd Maste lldb::user_id_t m_tid;
448512b93ac6SEd Maste char m_line_format[8];
448612b93ac6SEd Maste int m_line_width;
448712b93ac6SEd Maste uint32_t m_selected_line; // The selected line
448812b93ac6SEd Maste uint32_t m_pc_line; // The line with the PC
448912b93ac6SEd Maste uint32_t m_stop_id;
449012b93ac6SEd Maste uint32_t m_frame_idx;
449112b93ac6SEd Maste int m_first_visible_line;
449212b93ac6SEd Maste int m_min_x;
449312b93ac6SEd Maste int m_min_y;
449412b93ac6SEd Maste int m_max_x;
449512b93ac6SEd Maste int m_max_y;
449612b93ac6SEd Maste };
449712b93ac6SEd Maste
449812b93ac6SEd Maste DisplayOptions ValueObjectListDelegate::g_options = {true};
449912b93ac6SEd Maste
IOHandlerCursesGUI(Debugger & debugger)4500435933ddSDimitry Andric IOHandlerCursesGUI::IOHandlerCursesGUI(Debugger &debugger)
4501435933ddSDimitry Andric : IOHandler(debugger, IOHandler::Type::Curses) {}
450212b93ac6SEd Maste
Activate()4503435933ddSDimitry Andric void IOHandlerCursesGUI::Activate() {
450412b93ac6SEd Maste IOHandler::Activate();
4505435933ddSDimitry Andric if (!m_app_ap) {
450612b93ac6SEd Maste m_app_ap.reset(new Application(GetInputFILE(), GetOutputFILE()));
450712b93ac6SEd Maste
450812b93ac6SEd Maste // This is both a window and a menu delegate
4509435933ddSDimitry Andric std::shared_ptr<ApplicationDelegate> app_delegate_sp(
4510435933ddSDimitry Andric new ApplicationDelegate(*m_app_ap, m_debugger));
451112b93ac6SEd Maste
4512435933ddSDimitry Andric MenuDelegateSP app_menu_delegate_sp =
4513435933ddSDimitry Andric std::static_pointer_cast<MenuDelegate>(app_delegate_sp);
4514435933ddSDimitry Andric MenuSP lldb_menu_sp(
4515435933ddSDimitry Andric new Menu("LLDB", "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB));
4516435933ddSDimitry Andric MenuSP exit_menuitem_sp(
4517435933ddSDimitry Andric new Menu("Exit", nullptr, 'x', ApplicationDelegate::eMenuID_LLDBExit));
451812b93ac6SEd Maste exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit);
4519435933ddSDimitry Andric lldb_menu_sp->AddSubmenu(MenuSP(new Menu(
4520435933ddSDimitry Andric "About LLDB", nullptr, 'a', ApplicationDelegate::eMenuID_LLDBAbout)));
452112b93ac6SEd Maste lldb_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
452212b93ac6SEd Maste lldb_menu_sp->AddSubmenu(exit_menuitem_sp);
452312b93ac6SEd Maste
4524435933ddSDimitry Andric MenuSP target_menu_sp(new Menu("Target", "F2", KEY_F(2),
4525435933ddSDimitry Andric ApplicationDelegate::eMenuID_Target));
4526435933ddSDimitry Andric target_menu_sp->AddSubmenu(MenuSP(new Menu(
4527435933ddSDimitry Andric "Create", nullptr, 'c', ApplicationDelegate::eMenuID_TargetCreate)));
4528435933ddSDimitry Andric target_menu_sp->AddSubmenu(MenuSP(new Menu(
4529435933ddSDimitry Andric "Delete", nullptr, 'd', ApplicationDelegate::eMenuID_TargetDelete)));
453012b93ac6SEd Maste
4531435933ddSDimitry Andric MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3),
4532435933ddSDimitry Andric ApplicationDelegate::eMenuID_Process));
4533435933ddSDimitry Andric process_menu_sp->AddSubmenu(MenuSP(new Menu(
4534435933ddSDimitry Andric "Attach", nullptr, 'a', ApplicationDelegate::eMenuID_ProcessAttach)));
4535435933ddSDimitry Andric process_menu_sp->AddSubmenu(MenuSP(new Menu(
4536435933ddSDimitry Andric "Detach", nullptr, 'd', ApplicationDelegate::eMenuID_ProcessDetach)));
4537435933ddSDimitry Andric process_menu_sp->AddSubmenu(MenuSP(new Menu(
4538435933ddSDimitry Andric "Launch", nullptr, 'l', ApplicationDelegate::eMenuID_ProcessLaunch)));
453912b93ac6SEd Maste process_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
4540435933ddSDimitry Andric process_menu_sp->AddSubmenu(
4541435933ddSDimitry Andric MenuSP(new Menu("Continue", nullptr, 'c',
4542435933ddSDimitry Andric ApplicationDelegate::eMenuID_ProcessContinue)));
4543435933ddSDimitry Andric process_menu_sp->AddSubmenu(MenuSP(new Menu(
4544435933ddSDimitry Andric "Halt", nullptr, 'h', ApplicationDelegate::eMenuID_ProcessHalt)));
4545435933ddSDimitry Andric process_menu_sp->AddSubmenu(MenuSP(new Menu(
4546435933ddSDimitry Andric "Kill", nullptr, 'k', ApplicationDelegate::eMenuID_ProcessKill)));
454712b93ac6SEd Maste
4548435933ddSDimitry Andric MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4),
4549435933ddSDimitry Andric ApplicationDelegate::eMenuID_Thread));
4550435933ddSDimitry Andric thread_menu_sp->AddSubmenu(MenuSP(new Menu(
4551435933ddSDimitry Andric "Step In", nullptr, 'i', ApplicationDelegate::eMenuID_ThreadStepIn)));
4552435933ddSDimitry Andric thread_menu_sp->AddSubmenu(
4553435933ddSDimitry Andric MenuSP(new Menu("Step Over", nullptr, 'v',
4554435933ddSDimitry Andric ApplicationDelegate::eMenuID_ThreadStepOver)));
4555435933ddSDimitry Andric thread_menu_sp->AddSubmenu(MenuSP(new Menu(
4556435933ddSDimitry Andric "Step Out", nullptr, 'o', ApplicationDelegate::eMenuID_ThreadStepOut)));
455712b93ac6SEd Maste
4558435933ddSDimitry Andric MenuSP view_menu_sp(
4559435933ddSDimitry Andric new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View));
4560435933ddSDimitry Andric view_menu_sp->AddSubmenu(
4561435933ddSDimitry Andric MenuSP(new Menu("Backtrace", nullptr, 'b',
4562435933ddSDimitry Andric ApplicationDelegate::eMenuID_ViewBacktrace)));
4563435933ddSDimitry Andric view_menu_sp->AddSubmenu(
4564435933ddSDimitry Andric MenuSP(new Menu("Registers", nullptr, 'r',
4565435933ddSDimitry Andric ApplicationDelegate::eMenuID_ViewRegisters)));
4566435933ddSDimitry Andric view_menu_sp->AddSubmenu(MenuSP(new Menu(
4567435933ddSDimitry Andric "Source", nullptr, 's', ApplicationDelegate::eMenuID_ViewSource)));
4568435933ddSDimitry Andric view_menu_sp->AddSubmenu(
4569435933ddSDimitry Andric MenuSP(new Menu("Variables", nullptr, 'v',
4570435933ddSDimitry Andric ApplicationDelegate::eMenuID_ViewVariables)));
457112b93ac6SEd Maste
4572435933ddSDimitry Andric MenuSP help_menu_sp(
4573435933ddSDimitry Andric new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help));
4574435933ddSDimitry Andric help_menu_sp->AddSubmenu(MenuSP(new Menu(
4575435933ddSDimitry Andric "GUI Help", nullptr, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp)));
457612b93ac6SEd Maste
457712b93ac6SEd Maste m_app_ap->Initialize();
457812b93ac6SEd Maste WindowSP &main_window_sp = m_app_ap->GetMainWindow();
457912b93ac6SEd Maste
458012b93ac6SEd Maste MenuSP menubar_sp(new Menu(Menu::Type::Bar));
458112b93ac6SEd Maste menubar_sp->AddSubmenu(lldb_menu_sp);
458212b93ac6SEd Maste menubar_sp->AddSubmenu(target_menu_sp);
458312b93ac6SEd Maste menubar_sp->AddSubmenu(process_menu_sp);
458412b93ac6SEd Maste menubar_sp->AddSubmenu(thread_menu_sp);
458512b93ac6SEd Maste menubar_sp->AddSubmenu(view_menu_sp);
458612b93ac6SEd Maste menubar_sp->AddSubmenu(help_menu_sp);
458712b93ac6SEd Maste menubar_sp->SetDelegate(app_menu_delegate_sp);
458812b93ac6SEd Maste
458912b93ac6SEd Maste Rect content_bounds = main_window_sp->GetFrame();
459012b93ac6SEd Maste Rect menubar_bounds = content_bounds.MakeMenuBar();
459112b93ac6SEd Maste Rect status_bounds = content_bounds.MakeStatusBar();
459212b93ac6SEd Maste Rect source_bounds;
459312b93ac6SEd Maste Rect variables_bounds;
459412b93ac6SEd Maste Rect threads_bounds;
459512b93ac6SEd Maste Rect source_variables_bounds;
4596435933ddSDimitry Andric content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds,
4597435933ddSDimitry Andric threads_bounds);
4598435933ddSDimitry Andric source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds,
4599435933ddSDimitry Andric variables_bounds);
460012b93ac6SEd Maste
4601435933ddSDimitry Andric WindowSP menubar_window_sp =
4602435933ddSDimitry Andric main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false);
46034ba319b5SDimitry Andric // Let the menubar get keys if the active window doesn't handle the keys
46044ba319b5SDimitry Andric // that are typed so it can respond to menubar key presses.
4605435933ddSDimitry Andric menubar_window_sp->SetCanBeActive(
4606435933ddSDimitry Andric false); // Don't let the menubar become the active window
460712b93ac6SEd Maste menubar_window_sp->SetDelegate(menubar_sp);
460812b93ac6SEd Maste
4609435933ddSDimitry Andric WindowSP source_window_sp(
4610435933ddSDimitry Andric main_window_sp->CreateSubWindow("Source", source_bounds, true));
4611435933ddSDimitry Andric WindowSP variables_window_sp(
4612435933ddSDimitry Andric main_window_sp->CreateSubWindow("Variables", variables_bounds, false));
4613435933ddSDimitry Andric WindowSP threads_window_sp(
4614435933ddSDimitry Andric main_window_sp->CreateSubWindow("Threads", threads_bounds, false));
4615435933ddSDimitry Andric WindowSP status_window_sp(
4616d8866befSDimitry Andric main_window_sp->CreateSubWindow("Status", status_bounds, false));
4617435933ddSDimitry Andric status_window_sp->SetCanBeActive(
4618435933ddSDimitry Andric false); // Don't let the status bar become the active window
4619435933ddSDimitry Andric main_window_sp->SetDelegate(
4620435933ddSDimitry Andric std::static_pointer_cast<WindowDelegate>(app_delegate_sp));
4621435933ddSDimitry Andric source_window_sp->SetDelegate(
4622435933ddSDimitry Andric WindowDelegateSP(new SourceFileWindowDelegate(m_debugger)));
4623435933ddSDimitry Andric variables_window_sp->SetDelegate(
4624435933ddSDimitry Andric WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
46250127ef0fSEd Maste TreeDelegateSP thread_delegate_sp(new ThreadsTreeDelegate(m_debugger));
4626435933ddSDimitry Andric threads_window_sp->SetDelegate(WindowDelegateSP(
4627435933ddSDimitry Andric new TreeWindowDelegate(m_debugger, thread_delegate_sp)));
4628435933ddSDimitry Andric status_window_sp->SetDelegate(
4629435933ddSDimitry Andric WindowDelegateSP(new StatusBarWindowDelegate(m_debugger)));
463012b93ac6SEd Maste
463112b93ac6SEd Maste // Show the main help window once the first time the curses GUI is launched
463212b93ac6SEd Maste static bool g_showed_help = false;
4633435933ddSDimitry Andric if (!g_showed_help) {
463412b93ac6SEd Maste g_showed_help = true;
463512b93ac6SEd Maste main_window_sp->CreateHelpSubwindow();
463612b93ac6SEd Maste }
463712b93ac6SEd Maste
463812b93ac6SEd Maste init_pair(1, COLOR_WHITE, COLOR_BLUE);
463912b93ac6SEd Maste init_pair(2, COLOR_BLACK, COLOR_WHITE);
464012b93ac6SEd Maste init_pair(3, COLOR_MAGENTA, COLOR_WHITE);
464112b93ac6SEd Maste init_pair(4, COLOR_MAGENTA, COLOR_BLACK);
464212b93ac6SEd Maste init_pair(5, COLOR_RED, COLOR_BLACK);
464312b93ac6SEd Maste }
464412b93ac6SEd Maste }
464512b93ac6SEd Maste
Deactivate()4646435933ddSDimitry Andric void IOHandlerCursesGUI::Deactivate() { m_app_ap->Terminate(); }
464712b93ac6SEd Maste
Run()4648435933ddSDimitry Andric void IOHandlerCursesGUI::Run() {
464912b93ac6SEd Maste m_app_ap->Run(m_debugger);
465012b93ac6SEd Maste SetIsDone(true);
465112b93ac6SEd Maste }
465212b93ac6SEd Maste
46539f2f44ceSEd Maste IOHandlerCursesGUI::~IOHandlerCursesGUI() = default;
465412b93ac6SEd Maste
Cancel()4655435933ddSDimitry Andric void IOHandlerCursesGUI::Cancel() {}
465612b93ac6SEd Maste
Interrupt()4657435933ddSDimitry Andric bool IOHandlerCursesGUI::Interrupt() { return false; }
465812b93ac6SEd Maste
GotEOF()4659435933ddSDimitry Andric void IOHandlerCursesGUI::GotEOF() {}
466012b93ac6SEd Maste
46619f2f44ceSEd Maste #endif // LLDB_DISABLE_CURSES
4662