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