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