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