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