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