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