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