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