blob: 723001168511e3d9e899f67eb97505e899719014 [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
Eugene Zelenko315b6882015-10-26 17:00:13 +000010// C Includes
11#ifndef LLDB_DISABLE_CURSES
Bruce Mitchener27801f42015-11-06 00:21:18 +000012#include <curses.h>
Eugene Zelenko315b6882015-10-26 17:00:13 +000013#include <panel.h>
14#endif
Greg Clayton44d93782014-01-27 23:43:24 +000015
Eugene Zelenko315b6882015-10-26 17:00:13 +000016// C++ Includes
Todd Fiala7c9aa072015-10-28 15:24:19 +000017#if defined(__APPLE__)
18#include <deque>
19#endif
Greg Clayton44d93782014-01-27 23:43:24 +000020#include <string>
21
Eugene Zelenko315b6882015-10-26 17:00:13 +000022// Other libraries and framework includes
23// Project includes
Greg Clayton44d93782014-01-27 23:43:24 +000024#include "lldb/Breakpoint/BreakpointLocation.h"
25#include "lldb/Core/IOHandler.h"
26#include "lldb/Core/Debugger.h"
Greg Claytonec990862014-03-19 16:22:48 +000027#include "lldb/Core/Module.h"
Greg Clayton44d93782014-01-27 23:43:24 +000028#include "lldb/Core/State.h"
29#include "lldb/Core/StreamFile.h"
30#include "lldb/Core/ValueObjectRegister.h"
Todd Fialacacde7d2014-09-27 16:54:22 +000031#ifndef LLDB_DISABLE_LIBEDIT
Greg Clayton44d93782014-01-27 23:43:24 +000032#include "lldb/Host/Editline.h"
Todd Fialacacde7d2014-09-27 16:54:22 +000033#endif
Greg Clayton44d93782014-01-27 23:43:24 +000034#include "lldb/Interpreter/CommandCompletions.h"
35#include "lldb/Interpreter/CommandInterpreter.h"
36#include "lldb/Symbol/Block.h"
37#include "lldb/Symbol/Function.h"
38#include "lldb/Symbol/Symbol.h"
39#include "lldb/Target/RegisterContext.h"
40#include "lldb/Target/ThreadPlan.h"
41
Todd Fiala7c9aa072015-10-28 15:24:19 +000042
43
Greg Clayton44d93782014-01-27 23:43:24 +000044using namespace lldb;
45using namespace lldb_private;
46
Kate Stonee30f11d2014-11-17 19:06:59 +000047IOHandler::IOHandler (Debugger &debugger, IOHandler::Type type) :
Greg Clayton44d93782014-01-27 23:43:24 +000048 IOHandler (debugger,
Kate Stonee30f11d2014-11-17 19:06:59 +000049 type,
Greg Clayton340b0302014-02-05 17:57:57 +000050 StreamFileSP(), // Adopt STDIN from top input reader
51 StreamFileSP(), // Adopt STDOUT from top input reader
52 StreamFileSP(), // Adopt STDERR from top input reader
53 0) // Flags
Greg Clayton44d93782014-01-27 23:43:24 +000054{
55}
56
Greg Clayton44d93782014-01-27 23:43:24 +000057IOHandler::IOHandler (Debugger &debugger,
Kate Stonee30f11d2014-11-17 19:06:59 +000058 IOHandler::Type type,
Greg Clayton44d93782014-01-27 23:43:24 +000059 const lldb::StreamFileSP &input_sp,
60 const lldb::StreamFileSP &output_sp,
Greg Clayton340b0302014-02-05 17:57:57 +000061 const lldb::StreamFileSP &error_sp,
62 uint32_t flags) :
Greg Clayton44d93782014-01-27 23:43:24 +000063 m_debugger (debugger),
64 m_input_sp (input_sp),
65 m_output_sp (output_sp),
66 m_error_sp (error_sp),
Kate Stonee30f11d2014-11-17 19:06:59 +000067 m_popped (false),
Greg Clayton340b0302014-02-05 17:57:57 +000068 m_flags (flags),
Kate Stonee30f11d2014-11-17 19:06:59 +000069 m_type (type),
Greg Clayton44d93782014-01-27 23:43:24 +000070 m_user_data (NULL),
71 m_done (false),
72 m_active (false)
73{
74 // If any files are not specified, then adopt them from the top input reader.
75 if (!m_input_sp || !m_output_sp || !m_error_sp)
76 debugger.AdoptTopIOHandlerFilesIfInvalid (m_input_sp,
77 m_output_sp,
78 m_error_sp);
79}
80
Eugene Zelenko315b6882015-10-26 17:00:13 +000081IOHandler::~IOHandler() = default;
Greg Clayton44d93782014-01-27 23:43:24 +000082
83int
84IOHandler::GetInputFD()
85{
86 if (m_input_sp)
87 return m_input_sp->GetFile().GetDescriptor();
88 return -1;
89}
90
91int
92IOHandler::GetOutputFD()
93{
94 if (m_output_sp)
95 return m_output_sp->GetFile().GetDescriptor();
96 return -1;
97}
98
99int
100IOHandler::GetErrorFD()
101{
102 if (m_error_sp)
103 return m_error_sp->GetFile().GetDescriptor();
104 return -1;
105}
106
107FILE *
108IOHandler::GetInputFILE()
109{
110 if (m_input_sp)
111 return m_input_sp->GetFile().GetStream();
112 return NULL;
113}
114
115FILE *
116IOHandler::GetOutputFILE()
117{
118 if (m_output_sp)
119 return m_output_sp->GetFile().GetStream();
120 return NULL;
121}
122
123FILE *
124IOHandler::GetErrorFILE()
125{
126 if (m_error_sp)
127 return m_error_sp->GetFile().GetStream();
128 return NULL;
129}
130
131StreamFileSP &
132IOHandler::GetInputStreamFile()
133{
134 return m_input_sp;
135}
136
137StreamFileSP &
138IOHandler::GetOutputStreamFile()
139{
140 return m_output_sp;
141}
142
Greg Clayton44d93782014-01-27 23:43:24 +0000143StreamFileSP &
144IOHandler::GetErrorStreamFile()
145{
146 return m_error_sp;
147}
148
Greg Clayton340b0302014-02-05 17:57:57 +0000149bool
150IOHandler::GetIsInteractive ()
151{
152 return GetInputStreamFile()->GetFile().GetIsInteractive ();
153}
154
155bool
156IOHandler::GetIsRealTerminal ()
157{
158 return GetInputStreamFile()->GetFile().GetIsRealTerminal();
159}
Greg Clayton44d93782014-01-27 23:43:24 +0000160
Kate Stonee30f11d2014-11-17 19:06:59 +0000161void
162IOHandler::SetPopped (bool b)
163{
164 m_popped.SetValue(b, eBroadcastOnChange);
165}
166
167void
168IOHandler::WaitForPop ()
169{
170 m_popped.WaitForValueEqualTo(true);
171}
172
Pavel Labath44464872015-05-27 12:40:32 +0000173void
174IOHandlerStack::PrintAsync (Stream *stream, const char *s, size_t len)
175{
176 if (stream)
177 {
178 Mutex::Locker locker (m_mutex);
179 if (m_top)
180 m_top->PrintAsync (stream, s, len);
181 }
182}
183
Greg Clayton44d93782014-01-27 23:43:24 +0000184IOHandlerConfirm::IOHandlerConfirm (Debugger &debugger,
185 const char *prompt,
186 bool default_response) :
187 IOHandlerEditline(debugger,
Kate Stonee30f11d2014-11-17 19:06:59 +0000188 IOHandler::Type::Confirm,
Greg Clayton44d93782014-01-27 23:43:24 +0000189 NULL, // NULL editline_name means no history loaded/saved
Kate Stonee30f11d2014-11-17 19:06:59 +0000190 NULL, // No prompt
191 NULL, // No continuation prompt
Greg Clayton44d93782014-01-27 23:43:24 +0000192 false, // Multi-line
Kate Stonee30f11d2014-11-17 19:06:59 +0000193 false, // Don't colorize the prompt (i.e. the confirm message.)
Greg Claytonf6913cd2014-03-07 00:53:24 +0000194 0,
Greg Clayton44d93782014-01-27 23:43:24 +0000195 *this),
196 m_default_response (default_response),
197 m_user_response (default_response)
198{
199 StreamString prompt_stream;
200 prompt_stream.PutCString(prompt);
201 if (m_default_response)
202 prompt_stream.Printf(": [Y/n] ");
203 else
204 prompt_stream.Printf(": [y/N] ");
205
206 SetPrompt (prompt_stream.GetString().c_str());
207
208}
209
Eugene Zelenko315b6882015-10-26 17:00:13 +0000210IOHandlerConfirm::~IOHandlerConfirm() = default;
Greg Clayton44d93782014-01-27 23:43:24 +0000211
212int
213IOHandlerConfirm::IOHandlerComplete (IOHandler &io_handler,
214 const char *current_line,
215 const char *cursor,
216 const char *last_char,
217 int skip_first_n_matches,
218 int max_matches,
219 StringList &matches)
220{
221 if (current_line == cursor)
222 {
223 if (m_default_response)
224 {
225 matches.AppendString("y");
226 }
227 else
228 {
229 matches.AppendString("n");
230 }
231 }
232 return matches.GetSize();
233}
234
235void
236IOHandlerConfirm::IOHandlerInputComplete (IOHandler &io_handler, std::string &line)
237{
238 if (line.empty())
239 {
240 // User just hit enter, set the response to the default
241 m_user_response = m_default_response;
242 io_handler.SetIsDone(true);
243 return;
244 }
245
246 if (line.size() == 1)
247 {
248 switch (line[0])
249 {
250 case 'y':
251 case 'Y':
252 m_user_response = true;
253 io_handler.SetIsDone(true);
254 return;
255 case 'n':
256 case 'N':
257 m_user_response = false;
258 io_handler.SetIsDone(true);
259 return;
260 default:
261 break;
262 }
263 }
264
265 if (line == "yes" || line == "YES" || line == "Yes")
266 {
267 m_user_response = true;
268 io_handler.SetIsDone(true);
269 }
270 else if (line == "no" || line == "NO" || line == "No")
271 {
272 m_user_response = false;
273 io_handler.SetIsDone(true);
274 }
275}
276
277int
278IOHandlerDelegate::IOHandlerComplete (IOHandler &io_handler,
279 const char *current_line,
280 const char *cursor,
281 const char *last_char,
282 int skip_first_n_matches,
283 int max_matches,
284 StringList &matches)
285{
286 switch (m_completion)
287 {
288 case Completion::None:
289 break;
290
291 case Completion::LLDBCommand:
292 return io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion (current_line,
293 cursor,
294 last_char,
295 skip_first_n_matches,
296 max_matches,
297 matches);
298
299 case Completion::Expression:
300 {
301 bool word_complete = false;
302 const char *word_start = cursor;
303 if (cursor > current_line)
304 --word_start;
305 while (word_start > current_line && !isspace(*word_start))
306 --word_start;
307 CommandCompletions::InvokeCommonCompletionCallbacks (io_handler.GetDebugger().GetCommandInterpreter(),
308 CommandCompletions::eVariablePathCompletion,
309 word_start,
310 skip_first_n_matches,
311 max_matches,
312 NULL,
313 word_complete,
314 matches);
315
316 size_t num_matches = matches.GetSize();
317 if (num_matches > 0)
318 {
319 std::string common_prefix;
320 matches.LongestCommonPrefix (common_prefix);
321 const size_t partial_name_len = strlen(word_start);
322
323 // If we matched a unique single command, add a space...
324 // Only do this if the completer told us this was a complete word, however...
325 if (num_matches == 1 && word_complete)
326 {
327 common_prefix.push_back(' ');
328 }
329 common_prefix.erase (0, partial_name_len);
330 matches.InsertStringAtIndex(0, std::move(common_prefix));
331 }
332 return num_matches;
333 }
334 break;
335 }
336
Greg Clayton44d93782014-01-27 23:43:24 +0000337 return 0;
338}
339
Greg Clayton44d93782014-01-27 23:43:24 +0000340IOHandlerEditline::IOHandlerEditline (Debugger &debugger,
Kate Stonee30f11d2014-11-17 19:06:59 +0000341 IOHandler::Type type,
Greg Clayton44d93782014-01-27 23:43:24 +0000342 const char *editline_name, // Used for saving history files
343 const char *prompt,
Kate Stonee30f11d2014-11-17 19:06:59 +0000344 const char *continuation_prompt,
Greg Clayton44d93782014-01-27 23:43:24 +0000345 bool multi_line,
Kate Stonee30f11d2014-11-17 19:06:59 +0000346 bool color_prompts,
Greg Claytonf6913cd2014-03-07 00:53:24 +0000347 uint32_t line_number_start,
Greg Clayton44d93782014-01-27 23:43:24 +0000348 IOHandlerDelegate &delegate) :
349 IOHandlerEditline(debugger,
Kate Stonee30f11d2014-11-17 19:06:59 +0000350 type,
Greg Clayton44d93782014-01-27 23:43:24 +0000351 StreamFileSP(), // Inherit input from top input reader
352 StreamFileSP(), // Inherit output from top input reader
353 StreamFileSP(), // Inherit error from top input reader
Greg Clayton340b0302014-02-05 17:57:57 +0000354 0, // Flags
Greg Clayton44d93782014-01-27 23:43:24 +0000355 editline_name, // Used for saving history files
356 prompt,
Kate Stonee30f11d2014-11-17 19:06:59 +0000357 continuation_prompt,
Greg Clayton44d93782014-01-27 23:43:24 +0000358 multi_line,
Kate Stonee30f11d2014-11-17 19:06:59 +0000359 color_prompts,
Greg Claytonf6913cd2014-03-07 00:53:24 +0000360 line_number_start,
Greg Clayton44d93782014-01-27 23:43:24 +0000361 delegate)
362{
363}
364
365IOHandlerEditline::IOHandlerEditline (Debugger &debugger,
Kate Stonee30f11d2014-11-17 19:06:59 +0000366 IOHandler::Type type,
Greg Clayton44d93782014-01-27 23:43:24 +0000367 const lldb::StreamFileSP &input_sp,
368 const lldb::StreamFileSP &output_sp,
369 const lldb::StreamFileSP &error_sp,
Greg Clayton340b0302014-02-05 17:57:57 +0000370 uint32_t flags,
Greg Clayton44d93782014-01-27 23:43:24 +0000371 const char *editline_name, // Used for saving history files
372 const char *prompt,
Kate Stonee30f11d2014-11-17 19:06:59 +0000373 const char *continuation_prompt,
Greg Clayton44d93782014-01-27 23:43:24 +0000374 bool multi_line,
Kate Stonee30f11d2014-11-17 19:06:59 +0000375 bool color_prompts,
Greg Claytonf6913cd2014-03-07 00:53:24 +0000376 uint32_t line_number_start,
Greg Clayton44d93782014-01-27 23:43:24 +0000377 IOHandlerDelegate &delegate) :
Kate Stonee30f11d2014-11-17 19:06:59 +0000378 IOHandler (debugger, type, input_sp, output_sp, error_sp, flags),
Todd Fialacacde7d2014-09-27 16:54:22 +0000379#ifndef LLDB_DISABLE_LIBEDIT
Greg Clayton44d93782014-01-27 23:43:24 +0000380 m_editline_ap (),
Todd Fialacacde7d2014-09-27 16:54:22 +0000381#endif
Greg Clayton44d93782014-01-27 23:43:24 +0000382 m_delegate (delegate),
383 m_prompt (),
Kate Stonee30f11d2014-11-17 19:06:59 +0000384 m_continuation_prompt(),
385 m_current_lines_ptr (NULL),
Greg Claytonf6913cd2014-03-07 00:53:24 +0000386 m_base_line_number (line_number_start),
Kate Stonee30f11d2014-11-17 19:06:59 +0000387 m_curr_line_idx (UINT32_MAX),
388 m_multi_line (multi_line),
389 m_color_prompts (color_prompts),
Greg Claytone034a042015-05-21 20:52:06 +0000390 m_interrupt_exits (true),
391 m_editing (false)
Greg Clayton44d93782014-01-27 23:43:24 +0000392{
393 SetPrompt(prompt);
394
Todd Fialacacde7d2014-09-27 16:54:22 +0000395#ifndef LLDB_DISABLE_LIBEDIT
Deepak Panickal914b8d92014-01-31 18:48:46 +0000396 bool use_editline = false;
Greg Clayton340b0302014-02-05 17:57:57 +0000397
Greg Clayton340b0302014-02-05 17:57:57 +0000398 use_editline = m_input_sp->GetFile().GetIsRealTerminal();
Greg Clayton44d93782014-01-27 23:43:24 +0000399
400 if (use_editline)
401 {
402 m_editline_ap.reset(new Editline (editline_name,
Greg Clayton44d93782014-01-27 23:43:24 +0000403 GetInputFILE (),
404 GetOutputFILE (),
Kate Stonee30f11d2014-11-17 19:06:59 +0000405 GetErrorFILE (),
406 m_color_prompts));
407 m_editline_ap->SetIsInputCompleteCallback (IsInputCompleteCallback, this);
Greg Clayton44d93782014-01-27 23:43:24 +0000408 m_editline_ap->SetAutoCompleteCallback (AutoCompleteCallback, this);
Kate Stonee30f11d2014-11-17 19:06:59 +0000409 // See if the delegate supports fixing indentation
410 const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters();
411 if (indent_chars)
412 {
413 // The delegate does support indentation, hook it up so when any indentation
414 // character is typed, the delegate gets a chance to fix it
415 m_editline_ap->SetFixIndentationCallback (FixIndentationCallback, this, indent_chars);
416 }
Greg Clayton44d93782014-01-27 23:43:24 +0000417 }
Todd Fialacacde7d2014-09-27 16:54:22 +0000418#endif
Kate Stonee30f11d2014-11-17 19:06:59 +0000419 SetBaseLineNumber (m_base_line_number);
420 SetPrompt(prompt ? prompt : "");
421 SetContinuationPrompt(continuation_prompt);
Greg Clayton44d93782014-01-27 23:43:24 +0000422}
423
424IOHandlerEditline::~IOHandlerEditline ()
425{
Todd Fialacacde7d2014-09-27 16:54:22 +0000426#ifndef LLDB_DISABLE_LIBEDIT
Greg Clayton44d93782014-01-27 23:43:24 +0000427 m_editline_ap.reset();
Todd Fialacacde7d2014-09-27 16:54:22 +0000428#endif
Greg Clayton44d93782014-01-27 23:43:24 +0000429}
430
Kate Stonee30f11d2014-11-17 19:06:59 +0000431void
432IOHandlerEditline::Activate ()
433{
434 IOHandler::Activate();
435 m_delegate.IOHandlerActivated(*this);
436}
437
438void
439IOHandlerEditline::Deactivate ()
440{
441 IOHandler::Deactivate();
442 m_delegate.IOHandlerDeactivated(*this);
443}
444
Greg Clayton44d93782014-01-27 23:43:24 +0000445bool
Greg Claytonf0066ad2014-05-02 00:45:31 +0000446IOHandlerEditline::GetLine (std::string &line, bool &interrupted)
Greg Clayton44d93782014-01-27 23:43:24 +0000447{
Todd Fialacacde7d2014-09-27 16:54:22 +0000448#ifndef LLDB_DISABLE_LIBEDIT
Greg Clayton44d93782014-01-27 23:43:24 +0000449 if (m_editline_ap)
450 {
Kate Stonee30f11d2014-11-17 19:06:59 +0000451 return m_editline_ap->GetLine (line, interrupted);
Greg Clayton44d93782014-01-27 23:43:24 +0000452 }
453 else
454 {
Todd Fialacacde7d2014-09-27 16:54:22 +0000455#endif
Greg Clayton44d93782014-01-27 23:43:24 +0000456 line.clear();
457
458 FILE *in = GetInputFILE();
459 if (in)
460 {
Greg Clayton340b0302014-02-05 17:57:57 +0000461 if (GetIsInteractive())
Greg Clayton44d93782014-01-27 23:43:24 +0000462 {
Kate Stonee30f11d2014-11-17 19:06:59 +0000463 const char *prompt = NULL;
464
465 if (m_multi_line && m_curr_line_idx > 0)
466 prompt = GetContinuationPrompt();
467
468 if (prompt == NULL)
469 prompt = GetPrompt();
470
Greg Clayton44d93782014-01-27 23:43:24 +0000471 if (prompt && prompt[0])
472 {
473 FILE *out = GetOutputFILE();
474 if (out)
475 {
476 ::fprintf(out, "%s", prompt);
477 ::fflush(out);
478 }
479 }
480 }
481 char buffer[256];
482 bool done = false;
Greg Clayton0f86e6e2014-02-04 19:25:11 +0000483 bool got_line = false;
Greg Claytone034a042015-05-21 20:52:06 +0000484 m_editing = true;
Greg Clayton44d93782014-01-27 23:43:24 +0000485 while (!done)
486 {
487 if (fgets(buffer, sizeof(buffer), in) == NULL)
Greg Claytonc9cf5792014-04-04 18:11:31 +0000488 {
Greg Claytonc7797ac2014-04-07 21:37:59 +0000489 const int saved_errno = errno;
Greg Claytonc9cf5792014-04-04 18:11:31 +0000490 if (feof(in))
491 done = true;
Greg Claytonc7797ac2014-04-07 21:37:59 +0000492 else if (ferror(in))
493 {
494 if (saved_errno != EINTR)
495 done = true;
496 }
Greg Claytonc9cf5792014-04-04 18:11:31 +0000497 }
Greg Clayton44d93782014-01-27 23:43:24 +0000498 else
499 {
Greg Clayton0f86e6e2014-02-04 19:25:11 +0000500 got_line = true;
Greg Clayton44d93782014-01-27 23:43:24 +0000501 size_t buffer_len = strlen(buffer);
502 assert (buffer[buffer_len] == '\0');
503 char last_char = buffer[buffer_len-1];
504 if (last_char == '\r' || last_char == '\n')
505 {
506 done = true;
507 // Strip trailing newlines
508 while (last_char == '\r' || last_char == '\n')
509 {
510 --buffer_len;
511 if (buffer_len == 0)
512 break;
513 last_char = buffer[buffer_len-1];
514 }
515 }
516 line.append(buffer, buffer_len);
517 }
518 }
Greg Claytone034a042015-05-21 20:52:06 +0000519 m_editing = false;
Greg Clayton0f86e6e2014-02-04 19:25:11 +0000520 // We might have gotten a newline on a line by itself
521 // make sure to return true in this case.
522 return got_line;
Greg Clayton44d93782014-01-27 23:43:24 +0000523 }
524 else
525 {
526 // No more input file, we are done...
527 SetIsDone(true);
528 }
Greg Clayton340b0302014-02-05 17:57:57 +0000529 return false;
Todd Fialacacde7d2014-09-27 16:54:22 +0000530#ifndef LLDB_DISABLE_LIBEDIT
Greg Clayton44d93782014-01-27 23:43:24 +0000531 }
Todd Fialacacde7d2014-09-27 16:54:22 +0000532#endif
Greg Clayton44d93782014-01-27 23:43:24 +0000533}
534
535
Todd Fialacacde7d2014-09-27 16:54:22 +0000536#ifndef LLDB_DISABLE_LIBEDIT
Kate Stonee30f11d2014-11-17 19:06:59 +0000537bool
538IOHandlerEditline::IsInputCompleteCallback (Editline *editline,
Greg Clayton44d93782014-01-27 23:43:24 +0000539 StringList &lines,
Greg Clayton44d93782014-01-27 23:43:24 +0000540 void *baton)
541{
542 IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton;
Kate Stonee30f11d2014-11-17 19:06:59 +0000543 return editline_reader->m_delegate.IOHandlerIsInputComplete(*editline_reader, lines);
544}
545
546int
547IOHandlerEditline::FixIndentationCallback (Editline *editline,
548 const StringList &lines,
549 int cursor_position,
550 void *baton)
551{
552 IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton;
553 return editline_reader->m_delegate.IOHandlerFixIndentation(*editline_reader, lines, cursor_position);
Greg Clayton44d93782014-01-27 23:43:24 +0000554}
555
556int
557IOHandlerEditline::AutoCompleteCallback (const char *current_line,
558 const char *cursor,
559 const char *last_char,
560 int skip_first_n_matches,
561 int max_matches,
562 StringList &matches,
563 void *baton)
564{
565 IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton;
566 if (editline_reader)
567 return editline_reader->m_delegate.IOHandlerComplete (*editline_reader,
568 current_line,
569 cursor,
570 last_char,
571 skip_first_n_matches,
572 max_matches,
573 matches);
574 return 0;
575}
Todd Fialacacde7d2014-09-27 16:54:22 +0000576#endif
Greg Clayton44d93782014-01-27 23:43:24 +0000577
578const char *
579IOHandlerEditline::GetPrompt ()
580{
Todd Fialacacde7d2014-09-27 16:54:22 +0000581#ifndef LLDB_DISABLE_LIBEDIT
Greg Clayton44d93782014-01-27 23:43:24 +0000582 if (m_editline_ap)
Todd Fialacacde7d2014-09-27 16:54:22 +0000583 {
Greg Clayton44d93782014-01-27 23:43:24 +0000584 return m_editline_ap->GetPrompt ();
Todd Fialacacde7d2014-09-27 16:54:22 +0000585 }
586 else
587 {
588#endif
589 if (m_prompt.empty())
590 return NULL;
591#ifndef LLDB_DISABLE_LIBEDIT
592 }
593#endif
Greg Clayton44d93782014-01-27 23:43:24 +0000594 return m_prompt.c_str();
595}
596
597bool
598IOHandlerEditline::SetPrompt (const char *p)
599{
600 if (p && p[0])
601 m_prompt = p;
602 else
603 m_prompt.clear();
Todd Fialacacde7d2014-09-27 16:54:22 +0000604#ifndef LLDB_DISABLE_LIBEDIT
Greg Clayton44d93782014-01-27 23:43:24 +0000605 if (m_editline_ap)
606 m_editline_ap->SetPrompt (m_prompt.empty() ? NULL : m_prompt.c_str());
Todd Fialacacde7d2014-09-27 16:54:22 +0000607#endif
Greg Clayton44d93782014-01-27 23:43:24 +0000608 return true;
609}
610
Kate Stonee30f11d2014-11-17 19:06:59 +0000611const char *
612IOHandlerEditline::GetContinuationPrompt ()
613{
614 if (m_continuation_prompt.empty())
615 return NULL;
616 return m_continuation_prompt.c_str();
617}
618
Kate Stonee30f11d2014-11-17 19:06:59 +0000619void
620IOHandlerEditline::SetContinuationPrompt (const char *p)
621{
622 if (p && p[0])
623 m_continuation_prompt = p;
624 else
625 m_continuation_prompt.clear();
Zachary Turnerd553d002014-11-17 21:31:18 +0000626
627#ifndef LLDB_DISABLE_LIBEDIT
Kate Stonee30f11d2014-11-17 19:06:59 +0000628 if (m_editline_ap)
629 m_editline_ap->SetContinuationPrompt (m_continuation_prompt.empty() ? NULL : m_continuation_prompt.c_str());
Zachary Turnerd553d002014-11-17 21:31:18 +0000630#endif
Kate Stonee30f11d2014-11-17 19:06:59 +0000631}
632
Greg Claytonf6913cd2014-03-07 00:53:24 +0000633void
634IOHandlerEditline::SetBaseLineNumber (uint32_t line)
635{
636 m_base_line_number = line;
Greg Claytonf6913cd2014-03-07 00:53:24 +0000637}
Kate Stonee30f11d2014-11-17 19:06:59 +0000638
639uint32_t
640IOHandlerEditline::GetCurrentLineIndex () const
641{
Zachary Turnerd553d002014-11-17 21:31:18 +0000642#ifndef LLDB_DISABLE_LIBEDIT
Kate Stonee30f11d2014-11-17 19:06:59 +0000643 if (m_editline_ap)
644 return m_editline_ap->GetCurrentLine();
645#endif
646 return m_curr_line_idx;
647}
648
Greg Clayton44d93782014-01-27 23:43:24 +0000649bool
Greg Claytonf0066ad2014-05-02 00:45:31 +0000650IOHandlerEditline::GetLines (StringList &lines, bool &interrupted)
Greg Clayton44d93782014-01-27 23:43:24 +0000651{
Kate Stonee30f11d2014-11-17 19:06:59 +0000652 m_current_lines_ptr = &lines;
653
Greg Clayton44d93782014-01-27 23:43:24 +0000654 bool success = false;
Todd Fialacacde7d2014-09-27 16:54:22 +0000655#ifndef LLDB_DISABLE_LIBEDIT
Greg Clayton44d93782014-01-27 23:43:24 +0000656 if (m_editline_ap)
657 {
Kate Stonee30f11d2014-11-17 19:06:59 +0000658 return m_editline_ap->GetLines (m_base_line_number, lines, interrupted);
Greg Clayton44d93782014-01-27 23:43:24 +0000659 }
660 else
661 {
Todd Fialacacde7d2014-09-27 16:54:22 +0000662#endif
Kate Stonee30f11d2014-11-17 19:06:59 +0000663 bool done = false;
Greg Claytonc3d874a2014-05-08 16:59:00 +0000664 Error error;
Greg Clayton44d93782014-01-27 23:43:24 +0000665
Kate Stonee30f11d2014-11-17 19:06:59 +0000666 while (!done)
Greg Clayton44d93782014-01-27 23:43:24 +0000667 {
Greg Claytonf6913cd2014-03-07 00:53:24 +0000668 // Show line numbers if we are asked to
Greg Clayton44d93782014-01-27 23:43:24 +0000669 std::string line;
Greg Claytonf6913cd2014-03-07 00:53:24 +0000670 if (m_base_line_number > 0 && GetIsInteractive())
671 {
672 FILE *out = GetOutputFILE();
673 if (out)
Greg Claytonbc88d932014-06-11 23:10:41 +0000674 ::fprintf(out, "%u%s", m_base_line_number + (uint32_t)lines.GetSize(), GetPrompt() == NULL ? " " : "");
Greg Claytonf6913cd2014-03-07 00:53:24 +0000675 }
676
Kate Stonee30f11d2014-11-17 19:06:59 +0000677 m_curr_line_idx = lines.GetSize();
678
Greg Claytonf0066ad2014-05-02 00:45:31 +0000679 bool interrupted = false;
Kate Stonee30f11d2014-11-17 19:06:59 +0000680 if (GetLine(line, interrupted) && !interrupted)
Greg Clayton44d93782014-01-27 23:43:24 +0000681 {
Kate Stonee30f11d2014-11-17 19:06:59 +0000682 lines.AppendString(line);
683 done = m_delegate.IOHandlerIsInputComplete(*this, lines);
Greg Clayton44d93782014-01-27 23:43:24 +0000684 }
685 else
686 {
Kate Stonee30f11d2014-11-17 19:06:59 +0000687 done = true;
Greg Clayton44d93782014-01-27 23:43:24 +0000688 }
689 }
690 success = lines.GetSize() > 0;
Todd Fialacacde7d2014-09-27 16:54:22 +0000691#ifndef LLDB_DISABLE_LIBEDIT
Greg Clayton44d93782014-01-27 23:43:24 +0000692 }
Todd Fialacacde7d2014-09-27 16:54:22 +0000693#endif
Greg Clayton44d93782014-01-27 23:43:24 +0000694 return success;
695}
696
697// Each IOHandler gets to run until it is done. It should read data
698// from the "in" and place output into "out" and "err and return
699// when done.
700void
701IOHandlerEditline::Run ()
702{
703 std::string line;
704 while (IsActive())
705 {
Greg Claytonf0066ad2014-05-02 00:45:31 +0000706 bool interrupted = false;
Greg Clayton44d93782014-01-27 23:43:24 +0000707 if (m_multi_line)
708 {
709 StringList lines;
Greg Claytonf0066ad2014-05-02 00:45:31 +0000710 if (GetLines (lines, interrupted))
Greg Clayton44d93782014-01-27 23:43:24 +0000711 {
Greg Claytonf0066ad2014-05-02 00:45:31 +0000712 if (interrupted)
713 {
Kate Stonee30f11d2014-11-17 19:06:59 +0000714 m_done = m_interrupt_exits;
715 m_delegate.IOHandlerInputInterrupted (*this, line);
716
Greg Claytonf0066ad2014-05-02 00:45:31 +0000717 }
718 else
719 {
720 line = lines.CopyList();
Kate Stonee30f11d2014-11-17 19:06:59 +0000721 m_delegate.IOHandlerInputComplete (*this, line);
Greg Claytonf0066ad2014-05-02 00:45:31 +0000722 }
Greg Clayton44d93782014-01-27 23:43:24 +0000723 }
724 else
725 {
726 m_done = true;
727 }
728 }
729 else
730 {
Greg Claytonf0066ad2014-05-02 00:45:31 +0000731 if (GetLine(line, interrupted))
Greg Clayton44d93782014-01-27 23:43:24 +0000732 {
Kate Stonee30f11d2014-11-17 19:06:59 +0000733 if (interrupted)
734 m_delegate.IOHandlerInputInterrupted (*this, line);
735 else
736 m_delegate.IOHandlerInputComplete (*this, line);
Greg Clayton44d93782014-01-27 23:43:24 +0000737 }
738 else
739 {
740 m_done = true;
741 }
742 }
743 }
744}
745
746void
Greg Claytone68f5d62014-02-24 22:50:57 +0000747IOHandlerEditline::Cancel ()
748{
Todd Fialacacde7d2014-09-27 16:54:22 +0000749#ifndef LLDB_DISABLE_LIBEDIT
Greg Claytone68f5d62014-02-24 22:50:57 +0000750 if (m_editline_ap)
Pavel Labath44464872015-05-27 12:40:32 +0000751 m_editline_ap->Cancel ();
Todd Fialacacde7d2014-09-27 16:54:22 +0000752#endif
Greg Claytone68f5d62014-02-24 22:50:57 +0000753}
754
Greg Claytonf0066ad2014-05-02 00:45:31 +0000755bool
Greg Clayton44d93782014-01-27 23:43:24 +0000756IOHandlerEditline::Interrupt ()
757{
Greg Claytonf0066ad2014-05-02 00:45:31 +0000758 // Let the delgate handle it first
759 if (m_delegate.IOHandlerInterrupt(*this))
760 return true;
761
Todd Fialacacde7d2014-09-27 16:54:22 +0000762#ifndef LLDB_DISABLE_LIBEDIT
Greg Clayton44d93782014-01-27 23:43:24 +0000763 if (m_editline_ap)
Greg Claytonf0066ad2014-05-02 00:45:31 +0000764 return m_editline_ap->Interrupt();
Todd Fialacacde7d2014-09-27 16:54:22 +0000765#endif
Greg Claytonf0066ad2014-05-02 00:45:31 +0000766 return false;
Greg Clayton44d93782014-01-27 23:43:24 +0000767}
768
769void
770IOHandlerEditline::GotEOF()
771{
Todd Fialacacde7d2014-09-27 16:54:22 +0000772#ifndef LLDB_DISABLE_LIBEDIT
Greg Clayton44d93782014-01-27 23:43:24 +0000773 if (m_editline_ap)
774 m_editline_ap->Interrupt();
Todd Fialacacde7d2014-09-27 16:54:22 +0000775#endif
Greg Clayton44d93782014-01-27 23:43:24 +0000776}
777
Pavel Labath44464872015-05-27 12:40:32 +0000778void
779IOHandlerEditline::PrintAsync (Stream *stream, const char *s, size_t len)
780{
781#ifndef LLDB_DISABLE_LIBEDIT
782 if (m_editline_ap)
783 m_editline_ap->PrintAsync(stream, s, len);
784 else
785#endif
786 IOHandler::PrintAsync(stream, s, len);
787}
788
Deepak Panickal914b8d92014-01-31 18:48:46 +0000789// we may want curses to be disabled for some builds
790// for instance, windows
791#ifndef LLDB_DISABLE_CURSES
792
Greg Clayton44d93782014-01-27 23:43:24 +0000793#include "lldb/Core/ValueObject.h"
794#include "lldb/Symbol/VariableList.h"
795#include "lldb/Target/Target.h"
796#include "lldb/Target/Process.h"
797#include "lldb/Target/Thread.h"
798#include "lldb/Target/StackFrame.h"
799
800#define KEY_RETURN 10
801#define KEY_ESCAPE 27
802
803namespace curses
804{
805 class Menu;
806 class MenuDelegate;
807 class Window;
808 class WindowDelegate;
809 typedef std::shared_ptr<Menu> MenuSP;
810 typedef std::shared_ptr<MenuDelegate> MenuDelegateSP;
811 typedef std::shared_ptr<Window> WindowSP;
812 typedef std::shared_ptr<WindowDelegate> WindowDelegateSP;
813 typedef std::vector<MenuSP> Menus;
814 typedef std::vector<WindowSP> Windows;
815 typedef std::vector<WindowDelegateSP> WindowDelegates;
816
817#if 0
818type summary add -s "x=${var.x}, y=${var.y}" curses::Point
819type summary add -s "w=${var.width}, h=${var.height}" curses::Size
820type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect
821#endif
Eugene Zelenko315b6882015-10-26 17:00:13 +0000822
Greg Clayton44d93782014-01-27 23:43:24 +0000823 struct Point
824 {
825 int x;
826 int y;
827
828 Point (int _x = 0, int _y = 0) :
829 x(_x),
830 y(_y)
831 {
832 }
833
834 void
835 Clear ()
836 {
837 x = 0;
838 y = 0;
839 }
840
841 Point &
842 operator += (const Point &rhs)
843 {
844 x += rhs.x;
845 y += rhs.y;
846 return *this;
847 }
848
849 void
850 Dump ()
851 {
852 printf ("(x=%i, y=%i)\n", x, y);
853 }
Greg Clayton44d93782014-01-27 23:43:24 +0000854 };
855
856 bool operator == (const Point &lhs, const Point &rhs)
857 {
858 return lhs.x == rhs.x && lhs.y == rhs.y;
859 }
Eugene Zelenko315b6882015-10-26 17:00:13 +0000860
Greg Clayton44d93782014-01-27 23:43:24 +0000861 bool operator != (const Point &lhs, const Point &rhs)
862 {
863 return lhs.x != rhs.x || lhs.y != rhs.y;
864 }
865
866 struct Size
867 {
868 int width;
869 int height;
870 Size (int w = 0, int h = 0) :
871 width (w),
872 height (h)
873 {
874 }
875
876 void
877 Clear ()
878 {
879 width = 0;
880 height = 0;
881 }
882
883 void
884 Dump ()
885 {
886 printf ("(w=%i, h=%i)\n", width, height);
887 }
Greg Clayton44d93782014-01-27 23:43:24 +0000888 };
889
890 bool operator == (const Size &lhs, const Size &rhs)
891 {
892 return lhs.width == rhs.width && lhs.height == rhs.height;
893 }
Eugene Zelenko315b6882015-10-26 17:00:13 +0000894
Greg Clayton44d93782014-01-27 23:43:24 +0000895 bool operator != (const Size &lhs, const Size &rhs)
896 {
897 return lhs.width != rhs.width || lhs.height != rhs.height;
898 }
899
900 struct Rect
901 {
902 Point origin;
903 Size size;
904
905 Rect () :
906 origin(),
907 size()
908 {
909 }
910
911 Rect (const Point &p, const Size &s) :
912 origin (p),
913 size (s)
914 {
915 }
916
917 void
918 Clear ()
919 {
920 origin.Clear();
921 size.Clear();
922 }
923
924 void
925 Dump ()
926 {
927 printf ("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width, size.height);
928 }
929
930 void
931 Inset (int w, int h)
932 {
933 if (size.width > w*2)
934 size.width -= w*2;
935 origin.x += w;
936
937 if (size.height > h*2)
938 size.height -= h*2;
939 origin.y += h;
940 }
Eugene Zelenko315b6882015-10-26 17:00:13 +0000941
Greg Clayton44d93782014-01-27 23:43:24 +0000942 // Return a status bar rectangle which is the last line of
943 // this rectangle. This rectangle will be modified to not
944 // include the status bar area.
945 Rect
946 MakeStatusBar ()
947 {
948 Rect status_bar;
949 if (size.height > 1)
950 {
951 status_bar.origin.x = origin.x;
952 status_bar.origin.y = size.height;
953 status_bar.size.width = size.width;
954 status_bar.size.height = 1;
955 --size.height;
956 }
957 return status_bar;
958 }
959
960 // Return a menubar rectangle which is the first line of
961 // this rectangle. This rectangle will be modified to not
962 // include the menubar area.
963 Rect
964 MakeMenuBar ()
965 {
966 Rect menubar;
967 if (size.height > 1)
968 {
969 menubar.origin.x = origin.x;
970 menubar.origin.y = origin.y;
971 menubar.size.width = size.width;
972 menubar.size.height = 1;
973 ++origin.y;
974 --size.height;
975 }
976 return menubar;
977 }
978
979 void
980 HorizontalSplitPercentage (float top_percentage, Rect &top, Rect &bottom) const
981 {
982 float top_height = top_percentage * size.height;
983 HorizontalSplit (top_height, top, bottom);
984 }
985
986 void
987 HorizontalSplit (int top_height, Rect &top, Rect &bottom) const
988 {
989 top = *this;
990 if (top_height < size.height)
991 {
992 top.size.height = top_height;
993 bottom.origin.x = origin.x;
994 bottom.origin.y = origin.y + top.size.height;
995 bottom.size.width = size.width;
996 bottom.size.height = size.height - top.size.height;
997 }
998 else
999 {
1000 bottom.Clear();
1001 }
1002 }
1003
1004 void
1005 VerticalSplitPercentage (float left_percentage, Rect &left, Rect &right) const
1006 {
1007 float left_width = left_percentage * size.width;
1008 VerticalSplit (left_width, left, right);
1009 }
1010
Greg Clayton44d93782014-01-27 23:43:24 +00001011 void
1012 VerticalSplit (int left_width, Rect &left, Rect &right) const
1013 {
1014 left = *this;
1015 if (left_width < size.width)
1016 {
1017 left.size.width = left_width;
1018 right.origin.x = origin.x + left.size.width;
1019 right.origin.y = origin.y;
1020 right.size.width = size.width - left.size.width;
1021 right.size.height = size.height;
1022 }
1023 else
1024 {
1025 right.Clear();
1026 }
1027 }
1028 };
1029
1030 bool operator == (const Rect &lhs, const Rect &rhs)
1031 {
1032 return lhs.origin == rhs.origin && lhs.size == rhs.size;
1033 }
Eugene Zelenko315b6882015-10-26 17:00:13 +00001034
Greg Clayton44d93782014-01-27 23:43:24 +00001035 bool operator != (const Rect &lhs, const Rect &rhs)
1036 {
1037 return lhs.origin != rhs.origin || lhs.size != rhs.size;
1038 }
1039
1040 enum HandleCharResult
1041 {
1042 eKeyNotHandled = 0,
1043 eKeyHandled = 1,
1044 eQuitApplication = 2
1045 };
1046
1047 enum class MenuActionResult
1048 {
1049 Handled,
1050 NotHandled,
1051 Quit // Exit all menus and quit
1052 };
1053
1054 struct KeyHelp
1055 {
1056 int ch;
1057 const char *description;
1058 };
1059
1060 class WindowDelegate
1061 {
1062 public:
1063 virtual
Eugene Zelenko315b6882015-10-26 17:00:13 +00001064 ~WindowDelegate() = default;
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00001065
1066 virtual bool
Greg Clayton44d93782014-01-27 23:43:24 +00001067 WindowDelegateDraw (Window &window, bool force)
1068 {
1069 return false; // Drawing not handled
1070 }
1071
1072 virtual HandleCharResult
1073 WindowDelegateHandleChar (Window &window, int key)
1074 {
1075 return eKeyNotHandled;
1076 }
1077
1078 virtual const char *
1079 WindowDelegateGetHelpText ()
1080 {
1081 return NULL;
1082 }
1083
1084 virtual KeyHelp *
1085 WindowDelegateGetKeyHelp ()
1086 {
1087 return NULL;
1088 }
1089 };
1090
1091 class HelpDialogDelegate :
1092 public WindowDelegate
1093 {
1094 public:
1095 HelpDialogDelegate (const char *text, KeyHelp *key_help_array);
Eugene Zelenko315b6882015-10-26 17:00:13 +00001096
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00001097 ~HelpDialogDelegate() override;
Eugene Zelenko315b6882015-10-26 17:00:13 +00001098
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00001099 bool
1100 WindowDelegateDraw (Window &window, bool force) override;
Greg Clayton44d93782014-01-27 23:43:24 +00001101
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00001102 HandleCharResult
1103 WindowDelegateHandleChar (Window &window, int key) override;
Greg Clayton44d93782014-01-27 23:43:24 +00001104
1105 size_t
1106 GetNumLines() const
1107 {
1108 return m_text.GetSize();
1109 }
1110
1111 size_t
1112 GetMaxLineLength () const
1113 {
1114 return m_text.GetMaxStringLength();
1115 }
1116
1117 protected:
1118 StringList m_text;
1119 int m_first_visible_line;
1120 };
1121
Greg Clayton44d93782014-01-27 23:43:24 +00001122 class Window
1123 {
1124 public:
Greg Clayton44d93782014-01-27 23:43:24 +00001125 Window (const char *name) :
1126 m_name (name),
1127 m_window (NULL),
1128 m_panel (NULL),
1129 m_parent (NULL),
1130 m_subwindows (),
1131 m_delegate_sp (),
1132 m_curr_active_window_idx (UINT32_MAX),
1133 m_prev_active_window_idx (UINT32_MAX),
1134 m_delete (false),
1135 m_needs_update (true),
1136 m_can_activate (true),
1137 m_is_subwin (false)
1138 {
1139 }
1140
1141 Window (const char *name, WINDOW *w, bool del = true) :
1142 m_name (name),
1143 m_window (NULL),
1144 m_panel (NULL),
1145 m_parent (NULL),
1146 m_subwindows (),
1147 m_delegate_sp (),
1148 m_curr_active_window_idx (UINT32_MAX),
1149 m_prev_active_window_idx (UINT32_MAX),
1150 m_delete (del),
1151 m_needs_update (true),
1152 m_can_activate (true),
1153 m_is_subwin (false)
1154 {
1155 if (w)
1156 Reset(w);
1157 }
1158
1159 Window (const char *name, const Rect &bounds) :
1160 m_name (name),
1161 m_window (NULL),
1162 m_parent (NULL),
1163 m_subwindows (),
1164 m_delegate_sp (),
1165 m_curr_active_window_idx (UINT32_MAX),
1166 m_prev_active_window_idx (UINT32_MAX),
1167 m_delete (true),
1168 m_needs_update (true),
1169 m_can_activate (true),
1170 m_is_subwin (false)
1171 {
1172 Reset (::newwin (bounds.size.height, bounds.size.width, bounds.origin.y, bounds.origin.y));
1173 }
1174
1175 virtual
1176 ~Window ()
1177 {
1178 RemoveSubWindows ();
1179 Reset ();
1180 }
1181
1182 void
1183 Reset (WINDOW *w = NULL, bool del = true)
1184 {
1185 if (m_window == w)
1186 return;
1187
1188 if (m_panel)
1189 {
1190 ::del_panel (m_panel);
1191 m_panel = NULL;
1192 }
1193 if (m_window && m_delete)
1194 {
1195 ::delwin (m_window);
1196 m_window = NULL;
1197 m_delete = false;
1198 }
1199 if (w)
1200 {
1201 m_window = w;
1202 m_panel = ::new_panel (m_window);
1203 m_delete = del;
1204 }
1205 }
1206
1207 void AttributeOn (attr_t attr) { ::wattron (m_window, attr); }
1208 void AttributeOff (attr_t attr) { ::wattroff (m_window, attr); }
1209 void Box (chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) { ::box(m_window, v_char, h_char); }
1210 void Clear () { ::wclear (m_window); }
1211 void Erase () { ::werase (m_window); }
1212 Rect GetBounds () { return Rect (GetParentOrigin(), GetSize()); } // Get the rectangle in our parent window
1213 int GetChar () { return ::wgetch (m_window); }
1214 int GetCursorX () { return getcurx (m_window); }
1215 int GetCursorY () { return getcury (m_window); }
1216 Rect GetFrame () { return Rect (Point(), GetSize()); } // Get our rectangle in our own coordinate system
1217 Point GetParentOrigin() { return Point (GetParentX(), GetParentY()); }
1218 Size GetSize() { return Size (GetWidth(), GetHeight()); }
1219 int GetParentX () { return getparx (m_window); }
1220 int GetParentY () { return getpary (m_window); }
1221 int GetMaxX() { return getmaxx (m_window); }
1222 int GetMaxY() { return getmaxy (m_window); }
1223 int GetWidth() { return GetMaxX(); }
1224 int GetHeight() { return GetMaxY(); }
1225 void MoveCursor (int x, int y) { ::wmove (m_window, y, x); }
1226 void MoveWindow (int x, int y) { MoveWindow(Point(x,y)); }
1227 void Resize (int w, int h) { ::wresize(m_window, h, w); }
1228 void Resize (const Size &size) { ::wresize(m_window, size.height, size.width); }
1229 void PutChar (int ch) { ::waddch (m_window, ch); }
1230 void PutCString (const char *s, int len = -1) { ::waddnstr (m_window, s, len); }
1231 void Refresh () { ::wrefresh (m_window); }
1232 void DeferredRefresh ()
1233 {
1234 // We are using panels, so we don't need to call this...
1235 //::wnoutrefresh(m_window);
1236 }
1237 void SetBackground (int color_pair_idx) { ::wbkgd (m_window,COLOR_PAIR(color_pair_idx)); }
1238 void UnderlineOn () { AttributeOn(A_UNDERLINE); }
1239 void UnderlineOff () { AttributeOff(A_UNDERLINE); }
1240
1241 void PutCStringTruncated (const char *s, int right_pad)
1242 {
1243 int bytes_left = GetWidth() - GetCursorX();
1244 if (bytes_left > right_pad)
1245 {
1246 bytes_left -= right_pad;
1247 ::waddnstr (m_window, s, bytes_left);
1248 }
1249 }
1250
1251 void
1252 MoveWindow (const Point &origin)
1253 {
1254 const bool moving_window = origin != GetParentOrigin();
1255 if (m_is_subwin && moving_window)
1256 {
1257 // Can't move subwindows, must delete and re-create
1258 Size size = GetSize();
1259 Reset (::subwin (m_parent->m_window,
1260 size.height,
1261 size.width,
1262 origin.y,
1263 origin.x), true);
1264 }
1265 else
1266 {
1267 ::mvwin (m_window, origin.y, origin.x);
1268 }
1269 }
1270
1271 void
1272 SetBounds (const Rect &bounds)
1273 {
1274 const bool moving_window = bounds.origin != GetParentOrigin();
1275 if (m_is_subwin && moving_window)
1276 {
1277 // Can't move subwindows, must delete and re-create
1278 Reset (::subwin (m_parent->m_window,
1279 bounds.size.height,
1280 bounds.size.width,
1281 bounds.origin.y,
1282 bounds.origin.x), true);
1283 }
1284 else
1285 {
1286 if (moving_window)
1287 MoveWindow(bounds.origin);
1288 Resize (bounds.size);
1289 }
1290 }
1291
1292 void
1293 Printf (const char *format, ...) __attribute__ ((format (printf, 2, 3)))
1294 {
1295 va_list args;
1296 va_start (args, format);
1297 vwprintw(m_window, format, args);
1298 va_end (args);
1299 }
1300
1301 void
1302 Touch ()
1303 {
1304 ::touchwin (m_window);
1305 if (m_parent)
1306 m_parent->Touch();
1307 }
1308
1309 WindowSP
1310 CreateSubWindow (const char *name, const Rect &bounds, bool make_active)
1311 {
1312 WindowSP subwindow_sp;
1313 if (m_window)
1314 {
1315 subwindow_sp.reset(new Window(name, ::subwin (m_window,
1316 bounds.size.height,
1317 bounds.size.width,
1318 bounds.origin.y,
1319 bounds.origin.x), true));
1320 subwindow_sp->m_is_subwin = true;
1321 }
1322 else
1323 {
1324 subwindow_sp.reset(new Window(name, ::newwin (bounds.size.height,
1325 bounds.size.width,
1326 bounds.origin.y,
1327 bounds.origin.x), true));
1328 subwindow_sp->m_is_subwin = false;
1329 }
1330 subwindow_sp->m_parent = this;
1331 if (make_active)
1332 {
1333 m_prev_active_window_idx = m_curr_active_window_idx;
1334 m_curr_active_window_idx = m_subwindows.size();
1335 }
1336 m_subwindows.push_back(subwindow_sp);
1337 ::top_panel (subwindow_sp->m_panel);
1338 m_needs_update = true;
1339 return subwindow_sp;
1340 }
1341
1342 bool
1343 RemoveSubWindow (Window *window)
1344 {
1345 Windows::iterator pos, end = m_subwindows.end();
1346 size_t i = 0;
1347 for (pos = m_subwindows.begin(); pos != end; ++pos, ++i)
1348 {
1349 if ((*pos).get() == window)
1350 {
1351 if (m_prev_active_window_idx == i)
1352 m_prev_active_window_idx = UINT32_MAX;
1353 else if (m_prev_active_window_idx != UINT32_MAX && m_prev_active_window_idx > i)
1354 --m_prev_active_window_idx;
1355
1356 if (m_curr_active_window_idx == i)
1357 m_curr_active_window_idx = UINT32_MAX;
1358 else if (m_curr_active_window_idx != UINT32_MAX && m_curr_active_window_idx > i)
1359 --m_curr_active_window_idx;
1360 window->Erase();
1361 m_subwindows.erase(pos);
1362 m_needs_update = true;
1363 if (m_parent)
1364 m_parent->Touch();
1365 else
1366 ::touchwin (stdscr);
1367 return true;
1368 }
1369 }
1370 return false;
1371 }
1372
1373 WindowSP
1374 FindSubWindow (const char *name)
1375 {
1376 Windows::iterator pos, end = m_subwindows.end();
1377 size_t i = 0;
1378 for (pos = m_subwindows.begin(); pos != end; ++pos, ++i)
1379 {
1380 if ((*pos)->m_name.compare(name) == 0)
1381 return *pos;
1382 }
1383 return WindowSP();
1384 }
1385
1386 void
1387 RemoveSubWindows ()
1388 {
1389 m_curr_active_window_idx = UINT32_MAX;
1390 m_prev_active_window_idx = UINT32_MAX;
1391 for (Windows::iterator pos = m_subwindows.begin();
1392 pos != m_subwindows.end();
1393 pos = m_subwindows.erase(pos))
1394 {
1395 (*pos)->Erase();
1396 }
1397 if (m_parent)
1398 m_parent->Touch();
1399 else
1400 ::touchwin (stdscr);
1401 }
1402
1403 WINDOW *
1404 get()
1405 {
1406 return m_window;
1407 }
1408
1409 operator WINDOW *()
1410 {
1411 return m_window;
1412 }
1413
1414 //----------------------------------------------------------------------
1415 // Window drawing utilities
1416 //----------------------------------------------------------------------
1417 void
1418 DrawTitleBox (const char *title, const char *bottom_message = NULL)
1419 {
1420 attr_t attr = 0;
1421 if (IsActive())
1422 attr = A_BOLD | COLOR_PAIR(2);
1423 else
1424 attr = 0;
1425 if (attr)
1426 AttributeOn(attr);
1427
1428 Box();
1429 MoveCursor(3, 0);
1430
1431 if (title && title[0])
1432 {
1433 PutChar ('<');
1434 PutCString (title);
1435 PutChar ('>');
1436 }
1437
1438 if (bottom_message && bottom_message[0])
1439 {
1440 int bottom_message_length = strlen(bottom_message);
1441 int x = GetWidth() - 3 - (bottom_message_length + 2);
1442
1443 if (x > 0)
1444 {
1445 MoveCursor (x, GetHeight() - 1);
1446 PutChar ('[');
1447 PutCString(bottom_message);
1448 PutChar (']');
1449 }
1450 else
1451 {
1452 MoveCursor (1, GetHeight() - 1);
1453 PutChar ('[');
1454 PutCStringTruncated (bottom_message, 1);
1455 }
1456 }
1457 if (attr)
1458 AttributeOff(attr);
1459
1460 }
1461
1462 virtual void
1463 Draw (bool force)
1464 {
1465 if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw (*this, force))
1466 return;
1467
1468 for (auto &subwindow_sp : m_subwindows)
1469 subwindow_sp->Draw(force);
1470 }
1471
1472 bool
1473 CreateHelpSubwindow ()
1474 {
1475 if (m_delegate_sp)
1476 {
1477 const char *text = m_delegate_sp->WindowDelegateGetHelpText ();
1478 KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp ();
1479 if ((text && text[0]) || key_help)
1480 {
1481 std::auto_ptr<HelpDialogDelegate> help_delegate_ap(new HelpDialogDelegate(text, key_help));
1482 const size_t num_lines = help_delegate_ap->GetNumLines();
1483 const size_t max_length = help_delegate_ap->GetMaxLineLength();
1484 Rect bounds = GetBounds();
1485 bounds.Inset(1, 1);
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00001486 if (max_length + 4 < static_cast<size_t>(bounds.size.width))
Greg Clayton44d93782014-01-27 23:43:24 +00001487 {
1488 bounds.origin.x += (bounds.size.width - max_length + 4)/2;
1489 bounds.size.width = max_length + 4;
1490 }
1491 else
1492 {
1493 if (bounds.size.width > 100)
1494 {
1495 const int inset_w = bounds.size.width / 4;
1496 bounds.origin.x += inset_w;
1497 bounds.size.width -= 2*inset_w;
1498 }
1499 }
1500
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00001501 if (num_lines + 2 < static_cast<size_t>(bounds.size.height))
Greg Clayton44d93782014-01-27 23:43:24 +00001502 {
1503 bounds.origin.y += (bounds.size.height - num_lines + 2)/2;
1504 bounds.size.height = num_lines + 2;
1505 }
1506 else
1507 {
1508 if (bounds.size.height > 100)
1509 {
1510 const int inset_h = bounds.size.height / 4;
1511 bounds.origin.y += inset_h;
1512 bounds.size.height -= 2*inset_h;
1513 }
1514 }
Greg Clayton5fdb09b2014-01-28 18:41:35 +00001515 WindowSP help_window_sp;
1516 Window *parent_window = GetParent();
1517 if (parent_window)
1518 help_window_sp = parent_window->CreateSubWindow("Help", bounds, true);
1519 else
1520 help_window_sp = CreateSubWindow("Help", bounds, true);
Greg Clayton44d93782014-01-27 23:43:24 +00001521 help_window_sp->SetDelegate(WindowDelegateSP(help_delegate_ap.release()));
1522 return true;
1523 }
1524 }
1525 return false;
1526 }
1527
1528 virtual HandleCharResult
1529 HandleChar (int key)
1530 {
1531 // Always check the active window first
1532 HandleCharResult result = eKeyNotHandled;
1533 WindowSP active_window_sp = GetActiveWindow ();
1534 if (active_window_sp)
1535 {
1536 result = active_window_sp->HandleChar (key);
1537 if (result != eKeyNotHandled)
1538 return result;
1539 }
1540
1541 if (m_delegate_sp)
1542 {
1543 result = m_delegate_sp->WindowDelegateHandleChar (*this, key);
1544 if (result != eKeyNotHandled)
1545 return result;
1546 }
1547
1548 // Then check for any windows that want any keys
1549 // that weren't handled. This is typically only
1550 // for a menubar.
1551 // Make a copy of the subwindows in case any HandleChar()
1552 // functions muck with the subwindows. If we don't do this,
1553 // we can crash when iterating over the subwindows.
1554 Windows subwindows (m_subwindows);
1555 for (auto subwindow_sp : subwindows)
1556 {
1557 if (subwindow_sp->m_can_activate == false)
1558 {
1559 HandleCharResult result = subwindow_sp->HandleChar(key);
1560 if (result != eKeyNotHandled)
1561 return result;
1562 }
1563 }
1564
1565 return eKeyNotHandled;
1566 }
1567
1568 bool
1569 SetActiveWindow (Window *window)
1570 {
1571 const size_t num_subwindows = m_subwindows.size();
1572 for (size_t i=0; i<num_subwindows; ++i)
1573 {
1574 if (m_subwindows[i].get() == window)
1575 {
1576 m_prev_active_window_idx = m_curr_active_window_idx;
1577 ::top_panel (window->m_panel);
1578 m_curr_active_window_idx = i;
1579 return true;
1580 }
1581 }
1582 return false;
1583 }
1584
1585 WindowSP
1586 GetActiveWindow ()
1587 {
1588 if (!m_subwindows.empty())
1589 {
1590 if (m_curr_active_window_idx >= m_subwindows.size())
1591 {
1592 if (m_prev_active_window_idx < m_subwindows.size())
1593 {
1594 m_curr_active_window_idx = m_prev_active_window_idx;
1595 m_prev_active_window_idx = UINT32_MAX;
1596 }
1597 else if (IsActive())
1598 {
1599 m_prev_active_window_idx = UINT32_MAX;
1600 m_curr_active_window_idx = UINT32_MAX;
1601
1602 // Find first window that wants to be active if this window is active
1603 const size_t num_subwindows = m_subwindows.size();
1604 for (size_t i=0; i<num_subwindows; ++i)
1605 {
1606 if (m_subwindows[i]->GetCanBeActive())
1607 {
1608 m_curr_active_window_idx = i;
1609 break;
1610 }
1611 }
1612 }
1613 }
1614
1615 if (m_curr_active_window_idx < m_subwindows.size())
1616 return m_subwindows[m_curr_active_window_idx];
1617 }
1618 return WindowSP();
1619 }
1620
1621 bool
1622 GetCanBeActive () const
1623 {
1624 return m_can_activate;
1625 }
1626
1627 void
1628 SetCanBeActive (bool b)
1629 {
1630 m_can_activate = b;
1631 }
1632
1633 const WindowDelegateSP &
1634 GetDelegate () const
1635 {
1636 return m_delegate_sp;
1637 }
1638
1639 void
1640 SetDelegate (const WindowDelegateSP &delegate_sp)
1641 {
1642 m_delegate_sp = delegate_sp;
1643 }
1644
1645 Window *
1646 GetParent () const
1647 {
1648 return m_parent;
1649 }
1650
1651 bool
1652 IsActive () const
1653 {
1654 if (m_parent)
1655 return m_parent->GetActiveWindow().get() == this;
1656 else
1657 return true; // Top level window is always active
1658 }
1659
1660 void
1661 SelectNextWindowAsActive ()
1662 {
1663 // Move active focus to next window
1664 const size_t num_subwindows = m_subwindows.size();
1665 if (m_curr_active_window_idx == UINT32_MAX)
1666 {
1667 uint32_t idx = 0;
1668 for (auto subwindow_sp : m_subwindows)
1669 {
1670 if (subwindow_sp->GetCanBeActive())
1671 {
1672 m_curr_active_window_idx = idx;
1673 break;
1674 }
1675 ++idx;
1676 }
1677 }
1678 else if (m_curr_active_window_idx + 1 < num_subwindows)
1679 {
1680 bool handled = false;
1681 m_prev_active_window_idx = m_curr_active_window_idx;
1682 for (size_t idx=m_curr_active_window_idx + 1; idx<num_subwindows; ++idx)
1683 {
1684 if (m_subwindows[idx]->GetCanBeActive())
1685 {
1686 m_curr_active_window_idx = idx;
1687 handled = true;
1688 break;
1689 }
1690 }
1691 if (!handled)
1692 {
1693 for (size_t idx=0; idx<=m_prev_active_window_idx; ++idx)
1694 {
1695 if (m_subwindows[idx]->GetCanBeActive())
1696 {
1697 m_curr_active_window_idx = idx;
1698 break;
1699 }
1700 }
1701 }
1702 }
1703 else
1704 {
1705 m_prev_active_window_idx = m_curr_active_window_idx;
1706 for (size_t idx=0; idx<num_subwindows; ++idx)
1707 {
1708 if (m_subwindows[idx]->GetCanBeActive())
1709 {
1710 m_curr_active_window_idx = idx;
1711 break;
1712 }
1713 }
1714 }
1715 }
1716
1717 const char *
1718 GetName () const
1719 {
1720 return m_name.c_str();
1721 }
Eugene Zelenko315b6882015-10-26 17:00:13 +00001722
Greg Clayton44d93782014-01-27 23:43:24 +00001723 protected:
1724 std::string m_name;
1725 WINDOW *m_window;
1726 PANEL *m_panel;
1727 Window *m_parent;
1728 Windows m_subwindows;
1729 WindowDelegateSP m_delegate_sp;
1730 uint32_t m_curr_active_window_idx;
1731 uint32_t m_prev_active_window_idx;
1732 bool m_delete;
1733 bool m_needs_update;
1734 bool m_can_activate;
1735 bool m_is_subwin;
1736
1737 private:
1738 DISALLOW_COPY_AND_ASSIGN(Window);
1739 };
1740
1741 class MenuDelegate
1742 {
1743 public:
Eugene Zelenko315b6882015-10-26 17:00:13 +00001744 virtual ~MenuDelegate() = default;
1745
Greg Clayton44d93782014-01-27 23:43:24 +00001746 virtual MenuActionResult
1747 MenuDelegateAction (Menu &menu) = 0;
1748 };
1749
1750 class Menu : public WindowDelegate
1751 {
1752 public:
1753 enum class Type
1754 {
1755 Invalid,
1756 Bar,
1757 Item,
1758 Separator
1759 };
1760
1761 // Menubar or separator constructor
1762 Menu (Type type);
1763
1764 // Menuitem constructor
1765 Menu (const char *name,
1766 const char *key_name,
1767 int key_value,
1768 uint64_t identifier);
Eugene Zelenko315b6882015-10-26 17:00:13 +00001769
1770 ~Menu() override = default;
Greg Clayton44d93782014-01-27 23:43:24 +00001771
1772 const MenuDelegateSP &
1773 GetDelegate () const
1774 {
1775 return m_delegate_sp;
1776 }
1777
1778 void
1779 SetDelegate (const MenuDelegateSP &delegate_sp)
1780 {
1781 m_delegate_sp = delegate_sp;
1782 }
1783
1784 void
1785 RecalculateNameLengths();
1786
1787 void
1788 AddSubmenu (const MenuSP &menu_sp);
1789
1790 int
1791 DrawAndRunMenu (Window &window);
1792
1793 void
1794 DrawMenuTitle (Window &window, bool highlight);
1795
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00001796 bool
1797 WindowDelegateDraw (Window &window, bool force) override;
Greg Clayton44d93782014-01-27 23:43:24 +00001798
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00001799 HandleCharResult
1800 WindowDelegateHandleChar (Window &window, int key) override;
Greg Clayton44d93782014-01-27 23:43:24 +00001801
1802 MenuActionResult
1803 ActionPrivate (Menu &menu)
1804 {
1805 MenuActionResult result = MenuActionResult::NotHandled;
1806 if (m_delegate_sp)
1807 {
1808 result = m_delegate_sp->MenuDelegateAction (menu);
1809 if (result != MenuActionResult::NotHandled)
1810 return result;
1811 }
1812 else if (m_parent)
1813 {
1814 result = m_parent->ActionPrivate(menu);
1815 if (result != MenuActionResult::NotHandled)
1816 return result;
1817 }
1818 return m_canned_result;
1819 }
1820
1821 MenuActionResult
1822 Action ()
1823 {
1824 // Call the recursive action so it can try to handle it
1825 // with the menu delegate, and if not, try our parent menu
1826 return ActionPrivate (*this);
1827 }
1828
1829 void
1830 SetCannedResult (MenuActionResult result)
1831 {
1832 m_canned_result = result;
1833 }
1834
1835 Menus &
1836 GetSubmenus()
1837 {
1838 return m_submenus;
1839 }
1840
1841 const Menus &
1842 GetSubmenus() const
1843 {
1844 return m_submenus;
1845 }
1846
1847 int
1848 GetSelectedSubmenuIndex () const
1849 {
1850 return m_selected;
1851 }
1852
1853 void
1854 SetSelectedSubmenuIndex (int idx)
1855 {
1856 m_selected = idx;
1857 }
1858
1859 Type
1860 GetType () const
1861 {
1862 return m_type;
1863 }
1864
1865 int
1866 GetStartingColumn() const
1867 {
1868 return m_start_col;
1869 }
1870
1871 void
1872 SetStartingColumn(int col)
1873 {
1874 m_start_col = col;
1875 }
1876
1877 int
1878 GetKeyValue() const
1879 {
1880 return m_key_value;
1881 }
1882
1883 void
1884 SetKeyValue(int key_value)
1885 {
1886 m_key_value = key_value;
1887 }
1888
1889 std::string &
1890 GetName()
1891 {
1892 return m_name;
1893 }
1894
1895 std::string &
1896 GetKeyName()
1897 {
1898 return m_key_name;
1899 }
1900
1901 int
1902 GetDrawWidth () const
1903 {
1904 return m_max_submenu_name_length + m_max_submenu_key_name_length + 8;
1905 }
1906
Greg Clayton44d93782014-01-27 23:43:24 +00001907 uint64_t
1908 GetIdentifier() const
1909 {
1910 return m_identifier;
1911 }
1912
1913 void
1914 SetIdentifier (uint64_t identifier)
1915 {
1916 m_identifier = identifier;
1917 }
1918
1919 protected:
1920 std::string m_name;
1921 std::string m_key_name;
1922 uint64_t m_identifier;
1923 Type m_type;
1924 int m_key_value;
1925 int m_start_col;
1926 int m_max_submenu_name_length;
1927 int m_max_submenu_key_name_length;
1928 int m_selected;
1929 Menu *m_parent;
1930 Menus m_submenus;
1931 WindowSP m_menu_window_sp;
1932 MenuActionResult m_canned_result;
1933 MenuDelegateSP m_delegate_sp;
1934 };
1935
1936 // Menubar or separator constructor
1937 Menu::Menu (Type type) :
1938 m_name (),
1939 m_key_name (),
1940 m_identifier (0),
1941 m_type (type),
1942 m_key_value (0),
1943 m_start_col (0),
1944 m_max_submenu_name_length (0),
1945 m_max_submenu_key_name_length (0),
1946 m_selected (0),
1947 m_parent (NULL),
1948 m_submenus (),
1949 m_canned_result (MenuActionResult::NotHandled),
1950 m_delegate_sp()
1951 {
1952 }
1953
1954 // Menuitem constructor
1955 Menu::Menu (const char *name,
1956 const char *key_name,
1957 int key_value,
1958 uint64_t identifier) :
1959 m_name (),
1960 m_key_name (),
1961 m_identifier (identifier),
1962 m_type (Type::Invalid),
1963 m_key_value (key_value),
1964 m_start_col (0),
1965 m_max_submenu_name_length (0),
1966 m_max_submenu_key_name_length (0),
1967 m_selected (0),
1968 m_parent (NULL),
1969 m_submenus (),
1970 m_canned_result (MenuActionResult::NotHandled),
1971 m_delegate_sp()
1972 {
1973 if (name && name[0])
1974 {
1975 m_name = name;
1976 m_type = Type::Item;
1977 if (key_name && key_name[0])
1978 m_key_name = key_name;
1979 }
1980 else
1981 {
1982 m_type = Type::Separator;
1983 }
1984 }
1985
1986 void
1987 Menu::RecalculateNameLengths()
1988 {
1989 m_max_submenu_name_length = 0;
1990 m_max_submenu_key_name_length = 0;
1991 Menus &submenus = GetSubmenus();
1992 const size_t num_submenus = submenus.size();
1993 for (size_t i=0; i<num_submenus; ++i)
1994 {
1995 Menu *submenu = submenus[i].get();
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00001996 if (static_cast<size_t>(m_max_submenu_name_length) < submenu->m_name.size())
Greg Clayton44d93782014-01-27 23:43:24 +00001997 m_max_submenu_name_length = submenu->m_name.size();
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00001998 if (static_cast<size_t>(m_max_submenu_key_name_length) < submenu->m_key_name.size())
Greg Clayton44d93782014-01-27 23:43:24 +00001999 m_max_submenu_key_name_length = submenu->m_key_name.size();
2000 }
2001 }
2002
2003 void
2004 Menu::AddSubmenu (const MenuSP &menu_sp)
2005 {
2006 menu_sp->m_parent = this;
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00002007 if (static_cast<size_t>(m_max_submenu_name_length) < menu_sp->m_name.size())
Greg Clayton44d93782014-01-27 23:43:24 +00002008 m_max_submenu_name_length = menu_sp->m_name.size();
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00002009 if (static_cast<size_t>(m_max_submenu_key_name_length) < menu_sp->m_key_name.size())
Greg Clayton44d93782014-01-27 23:43:24 +00002010 m_max_submenu_key_name_length = menu_sp->m_key_name.size();
2011 m_submenus.push_back(menu_sp);
2012 }
2013
2014 void
2015 Menu::DrawMenuTitle (Window &window, bool highlight)
2016 {
2017 if (m_type == Type::Separator)
2018 {
2019 window.MoveCursor(0, window.GetCursorY());
2020 window.PutChar(ACS_LTEE);
2021 int width = window.GetWidth();
2022 if (width > 2)
2023 {
2024 width -= 2;
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00002025 for (int i=0; i< width; ++i)
Greg Clayton44d93782014-01-27 23:43:24 +00002026 window.PutChar(ACS_HLINE);
2027 }
2028 window.PutChar(ACS_RTEE);
2029 }
2030 else
2031 {
2032 const int shortcut_key = m_key_value;
2033 bool underlined_shortcut = false;
2034 const attr_t hilgight_attr = A_REVERSE;
2035 if (highlight)
2036 window.AttributeOn(hilgight_attr);
2037 if (isprint(shortcut_key))
2038 {
2039 size_t lower_pos = m_name.find(tolower(shortcut_key));
2040 size_t upper_pos = m_name.find(toupper(shortcut_key));
2041 const char *name = m_name.c_str();
2042 size_t pos = std::min<size_t>(lower_pos, upper_pos);
2043 if (pos != std::string::npos)
2044 {
2045 underlined_shortcut = true;
2046 if (pos > 0)
2047 {
2048 window.PutCString(name, pos);
2049 name += pos;
2050 }
2051 const attr_t shortcut_attr = A_UNDERLINE|A_BOLD;
2052 window.AttributeOn (shortcut_attr);
2053 window.PutChar(name[0]);
2054 window.AttributeOff(shortcut_attr);
2055 name++;
2056 if (name[0])
2057 window.PutCString(name);
2058 }
2059 }
2060
2061 if (!underlined_shortcut)
2062 {
2063 window.PutCString(m_name.c_str());
2064 }
2065
2066 if (highlight)
2067 window.AttributeOff(hilgight_attr);
2068
2069 if (m_key_name.empty())
2070 {
2071 if (!underlined_shortcut && isprint(m_key_value))
2072 {
2073 window.AttributeOn (COLOR_PAIR(3));
2074 window.Printf (" (%c)", m_key_value);
2075 window.AttributeOff (COLOR_PAIR(3));
2076 }
2077 }
2078 else
2079 {
2080 window.AttributeOn (COLOR_PAIR(3));
2081 window.Printf (" (%s)", m_key_name.c_str());
2082 window.AttributeOff (COLOR_PAIR(3));
2083 }
2084 }
2085 }
2086
2087 bool
2088 Menu::WindowDelegateDraw (Window &window, bool force)
2089 {
2090 Menus &submenus = GetSubmenus();
2091 const size_t num_submenus = submenus.size();
2092 const int selected_idx = GetSelectedSubmenuIndex();
2093 Menu::Type menu_type = GetType ();
2094 switch (menu_type)
2095 {
2096 case Menu::Type::Bar:
2097 {
2098 window.SetBackground(2);
2099 window.MoveCursor(0, 0);
2100 for (size_t i=0; i<num_submenus; ++i)
2101 {
2102 Menu *menu = submenus[i].get();
2103 if (i > 0)
2104 window.PutChar(' ');
2105 menu->SetStartingColumn (window.GetCursorX());
2106 window.PutCString("| ");
2107 menu->DrawMenuTitle (window, false);
2108 }
2109 window.PutCString(" |");
2110 window.DeferredRefresh();
2111 }
2112 break;
2113
2114 case Menu::Type::Item:
2115 {
2116 int y = 1;
2117 int x = 3;
2118 // Draw the menu
2119 int cursor_x = 0;
2120 int cursor_y = 0;
2121 window.Erase();
2122 window.SetBackground(2);
2123 window.Box();
2124 for (size_t i=0; i<num_submenus; ++i)
2125 {
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00002126 const bool is_selected =
2127 (i == static_cast<size_t>(selected_idx));
Greg Clayton44d93782014-01-27 23:43:24 +00002128 window.MoveCursor(x, y + i);
2129 if (is_selected)
2130 {
2131 // Remember where we want the cursor to be
2132 cursor_x = x-1;
2133 cursor_y = y+i;
2134 }
2135 submenus[i]->DrawMenuTitle (window, is_selected);
2136 }
2137 window.MoveCursor(cursor_x, cursor_y);
2138 window.DeferredRefresh();
2139 }
2140 break;
2141
2142 default:
2143 case Menu::Type::Separator:
2144 break;
2145 }
2146 return true; // Drawing handled...
2147 }
2148
2149 HandleCharResult
2150 Menu::WindowDelegateHandleChar (Window &window, int key)
2151 {
2152 HandleCharResult result = eKeyNotHandled;
2153
2154 Menus &submenus = GetSubmenus();
2155 const size_t num_submenus = submenus.size();
2156 const int selected_idx = GetSelectedSubmenuIndex();
2157 Menu::Type menu_type = GetType ();
2158 if (menu_type == Menu::Type::Bar)
2159 {
2160 MenuSP run_menu_sp;
2161 switch (key)
2162 {
2163 case KEY_DOWN:
2164 case KEY_UP:
2165 // Show last menu or first menu
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00002166 if (selected_idx < static_cast<int>(num_submenus))
Greg Clayton44d93782014-01-27 23:43:24 +00002167 run_menu_sp = submenus[selected_idx];
2168 else if (!submenus.empty())
2169 run_menu_sp = submenus.front();
2170 result = eKeyHandled;
2171 break;
2172
2173 case KEY_RIGHT:
2174 {
2175 ++m_selected;
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00002176 if (m_selected >= static_cast<int>(num_submenus))
Greg Clayton44d93782014-01-27 23:43:24 +00002177 m_selected = 0;
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00002178 if (m_selected < static_cast<int>(num_submenus))
Greg Clayton44d93782014-01-27 23:43:24 +00002179 run_menu_sp = submenus[m_selected];
2180 else if (!submenus.empty())
2181 run_menu_sp = submenus.front();
2182 result = eKeyHandled;
2183 }
2184 break;
2185
2186 case KEY_LEFT:
2187 {
2188 --m_selected;
2189 if (m_selected < 0)
2190 m_selected = num_submenus - 1;
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00002191 if (m_selected < static_cast<int>(num_submenus))
Greg Clayton44d93782014-01-27 23:43:24 +00002192 run_menu_sp = submenus[m_selected];
2193 else if (!submenus.empty())
2194 run_menu_sp = submenus.front();
2195 result = eKeyHandled;
2196 }
2197 break;
2198
2199 default:
2200 for (size_t i=0; i<num_submenus; ++i)
2201 {
2202 if (submenus[i]->GetKeyValue() == key)
2203 {
2204 SetSelectedSubmenuIndex(i);
2205 run_menu_sp = submenus[i];
2206 result = eKeyHandled;
2207 break;
2208 }
2209 }
2210 break;
2211 }
2212
2213 if (run_menu_sp)
2214 {
2215 // Run the action on this menu in case we need to populate the
2216 // menu with dynamic content and also in case check marks, and
Kamil Rytarowski85025452015-12-04 21:23:24 +00002217 // any other menu decorations need to be calculated
Greg Clayton44d93782014-01-27 23:43:24 +00002218 if (run_menu_sp->Action() == MenuActionResult::Quit)
2219 return eQuitApplication;
2220
2221 Rect menu_bounds;
2222 menu_bounds.origin.x = run_menu_sp->GetStartingColumn();
2223 menu_bounds.origin.y = 1;
2224 menu_bounds.size.width = run_menu_sp->GetDrawWidth();
2225 menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2;
2226 if (m_menu_window_sp)
2227 window.GetParent()->RemoveSubWindow(m_menu_window_sp.get());
2228
2229 m_menu_window_sp = window.GetParent()->CreateSubWindow (run_menu_sp->GetName().c_str(),
2230 menu_bounds,
2231 true);
2232 m_menu_window_sp->SetDelegate (run_menu_sp);
2233 }
2234 }
2235 else if (menu_type == Menu::Type::Item)
2236 {
2237 switch (key)
2238 {
2239 case KEY_DOWN:
2240 if (m_submenus.size() > 1)
2241 {
2242 const int start_select = m_selected;
2243 while (++m_selected != start_select)
2244 {
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00002245 if (static_cast<size_t>(m_selected) >= num_submenus)
Greg Clayton44d93782014-01-27 23:43:24 +00002246 m_selected = 0;
2247 if (m_submenus[m_selected]->GetType() == Type::Separator)
2248 continue;
2249 else
2250 break;
2251 }
2252 return eKeyHandled;
2253 }
2254 break;
2255
2256 case KEY_UP:
2257 if (m_submenus.size() > 1)
2258 {
2259 const int start_select = m_selected;
2260 while (--m_selected != start_select)
2261 {
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00002262 if (m_selected < static_cast<int>(0))
Greg Clayton44d93782014-01-27 23:43:24 +00002263 m_selected = num_submenus - 1;
2264 if (m_submenus[m_selected]->GetType() == Type::Separator)
2265 continue;
2266 else
2267 break;
2268 }
2269 return eKeyHandled;
2270 }
2271 break;
2272
2273 case KEY_RETURN:
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00002274 if (static_cast<size_t>(selected_idx) < num_submenus)
Greg Clayton44d93782014-01-27 23:43:24 +00002275 {
2276 if (submenus[selected_idx]->Action() == MenuActionResult::Quit)
2277 return eQuitApplication;
2278 window.GetParent()->RemoveSubWindow(&window);
2279 return eKeyHandled;
2280 }
2281 break;
2282
2283 case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in case other chars are entered for escaped sequences
2284 window.GetParent()->RemoveSubWindow(&window);
2285 return eKeyHandled;
2286
2287 default:
2288 {
Greg Clayton44d93782014-01-27 23:43:24 +00002289 for (size_t i=0; i<num_submenus; ++i)
2290 {
2291 Menu *menu = submenus[i].get();
2292 if (menu->GetKeyValue() == key)
2293 {
Greg Clayton44d93782014-01-27 23:43:24 +00002294 SetSelectedSubmenuIndex(i);
2295 window.GetParent()->RemoveSubWindow(&window);
2296 if (menu->Action() == MenuActionResult::Quit)
2297 return eQuitApplication;
2298 return eKeyHandled;
2299 }
2300 }
2301 }
2302 break;
2303
2304 }
2305 }
2306 else if (menu_type == Menu::Type::Separator)
2307 {
Greg Clayton44d93782014-01-27 23:43:24 +00002308 }
2309 return result;
2310 }
2311
Greg Clayton44d93782014-01-27 23:43:24 +00002312 class Application
2313 {
2314 public:
2315 Application (FILE *in, FILE *out) :
2316 m_window_sp(),
2317 m_screen (NULL),
2318 m_in (in),
2319 m_out (out)
2320 {
2321
2322 }
2323
2324 ~Application ()
2325 {
2326 m_window_delegates.clear();
2327 m_window_sp.reset();
2328 if (m_screen)
2329 {
2330 ::delscreen(m_screen);
2331 m_screen = NULL;
2332 }
2333 }
2334
2335 void
2336 Initialize ()
2337 {
2338 ::setlocale(LC_ALL, "");
2339 ::setlocale(LC_CTYPE, "");
2340#if 0
2341 ::initscr();
2342#else
2343 m_screen = ::newterm(NULL, m_out, m_in);
2344#endif
2345 ::start_color();
2346 ::curs_set(0);
2347 ::noecho();
2348 ::keypad(stdscr,TRUE);
2349 }
2350
2351 void
2352 Terminate ()
2353 {
2354 ::endwin();
2355 }
2356
2357 void
2358 Run (Debugger &debugger)
2359 {
2360 bool done = false;
2361 int delay_in_tenths_of_a_second = 1;
2362
2363 // Alas the threading model in curses is a bit lame so we need to
2364 // resort to polling every 0.5 seconds. We could poll for stdin
2365 // ourselves and then pass the keys down but then we need to
2366 // translate all of the escape sequences ourselves. So we resort to
2367 // polling for input because we need to receive async process events
2368 // while in this loop.
2369
2370 halfdelay(delay_in_tenths_of_a_second); // Poll using some number of tenths of seconds seconds when calling Window::GetChar()
2371
Jim Ingham583bbb12016-03-07 21:50:25 +00002372 ListenerSP listener_sp (Listener::MakeListener("lldb.IOHandler.curses.Application"));
Greg Clayton44d93782014-01-27 23:43:24 +00002373 ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass());
2374 ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass());
2375 ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass());
2376 debugger.EnableForwardEvents (listener_sp);
2377
2378 bool update = true;
2379#if defined(__APPLE__)
2380 std::deque<int> escape_chars;
2381#endif
2382
2383 while (!done)
2384 {
2385 if (update)
2386 {
2387 m_window_sp->Draw(false);
2388 // All windows should be calling Window::DeferredRefresh() instead
2389 // of Window::Refresh() so we can do a single update and avoid
2390 // any screen blinking
2391 update_panels();
2392
2393 // Cursor hiding isn't working on MacOSX, so hide it in the top left corner
2394 m_window_sp->MoveCursor(0, 0);
2395
2396 doupdate();
2397 update = false;
2398 }
2399
2400#if defined(__APPLE__)
2401 // Terminal.app doesn't map its function keys correctly, F1-F4 default to:
2402 // \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if possible
2403 int ch;
2404 if (escape_chars.empty())
2405 ch = m_window_sp->GetChar();
2406 else
2407 {
2408 ch = escape_chars.front();
2409 escape_chars.pop_front();
2410 }
2411 if (ch == KEY_ESCAPE)
2412 {
2413 int ch2 = m_window_sp->GetChar();
2414 if (ch2 == 'O')
2415 {
2416 int ch3 = m_window_sp->GetChar();
2417 switch (ch3)
2418 {
2419 case 'P': ch = KEY_F(1); break;
2420 case 'Q': ch = KEY_F(2); break;
2421 case 'R': ch = KEY_F(3); break;
2422 case 'S': ch = KEY_F(4); break;
2423 default:
2424 escape_chars.push_back(ch2);
2425 if (ch3 != -1)
2426 escape_chars.push_back(ch3);
2427 break;
2428 }
2429 }
2430 else if (ch2 != -1)
2431 escape_chars.push_back(ch2);
2432 }
2433#else
2434 int ch = m_window_sp->GetChar();
2435
2436#endif
2437 if (ch == -1)
2438 {
2439 if (feof(m_in) || ferror(m_in))
2440 {
2441 done = true;
2442 }
2443 else
2444 {
2445 // Just a timeout from using halfdelay(), check for events
2446 EventSP event_sp;
2447 while (listener_sp->PeekAtNextEvent())
2448 {
2449 listener_sp->GetNextEvent(event_sp);
2450
2451 if (event_sp)
2452 {
2453 Broadcaster *broadcaster = event_sp->GetBroadcaster();
2454 if (broadcaster)
2455 {
2456 //uint32_t event_type = event_sp->GetType();
2457 ConstString broadcaster_class (broadcaster->GetBroadcasterClass());
2458 if (broadcaster_class == broadcaster_class_process)
2459 {
Greg Claytonec990862014-03-19 16:22:48 +00002460 debugger.GetCommandInterpreter().UpdateExecutionContext(NULL);
Greg Clayton44d93782014-01-27 23:43:24 +00002461 update = true;
2462 continue; // Don't get any key, just update our view
2463 }
2464 }
2465 }
2466 }
2467 }
2468 }
2469 else
2470 {
2471 HandleCharResult key_result = m_window_sp->HandleChar(ch);
2472 switch (key_result)
2473 {
2474 case eKeyHandled:
Greg Claytonec990862014-03-19 16:22:48 +00002475 debugger.GetCommandInterpreter().UpdateExecutionContext(NULL);
Greg Clayton44d93782014-01-27 23:43:24 +00002476 update = true;
2477 break;
2478 case eKeyNotHandled:
2479 break;
2480 case eQuitApplication:
2481 done = true;
2482 break;
2483 }
2484 }
2485 }
2486
2487 debugger.CancelForwardEvents (listener_sp);
Greg Clayton44d93782014-01-27 23:43:24 +00002488 }
2489
2490 WindowSP &
2491 GetMainWindow ()
2492 {
2493 if (!m_window_sp)
2494 m_window_sp.reset (new Window ("main", stdscr, false));
2495 return m_window_sp;
2496 }
2497
2498 WindowDelegates &
2499 GetWindowDelegates ()
2500 {
2501 return m_window_delegates;
2502 }
2503
2504 protected:
2505 WindowSP m_window_sp;
2506 WindowDelegates m_window_delegates;
2507 SCREEN *m_screen;
2508 FILE *m_in;
2509 FILE *m_out;
2510 };
Greg Clayton44d93782014-01-27 23:43:24 +00002511
2512} // namespace curses
2513
Greg Clayton44d93782014-01-27 23:43:24 +00002514using namespace curses;
2515
2516struct Row
2517{
2518 ValueObjectSP valobj;
2519 Row *parent;
2520 int row_idx;
2521 int x;
2522 int y;
2523 bool might_have_children;
2524 bool expanded;
2525 bool calculated_children;
2526 std::vector<Row> children;
2527
2528 Row (const ValueObjectSP &v, Row *p) :
2529 valobj (v),
2530 parent (p),
2531 row_idx(0),
2532 x(1),
2533 y(1),
2534 might_have_children (v ? v->MightHaveChildren() : false),
2535 expanded (false),
2536 calculated_children (false),
2537 children()
2538 {
2539 }
2540
2541 size_t
2542 GetDepth () const
2543 {
2544 if (parent)
2545 return 1 + parent->GetDepth();
2546 return 0;
2547 }
2548
2549 void
2550 Expand()
2551 {
2552 expanded = true;
2553 if (!calculated_children)
2554 {
2555 calculated_children = true;
2556 if (valobj)
2557 {
2558 const size_t num_children = valobj->GetNumChildren();
2559 for (size_t i=0; i<num_children; ++i)
2560 {
2561 children.push_back(Row (valobj->GetChildAtIndex(i, true), this));
2562 }
2563 }
2564 }
2565 }
2566
2567 void
2568 Unexpand ()
2569 {
2570 expanded = false;
2571 }
2572
2573 void
2574 DrawTree (Window &window)
2575 {
2576 if (parent)
2577 parent->DrawTreeForChild (window, this, 0);
2578
2579 if (might_have_children)
2580 {
2581 // It we can get UTF8 characters to work we should try to use the "symbol"
2582 // UTF8 string below
2583// const char *symbol = "";
2584// if (row.expanded)
2585// symbol = "\xe2\x96\xbd ";
2586// else
2587// symbol = "\xe2\x96\xb7 ";
2588// window.PutCString (symbol);
2589
2590 // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
2591 // 'v' or '>' character...
2592// if (expanded)
2593// window.PutChar (ACS_DARROW);
2594// else
2595// window.PutChar (ACS_RARROW);
2596 // Since we can't find any good looking right arrow/down arrow
2597 // symbols, just use a diamond...
2598 window.PutChar (ACS_DIAMOND);
2599 window.PutChar (ACS_HLINE);
2600 }
2601 }
2602
2603 void
2604 DrawTreeForChild (Window &window, Row *child, uint32_t reverse_depth)
2605 {
2606 if (parent)
2607 parent->DrawTreeForChild (window, this, reverse_depth + 1);
2608
2609 if (&children.back() == child)
2610 {
2611 // Last child
2612 if (reverse_depth == 0)
2613 {
2614 window.PutChar (ACS_LLCORNER);
2615 window.PutChar (ACS_HLINE);
2616 }
2617 else
2618 {
2619 window.PutChar (' ');
2620 window.PutChar (' ');
2621 }
2622 }
2623 else
2624 {
2625 if (reverse_depth == 0)
2626 {
2627 window.PutChar (ACS_LTEE);
2628 window.PutChar (ACS_HLINE);
2629 }
2630 else
2631 {
2632 window.PutChar (ACS_VLINE);
2633 window.PutChar (' ');
2634 }
2635 }
2636 }
2637};
2638
2639struct DisplayOptions
2640{
2641 bool show_types;
2642};
2643
2644class TreeItem;
2645
2646class TreeDelegate
2647{
2648public:
2649 TreeDelegate() {}
Eugene Zelenko315b6882015-10-26 17:00:13 +00002650 virtual ~TreeDelegate() = default;
2651
Greg Clayton44d93782014-01-27 23:43:24 +00002652 virtual void TreeDelegateDrawTreeItem (TreeItem &item, Window &window) = 0;
2653 virtual void TreeDelegateGenerateChildren (TreeItem &item) = 0;
2654 virtual bool TreeDelegateItemSelected (TreeItem &item) = 0; // Return true if we need to update views
2655};
Eugene Zelenko315b6882015-10-26 17:00:13 +00002656
Greg Clayton44d93782014-01-27 23:43:24 +00002657typedef std::shared_ptr<TreeDelegate> TreeDelegateSP;
2658
2659class TreeItem
2660{
2661public:
2662
2663 TreeItem (TreeItem *parent, TreeDelegate &delegate, bool might_have_children) :
2664 m_parent (parent),
2665 m_delegate (delegate),
Greg Claytonec990862014-03-19 16:22:48 +00002666 m_user_data (NULL),
Greg Clayton44d93782014-01-27 23:43:24 +00002667 m_identifier (0),
2668 m_row_idx (-1),
2669 m_children (),
2670 m_might_have_children (might_have_children),
2671 m_is_expanded (false)
2672 {
2673 }
2674
2675 TreeItem &
2676 operator=(const TreeItem &rhs)
2677 {
2678 if (this != &rhs)
2679 {
2680 m_parent = rhs.m_parent;
2681 m_delegate = rhs.m_delegate;
Greg Claytonec990862014-03-19 16:22:48 +00002682 m_user_data = rhs.m_user_data;
Greg Clayton44d93782014-01-27 23:43:24 +00002683 m_identifier = rhs.m_identifier;
2684 m_row_idx = rhs.m_row_idx;
2685 m_children = rhs.m_children;
2686 m_might_have_children = rhs.m_might_have_children;
2687 m_is_expanded = rhs.m_is_expanded;
2688 }
2689 return *this;
2690 }
2691
2692 size_t
2693 GetDepth () const
2694 {
2695 if (m_parent)
2696 return 1 + m_parent->GetDepth();
2697 return 0;
2698 }
2699
2700 int
2701 GetRowIndex () const
2702 {
2703 return m_row_idx;
2704 }
2705
2706 void
2707 ClearChildren ()
2708 {
2709 m_children.clear();
2710 }
2711
2712 void
2713 Resize (size_t n, const TreeItem &t)
2714 {
2715 m_children.resize(n, t);
2716 }
2717
2718 TreeItem &
2719 operator [](size_t i)
2720 {
2721 return m_children[i];
2722 }
2723
2724 void
2725 SetRowIndex (int row_idx)
2726 {
2727 m_row_idx = row_idx;
2728 }
2729
2730 size_t
2731 GetNumChildren ()
2732 {
2733 m_delegate.TreeDelegateGenerateChildren (*this);
2734 return m_children.size();
2735 }
2736
2737 void
2738 ItemWasSelected ()
2739 {
2740 m_delegate.TreeDelegateItemSelected(*this);
2741 }
Eugene Zelenko315b6882015-10-26 17:00:13 +00002742
Greg Clayton44d93782014-01-27 23:43:24 +00002743 void
2744 CalculateRowIndexes (int &row_idx)
2745 {
2746 SetRowIndex(row_idx);
2747 ++row_idx;
2748
Greg Claytonec990862014-03-19 16:22:48 +00002749 const bool expanded = IsExpanded();
2750
2751 // The root item must calculate its children,
2752 // or we must calculate the number of children
2753 // if the item is expanded
2754 if (m_parent == NULL || expanded)
Greg Clayton44d93782014-01-27 23:43:24 +00002755 GetNumChildren();
2756
Greg Clayton44d93782014-01-27 23:43:24 +00002757 for (auto &item : m_children)
2758 {
2759 if (expanded)
2760 item.CalculateRowIndexes(row_idx);
2761 else
2762 item.SetRowIndex(-1);
2763 }
2764 }
2765
2766 TreeItem *
2767 GetParent ()
2768 {
2769 return m_parent;
2770 }
2771
2772 bool
2773 IsExpanded () const
2774 {
2775 return m_is_expanded;
2776 }
2777
2778 void
2779 Expand()
2780 {
2781 m_is_expanded = true;
2782 }
2783
2784 void
2785 Unexpand ()
2786 {
2787 m_is_expanded = false;
2788 }
2789
2790 bool
2791 Draw (Window &window,
2792 const int first_visible_row,
2793 const uint32_t selected_row_idx,
2794 int &row_idx,
2795 int &num_rows_left)
2796 {
2797 if (num_rows_left <= 0)
2798 return false;
2799
2800 if (m_row_idx >= first_visible_row)
2801 {
2802 window.MoveCursor(2, row_idx + 1);
2803
2804 if (m_parent)
2805 m_parent->DrawTreeForChild (window, this, 0);
2806
2807 if (m_might_have_children)
2808 {
2809 // It we can get UTF8 characters to work we should try to use the "symbol"
2810 // UTF8 string below
2811 // const char *symbol = "";
2812 // if (row.expanded)
2813 // symbol = "\xe2\x96\xbd ";
2814 // else
2815 // symbol = "\xe2\x96\xb7 ";
2816 // window.PutCString (symbol);
2817
2818 // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
2819 // 'v' or '>' character...
2820 // if (expanded)
2821 // window.PutChar (ACS_DARROW);
2822 // else
2823 // window.PutChar (ACS_RARROW);
2824 // Since we can't find any good looking right arrow/down arrow
2825 // symbols, just use a diamond...
2826 window.PutChar (ACS_DIAMOND);
2827 window.PutChar (ACS_HLINE);
2828 }
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00002829 bool highlight =
2830 (selected_row_idx == static_cast<size_t>(m_row_idx)) && window.IsActive();
Greg Clayton44d93782014-01-27 23:43:24 +00002831
2832 if (highlight)
2833 window.AttributeOn(A_REVERSE);
2834
2835 m_delegate.TreeDelegateDrawTreeItem(*this, window);
2836
2837 if (highlight)
2838 window.AttributeOff(A_REVERSE);
2839 ++row_idx;
2840 --num_rows_left;
2841 }
2842
2843 if (num_rows_left <= 0)
2844 return false; // We are done drawing...
2845
2846 if (IsExpanded())
2847 {
2848 for (auto &item : m_children)
2849 {
2850 // If we displayed all the rows and item.Draw() returns
2851 // false we are done drawing and can exit this for loop
2852 if (item.Draw(window, first_visible_row, selected_row_idx, row_idx, num_rows_left) == false)
2853 break;
2854 }
2855 }
2856 return num_rows_left >= 0; // Return true if not done drawing yet
2857 }
2858
2859 void
2860 DrawTreeForChild (Window &window, TreeItem *child, uint32_t reverse_depth)
2861 {
2862 if (m_parent)
2863 m_parent->DrawTreeForChild (window, this, reverse_depth + 1);
2864
2865 if (&m_children.back() == child)
2866 {
2867 // Last child
2868 if (reverse_depth == 0)
2869 {
2870 window.PutChar (ACS_LLCORNER);
2871 window.PutChar (ACS_HLINE);
2872 }
2873 else
2874 {
2875 window.PutChar (' ');
2876 window.PutChar (' ');
2877 }
2878 }
2879 else
2880 {
2881 if (reverse_depth == 0)
2882 {
2883 window.PutChar (ACS_LTEE);
2884 window.PutChar (ACS_HLINE);
2885 }
2886 else
2887 {
2888 window.PutChar (ACS_VLINE);
2889 window.PutChar (' ');
2890 }
2891 }
2892 }
2893
2894 TreeItem *
2895 GetItemForRowIndex (uint32_t row_idx)
2896 {
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00002897 if (static_cast<uint32_t>(m_row_idx) == row_idx)
Greg Clayton44d93782014-01-27 23:43:24 +00002898 return this;
2899 if (m_children.empty())
2900 return NULL;
Greg Clayton44d93782014-01-27 23:43:24 +00002901 if (IsExpanded())
2902 {
2903 for (auto &item : m_children)
2904 {
2905 TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx);
2906 if (selected_item_ptr)
2907 return selected_item_ptr;
2908 }
2909 }
2910 return NULL;
2911 }
2912
Greg Claytonec990862014-03-19 16:22:48 +00002913 void *
2914 GetUserData() const
2915 {
2916 return m_user_data;
2917 }
2918
2919 void
2920 SetUserData (void *user_data)
2921 {
2922 m_user_data = user_data;
2923 }
2924
Greg Clayton44d93782014-01-27 23:43:24 +00002925 uint64_t
2926 GetIdentifier() const
2927 {
2928 return m_identifier;
2929 }
2930
2931 void
2932 SetIdentifier (uint64_t identifier)
2933 {
2934 m_identifier = identifier;
2935 }
Greg Clayton44d93782014-01-27 23:43:24 +00002936
Greg Claytonec990862014-03-19 16:22:48 +00002937 void
2938 SetMightHaveChildren (bool b)
2939 {
2940 m_might_have_children = b;
2941 }
2942
Greg Clayton44d93782014-01-27 23:43:24 +00002943protected:
2944 TreeItem *m_parent;
2945 TreeDelegate &m_delegate;
Greg Claytonec990862014-03-19 16:22:48 +00002946 void *m_user_data;
Greg Clayton44d93782014-01-27 23:43:24 +00002947 uint64_t m_identifier;
2948 int m_row_idx; // Zero based visible row index, -1 if not visible or for the root item
2949 std::vector<TreeItem> m_children;
2950 bool m_might_have_children;
2951 bool m_is_expanded;
Greg Clayton44d93782014-01-27 23:43:24 +00002952};
2953
2954class TreeWindowDelegate : public WindowDelegate
2955{
2956public:
2957 TreeWindowDelegate (Debugger &debugger, const TreeDelegateSP &delegate_sp) :
2958 m_debugger (debugger),
2959 m_delegate_sp (delegate_sp),
2960 m_root (NULL, *delegate_sp, true),
2961 m_selected_item (NULL),
2962 m_num_rows (0),
2963 m_selected_row_idx (0),
2964 m_first_visible_row (0),
2965 m_min_x (0),
2966 m_min_y (0),
2967 m_max_x (0),
2968 m_max_y (0)
2969 {
2970 }
2971
2972 int
2973 NumVisibleRows () const
2974 {
2975 return m_max_y - m_min_y;
2976 }
2977
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00002978 bool
2979 WindowDelegateDraw (Window &window, bool force) override
Greg Clayton44d93782014-01-27 23:43:24 +00002980 {
2981 ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext());
2982 Process *process = exe_ctx.GetProcessPtr();
2983
2984 bool display_content = false;
2985 if (process)
2986 {
2987 StateType state = process->GetState();
2988 if (StateIsStoppedState(state, true))
2989 {
2990 // We are stopped, so it is ok to
2991 display_content = true;
2992 }
2993 else if (StateIsRunningState(state))
2994 {
2995 return true; // Don't do any updating when we are running
2996 }
2997 }
2998
2999 m_min_x = 2;
3000 m_min_y = 1;
3001 m_max_x = window.GetWidth() - 1;
3002 m_max_y = window.GetHeight() - 1;
3003
3004 window.Erase();
3005 window.DrawTitleBox (window.GetName());
3006
3007 if (display_content)
3008 {
3009 const int num_visible_rows = NumVisibleRows();
3010 m_num_rows = 0;
3011 m_root.CalculateRowIndexes(m_num_rows);
3012
3013 // If we unexpanded while having something selected our
3014 // total number of rows is less than the num visible rows,
3015 // then make sure we show all the rows by setting the first
3016 // visible row accordingly.
3017 if (m_first_visible_row > 0 && m_num_rows < num_visible_rows)
3018 m_first_visible_row = 0;
3019
3020 // Make sure the selected row is always visible
3021 if (m_selected_row_idx < m_first_visible_row)
3022 m_first_visible_row = m_selected_row_idx;
3023 else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
3024 m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
3025
3026 int row_idx = 0;
3027 int num_rows_left = num_visible_rows;
3028 m_root.Draw (window, m_first_visible_row, m_selected_row_idx, row_idx, num_rows_left);
3029 // Get the selected row
3030 m_selected_item = m_root.GetItemForRowIndex (m_selected_row_idx);
3031 }
3032 else
3033 {
3034 m_selected_item = NULL;
3035 }
3036
3037 window.DeferredRefresh();
3038
3039
3040 return true; // Drawing handled
3041 }
Eugene Zelenko315b6882015-10-26 17:00:13 +00003042
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00003043 const char *
3044 WindowDelegateGetHelpText () override
Greg Clayton44d93782014-01-27 23:43:24 +00003045 {
3046 return "Thread window keyboard shortcuts:";
3047 }
3048
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00003049 KeyHelp *
3050 WindowDelegateGetKeyHelp () override
Greg Clayton44d93782014-01-27 23:43:24 +00003051 {
3052 static curses::KeyHelp g_source_view_key_help[] = {
3053 { KEY_UP, "Select previous item" },
3054 { KEY_DOWN, "Select next item" },
3055 { KEY_RIGHT, "Expand the selected item" },
3056 { KEY_LEFT, "Unexpand the selected item or select parent if not expanded" },
3057 { KEY_PPAGE, "Page up" },
3058 { KEY_NPAGE, "Page down" },
3059 { 'h', "Show help dialog" },
3060 { ' ', "Toggle item expansion" },
3061 { ',', "Page up" },
3062 { '.', "Page down" },
3063 { '\0', NULL }
3064 };
3065 return g_source_view_key_help;
3066 }
3067
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00003068 HandleCharResult
3069 WindowDelegateHandleChar (Window &window, int c) override
Greg Clayton44d93782014-01-27 23:43:24 +00003070 {
3071 switch(c)
3072 {
3073 case ',':
3074 case KEY_PPAGE:
3075 // Page up key
3076 if (m_first_visible_row > 0)
3077 {
3078 if (m_first_visible_row > m_max_y)
3079 m_first_visible_row -= m_max_y;
3080 else
3081 m_first_visible_row = 0;
3082 m_selected_row_idx = m_first_visible_row;
3083 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3084 if (m_selected_item)
3085 m_selected_item->ItemWasSelected ();
3086 }
3087 return eKeyHandled;
3088
3089 case '.':
3090 case KEY_NPAGE:
3091 // Page down key
3092 if (m_num_rows > m_max_y)
3093 {
3094 if (m_first_visible_row + m_max_y < m_num_rows)
3095 {
3096 m_first_visible_row += m_max_y;
3097 m_selected_row_idx = m_first_visible_row;
3098 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3099 if (m_selected_item)
3100 m_selected_item->ItemWasSelected ();
3101 }
3102 }
3103 return eKeyHandled;
3104
3105 case KEY_UP:
3106 if (m_selected_row_idx > 0)
3107 {
3108 --m_selected_row_idx;
3109 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3110 if (m_selected_item)
3111 m_selected_item->ItemWasSelected ();
3112 }
3113 return eKeyHandled;
Eugene Zelenko315b6882015-10-26 17:00:13 +00003114
Greg Clayton44d93782014-01-27 23:43:24 +00003115 case KEY_DOWN:
3116 if (m_selected_row_idx + 1 < m_num_rows)
3117 {
3118 ++m_selected_row_idx;
3119 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3120 if (m_selected_item)
3121 m_selected_item->ItemWasSelected ();
3122 }
3123 return eKeyHandled;
3124
3125 case KEY_RIGHT:
3126 if (m_selected_item)
3127 {
3128 if (!m_selected_item->IsExpanded())
3129 m_selected_item->Expand();
3130 }
3131 return eKeyHandled;
3132
3133 case KEY_LEFT:
3134 if (m_selected_item)
3135 {
3136 if (m_selected_item->IsExpanded())
3137 m_selected_item->Unexpand();
3138 else if (m_selected_item->GetParent())
3139 {
3140 m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex();
3141 m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
3142 if (m_selected_item)
3143 m_selected_item->ItemWasSelected ();
3144 }
3145 }
3146 return eKeyHandled;
3147
3148 case ' ':
3149 // Toggle expansion state when SPACE is pressed
3150 if (m_selected_item)
3151 {
3152 if (m_selected_item->IsExpanded())
3153 m_selected_item->Unexpand();
3154 else
3155 m_selected_item->Expand();
3156 }
3157 return eKeyHandled;
3158
3159 case 'h':
3160 window.CreateHelpSubwindow ();
3161 return eKeyHandled;
3162
3163 default:
3164 break;
3165 }
3166 return eKeyNotHandled;
3167 }
3168
3169protected:
3170 Debugger &m_debugger;
3171 TreeDelegateSP m_delegate_sp;
3172 TreeItem m_root;
3173 TreeItem *m_selected_item;
3174 int m_num_rows;
3175 int m_selected_row_idx;
3176 int m_first_visible_row;
3177 int m_min_x;
3178 int m_min_y;
3179 int m_max_x;
3180 int m_max_y;
Greg Clayton44d93782014-01-27 23:43:24 +00003181};
3182
3183class FrameTreeDelegate : public TreeDelegate
3184{
3185public:
Greg Claytonec990862014-03-19 16:22:48 +00003186 FrameTreeDelegate () :
3187 TreeDelegate()
Greg Clayton44d93782014-01-27 23:43:24 +00003188 {
Greg Clayton554f68d2015-02-04 22:00:53 +00003189 FormatEntity::Parse ("frame #${frame.index}: {${function.name}${function.pc-offset}}}",
3190 m_format);
Greg Clayton44d93782014-01-27 23:43:24 +00003191 }
Eugene Zelenko315b6882015-10-26 17:00:13 +00003192
3193 ~FrameTreeDelegate() override = default;
3194
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00003195 void
3196 TreeDelegateDrawTreeItem (TreeItem &item, Window &window) override
Greg Clayton44d93782014-01-27 23:43:24 +00003197 {
Greg Claytonec990862014-03-19 16:22:48 +00003198 Thread* thread = (Thread*)item.GetUserData();
3199 if (thread)
Greg Clayton44d93782014-01-27 23:43:24 +00003200 {
3201 const uint64_t frame_idx = item.GetIdentifier();
Greg Claytonec990862014-03-19 16:22:48 +00003202 StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_idx);
Greg Clayton44d93782014-01-27 23:43:24 +00003203 if (frame_sp)
3204 {
3205 StreamString strm;
3206 const SymbolContext &sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
3207 ExecutionContext exe_ctx (frame_sp);
Greg Clayton554f68d2015-02-04 22:00:53 +00003208 if (FormatEntity::Format(m_format, strm, &sc, &exe_ctx, NULL, NULL, false, false))
Greg Clayton44d93782014-01-27 23:43:24 +00003209 {
3210 int right_pad = 1;
3211 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
3212 }
3213 }
3214 }
3215 }
Eugene Zelenko315b6882015-10-26 17:00:13 +00003216
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00003217 void
3218 TreeDelegateGenerateChildren (TreeItem &item) override
Greg Clayton44d93782014-01-27 23:43:24 +00003219 {
3220 // No children for frames yet...
3221 }
3222
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00003223 bool
3224 TreeDelegateItemSelected (TreeItem &item) override
Greg Clayton44d93782014-01-27 23:43:24 +00003225 {
Greg Claytonec990862014-03-19 16:22:48 +00003226 Thread* thread = (Thread*)item.GetUserData();
3227 if (thread)
Greg Clayton44d93782014-01-27 23:43:24 +00003228 {
Greg Claytonec990862014-03-19 16:22:48 +00003229 thread->GetProcess()->GetThreadList().SetSelectedThreadByID(thread->GetID());
Greg Clayton44d93782014-01-27 23:43:24 +00003230 const uint64_t frame_idx = item.GetIdentifier();
Greg Claytonec990862014-03-19 16:22:48 +00003231 thread->SetSelectedFrameByIndex(frame_idx);
Greg Clayton44d93782014-01-27 23:43:24 +00003232 return true;
3233 }
3234 return false;
3235 }
Eugene Zelenko315b6882015-10-26 17:00:13 +00003236
Greg Clayton554f68d2015-02-04 22:00:53 +00003237protected:
3238 FormatEntity::Entry m_format;
Greg Clayton44d93782014-01-27 23:43:24 +00003239};
3240
3241class ThreadTreeDelegate : public TreeDelegate
3242{
3243public:
3244 ThreadTreeDelegate (Debugger &debugger) :
3245 TreeDelegate(),
3246 m_debugger (debugger),
Greg Clayton44d93782014-01-27 23:43:24 +00003247 m_tid (LLDB_INVALID_THREAD_ID),
3248 m_stop_id (UINT32_MAX)
3249 {
Greg Clayton554f68d2015-02-04 22:00:53 +00003250 FormatEntity::Parse ("thread #${thread.index}: tid = ${thread.id}{, stop reason = ${thread.stop-reason}}",
3251 m_format);
Greg Clayton44d93782014-01-27 23:43:24 +00003252 }
Eugene Zelenko315b6882015-10-26 17:00:13 +00003253
3254 ~ThreadTreeDelegate() override = default;
3255
Greg Claytonec990862014-03-19 16:22:48 +00003256 ProcessSP
3257 GetProcess ()
3258 {
3259 return m_debugger.GetCommandInterpreter().GetExecutionContext().GetProcessSP();
3260 }
3261
3262 ThreadSP
3263 GetThread (const TreeItem &item)
3264 {
3265 ProcessSP process_sp = GetProcess ();
3266 if (process_sp)
3267 return process_sp->GetThreadList().FindThreadByID(item.GetIdentifier());
3268 return ThreadSP();
3269 }
3270
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00003271 void
3272 TreeDelegateDrawTreeItem (TreeItem &item, Window &window) override
Greg Clayton44d93782014-01-27 23:43:24 +00003273 {
Greg Claytonec990862014-03-19 16:22:48 +00003274 ThreadSP thread_sp = GetThread (item);
Greg Clayton44d93782014-01-27 23:43:24 +00003275 if (thread_sp)
3276 {
3277 StreamString strm;
3278 ExecutionContext exe_ctx (thread_sp);
Greg Clayton554f68d2015-02-04 22:00:53 +00003279 if (FormatEntity::Format (m_format, strm, NULL, &exe_ctx, NULL, NULL, false, false))
Greg Clayton44d93782014-01-27 23:43:24 +00003280 {
3281 int right_pad = 1;
3282 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
3283 }
3284 }
3285 }
Eugene Zelenko315b6882015-10-26 17:00:13 +00003286
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00003287 void
3288 TreeDelegateGenerateChildren (TreeItem &item) override
Greg Clayton44d93782014-01-27 23:43:24 +00003289 {
Greg Claytonec990862014-03-19 16:22:48 +00003290 ProcessSP process_sp = GetProcess ();
3291 if (process_sp && process_sp->IsAlive())
Greg Clayton44d93782014-01-27 23:43:24 +00003292 {
Greg Claytonec990862014-03-19 16:22:48 +00003293 StateType state = process_sp->GetState();
3294 if (StateIsStoppedState(state, true))
Greg Clayton44d93782014-01-27 23:43:24 +00003295 {
Greg Claytonec990862014-03-19 16:22:48 +00003296 ThreadSP thread_sp = GetThread (item);
3297 if (thread_sp)
Greg Clayton44d93782014-01-27 23:43:24 +00003298 {
Greg Claytonec990862014-03-19 16:22:48 +00003299 if (m_stop_id == process_sp->GetStopID() && thread_sp->GetID() == m_tid)
3300 return; // Children are already up to date
3301 if (!m_frame_delegate_sp)
Greg Clayton44d93782014-01-27 23:43:24 +00003302 {
Greg Claytonec990862014-03-19 16:22:48 +00003303 // Always expand the thread item the first time we show it
3304 m_frame_delegate_sp.reset (new FrameTreeDelegate());
Greg Clayton44d93782014-01-27 23:43:24 +00003305 }
Greg Claytonec990862014-03-19 16:22:48 +00003306
3307 m_stop_id = process_sp->GetStopID();
3308 m_tid = thread_sp->GetID();
3309
3310 TreeItem t (&item, *m_frame_delegate_sp, false);
3311 size_t num_frames = thread_sp->GetStackFrameCount();
3312 item.Resize (num_frames, t);
3313 for (size_t i=0; i<num_frames; ++i)
3314 {
3315 item[i].SetUserData(thread_sp.get());
3316 item[i].SetIdentifier(i);
3317 }
Greg Clayton44d93782014-01-27 23:43:24 +00003318 }
Greg Claytonec990862014-03-19 16:22:48 +00003319 return;
Greg Clayton44d93782014-01-27 23:43:24 +00003320 }
3321 }
3322 item.ClearChildren();
3323 }
3324
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00003325 bool
3326 TreeDelegateItemSelected (TreeItem &item) override
Greg Clayton44d93782014-01-27 23:43:24 +00003327 {
Greg Claytonec990862014-03-19 16:22:48 +00003328 ProcessSP process_sp = GetProcess ();
3329 if (process_sp && process_sp->IsAlive())
Greg Clayton44d93782014-01-27 23:43:24 +00003330 {
Greg Claytonec990862014-03-19 16:22:48 +00003331 StateType state = process_sp->GetState();
3332 if (StateIsStoppedState(state, true))
Greg Clayton44d93782014-01-27 23:43:24 +00003333 {
Greg Claytonec990862014-03-19 16:22:48 +00003334 ThreadSP thread_sp = GetThread (item);
3335 if (thread_sp)
3336 {
3337 ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList();
3338 Mutex::Locker locker (thread_list.GetMutex());
3339 ThreadSP selected_thread_sp = thread_list.GetSelectedThread();
3340 if (selected_thread_sp->GetID() != thread_sp->GetID())
3341 {
3342 thread_list.SetSelectedThreadByID(thread_sp->GetID());
3343 return true;
3344 }
3345 }
Greg Clayton44d93782014-01-27 23:43:24 +00003346 }
3347 }
3348 return false;
3349 }
3350
3351protected:
3352 Debugger &m_debugger;
Greg Clayton44d93782014-01-27 23:43:24 +00003353 std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp;
3354 lldb::user_id_t m_tid;
3355 uint32_t m_stop_id;
Greg Clayton554f68d2015-02-04 22:00:53 +00003356 FormatEntity::Entry m_format;
Greg Clayton44d93782014-01-27 23:43:24 +00003357};
3358
Greg Claytonec990862014-03-19 16:22:48 +00003359class ThreadsTreeDelegate : public TreeDelegate
3360{
3361public:
3362 ThreadsTreeDelegate (Debugger &debugger) :
3363 TreeDelegate(),
3364 m_thread_delegate_sp (),
3365 m_debugger (debugger),
3366 m_stop_id (UINT32_MAX)
3367 {
Greg Clayton554f68d2015-02-04 22:00:53 +00003368 FormatEntity::Parse("process ${process.id}{, name = ${process.name}}",
3369 m_format);
Greg Claytonec990862014-03-19 16:22:48 +00003370 }
Eugene Zelenko315b6882015-10-26 17:00:13 +00003371
3372 ~ThreadsTreeDelegate() override = default;
3373
Greg Claytonec990862014-03-19 16:22:48 +00003374 ProcessSP
3375 GetProcess ()
3376 {
3377 return m_debugger.GetCommandInterpreter().GetExecutionContext().GetProcessSP();
3378 }
3379
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00003380 void
3381 TreeDelegateDrawTreeItem (TreeItem &item, Window &window) override
Greg Claytonec990862014-03-19 16:22:48 +00003382 {
3383 ProcessSP process_sp = GetProcess ();
3384 if (process_sp && process_sp->IsAlive())
3385 {
3386 StreamString strm;
3387 ExecutionContext exe_ctx (process_sp);
Greg Clayton554f68d2015-02-04 22:00:53 +00003388 if (FormatEntity::Format (m_format, strm, NULL, &exe_ctx, NULL, NULL, false, false))
Greg Claytonec990862014-03-19 16:22:48 +00003389 {
3390 int right_pad = 1;
3391 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
3392 }
3393 }
3394 }
3395
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00003396 void
3397 TreeDelegateGenerateChildren (TreeItem &item) override
Greg Claytonec990862014-03-19 16:22:48 +00003398 {
3399 ProcessSP process_sp = GetProcess ();
3400 if (process_sp && process_sp->IsAlive())
3401 {
3402 StateType state = process_sp->GetState();
3403 if (StateIsStoppedState(state, true))
3404 {
3405 const uint32_t stop_id = process_sp->GetStopID();
3406 if (m_stop_id == stop_id)
3407 return; // Children are already up to date
3408
3409 m_stop_id = stop_id;
3410
3411 if (!m_thread_delegate_sp)
3412 {
3413 // Always expand the thread item the first time we show it
3414 //item.Expand();
3415 m_thread_delegate_sp.reset (new ThreadTreeDelegate(m_debugger));
3416 }
3417
3418 TreeItem t (&item, *m_thread_delegate_sp, false);
3419 ThreadList &threads = process_sp->GetThreadList();
3420 Mutex::Locker locker (threads.GetMutex());
3421 size_t num_threads = threads.GetSize();
3422 item.Resize (num_threads, t);
3423 for (size_t i=0; i<num_threads; ++i)
3424 {
3425 item[i].SetIdentifier(threads.GetThreadAtIndex(i)->GetID());
3426 item[i].SetMightHaveChildren(true);
3427 }
3428 return;
3429 }
3430 }
3431 item.ClearChildren();
3432 }
3433
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00003434 bool
3435 TreeDelegateItemSelected (TreeItem &item) override
Greg Claytonec990862014-03-19 16:22:48 +00003436 {
3437 return false;
3438 }
3439
3440protected:
3441 std::shared_ptr<ThreadTreeDelegate> m_thread_delegate_sp;
3442 Debugger &m_debugger;
3443 uint32_t m_stop_id;
Greg Clayton554f68d2015-02-04 22:00:53 +00003444 FormatEntity::Entry m_format;
Greg Claytonec990862014-03-19 16:22:48 +00003445};
3446
Greg Clayton44d93782014-01-27 23:43:24 +00003447class ValueObjectListDelegate : public WindowDelegate
3448{
3449public:
3450 ValueObjectListDelegate () :
3451 m_valobj_list (),
3452 m_rows (),
3453 m_selected_row (NULL),
3454 m_selected_row_idx (0),
3455 m_first_visible_row (0),
3456 m_num_rows (0),
3457 m_max_x (0),
3458 m_max_y (0)
3459 {
3460 }
3461
3462 ValueObjectListDelegate (ValueObjectList &valobj_list) :
3463 m_valobj_list (valobj_list),
3464 m_rows (),
3465 m_selected_row (NULL),
3466 m_selected_row_idx (0),
3467 m_first_visible_row (0),
3468 m_num_rows (0),
3469 m_max_x (0),
3470 m_max_y (0)
3471 {
3472 SetValues (valobj_list);
3473 }
Eugene Zelenko315b6882015-10-26 17:00:13 +00003474
3475 ~ValueObjectListDelegate() override = default;
Greg Clayton44d93782014-01-27 23:43:24 +00003476
3477 void
3478 SetValues (ValueObjectList &valobj_list)
3479 {
3480 m_selected_row = NULL;
3481 m_selected_row_idx = 0;
3482 m_first_visible_row = 0;
3483 m_num_rows = 0;
3484 m_rows.clear();
3485 m_valobj_list = valobj_list;
3486 const size_t num_values = m_valobj_list.GetSize();
3487 for (size_t i=0; i<num_values; ++i)
3488 m_rows.push_back(Row(m_valobj_list.GetValueObjectAtIndex(i), NULL));
3489 }
3490
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00003491 bool
3492 WindowDelegateDraw (Window &window, bool force) override
Greg Clayton44d93782014-01-27 23:43:24 +00003493 {
3494 m_num_rows = 0;
3495 m_min_x = 2;
3496 m_min_y = 1;
3497 m_max_x = window.GetWidth() - 1;
3498 m_max_y = window.GetHeight() - 1;
3499
3500 window.Erase();
3501 window.DrawTitleBox (window.GetName());
3502
3503 const int num_visible_rows = NumVisibleRows();
3504 const int num_rows = CalculateTotalNumberRows (m_rows);
3505
3506 // If we unexpanded while having something selected our
3507 // total number of rows is less than the num visible rows,
3508 // then make sure we show all the rows by setting the first
3509 // visible row accordingly.
3510 if (m_first_visible_row > 0 && num_rows < num_visible_rows)
3511 m_first_visible_row = 0;
3512
3513 // Make sure the selected row is always visible
3514 if (m_selected_row_idx < m_first_visible_row)
3515 m_first_visible_row = m_selected_row_idx;
3516 else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
3517 m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
3518
3519 DisplayRows (window, m_rows, g_options);
3520
3521 window.DeferredRefresh();
3522
3523 // Get the selected row
3524 m_selected_row = GetRowForRowIndex (m_selected_row_idx);
3525 // Keep the cursor on the selected row so the highlight and the cursor
3526 // are always on the same line
3527 if (m_selected_row)
3528 window.MoveCursor (m_selected_row->x,
3529 m_selected_row->y);
3530
3531 return true; // Drawing handled
3532 }
3533
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00003534 KeyHelp *
3535 WindowDelegateGetKeyHelp () override
Greg Clayton44d93782014-01-27 23:43:24 +00003536 {
3537 static curses::KeyHelp g_source_view_key_help[] = {
3538 { KEY_UP, "Select previous item" },
3539 { KEY_DOWN, "Select next item" },
3540 { KEY_RIGHT, "Expand selected item" },
3541 { KEY_LEFT, "Unexpand selected item or select parent if not expanded" },
3542 { KEY_PPAGE, "Page up" },
3543 { KEY_NPAGE, "Page down" },
3544 { 'A', "Format as annotated address" },
3545 { 'b', "Format as binary" },
3546 { 'B', "Format as hex bytes with ASCII" },
3547 { 'c', "Format as character" },
3548 { 'd', "Format as a signed integer" },
3549 { 'D', "Format selected value using the default format for the type" },
3550 { 'f', "Format as float" },
3551 { 'h', "Show help dialog" },
3552 { 'i', "Format as instructions" },
3553 { 'o', "Format as octal" },
3554 { 'p', "Format as pointer" },
3555 { 's', "Format as C string" },
3556 { 't', "Toggle showing/hiding type names" },
3557 { 'u', "Format as an unsigned integer" },
3558 { 'x', "Format as hex" },
3559 { 'X', "Format as uppercase hex" },
3560 { ' ', "Toggle item expansion" },
3561 { ',', "Page up" },
3562 { '.', "Page down" },
3563 { '\0', NULL }
3564 };
3565 return g_source_view_key_help;
3566 }
3567
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00003568 HandleCharResult
3569 WindowDelegateHandleChar (Window &window, int c) override
Greg Clayton44d93782014-01-27 23:43:24 +00003570 {
3571 switch(c)
3572 {
3573 case 'x':
3574 case 'X':
3575 case 'o':
3576 case 's':
3577 case 'u':
3578 case 'd':
3579 case 'D':
3580 case 'i':
3581 case 'A':
3582 case 'p':
3583 case 'c':
3584 case 'b':
3585 case 'B':
3586 case 'f':
3587 // Change the format for the currently selected item
3588 if (m_selected_row)
3589 m_selected_row->valobj->SetFormat (FormatForChar (c));
3590 return eKeyHandled;
3591
3592 case 't':
3593 // Toggle showing type names
3594 g_options.show_types = !g_options.show_types;
3595 return eKeyHandled;
3596
3597 case ',':
3598 case KEY_PPAGE:
3599 // Page up key
3600 if (m_first_visible_row > 0)
3601 {
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00003602 if (static_cast<int>(m_first_visible_row) > m_max_y)
Greg Clayton44d93782014-01-27 23:43:24 +00003603 m_first_visible_row -= m_max_y;
3604 else
3605 m_first_visible_row = 0;
3606 m_selected_row_idx = m_first_visible_row;
3607 }
3608 return eKeyHandled;
3609
3610 case '.':
3611 case KEY_NPAGE:
3612 // Page down key
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00003613 if (m_num_rows > static_cast<size_t>(m_max_y))
Greg Clayton44d93782014-01-27 23:43:24 +00003614 {
3615 if (m_first_visible_row + m_max_y < m_num_rows)
3616 {
3617 m_first_visible_row += m_max_y;
3618 m_selected_row_idx = m_first_visible_row;
3619 }
3620 }
3621 return eKeyHandled;
3622
3623 case KEY_UP:
3624 if (m_selected_row_idx > 0)
3625 --m_selected_row_idx;
3626 return eKeyHandled;
Eugene Zelenko315b6882015-10-26 17:00:13 +00003627
Greg Clayton44d93782014-01-27 23:43:24 +00003628 case KEY_DOWN:
3629 if (m_selected_row_idx + 1 < m_num_rows)
3630 ++m_selected_row_idx;
3631 return eKeyHandled;
3632
3633 case KEY_RIGHT:
3634 if (m_selected_row)
3635 {
3636 if (!m_selected_row->expanded)
3637 m_selected_row->Expand();
3638 }
3639 return eKeyHandled;
3640
3641 case KEY_LEFT:
3642 if (m_selected_row)
3643 {
3644 if (m_selected_row->expanded)
3645 m_selected_row->Unexpand();
3646 else if (m_selected_row->parent)
3647 m_selected_row_idx = m_selected_row->parent->row_idx;
3648 }
3649 return eKeyHandled;
3650
3651 case ' ':
3652 // Toggle expansion state when SPACE is pressed
3653 if (m_selected_row)
3654 {
3655 if (m_selected_row->expanded)
3656 m_selected_row->Unexpand();
3657 else
3658 m_selected_row->Expand();
3659 }
3660 return eKeyHandled;
3661
3662 case 'h':
3663 window.CreateHelpSubwindow ();
3664 return eKeyHandled;
3665
3666 default:
3667 break;
3668 }
3669 return eKeyNotHandled;
3670 }
3671
3672protected:
3673 ValueObjectList m_valobj_list;
3674 std::vector<Row> m_rows;
3675 Row *m_selected_row;
3676 uint32_t m_selected_row_idx;
3677 uint32_t m_first_visible_row;
3678 uint32_t m_num_rows;
3679 int m_min_x;
3680 int m_min_y;
3681 int m_max_x;
3682 int m_max_y;
3683
3684 static Format
3685 FormatForChar (int c)
3686 {
3687 switch (c)
3688 {
3689 case 'x': return eFormatHex;
3690 case 'X': return eFormatHexUppercase;
3691 case 'o': return eFormatOctal;
3692 case 's': return eFormatCString;
3693 case 'u': return eFormatUnsigned;
3694 case 'd': return eFormatDecimal;
3695 case 'D': return eFormatDefault;
3696 case 'i': return eFormatInstruction;
3697 case 'A': return eFormatAddressInfo;
3698 case 'p': return eFormatPointer;
3699 case 'c': return eFormatChar;
3700 case 'b': return eFormatBinary;
3701 case 'B': return eFormatBytesWithASCII;
3702 case 'f': return eFormatFloat;
3703 }
3704 return eFormatDefault;
3705 }
3706
3707 bool
3708 DisplayRowObject (Window &window,
3709 Row &row,
3710 DisplayOptions &options,
3711 bool highlight,
3712 bool last_child)
3713 {
3714 ValueObject *valobj = row.valobj.get();
3715
3716 if (valobj == NULL)
3717 return false;
3718
3719 const char *type_name = options.show_types ? valobj->GetTypeName().GetCString() : NULL;
3720 const char *name = valobj->GetName().GetCString();
3721 const char *value = valobj->GetValueAsCString ();
3722 const char *summary = valobj->GetSummaryAsCString ();
3723
3724 window.MoveCursor (row.x, row.y);
3725
3726 row.DrawTree (window);
3727
3728 if (highlight)
3729 window.AttributeOn(A_REVERSE);
3730
3731 if (type_name && type_name[0])
3732 window.Printf ("(%s) ", type_name);
3733
3734 if (name && name[0])
3735 window.PutCString(name);
3736
3737 attr_t changd_attr = 0;
3738 if (valobj->GetValueDidChange())
3739 changd_attr = COLOR_PAIR(5) | A_BOLD;
3740
3741 if (value && value[0])
3742 {
3743 window.PutCString(" = ");
3744 if (changd_attr)
3745 window.AttributeOn(changd_attr);
3746 window.PutCString (value);
3747 if (changd_attr)
3748 window.AttributeOff(changd_attr);
3749 }
3750
3751 if (summary && summary[0])
3752 {
3753 window.PutChar(' ');
3754 if (changd_attr)
3755 window.AttributeOn(changd_attr);
3756 window.PutCString(summary);
3757 if (changd_attr)
3758 window.AttributeOff(changd_attr);
3759 }
3760
3761 if (highlight)
3762 window.AttributeOff (A_REVERSE);
3763
3764 return true;
3765 }
Eugene Zelenko315b6882015-10-26 17:00:13 +00003766
Greg Clayton44d93782014-01-27 23:43:24 +00003767 void
3768 DisplayRows (Window &window,
3769 std::vector<Row> &rows,
3770 DisplayOptions &options)
3771 {
3772 // > 0x25B7
3773 // \/ 0x25BD
3774
3775 bool window_is_active = window.IsActive();
3776 for (auto &row : rows)
3777 {
3778 const bool last_child = row.parent && &rows[rows.size()-1] == &row;
3779 // Save the row index in each Row structure
3780 row.row_idx = m_num_rows;
3781 if ((m_num_rows >= m_first_visible_row) &&
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00003782 ((m_num_rows - m_first_visible_row) < static_cast<size_t>(NumVisibleRows())))
Greg Clayton44d93782014-01-27 23:43:24 +00003783 {
3784 row.x = m_min_x;
3785 row.y = m_num_rows - m_first_visible_row + 1;
3786 if (DisplayRowObject (window,
3787 row,
3788 options,
3789 window_is_active && m_num_rows == m_selected_row_idx,
3790 last_child))
3791 {
3792 ++m_num_rows;
3793 }
3794 else
3795 {
3796 row.x = 0;
3797 row.y = 0;
3798 }
3799 }
3800 else
3801 {
3802 row.x = 0;
3803 row.y = 0;
3804 ++m_num_rows;
3805 }
3806
3807 if (row.expanded && !row.children.empty())
3808 {
3809 DisplayRows (window,
3810 row.children,
3811 options);
3812 }
3813 }
3814 }
3815
3816 int
3817 CalculateTotalNumberRows (const std::vector<Row> &rows)
3818 {
3819 int row_count = 0;
3820 for (const auto &row : rows)
3821 {
3822 ++row_count;
3823 if (row.expanded)
3824 row_count += CalculateTotalNumberRows(row.children);
3825 }
3826 return row_count;
3827 }
Eugene Zelenko315b6882015-10-26 17:00:13 +00003828
Greg Clayton44d93782014-01-27 23:43:24 +00003829 static Row *
3830 GetRowForRowIndexImpl (std::vector<Row> &rows, size_t &row_index)
3831 {
3832 for (auto &row : rows)
3833 {
3834 if (row_index == 0)
3835 return &row;
3836 else
3837 {
3838 --row_index;
3839 if (row.expanded && !row.children.empty())
3840 {
3841 Row *result = GetRowForRowIndexImpl (row.children, row_index);
3842 if (result)
3843 return result;
3844 }
3845 }
3846 }
3847 return NULL;
3848 }
3849
3850 Row *
3851 GetRowForRowIndex (size_t row_index)
3852 {
3853 return GetRowForRowIndexImpl (m_rows, row_index);
3854 }
3855
3856 int
3857 NumVisibleRows () const
3858 {
3859 return m_max_y - m_min_y;
3860 }
3861
3862 static DisplayOptions g_options;
3863};
3864
3865class FrameVariablesWindowDelegate : public ValueObjectListDelegate
3866{
3867public:
3868 FrameVariablesWindowDelegate (Debugger &debugger) :
3869 ValueObjectListDelegate (),
3870 m_debugger (debugger),
3871 m_frame_block (NULL)
3872 {
3873 }
Eugene Zelenko315b6882015-10-26 17:00:13 +00003874
3875 ~FrameVariablesWindowDelegate() override = default;
3876
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00003877 const char *
3878 WindowDelegateGetHelpText () override
Greg Clayton44d93782014-01-27 23:43:24 +00003879 {
3880 return "Frame variable window keyboard shortcuts:";
3881 }
3882
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00003883 bool
3884 WindowDelegateDraw (Window &window, bool force) override
Greg Clayton44d93782014-01-27 23:43:24 +00003885 {
3886 ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext());
3887 Process *process = exe_ctx.GetProcessPtr();
3888 Block *frame_block = NULL;
3889 StackFrame *frame = NULL;
3890
3891 if (process)
3892 {
3893 StateType state = process->GetState();
3894 if (StateIsStoppedState(state, true))
3895 {
3896 frame = exe_ctx.GetFramePtr();
3897 if (frame)
3898 frame_block = frame->GetFrameBlock ();
3899 }
3900 else if (StateIsRunningState(state))
3901 {
3902 return true; // Don't do any updating when we are running
3903 }
3904 }
Greg Claytoneb72dc72015-04-10 21:34:10 +00003905
Greg Clayton44d93782014-01-27 23:43:24 +00003906 ValueObjectList local_values;
3907 if (frame_block)
3908 {
3909 // Only update the variables if they have changed
3910 if (m_frame_block != frame_block)
3911 {
3912 m_frame_block = frame_block;
3913
3914 VariableList *locals = frame->GetVariableList(true);
3915 if (locals)
3916 {
3917 const DynamicValueType use_dynamic = eDynamicDontRunTarget;
3918 const size_t num_locals = locals->GetSize();
3919 for (size_t i=0; i<num_locals; ++i)
Greg Claytoneb72dc72015-04-10 21:34:10 +00003920 {
3921 ValueObjectSP value_sp = frame->GetValueObjectForFrameVariable (locals->GetVariableAtIndex(i), use_dynamic);
3922 if (value_sp)
3923 {
3924 ValueObjectSP synthetic_value_sp = value_sp->GetSyntheticValue();
3925 if (synthetic_value_sp)
3926 local_values.Append(synthetic_value_sp);
3927 else
3928 local_values.Append(value_sp);
3929
3930 }
3931 }
Greg Clayton44d93782014-01-27 23:43:24 +00003932 // Update the values
3933 SetValues(local_values);
3934 }
3935 }
3936 }
3937 else
3938 {
3939 m_frame_block = NULL;
3940 // Update the values with an empty list if there is no frame
3941 SetValues(local_values);
3942 }
3943
3944 return ValueObjectListDelegate::WindowDelegateDraw (window, force);
Greg Clayton44d93782014-01-27 23:43:24 +00003945 }
3946
3947protected:
3948 Debugger &m_debugger;
3949 Block *m_frame_block;
3950};
3951
Greg Clayton44d93782014-01-27 23:43:24 +00003952class RegistersWindowDelegate : public ValueObjectListDelegate
3953{
3954public:
3955 RegistersWindowDelegate (Debugger &debugger) :
3956 ValueObjectListDelegate (),
3957 m_debugger (debugger)
3958 {
3959 }
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00003960
Eugene Zelenko315b6882015-10-26 17:00:13 +00003961 ~RegistersWindowDelegate() override = default;
3962
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00003963 const char *
3964 WindowDelegateGetHelpText () override
Greg Clayton44d93782014-01-27 23:43:24 +00003965 {
3966 return "Register window keyboard shortcuts:";
3967 }
3968
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00003969 bool
3970 WindowDelegateDraw (Window &window, bool force) override
Greg Clayton44d93782014-01-27 23:43:24 +00003971 {
3972 ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext());
3973 StackFrame *frame = exe_ctx.GetFramePtr();
3974
3975 ValueObjectList value_list;
3976 if (frame)
3977 {
3978 if (frame->GetStackID() != m_stack_id)
3979 {
3980 m_stack_id = frame->GetStackID();
3981 RegisterContextSP reg_ctx (frame->GetRegisterContext());
3982 if (reg_ctx)
3983 {
3984 const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
3985 for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx)
3986 {
3987 value_list.Append(ValueObjectRegisterSet::Create (frame, reg_ctx, set_idx));
3988 }
3989 }
3990 SetValues(value_list);
3991 }
3992 }
3993 else
3994 {
3995 Process *process = exe_ctx.GetProcessPtr();
3996 if (process && process->IsAlive())
3997 return true; // Don't do any updating if we are running
3998 else
3999 {
4000 // Update the values with an empty list if there
4001 // is no process or the process isn't alive anymore
4002 SetValues(value_list);
4003 }
4004 }
4005 return ValueObjectListDelegate::WindowDelegateDraw (window, force);
4006 }
4007
4008protected:
4009 Debugger &m_debugger;
4010 StackID m_stack_id;
4011};
4012
4013static const char *
4014CursesKeyToCString (int ch)
4015{
4016 static char g_desc[32];
4017 if (ch >= KEY_F0 && ch < KEY_F0 + 64)
4018 {
4019 snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0);
4020 return g_desc;
4021 }
4022 switch (ch)
4023 {
4024 case KEY_DOWN: return "down";
4025 case KEY_UP: return "up";
4026 case KEY_LEFT: return "left";
4027 case KEY_RIGHT: return "right";
4028 case KEY_HOME: return "home";
4029 case KEY_BACKSPACE: return "backspace";
4030 case KEY_DL: return "delete-line";
4031 case KEY_IL: return "insert-line";
4032 case KEY_DC: return "delete-char";
4033 case KEY_IC: return "insert-char";
4034 case KEY_CLEAR: return "clear";
4035 case KEY_EOS: return "clear-to-eos";
4036 case KEY_EOL: return "clear-to-eol";
4037 case KEY_SF: return "scroll-forward";
4038 case KEY_SR: return "scroll-backward";
4039 case KEY_NPAGE: return "page-down";
4040 case KEY_PPAGE: return "page-up";
4041 case KEY_STAB: return "set-tab";
4042 case KEY_CTAB: return "clear-tab";
4043 case KEY_CATAB: return "clear-all-tabs";
4044 case KEY_ENTER: return "enter";
4045 case KEY_PRINT: return "print";
4046 case KEY_LL: return "lower-left key";
4047 case KEY_A1: return "upper left of keypad";
4048 case KEY_A3: return "upper right of keypad";
4049 case KEY_B2: return "center of keypad";
4050 case KEY_C1: return "lower left of keypad";
4051 case KEY_C3: return "lower right of keypad";
4052 case KEY_BTAB: return "back-tab key";
4053 case KEY_BEG: return "begin key";
4054 case KEY_CANCEL: return "cancel key";
4055 case KEY_CLOSE: return "close key";
4056 case KEY_COMMAND: return "command key";
4057 case KEY_COPY: return "copy key";
4058 case KEY_CREATE: return "create key";
4059 case KEY_END: return "end key";
4060 case KEY_EXIT: return "exit key";
4061 case KEY_FIND: return "find key";
4062 case KEY_HELP: return "help key";
4063 case KEY_MARK: return "mark key";
4064 case KEY_MESSAGE: return "message key";
4065 case KEY_MOVE: return "move key";
4066 case KEY_NEXT: return "next key";
4067 case KEY_OPEN: return "open key";
4068 case KEY_OPTIONS: return "options key";
4069 case KEY_PREVIOUS: return "previous key";
4070 case KEY_REDO: return "redo key";
4071 case KEY_REFERENCE: return "reference key";
4072 case KEY_REFRESH: return "refresh key";
4073 case KEY_REPLACE: return "replace key";
4074 case KEY_RESTART: return "restart key";
4075 case KEY_RESUME: return "resume key";
4076 case KEY_SAVE: return "save key";
4077 case KEY_SBEG: return "shifted begin key";
4078 case KEY_SCANCEL: return "shifted cancel key";
4079 case KEY_SCOMMAND: return "shifted command key";
4080 case KEY_SCOPY: return "shifted copy key";
4081 case KEY_SCREATE: return "shifted create key";
4082 case KEY_SDC: return "shifted delete-character key";
4083 case KEY_SDL: return "shifted delete-line key";
4084 case KEY_SELECT: return "select key";
4085 case KEY_SEND: return "shifted end key";
4086 case KEY_SEOL: return "shifted clear-to-end-of-line key";
4087 case KEY_SEXIT: return "shifted exit key";
4088 case KEY_SFIND: return "shifted find key";
4089 case KEY_SHELP: return "shifted help key";
4090 case KEY_SHOME: return "shifted home key";
4091 case KEY_SIC: return "shifted insert-character key";
4092 case KEY_SLEFT: return "shifted left-arrow key";
4093 case KEY_SMESSAGE: return "shifted message key";
4094 case KEY_SMOVE: return "shifted move key";
4095 case KEY_SNEXT: return "shifted next key";
4096 case KEY_SOPTIONS: return "shifted options key";
4097 case KEY_SPREVIOUS: return "shifted previous key";
4098 case KEY_SPRINT: return "shifted print key";
4099 case KEY_SREDO: return "shifted redo key";
4100 case KEY_SREPLACE: return "shifted replace key";
4101 case KEY_SRIGHT: return "shifted right-arrow key";
4102 case KEY_SRSUME: return "shifted resume key";
4103 case KEY_SSAVE: return "shifted save key";
4104 case KEY_SSUSPEND: return "shifted suspend key";
4105 case KEY_SUNDO: return "shifted undo key";
4106 case KEY_SUSPEND: return "suspend key";
4107 case KEY_UNDO: return "undo key";
4108 case KEY_MOUSE: return "Mouse event has occurred";
4109 case KEY_RESIZE: return "Terminal resize event";
Bruce Mitchener27801f42015-11-06 00:21:18 +00004110#ifdef KEY_EVENT
Greg Clayton44d93782014-01-27 23:43:24 +00004111 case KEY_EVENT: return "We were interrupted by an event";
Bruce Mitchener27801f42015-11-06 00:21:18 +00004112#endif
Greg Clayton44d93782014-01-27 23:43:24 +00004113 case KEY_RETURN: return "return";
4114 case ' ': return "space";
Greg Clayton5fdb09b2014-01-28 18:41:35 +00004115 case '\t': return "tab";
Greg Clayton44d93782014-01-27 23:43:24 +00004116 case KEY_ESCAPE: return "escape";
4117 default:
4118 if (isprint(ch))
4119 snprintf(g_desc, sizeof(g_desc), "%c", ch);
4120 else
4121 snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch);
4122 return g_desc;
4123 }
4124 return NULL;
4125}
4126
4127HelpDialogDelegate::HelpDialogDelegate (const char *text, KeyHelp *key_help_array) :
4128 m_text (),
4129 m_first_visible_line (0)
4130{
4131 if (text && text[0])
4132 {
4133 m_text.SplitIntoLines(text);
4134 m_text.AppendString("");
4135 }
4136 if (key_help_array)
4137 {
4138 for (KeyHelp *key = key_help_array; key->ch; ++key)
4139 {
4140 StreamString key_description;
4141 key_description.Printf("%10s - %s", CursesKeyToCString(key->ch), key->description);
4142 m_text.AppendString(std::move(key_description.GetString()));
4143 }
4144 }
4145}
4146
Eugene Zelenko315b6882015-10-26 17:00:13 +00004147HelpDialogDelegate::~HelpDialogDelegate() = default;
Greg Clayton44d93782014-01-27 23:43:24 +00004148
4149bool
4150HelpDialogDelegate::WindowDelegateDraw (Window &window, bool force)
4151{
4152 window.Erase();
4153 const int window_height = window.GetHeight();
4154 int x = 2;
4155 int y = 1;
4156 const int min_y = y;
4157 const int max_y = window_height - 1 - y;
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00004158 const size_t num_visible_lines = max_y - min_y + 1;
Greg Clayton44d93782014-01-27 23:43:24 +00004159 const size_t num_lines = m_text.GetSize();
4160 const char *bottom_message;
4161 if (num_lines <= num_visible_lines)
4162 bottom_message = "Press any key to exit";
4163 else
4164 bottom_message = "Use arrows to scroll, any other key to exit";
4165 window.DrawTitleBox(window.GetName(), bottom_message);
4166 while (y <= max_y)
4167 {
4168 window.MoveCursor(x, y);
4169 window.PutCStringTruncated(m_text.GetStringAtIndex(m_first_visible_line + y - min_y), 1);
4170 ++y;
4171 }
4172 return true;
4173}
4174
4175HandleCharResult
4176HelpDialogDelegate::WindowDelegateHandleChar (Window &window, int key)
4177{
4178 bool done = false;
4179 const size_t num_lines = m_text.GetSize();
4180 const size_t num_visible_lines = window.GetHeight() - 2;
4181
4182 if (num_lines <= num_visible_lines)
4183 {
4184 done = true;
4185 // If we have all lines visible and don't need scrolling, then any
4186 // key press will cause us to exit
4187 }
4188 else
4189 {
4190 switch (key)
4191 {
4192 case KEY_UP:
4193 if (m_first_visible_line > 0)
4194 --m_first_visible_line;
4195 break;
4196
4197 case KEY_DOWN:
4198 if (m_first_visible_line + num_visible_lines < num_lines)
4199 ++m_first_visible_line;
4200 break;
4201
4202 case KEY_PPAGE:
4203 case ',':
4204 if (m_first_visible_line > 0)
4205 {
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00004206 if (static_cast<size_t>(m_first_visible_line) >= num_visible_lines)
Greg Clayton44d93782014-01-27 23:43:24 +00004207 m_first_visible_line -= num_visible_lines;
4208 else
4209 m_first_visible_line = 0;
4210 }
4211 break;
Eugene Zelenko315b6882015-10-26 17:00:13 +00004212
Greg Clayton44d93782014-01-27 23:43:24 +00004213 case KEY_NPAGE:
4214 case '.':
4215 if (m_first_visible_line + num_visible_lines < num_lines)
4216 {
4217 m_first_visible_line += num_visible_lines;
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00004218 if (static_cast<size_t>(m_first_visible_line) > num_lines)
Greg Clayton44d93782014-01-27 23:43:24 +00004219 m_first_visible_line = num_lines - num_visible_lines;
4220 }
4221 break;
Eugene Zelenko315b6882015-10-26 17:00:13 +00004222
Greg Clayton44d93782014-01-27 23:43:24 +00004223 default:
4224 done = true;
4225 break;
4226 }
4227 }
4228 if (done)
4229 window.GetParent()->RemoveSubWindow(&window);
4230 return eKeyHandled;
4231}
4232
4233class ApplicationDelegate :
4234 public WindowDelegate,
4235 public MenuDelegate
4236{
4237public:
4238 enum {
4239 eMenuID_LLDB = 1,
4240 eMenuID_LLDBAbout,
4241 eMenuID_LLDBExit,
4242
4243 eMenuID_Target,
4244 eMenuID_TargetCreate,
4245 eMenuID_TargetDelete,
4246
4247 eMenuID_Process,
4248 eMenuID_ProcessAttach,
4249 eMenuID_ProcessDetach,
4250 eMenuID_ProcessLaunch,
4251 eMenuID_ProcessContinue,
4252 eMenuID_ProcessHalt,
4253 eMenuID_ProcessKill,
4254
4255 eMenuID_Thread,
4256 eMenuID_ThreadStepIn,
4257 eMenuID_ThreadStepOver,
4258 eMenuID_ThreadStepOut,
4259
4260 eMenuID_View,
4261 eMenuID_ViewBacktrace,
4262 eMenuID_ViewRegisters,
4263 eMenuID_ViewSource,
4264 eMenuID_ViewVariables,
4265
4266 eMenuID_Help,
4267 eMenuID_HelpGUIHelp
4268 };
4269
4270 ApplicationDelegate (Application &app, Debugger &debugger) :
4271 WindowDelegate (),
4272 MenuDelegate (),
4273 m_app (app),
4274 m_debugger (debugger)
4275 {
4276 }
Eugene Zelenko315b6882015-10-26 17:00:13 +00004277
4278 ~ApplicationDelegate() override = default;
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00004279
4280 bool
4281 WindowDelegateDraw (Window &window, bool force) override
Greg Clayton44d93782014-01-27 23:43:24 +00004282 {
4283 return false; // Drawing not handled, let standard window drawing happen
4284 }
Greg Clayton5fdb09b2014-01-28 18:41:35 +00004285
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00004286 HandleCharResult
4287 WindowDelegateHandleChar (Window &window, int key) override
Greg Clayton44d93782014-01-27 23:43:24 +00004288 {
Greg Clayton5fdb09b2014-01-28 18:41:35 +00004289 switch (key)
Greg Clayton44d93782014-01-27 23:43:24 +00004290 {
Greg Clayton5fdb09b2014-01-28 18:41:35 +00004291 case '\t':
4292 window.SelectNextWindowAsActive();
4293 return eKeyHandled;
4294
4295 case 'h':
4296 window.CreateHelpSubwindow();
4297 return eKeyHandled;
4298
4299 case KEY_ESCAPE:
4300 return eQuitApplication;
4301
4302 default:
4303 break;
Greg Clayton44d93782014-01-27 23:43:24 +00004304 }
4305 return eKeyNotHandled;
4306 }
Eugene Zelenko315b6882015-10-26 17:00:13 +00004307
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00004308 const char *
4309 WindowDelegateGetHelpText () override
Greg Clayton5fdb09b2014-01-28 18:41:35 +00004310 {
4311 return "Welcome to the LLDB curses GUI.\n\n"
4312 "Press the TAB key to change the selected view.\n"
4313 "Each view has its own keyboard shortcuts, press 'h' to open a dialog to display them.\n\n"
4314 "Common key bindings for all views:";
4315 }
4316
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00004317 KeyHelp *
4318 WindowDelegateGetKeyHelp () override
Greg Clayton5fdb09b2014-01-28 18:41:35 +00004319 {
4320 static curses::KeyHelp g_source_view_key_help[] = {
4321 { '\t', "Select next view" },
4322 { 'h', "Show help dialog with view specific key bindings" },
4323 { ',', "Page up" },
4324 { '.', "Page down" },
4325 { KEY_UP, "Select previous" },
4326 { KEY_DOWN, "Select next" },
4327 { KEY_LEFT, "Unexpand or select parent" },
4328 { KEY_RIGHT, "Expand" },
4329 { KEY_PPAGE, "Page up" },
4330 { KEY_NPAGE, "Page down" },
4331 { '\0', NULL }
4332 };
4333 return g_source_view_key_help;
4334 }
4335
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00004336 MenuActionResult
4337 MenuDelegateAction (Menu &menu) override
Greg Clayton44d93782014-01-27 23:43:24 +00004338 {
4339 switch (menu.GetIdentifier())
4340 {
4341 case eMenuID_ThreadStepIn:
4342 {
4343 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4344 if (exe_ctx.HasThreadScope())
4345 {
4346 Process *process = exe_ctx.GetProcessPtr();
4347 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
Jim Ingham4b4b2472014-03-13 02:47:14 +00004348 exe_ctx.GetThreadRef().StepIn(true);
Greg Clayton44d93782014-01-27 23:43:24 +00004349 }
4350 }
4351 return MenuActionResult::Handled;
4352
4353 case eMenuID_ThreadStepOut:
4354 {
4355 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4356 if (exe_ctx.HasThreadScope())
4357 {
4358 Process *process = exe_ctx.GetProcessPtr();
4359 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4360 exe_ctx.GetThreadRef().StepOut();
4361 }
4362 }
4363 return MenuActionResult::Handled;
4364
4365 case eMenuID_ThreadStepOver:
4366 {
4367 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4368 if (exe_ctx.HasThreadScope())
4369 {
4370 Process *process = exe_ctx.GetProcessPtr();
4371 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4372 exe_ctx.GetThreadRef().StepOver(true);
4373 }
4374 }
4375 return MenuActionResult::Handled;
4376
4377 case eMenuID_ProcessContinue:
4378 {
4379 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4380 if (exe_ctx.HasProcessScope())
4381 {
4382 Process *process = exe_ctx.GetProcessPtr();
4383 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4384 process->Resume();
4385 }
4386 }
4387 return MenuActionResult::Handled;
4388
4389 case eMenuID_ProcessKill:
4390 {
4391 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4392 if (exe_ctx.HasProcessScope())
4393 {
4394 Process *process = exe_ctx.GetProcessPtr();
4395 if (process && process->IsAlive())
Jason Molendaede31932015-04-17 05:01:58 +00004396 process->Destroy(false);
Greg Clayton44d93782014-01-27 23:43:24 +00004397 }
4398 }
4399 return MenuActionResult::Handled;
4400
4401 case eMenuID_ProcessHalt:
4402 {
4403 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4404 if (exe_ctx.HasProcessScope())
4405 {
4406 Process *process = exe_ctx.GetProcessPtr();
4407 if (process && process->IsAlive())
4408 process->Halt();
4409 }
4410 }
4411 return MenuActionResult::Handled;
4412
4413 case eMenuID_ProcessDetach:
4414 {
4415 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4416 if (exe_ctx.HasProcessScope())
4417 {
4418 Process *process = exe_ctx.GetProcessPtr();
4419 if (process && process->IsAlive())
4420 process->Detach(false);
4421 }
4422 }
4423 return MenuActionResult::Handled;
4424
4425 case eMenuID_Process:
4426 {
4427 // Populate the menu with all of the threads if the process is stopped when
4428 // the Process menu gets selected and is about to display its submenu.
4429 Menus &submenus = menu.GetSubmenus();
4430 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4431 Process *process = exe_ctx.GetProcessPtr();
4432 if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true))
4433 {
4434 if (submenus.size() == 7)
4435 menu.AddSubmenu (MenuSP (new Menu(Menu::Type::Separator)));
4436 else if (submenus.size() > 8)
4437 submenus.erase (submenus.begin() + 8, submenus.end());
4438
4439 ThreadList &threads = process->GetThreadList();
4440 Mutex::Locker locker (threads.GetMutex());
4441 size_t num_threads = threads.GetSize();
4442 for (size_t i=0; i<num_threads; ++i)
4443 {
4444 ThreadSP thread_sp = threads.GetThreadAtIndex(i);
4445 char menu_char = '\0';
4446 if (i < 9)
4447 menu_char = '1' + i;
4448 StreamString thread_menu_title;
4449 thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID());
4450 const char *thread_name = thread_sp->GetName();
4451 if (thread_name && thread_name[0])
4452 thread_menu_title.Printf (" %s", thread_name);
4453 else
4454 {
4455 const char *queue_name = thread_sp->GetQueueName();
4456 if (queue_name && queue_name[0])
4457 thread_menu_title.Printf (" %s", queue_name);
4458 }
4459 menu.AddSubmenu (MenuSP (new Menu(thread_menu_title.GetString().c_str(), NULL, menu_char, thread_sp->GetID())));
4460 }
4461 }
4462 else if (submenus.size() > 7)
4463 {
4464 // Remove the separator and any other thread submenu items
4465 // that were previously added
4466 submenus.erase (submenus.begin() + 7, submenus.end());
4467 }
4468 // Since we are adding and removing items we need to recalculate the name lengths
4469 menu.RecalculateNameLengths();
4470 }
4471 return MenuActionResult::Handled;
4472
4473 case eMenuID_ViewVariables:
4474 {
4475 WindowSP main_window_sp = m_app.GetMainWindow();
4476 WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
4477 WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
4478 WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
4479 const Rect source_bounds = source_window_sp->GetBounds();
4480
4481 if (variables_window_sp)
4482 {
4483 const Rect variables_bounds = variables_window_sp->GetBounds();
4484
4485 main_window_sp->RemoveSubWindow(variables_window_sp.get());
4486
4487 if (registers_window_sp)
4488 {
4489 // We have a registers window, so give all the area back to the registers window
4490 Rect registers_bounds = variables_bounds;
4491 registers_bounds.size.width = source_bounds.size.width;
4492 registers_window_sp->SetBounds(registers_bounds);
4493 }
4494 else
4495 {
4496 // We have no registers window showing so give the bottom
4497 // area back to the source view
4498 source_window_sp->Resize (source_bounds.size.width,
4499 source_bounds.size.height + variables_bounds.size.height);
4500 }
4501 }
4502 else
4503 {
4504 Rect new_variables_rect;
4505 if (registers_window_sp)
4506 {
4507 // We have a registers window so split the area of the registers
4508 // window into two columns where the left hand side will be the
4509 // variables and the right hand side will be the registers
4510 const Rect variables_bounds = registers_window_sp->GetBounds();
4511 Rect new_registers_rect;
4512 variables_bounds.VerticalSplitPercentage (0.50, new_variables_rect, new_registers_rect);
4513 registers_window_sp->SetBounds (new_registers_rect);
4514 }
4515 else
4516 {
4517 // No variables window, grab the bottom part of the source window
4518 Rect new_source_rect;
4519 source_bounds.HorizontalSplitPercentage (0.70, new_source_rect, new_variables_rect);
4520 source_window_sp->SetBounds (new_source_rect);
4521 }
4522 WindowSP new_window_sp = main_window_sp->CreateSubWindow ("Variables",
4523 new_variables_rect,
4524 false);
4525 new_window_sp->SetDelegate (WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
4526 }
4527 touchwin(stdscr);
4528 }
4529 return MenuActionResult::Handled;
4530
4531 case eMenuID_ViewRegisters:
4532 {
4533 WindowSP main_window_sp = m_app.GetMainWindow();
4534 WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
4535 WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
4536 WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
4537 const Rect source_bounds = source_window_sp->GetBounds();
4538
4539 if (registers_window_sp)
4540 {
4541 if (variables_window_sp)
4542 {
4543 const Rect variables_bounds = variables_window_sp->GetBounds();
4544
4545 // We have a variables window, so give all the area back to the variables window
4546 variables_window_sp->Resize (variables_bounds.size.width + registers_window_sp->GetWidth(),
4547 variables_bounds.size.height);
4548 }
4549 else
4550 {
4551 // We have no variables window showing so give the bottom
4552 // area back to the source view
4553 source_window_sp->Resize (source_bounds.size.width,
4554 source_bounds.size.height + registers_window_sp->GetHeight());
4555 }
4556 main_window_sp->RemoveSubWindow(registers_window_sp.get());
4557 }
4558 else
4559 {
4560 Rect new_regs_rect;
4561 if (variables_window_sp)
4562 {
4563 // We have a variables window, split it into two columns
4564 // where the left hand side will be the variables and the
4565 // right hand side will be the registers
4566 const Rect variables_bounds = variables_window_sp->GetBounds();
4567 Rect new_vars_rect;
4568 variables_bounds.VerticalSplitPercentage (0.50, new_vars_rect, new_regs_rect);
4569 variables_window_sp->SetBounds (new_vars_rect);
4570 }
4571 else
4572 {
4573 // No registers window, grab the bottom part of the source window
4574 Rect new_source_rect;
4575 source_bounds.HorizontalSplitPercentage (0.70, new_source_rect, new_regs_rect);
4576 source_window_sp->SetBounds (new_source_rect);
4577 }
4578 WindowSP new_window_sp = main_window_sp->CreateSubWindow ("Registers",
4579 new_regs_rect,
4580 false);
4581 new_window_sp->SetDelegate (WindowDelegateSP(new RegistersWindowDelegate(m_debugger)));
4582 }
4583 touchwin(stdscr);
4584 }
4585 return MenuActionResult::Handled;
4586
4587 case eMenuID_HelpGUIHelp:
Greg Clayton5fdb09b2014-01-28 18:41:35 +00004588 m_app.GetMainWindow ()->CreateHelpSubwindow();
Greg Clayton44d93782014-01-27 23:43:24 +00004589 return MenuActionResult::Handled;
4590
4591 default:
4592 break;
4593 }
4594
4595 return MenuActionResult::NotHandled;
4596 }
4597protected:
4598 Application &m_app;
4599 Debugger &m_debugger;
4600};
4601
Greg Clayton44d93782014-01-27 23:43:24 +00004602class StatusBarWindowDelegate : public WindowDelegate
4603{
4604public:
4605 StatusBarWindowDelegate (Debugger &debugger) :
4606 m_debugger (debugger)
4607 {
Greg Clayton554f68d2015-02-04 22:00:53 +00004608 FormatEntity::Parse("Thread: ${thread.id%tid}",
4609 m_format);
Greg Clayton44d93782014-01-27 23:43:24 +00004610 }
Eugene Zelenko315b6882015-10-26 17:00:13 +00004611
4612 ~StatusBarWindowDelegate() override = default;
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00004613
4614 bool
4615 WindowDelegateDraw (Window &window, bool force) override
Greg Clayton44d93782014-01-27 23:43:24 +00004616 {
4617 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4618 Process *process = exe_ctx.GetProcessPtr();
4619 Thread *thread = exe_ctx.GetThreadPtr();
4620 StackFrame *frame = exe_ctx.GetFramePtr();
4621 window.Erase();
4622 window.SetBackground(2);
4623 window.MoveCursor (0, 0);
4624 if (process)
4625 {
4626 const StateType state = process->GetState();
4627 window.Printf ("Process: %5" PRIu64 " %10s", process->GetID(), StateAsCString(state));
4628
4629 if (StateIsStoppedState(state, true))
4630 {
Ed Maste5b031eb2014-04-09 16:39:30 +00004631 StreamString strm;
Greg Clayton554f68d2015-02-04 22:00:53 +00004632 if (thread && FormatEntity::Format (m_format, strm, NULL, &exe_ctx, NULL, NULL, false, false))
Ed Maste5b031eb2014-04-09 16:39:30 +00004633 {
4634 window.MoveCursor (40, 0);
4635 window.PutCStringTruncated(strm.GetString().c_str(), 1);
4636 }
Greg Clayton44d93782014-01-27 23:43:24 +00004637
4638 window.MoveCursor (60, 0);
4639 if (frame)
4640 window.Printf ("Frame: %3u PC = 0x%16.16" PRIx64, frame->GetFrameIndex(), frame->GetFrameCodeAddress().GetOpcodeLoadAddress (exe_ctx.GetTargetPtr()));
4641 }
4642 else if (state == eStateExited)
4643 {
4644 const char *exit_desc = process->GetExitDescription();
4645 const int exit_status = process->GetExitStatus();
4646 if (exit_desc && exit_desc[0])
4647 window.Printf (" with status = %i (%s)", exit_status, exit_desc);
4648 else
4649 window.Printf (" with status = %i", exit_status);
4650 }
4651 }
4652 window.DeferredRefresh();
4653 return true;
4654 }
4655
4656protected:
4657 Debugger &m_debugger;
Greg Clayton554f68d2015-02-04 22:00:53 +00004658 FormatEntity::Entry m_format;
Greg Clayton44d93782014-01-27 23:43:24 +00004659};
4660
4661class SourceFileWindowDelegate : public WindowDelegate
4662{
4663public:
4664 SourceFileWindowDelegate (Debugger &debugger) :
4665 WindowDelegate (),
4666 m_debugger (debugger),
4667 m_sc (),
4668 m_file_sp (),
4669 m_disassembly_scope (NULL),
4670 m_disassembly_sp (),
4671 m_disassembly_range (),
Greg Claytonec990862014-03-19 16:22:48 +00004672 m_title (),
Greg Clayton44d93782014-01-27 23:43:24 +00004673 m_line_width (4),
4674 m_selected_line (0),
4675 m_pc_line (0),
4676 m_stop_id (0),
4677 m_frame_idx (UINT32_MAX),
4678 m_first_visible_line (0),
4679 m_min_x (0),
4680 m_min_y (0),
4681 m_max_x (0),
4682 m_max_y (0)
4683 {
4684 }
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00004685
Eugene Zelenko315b6882015-10-26 17:00:13 +00004686 ~SourceFileWindowDelegate() override = default;
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00004687
Greg Clayton44d93782014-01-27 23:43:24 +00004688 void
4689 Update (const SymbolContext &sc)
4690 {
4691 m_sc = sc;
4692 }
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00004693
Greg Clayton44d93782014-01-27 23:43:24 +00004694 uint32_t
4695 NumVisibleLines () const
4696 {
4697 return m_max_y - m_min_y;
4698 }
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00004699
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00004700 const char *
4701 WindowDelegateGetHelpText () override
Greg Clayton44d93782014-01-27 23:43:24 +00004702 {
4703 return "Source/Disassembly window keyboard shortcuts:";
4704 }
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00004705
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00004706 KeyHelp *
4707 WindowDelegateGetKeyHelp () override
Greg Clayton44d93782014-01-27 23:43:24 +00004708 {
4709 static curses::KeyHelp g_source_view_key_help[] = {
4710 { KEY_RETURN, "Run to selected line with one shot breakpoint" },
4711 { KEY_UP, "Select previous source line" },
4712 { KEY_DOWN, "Select next source line" },
4713 { KEY_PPAGE, "Page up" },
4714 { KEY_NPAGE, "Page down" },
4715 { 'b', "Set breakpoint on selected source/disassembly line" },
4716 { 'c', "Continue process" },
4717 { 'd', "Detach and resume process" },
4718 { 'D', "Detach with process suspended" },
4719 { 'h', "Show help dialog" },
4720 { 'k', "Kill process" },
4721 { 'n', "Step over (source line)" },
4722 { 'N', "Step over (single instruction)" },
4723 { 'o', "Step out" },
4724 { 's', "Step in (source line)" },
4725 { 'S', "Step in (single instruction)" },
4726 { ',', "Page up" },
4727 { '.', "Page down" },
4728 { '\0', NULL }
4729 };
4730 return g_source_view_key_help;
4731 }
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00004732
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00004733 bool
4734 WindowDelegateDraw (Window &window, bool force) override
Greg Clayton44d93782014-01-27 23:43:24 +00004735 {
4736 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
4737 Process *process = exe_ctx.GetProcessPtr();
4738 Thread *thread = NULL;
4739
4740 bool update_location = false;
4741 if (process)
4742 {
4743 StateType state = process->GetState();
4744 if (StateIsStoppedState(state, true))
4745 {
4746 // We are stopped, so it is ok to
4747 update_location = true;
4748 }
4749 }
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00004750
Greg Clayton44d93782014-01-27 23:43:24 +00004751 m_min_x = 1;
Greg Claytonec990862014-03-19 16:22:48 +00004752 m_min_y = 2;
Greg Clayton44d93782014-01-27 23:43:24 +00004753 m_max_x = window.GetMaxX()-1;
4754 m_max_y = window.GetMaxY()-1;
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00004755
Greg Clayton44d93782014-01-27 23:43:24 +00004756 const uint32_t num_visible_lines = NumVisibleLines();
4757 StackFrameSP frame_sp;
4758 bool set_selected_line_to_pc = false;
4759
Greg Clayton44d93782014-01-27 23:43:24 +00004760 if (update_location)
4761 {
Greg Clayton44d93782014-01-27 23:43:24 +00004762 const bool process_alive = process ? process->IsAlive() : false;
4763 bool thread_changed = false;
4764 if (process_alive)
4765 {
4766 thread = exe_ctx.GetThreadPtr();
4767 if (thread)
4768 {
4769 frame_sp = thread->GetSelectedFrame();
4770 auto tid = thread->GetID();
4771 thread_changed = tid != m_tid;
4772 m_tid = tid;
4773 }
4774 else
4775 {
4776 if (m_tid != LLDB_INVALID_THREAD_ID)
4777 {
4778 thread_changed = true;
4779 m_tid = LLDB_INVALID_THREAD_ID;
4780 }
4781 }
4782 }
4783 const uint32_t stop_id = process ? process->GetStopID() : 0;
4784 const bool stop_id_changed = stop_id != m_stop_id;
4785 bool frame_changed = false;
4786 m_stop_id = stop_id;
Greg Claytonec990862014-03-19 16:22:48 +00004787 m_title.Clear();
Greg Clayton44d93782014-01-27 23:43:24 +00004788 if (frame_sp)
4789 {
4790 m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
Greg Claytonec990862014-03-19 16:22:48 +00004791 if (m_sc.module_sp)
4792 {
4793 m_title.Printf("%s", m_sc.module_sp->GetFileSpec().GetFilename().GetCString());
4794 ConstString func_name = m_sc.GetFunctionName();
4795 if (func_name)
4796 m_title.Printf("`%s", func_name.GetCString());
4797 }
Greg Clayton44d93782014-01-27 23:43:24 +00004798 const uint32_t frame_idx = frame_sp->GetFrameIndex();
4799 frame_changed = frame_idx != m_frame_idx;
4800 m_frame_idx = frame_idx;
4801 }
4802 else
4803 {
4804 m_sc.Clear(true);
4805 frame_changed = m_frame_idx != UINT32_MAX;
4806 m_frame_idx = UINT32_MAX;
4807 }
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00004808
Greg Clayton44d93782014-01-27 23:43:24 +00004809 const bool context_changed = thread_changed || frame_changed || stop_id_changed;
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00004810
Greg Clayton44d93782014-01-27 23:43:24 +00004811 if (process_alive)
4812 {
4813 if (m_sc.line_entry.IsValid())
4814 {
4815 m_pc_line = m_sc.line_entry.line;
4816 if (m_pc_line != UINT32_MAX)
4817 --m_pc_line; // Convert to zero based line number...
4818 // Update the selected line if the stop ID changed...
4819 if (context_changed)
4820 m_selected_line = m_pc_line;
4821
4822 if (m_file_sp && m_file_sp->FileSpecMatches(m_sc.line_entry.file))
4823 {
4824 // Same file, nothing to do, we should either have the
4825 // lines or not (source file missing)
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00004826 if (m_selected_line >= static_cast<size_t>(m_first_visible_line))
Greg Clayton44d93782014-01-27 23:43:24 +00004827 {
4828 if (m_selected_line >= m_first_visible_line + num_visible_lines)
4829 m_first_visible_line = m_selected_line - 10;
4830 }
4831 else
4832 {
4833 if (m_selected_line > 10)
4834 m_first_visible_line = m_selected_line - 10;
4835 else
4836 m_first_visible_line = 0;
4837 }
4838 }
4839 else
4840 {
4841 // File changed, set selected line to the line with the PC
4842 m_selected_line = m_pc_line;
4843 m_file_sp = m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file);
4844 if (m_file_sp)
4845 {
4846 const size_t num_lines = m_file_sp->GetNumLines();
4847 int m_line_width = 1;
4848 for (size_t n = num_lines; n >= 10; n = n / 10)
4849 ++m_line_width;
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00004850
Greg Clayton44d93782014-01-27 23:43:24 +00004851 snprintf (m_line_format, sizeof(m_line_format), " %%%iu ", m_line_width);
4852 if (num_lines < num_visible_lines || m_selected_line < num_visible_lines)
4853 m_first_visible_line = 0;
4854 else
4855 m_first_visible_line = m_selected_line - 10;
4856 }
4857 }
4858 }
4859 else
4860 {
4861 m_file_sp.reset();
4862 }
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00004863
Greg Clayton44d93782014-01-27 23:43:24 +00004864 if (!m_file_sp || m_file_sp->GetNumLines() == 0)
4865 {
4866 // Show disassembly
4867 bool prefer_file_cache = false;
4868 if (m_sc.function)
4869 {
4870 if (m_disassembly_scope != m_sc.function)
4871 {
4872 m_disassembly_scope = m_sc.function;
4873 m_disassembly_sp = m_sc.function->GetInstructions (exe_ctx, NULL, prefer_file_cache);
4874 if (m_disassembly_sp)
4875 {
4876 set_selected_line_to_pc = true;
4877 m_disassembly_range = m_sc.function->GetAddressRange();
4878 }
4879 else
4880 {
4881 m_disassembly_range.Clear();
4882 }
4883 }
4884 else
4885 {
4886 set_selected_line_to_pc = context_changed;
4887 }
4888 }
4889 else if (m_sc.symbol)
4890 {
4891 if (m_disassembly_scope != m_sc.symbol)
4892 {
4893 m_disassembly_scope = m_sc.symbol;
4894 m_disassembly_sp = m_sc.symbol->GetInstructions (exe_ctx, NULL, prefer_file_cache);
4895 if (m_disassembly_sp)
4896 {
4897 set_selected_line_to_pc = true;
4898 m_disassembly_range.GetBaseAddress() = m_sc.symbol->GetAddress();
4899 m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize());
4900 }
4901 else
4902 {
4903 m_disassembly_range.Clear();
4904 }
4905 }
4906 else
4907 {
4908 set_selected_line_to_pc = context_changed;
4909 }
4910 }
4911 }
4912 }
4913 else
4914 {
4915 m_pc_line = UINT32_MAX;
4916 }
4917 }
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00004918
Greg Claytonec990862014-03-19 16:22:48 +00004919 const int window_width = window.GetWidth();
Greg Clayton44d93782014-01-27 23:43:24 +00004920 window.Erase();
4921 window.DrawTitleBox ("Sources");
Greg Claytonec990862014-03-19 16:22:48 +00004922 if (!m_title.GetString().empty())
4923 {
4924 window.AttributeOn(A_REVERSE);
4925 window.MoveCursor(1, 1);
4926 window.PutChar(' ');
4927 window.PutCStringTruncated(m_title.GetString().c_str(), 1);
4928 int x = window.GetCursorX();
4929 if (x < window_width - 1)
4930 {
4931 window.Printf ("%*s", window_width - x - 1, "");
4932 }
4933 window.AttributeOff(A_REVERSE);
4934 }
Greg Clayton44d93782014-01-27 23:43:24 +00004935
4936 Target *target = exe_ctx.GetTargetPtr();
4937 const size_t num_source_lines = GetNumSourceLines();
4938 if (num_source_lines > 0)
4939 {
4940 // Display source
4941 BreakpointLines bp_lines;
4942 if (target)
4943 {
4944 BreakpointList &bp_list = target->GetBreakpointList();
4945 const size_t num_bps = bp_list.GetSize();
4946 for (size_t bp_idx=0; bp_idx<num_bps; ++bp_idx)
4947 {
4948 BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
4949 const size_t num_bps_locs = bp_sp->GetNumLocations();
4950 for (size_t bp_loc_idx=0; bp_loc_idx<num_bps_locs; ++bp_loc_idx)
4951 {
4952 BreakpointLocationSP bp_loc_sp = bp_sp->GetLocationAtIndex(bp_loc_idx);
4953 LineEntry bp_loc_line_entry;
4954 if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry (bp_loc_line_entry))
4955 {
4956 if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file)
4957 {
4958 bp_lines.insert(bp_loc_line_entry.line);
4959 }
4960 }
4961 }
4962 }
4963 }
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00004964
Greg Clayton44d93782014-01-27 23:43:24 +00004965 const attr_t selected_highlight_attr = A_REVERSE;
4966 const attr_t pc_highlight_attr = COLOR_PAIR(1);
4967
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00004968 for (size_t i=0; i<num_visible_lines; ++i)
Greg Clayton44d93782014-01-27 23:43:24 +00004969 {
4970 const uint32_t curr_line = m_first_visible_line + i;
4971 if (curr_line < num_source_lines)
4972 {
Greg Claytonec990862014-03-19 16:22:48 +00004973 const int line_y = m_min_y+i;
Greg Clayton44d93782014-01-27 23:43:24 +00004974 window.MoveCursor(1, line_y);
4975 const bool is_pc_line = curr_line == m_pc_line;
4976 const bool line_is_selected = m_selected_line == curr_line;
4977 // Highlight the line as the PC line first, then if the selected line
4978 // isn't the same as the PC line, highlight it differently
4979 attr_t highlight_attr = 0;
4980 attr_t bp_attr = 0;
4981 if (is_pc_line)
4982 highlight_attr = pc_highlight_attr;
4983 else if (line_is_selected)
4984 highlight_attr = selected_highlight_attr;
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00004985
Greg Clayton44d93782014-01-27 23:43:24 +00004986 if (bp_lines.find(curr_line+1) != bp_lines.end())
4987 bp_attr = COLOR_PAIR(2);
4988
4989 if (bp_attr)
4990 window.AttributeOn(bp_attr);
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00004991
Greg Clayton44d93782014-01-27 23:43:24 +00004992 window.Printf (m_line_format, curr_line + 1);
4993
4994 if (bp_attr)
4995 window.AttributeOff(bp_attr);
4996
4997 window.PutChar(ACS_VLINE);
4998 // Mark the line with the PC with a diamond
4999 if (is_pc_line)
5000 window.PutChar(ACS_DIAMOND);
5001 else
5002 window.PutChar(' ');
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00005003
Greg Clayton44d93782014-01-27 23:43:24 +00005004 if (highlight_attr)
5005 window.AttributeOn(highlight_attr);
5006 const uint32_t line_len = m_file_sp->GetLineLength(curr_line + 1, false);
5007 if (line_len > 0)
5008 window.PutCString(m_file_sp->PeekLineData(curr_line + 1), line_len);
5009
5010 if (is_pc_line && frame_sp && frame_sp->GetConcreteFrameIndex() == 0)
5011 {
5012 StopInfoSP stop_info_sp;
5013 if (thread)
5014 stop_info_sp = thread->GetStopInfo();
5015 if (stop_info_sp)
5016 {
5017 const char *stop_description = stop_info_sp->GetDescription();
5018 if (stop_description && stop_description[0])
5019 {
5020 size_t stop_description_len = strlen(stop_description);
Greg Claytonec990862014-03-19 16:22:48 +00005021 int desc_x = window_width - stop_description_len - 16;
Greg Clayton44d93782014-01-27 23:43:24 +00005022 window.Printf ("%*s", desc_x - window.GetCursorX(), "");
Greg Claytonec990862014-03-19 16:22:48 +00005023 //window.MoveCursor(window_width - stop_description_len - 15, line_y);
Greg Clayton44d93782014-01-27 23:43:24 +00005024 window.Printf ("<<< Thread %u: %s ", thread->GetIndexID(), stop_description);
5025 }
5026 }
5027 else
5028 {
Greg Claytonec990862014-03-19 16:22:48 +00005029 window.Printf ("%*s", window_width - window.GetCursorX() - 1, "");
Greg Clayton44d93782014-01-27 23:43:24 +00005030 }
5031 }
5032 if (highlight_attr)
5033 window.AttributeOff(highlight_attr);
Greg Clayton44d93782014-01-27 23:43:24 +00005034 }
5035 else
5036 {
5037 break;
5038 }
5039 }
5040 }
5041 else
5042 {
5043 size_t num_disassembly_lines = GetNumDisassemblyLines();
5044 if (num_disassembly_lines > 0)
5045 {
5046 // Display disassembly
5047 BreakpointAddrs bp_file_addrs;
5048 Target *target = exe_ctx.GetTargetPtr();
5049 if (target)
5050 {
5051 BreakpointList &bp_list = target->GetBreakpointList();
5052 const size_t num_bps = bp_list.GetSize();
5053 for (size_t bp_idx=0; bp_idx<num_bps; ++bp_idx)
5054 {
5055 BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
5056 const size_t num_bps_locs = bp_sp->GetNumLocations();
5057 for (size_t bp_loc_idx=0; bp_loc_idx<num_bps_locs; ++bp_loc_idx)
5058 {
5059 BreakpointLocationSP bp_loc_sp = bp_sp->GetLocationAtIndex(bp_loc_idx);
5060 LineEntry bp_loc_line_entry;
5061 const lldb::addr_t file_addr = bp_loc_sp->GetAddress().GetFileAddress();
5062 if (file_addr != LLDB_INVALID_ADDRESS)
5063 {
5064 if (m_disassembly_range.ContainsFileAddress(file_addr))
5065 bp_file_addrs.insert(file_addr);
5066 }
5067 }
5068 }
5069 }
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00005070
Greg Clayton44d93782014-01-27 23:43:24 +00005071 const attr_t selected_highlight_attr = A_REVERSE;
5072 const attr_t pc_highlight_attr = COLOR_PAIR(1);
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00005073
Greg Clayton44d93782014-01-27 23:43:24 +00005074 StreamString strm;
5075
5076 InstructionList &insts = m_disassembly_sp->GetInstructionList();
5077 Address pc_address;
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00005078
Greg Clayton44d93782014-01-27 23:43:24 +00005079 if (frame_sp)
5080 pc_address = frame_sp->GetFrameCodeAddress();
5081 const uint32_t pc_idx = pc_address.IsValid() ? insts.GetIndexOfInstructionAtAddress (pc_address) : UINT32_MAX;
5082 if (set_selected_line_to_pc)
5083 {
5084 m_selected_line = pc_idx;
5085 }
5086
5087 const uint32_t non_visible_pc_offset = (num_visible_lines / 5);
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00005088 if (static_cast<size_t>(m_first_visible_line) >= num_disassembly_lines)
Greg Clayton44d93782014-01-27 23:43:24 +00005089 m_first_visible_line = 0;
5090
5091 if (pc_idx < num_disassembly_lines)
5092 {
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00005093 if (pc_idx < static_cast<uint32_t>(m_first_visible_line) ||
Greg Clayton44d93782014-01-27 23:43:24 +00005094 pc_idx >= m_first_visible_line + num_visible_lines)
5095 m_first_visible_line = pc_idx - non_visible_pc_offset;
5096 }
5097
5098 for (size_t i=0; i<num_visible_lines; ++i)
5099 {
5100 const uint32_t inst_idx = m_first_visible_line + i;
5101 Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get();
5102 if (!inst)
5103 break;
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00005104
Greg Claytonec990862014-03-19 16:22:48 +00005105 const int line_y = m_min_y+i;
5106 window.MoveCursor(1, line_y);
Greg Clayton44d93782014-01-27 23:43:24 +00005107 const bool is_pc_line = frame_sp && inst_idx == pc_idx;
5108 const bool line_is_selected = m_selected_line == inst_idx;
5109 // Highlight the line as the PC line first, then if the selected line
5110 // isn't the same as the PC line, highlight it differently
5111 attr_t highlight_attr = 0;
5112 attr_t bp_attr = 0;
5113 if (is_pc_line)
5114 highlight_attr = pc_highlight_attr;
5115 else if (line_is_selected)
5116 highlight_attr = selected_highlight_attr;
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00005117
Greg Clayton44d93782014-01-27 23:43:24 +00005118 if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) != bp_file_addrs.end())
5119 bp_attr = COLOR_PAIR(2);
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00005120
Greg Clayton44d93782014-01-27 23:43:24 +00005121 if (bp_attr)
5122 window.AttributeOn(bp_attr);
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00005123
5124 window.Printf (" 0x%16.16llx ",
5125 static_cast<unsigned long long>(inst->GetAddress().GetLoadAddress(target)));
5126
Greg Clayton44d93782014-01-27 23:43:24 +00005127 if (bp_attr)
5128 window.AttributeOff(bp_attr);
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00005129
Greg Clayton44d93782014-01-27 23:43:24 +00005130 window.PutChar(ACS_VLINE);
5131 // Mark the line with the PC with a diamond
5132 if (is_pc_line)
5133 window.PutChar(ACS_DIAMOND);
5134 else
5135 window.PutChar(' ');
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00005136
Greg Clayton44d93782014-01-27 23:43:24 +00005137 if (highlight_attr)
5138 window.AttributeOn(highlight_attr);
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00005139
Greg Clayton44d93782014-01-27 23:43:24 +00005140 const char *mnemonic = inst->GetMnemonic(&exe_ctx);
5141 const char *operands = inst->GetOperands(&exe_ctx);
5142 const char *comment = inst->GetComment(&exe_ctx);
5143
5144 if (mnemonic && mnemonic[0] == '\0')
5145 mnemonic = NULL;
5146 if (operands && operands[0] == '\0')
5147 operands = NULL;
5148 if (comment && comment[0] == '\0')
5149 comment = NULL;
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00005150
Greg Clayton44d93782014-01-27 23:43:24 +00005151 strm.Clear();
5152
5153 if (mnemonic && operands && comment)
5154 strm.Printf ("%-8s %-25s ; %s", mnemonic, operands, comment);
5155 else if (mnemonic && operands)
5156 strm.Printf ("%-8s %s", mnemonic, operands);
5157 else if (mnemonic)
5158 strm.Printf ("%s", mnemonic);
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00005159
Greg Clayton44d93782014-01-27 23:43:24 +00005160 int right_pad = 1;
5161 window.PutCStringTruncated(strm.GetString().c_str(), right_pad);
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00005162
Greg Clayton44d93782014-01-27 23:43:24 +00005163 if (is_pc_line && frame_sp && frame_sp->GetConcreteFrameIndex() == 0)
5164 {
5165 StopInfoSP stop_info_sp;
5166 if (thread)
5167 stop_info_sp = thread->GetStopInfo();
5168 if (stop_info_sp)
5169 {
5170 const char *stop_description = stop_info_sp->GetDescription();
5171 if (stop_description && stop_description[0])
5172 {
5173 size_t stop_description_len = strlen(stop_description);
Greg Claytonec990862014-03-19 16:22:48 +00005174 int desc_x = window_width - stop_description_len - 16;
Greg Clayton44d93782014-01-27 23:43:24 +00005175 window.Printf ("%*s", desc_x - window.GetCursorX(), "");
Greg Claytonec990862014-03-19 16:22:48 +00005176 //window.MoveCursor(window_width - stop_description_len - 15, line_y);
Greg Clayton44d93782014-01-27 23:43:24 +00005177 window.Printf ("<<< Thread %u: %s ", thread->GetIndexID(), stop_description);
5178 }
5179 }
5180 else
5181 {
Greg Claytonec990862014-03-19 16:22:48 +00005182 window.Printf ("%*s", window_width - window.GetCursorX() - 1, "");
Greg Clayton44d93782014-01-27 23:43:24 +00005183 }
5184 }
5185 if (highlight_attr)
5186 window.AttributeOff(highlight_attr);
5187 }
5188 }
5189 }
5190 window.DeferredRefresh();
5191 return true; // Drawing handled
5192 }
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00005193
Greg Clayton44d93782014-01-27 23:43:24 +00005194 size_t
5195 GetNumLines ()
5196 {
5197 size_t num_lines = GetNumSourceLines();
5198 if (num_lines == 0)
5199 num_lines = GetNumDisassemblyLines();
5200 return num_lines;
5201 }
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00005202
Greg Clayton44d93782014-01-27 23:43:24 +00005203 size_t
5204 GetNumSourceLines () const
5205 {
5206 if (m_file_sp)
5207 return m_file_sp->GetNumLines();
5208 return 0;
5209 }
Eugene Zelenko315b6882015-10-26 17:00:13 +00005210
Greg Clayton44d93782014-01-27 23:43:24 +00005211 size_t
5212 GetNumDisassemblyLines () const
5213 {
5214 if (m_disassembly_sp)
5215 return m_disassembly_sp->GetInstructionList().GetSize();
5216 return 0;
5217 }
5218
Greg Claytonbd5ae6b2015-04-10 21:21:09 +00005219 HandleCharResult
5220 WindowDelegateHandleChar (Window &window, int c) override
Greg Clayton44d93782014-01-27 23:43:24 +00005221 {
5222 const uint32_t num_visible_lines = NumVisibleLines();
5223 const size_t num_lines = GetNumLines ();
5224
5225 switch (c)
5226 {
5227 case ',':
5228 case KEY_PPAGE:
5229 // Page up key
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00005230 if (static_cast<uint32_t>(m_first_visible_line) > num_visible_lines)
Greg Clayton44d93782014-01-27 23:43:24 +00005231 m_first_visible_line -= num_visible_lines;
5232 else
5233 m_first_visible_line = 0;
5234 m_selected_line = m_first_visible_line;
5235 return eKeyHandled;
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00005236
Greg Clayton44d93782014-01-27 23:43:24 +00005237 case '.':
5238 case KEY_NPAGE:
5239 // Page down key
5240 {
5241 if (m_first_visible_line + num_visible_lines < num_lines)
5242 m_first_visible_line += num_visible_lines;
5243 else if (num_lines < num_visible_lines)
5244 m_first_visible_line = 0;
5245 else
5246 m_first_visible_line = num_lines - num_visible_lines;
5247 m_selected_line = m_first_visible_line;
5248 }
5249 return eKeyHandled;
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00005250
Greg Clayton44d93782014-01-27 23:43:24 +00005251 case KEY_UP:
5252 if (m_selected_line > 0)
5253 {
5254 m_selected_line--;
Saleem Abdulrasool3985c8c2014-04-02 03:51:35 +00005255 if (static_cast<size_t>(m_first_visible_line) > m_selected_line)
Greg Clayton44d93782014-01-27 23:43:24 +00005256 m_first_visible_line = m_selected_line;
5257 }
5258 return eKeyHandled;
5259
5260 case KEY_DOWN:
5261 if (m_selected_line + 1 < num_lines)
5262 {
5263 m_selected_line++;
5264 if (m_first_visible_line + num_visible_lines < m_selected_line)
5265 m_first_visible_line++;
5266 }
5267 return eKeyHandled;
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00005268
Greg Clayton44d93782014-01-27 23:43:24 +00005269 case '\r':
5270 case '\n':
5271 case KEY_ENTER:
5272 // Set a breakpoint and run to the line using a one shot breakpoint
5273 if (GetNumSourceLines() > 0)
5274 {
5275 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5276 if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive())
5277 {
5278 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (NULL, // Don't limit the breakpoint to certain modules
5279 m_file_sp->GetFileSpec(), // Source file
5280 m_selected_line + 1, // Source line number (m_selected_line is zero based)
Jim Ingham24111672016-03-09 18:59:13 +00005281 0, // No offset
Greg Clayton44d93782014-01-27 23:43:24 +00005282 eLazyBoolCalculate, // Check inlines using global setting
5283 eLazyBoolCalculate, // Skip prologue using global setting,
5284 false, // internal
Ilia K055ad9b2015-05-18 13:41:01 +00005285 false, // request_hardware
5286 eLazyBoolCalculate); // move_to_nearest_code
Greg Clayton44d93782014-01-27 23:43:24 +00005287 // Make breakpoint one shot
5288 bp_sp->GetOptions()->SetOneShot(true);
5289 exe_ctx.GetProcessRef().Resume();
5290 }
5291 }
5292 else if (m_selected_line < GetNumDisassemblyLines())
5293 {
5294 const Instruction *inst = m_disassembly_sp->GetInstructionList().GetInstructionAtIndex(m_selected_line).get();
5295 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5296 if (exe_ctx.HasTargetScope())
5297 {
5298 Address addr = inst->GetAddress();
5299 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (addr, // lldb_private::Address
5300 false, // internal
5301 false); // request_hardware
5302 // Make breakpoint one shot
5303 bp_sp->GetOptions()->SetOneShot(true);
5304 exe_ctx.GetProcessRef().Resume();
5305 }
5306 }
5307 return eKeyHandled;
5308
5309 case 'b': // 'b' == toggle breakpoint on currently selected line
5310 if (m_selected_line < GetNumSourceLines())
5311 {
5312 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5313 if (exe_ctx.HasTargetScope())
5314 {
5315 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (NULL, // Don't limit the breakpoint to certain modules
5316 m_file_sp->GetFileSpec(), // Source file
5317 m_selected_line + 1, // Source line number (m_selected_line is zero based)
Jim Ingham24111672016-03-09 18:59:13 +00005318 0, // No offset
Greg Clayton44d93782014-01-27 23:43:24 +00005319 eLazyBoolCalculate, // Check inlines using global setting
5320 eLazyBoolCalculate, // Skip prologue using global setting,
5321 false, // internal
Ilia K055ad9b2015-05-18 13:41:01 +00005322 false, // request_hardware
5323 eLazyBoolCalculate); // move_to_nearest_code
Greg Clayton44d93782014-01-27 23:43:24 +00005324 }
5325 }
5326 else if (m_selected_line < GetNumDisassemblyLines())
5327 {
5328 const Instruction *inst = m_disassembly_sp->GetInstructionList().GetInstructionAtIndex(m_selected_line).get();
5329 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5330 if (exe_ctx.HasTargetScope())
5331 {
5332 Address addr = inst->GetAddress();
5333 BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (addr, // lldb_private::Address
5334 false, // internal
5335 false); // request_hardware
5336 }
5337 }
5338 return eKeyHandled;
5339
5340 case 'd': // 'd' == detach and let run
5341 case 'D': // 'D' == detach and keep stopped
5342 {
5343 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5344 if (exe_ctx.HasProcessScope())
5345 exe_ctx.GetProcessRef().Detach(c == 'D');
5346 }
5347 return eKeyHandled;
5348
5349 case 'k':
5350 // 'k' == kill
5351 {
5352 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5353 if (exe_ctx.HasProcessScope())
Jason Molendaede31932015-04-17 05:01:58 +00005354 exe_ctx.GetProcessRef().Destroy(false);
Greg Clayton44d93782014-01-27 23:43:24 +00005355 }
5356 return eKeyHandled;
5357
5358 case 'c':
5359 // 'c' == continue
5360 {
5361 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5362 if (exe_ctx.HasProcessScope())
5363 exe_ctx.GetProcessRef().Resume();
5364 }
5365 return eKeyHandled;
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00005366
Greg Clayton44d93782014-01-27 23:43:24 +00005367 case 'o':
5368 // 'o' == step out
5369 {
5370 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5371 if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true))
5372 {
5373 exe_ctx.GetThreadRef().StepOut();
5374 }
5375 }
5376 return eKeyHandled;
Eugene Zelenko315b6882015-10-26 17:00:13 +00005377
Greg Clayton44d93782014-01-27 23:43:24 +00005378 case 'n': // 'n' == step over
5379 case 'N': // 'N' == step over instruction
5380 {
5381 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5382 if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true))
5383 {
5384 bool source_step = (c == 'n');
5385 exe_ctx.GetThreadRef().StepOver(source_step);
5386 }
5387 }
5388 return eKeyHandled;
Eugene Zelenko315b6882015-10-26 17:00:13 +00005389
Greg Clayton44d93782014-01-27 23:43:24 +00005390 case 's': // 's' == step into
5391 case 'S': // 'S' == step into instruction
5392 {
5393 ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext();
5394 if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true))
5395 {
5396 bool source_step = (c == 's');
Jim Ingham4b4b2472014-03-13 02:47:14 +00005397 exe_ctx.GetThreadRef().StepIn(source_step);
Greg Clayton44d93782014-01-27 23:43:24 +00005398 }
5399 }
5400 return eKeyHandled;
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00005401
Greg Clayton44d93782014-01-27 23:43:24 +00005402 case 'h':
5403 window.CreateHelpSubwindow ();
5404 return eKeyHandled;
5405
5406 default:
5407 break;
5408 }
5409 return eKeyNotHandled;
5410 }
Saleem Abdulrasool324a1032014-04-04 04:06:10 +00005411
Greg Clayton44d93782014-01-27 23:43:24 +00005412protected:
5413 typedef std::set<uint32_t> BreakpointLines;
5414 typedef std::set<lldb::addr_t> BreakpointAddrs;
5415
5416 Debugger &m_debugger;
5417 SymbolContext m_sc;
5418 SourceManager::FileSP m_file_sp;
5419 SymbolContextScope *m_disassembly_scope;
5420 lldb::DisassemblerSP m_disassembly_sp;
5421 AddressRange m_disassembly_range;
Greg Claytonec990862014-03-19 16:22:48 +00005422 StreamString m_title;
Greg Clayton44d93782014-01-27 23:43:24 +00005423 lldb::user_id_t m_tid;
5424 char m_line_format[8];
5425 int m_line_width;
5426 uint32_t m_selected_line; // The selected line
5427 uint32_t m_pc_line; // The line with the PC
5428 uint32_t m_stop_id;
5429 uint32_t m_frame_idx;
5430 int m_first_visible_line;
5431 int m_min_x;
5432 int m_min_y;
5433 int m_max_x;
5434 int m_max_y;
Greg Clayton44d93782014-01-27 23:43:24 +00005435};
5436
5437DisplayOptions ValueObjectListDelegate::g_options = { true };
5438
5439IOHandlerCursesGUI::IOHandlerCursesGUI (Debugger &debugger) :
Kate Stonee30f11d2014-11-17 19:06:59 +00005440 IOHandler (debugger, IOHandler::Type::Curses)
Greg Clayton44d93782014-01-27 23:43:24 +00005441{
5442}
5443
5444void
5445IOHandlerCursesGUI::Activate ()
5446{
5447 IOHandler::Activate();
5448 if (!m_app_ap)
5449 {
5450 m_app_ap.reset (new Application (GetInputFILE(), GetOutputFILE()));
Eugene Zelenko315b6882015-10-26 17:00:13 +00005451
Greg Clayton44d93782014-01-27 23:43:24 +00005452 // This is both a window and a menu delegate
5453 std::shared_ptr<ApplicationDelegate> app_delegate_sp(new ApplicationDelegate(*m_app_ap, m_debugger));
5454
5455 MenuDelegateSP app_menu_delegate_sp = std::static_pointer_cast<MenuDelegate>(app_delegate_sp);
5456 MenuSP lldb_menu_sp(new Menu("LLDB" , "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB));
5457 MenuSP exit_menuitem_sp(new Menu("Exit", NULL, 'x', ApplicationDelegate::eMenuID_LLDBExit));
5458 exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit);
5459 lldb_menu_sp->AddSubmenu (MenuSP (new Menu("About LLDB", NULL, 'a', ApplicationDelegate::eMenuID_LLDBAbout)));
5460 lldb_menu_sp->AddSubmenu (MenuSP (new Menu(Menu::Type::Separator)));
5461 lldb_menu_sp->AddSubmenu (exit_menuitem_sp);
5462
5463 MenuSP target_menu_sp(new Menu("Target" ,"F2", KEY_F(2), ApplicationDelegate::eMenuID_Target));
5464 target_menu_sp->AddSubmenu (MenuSP (new Menu("Create", NULL, 'c', ApplicationDelegate::eMenuID_TargetCreate)));
5465 target_menu_sp->AddSubmenu (MenuSP (new Menu("Delete", NULL, 'd', ApplicationDelegate::eMenuID_TargetDelete)));
5466
5467 MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3), ApplicationDelegate::eMenuID_Process));
5468 process_menu_sp->AddSubmenu (MenuSP (new Menu("Attach" , NULL, 'a', ApplicationDelegate::eMenuID_ProcessAttach)));
5469 process_menu_sp->AddSubmenu (MenuSP (new Menu("Detach" , NULL, 'd', ApplicationDelegate::eMenuID_ProcessDetach)));
5470 process_menu_sp->AddSubmenu (MenuSP (new Menu("Launch" , NULL, 'l', ApplicationDelegate::eMenuID_ProcessLaunch)));
5471 process_menu_sp->AddSubmenu (MenuSP (new Menu(Menu::Type::Separator)));
5472 process_menu_sp->AddSubmenu (MenuSP (new Menu("Continue", NULL, 'c', ApplicationDelegate::eMenuID_ProcessContinue)));
5473 process_menu_sp->AddSubmenu (MenuSP (new Menu("Halt" , NULL, 'h', ApplicationDelegate::eMenuID_ProcessHalt)));
5474 process_menu_sp->AddSubmenu (MenuSP (new Menu("Kill" , NULL, 'k', ApplicationDelegate::eMenuID_ProcessKill)));
5475
5476 MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4), ApplicationDelegate::eMenuID_Thread));
5477 thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step In" , NULL, 'i', ApplicationDelegate::eMenuID_ThreadStepIn)));
5478 thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step Over", NULL, 'v', ApplicationDelegate::eMenuID_ThreadStepOver)));
5479 thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step Out" , NULL, 'o', ApplicationDelegate::eMenuID_ThreadStepOut)));
5480
5481 MenuSP view_menu_sp(new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View));
5482 view_menu_sp->AddSubmenu (MenuSP (new Menu("Backtrace", NULL, 'b', ApplicationDelegate::eMenuID_ViewBacktrace)));
5483 view_menu_sp->AddSubmenu (MenuSP (new Menu("Registers", NULL, 'r', ApplicationDelegate::eMenuID_ViewRegisters)));
5484 view_menu_sp->AddSubmenu (MenuSP (new Menu("Source" , NULL, 's', ApplicationDelegate::eMenuID_ViewSource)));
5485 view_menu_sp->AddSubmenu (MenuSP (new Menu("Variables", NULL, 'v', ApplicationDelegate::eMenuID_ViewVariables)));
5486
5487 MenuSP help_menu_sp(new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help));
5488 help_menu_sp->AddSubmenu (MenuSP (new Menu("GUI Help", NULL, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp)));
5489
5490 m_app_ap->Initialize();
5491 WindowSP &main_window_sp = m_app_ap->GetMainWindow();
5492
5493 MenuSP menubar_sp(new Menu(Menu::Type::Bar));
5494 menubar_sp->AddSubmenu (lldb_menu_sp);
5495 menubar_sp->AddSubmenu (target_menu_sp);
5496 menubar_sp->AddSubmenu (process_menu_sp);
5497 menubar_sp->AddSubmenu (thread_menu_sp);
5498 menubar_sp->AddSubmenu (view_menu_sp);
5499 menubar_sp->AddSubmenu (help_menu_sp);
5500 menubar_sp->SetDelegate(app_menu_delegate_sp);
5501
5502 Rect content_bounds = main_window_sp->GetFrame();
5503 Rect menubar_bounds = content_bounds.MakeMenuBar();
5504 Rect status_bounds = content_bounds.MakeStatusBar();
5505 Rect source_bounds;
5506 Rect variables_bounds;
5507 Rect threads_bounds;
5508 Rect source_variables_bounds;
5509 content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds, threads_bounds);
5510 source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds, variables_bounds);
5511
5512 WindowSP menubar_window_sp = main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false);
5513 // Let the menubar get keys if the active window doesn't handle the
5514 // keys that are typed so it can respond to menubar key presses.
5515 menubar_window_sp->SetCanBeActive(false); // Don't let the menubar become the active window
5516 menubar_window_sp->SetDelegate(menubar_sp);
5517
5518 WindowSP source_window_sp (main_window_sp->CreateSubWindow("Source",
5519 source_bounds,
5520 true));
5521 WindowSP variables_window_sp (main_window_sp->CreateSubWindow("Variables",
5522 variables_bounds,
5523 false));
5524 WindowSP threads_window_sp (main_window_sp->CreateSubWindow("Threads",
5525 threads_bounds,
5526 false));
5527 WindowSP status_window_sp (main_window_sp->CreateSubWindow("Status",
5528 status_bounds,
5529 false));
5530 status_window_sp->SetCanBeActive(false); // Don't let the status bar become the active window
5531 main_window_sp->SetDelegate (std::static_pointer_cast<WindowDelegate>(app_delegate_sp));
5532 source_window_sp->SetDelegate (WindowDelegateSP(new SourceFileWindowDelegate(m_debugger)));
5533 variables_window_sp->SetDelegate (WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
Greg Claytonec990862014-03-19 16:22:48 +00005534 TreeDelegateSP thread_delegate_sp (new ThreadsTreeDelegate(m_debugger));
Greg Clayton44d93782014-01-27 23:43:24 +00005535 threads_window_sp->SetDelegate (WindowDelegateSP(new TreeWindowDelegate(m_debugger, thread_delegate_sp)));
5536 status_window_sp->SetDelegate (WindowDelegateSP(new StatusBarWindowDelegate(m_debugger)));
Greg Clayton5fdb09b2014-01-28 18:41:35 +00005537
5538 // Show the main help window once the first time the curses GUI is launched
5539 static bool g_showed_help = false;
5540 if (!g_showed_help)
5541 {
5542 g_showed_help = true;
5543 main_window_sp->CreateHelpSubwindow();
5544 }
5545
Greg Clayton44d93782014-01-27 23:43:24 +00005546 init_pair (1, COLOR_WHITE , COLOR_BLUE );
5547 init_pair (2, COLOR_BLACK , COLOR_WHITE );
5548 init_pair (3, COLOR_MAGENTA , COLOR_WHITE );
5549 init_pair (4, COLOR_MAGENTA , COLOR_BLACK );
5550 init_pair (5, COLOR_RED , COLOR_BLACK );
Greg Clayton44d93782014-01-27 23:43:24 +00005551 }
5552}
5553
5554void
5555IOHandlerCursesGUI::Deactivate ()
5556{
5557 m_app_ap->Terminate();
5558}
5559
5560void
5561IOHandlerCursesGUI::Run ()
5562{
5563 m_app_ap->Run(m_debugger);
5564 SetIsDone(true);
5565}
5566
Eugene Zelenko315b6882015-10-26 17:00:13 +00005567IOHandlerCursesGUI::~IOHandlerCursesGUI() = default;
Greg Clayton44d93782014-01-27 23:43:24 +00005568
5569void
Greg Claytone68f5d62014-02-24 22:50:57 +00005570IOHandlerCursesGUI::Cancel ()
5571{
5572}
Greg Clayton44d93782014-01-27 23:43:24 +00005573
Greg Claytonf0066ad2014-05-02 00:45:31 +00005574bool
Greg Clayton44d93782014-01-27 23:43:24 +00005575IOHandlerCursesGUI::Interrupt ()
5576{
Greg Claytonf0066ad2014-05-02 00:45:31 +00005577 return false;
Greg Clayton44d93782014-01-27 23:43:24 +00005578}
5579
Greg Clayton44d93782014-01-27 23:43:24 +00005580void
5581IOHandlerCursesGUI::GotEOF()
5582{
5583}
5584
Eugene Zelenko315b6882015-10-26 17:00:13 +00005585#endif // LLDB_DISABLE_CURSES