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