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