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