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