1262182Semaste//===-- IOHandler.cpp -------------------------------------------*- C++ -*-===// 2262182Semaste// 3262182Semaste// The LLVM Compiler Infrastructure 4262182Semaste// 5262182Semaste// This file is distributed under the University of Illinois Open Source 6262182Semaste// License. See LICENSE.TXT for details. 7262182Semaste// 8262182Semaste//===----------------------------------------------------------------------===// 9262182Semaste 10262182Semaste 11262182Semaste#include "lldb/lldb-python.h" 12262182Semaste 13262182Semaste#include <string> 14262182Semaste 15262182Semaste#include "lldb/Breakpoint/BreakpointLocation.h" 16262182Semaste#include "lldb/Core/IOHandler.h" 17262182Semaste#include "lldb/Core/Debugger.h" 18262182Semaste#include "lldb/Core/State.h" 19262182Semaste#include "lldb/Core/StreamFile.h" 20262182Semaste#include "lldb/Core/ValueObjectRegister.h" 21262182Semaste#include "lldb/Host/Editline.h" 22262182Semaste#include "lldb/Interpreter/CommandCompletions.h" 23262182Semaste#include "lldb/Interpreter/CommandInterpreter.h" 24262182Semaste#include "lldb/Symbol/Block.h" 25262182Semaste#include "lldb/Symbol/Function.h" 26262182Semaste#include "lldb/Symbol/Symbol.h" 27262182Semaste#include "lldb/Target/RegisterContext.h" 28262182Semaste#include "lldb/Target/ThreadPlan.h" 29262182Semaste 30262182Semaste#ifndef LLDB_DISABLE_CURSES 31262182Semaste#include <ncurses.h> 32262182Semaste#include <panel.h> 33262182Semaste#endif 34262182Semaste 35262182Semasteusing namespace lldb; 36262182Semasteusing namespace lldb_private; 37262182Semaste 38262182SemasteIOHandler::IOHandler (Debugger &debugger) : 39262182Semaste IOHandler (debugger, 40262182Semaste StreamFileSP(), // Adopt STDIN from top input reader 41262182Semaste StreamFileSP(), // Adopt STDOUT from top input reader 42262182Semaste StreamFileSP(), // Adopt STDERR from top input reader 43262182Semaste 0) // Flags 44262182Semaste{ 45262182Semaste} 46262182Semaste 47262182Semaste 48262182SemasteIOHandler::IOHandler (Debugger &debugger, 49262182Semaste const lldb::StreamFileSP &input_sp, 50262182Semaste const lldb::StreamFileSP &output_sp, 51262182Semaste const lldb::StreamFileSP &error_sp, 52262182Semaste uint32_t flags) : 53262182Semaste m_debugger (debugger), 54262182Semaste m_input_sp (input_sp), 55262182Semaste m_output_sp (output_sp), 56262182Semaste m_error_sp (error_sp), 57262182Semaste m_flags (flags), 58262182Semaste m_user_data (NULL), 59262182Semaste m_done (false), 60262182Semaste m_active (false) 61262182Semaste{ 62262182Semaste // If any files are not specified, then adopt them from the top input reader. 63262182Semaste if (!m_input_sp || !m_output_sp || !m_error_sp) 64262182Semaste debugger.AdoptTopIOHandlerFilesIfInvalid (m_input_sp, 65262182Semaste m_output_sp, 66262182Semaste m_error_sp); 67262182Semaste} 68262182Semaste 69262182SemasteIOHandler::~IOHandler() 70262182Semaste{ 71262182Semaste} 72262182Semaste 73262182Semaste 74262182Semasteint 75262182SemasteIOHandler::GetInputFD() 76262182Semaste{ 77262182Semaste if (m_input_sp) 78262182Semaste return m_input_sp->GetFile().GetDescriptor(); 79262182Semaste return -1; 80262182Semaste} 81262182Semaste 82262182Semasteint 83262182SemasteIOHandler::GetOutputFD() 84262182Semaste{ 85262182Semaste if (m_output_sp) 86262182Semaste return m_output_sp->GetFile().GetDescriptor(); 87262182Semaste return -1; 88262182Semaste} 89262182Semaste 90262182Semasteint 91262182SemasteIOHandler::GetErrorFD() 92262182Semaste{ 93262182Semaste if (m_error_sp) 94262182Semaste return m_error_sp->GetFile().GetDescriptor(); 95262182Semaste return -1; 96262182Semaste} 97262182Semaste 98262182SemasteFILE * 99262182SemasteIOHandler::GetInputFILE() 100262182Semaste{ 101262182Semaste if (m_input_sp) 102262182Semaste return m_input_sp->GetFile().GetStream(); 103262182Semaste return NULL; 104262182Semaste} 105262182Semaste 106262182SemasteFILE * 107262182SemasteIOHandler::GetOutputFILE() 108262182Semaste{ 109262182Semaste if (m_output_sp) 110262182Semaste return m_output_sp->GetFile().GetStream(); 111262182Semaste return NULL; 112262182Semaste} 113262182Semaste 114262182SemasteFILE * 115262182SemasteIOHandler::GetErrorFILE() 116262182Semaste{ 117262182Semaste if (m_error_sp) 118262182Semaste return m_error_sp->GetFile().GetStream(); 119262182Semaste return NULL; 120262182Semaste} 121262182Semaste 122262182SemasteStreamFileSP & 123262182SemasteIOHandler::GetInputStreamFile() 124262182Semaste{ 125262182Semaste return m_input_sp; 126262182Semaste} 127262182Semaste 128262182SemasteStreamFileSP & 129262182SemasteIOHandler::GetOutputStreamFile() 130262182Semaste{ 131262182Semaste return m_output_sp; 132262182Semaste} 133262182Semaste 134262182Semaste 135262182SemasteStreamFileSP & 136262182SemasteIOHandler::GetErrorStreamFile() 137262182Semaste{ 138262182Semaste return m_error_sp; 139262182Semaste} 140262182Semaste 141262182Semastebool 142262182SemasteIOHandler::GetIsInteractive () 143262182Semaste{ 144262182Semaste return GetInputStreamFile()->GetFile().GetIsInteractive (); 145262182Semaste} 146262182Semaste 147262182Semastebool 148262182SemasteIOHandler::GetIsRealTerminal () 149262182Semaste{ 150262182Semaste return GetInputStreamFile()->GetFile().GetIsRealTerminal(); 151262182Semaste} 152262182Semaste 153262182SemasteIOHandlerConfirm::IOHandlerConfirm (Debugger &debugger, 154262182Semaste const char *prompt, 155262182Semaste bool default_response) : 156262182Semaste IOHandlerEditline(debugger, 157262182Semaste NULL, // NULL editline_name means no history loaded/saved 158262182Semaste NULL, 159262182Semaste false, // Multi-line 160262182Semaste *this), 161262182Semaste m_default_response (default_response), 162262182Semaste m_user_response (default_response) 163262182Semaste{ 164262182Semaste StreamString prompt_stream; 165262182Semaste prompt_stream.PutCString(prompt); 166262182Semaste if (m_default_response) 167262182Semaste prompt_stream.Printf(": [Y/n] "); 168262182Semaste else 169262182Semaste prompt_stream.Printf(": [y/N] "); 170262182Semaste 171262182Semaste SetPrompt (prompt_stream.GetString().c_str()); 172262182Semaste 173262182Semaste} 174262182Semaste 175262182Semaste 176262182SemasteIOHandlerConfirm::~IOHandlerConfirm () 177262182Semaste{ 178262182Semaste} 179262182Semaste 180262182Semasteint 181262182SemasteIOHandlerConfirm::IOHandlerComplete (IOHandler &io_handler, 182262182Semaste const char *current_line, 183262182Semaste const char *cursor, 184262182Semaste const char *last_char, 185262182Semaste int skip_first_n_matches, 186262182Semaste int max_matches, 187262182Semaste StringList &matches) 188262182Semaste{ 189262182Semaste if (current_line == cursor) 190262182Semaste { 191262182Semaste if (m_default_response) 192262182Semaste { 193262182Semaste matches.AppendString("y"); 194262182Semaste } 195262182Semaste else 196262182Semaste { 197262182Semaste matches.AppendString("n"); 198262182Semaste } 199262182Semaste } 200262182Semaste return matches.GetSize(); 201262182Semaste} 202262182Semaste 203262182Semastevoid 204262182SemasteIOHandlerConfirm::IOHandlerInputComplete (IOHandler &io_handler, std::string &line) 205262182Semaste{ 206262182Semaste if (line.empty()) 207262182Semaste { 208262182Semaste // User just hit enter, set the response to the default 209262182Semaste m_user_response = m_default_response; 210262182Semaste io_handler.SetIsDone(true); 211262182Semaste return; 212262182Semaste } 213262182Semaste 214262182Semaste if (line.size() == 1) 215262182Semaste { 216262182Semaste switch (line[0]) 217262182Semaste { 218262182Semaste case 'y': 219262182Semaste case 'Y': 220262182Semaste m_user_response = true; 221262182Semaste io_handler.SetIsDone(true); 222262182Semaste return; 223262182Semaste case 'n': 224262182Semaste case 'N': 225262182Semaste m_user_response = false; 226262182Semaste io_handler.SetIsDone(true); 227262182Semaste return; 228262182Semaste default: 229262182Semaste break; 230262182Semaste } 231262182Semaste } 232262182Semaste 233262182Semaste if (line == "yes" || line == "YES" || line == "Yes") 234262182Semaste { 235262182Semaste m_user_response = true; 236262182Semaste io_handler.SetIsDone(true); 237262182Semaste } 238262182Semaste else if (line == "no" || line == "NO" || line == "No") 239262182Semaste { 240262182Semaste m_user_response = false; 241262182Semaste io_handler.SetIsDone(true); 242262182Semaste } 243262182Semaste} 244262182Semaste 245262182Semasteint 246262182SemasteIOHandlerDelegate::IOHandlerComplete (IOHandler &io_handler, 247262182Semaste const char *current_line, 248262182Semaste const char *cursor, 249262182Semaste const char *last_char, 250262182Semaste int skip_first_n_matches, 251262182Semaste int max_matches, 252262182Semaste StringList &matches) 253262182Semaste{ 254262182Semaste switch (m_completion) 255262182Semaste { 256262182Semaste case Completion::None: 257262182Semaste break; 258262182Semaste 259262182Semaste case Completion::LLDBCommand: 260262182Semaste return io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion (current_line, 261262182Semaste cursor, 262262182Semaste last_char, 263262182Semaste skip_first_n_matches, 264262182Semaste max_matches, 265262182Semaste matches); 266262182Semaste 267262182Semaste case Completion::Expression: 268262182Semaste { 269262182Semaste bool word_complete = false; 270262182Semaste const char *word_start = cursor; 271262182Semaste if (cursor > current_line) 272262182Semaste --word_start; 273262182Semaste while (word_start > current_line && !isspace(*word_start)) 274262182Semaste --word_start; 275262182Semaste CommandCompletions::InvokeCommonCompletionCallbacks (io_handler.GetDebugger().GetCommandInterpreter(), 276262182Semaste CommandCompletions::eVariablePathCompletion, 277262182Semaste word_start, 278262182Semaste skip_first_n_matches, 279262182Semaste max_matches, 280262182Semaste NULL, 281262182Semaste word_complete, 282262182Semaste matches); 283262182Semaste 284262182Semaste size_t num_matches = matches.GetSize(); 285262182Semaste if (num_matches > 0) 286262182Semaste { 287262182Semaste std::string common_prefix; 288262182Semaste matches.LongestCommonPrefix (common_prefix); 289262182Semaste const size_t partial_name_len = strlen(word_start); 290262182Semaste 291262182Semaste // If we matched a unique single command, add a space... 292262182Semaste // Only do this if the completer told us this was a complete word, however... 293262182Semaste if (num_matches == 1 && word_complete) 294262182Semaste { 295262182Semaste common_prefix.push_back(' '); 296262182Semaste } 297262182Semaste common_prefix.erase (0, partial_name_len); 298262182Semaste matches.InsertStringAtIndex(0, std::move(common_prefix)); 299262182Semaste } 300262182Semaste return num_matches; 301262182Semaste } 302262182Semaste break; 303262182Semaste } 304262182Semaste 305262182Semaste 306262182Semaste return 0; 307262182Semaste} 308262182Semaste 309262182Semaste 310262182SemasteIOHandlerEditline::IOHandlerEditline (Debugger &debugger, 311262182Semaste const char *editline_name, // Used for saving history files 312262182Semaste const char *prompt, 313262182Semaste bool multi_line, 314262182Semaste IOHandlerDelegate &delegate) : 315262182Semaste IOHandlerEditline(debugger, 316262182Semaste StreamFileSP(), // Inherit input from top input reader 317262182Semaste StreamFileSP(), // Inherit output from top input reader 318262182Semaste StreamFileSP(), // Inherit error from top input reader 319262182Semaste 0, // Flags 320262182Semaste editline_name, // Used for saving history files 321262182Semaste prompt, 322262182Semaste multi_line, 323262182Semaste delegate) 324262182Semaste{ 325262182Semaste} 326262182Semaste 327262182SemasteIOHandlerEditline::IOHandlerEditline (Debugger &debugger, 328262182Semaste const lldb::StreamFileSP &input_sp, 329262182Semaste const lldb::StreamFileSP &output_sp, 330262182Semaste const lldb::StreamFileSP &error_sp, 331262182Semaste uint32_t flags, 332262182Semaste const char *editline_name, // Used for saving history files 333262182Semaste const char *prompt, 334262182Semaste bool multi_line, 335262182Semaste IOHandlerDelegate &delegate) : 336262182Semaste IOHandler (debugger, input_sp, output_sp, error_sp, flags), 337262182Semaste m_editline_ap (), 338262182Semaste m_delegate (delegate), 339262182Semaste m_prompt (), 340262182Semaste m_multi_line (multi_line) 341262182Semaste{ 342262182Semaste SetPrompt(prompt); 343262182Semaste 344262182Semaste bool use_editline = false; 345262182Semaste 346262182Semaste#ifndef _MSC_VER 347262182Semaste use_editline = m_input_sp->GetFile().GetIsRealTerminal(); 348262182Semaste#else 349262182Semaste use_editline = true; 350262182Semaste#endif 351262182Semaste 352262182Semaste if (use_editline) 353262182Semaste { 354262182Semaste m_editline_ap.reset(new Editline (editline_name, 355262182Semaste prompt ? prompt : "", 356262182Semaste GetInputFILE (), 357262182Semaste GetOutputFILE (), 358262182Semaste GetErrorFILE ())); 359262182Semaste m_editline_ap->SetLineCompleteCallback (LineCompletedCallback, this); 360262182Semaste m_editline_ap->SetAutoCompleteCallback (AutoCompleteCallback, this); 361262182Semaste } 362262182Semaste 363262182Semaste} 364262182Semaste 365262182SemasteIOHandlerEditline::~IOHandlerEditline () 366262182Semaste{ 367262182Semaste m_editline_ap.reset(); 368262182Semaste} 369262182Semaste 370262182Semaste 371262182Semastebool 372262182SemasteIOHandlerEditline::GetLine (std::string &line) 373262182Semaste{ 374262182Semaste if (m_editline_ap) 375262182Semaste { 376262182Semaste return m_editline_ap->GetLine(line).Success(); 377262182Semaste } 378262182Semaste else 379262182Semaste { 380262182Semaste line.clear(); 381262182Semaste 382262182Semaste FILE *in = GetInputFILE(); 383262182Semaste if (in) 384262182Semaste { 385262182Semaste if (GetIsInteractive()) 386262182Semaste { 387262182Semaste const char *prompt = GetPrompt(); 388262182Semaste if (prompt && prompt[0]) 389262182Semaste { 390262182Semaste FILE *out = GetOutputFILE(); 391262182Semaste if (out) 392262182Semaste { 393262182Semaste ::fprintf(out, "%s", prompt); 394262182Semaste ::fflush(out); 395262182Semaste } 396262182Semaste } 397262182Semaste } 398262182Semaste char buffer[256]; 399262182Semaste bool done = false; 400262182Semaste bool got_line = false; 401262182Semaste while (!done) 402262182Semaste { 403262182Semaste if (fgets(buffer, sizeof(buffer), in) == NULL) 404262182Semaste done = true; 405262182Semaste else 406262182Semaste { 407262182Semaste got_line = true; 408262182Semaste size_t buffer_len = strlen(buffer); 409262182Semaste assert (buffer[buffer_len] == '\0'); 410262182Semaste char last_char = buffer[buffer_len-1]; 411262182Semaste if (last_char == '\r' || last_char == '\n') 412262182Semaste { 413262182Semaste done = true; 414262182Semaste // Strip trailing newlines 415262182Semaste while (last_char == '\r' || last_char == '\n') 416262182Semaste { 417262182Semaste --buffer_len; 418262182Semaste if (buffer_len == 0) 419262182Semaste break; 420262182Semaste last_char = buffer[buffer_len-1]; 421262182Semaste } 422262182Semaste } 423262182Semaste line.append(buffer, buffer_len); 424262182Semaste } 425262182Semaste } 426262182Semaste // We might have gotten a newline on a line by itself 427262182Semaste // make sure to return true in this case. 428262182Semaste return got_line; 429262182Semaste } 430262182Semaste else 431262182Semaste { 432262182Semaste // No more input file, we are done... 433262182Semaste SetIsDone(true); 434262182Semaste } 435262182Semaste return false; 436262182Semaste } 437262182Semaste} 438262182Semaste 439262182Semaste 440262182SemasteLineStatus 441262182SemasteIOHandlerEditline::LineCompletedCallback (Editline *editline, 442262182Semaste StringList &lines, 443262182Semaste uint32_t line_idx, 444262182Semaste Error &error, 445262182Semaste void *baton) 446262182Semaste{ 447262182Semaste IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton; 448262182Semaste return editline_reader->m_delegate.IOHandlerLinesUpdated(*editline_reader, lines, line_idx, error); 449262182Semaste} 450262182Semaste 451262182Semasteint 452262182SemasteIOHandlerEditline::AutoCompleteCallback (const char *current_line, 453262182Semaste const char *cursor, 454262182Semaste const char *last_char, 455262182Semaste int skip_first_n_matches, 456262182Semaste int max_matches, 457262182Semaste StringList &matches, 458262182Semaste void *baton) 459262182Semaste{ 460262182Semaste IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton; 461262182Semaste if (editline_reader) 462262182Semaste return editline_reader->m_delegate.IOHandlerComplete (*editline_reader, 463262182Semaste current_line, 464262182Semaste cursor, 465262182Semaste last_char, 466262182Semaste skip_first_n_matches, 467262182Semaste max_matches, 468262182Semaste matches); 469262182Semaste return 0; 470262182Semaste} 471262182Semaste 472262182Semasteconst char * 473262182SemasteIOHandlerEditline::GetPrompt () 474262182Semaste{ 475262182Semaste if (m_editline_ap) 476262182Semaste return m_editline_ap->GetPrompt (); 477262182Semaste else if (m_prompt.empty()) 478262182Semaste return NULL; 479262182Semaste return m_prompt.c_str(); 480262182Semaste} 481262182Semaste 482262182Semastebool 483262182SemasteIOHandlerEditline::SetPrompt (const char *p) 484262182Semaste{ 485262182Semaste if (p && p[0]) 486262182Semaste m_prompt = p; 487262182Semaste else 488262182Semaste m_prompt.clear(); 489262182Semaste if (m_editline_ap) 490262182Semaste m_editline_ap->SetPrompt (m_prompt.empty() ? NULL : m_prompt.c_str()); 491262182Semaste return true; 492262182Semaste} 493262182Semaste 494262182Semastebool 495262182SemasteIOHandlerEditline::GetLines (StringList &lines) 496262182Semaste{ 497262182Semaste bool success = false; 498262182Semaste if (m_editline_ap) 499262182Semaste { 500262182Semaste std::string end_token; 501262182Semaste success = m_editline_ap->GetLines(end_token, lines).Success(); 502262182Semaste } 503262182Semaste else 504262182Semaste { 505262182Semaste LineStatus lines_status = LineStatus::Success; 506262182Semaste 507262182Semaste while (lines_status == LineStatus::Success) 508262182Semaste { 509262182Semaste std::string line; 510262182Semaste if (GetLine(line)) 511262182Semaste { 512262182Semaste lines.AppendString(line); 513262182Semaste Error error; 514262182Semaste lines_status = m_delegate.IOHandlerLinesUpdated(*this, lines, lines.GetSize() - 1, error); 515262182Semaste } 516262182Semaste else 517262182Semaste { 518262182Semaste lines_status = LineStatus::Done; 519262182Semaste } 520262182Semaste } 521262182Semaste success = lines.GetSize() > 0; 522262182Semaste } 523262182Semaste return success; 524262182Semaste} 525262182Semaste 526262182Semaste// Each IOHandler gets to run until it is done. It should read data 527262182Semaste// from the "in" and place output into "out" and "err and return 528262182Semaste// when done. 529262182Semastevoid 530262182SemasteIOHandlerEditline::Run () 531262182Semaste{ 532262182Semaste std::string line; 533262182Semaste while (IsActive()) 534262182Semaste { 535262182Semaste if (m_multi_line) 536262182Semaste { 537262182Semaste StringList lines; 538262182Semaste if (GetLines (lines)) 539262182Semaste { 540262182Semaste line = lines.CopyList(); 541262182Semaste m_delegate.IOHandlerInputComplete(*this, line); 542262182Semaste } 543262182Semaste else 544262182Semaste { 545262182Semaste m_done = true; 546262182Semaste } 547262182Semaste } 548262182Semaste else 549262182Semaste { 550262182Semaste if (GetLine(line)) 551262182Semaste { 552262182Semaste m_delegate.IOHandlerInputComplete(*this, line); 553262182Semaste } 554262182Semaste else 555262182Semaste { 556262182Semaste m_done = true; 557262182Semaste } 558262182Semaste } 559262182Semaste } 560262182Semaste} 561262182Semaste 562262182Semastevoid 563262182SemasteIOHandlerEditline::Hide () 564262182Semaste{ 565262182Semaste if (m_editline_ap && m_editline_ap->GettingLine()) 566262182Semaste m_editline_ap->Hide(); 567262182Semaste} 568262182Semaste 569262182Semaste 570262182Semastevoid 571262182SemasteIOHandlerEditline::Refresh () 572262182Semaste{ 573262182Semaste if (m_editline_ap && m_editline_ap->GettingLine()) 574262182Semaste m_editline_ap->Refresh(); 575262182Semaste else 576262182Semaste { 577262182Semaste const char *prompt = GetPrompt(); 578262182Semaste if (prompt && prompt[0]) 579262182Semaste { 580262182Semaste FILE *out = GetOutputFILE(); 581262182Semaste if (out) 582262182Semaste { 583262182Semaste ::fprintf(out, "%s", prompt); 584262182Semaste ::fflush(out); 585262182Semaste } 586262182Semaste } 587262182Semaste } 588262182Semaste} 589262182Semaste 590262182Semastevoid 591262500SemasteIOHandlerEditline::Cancel () 592262500Semaste{ 593262500Semaste if (m_editline_ap) 594262500Semaste m_editline_ap->Interrupt (); 595262500Semaste} 596262500Semaste 597262500Semastevoid 598262182SemasteIOHandlerEditline::Interrupt () 599262182Semaste{ 600262182Semaste if (m_editline_ap) 601262182Semaste m_editline_ap->Interrupt(); 602262182Semaste} 603262182Semaste 604262182Semastevoid 605262182SemasteIOHandlerEditline::GotEOF() 606262182Semaste{ 607262182Semaste if (m_editline_ap) 608262182Semaste m_editline_ap->Interrupt(); 609262182Semaste} 610262182Semaste 611262182Semaste// we may want curses to be disabled for some builds 612262182Semaste// for instance, windows 613262182Semaste#ifndef LLDB_DISABLE_CURSES 614262182Semaste 615262182Semaste#include "lldb/Core/ValueObject.h" 616262182Semaste#include "lldb/Symbol/VariableList.h" 617262182Semaste#include "lldb/Target/Target.h" 618262182Semaste#include "lldb/Target/Process.h" 619262182Semaste#include "lldb/Target/Thread.h" 620262182Semaste#include "lldb/Target/StackFrame.h" 621262182Semaste 622262182Semaste#define KEY_RETURN 10 623262182Semaste#define KEY_ESCAPE 27 624262182Semaste 625262182Semastenamespace curses 626262182Semaste{ 627262182Semaste class Menu; 628262182Semaste class MenuDelegate; 629262182Semaste class Window; 630262182Semaste class WindowDelegate; 631262182Semaste typedef std::shared_ptr<Menu> MenuSP; 632262182Semaste typedef std::shared_ptr<MenuDelegate> MenuDelegateSP; 633262182Semaste typedef std::shared_ptr<Window> WindowSP; 634262182Semaste typedef std::shared_ptr<WindowDelegate> WindowDelegateSP; 635262182Semaste typedef std::vector<MenuSP> Menus; 636262182Semaste typedef std::vector<WindowSP> Windows; 637262182Semaste typedef std::vector<WindowDelegateSP> WindowDelegates; 638262182Semaste 639262182Semaste#if 0 640262182Semastetype summary add -s "x=${var.x}, y=${var.y}" curses::Point 641262182Semastetype summary add -s "w=${var.width}, h=${var.height}" curses::Size 642262182Semastetype summary add -s "${var.origin%S} ${var.size%S}" curses::Rect 643262182Semaste#endif 644262182Semaste struct Point 645262182Semaste { 646262182Semaste int x; 647262182Semaste int y; 648262182Semaste 649262182Semaste Point (int _x = 0, int _y = 0) : 650262182Semaste x(_x), 651262182Semaste y(_y) 652262182Semaste { 653262182Semaste } 654262182Semaste 655262182Semaste void 656262182Semaste Clear () 657262182Semaste { 658262182Semaste x = 0; 659262182Semaste y = 0; 660262182Semaste } 661262182Semaste 662262182Semaste Point & 663262182Semaste operator += (const Point &rhs) 664262182Semaste { 665262182Semaste x += rhs.x; 666262182Semaste y += rhs.y; 667262182Semaste return *this; 668262182Semaste } 669262182Semaste 670262182Semaste void 671262182Semaste Dump () 672262182Semaste { 673262182Semaste printf ("(x=%i, y=%i)\n", x, y); 674262182Semaste } 675262182Semaste 676262182Semaste }; 677262182Semaste 678262182Semaste bool operator == (const Point &lhs, const Point &rhs) 679262182Semaste { 680262182Semaste return lhs.x == rhs.x && lhs.y == rhs.y; 681262182Semaste } 682262182Semaste bool operator != (const Point &lhs, const Point &rhs) 683262182Semaste { 684262182Semaste return lhs.x != rhs.x || lhs.y != rhs.y; 685262182Semaste } 686262182Semaste 687262182Semaste struct Size 688262182Semaste { 689262182Semaste int width; 690262182Semaste int height; 691262182Semaste Size (int w = 0, int h = 0) : 692262182Semaste width (w), 693262182Semaste height (h) 694262182Semaste { 695262182Semaste } 696262182Semaste 697262182Semaste void 698262182Semaste Clear () 699262182Semaste { 700262182Semaste width = 0; 701262182Semaste height = 0; 702262182Semaste } 703262182Semaste 704262182Semaste void 705262182Semaste Dump () 706262182Semaste { 707262182Semaste printf ("(w=%i, h=%i)\n", width, height); 708262182Semaste } 709262182Semaste 710262182Semaste }; 711262182Semaste 712262182Semaste bool operator == (const Size &lhs, const Size &rhs) 713262182Semaste { 714262182Semaste return lhs.width == rhs.width && lhs.height == rhs.height; 715262182Semaste } 716262182Semaste bool operator != (const Size &lhs, const Size &rhs) 717262182Semaste { 718262182Semaste return lhs.width != rhs.width || lhs.height != rhs.height; 719262182Semaste } 720262182Semaste 721262182Semaste struct Rect 722262182Semaste { 723262182Semaste Point origin; 724262182Semaste Size size; 725262182Semaste 726262182Semaste Rect () : 727262182Semaste origin(), 728262182Semaste size() 729262182Semaste { 730262182Semaste } 731262182Semaste 732262182Semaste Rect (const Point &p, const Size &s) : 733262182Semaste origin (p), 734262182Semaste size (s) 735262182Semaste { 736262182Semaste } 737262182Semaste 738262182Semaste void 739262182Semaste Clear () 740262182Semaste { 741262182Semaste origin.Clear(); 742262182Semaste size.Clear(); 743262182Semaste } 744262182Semaste 745262182Semaste void 746262182Semaste Dump () 747262182Semaste { 748262182Semaste printf ("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width, size.height); 749262182Semaste } 750262182Semaste 751262182Semaste void 752262182Semaste Inset (int w, int h) 753262182Semaste { 754262182Semaste if (size.width > w*2) 755262182Semaste size.width -= w*2; 756262182Semaste origin.x += w; 757262182Semaste 758262182Semaste if (size.height > h*2) 759262182Semaste size.height -= h*2; 760262182Semaste origin.y += h; 761262182Semaste } 762262182Semaste // Return a status bar rectangle which is the last line of 763262182Semaste // this rectangle. This rectangle will be modified to not 764262182Semaste // include the status bar area. 765262182Semaste Rect 766262182Semaste MakeStatusBar () 767262182Semaste { 768262182Semaste Rect status_bar; 769262182Semaste if (size.height > 1) 770262182Semaste { 771262182Semaste status_bar.origin.x = origin.x; 772262182Semaste status_bar.origin.y = size.height; 773262182Semaste status_bar.size.width = size.width; 774262182Semaste status_bar.size.height = 1; 775262182Semaste --size.height; 776262182Semaste } 777262182Semaste return status_bar; 778262182Semaste } 779262182Semaste 780262182Semaste // Return a menubar rectangle which is the first line of 781262182Semaste // this rectangle. This rectangle will be modified to not 782262182Semaste // include the menubar area. 783262182Semaste Rect 784262182Semaste MakeMenuBar () 785262182Semaste { 786262182Semaste Rect menubar; 787262182Semaste if (size.height > 1) 788262182Semaste { 789262182Semaste menubar.origin.x = origin.x; 790262182Semaste menubar.origin.y = origin.y; 791262182Semaste menubar.size.width = size.width; 792262182Semaste menubar.size.height = 1; 793262182Semaste ++origin.y; 794262182Semaste --size.height; 795262182Semaste } 796262182Semaste return menubar; 797262182Semaste } 798262182Semaste 799262182Semaste void 800262182Semaste HorizontalSplitPercentage (float top_percentage, Rect &top, Rect &bottom) const 801262182Semaste { 802262182Semaste float top_height = top_percentage * size.height; 803262182Semaste HorizontalSplit (top_height, top, bottom); 804262182Semaste } 805262182Semaste 806262182Semaste void 807262182Semaste HorizontalSplit (int top_height, Rect &top, Rect &bottom) const 808262182Semaste { 809262182Semaste top = *this; 810262182Semaste if (top_height < size.height) 811262182Semaste { 812262182Semaste top.size.height = top_height; 813262182Semaste bottom.origin.x = origin.x; 814262182Semaste bottom.origin.y = origin.y + top.size.height; 815262182Semaste bottom.size.width = size.width; 816262182Semaste bottom.size.height = size.height - top.size.height; 817262182Semaste } 818262182Semaste else 819262182Semaste { 820262182Semaste bottom.Clear(); 821262182Semaste } 822262182Semaste } 823262182Semaste 824262182Semaste void 825262182Semaste VerticalSplitPercentage (float left_percentage, Rect &left, Rect &right) const 826262182Semaste { 827262182Semaste float left_width = left_percentage * size.width; 828262182Semaste VerticalSplit (left_width, left, right); 829262182Semaste } 830262182Semaste 831262182Semaste 832262182Semaste void 833262182Semaste VerticalSplit (int left_width, Rect &left, Rect &right) const 834262182Semaste { 835262182Semaste left = *this; 836262182Semaste if (left_width < size.width) 837262182Semaste { 838262182Semaste left.size.width = left_width; 839262182Semaste right.origin.x = origin.x + left.size.width; 840262182Semaste right.origin.y = origin.y; 841262182Semaste right.size.width = size.width - left.size.width; 842262182Semaste right.size.height = size.height; 843262182Semaste } 844262182Semaste else 845262182Semaste { 846262182Semaste right.Clear(); 847262182Semaste } 848262182Semaste } 849262182Semaste }; 850262182Semaste 851262182Semaste bool operator == (const Rect &lhs, const Rect &rhs) 852262182Semaste { 853262182Semaste return lhs.origin == rhs.origin && lhs.size == rhs.size; 854262182Semaste } 855262182Semaste bool operator != (const Rect &lhs, const Rect &rhs) 856262182Semaste { 857262182Semaste return lhs.origin != rhs.origin || lhs.size != rhs.size; 858262182Semaste } 859262182Semaste 860262182Semaste enum HandleCharResult 861262182Semaste { 862262182Semaste eKeyNotHandled = 0, 863262182Semaste eKeyHandled = 1, 864262182Semaste eQuitApplication = 2 865262182Semaste }; 866262182Semaste 867262182Semaste enum class MenuActionResult 868262182Semaste { 869262182Semaste Handled, 870262182Semaste NotHandled, 871262182Semaste Quit // Exit all menus and quit 872262182Semaste }; 873262182Semaste 874262182Semaste struct KeyHelp 875262182Semaste { 876262182Semaste int ch; 877262182Semaste const char *description; 878262182Semaste }; 879262182Semaste 880262182Semaste class WindowDelegate 881262182Semaste { 882262182Semaste public: 883262182Semaste virtual 884262182Semaste ~WindowDelegate() 885262182Semaste { 886262182Semaste } 887262182Semaste 888262182Semaste virtual bool 889262182Semaste WindowDelegateDraw (Window &window, bool force) 890262182Semaste { 891262182Semaste return false; // Drawing not handled 892262182Semaste } 893262182Semaste 894262182Semaste virtual HandleCharResult 895262182Semaste WindowDelegateHandleChar (Window &window, int key) 896262182Semaste { 897262182Semaste return eKeyNotHandled; 898262182Semaste } 899262182Semaste 900262182Semaste virtual const char * 901262182Semaste WindowDelegateGetHelpText () 902262182Semaste { 903262182Semaste return NULL; 904262182Semaste } 905262182Semaste 906262182Semaste virtual KeyHelp * 907262182Semaste WindowDelegateGetKeyHelp () 908262182Semaste { 909262182Semaste return NULL; 910262182Semaste } 911262182Semaste }; 912262182Semaste 913262182Semaste class HelpDialogDelegate : 914262182Semaste public WindowDelegate 915262182Semaste { 916262182Semaste public: 917262182Semaste HelpDialogDelegate (const char *text, KeyHelp *key_help_array); 918262182Semaste 919262182Semaste virtual 920262182Semaste ~HelpDialogDelegate(); 921262182Semaste 922262182Semaste virtual bool 923262182Semaste WindowDelegateDraw (Window &window, bool force); 924262182Semaste 925262182Semaste virtual HandleCharResult 926262182Semaste WindowDelegateHandleChar (Window &window, int key); 927262182Semaste 928262182Semaste size_t 929262182Semaste GetNumLines() const 930262182Semaste { 931262182Semaste return m_text.GetSize(); 932262182Semaste } 933262182Semaste 934262182Semaste size_t 935262182Semaste GetMaxLineLength () const 936262182Semaste { 937262182Semaste return m_text.GetMaxStringLength(); 938262182Semaste } 939262182Semaste 940262182Semaste protected: 941262182Semaste StringList m_text; 942262182Semaste int m_first_visible_line; 943262182Semaste }; 944262182Semaste 945262182Semaste 946262182Semaste class Window 947262182Semaste { 948262182Semaste public: 949262182Semaste 950262182Semaste Window (const char *name) : 951262182Semaste m_name (name), 952262182Semaste m_window (NULL), 953262182Semaste m_panel (NULL), 954262182Semaste m_parent (NULL), 955262182Semaste m_subwindows (), 956262182Semaste m_delegate_sp (), 957262182Semaste m_curr_active_window_idx (UINT32_MAX), 958262182Semaste m_prev_active_window_idx (UINT32_MAX), 959262182Semaste m_delete (false), 960262182Semaste m_needs_update (true), 961262182Semaste m_can_activate (true), 962262182Semaste m_is_subwin (false) 963262182Semaste { 964262182Semaste } 965262182Semaste 966262182Semaste Window (const char *name, WINDOW *w, bool del = true) : 967262182Semaste m_name (name), 968262182Semaste m_window (NULL), 969262182Semaste m_panel (NULL), 970262182Semaste m_parent (NULL), 971262182Semaste m_subwindows (), 972262182Semaste m_delegate_sp (), 973262182Semaste m_curr_active_window_idx (UINT32_MAX), 974262182Semaste m_prev_active_window_idx (UINT32_MAX), 975262182Semaste m_delete (del), 976262182Semaste m_needs_update (true), 977262182Semaste m_can_activate (true), 978262182Semaste m_is_subwin (false) 979262182Semaste { 980262182Semaste if (w) 981262182Semaste Reset(w); 982262182Semaste } 983262182Semaste 984262182Semaste Window (const char *name, const Rect &bounds) : 985262182Semaste m_name (name), 986262182Semaste m_window (NULL), 987262182Semaste m_parent (NULL), 988262182Semaste m_subwindows (), 989262182Semaste m_delegate_sp (), 990262182Semaste m_curr_active_window_idx (UINT32_MAX), 991262182Semaste m_prev_active_window_idx (UINT32_MAX), 992262182Semaste m_delete (true), 993262182Semaste m_needs_update (true), 994262182Semaste m_can_activate (true), 995262182Semaste m_is_subwin (false) 996262182Semaste { 997262182Semaste Reset (::newwin (bounds.size.height, bounds.size.width, bounds.origin.y, bounds.origin.y)); 998262182Semaste } 999262182Semaste 1000262182Semaste virtual 1001262182Semaste ~Window () 1002262182Semaste { 1003262182Semaste RemoveSubWindows (); 1004262182Semaste Reset (); 1005262182Semaste } 1006262182Semaste 1007262182Semaste void 1008262182Semaste Reset (WINDOW *w = NULL, bool del = true) 1009262182Semaste { 1010262182Semaste if (m_window == w) 1011262182Semaste return; 1012262182Semaste 1013262182Semaste if (m_panel) 1014262182Semaste { 1015262182Semaste ::del_panel (m_panel); 1016262182Semaste m_panel = NULL; 1017262182Semaste } 1018262182Semaste if (m_window && m_delete) 1019262182Semaste { 1020262182Semaste ::delwin (m_window); 1021262182Semaste m_window = NULL; 1022262182Semaste m_delete = false; 1023262182Semaste } 1024262182Semaste if (w) 1025262182Semaste { 1026262182Semaste m_window = w; 1027262182Semaste m_panel = ::new_panel (m_window); 1028262182Semaste m_delete = del; 1029262182Semaste } 1030262182Semaste } 1031262182Semaste 1032262182Semaste void AttributeOn (attr_t attr) { ::wattron (m_window, attr); } 1033262182Semaste void AttributeOff (attr_t attr) { ::wattroff (m_window, attr); } 1034262182Semaste void Box (chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) { ::box(m_window, v_char, h_char); } 1035262182Semaste void Clear () { ::wclear (m_window); } 1036262182Semaste void Erase () { ::werase (m_window); } 1037262182Semaste Rect GetBounds () { return Rect (GetParentOrigin(), GetSize()); } // Get the rectangle in our parent window 1038262182Semaste int GetChar () { return ::wgetch (m_window); } 1039262182Semaste int GetCursorX () { return getcurx (m_window); } 1040262182Semaste int GetCursorY () { return getcury (m_window); } 1041262182Semaste Rect GetFrame () { return Rect (Point(), GetSize()); } // Get our rectangle in our own coordinate system 1042262182Semaste Point GetParentOrigin() { return Point (GetParentX(), GetParentY()); } 1043262182Semaste Size GetSize() { return Size (GetWidth(), GetHeight()); } 1044262182Semaste int GetParentX () { return getparx (m_window); } 1045262182Semaste int GetParentY () { return getpary (m_window); } 1046262182Semaste int GetMaxX() { return getmaxx (m_window); } 1047262182Semaste int GetMaxY() { return getmaxy (m_window); } 1048262182Semaste int GetWidth() { return GetMaxX(); } 1049262182Semaste int GetHeight() { return GetMaxY(); } 1050262182Semaste void MoveCursor (int x, int y) { ::wmove (m_window, y, x); } 1051262182Semaste void MoveWindow (int x, int y) { MoveWindow(Point(x,y)); } 1052262182Semaste void Resize (int w, int h) { ::wresize(m_window, h, w); } 1053262182Semaste void Resize (const Size &size) { ::wresize(m_window, size.height, size.width); } 1054262182Semaste void PutChar (int ch) { ::waddch (m_window, ch); } 1055262182Semaste void PutCString (const char *s, int len = -1) { ::waddnstr (m_window, s, len); } 1056262182Semaste void Refresh () { ::wrefresh (m_window); } 1057262182Semaste void DeferredRefresh () 1058262182Semaste { 1059262182Semaste // We are using panels, so we don't need to call this... 1060262182Semaste //::wnoutrefresh(m_window); 1061262182Semaste } 1062262182Semaste void SetBackground (int color_pair_idx) { ::wbkgd (m_window,COLOR_PAIR(color_pair_idx)); } 1063262182Semaste void UnderlineOn () { AttributeOn(A_UNDERLINE); } 1064262182Semaste void UnderlineOff () { AttributeOff(A_UNDERLINE); } 1065262182Semaste 1066262182Semaste void PutCStringTruncated (const char *s, int right_pad) 1067262182Semaste { 1068262182Semaste int bytes_left = GetWidth() - GetCursorX(); 1069262182Semaste if (bytes_left > right_pad) 1070262182Semaste { 1071262182Semaste bytes_left -= right_pad; 1072262182Semaste ::waddnstr (m_window, s, bytes_left); 1073262182Semaste } 1074262182Semaste } 1075262182Semaste 1076262182Semaste void 1077262182Semaste MoveWindow (const Point &origin) 1078262182Semaste { 1079262182Semaste const bool moving_window = origin != GetParentOrigin(); 1080262182Semaste if (m_is_subwin && moving_window) 1081262182Semaste { 1082262182Semaste // Can't move subwindows, must delete and re-create 1083262182Semaste Size size = GetSize(); 1084262182Semaste Reset (::subwin (m_parent->m_window, 1085262182Semaste size.height, 1086262182Semaste size.width, 1087262182Semaste origin.y, 1088262182Semaste origin.x), true); 1089262182Semaste } 1090262182Semaste else 1091262182Semaste { 1092262182Semaste ::mvwin (m_window, origin.y, origin.x); 1093262182Semaste } 1094262182Semaste } 1095262182Semaste 1096262182Semaste void 1097262182Semaste SetBounds (const Rect &bounds) 1098262182Semaste { 1099262182Semaste const bool moving_window = bounds.origin != GetParentOrigin(); 1100262182Semaste if (m_is_subwin && moving_window) 1101262182Semaste { 1102262182Semaste // Can't move subwindows, must delete and re-create 1103262182Semaste Reset (::subwin (m_parent->m_window, 1104262182Semaste bounds.size.height, 1105262182Semaste bounds.size.width, 1106262182Semaste bounds.origin.y, 1107262182Semaste bounds.origin.x), true); 1108262182Semaste } 1109262182Semaste else 1110262182Semaste { 1111262182Semaste if (moving_window) 1112262182Semaste MoveWindow(bounds.origin); 1113262182Semaste Resize (bounds.size); 1114262182Semaste } 1115262182Semaste } 1116262182Semaste 1117262182Semaste void 1118262182Semaste Printf (const char *format, ...) __attribute__ ((format (printf, 2, 3))) 1119262182Semaste { 1120262182Semaste va_list args; 1121262182Semaste va_start (args, format); 1122262182Semaste vwprintw(m_window, format, args); 1123262182Semaste va_end (args); 1124262182Semaste } 1125262182Semaste 1126262182Semaste void 1127262182Semaste Touch () 1128262182Semaste { 1129262182Semaste ::touchwin (m_window); 1130262182Semaste if (m_parent) 1131262182Semaste m_parent->Touch(); 1132262182Semaste } 1133262182Semaste 1134262182Semaste WindowSP 1135262182Semaste CreateSubWindow (const char *name, const Rect &bounds, bool make_active) 1136262182Semaste { 1137262182Semaste WindowSP subwindow_sp; 1138262182Semaste if (m_window) 1139262182Semaste { 1140262182Semaste subwindow_sp.reset(new Window(name, ::subwin (m_window, 1141262182Semaste bounds.size.height, 1142262182Semaste bounds.size.width, 1143262182Semaste bounds.origin.y, 1144262182Semaste bounds.origin.x), true)); 1145262182Semaste subwindow_sp->m_is_subwin = true; 1146262182Semaste } 1147262182Semaste else 1148262182Semaste { 1149262182Semaste subwindow_sp.reset(new Window(name, ::newwin (bounds.size.height, 1150262182Semaste bounds.size.width, 1151262182Semaste bounds.origin.y, 1152262182Semaste bounds.origin.x), true)); 1153262182Semaste subwindow_sp->m_is_subwin = false; 1154262182Semaste } 1155262182Semaste subwindow_sp->m_parent = this; 1156262182Semaste if (make_active) 1157262182Semaste { 1158262182Semaste m_prev_active_window_idx = m_curr_active_window_idx; 1159262182Semaste m_curr_active_window_idx = m_subwindows.size(); 1160262182Semaste } 1161262182Semaste m_subwindows.push_back(subwindow_sp); 1162262182Semaste ::top_panel (subwindow_sp->m_panel); 1163262182Semaste m_needs_update = true; 1164262182Semaste return subwindow_sp; 1165262182Semaste } 1166262182Semaste 1167262182Semaste bool 1168262182Semaste RemoveSubWindow (Window *window) 1169262182Semaste { 1170262182Semaste Windows::iterator pos, end = m_subwindows.end(); 1171262182Semaste size_t i = 0; 1172262182Semaste for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) 1173262182Semaste { 1174262182Semaste if ((*pos).get() == window) 1175262182Semaste { 1176262182Semaste if (m_prev_active_window_idx == i) 1177262182Semaste m_prev_active_window_idx = UINT32_MAX; 1178262182Semaste else if (m_prev_active_window_idx != UINT32_MAX && m_prev_active_window_idx > i) 1179262182Semaste --m_prev_active_window_idx; 1180262182Semaste 1181262182Semaste if (m_curr_active_window_idx == i) 1182262182Semaste m_curr_active_window_idx = UINT32_MAX; 1183262182Semaste else if (m_curr_active_window_idx != UINT32_MAX && m_curr_active_window_idx > i) 1184262182Semaste --m_curr_active_window_idx; 1185262182Semaste window->Erase(); 1186262182Semaste m_subwindows.erase(pos); 1187262182Semaste m_needs_update = true; 1188262182Semaste if (m_parent) 1189262182Semaste m_parent->Touch(); 1190262182Semaste else 1191262182Semaste ::touchwin (stdscr); 1192262182Semaste return true; 1193262182Semaste } 1194262182Semaste } 1195262182Semaste return false; 1196262182Semaste } 1197262182Semaste 1198262182Semaste WindowSP 1199262182Semaste FindSubWindow (const char *name) 1200262182Semaste { 1201262182Semaste Windows::iterator pos, end = m_subwindows.end(); 1202262182Semaste size_t i = 0; 1203262182Semaste for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) 1204262182Semaste { 1205262182Semaste if ((*pos)->m_name.compare(name) == 0) 1206262182Semaste return *pos; 1207262182Semaste } 1208262182Semaste return WindowSP(); 1209262182Semaste } 1210262182Semaste 1211262182Semaste void 1212262182Semaste RemoveSubWindows () 1213262182Semaste { 1214262182Semaste m_curr_active_window_idx = UINT32_MAX; 1215262182Semaste m_prev_active_window_idx = UINT32_MAX; 1216262182Semaste for (Windows::iterator pos = m_subwindows.begin(); 1217262182Semaste pos != m_subwindows.end(); 1218262182Semaste pos = m_subwindows.erase(pos)) 1219262182Semaste { 1220262182Semaste (*pos)->Erase(); 1221262182Semaste } 1222262182Semaste if (m_parent) 1223262182Semaste m_parent->Touch(); 1224262182Semaste else 1225262182Semaste ::touchwin (stdscr); 1226262182Semaste } 1227262182Semaste 1228262182Semaste WINDOW * 1229262182Semaste get() 1230262182Semaste { 1231262182Semaste return m_window; 1232262182Semaste } 1233262182Semaste 1234262182Semaste operator WINDOW *() 1235262182Semaste { 1236262182Semaste return m_window; 1237262182Semaste } 1238262182Semaste 1239262182Semaste //---------------------------------------------------------------------- 1240262182Semaste // Window drawing utilities 1241262182Semaste //---------------------------------------------------------------------- 1242262182Semaste void 1243262182Semaste DrawTitleBox (const char *title, const char *bottom_message = NULL) 1244262182Semaste { 1245262182Semaste attr_t attr = 0; 1246262182Semaste if (IsActive()) 1247262182Semaste attr = A_BOLD | COLOR_PAIR(2); 1248262182Semaste else 1249262182Semaste attr = 0; 1250262182Semaste if (attr) 1251262182Semaste AttributeOn(attr); 1252262182Semaste 1253262182Semaste Box(); 1254262182Semaste MoveCursor(3, 0); 1255262182Semaste 1256262182Semaste if (title && title[0]) 1257262182Semaste { 1258262182Semaste PutChar ('<'); 1259262182Semaste PutCString (title); 1260262182Semaste PutChar ('>'); 1261262182Semaste } 1262262182Semaste 1263262182Semaste if (bottom_message && bottom_message[0]) 1264262182Semaste { 1265262182Semaste int bottom_message_length = strlen(bottom_message); 1266262182Semaste int x = GetWidth() - 3 - (bottom_message_length + 2); 1267262182Semaste 1268262182Semaste if (x > 0) 1269262182Semaste { 1270262182Semaste MoveCursor (x, GetHeight() - 1); 1271262182Semaste PutChar ('['); 1272262182Semaste PutCString(bottom_message); 1273262182Semaste PutChar (']'); 1274262182Semaste } 1275262182Semaste else 1276262182Semaste { 1277262182Semaste MoveCursor (1, GetHeight() - 1); 1278262182Semaste PutChar ('['); 1279262182Semaste PutCStringTruncated (bottom_message, 1); 1280262182Semaste } 1281262182Semaste } 1282262182Semaste if (attr) 1283262182Semaste AttributeOff(attr); 1284262182Semaste 1285262182Semaste } 1286262182Semaste 1287262182Semaste virtual void 1288262182Semaste Draw (bool force) 1289262182Semaste { 1290262182Semaste if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw (*this, force)) 1291262182Semaste return; 1292262182Semaste 1293262182Semaste for (auto &subwindow_sp : m_subwindows) 1294262182Semaste subwindow_sp->Draw(force); 1295262182Semaste } 1296262182Semaste 1297262182Semaste bool 1298262182Semaste CreateHelpSubwindow () 1299262182Semaste { 1300262182Semaste if (m_delegate_sp) 1301262182Semaste { 1302262182Semaste const char *text = m_delegate_sp->WindowDelegateGetHelpText (); 1303262182Semaste KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp (); 1304262182Semaste if ((text && text[0]) || key_help) 1305262182Semaste { 1306262182Semaste std::auto_ptr<HelpDialogDelegate> help_delegate_ap(new HelpDialogDelegate(text, key_help)); 1307262182Semaste const size_t num_lines = help_delegate_ap->GetNumLines(); 1308262182Semaste const size_t max_length = help_delegate_ap->GetMaxLineLength(); 1309262182Semaste Rect bounds = GetBounds(); 1310262182Semaste bounds.Inset(1, 1); 1311262182Semaste if (max_length + 4 < bounds.size.width) 1312262182Semaste { 1313262182Semaste bounds.origin.x += (bounds.size.width - max_length + 4)/2; 1314262182Semaste bounds.size.width = max_length + 4; 1315262182Semaste } 1316262182Semaste else 1317262182Semaste { 1318262182Semaste if (bounds.size.width > 100) 1319262182Semaste { 1320262182Semaste const int inset_w = bounds.size.width / 4; 1321262182Semaste bounds.origin.x += inset_w; 1322262182Semaste bounds.size.width -= 2*inset_w; 1323262182Semaste } 1324262182Semaste } 1325262182Semaste 1326262182Semaste if (num_lines + 2 < bounds.size.height) 1327262182Semaste { 1328262182Semaste bounds.origin.y += (bounds.size.height - num_lines + 2)/2; 1329262182Semaste bounds.size.height = num_lines + 2; 1330262182Semaste } 1331262182Semaste else 1332262182Semaste { 1333262182Semaste if (bounds.size.height > 100) 1334262182Semaste { 1335262182Semaste const int inset_h = bounds.size.height / 4; 1336262182Semaste bounds.origin.y += inset_h; 1337262182Semaste bounds.size.height -= 2*inset_h; 1338262182Semaste } 1339262182Semaste } 1340262182Semaste WindowSP help_window_sp; 1341262182Semaste Window *parent_window = GetParent(); 1342262182Semaste if (parent_window) 1343262182Semaste help_window_sp = parent_window->CreateSubWindow("Help", bounds, true); 1344262182Semaste else 1345262182Semaste help_window_sp = CreateSubWindow("Help", bounds, true); 1346262182Semaste help_window_sp->SetDelegate(WindowDelegateSP(help_delegate_ap.release())); 1347262182Semaste return true; 1348262182Semaste } 1349262182Semaste } 1350262182Semaste return false; 1351262182Semaste } 1352262182Semaste 1353262182Semaste virtual HandleCharResult 1354262182Semaste HandleChar (int key) 1355262182Semaste { 1356262182Semaste // Always check the active window first 1357262182Semaste HandleCharResult result = eKeyNotHandled; 1358262182Semaste WindowSP active_window_sp = GetActiveWindow (); 1359262182Semaste if (active_window_sp) 1360262182Semaste { 1361262182Semaste result = active_window_sp->HandleChar (key); 1362262182Semaste if (result != eKeyNotHandled) 1363262182Semaste return result; 1364262182Semaste } 1365262182Semaste 1366262182Semaste if (m_delegate_sp) 1367262182Semaste { 1368262182Semaste result = m_delegate_sp->WindowDelegateHandleChar (*this, key); 1369262182Semaste if (result != eKeyNotHandled) 1370262182Semaste return result; 1371262182Semaste } 1372262182Semaste 1373262182Semaste // Then check for any windows that want any keys 1374262182Semaste // that weren't handled. This is typically only 1375262182Semaste // for a menubar. 1376262182Semaste // Make a copy of the subwindows in case any HandleChar() 1377262182Semaste // functions muck with the subwindows. If we don't do this, 1378262182Semaste // we can crash when iterating over the subwindows. 1379262182Semaste Windows subwindows (m_subwindows); 1380262182Semaste for (auto subwindow_sp : subwindows) 1381262182Semaste { 1382262182Semaste if (subwindow_sp->m_can_activate == false) 1383262182Semaste { 1384262182Semaste HandleCharResult result = subwindow_sp->HandleChar(key); 1385262182Semaste if (result != eKeyNotHandled) 1386262182Semaste return result; 1387262182Semaste } 1388262182Semaste } 1389262182Semaste 1390262182Semaste return eKeyNotHandled; 1391262182Semaste } 1392262182Semaste 1393262182Semaste bool 1394262182Semaste SetActiveWindow (Window *window) 1395262182Semaste { 1396262182Semaste const size_t num_subwindows = m_subwindows.size(); 1397262182Semaste for (size_t i=0; i<num_subwindows; ++i) 1398262182Semaste { 1399262182Semaste if (m_subwindows[i].get() == window) 1400262182Semaste { 1401262182Semaste m_prev_active_window_idx = m_curr_active_window_idx; 1402262182Semaste ::top_panel (window->m_panel); 1403262182Semaste m_curr_active_window_idx = i; 1404262182Semaste return true; 1405262182Semaste } 1406262182Semaste } 1407262182Semaste return false; 1408262182Semaste } 1409262182Semaste 1410262182Semaste WindowSP 1411262182Semaste GetActiveWindow () 1412262182Semaste { 1413262182Semaste if (!m_subwindows.empty()) 1414262182Semaste { 1415262182Semaste if (m_curr_active_window_idx >= m_subwindows.size()) 1416262182Semaste { 1417262182Semaste if (m_prev_active_window_idx < m_subwindows.size()) 1418262182Semaste { 1419262182Semaste m_curr_active_window_idx = m_prev_active_window_idx; 1420262182Semaste m_prev_active_window_idx = UINT32_MAX; 1421262182Semaste } 1422262182Semaste else if (IsActive()) 1423262182Semaste { 1424262182Semaste m_prev_active_window_idx = UINT32_MAX; 1425262182Semaste m_curr_active_window_idx = UINT32_MAX; 1426262182Semaste 1427262182Semaste // Find first window that wants to be active if this window is active 1428262182Semaste const size_t num_subwindows = m_subwindows.size(); 1429262182Semaste for (size_t i=0; i<num_subwindows; ++i) 1430262182Semaste { 1431262182Semaste if (m_subwindows[i]->GetCanBeActive()) 1432262182Semaste { 1433262182Semaste m_curr_active_window_idx = i; 1434262182Semaste break; 1435262182Semaste } 1436262182Semaste } 1437262182Semaste } 1438262182Semaste } 1439262182Semaste 1440262182Semaste if (m_curr_active_window_idx < m_subwindows.size()) 1441262182Semaste return m_subwindows[m_curr_active_window_idx]; 1442262182Semaste } 1443262182Semaste return WindowSP(); 1444262182Semaste } 1445262182Semaste 1446262182Semaste bool 1447262182Semaste GetCanBeActive () const 1448262182Semaste { 1449262182Semaste return m_can_activate; 1450262182Semaste } 1451262182Semaste 1452262182Semaste void 1453262182Semaste SetCanBeActive (bool b) 1454262182Semaste { 1455262182Semaste m_can_activate = b; 1456262182Semaste } 1457262182Semaste 1458262182Semaste const WindowDelegateSP & 1459262182Semaste GetDelegate () const 1460262182Semaste { 1461262182Semaste return m_delegate_sp; 1462262182Semaste } 1463262182Semaste 1464262182Semaste void 1465262182Semaste SetDelegate (const WindowDelegateSP &delegate_sp) 1466262182Semaste { 1467262182Semaste m_delegate_sp = delegate_sp; 1468262182Semaste } 1469262182Semaste 1470262182Semaste Window * 1471262182Semaste GetParent () const 1472262182Semaste { 1473262182Semaste return m_parent; 1474262182Semaste } 1475262182Semaste 1476262182Semaste bool 1477262182Semaste IsActive () const 1478262182Semaste { 1479262182Semaste if (m_parent) 1480262182Semaste return m_parent->GetActiveWindow().get() == this; 1481262182Semaste else 1482262182Semaste return true; // Top level window is always active 1483262182Semaste } 1484262182Semaste 1485262182Semaste void 1486262182Semaste SelectNextWindowAsActive () 1487262182Semaste { 1488262182Semaste // Move active focus to next window 1489262182Semaste const size_t num_subwindows = m_subwindows.size(); 1490262182Semaste if (m_curr_active_window_idx == UINT32_MAX) 1491262182Semaste { 1492262182Semaste uint32_t idx = 0; 1493262182Semaste for (auto subwindow_sp : m_subwindows) 1494262182Semaste { 1495262182Semaste if (subwindow_sp->GetCanBeActive()) 1496262182Semaste { 1497262182Semaste m_curr_active_window_idx = idx; 1498262182Semaste break; 1499262182Semaste } 1500262182Semaste ++idx; 1501262182Semaste } 1502262182Semaste } 1503262182Semaste else if (m_curr_active_window_idx + 1 < num_subwindows) 1504262182Semaste { 1505262182Semaste bool handled = false; 1506262182Semaste m_prev_active_window_idx = m_curr_active_window_idx; 1507262182Semaste for (size_t idx=m_curr_active_window_idx + 1; idx<num_subwindows; ++idx) 1508262182Semaste { 1509262182Semaste if (m_subwindows[idx]->GetCanBeActive()) 1510262182Semaste { 1511262182Semaste m_curr_active_window_idx = idx; 1512262182Semaste handled = true; 1513262182Semaste break; 1514262182Semaste } 1515262182Semaste } 1516262182Semaste if (!handled) 1517262182Semaste { 1518262182Semaste for (size_t idx=0; idx<=m_prev_active_window_idx; ++idx) 1519262182Semaste { 1520262182Semaste if (m_subwindows[idx]->GetCanBeActive()) 1521262182Semaste { 1522262182Semaste m_curr_active_window_idx = idx; 1523262182Semaste break; 1524262182Semaste } 1525262182Semaste } 1526262182Semaste } 1527262182Semaste } 1528262182Semaste else 1529262182Semaste { 1530262182Semaste m_prev_active_window_idx = m_curr_active_window_idx; 1531262182Semaste for (size_t idx=0; idx<num_subwindows; ++idx) 1532262182Semaste { 1533262182Semaste if (m_subwindows[idx]->GetCanBeActive()) 1534262182Semaste { 1535262182Semaste m_curr_active_window_idx = idx; 1536262182Semaste break; 1537262182Semaste } 1538262182Semaste } 1539262182Semaste } 1540262182Semaste } 1541262182Semaste 1542262182Semaste const char * 1543262182Semaste GetName () const 1544262182Semaste { 1545262182Semaste return m_name.c_str(); 1546262182Semaste } 1547262182Semaste protected: 1548262182Semaste std::string m_name; 1549262182Semaste WINDOW *m_window; 1550262182Semaste PANEL *m_panel; 1551262182Semaste Window *m_parent; 1552262182Semaste Windows m_subwindows; 1553262182Semaste WindowDelegateSP m_delegate_sp; 1554262182Semaste uint32_t m_curr_active_window_idx; 1555262182Semaste uint32_t m_prev_active_window_idx; 1556262182Semaste bool m_delete; 1557262182Semaste bool m_needs_update; 1558262182Semaste bool m_can_activate; 1559262182Semaste bool m_is_subwin; 1560262182Semaste 1561262182Semaste private: 1562262182Semaste DISALLOW_COPY_AND_ASSIGN(Window); 1563262182Semaste }; 1564262182Semaste 1565262182Semaste class MenuDelegate 1566262182Semaste { 1567262182Semaste public: 1568262182Semaste virtual ~MenuDelegate() {} 1569262182Semaste 1570262182Semaste virtual MenuActionResult 1571262182Semaste MenuDelegateAction (Menu &menu) = 0; 1572262182Semaste }; 1573262182Semaste 1574262182Semaste class Menu : public WindowDelegate 1575262182Semaste { 1576262182Semaste public: 1577262182Semaste enum class Type 1578262182Semaste { 1579262182Semaste Invalid, 1580262182Semaste Bar, 1581262182Semaste Item, 1582262182Semaste Separator 1583262182Semaste }; 1584262182Semaste 1585262182Semaste // Menubar or separator constructor 1586262182Semaste Menu (Type type); 1587262182Semaste 1588262182Semaste // Menuitem constructor 1589262182Semaste Menu (const char *name, 1590262182Semaste const char *key_name, 1591262182Semaste int key_value, 1592262182Semaste uint64_t identifier); 1593262182Semaste 1594262182Semaste virtual ~ 1595262182Semaste Menu () 1596262182Semaste { 1597262182Semaste } 1598262182Semaste 1599262182Semaste const MenuDelegateSP & 1600262182Semaste GetDelegate () const 1601262182Semaste { 1602262182Semaste return m_delegate_sp; 1603262182Semaste } 1604262182Semaste 1605262182Semaste void 1606262182Semaste SetDelegate (const MenuDelegateSP &delegate_sp) 1607262182Semaste { 1608262182Semaste m_delegate_sp = delegate_sp; 1609262182Semaste } 1610262182Semaste 1611262182Semaste void 1612262182Semaste RecalculateNameLengths(); 1613262182Semaste 1614262182Semaste void 1615262182Semaste AddSubmenu (const MenuSP &menu_sp); 1616262182Semaste 1617262182Semaste int 1618262182Semaste DrawAndRunMenu (Window &window); 1619262182Semaste 1620262182Semaste void 1621262182Semaste DrawMenuTitle (Window &window, bool highlight); 1622262182Semaste 1623262182Semaste virtual bool 1624262182Semaste WindowDelegateDraw (Window &window, bool force); 1625262182Semaste 1626262182Semaste virtual HandleCharResult 1627262182Semaste WindowDelegateHandleChar (Window &window, int key); 1628262182Semaste 1629262182Semaste MenuActionResult 1630262182Semaste ActionPrivate (Menu &menu) 1631262182Semaste { 1632262182Semaste MenuActionResult result = MenuActionResult::NotHandled; 1633262182Semaste if (m_delegate_sp) 1634262182Semaste { 1635262182Semaste result = m_delegate_sp->MenuDelegateAction (menu); 1636262182Semaste if (result != MenuActionResult::NotHandled) 1637262182Semaste return result; 1638262182Semaste } 1639262182Semaste else if (m_parent) 1640262182Semaste { 1641262182Semaste result = m_parent->ActionPrivate(menu); 1642262182Semaste if (result != MenuActionResult::NotHandled) 1643262182Semaste return result; 1644262182Semaste } 1645262182Semaste return m_canned_result; 1646262182Semaste } 1647262182Semaste 1648262182Semaste MenuActionResult 1649262182Semaste Action () 1650262182Semaste { 1651262182Semaste // Call the recursive action so it can try to handle it 1652262182Semaste // with the menu delegate, and if not, try our parent menu 1653262182Semaste return ActionPrivate (*this); 1654262182Semaste } 1655262182Semaste 1656262182Semaste void 1657262182Semaste SetCannedResult (MenuActionResult result) 1658262182Semaste { 1659262182Semaste m_canned_result = result; 1660262182Semaste } 1661262182Semaste 1662262182Semaste Menus & 1663262182Semaste GetSubmenus() 1664262182Semaste { 1665262182Semaste return m_submenus; 1666262182Semaste } 1667262182Semaste 1668262182Semaste const Menus & 1669262182Semaste GetSubmenus() const 1670262182Semaste { 1671262182Semaste return m_submenus; 1672262182Semaste } 1673262182Semaste 1674262182Semaste int 1675262182Semaste GetSelectedSubmenuIndex () const 1676262182Semaste { 1677262182Semaste return m_selected; 1678262182Semaste } 1679262182Semaste 1680262182Semaste void 1681262182Semaste SetSelectedSubmenuIndex (int idx) 1682262182Semaste { 1683262182Semaste m_selected = idx; 1684262182Semaste } 1685262182Semaste 1686262182Semaste Type 1687262182Semaste GetType () const 1688262182Semaste { 1689262182Semaste return m_type; 1690262182Semaste } 1691262182Semaste 1692262182Semaste int 1693262182Semaste GetStartingColumn() const 1694262182Semaste { 1695262182Semaste return m_start_col; 1696262182Semaste } 1697262182Semaste 1698262182Semaste void 1699262182Semaste SetStartingColumn(int col) 1700262182Semaste { 1701262182Semaste m_start_col = col; 1702262182Semaste } 1703262182Semaste 1704262182Semaste int 1705262182Semaste GetKeyValue() const 1706262182Semaste { 1707262182Semaste return m_key_value; 1708262182Semaste } 1709262182Semaste 1710262182Semaste void 1711262182Semaste SetKeyValue(int key_value) 1712262182Semaste { 1713262182Semaste m_key_value = key_value; 1714262182Semaste } 1715262182Semaste 1716262182Semaste std::string & 1717262182Semaste GetName() 1718262182Semaste { 1719262182Semaste return m_name; 1720262182Semaste } 1721262182Semaste 1722262182Semaste std::string & 1723262182Semaste GetKeyName() 1724262182Semaste { 1725262182Semaste return m_key_name; 1726262182Semaste } 1727262182Semaste 1728262182Semaste int 1729262182Semaste GetDrawWidth () const 1730262182Semaste { 1731262182Semaste return m_max_submenu_name_length + m_max_submenu_key_name_length + 8; 1732262182Semaste } 1733262182Semaste 1734262182Semaste 1735262182Semaste uint64_t 1736262182Semaste GetIdentifier() const 1737262182Semaste { 1738262182Semaste return m_identifier; 1739262182Semaste } 1740262182Semaste 1741262182Semaste void 1742262182Semaste SetIdentifier (uint64_t identifier) 1743262182Semaste { 1744262182Semaste m_identifier = identifier; 1745262182Semaste } 1746262182Semaste 1747262182Semaste protected: 1748262182Semaste std::string m_name; 1749262182Semaste std::string m_key_name; 1750262182Semaste uint64_t m_identifier; 1751262182Semaste Type m_type; 1752262182Semaste int m_key_value; 1753262182Semaste int m_start_col; 1754262182Semaste int m_max_submenu_name_length; 1755262182Semaste int m_max_submenu_key_name_length; 1756262182Semaste int m_selected; 1757262182Semaste Menu *m_parent; 1758262182Semaste Menus m_submenus; 1759262182Semaste WindowSP m_menu_window_sp; 1760262182Semaste MenuActionResult m_canned_result; 1761262182Semaste MenuDelegateSP m_delegate_sp; 1762262182Semaste }; 1763262182Semaste 1764262182Semaste // Menubar or separator constructor 1765262182Semaste Menu::Menu (Type type) : 1766262182Semaste m_name (), 1767262182Semaste m_key_name (), 1768262182Semaste m_identifier (0), 1769262182Semaste m_type (type), 1770262182Semaste m_key_value (0), 1771262182Semaste m_start_col (0), 1772262182Semaste m_max_submenu_name_length (0), 1773262182Semaste m_max_submenu_key_name_length (0), 1774262182Semaste m_selected (0), 1775262182Semaste m_parent (NULL), 1776262182Semaste m_submenus (), 1777262182Semaste m_canned_result (MenuActionResult::NotHandled), 1778262182Semaste m_delegate_sp() 1779262182Semaste { 1780262182Semaste } 1781262182Semaste 1782262182Semaste // Menuitem constructor 1783262182Semaste Menu::Menu (const char *name, 1784262182Semaste const char *key_name, 1785262182Semaste int key_value, 1786262182Semaste uint64_t identifier) : 1787262182Semaste m_name (), 1788262182Semaste m_key_name (), 1789262182Semaste m_identifier (identifier), 1790262182Semaste m_type (Type::Invalid), 1791262182Semaste m_key_value (key_value), 1792262182Semaste m_start_col (0), 1793262182Semaste m_max_submenu_name_length (0), 1794262182Semaste m_max_submenu_key_name_length (0), 1795262182Semaste m_selected (0), 1796262182Semaste m_parent (NULL), 1797262182Semaste m_submenus (), 1798262182Semaste m_canned_result (MenuActionResult::NotHandled), 1799262182Semaste m_delegate_sp() 1800262182Semaste { 1801262182Semaste if (name && name[0]) 1802262182Semaste { 1803262182Semaste m_name = name; 1804262182Semaste m_type = Type::Item; 1805262182Semaste if (key_name && key_name[0]) 1806262182Semaste m_key_name = key_name; 1807262182Semaste } 1808262182Semaste else 1809262182Semaste { 1810262182Semaste m_type = Type::Separator; 1811262182Semaste } 1812262182Semaste } 1813262182Semaste 1814262182Semaste void 1815262182Semaste Menu::RecalculateNameLengths() 1816262182Semaste { 1817262182Semaste m_max_submenu_name_length = 0; 1818262182Semaste m_max_submenu_key_name_length = 0; 1819262182Semaste Menus &submenus = GetSubmenus(); 1820262182Semaste const size_t num_submenus = submenus.size(); 1821262182Semaste for (size_t i=0; i<num_submenus; ++i) 1822262182Semaste { 1823262182Semaste Menu *submenu = submenus[i].get(); 1824262182Semaste if (m_max_submenu_name_length < submenu->m_name.size()) 1825262182Semaste m_max_submenu_name_length = submenu->m_name.size(); 1826262182Semaste if (m_max_submenu_key_name_length < submenu->m_key_name.size()) 1827262182Semaste m_max_submenu_key_name_length = submenu->m_key_name.size(); 1828262182Semaste } 1829262182Semaste } 1830262182Semaste 1831262182Semaste void 1832262182Semaste Menu::AddSubmenu (const MenuSP &menu_sp) 1833262182Semaste { 1834262182Semaste menu_sp->m_parent = this; 1835262182Semaste if (m_max_submenu_name_length < menu_sp->m_name.size()) 1836262182Semaste m_max_submenu_name_length = menu_sp->m_name.size(); 1837262182Semaste if (m_max_submenu_key_name_length < menu_sp->m_key_name.size()) 1838262182Semaste m_max_submenu_key_name_length = menu_sp->m_key_name.size(); 1839262182Semaste m_submenus.push_back(menu_sp); 1840262182Semaste } 1841262182Semaste 1842262182Semaste void 1843262182Semaste Menu::DrawMenuTitle (Window &window, bool highlight) 1844262182Semaste { 1845262182Semaste if (m_type == Type::Separator) 1846262182Semaste { 1847262182Semaste window.MoveCursor(0, window.GetCursorY()); 1848262182Semaste window.PutChar(ACS_LTEE); 1849262182Semaste int width = window.GetWidth(); 1850262182Semaste if (width > 2) 1851262182Semaste { 1852262182Semaste width -= 2; 1853262182Semaste for (size_t i=0; i< width; ++i) 1854262182Semaste window.PutChar(ACS_HLINE); 1855262182Semaste } 1856262182Semaste window.PutChar(ACS_RTEE); 1857262182Semaste } 1858262182Semaste else 1859262182Semaste { 1860262182Semaste const int shortcut_key = m_key_value; 1861262182Semaste bool underlined_shortcut = false; 1862262182Semaste const attr_t hilgight_attr = A_REVERSE; 1863262182Semaste if (highlight) 1864262182Semaste window.AttributeOn(hilgight_attr); 1865262182Semaste if (isprint(shortcut_key)) 1866262182Semaste { 1867262182Semaste size_t lower_pos = m_name.find(tolower(shortcut_key)); 1868262182Semaste size_t upper_pos = m_name.find(toupper(shortcut_key)); 1869262182Semaste const char *name = m_name.c_str(); 1870262182Semaste size_t pos = std::min<size_t>(lower_pos, upper_pos); 1871262182Semaste if (pos != std::string::npos) 1872262182Semaste { 1873262182Semaste underlined_shortcut = true; 1874262182Semaste if (pos > 0) 1875262182Semaste { 1876262182Semaste window.PutCString(name, pos); 1877262182Semaste name += pos; 1878262182Semaste } 1879262182Semaste const attr_t shortcut_attr = A_UNDERLINE|A_BOLD; 1880262182Semaste window.AttributeOn (shortcut_attr); 1881262182Semaste window.PutChar(name[0]); 1882262182Semaste window.AttributeOff(shortcut_attr); 1883262182Semaste name++; 1884262182Semaste if (name[0]) 1885262182Semaste window.PutCString(name); 1886262182Semaste } 1887262182Semaste } 1888262182Semaste 1889262182Semaste if (!underlined_shortcut) 1890262182Semaste { 1891262182Semaste window.PutCString(m_name.c_str()); 1892262182Semaste } 1893262182Semaste 1894262182Semaste if (highlight) 1895262182Semaste window.AttributeOff(hilgight_attr); 1896262182Semaste 1897262182Semaste if (m_key_name.empty()) 1898262182Semaste { 1899262182Semaste if (!underlined_shortcut && isprint(m_key_value)) 1900262182Semaste { 1901262182Semaste window.AttributeOn (COLOR_PAIR(3)); 1902262182Semaste window.Printf (" (%c)", m_key_value); 1903262182Semaste window.AttributeOff (COLOR_PAIR(3)); 1904262182Semaste } 1905262182Semaste } 1906262182Semaste else 1907262182Semaste { 1908262182Semaste window.AttributeOn (COLOR_PAIR(3)); 1909262182Semaste window.Printf (" (%s)", m_key_name.c_str()); 1910262182Semaste window.AttributeOff (COLOR_PAIR(3)); 1911262182Semaste } 1912262182Semaste } 1913262182Semaste } 1914262182Semaste 1915262182Semaste bool 1916262182Semaste Menu::WindowDelegateDraw (Window &window, bool force) 1917262182Semaste { 1918262182Semaste Menus &submenus = GetSubmenus(); 1919262182Semaste const size_t num_submenus = submenus.size(); 1920262182Semaste const int selected_idx = GetSelectedSubmenuIndex(); 1921262182Semaste Menu::Type menu_type = GetType (); 1922262182Semaste switch (menu_type) 1923262182Semaste { 1924262182Semaste case Menu::Type::Bar: 1925262182Semaste { 1926262182Semaste window.SetBackground(2); 1927262182Semaste window.MoveCursor(0, 0); 1928262182Semaste for (size_t i=0; i<num_submenus; ++i) 1929262182Semaste { 1930262182Semaste Menu *menu = submenus[i].get(); 1931262182Semaste if (i > 0) 1932262182Semaste window.PutChar(' '); 1933262182Semaste menu->SetStartingColumn (window.GetCursorX()); 1934262182Semaste window.PutCString("| "); 1935262182Semaste menu->DrawMenuTitle (window, false); 1936262182Semaste } 1937262182Semaste window.PutCString(" |"); 1938262182Semaste window.DeferredRefresh(); 1939262182Semaste } 1940262182Semaste break; 1941262182Semaste 1942262182Semaste case Menu::Type::Item: 1943262182Semaste { 1944262182Semaste int y = 1; 1945262182Semaste int x = 3; 1946262182Semaste // Draw the menu 1947262182Semaste int cursor_x = 0; 1948262182Semaste int cursor_y = 0; 1949262182Semaste window.Erase(); 1950262182Semaste window.SetBackground(2); 1951262182Semaste window.Box(); 1952262182Semaste for (size_t i=0; i<num_submenus; ++i) 1953262182Semaste { 1954262182Semaste const bool is_selected = i == selected_idx; 1955262182Semaste window.MoveCursor(x, y + i); 1956262182Semaste if (is_selected) 1957262182Semaste { 1958262182Semaste // Remember where we want the cursor to be 1959262182Semaste cursor_x = x-1; 1960262182Semaste cursor_y = y+i; 1961262182Semaste } 1962262182Semaste submenus[i]->DrawMenuTitle (window, is_selected); 1963262182Semaste } 1964262182Semaste window.MoveCursor(cursor_x, cursor_y); 1965262182Semaste window.DeferredRefresh(); 1966262182Semaste } 1967262182Semaste break; 1968262182Semaste 1969262182Semaste default: 1970262182Semaste case Menu::Type::Separator: 1971262182Semaste break; 1972262182Semaste } 1973262182Semaste return true; // Drawing handled... 1974262182Semaste } 1975262182Semaste 1976262182Semaste HandleCharResult 1977262182Semaste Menu::WindowDelegateHandleChar (Window &window, int key) 1978262182Semaste { 1979262182Semaste HandleCharResult result = eKeyNotHandled; 1980262182Semaste 1981262182Semaste Menus &submenus = GetSubmenus(); 1982262182Semaste const size_t num_submenus = submenus.size(); 1983262182Semaste const int selected_idx = GetSelectedSubmenuIndex(); 1984262182Semaste Menu::Type menu_type = GetType (); 1985262182Semaste if (menu_type == Menu::Type::Bar) 1986262182Semaste { 1987262182Semaste MenuSP run_menu_sp; 1988262182Semaste switch (key) 1989262182Semaste { 1990262182Semaste case KEY_DOWN: 1991262182Semaste case KEY_UP: 1992262182Semaste // Show last menu or first menu 1993262182Semaste if (selected_idx < num_submenus) 1994262182Semaste run_menu_sp = submenus[selected_idx]; 1995262182Semaste else if (!submenus.empty()) 1996262182Semaste run_menu_sp = submenus.front(); 1997262182Semaste result = eKeyHandled; 1998262182Semaste break; 1999262182Semaste 2000262182Semaste case KEY_RIGHT: 2001262182Semaste { 2002262182Semaste ++m_selected; 2003262182Semaste if (m_selected >= num_submenus) 2004262182Semaste m_selected = 0; 2005262182Semaste if (m_selected < num_submenus) 2006262182Semaste run_menu_sp = submenus[m_selected]; 2007262182Semaste else if (!submenus.empty()) 2008262182Semaste run_menu_sp = submenus.front(); 2009262182Semaste result = eKeyHandled; 2010262182Semaste } 2011262182Semaste break; 2012262182Semaste 2013262182Semaste case KEY_LEFT: 2014262182Semaste { 2015262182Semaste --m_selected; 2016262182Semaste if (m_selected < 0) 2017262182Semaste m_selected = num_submenus - 1; 2018262182Semaste if (m_selected < num_submenus) 2019262182Semaste run_menu_sp = submenus[m_selected]; 2020262182Semaste else if (!submenus.empty()) 2021262182Semaste run_menu_sp = submenus.front(); 2022262182Semaste result = eKeyHandled; 2023262182Semaste } 2024262182Semaste break; 2025262182Semaste 2026262182Semaste default: 2027262182Semaste for (size_t i=0; i<num_submenus; ++i) 2028262182Semaste { 2029262182Semaste if (submenus[i]->GetKeyValue() == key) 2030262182Semaste { 2031262182Semaste SetSelectedSubmenuIndex(i); 2032262182Semaste run_menu_sp = submenus[i]; 2033262182Semaste result = eKeyHandled; 2034262182Semaste break; 2035262182Semaste } 2036262182Semaste } 2037262182Semaste break; 2038262182Semaste } 2039262182Semaste 2040262182Semaste if (run_menu_sp) 2041262182Semaste { 2042262182Semaste // Run the action on this menu in case we need to populate the 2043262182Semaste // menu with dynamic content and also in case check marks, and 2044262182Semaste // any other menu decorations need to be caclulated 2045262182Semaste if (run_menu_sp->Action() == MenuActionResult::Quit) 2046262182Semaste return eQuitApplication; 2047262182Semaste 2048262182Semaste Rect menu_bounds; 2049262182Semaste menu_bounds.origin.x = run_menu_sp->GetStartingColumn(); 2050262182Semaste menu_bounds.origin.y = 1; 2051262182Semaste menu_bounds.size.width = run_menu_sp->GetDrawWidth(); 2052262182Semaste menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2; 2053262182Semaste if (m_menu_window_sp) 2054262182Semaste window.GetParent()->RemoveSubWindow(m_menu_window_sp.get()); 2055262182Semaste 2056262182Semaste m_menu_window_sp = window.GetParent()->CreateSubWindow (run_menu_sp->GetName().c_str(), 2057262182Semaste menu_bounds, 2058262182Semaste true); 2059262182Semaste m_menu_window_sp->SetDelegate (run_menu_sp); 2060262182Semaste } 2061262182Semaste } 2062262182Semaste else if (menu_type == Menu::Type::Item) 2063262182Semaste { 2064262182Semaste switch (key) 2065262182Semaste { 2066262182Semaste case KEY_DOWN: 2067262182Semaste if (m_submenus.size() > 1) 2068262182Semaste { 2069262182Semaste const int start_select = m_selected; 2070262182Semaste while (++m_selected != start_select) 2071262182Semaste { 2072262182Semaste if (m_selected >= num_submenus) 2073262182Semaste m_selected = 0; 2074262182Semaste if (m_submenus[m_selected]->GetType() == Type::Separator) 2075262182Semaste continue; 2076262182Semaste else 2077262182Semaste break; 2078262182Semaste } 2079262182Semaste return eKeyHandled; 2080262182Semaste } 2081262182Semaste break; 2082262182Semaste 2083262182Semaste case KEY_UP: 2084262182Semaste if (m_submenus.size() > 1) 2085262182Semaste { 2086262182Semaste const int start_select = m_selected; 2087262182Semaste while (--m_selected != start_select) 2088262182Semaste { 2089262182Semaste if (m_selected < 0) 2090262182Semaste m_selected = num_submenus - 1; 2091262182Semaste if (m_submenus[m_selected]->GetType() == Type::Separator) 2092262182Semaste continue; 2093262182Semaste else 2094262182Semaste break; 2095262182Semaste } 2096262182Semaste return eKeyHandled; 2097262182Semaste } 2098262182Semaste break; 2099262182Semaste 2100262182Semaste case KEY_RETURN: 2101262182Semaste if (selected_idx < num_submenus) 2102262182Semaste { 2103262182Semaste if (submenus[selected_idx]->Action() == MenuActionResult::Quit) 2104262182Semaste return eQuitApplication; 2105262182Semaste window.GetParent()->RemoveSubWindow(&window); 2106262182Semaste return eKeyHandled; 2107262182Semaste } 2108262182Semaste break; 2109262182Semaste 2110262182Semaste case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in case other chars are entered for escaped sequences 2111262182Semaste window.GetParent()->RemoveSubWindow(&window); 2112262182Semaste return eKeyHandled; 2113262182Semaste 2114262182Semaste default: 2115262182Semaste { 2116262182Semaste bool handled = false; 2117262182Semaste for (size_t i=0; i<num_submenus; ++i) 2118262182Semaste { 2119262182Semaste Menu *menu = submenus[i].get(); 2120262182Semaste if (menu->GetKeyValue() == key) 2121262182Semaste { 2122262182Semaste handled = true; 2123262182Semaste SetSelectedSubmenuIndex(i); 2124262182Semaste window.GetParent()->RemoveSubWindow(&window); 2125262182Semaste if (menu->Action() == MenuActionResult::Quit) 2126262182Semaste return eQuitApplication; 2127262182Semaste return eKeyHandled; 2128262182Semaste } 2129262182Semaste } 2130262182Semaste } 2131262182Semaste break; 2132262182Semaste 2133262182Semaste } 2134262182Semaste } 2135262182Semaste else if (menu_type == Menu::Type::Separator) 2136262182Semaste { 2137262182Semaste 2138262182Semaste } 2139262182Semaste return result; 2140262182Semaste } 2141262182Semaste 2142262182Semaste 2143262182Semaste class Application 2144262182Semaste { 2145262182Semaste public: 2146262182Semaste Application (FILE *in, FILE *out) : 2147262182Semaste m_window_sp(), 2148262182Semaste m_screen (NULL), 2149262182Semaste m_in (in), 2150262182Semaste m_out (out) 2151262182Semaste { 2152262182Semaste 2153262182Semaste } 2154262182Semaste 2155262182Semaste ~Application () 2156262182Semaste { 2157262182Semaste m_window_delegates.clear(); 2158262182Semaste m_window_sp.reset(); 2159262182Semaste if (m_screen) 2160262182Semaste { 2161262182Semaste ::delscreen(m_screen); 2162262182Semaste m_screen = NULL; 2163262182Semaste } 2164262182Semaste } 2165262182Semaste 2166262182Semaste void 2167262182Semaste Initialize () 2168262182Semaste { 2169262182Semaste ::setlocale(LC_ALL, ""); 2170262182Semaste ::setlocale(LC_CTYPE, ""); 2171262182Semaste#if 0 2172262182Semaste ::initscr(); 2173262182Semaste#else 2174262182Semaste m_screen = ::newterm(NULL, m_out, m_in); 2175262182Semaste#endif 2176262182Semaste ::start_color(); 2177262182Semaste ::curs_set(0); 2178262182Semaste ::noecho(); 2179262182Semaste ::keypad(stdscr,TRUE); 2180262182Semaste } 2181262182Semaste 2182262182Semaste void 2183262182Semaste Terminate () 2184262182Semaste { 2185262182Semaste ::endwin(); 2186262182Semaste } 2187262182Semaste 2188262182Semaste void 2189262182Semaste Run (Debugger &debugger) 2190262182Semaste { 2191262182Semaste bool done = false; 2192262182Semaste int delay_in_tenths_of_a_second = 1; 2193262182Semaste 2194262182Semaste // Alas the threading model in curses is a bit lame so we need to 2195262182Semaste // resort to polling every 0.5 seconds. We could poll for stdin 2196262182Semaste // ourselves and then pass the keys down but then we need to 2197262182Semaste // translate all of the escape sequences ourselves. So we resort to 2198262182Semaste // polling for input because we need to receive async process events 2199262182Semaste // while in this loop. 2200262182Semaste 2201262182Semaste halfdelay(delay_in_tenths_of_a_second); // Poll using some number of tenths of seconds seconds when calling Window::GetChar() 2202262182Semaste 2203262182Semaste ListenerSP listener_sp (new Listener ("lldb.IOHandler.curses.Application")); 2204262182Semaste ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass()); 2205262182Semaste ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass()); 2206262182Semaste ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass()); 2207262182Semaste debugger.EnableForwardEvents (listener_sp); 2208262182Semaste 2209262182Semaste bool update = true; 2210262182Semaste#if defined(__APPLE__) 2211262182Semaste std::deque<int> escape_chars; 2212262182Semaste#endif 2213262182Semaste 2214262182Semaste while (!done) 2215262182Semaste { 2216262182Semaste if (update) 2217262182Semaste { 2218262182Semaste m_window_sp->Draw(false); 2219262182Semaste // All windows should be calling Window::DeferredRefresh() instead 2220262182Semaste // of Window::Refresh() so we can do a single update and avoid 2221262182Semaste // any screen blinking 2222262182Semaste update_panels(); 2223262182Semaste 2224262182Semaste // Cursor hiding isn't working on MacOSX, so hide it in the top left corner 2225262182Semaste m_window_sp->MoveCursor(0, 0); 2226262182Semaste 2227262182Semaste doupdate(); 2228262182Semaste update = false; 2229262182Semaste } 2230262182Semaste 2231262182Semaste#if defined(__APPLE__) 2232262182Semaste // Terminal.app doesn't map its function keys correctly, F1-F4 default to: 2233262182Semaste // \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if possible 2234262182Semaste int ch; 2235262182Semaste if (escape_chars.empty()) 2236262182Semaste ch = m_window_sp->GetChar(); 2237262182Semaste else 2238262182Semaste { 2239262182Semaste ch = escape_chars.front(); 2240262182Semaste escape_chars.pop_front(); 2241262182Semaste } 2242262182Semaste if (ch == KEY_ESCAPE) 2243262182Semaste { 2244262182Semaste int ch2 = m_window_sp->GetChar(); 2245262182Semaste if (ch2 == 'O') 2246262182Semaste { 2247262182Semaste int ch3 = m_window_sp->GetChar(); 2248262182Semaste switch (ch3) 2249262182Semaste { 2250262182Semaste case 'P': ch = KEY_F(1); break; 2251262182Semaste case 'Q': ch = KEY_F(2); break; 2252262182Semaste case 'R': ch = KEY_F(3); break; 2253262182Semaste case 'S': ch = KEY_F(4); break; 2254262182Semaste default: 2255262182Semaste escape_chars.push_back(ch2); 2256262182Semaste if (ch3 != -1) 2257262182Semaste escape_chars.push_back(ch3); 2258262182Semaste break; 2259262182Semaste } 2260262182Semaste } 2261262182Semaste else if (ch2 != -1) 2262262182Semaste escape_chars.push_back(ch2); 2263262182Semaste } 2264262182Semaste#else 2265262182Semaste int ch = m_window_sp->GetChar(); 2266262182Semaste 2267262182Semaste#endif 2268262182Semaste if (ch == -1) 2269262182Semaste { 2270262182Semaste if (feof(m_in) || ferror(m_in)) 2271262182Semaste { 2272262182Semaste done = true; 2273262182Semaste } 2274262182Semaste else 2275262182Semaste { 2276262182Semaste // Just a timeout from using halfdelay(), check for events 2277262182Semaste EventSP event_sp; 2278262182Semaste while (listener_sp->PeekAtNextEvent()) 2279262182Semaste { 2280262182Semaste listener_sp->GetNextEvent(event_sp); 2281262182Semaste 2282262182Semaste if (event_sp) 2283262182Semaste { 2284262182Semaste Broadcaster *broadcaster = event_sp->GetBroadcaster(); 2285262182Semaste if (broadcaster) 2286262182Semaste { 2287262182Semaste //uint32_t event_type = event_sp->GetType(); 2288262182Semaste ConstString broadcaster_class (broadcaster->GetBroadcasterClass()); 2289262182Semaste if (broadcaster_class == broadcaster_class_process) 2290262182Semaste { 2291262182Semaste update = true; 2292262182Semaste continue; // Don't get any key, just update our view 2293262182Semaste } 2294262182Semaste } 2295262182Semaste } 2296262182Semaste } 2297262182Semaste } 2298262182Semaste } 2299262182Semaste else 2300262182Semaste { 2301262182Semaste HandleCharResult key_result = m_window_sp->HandleChar(ch); 2302262182Semaste switch (key_result) 2303262182Semaste { 2304262182Semaste case eKeyHandled: 2305262182Semaste update = true; 2306262182Semaste break; 2307262182Semaste case eKeyNotHandled: 2308262182Semaste break; 2309262182Semaste case eQuitApplication: 2310262182Semaste done = true; 2311262182Semaste break; 2312262182Semaste } 2313262182Semaste } 2314262182Semaste } 2315262182Semaste 2316262182Semaste debugger.CancelForwardEvents (listener_sp); 2317262182Semaste 2318262182Semaste } 2319262182Semaste 2320262182Semaste WindowSP & 2321262182Semaste GetMainWindow () 2322262182Semaste { 2323262182Semaste if (!m_window_sp) 2324262182Semaste m_window_sp.reset (new Window ("main", stdscr, false)); 2325262182Semaste return m_window_sp; 2326262182Semaste } 2327262182Semaste 2328262182Semaste WindowDelegates & 2329262182Semaste GetWindowDelegates () 2330262182Semaste { 2331262182Semaste return m_window_delegates; 2332262182Semaste } 2333262182Semaste 2334262182Semaste protected: 2335262182Semaste WindowSP m_window_sp; 2336262182Semaste WindowDelegates m_window_delegates; 2337262182Semaste SCREEN *m_screen; 2338262182Semaste FILE *m_in; 2339262182Semaste FILE *m_out; 2340262182Semaste }; 2341262182Semaste 2342262182Semaste 2343262182Semaste} // namespace curses 2344262182Semaste 2345262182Semaste 2346262182Semasteusing namespace curses; 2347262182Semaste 2348262182Semastestruct Row 2349262182Semaste{ 2350262182Semaste ValueObjectSP valobj; 2351262182Semaste Row *parent; 2352262182Semaste int row_idx; 2353262182Semaste int x; 2354262182Semaste int y; 2355262182Semaste bool might_have_children; 2356262182Semaste bool expanded; 2357262182Semaste bool calculated_children; 2358262182Semaste std::vector<Row> children; 2359262182Semaste 2360262182Semaste Row (const ValueObjectSP &v, Row *p) : 2361262182Semaste valobj (v), 2362262182Semaste parent (p), 2363262182Semaste row_idx(0), 2364262182Semaste x(1), 2365262182Semaste y(1), 2366262182Semaste might_have_children (v ? v->MightHaveChildren() : false), 2367262182Semaste expanded (false), 2368262182Semaste calculated_children (false), 2369262182Semaste children() 2370262182Semaste { 2371262182Semaste } 2372262182Semaste 2373262182Semaste size_t 2374262182Semaste GetDepth () const 2375262182Semaste { 2376262182Semaste if (parent) 2377262182Semaste return 1 + parent->GetDepth(); 2378262182Semaste return 0; 2379262182Semaste } 2380262182Semaste 2381262182Semaste void 2382262182Semaste Expand() 2383262182Semaste { 2384262182Semaste expanded = true; 2385262182Semaste if (!calculated_children) 2386262182Semaste { 2387262182Semaste calculated_children = true; 2388262182Semaste if (valobj) 2389262182Semaste { 2390262182Semaste const size_t num_children = valobj->GetNumChildren(); 2391262182Semaste for (size_t i=0; i<num_children; ++i) 2392262182Semaste { 2393262182Semaste children.push_back(Row (valobj->GetChildAtIndex(i, true), this)); 2394262182Semaste } 2395262182Semaste } 2396262182Semaste } 2397262182Semaste } 2398262182Semaste 2399262182Semaste void 2400262182Semaste Unexpand () 2401262182Semaste { 2402262182Semaste expanded = false; 2403262182Semaste } 2404262182Semaste 2405262182Semaste void 2406262182Semaste DrawTree (Window &window) 2407262182Semaste { 2408262182Semaste if (parent) 2409262182Semaste parent->DrawTreeForChild (window, this, 0); 2410262182Semaste 2411262182Semaste if (might_have_children) 2412262182Semaste { 2413262182Semaste // It we can get UTF8 characters to work we should try to use the "symbol" 2414262182Semaste // UTF8 string below 2415262182Semaste// const char *symbol = ""; 2416262182Semaste// if (row.expanded) 2417262182Semaste// symbol = "\xe2\x96\xbd "; 2418262182Semaste// else 2419262182Semaste// symbol = "\xe2\x96\xb7 "; 2420262182Semaste// window.PutCString (symbol); 2421262182Semaste 2422262182Semaste // The ACS_DARROW and ACS_RARROW don't look very nice they are just a 2423262182Semaste // 'v' or '>' character... 2424262182Semaste// if (expanded) 2425262182Semaste// window.PutChar (ACS_DARROW); 2426262182Semaste// else 2427262182Semaste// window.PutChar (ACS_RARROW); 2428262182Semaste // Since we can't find any good looking right arrow/down arrow 2429262182Semaste // symbols, just use a diamond... 2430262182Semaste window.PutChar (ACS_DIAMOND); 2431262182Semaste window.PutChar (ACS_HLINE); 2432262182Semaste } 2433262182Semaste } 2434262182Semaste 2435262182Semaste void 2436262182Semaste DrawTreeForChild (Window &window, Row *child, uint32_t reverse_depth) 2437262182Semaste { 2438262182Semaste if (parent) 2439262182Semaste parent->DrawTreeForChild (window, this, reverse_depth + 1); 2440262182Semaste 2441262182Semaste if (&children.back() == child) 2442262182Semaste { 2443262182Semaste // Last child 2444262182Semaste if (reverse_depth == 0) 2445262182Semaste { 2446262182Semaste window.PutChar (ACS_LLCORNER); 2447262182Semaste window.PutChar (ACS_HLINE); 2448262182Semaste } 2449262182Semaste else 2450262182Semaste { 2451262182Semaste window.PutChar (' '); 2452262182Semaste window.PutChar (' '); 2453262182Semaste } 2454262182Semaste } 2455262182Semaste else 2456262182Semaste { 2457262182Semaste if (reverse_depth == 0) 2458262182Semaste { 2459262182Semaste window.PutChar (ACS_LTEE); 2460262182Semaste window.PutChar (ACS_HLINE); 2461262182Semaste } 2462262182Semaste else 2463262182Semaste { 2464262182Semaste window.PutChar (ACS_VLINE); 2465262182Semaste window.PutChar (' '); 2466262182Semaste } 2467262182Semaste } 2468262182Semaste } 2469262182Semaste}; 2470262182Semaste 2471262182Semastestruct DisplayOptions 2472262182Semaste{ 2473262182Semaste bool show_types; 2474262182Semaste}; 2475262182Semaste 2476262182Semasteclass TreeItem; 2477262182Semaste 2478262182Semasteclass TreeDelegate 2479262182Semaste{ 2480262182Semastepublic: 2481262182Semaste TreeDelegate() {} 2482262182Semaste virtual ~TreeDelegate() {} 2483262182Semaste virtual void TreeDelegateDrawTreeItem (TreeItem &item, Window &window) = 0; 2484262182Semaste virtual void TreeDelegateGenerateChildren (TreeItem &item) = 0; 2485262182Semaste virtual bool TreeDelegateItemSelected (TreeItem &item) = 0; // Return true if we need to update views 2486262182Semaste}; 2487262182Semastetypedef std::shared_ptr<TreeDelegate> TreeDelegateSP; 2488262182Semaste 2489262182Semasteclass TreeItem 2490262182Semaste{ 2491262182Semastepublic: 2492262182Semaste 2493262182Semaste TreeItem (TreeItem *parent, TreeDelegate &delegate, bool might_have_children) : 2494262182Semaste m_parent (parent), 2495262182Semaste m_delegate (delegate), 2496262182Semaste m_identifier (0), 2497262182Semaste m_row_idx (-1), 2498262182Semaste m_children (), 2499262182Semaste m_might_have_children (might_have_children), 2500262182Semaste m_is_expanded (false) 2501262182Semaste { 2502262182Semaste } 2503262182Semaste 2504262182Semaste TreeItem & 2505262182Semaste operator=(const TreeItem &rhs) 2506262182Semaste { 2507262182Semaste if (this != &rhs) 2508262182Semaste { 2509262182Semaste m_parent = rhs.m_parent; 2510262182Semaste m_delegate = rhs.m_delegate; 2511262182Semaste m_identifier = rhs.m_identifier; 2512262182Semaste m_row_idx = rhs.m_row_idx; 2513262182Semaste m_children = rhs.m_children; 2514262182Semaste m_might_have_children = rhs.m_might_have_children; 2515262182Semaste m_is_expanded = rhs.m_is_expanded; 2516262182Semaste } 2517262182Semaste return *this; 2518262182Semaste } 2519262182Semaste 2520262182Semaste size_t 2521262182Semaste GetDepth () const 2522262182Semaste { 2523262182Semaste if (m_parent) 2524262182Semaste return 1 + m_parent->GetDepth(); 2525262182Semaste return 0; 2526262182Semaste } 2527262182Semaste 2528262182Semaste int 2529262182Semaste GetRowIndex () const 2530262182Semaste { 2531262182Semaste return m_row_idx; 2532262182Semaste } 2533262182Semaste 2534262182Semaste void 2535262182Semaste ClearChildren () 2536262182Semaste { 2537262182Semaste m_children.clear(); 2538262182Semaste } 2539262182Semaste 2540262182Semaste void 2541262182Semaste Resize (size_t n, const TreeItem &t) 2542262182Semaste { 2543262182Semaste m_children.resize(n, t); 2544262182Semaste } 2545262182Semaste 2546262182Semaste TreeItem & 2547262182Semaste operator [](size_t i) 2548262182Semaste { 2549262182Semaste return m_children[i]; 2550262182Semaste } 2551262182Semaste 2552262182Semaste void 2553262182Semaste SetRowIndex (int row_idx) 2554262182Semaste { 2555262182Semaste m_row_idx = row_idx; 2556262182Semaste } 2557262182Semaste 2558262182Semaste size_t 2559262182Semaste GetNumChildren () 2560262182Semaste { 2561262182Semaste m_delegate.TreeDelegateGenerateChildren (*this); 2562262182Semaste return m_children.size(); 2563262182Semaste } 2564262182Semaste 2565262182Semaste void 2566262182Semaste ItemWasSelected () 2567262182Semaste { 2568262182Semaste m_delegate.TreeDelegateItemSelected(*this); 2569262182Semaste } 2570262182Semaste void 2571262182Semaste CalculateRowIndexes (int &row_idx) 2572262182Semaste { 2573262182Semaste SetRowIndex(row_idx); 2574262182Semaste ++row_idx; 2575262182Semaste 2576262182Semaste // The root item must calculate its children 2577262182Semaste if (m_parent == NULL) 2578262182Semaste GetNumChildren(); 2579262182Semaste 2580262182Semaste const bool expanded = IsExpanded(); 2581262182Semaste for (auto &item : m_children) 2582262182Semaste { 2583262182Semaste if (expanded) 2584262182Semaste item.CalculateRowIndexes(row_idx); 2585262182Semaste else 2586262182Semaste item.SetRowIndex(-1); 2587262182Semaste } 2588262182Semaste } 2589262182Semaste 2590262182Semaste TreeItem * 2591262182Semaste GetParent () 2592262182Semaste { 2593262182Semaste return m_parent; 2594262182Semaste } 2595262182Semaste 2596262182Semaste bool 2597262182Semaste IsExpanded () const 2598262182Semaste { 2599262182Semaste return m_is_expanded; 2600262182Semaste } 2601262182Semaste 2602262182Semaste void 2603262182Semaste Expand() 2604262182Semaste { 2605262182Semaste m_is_expanded = true; 2606262182Semaste } 2607262182Semaste 2608262182Semaste void 2609262182Semaste Unexpand () 2610262182Semaste { 2611262182Semaste m_is_expanded = false; 2612262182Semaste } 2613262182Semaste 2614262182Semaste bool 2615262182Semaste Draw (Window &window, 2616262182Semaste const int first_visible_row, 2617262182Semaste const uint32_t selected_row_idx, 2618262182Semaste int &row_idx, 2619262182Semaste int &num_rows_left) 2620262182Semaste { 2621262182Semaste if (num_rows_left <= 0) 2622262182Semaste return false; 2623262182Semaste 2624262182Semaste if (m_row_idx >= first_visible_row) 2625262182Semaste { 2626262182Semaste window.MoveCursor(2, row_idx + 1); 2627262182Semaste 2628262182Semaste if (m_parent) 2629262182Semaste m_parent->DrawTreeForChild (window, this, 0); 2630262182Semaste 2631262182Semaste if (m_might_have_children) 2632262182Semaste { 2633262182Semaste // It we can get UTF8 characters to work we should try to use the "symbol" 2634262182Semaste // UTF8 string below 2635262182Semaste // const char *symbol = ""; 2636262182Semaste // if (row.expanded) 2637262182Semaste // symbol = "\xe2\x96\xbd "; 2638262182Semaste // else 2639262182Semaste // symbol = "\xe2\x96\xb7 "; 2640262182Semaste // window.PutCString (symbol); 2641262182Semaste 2642262182Semaste // The ACS_DARROW and ACS_RARROW don't look very nice they are just a 2643262182Semaste // 'v' or '>' character... 2644262182Semaste // if (expanded) 2645262182Semaste // window.PutChar (ACS_DARROW); 2646262182Semaste // else 2647262182Semaste // window.PutChar (ACS_RARROW); 2648262182Semaste // Since we can't find any good looking right arrow/down arrow 2649262182Semaste // symbols, just use a diamond... 2650262182Semaste window.PutChar (ACS_DIAMOND); 2651262182Semaste window.PutChar (ACS_HLINE); 2652262182Semaste } 2653262182Semaste bool highlight = (selected_row_idx == m_row_idx) && window.IsActive(); 2654262182Semaste 2655262182Semaste if (highlight) 2656262182Semaste window.AttributeOn(A_REVERSE); 2657262182Semaste 2658262182Semaste m_delegate.TreeDelegateDrawTreeItem(*this, window); 2659262182Semaste 2660262182Semaste if (highlight) 2661262182Semaste window.AttributeOff(A_REVERSE); 2662262182Semaste ++row_idx; 2663262182Semaste --num_rows_left; 2664262182Semaste } 2665262182Semaste 2666262182Semaste if (num_rows_left <= 0) 2667262182Semaste return false; // We are done drawing... 2668262182Semaste 2669262182Semaste if (IsExpanded()) 2670262182Semaste { 2671262182Semaste for (auto &item : m_children) 2672262182Semaste { 2673262182Semaste // If we displayed all the rows and item.Draw() returns 2674262182Semaste // false we are done drawing and can exit this for loop 2675262182Semaste if (item.Draw(window, first_visible_row, selected_row_idx, row_idx, num_rows_left) == false) 2676262182Semaste break; 2677262182Semaste } 2678262182Semaste } 2679262182Semaste return num_rows_left >= 0; // Return true if not done drawing yet 2680262182Semaste } 2681262182Semaste 2682262182Semaste void 2683262182Semaste DrawTreeForChild (Window &window, TreeItem *child, uint32_t reverse_depth) 2684262182Semaste { 2685262182Semaste if (m_parent) 2686262182Semaste m_parent->DrawTreeForChild (window, this, reverse_depth + 1); 2687262182Semaste 2688262182Semaste if (&m_children.back() == child) 2689262182Semaste { 2690262182Semaste // Last child 2691262182Semaste if (reverse_depth == 0) 2692262182Semaste { 2693262182Semaste window.PutChar (ACS_LLCORNER); 2694262182Semaste window.PutChar (ACS_HLINE); 2695262182Semaste } 2696262182Semaste else 2697262182Semaste { 2698262182Semaste window.PutChar (' '); 2699262182Semaste window.PutChar (' '); 2700262182Semaste } 2701262182Semaste } 2702262182Semaste else 2703262182Semaste { 2704262182Semaste if (reverse_depth == 0) 2705262182Semaste { 2706262182Semaste window.PutChar (ACS_LTEE); 2707262182Semaste window.PutChar (ACS_HLINE); 2708262182Semaste } 2709262182Semaste else 2710262182Semaste { 2711262182Semaste window.PutChar (ACS_VLINE); 2712262182Semaste window.PutChar (' '); 2713262182Semaste } 2714262182Semaste } 2715262182Semaste } 2716262182Semaste 2717262182Semaste TreeItem * 2718262182Semaste GetItemForRowIndex (uint32_t row_idx) 2719262182Semaste { 2720262182Semaste if (m_row_idx == row_idx) 2721262182Semaste return this; 2722262182Semaste if (m_children.empty()) 2723262182Semaste return NULL; 2724262182Semaste if (m_children.back().m_row_idx < row_idx) 2725262182Semaste return NULL; 2726262182Semaste if (IsExpanded()) 2727262182Semaste { 2728262182Semaste for (auto &item : m_children) 2729262182Semaste { 2730262182Semaste TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx); 2731262182Semaste if (selected_item_ptr) 2732262182Semaste return selected_item_ptr; 2733262182Semaste } 2734262182Semaste } 2735262182Semaste return NULL; 2736262182Semaste } 2737262182Semaste 2738262182Semaste// void * 2739262182Semaste// GetUserData() const 2740262182Semaste// { 2741262182Semaste// return m_user_data; 2742262182Semaste// } 2743262182Semaste// 2744262182Semaste// void 2745262182Semaste// SetUserData (void *user_data) 2746262182Semaste// { 2747262182Semaste// m_user_data = user_data; 2748262182Semaste// } 2749262182Semaste uint64_t 2750262182Semaste GetIdentifier() const 2751262182Semaste { 2752262182Semaste return m_identifier; 2753262182Semaste } 2754262182Semaste 2755262182Semaste void 2756262182Semaste SetIdentifier (uint64_t identifier) 2757262182Semaste { 2758262182Semaste m_identifier = identifier; 2759262182Semaste } 2760262182Semaste 2761262182Semaste 2762262182Semasteprotected: 2763262182Semaste TreeItem *m_parent; 2764262182Semaste TreeDelegate &m_delegate; 2765262182Semaste //void *m_user_data; 2766262182Semaste uint64_t m_identifier; 2767262182Semaste int m_row_idx; // Zero based visible row index, -1 if not visible or for the root item 2768262182Semaste std::vector<TreeItem> m_children; 2769262182Semaste bool m_might_have_children; 2770262182Semaste bool m_is_expanded; 2771262182Semaste 2772262182Semaste}; 2773262182Semaste 2774262182Semasteclass TreeWindowDelegate : public WindowDelegate 2775262182Semaste{ 2776262182Semastepublic: 2777262182Semaste TreeWindowDelegate (Debugger &debugger, const TreeDelegateSP &delegate_sp) : 2778262182Semaste m_debugger (debugger), 2779262182Semaste m_delegate_sp (delegate_sp), 2780262182Semaste m_root (NULL, *delegate_sp, true), 2781262182Semaste m_selected_item (NULL), 2782262182Semaste m_num_rows (0), 2783262182Semaste m_selected_row_idx (0), 2784262182Semaste m_first_visible_row (0), 2785262182Semaste m_min_x (0), 2786262182Semaste m_min_y (0), 2787262182Semaste m_max_x (0), 2788262182Semaste m_max_y (0) 2789262182Semaste { 2790262182Semaste } 2791262182Semaste 2792262182Semaste int 2793262182Semaste NumVisibleRows () const 2794262182Semaste { 2795262182Semaste return m_max_y - m_min_y; 2796262182Semaste } 2797262182Semaste 2798262182Semaste virtual bool 2799262182Semaste WindowDelegateDraw (Window &window, bool force) 2800262182Semaste { 2801262182Semaste ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext()); 2802262182Semaste Process *process = exe_ctx.GetProcessPtr(); 2803262182Semaste 2804262182Semaste bool display_content = false; 2805262182Semaste if (process) 2806262182Semaste { 2807262182Semaste StateType state = process->GetState(); 2808262182Semaste if (StateIsStoppedState(state, true)) 2809262182Semaste { 2810262182Semaste // We are stopped, so it is ok to 2811262182Semaste display_content = true; 2812262182Semaste } 2813262182Semaste else if (StateIsRunningState(state)) 2814262182Semaste { 2815262182Semaste return true; // Don't do any updating when we are running 2816262182Semaste } 2817262182Semaste } 2818262182Semaste 2819262182Semaste m_min_x = 2; 2820262182Semaste m_min_y = 1; 2821262182Semaste m_max_x = window.GetWidth() - 1; 2822262182Semaste m_max_y = window.GetHeight() - 1; 2823262182Semaste 2824262182Semaste window.Erase(); 2825262182Semaste window.DrawTitleBox (window.GetName()); 2826262182Semaste 2827262182Semaste if (display_content) 2828262182Semaste { 2829262182Semaste const int num_visible_rows = NumVisibleRows(); 2830262182Semaste m_num_rows = 0; 2831262182Semaste m_root.CalculateRowIndexes(m_num_rows); 2832262182Semaste 2833262182Semaste // If we unexpanded while having something selected our 2834262182Semaste // total number of rows is less than the num visible rows, 2835262182Semaste // then make sure we show all the rows by setting the first 2836262182Semaste // visible row accordingly. 2837262182Semaste if (m_first_visible_row > 0 && m_num_rows < num_visible_rows) 2838262182Semaste m_first_visible_row = 0; 2839262182Semaste 2840262182Semaste // Make sure the selected row is always visible 2841262182Semaste if (m_selected_row_idx < m_first_visible_row) 2842262182Semaste m_first_visible_row = m_selected_row_idx; 2843262182Semaste else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx) 2844262182Semaste m_first_visible_row = m_selected_row_idx - num_visible_rows + 1; 2845262182Semaste 2846262182Semaste int row_idx = 0; 2847262182Semaste int num_rows_left = num_visible_rows; 2848262182Semaste m_root.Draw (window, m_first_visible_row, m_selected_row_idx, row_idx, num_rows_left); 2849262182Semaste // Get the selected row 2850262182Semaste m_selected_item = m_root.GetItemForRowIndex (m_selected_row_idx); 2851262182Semaste } 2852262182Semaste else 2853262182Semaste { 2854262182Semaste m_selected_item = NULL; 2855262182Semaste } 2856262182Semaste 2857262182Semaste window.DeferredRefresh(); 2858262182Semaste 2859262182Semaste 2860262182Semaste return true; // Drawing handled 2861262182Semaste } 2862262182Semaste 2863262182Semaste 2864262182Semaste virtual const char * 2865262182Semaste WindowDelegateGetHelpText () 2866262182Semaste { 2867262182Semaste return "Thread window keyboard shortcuts:"; 2868262182Semaste } 2869262182Semaste 2870262182Semaste virtual KeyHelp * 2871262182Semaste WindowDelegateGetKeyHelp () 2872262182Semaste { 2873262182Semaste static curses::KeyHelp g_source_view_key_help[] = { 2874262182Semaste { KEY_UP, "Select previous item" }, 2875262182Semaste { KEY_DOWN, "Select next item" }, 2876262182Semaste { KEY_RIGHT, "Expand the selected item" }, 2877262182Semaste { KEY_LEFT, "Unexpand the selected item or select parent if not expanded" }, 2878262182Semaste { KEY_PPAGE, "Page up" }, 2879262182Semaste { KEY_NPAGE, "Page down" }, 2880262182Semaste { 'h', "Show help dialog" }, 2881262182Semaste { ' ', "Toggle item expansion" }, 2882262182Semaste { ',', "Page up" }, 2883262182Semaste { '.', "Page down" }, 2884262182Semaste { '\0', NULL } 2885262182Semaste }; 2886262182Semaste return g_source_view_key_help; 2887262182Semaste } 2888262182Semaste 2889262182Semaste virtual HandleCharResult 2890262182Semaste WindowDelegateHandleChar (Window &window, int c) 2891262182Semaste { 2892262182Semaste switch(c) 2893262182Semaste { 2894262182Semaste case ',': 2895262182Semaste case KEY_PPAGE: 2896262182Semaste // Page up key 2897262182Semaste if (m_first_visible_row > 0) 2898262182Semaste { 2899262182Semaste if (m_first_visible_row > m_max_y) 2900262182Semaste m_first_visible_row -= m_max_y; 2901262182Semaste else 2902262182Semaste m_first_visible_row = 0; 2903262182Semaste m_selected_row_idx = m_first_visible_row; 2904262182Semaste m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); 2905262182Semaste if (m_selected_item) 2906262182Semaste m_selected_item->ItemWasSelected (); 2907262182Semaste } 2908262182Semaste return eKeyHandled; 2909262182Semaste 2910262182Semaste case '.': 2911262182Semaste case KEY_NPAGE: 2912262182Semaste // Page down key 2913262182Semaste if (m_num_rows > m_max_y) 2914262182Semaste { 2915262182Semaste if (m_first_visible_row + m_max_y < m_num_rows) 2916262182Semaste { 2917262182Semaste m_first_visible_row += m_max_y; 2918262182Semaste m_selected_row_idx = m_first_visible_row; 2919262182Semaste m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); 2920262182Semaste if (m_selected_item) 2921262182Semaste m_selected_item->ItemWasSelected (); 2922262182Semaste } 2923262182Semaste } 2924262182Semaste return eKeyHandled; 2925262182Semaste 2926262182Semaste case KEY_UP: 2927262182Semaste if (m_selected_row_idx > 0) 2928262182Semaste { 2929262182Semaste --m_selected_row_idx; 2930262182Semaste m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); 2931262182Semaste if (m_selected_item) 2932262182Semaste m_selected_item->ItemWasSelected (); 2933262182Semaste } 2934262182Semaste return eKeyHandled; 2935262182Semaste case KEY_DOWN: 2936262182Semaste if (m_selected_row_idx + 1 < m_num_rows) 2937262182Semaste { 2938262182Semaste ++m_selected_row_idx; 2939262182Semaste m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); 2940262182Semaste if (m_selected_item) 2941262182Semaste m_selected_item->ItemWasSelected (); 2942262182Semaste } 2943262182Semaste return eKeyHandled; 2944262182Semaste 2945262182Semaste case KEY_RIGHT: 2946262182Semaste if (m_selected_item) 2947262182Semaste { 2948262182Semaste if (!m_selected_item->IsExpanded()) 2949262182Semaste m_selected_item->Expand(); 2950262182Semaste } 2951262182Semaste return eKeyHandled; 2952262182Semaste 2953262182Semaste case KEY_LEFT: 2954262182Semaste if (m_selected_item) 2955262182Semaste { 2956262182Semaste if (m_selected_item->IsExpanded()) 2957262182Semaste m_selected_item->Unexpand(); 2958262182Semaste else if (m_selected_item->GetParent()) 2959262182Semaste { 2960262182Semaste m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex(); 2961262182Semaste m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); 2962262182Semaste if (m_selected_item) 2963262182Semaste m_selected_item->ItemWasSelected (); 2964262182Semaste } 2965262182Semaste } 2966262182Semaste return eKeyHandled; 2967262182Semaste 2968262182Semaste case ' ': 2969262182Semaste // Toggle expansion state when SPACE is pressed 2970262182Semaste if (m_selected_item) 2971262182Semaste { 2972262182Semaste if (m_selected_item->IsExpanded()) 2973262182Semaste m_selected_item->Unexpand(); 2974262182Semaste else 2975262182Semaste m_selected_item->Expand(); 2976262182Semaste } 2977262182Semaste return eKeyHandled; 2978262182Semaste 2979262182Semaste case 'h': 2980262182Semaste window.CreateHelpSubwindow (); 2981262182Semaste return eKeyHandled; 2982262182Semaste 2983262182Semaste default: 2984262182Semaste break; 2985262182Semaste } 2986262182Semaste return eKeyNotHandled; 2987262182Semaste } 2988262182Semaste 2989262182Semasteprotected: 2990262182Semaste Debugger &m_debugger; 2991262182Semaste TreeDelegateSP m_delegate_sp; 2992262182Semaste TreeItem m_root; 2993262182Semaste TreeItem *m_selected_item; 2994262182Semaste int m_num_rows; 2995262182Semaste int m_selected_row_idx; 2996262182Semaste int m_first_visible_row; 2997262182Semaste int m_min_x; 2998262182Semaste int m_min_y; 2999262182Semaste int m_max_x; 3000262182Semaste int m_max_y; 3001262182Semaste 3002262182Semaste}; 3003262182Semaste 3004262182Semasteclass FrameTreeDelegate : public TreeDelegate 3005262182Semaste{ 3006262182Semastepublic: 3007262182Semaste FrameTreeDelegate (const ThreadSP &thread_sp) : 3008262182Semaste TreeDelegate(), 3009262182Semaste m_thread_wp() 3010262182Semaste { 3011262182Semaste if (thread_sp) 3012262182Semaste m_thread_wp = thread_sp; 3013262182Semaste } 3014262182Semaste 3015262182Semaste virtual ~FrameTreeDelegate() 3016262182Semaste { 3017262182Semaste } 3018262182Semaste 3019262182Semaste virtual void 3020262182Semaste TreeDelegateDrawTreeItem (TreeItem &item, Window &window) 3021262182Semaste { 3022262182Semaste ThreadSP thread_sp = m_thread_wp.lock(); 3023262182Semaste if (thread_sp) 3024262182Semaste { 3025262182Semaste const uint64_t frame_idx = item.GetIdentifier(); 3026262182Semaste StackFrameSP frame_sp = thread_sp->GetStackFrameAtIndex(frame_idx); 3027262182Semaste if (frame_sp) 3028262182Semaste { 3029262182Semaste StreamString strm; 3030262182Semaste const SymbolContext &sc = frame_sp->GetSymbolContext(eSymbolContextEverything); 3031262182Semaste ExecutionContext exe_ctx (frame_sp); 3032262182Semaste //const char *frame_format = "frame #${frame.index}: ${module.file.basename}{`${function.name}${function.pc-offset}}}"; 3033262182Semaste const char *frame_format = "frame #${frame.index}: {${function.name}${function.pc-offset}}}"; 3034262182Semaste if (Debugger::FormatPrompt (frame_format, &sc, &exe_ctx, NULL, strm)) 3035262182Semaste { 3036262182Semaste int right_pad = 1; 3037262182Semaste window.PutCStringTruncated(strm.GetString().c_str(), right_pad); 3038262182Semaste } 3039262182Semaste } 3040262182Semaste } 3041262182Semaste } 3042262182Semaste virtual void 3043262182Semaste TreeDelegateGenerateChildren (TreeItem &item) 3044262182Semaste { 3045262182Semaste // No children for frames yet... 3046262182Semaste } 3047262182Semaste 3048262182Semaste virtual bool 3049262182Semaste TreeDelegateItemSelected (TreeItem &item) 3050262182Semaste { 3051262182Semaste ThreadSP thread_sp = m_thread_wp.lock(); 3052262182Semaste if (thread_sp) 3053262182Semaste { 3054262182Semaste const uint64_t frame_idx = item.GetIdentifier(); 3055262182Semaste thread_sp->SetSelectedFrameByIndex(frame_idx); 3056262182Semaste return true; 3057262182Semaste } 3058262182Semaste return false; 3059262182Semaste } 3060262182Semaste void 3061262182Semaste SetThread (ThreadSP thread_sp) 3062262182Semaste { 3063262182Semaste m_thread_wp = thread_sp; 3064262182Semaste } 3065262182Semaste 3066262182Semasteprotected: 3067262182Semaste ThreadWP m_thread_wp; 3068262182Semaste}; 3069262182Semaste 3070262182Semasteclass ThreadTreeDelegate : public TreeDelegate 3071262182Semaste{ 3072262182Semastepublic: 3073262182Semaste ThreadTreeDelegate (Debugger &debugger) : 3074262182Semaste TreeDelegate(), 3075262182Semaste m_debugger (debugger), 3076262182Semaste m_thread_wp (), 3077262182Semaste m_tid (LLDB_INVALID_THREAD_ID), 3078262182Semaste m_stop_id (UINT32_MAX) 3079262182Semaste { 3080262182Semaste } 3081262182Semaste 3082262182Semaste virtual 3083262182Semaste ~ThreadTreeDelegate() 3084262182Semaste { 3085262182Semaste } 3086262182Semaste 3087262182Semaste virtual void 3088262182Semaste TreeDelegateDrawTreeItem (TreeItem &item, Window &window) 3089262182Semaste { 3090262182Semaste ThreadSP thread_sp = m_thread_wp.lock(); 3091262182Semaste if (thread_sp) 3092262182Semaste { 3093262182Semaste StreamString strm; 3094262182Semaste ExecutionContext exe_ctx (thread_sp); 3095262182Semaste const char *format = "thread #${thread.index}: tid = ${thread.id}{, stop reason = ${thread.stop-reason}}"; 3096262182Semaste if (Debugger::FormatPrompt (format, NULL, &exe_ctx, NULL, strm)) 3097262182Semaste { 3098262182Semaste int right_pad = 1; 3099262182Semaste window.PutCStringTruncated(strm.GetString().c_str(), right_pad); 3100262182Semaste } 3101262182Semaste } 3102262182Semaste } 3103262182Semaste virtual void 3104262182Semaste TreeDelegateGenerateChildren (TreeItem &item) 3105262182Semaste { 3106262182Semaste TargetSP target_sp (m_debugger.GetSelectedTarget()); 3107262182Semaste if (target_sp) 3108262182Semaste { 3109262182Semaste ProcessSP process_sp = target_sp->GetProcessSP(); 3110262182Semaste if (process_sp && process_sp->IsAlive()) 3111262182Semaste { 3112262182Semaste StateType state = process_sp->GetState(); 3113262182Semaste if (StateIsStoppedState(state, true)) 3114262182Semaste { 3115262182Semaste ThreadSP thread_sp = process_sp->GetThreadList().GetSelectedThread(); 3116262182Semaste if (thread_sp) 3117262182Semaste { 3118262182Semaste if (m_stop_id == process_sp->GetStopID() && thread_sp->GetID() == m_tid) 3119262182Semaste return; // Children are already up to date 3120262182Semaste if (m_frame_delegate_sp) 3121262182Semaste m_frame_delegate_sp->SetThread(thread_sp); 3122262182Semaste else 3123262182Semaste { 3124262182Semaste // Always expand the thread item the first time we show it 3125262182Semaste item.Expand(); 3126262182Semaste m_frame_delegate_sp.reset (new FrameTreeDelegate(thread_sp)); 3127262182Semaste } 3128262182Semaste 3129262182Semaste m_stop_id = process_sp->GetStopID(); 3130262182Semaste m_thread_wp = thread_sp; 3131262182Semaste m_tid = thread_sp->GetID(); 3132262182Semaste 3133262182Semaste TreeItem t (&item, *m_frame_delegate_sp, false); 3134262182Semaste size_t num_frames = thread_sp->GetStackFrameCount(); 3135262182Semaste item.Resize (num_frames, t); 3136262182Semaste for (size_t i=0; i<num_frames; ++i) 3137262182Semaste { 3138262182Semaste item[i].SetIdentifier(i); 3139262182Semaste } 3140262182Semaste } 3141262182Semaste return; 3142262182Semaste } 3143262182Semaste } 3144262182Semaste } 3145262182Semaste item.ClearChildren(); 3146262182Semaste } 3147262182Semaste 3148262182Semaste virtual bool 3149262182Semaste TreeDelegateItemSelected (TreeItem &item) 3150262182Semaste { 3151262182Semaste ThreadSP thread_sp = m_thread_wp.lock(); 3152262182Semaste if (thread_sp) 3153262182Semaste { 3154262182Semaste ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList(); 3155262182Semaste Mutex::Locker locker (thread_list.GetMutex()); 3156262182Semaste ThreadSP selected_thread_sp = thread_list.GetSelectedThread(); 3157262182Semaste if (selected_thread_sp->GetID() != thread_sp->GetID()) 3158262182Semaste { 3159262182Semaste thread_list.SetSelectedThreadByID(thread_sp->GetID()); 3160262182Semaste return true; 3161262182Semaste } 3162262182Semaste } 3163262182Semaste return false; 3164262182Semaste } 3165262182Semaste 3166262182Semasteprotected: 3167262182Semaste Debugger &m_debugger; 3168262182Semaste ThreadWP m_thread_wp; 3169262182Semaste std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp; 3170262182Semaste lldb::user_id_t m_tid; 3171262182Semaste uint32_t m_stop_id; 3172262182Semaste}; 3173262182Semaste 3174262182Semasteclass ValueObjectListDelegate : public WindowDelegate 3175262182Semaste{ 3176262182Semastepublic: 3177262182Semaste ValueObjectListDelegate () : 3178262182Semaste m_valobj_list (), 3179262182Semaste m_rows (), 3180262182Semaste m_selected_row (NULL), 3181262182Semaste m_selected_row_idx (0), 3182262182Semaste m_first_visible_row (0), 3183262182Semaste m_num_rows (0), 3184262182Semaste m_max_x (0), 3185262182Semaste m_max_y (0) 3186262182Semaste { 3187262182Semaste } 3188262182Semaste 3189262182Semaste ValueObjectListDelegate (ValueObjectList &valobj_list) : 3190262182Semaste m_valobj_list (valobj_list), 3191262182Semaste m_rows (), 3192262182Semaste m_selected_row (NULL), 3193262182Semaste m_selected_row_idx (0), 3194262182Semaste m_first_visible_row (0), 3195262182Semaste m_num_rows (0), 3196262182Semaste m_max_x (0), 3197262182Semaste m_max_y (0) 3198262182Semaste { 3199262182Semaste SetValues (valobj_list); 3200262182Semaste } 3201262182Semaste 3202262182Semaste virtual 3203262182Semaste ~ValueObjectListDelegate() 3204262182Semaste { 3205262182Semaste } 3206262182Semaste 3207262182Semaste void 3208262182Semaste SetValues (ValueObjectList &valobj_list) 3209262182Semaste { 3210262182Semaste m_selected_row = NULL; 3211262182Semaste m_selected_row_idx = 0; 3212262182Semaste m_first_visible_row = 0; 3213262182Semaste m_num_rows = 0; 3214262182Semaste m_rows.clear(); 3215262182Semaste m_valobj_list = valobj_list; 3216262182Semaste const size_t num_values = m_valobj_list.GetSize(); 3217262182Semaste for (size_t i=0; i<num_values; ++i) 3218262182Semaste m_rows.push_back(Row(m_valobj_list.GetValueObjectAtIndex(i), NULL)); 3219262182Semaste } 3220262182Semaste 3221262182Semaste virtual bool 3222262182Semaste WindowDelegateDraw (Window &window, bool force) 3223262182Semaste { 3224262182Semaste m_num_rows = 0; 3225262182Semaste m_min_x = 2; 3226262182Semaste m_min_y = 1; 3227262182Semaste m_max_x = window.GetWidth() - 1; 3228262182Semaste m_max_y = window.GetHeight() - 1; 3229262182Semaste 3230262182Semaste window.Erase(); 3231262182Semaste window.DrawTitleBox (window.GetName()); 3232262182Semaste 3233262182Semaste const int num_visible_rows = NumVisibleRows(); 3234262182Semaste const int num_rows = CalculateTotalNumberRows (m_rows); 3235262182Semaste 3236262182Semaste // If we unexpanded while having something selected our 3237262182Semaste // total number of rows is less than the num visible rows, 3238262182Semaste // then make sure we show all the rows by setting the first 3239262182Semaste // visible row accordingly. 3240262182Semaste if (m_first_visible_row > 0 && num_rows < num_visible_rows) 3241262182Semaste m_first_visible_row = 0; 3242262182Semaste 3243262182Semaste // Make sure the selected row is always visible 3244262182Semaste if (m_selected_row_idx < m_first_visible_row) 3245262182Semaste m_first_visible_row = m_selected_row_idx; 3246262182Semaste else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx) 3247262182Semaste m_first_visible_row = m_selected_row_idx - num_visible_rows + 1; 3248262182Semaste 3249262182Semaste DisplayRows (window, m_rows, g_options); 3250262182Semaste 3251262182Semaste window.DeferredRefresh(); 3252262182Semaste 3253262182Semaste // Get the selected row 3254262182Semaste m_selected_row = GetRowForRowIndex (m_selected_row_idx); 3255262182Semaste // Keep the cursor on the selected row so the highlight and the cursor 3256262182Semaste // are always on the same line 3257262182Semaste if (m_selected_row) 3258262182Semaste window.MoveCursor (m_selected_row->x, 3259262182Semaste m_selected_row->y); 3260262182Semaste 3261262182Semaste return true; // Drawing handled 3262262182Semaste } 3263262182Semaste 3264262182Semaste virtual KeyHelp * 3265262182Semaste WindowDelegateGetKeyHelp () 3266262182Semaste { 3267262182Semaste static curses::KeyHelp g_source_view_key_help[] = { 3268262182Semaste { KEY_UP, "Select previous item" }, 3269262182Semaste { KEY_DOWN, "Select next item" }, 3270262182Semaste { KEY_RIGHT, "Expand selected item" }, 3271262182Semaste { KEY_LEFT, "Unexpand selected item or select parent if not expanded" }, 3272262182Semaste { KEY_PPAGE, "Page up" }, 3273262182Semaste { KEY_NPAGE, "Page down" }, 3274262182Semaste { 'A', "Format as annotated address" }, 3275262182Semaste { 'b', "Format as binary" }, 3276262182Semaste { 'B', "Format as hex bytes with ASCII" }, 3277262182Semaste { 'c', "Format as character" }, 3278262182Semaste { 'd', "Format as a signed integer" }, 3279262182Semaste { 'D', "Format selected value using the default format for the type" }, 3280262182Semaste { 'f', "Format as float" }, 3281262182Semaste { 'h', "Show help dialog" }, 3282262182Semaste { 'i', "Format as instructions" }, 3283262182Semaste { 'o', "Format as octal" }, 3284262182Semaste { 'p', "Format as pointer" }, 3285262182Semaste { 's', "Format as C string" }, 3286262182Semaste { 't', "Toggle showing/hiding type names" }, 3287262182Semaste { 'u', "Format as an unsigned integer" }, 3288262182Semaste { 'x', "Format as hex" }, 3289262182Semaste { 'X', "Format as uppercase hex" }, 3290262182Semaste { ' ', "Toggle item expansion" }, 3291262182Semaste { ',', "Page up" }, 3292262182Semaste { '.', "Page down" }, 3293262182Semaste { '\0', NULL } 3294262182Semaste }; 3295262182Semaste return g_source_view_key_help; 3296262182Semaste } 3297262182Semaste 3298262182Semaste 3299262182Semaste virtual HandleCharResult 3300262182Semaste WindowDelegateHandleChar (Window &window, int c) 3301262182Semaste { 3302262182Semaste switch(c) 3303262182Semaste { 3304262182Semaste case 'x': 3305262182Semaste case 'X': 3306262182Semaste case 'o': 3307262182Semaste case 's': 3308262182Semaste case 'u': 3309262182Semaste case 'd': 3310262182Semaste case 'D': 3311262182Semaste case 'i': 3312262182Semaste case 'A': 3313262182Semaste case 'p': 3314262182Semaste case 'c': 3315262182Semaste case 'b': 3316262182Semaste case 'B': 3317262182Semaste case 'f': 3318262182Semaste // Change the format for the currently selected item 3319262182Semaste if (m_selected_row) 3320262182Semaste m_selected_row->valobj->SetFormat (FormatForChar (c)); 3321262182Semaste return eKeyHandled; 3322262182Semaste 3323262182Semaste case 't': 3324262182Semaste // Toggle showing type names 3325262182Semaste g_options.show_types = !g_options.show_types; 3326262182Semaste return eKeyHandled; 3327262182Semaste 3328262182Semaste case ',': 3329262182Semaste case KEY_PPAGE: 3330262182Semaste // Page up key 3331262182Semaste if (m_first_visible_row > 0) 3332262182Semaste { 3333262182Semaste if (m_first_visible_row > m_max_y) 3334262182Semaste m_first_visible_row -= m_max_y; 3335262182Semaste else 3336262182Semaste m_first_visible_row = 0; 3337262182Semaste m_selected_row_idx = m_first_visible_row; 3338262182Semaste } 3339262182Semaste return eKeyHandled; 3340262182Semaste 3341262182Semaste case '.': 3342262182Semaste case KEY_NPAGE: 3343262182Semaste // Page down key 3344262182Semaste if (m_num_rows > m_max_y) 3345262182Semaste { 3346262182Semaste if (m_first_visible_row + m_max_y < m_num_rows) 3347262182Semaste { 3348262182Semaste m_first_visible_row += m_max_y; 3349262182Semaste m_selected_row_idx = m_first_visible_row; 3350262182Semaste } 3351262182Semaste } 3352262182Semaste return eKeyHandled; 3353262182Semaste 3354262182Semaste case KEY_UP: 3355262182Semaste if (m_selected_row_idx > 0) 3356262182Semaste --m_selected_row_idx; 3357262182Semaste return eKeyHandled; 3358262182Semaste case KEY_DOWN: 3359262182Semaste if (m_selected_row_idx + 1 < m_num_rows) 3360262182Semaste ++m_selected_row_idx; 3361262182Semaste return eKeyHandled; 3362262182Semaste 3363262182Semaste case KEY_RIGHT: 3364262182Semaste if (m_selected_row) 3365262182Semaste { 3366262182Semaste if (!m_selected_row->expanded) 3367262182Semaste m_selected_row->Expand(); 3368262182Semaste } 3369262182Semaste return eKeyHandled; 3370262182Semaste 3371262182Semaste case KEY_LEFT: 3372262182Semaste if (m_selected_row) 3373262182Semaste { 3374262182Semaste if (m_selected_row->expanded) 3375262182Semaste m_selected_row->Unexpand(); 3376262182Semaste else if (m_selected_row->parent) 3377262182Semaste m_selected_row_idx = m_selected_row->parent->row_idx; 3378262182Semaste } 3379262182Semaste return eKeyHandled; 3380262182Semaste 3381262182Semaste case ' ': 3382262182Semaste // Toggle expansion state when SPACE is pressed 3383262182Semaste if (m_selected_row) 3384262182Semaste { 3385262182Semaste if (m_selected_row->expanded) 3386262182Semaste m_selected_row->Unexpand(); 3387262182Semaste else 3388262182Semaste m_selected_row->Expand(); 3389262182Semaste } 3390262182Semaste return eKeyHandled; 3391262182Semaste 3392262182Semaste case 'h': 3393262182Semaste window.CreateHelpSubwindow (); 3394262182Semaste return eKeyHandled; 3395262182Semaste 3396262182Semaste default: 3397262182Semaste break; 3398262182Semaste } 3399262182Semaste return eKeyNotHandled; 3400262182Semaste } 3401262182Semaste 3402262182Semasteprotected: 3403262182Semaste ValueObjectList m_valobj_list; 3404262182Semaste std::vector<Row> m_rows; 3405262182Semaste Row *m_selected_row; 3406262182Semaste uint32_t m_selected_row_idx; 3407262182Semaste uint32_t m_first_visible_row; 3408262182Semaste uint32_t m_num_rows; 3409262182Semaste int m_min_x; 3410262182Semaste int m_min_y; 3411262182Semaste int m_max_x; 3412262182Semaste int m_max_y; 3413262182Semaste 3414262182Semaste static Format 3415262182Semaste FormatForChar (int c) 3416262182Semaste { 3417262182Semaste switch (c) 3418262182Semaste { 3419262182Semaste case 'x': return eFormatHex; 3420262182Semaste case 'X': return eFormatHexUppercase; 3421262182Semaste case 'o': return eFormatOctal; 3422262182Semaste case 's': return eFormatCString; 3423262182Semaste case 'u': return eFormatUnsigned; 3424262182Semaste case 'd': return eFormatDecimal; 3425262182Semaste case 'D': return eFormatDefault; 3426262182Semaste case 'i': return eFormatInstruction; 3427262182Semaste case 'A': return eFormatAddressInfo; 3428262182Semaste case 'p': return eFormatPointer; 3429262182Semaste case 'c': return eFormatChar; 3430262182Semaste case 'b': return eFormatBinary; 3431262182Semaste case 'B': return eFormatBytesWithASCII; 3432262182Semaste case 'f': return eFormatFloat; 3433262182Semaste } 3434262182Semaste return eFormatDefault; 3435262182Semaste } 3436262182Semaste 3437262182Semaste bool 3438262182Semaste DisplayRowObject (Window &window, 3439262182Semaste Row &row, 3440262182Semaste DisplayOptions &options, 3441262182Semaste bool highlight, 3442262182Semaste bool last_child) 3443262182Semaste { 3444262182Semaste ValueObject *valobj = row.valobj.get(); 3445262182Semaste 3446262182Semaste if (valobj == NULL) 3447262182Semaste return false; 3448262182Semaste 3449262182Semaste const char *type_name = options.show_types ? valobj->GetTypeName().GetCString() : NULL; 3450262182Semaste const char *name = valobj->GetName().GetCString(); 3451262182Semaste const char *value = valobj->GetValueAsCString (); 3452262182Semaste const char *summary = valobj->GetSummaryAsCString (); 3453262182Semaste 3454262182Semaste window.MoveCursor (row.x, row.y); 3455262182Semaste 3456262182Semaste row.DrawTree (window); 3457262182Semaste 3458262182Semaste if (highlight) 3459262182Semaste window.AttributeOn(A_REVERSE); 3460262182Semaste 3461262182Semaste if (type_name && type_name[0]) 3462262182Semaste window.Printf ("(%s) ", type_name); 3463262182Semaste 3464262182Semaste if (name && name[0]) 3465262182Semaste window.PutCString(name); 3466262182Semaste 3467262182Semaste attr_t changd_attr = 0; 3468262182Semaste if (valobj->GetValueDidChange()) 3469262182Semaste changd_attr = COLOR_PAIR(5) | A_BOLD; 3470262182Semaste 3471262182Semaste if (value && value[0]) 3472262182Semaste { 3473262182Semaste window.PutCString(" = "); 3474262182Semaste if (changd_attr) 3475262182Semaste window.AttributeOn(changd_attr); 3476262182Semaste window.PutCString (value); 3477262182Semaste if (changd_attr) 3478262182Semaste window.AttributeOff(changd_attr); 3479262182Semaste } 3480262182Semaste 3481262182Semaste if (summary && summary[0]) 3482262182Semaste { 3483262182Semaste window.PutChar(' '); 3484262182Semaste if (changd_attr) 3485262182Semaste window.AttributeOn(changd_attr); 3486262182Semaste window.PutCString(summary); 3487262182Semaste if (changd_attr) 3488262182Semaste window.AttributeOff(changd_attr); 3489262182Semaste } 3490262182Semaste 3491262182Semaste if (highlight) 3492262182Semaste window.AttributeOff (A_REVERSE); 3493262182Semaste 3494262182Semaste return true; 3495262182Semaste } 3496262182Semaste void 3497262182Semaste DisplayRows (Window &window, 3498262182Semaste std::vector<Row> &rows, 3499262182Semaste DisplayOptions &options) 3500262182Semaste { 3501262182Semaste // > 0x25B7 3502262182Semaste // \/ 0x25BD 3503262182Semaste 3504262182Semaste bool window_is_active = window.IsActive(); 3505262182Semaste for (auto &row : rows) 3506262182Semaste { 3507262182Semaste const bool last_child = row.parent && &rows[rows.size()-1] == &row; 3508262182Semaste // Save the row index in each Row structure 3509262182Semaste row.row_idx = m_num_rows; 3510262182Semaste if ((m_num_rows >= m_first_visible_row) && 3511262182Semaste ((m_num_rows - m_first_visible_row) < NumVisibleRows())) 3512262182Semaste { 3513262182Semaste row.x = m_min_x; 3514262182Semaste row.y = m_num_rows - m_first_visible_row + 1; 3515262182Semaste if (DisplayRowObject (window, 3516262182Semaste row, 3517262182Semaste options, 3518262182Semaste window_is_active && m_num_rows == m_selected_row_idx, 3519262182Semaste last_child)) 3520262182Semaste { 3521262182Semaste ++m_num_rows; 3522262182Semaste } 3523262182Semaste else 3524262182Semaste { 3525262182Semaste row.x = 0; 3526262182Semaste row.y = 0; 3527262182Semaste } 3528262182Semaste } 3529262182Semaste else 3530262182Semaste { 3531262182Semaste row.x = 0; 3532262182Semaste row.y = 0; 3533262182Semaste ++m_num_rows; 3534262182Semaste } 3535262182Semaste 3536262182Semaste if (row.expanded && !row.children.empty()) 3537262182Semaste { 3538262182Semaste DisplayRows (window, 3539262182Semaste row.children, 3540262182Semaste options); 3541262182Semaste } 3542262182Semaste } 3543262182Semaste } 3544262182Semaste 3545262182Semaste int 3546262182Semaste CalculateTotalNumberRows (const std::vector<Row> &rows) 3547262182Semaste { 3548262182Semaste int row_count = 0; 3549262182Semaste for (const auto &row : rows) 3550262182Semaste { 3551262182Semaste ++row_count; 3552262182Semaste if (row.expanded) 3553262182Semaste row_count += CalculateTotalNumberRows(row.children); 3554262182Semaste } 3555262182Semaste return row_count; 3556262182Semaste } 3557262182Semaste static Row * 3558262182Semaste GetRowForRowIndexImpl (std::vector<Row> &rows, size_t &row_index) 3559262182Semaste { 3560262182Semaste for (auto &row : rows) 3561262182Semaste { 3562262182Semaste if (row_index == 0) 3563262182Semaste return &row; 3564262182Semaste else 3565262182Semaste { 3566262182Semaste --row_index; 3567262182Semaste if (row.expanded && !row.children.empty()) 3568262182Semaste { 3569262182Semaste Row *result = GetRowForRowIndexImpl (row.children, row_index); 3570262182Semaste if (result) 3571262182Semaste return result; 3572262182Semaste } 3573262182Semaste } 3574262182Semaste } 3575262182Semaste return NULL; 3576262182Semaste } 3577262182Semaste 3578262182Semaste Row * 3579262182Semaste GetRowForRowIndex (size_t row_index) 3580262182Semaste { 3581262182Semaste return GetRowForRowIndexImpl (m_rows, row_index); 3582262182Semaste } 3583262182Semaste 3584262182Semaste int 3585262182Semaste NumVisibleRows () const 3586262182Semaste { 3587262182Semaste return m_max_y - m_min_y; 3588262182Semaste } 3589262182Semaste 3590262182Semaste static DisplayOptions g_options; 3591262182Semaste}; 3592262182Semaste 3593262182Semasteclass FrameVariablesWindowDelegate : public ValueObjectListDelegate 3594262182Semaste{ 3595262182Semastepublic: 3596262182Semaste FrameVariablesWindowDelegate (Debugger &debugger) : 3597262182Semaste ValueObjectListDelegate (), 3598262182Semaste m_debugger (debugger), 3599262182Semaste m_frame_block (NULL) 3600262182Semaste { 3601262182Semaste } 3602262182Semaste 3603262182Semaste virtual 3604262182Semaste ~FrameVariablesWindowDelegate() 3605262182Semaste { 3606262182Semaste } 3607262182Semaste 3608262182Semaste virtual const char * 3609262182Semaste WindowDelegateGetHelpText () 3610262182Semaste { 3611262182Semaste return "Frame variable window keyboard shortcuts:"; 3612262182Semaste } 3613262182Semaste 3614262182Semaste virtual bool 3615262182Semaste WindowDelegateDraw (Window &window, bool force) 3616262182Semaste { 3617262182Semaste ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext()); 3618262182Semaste Process *process = exe_ctx.GetProcessPtr(); 3619262182Semaste Block *frame_block = NULL; 3620262182Semaste StackFrame *frame = NULL; 3621262182Semaste 3622262182Semaste if (process) 3623262182Semaste { 3624262182Semaste StateType state = process->GetState(); 3625262182Semaste if (StateIsStoppedState(state, true)) 3626262182Semaste { 3627262182Semaste frame = exe_ctx.GetFramePtr(); 3628262182Semaste if (frame) 3629262182Semaste frame_block = frame->GetFrameBlock (); 3630262182Semaste } 3631262182Semaste else if (StateIsRunningState(state)) 3632262182Semaste { 3633262182Semaste return true; // Don't do any updating when we are running 3634262182Semaste } 3635262182Semaste } 3636262182Semaste 3637262182Semaste ValueObjectList local_values; 3638262182Semaste if (frame_block) 3639262182Semaste { 3640262182Semaste // Only update the variables if they have changed 3641262182Semaste if (m_frame_block != frame_block) 3642262182Semaste { 3643262182Semaste m_frame_block = frame_block; 3644262182Semaste 3645262182Semaste VariableList *locals = frame->GetVariableList(true); 3646262182Semaste if (locals) 3647262182Semaste { 3648262182Semaste const DynamicValueType use_dynamic = eDynamicDontRunTarget; 3649262182Semaste const size_t num_locals = locals->GetSize(); 3650262182Semaste for (size_t i=0; i<num_locals; ++i) 3651262182Semaste local_values.Append(frame->GetValueObjectForFrameVariable (locals->GetVariableAtIndex(i), use_dynamic)); 3652262182Semaste // Update the values 3653262182Semaste SetValues(local_values); 3654262182Semaste } 3655262182Semaste } 3656262182Semaste } 3657262182Semaste else 3658262182Semaste { 3659262182Semaste m_frame_block = NULL; 3660262182Semaste // Update the values with an empty list if there is no frame 3661262182Semaste SetValues(local_values); 3662262182Semaste } 3663262182Semaste 3664262182Semaste return ValueObjectListDelegate::WindowDelegateDraw (window, force); 3665262182Semaste 3666262182Semaste } 3667262182Semaste 3668262182Semasteprotected: 3669262182Semaste Debugger &m_debugger; 3670262182Semaste Block *m_frame_block; 3671262182Semaste}; 3672262182Semaste 3673262182Semaste 3674262182Semasteclass RegistersWindowDelegate : public ValueObjectListDelegate 3675262182Semaste{ 3676262182Semastepublic: 3677262182Semaste RegistersWindowDelegate (Debugger &debugger) : 3678262182Semaste ValueObjectListDelegate (), 3679262182Semaste m_debugger (debugger) 3680262182Semaste { 3681262182Semaste } 3682262182Semaste 3683262182Semaste virtual 3684262182Semaste ~RegistersWindowDelegate() 3685262182Semaste { 3686262182Semaste } 3687262182Semaste 3688262182Semaste virtual const char * 3689262182Semaste WindowDelegateGetHelpText () 3690262182Semaste { 3691262182Semaste return "Register window keyboard shortcuts:"; 3692262182Semaste } 3693262182Semaste 3694262182Semaste virtual bool 3695262182Semaste WindowDelegateDraw (Window &window, bool force) 3696262182Semaste { 3697262182Semaste ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext()); 3698262182Semaste StackFrame *frame = exe_ctx.GetFramePtr(); 3699262182Semaste 3700262182Semaste ValueObjectList value_list; 3701262182Semaste if (frame) 3702262182Semaste { 3703262182Semaste if (frame->GetStackID() != m_stack_id) 3704262182Semaste { 3705262182Semaste m_stack_id = frame->GetStackID(); 3706262182Semaste RegisterContextSP reg_ctx (frame->GetRegisterContext()); 3707262182Semaste if (reg_ctx) 3708262182Semaste { 3709262182Semaste const uint32_t num_sets = reg_ctx->GetRegisterSetCount(); 3710262182Semaste for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) 3711262182Semaste { 3712262182Semaste value_list.Append(ValueObjectRegisterSet::Create (frame, reg_ctx, set_idx)); 3713262182Semaste } 3714262182Semaste } 3715262182Semaste SetValues(value_list); 3716262182Semaste } 3717262182Semaste } 3718262182Semaste else 3719262182Semaste { 3720262182Semaste Process *process = exe_ctx.GetProcessPtr(); 3721262182Semaste if (process && process->IsAlive()) 3722262182Semaste return true; // Don't do any updating if we are running 3723262182Semaste else 3724262182Semaste { 3725262182Semaste // Update the values with an empty list if there 3726262182Semaste // is no process or the process isn't alive anymore 3727262182Semaste SetValues(value_list); 3728262182Semaste } 3729262182Semaste } 3730262182Semaste return ValueObjectListDelegate::WindowDelegateDraw (window, force); 3731262182Semaste } 3732262182Semaste 3733262182Semasteprotected: 3734262182Semaste Debugger &m_debugger; 3735262182Semaste StackID m_stack_id; 3736262182Semaste}; 3737262182Semaste 3738262182Semastestatic const char * 3739262182SemasteCursesKeyToCString (int ch) 3740262182Semaste{ 3741262182Semaste static char g_desc[32]; 3742262182Semaste if (ch >= KEY_F0 && ch < KEY_F0 + 64) 3743262182Semaste { 3744262182Semaste snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0); 3745262182Semaste return g_desc; 3746262182Semaste } 3747262182Semaste switch (ch) 3748262182Semaste { 3749262182Semaste case KEY_DOWN: return "down"; 3750262182Semaste case KEY_UP: return "up"; 3751262182Semaste case KEY_LEFT: return "left"; 3752262182Semaste case KEY_RIGHT: return "right"; 3753262182Semaste case KEY_HOME: return "home"; 3754262182Semaste case KEY_BACKSPACE: return "backspace"; 3755262182Semaste case KEY_DL: return "delete-line"; 3756262182Semaste case KEY_IL: return "insert-line"; 3757262182Semaste case KEY_DC: return "delete-char"; 3758262182Semaste case KEY_IC: return "insert-char"; 3759262182Semaste case KEY_CLEAR: return "clear"; 3760262182Semaste case KEY_EOS: return "clear-to-eos"; 3761262182Semaste case KEY_EOL: return "clear-to-eol"; 3762262182Semaste case KEY_SF: return "scroll-forward"; 3763262182Semaste case KEY_SR: return "scroll-backward"; 3764262182Semaste case KEY_NPAGE: return "page-down"; 3765262182Semaste case KEY_PPAGE: return "page-up"; 3766262182Semaste case KEY_STAB: return "set-tab"; 3767262182Semaste case KEY_CTAB: return "clear-tab"; 3768262182Semaste case KEY_CATAB: return "clear-all-tabs"; 3769262182Semaste case KEY_ENTER: return "enter"; 3770262182Semaste case KEY_PRINT: return "print"; 3771262182Semaste case KEY_LL: return "lower-left key"; 3772262182Semaste case KEY_A1: return "upper left of keypad"; 3773262182Semaste case KEY_A3: return "upper right of keypad"; 3774262182Semaste case KEY_B2: return "center of keypad"; 3775262182Semaste case KEY_C1: return "lower left of keypad"; 3776262182Semaste case KEY_C3: return "lower right of keypad"; 3777262182Semaste case KEY_BTAB: return "back-tab key"; 3778262182Semaste case KEY_BEG: return "begin key"; 3779262182Semaste case KEY_CANCEL: return "cancel key"; 3780262182Semaste case KEY_CLOSE: return "close key"; 3781262182Semaste case KEY_COMMAND: return "command key"; 3782262182Semaste case KEY_COPY: return "copy key"; 3783262182Semaste case KEY_CREATE: return "create key"; 3784262182Semaste case KEY_END: return "end key"; 3785262182Semaste case KEY_EXIT: return "exit key"; 3786262182Semaste case KEY_FIND: return "find key"; 3787262182Semaste case KEY_HELP: return "help key"; 3788262182Semaste case KEY_MARK: return "mark key"; 3789262182Semaste case KEY_MESSAGE: return "message key"; 3790262182Semaste case KEY_MOVE: return "move key"; 3791262182Semaste case KEY_NEXT: return "next key"; 3792262182Semaste case KEY_OPEN: return "open key"; 3793262182Semaste case KEY_OPTIONS: return "options key"; 3794262182Semaste case KEY_PREVIOUS: return "previous key"; 3795262182Semaste case KEY_REDO: return "redo key"; 3796262182Semaste case KEY_REFERENCE: return "reference key"; 3797262182Semaste case KEY_REFRESH: return "refresh key"; 3798262182Semaste case KEY_REPLACE: return "replace key"; 3799262182Semaste case KEY_RESTART: return "restart key"; 3800262182Semaste case KEY_RESUME: return "resume key"; 3801262182Semaste case KEY_SAVE: return "save key"; 3802262182Semaste case KEY_SBEG: return "shifted begin key"; 3803262182Semaste case KEY_SCANCEL: return "shifted cancel key"; 3804262182Semaste case KEY_SCOMMAND: return "shifted command key"; 3805262182Semaste case KEY_SCOPY: return "shifted copy key"; 3806262182Semaste case KEY_SCREATE: return "shifted create key"; 3807262182Semaste case KEY_SDC: return "shifted delete-character key"; 3808262182Semaste case KEY_SDL: return "shifted delete-line key"; 3809262182Semaste case KEY_SELECT: return "select key"; 3810262182Semaste case KEY_SEND: return "shifted end key"; 3811262182Semaste case KEY_SEOL: return "shifted clear-to-end-of-line key"; 3812262182Semaste case KEY_SEXIT: return "shifted exit key"; 3813262182Semaste case KEY_SFIND: return "shifted find key"; 3814262182Semaste case KEY_SHELP: return "shifted help key"; 3815262182Semaste case KEY_SHOME: return "shifted home key"; 3816262182Semaste case KEY_SIC: return "shifted insert-character key"; 3817262182Semaste case KEY_SLEFT: return "shifted left-arrow key"; 3818262182Semaste case KEY_SMESSAGE: return "shifted message key"; 3819262182Semaste case KEY_SMOVE: return "shifted move key"; 3820262182Semaste case KEY_SNEXT: return "shifted next key"; 3821262182Semaste case KEY_SOPTIONS: return "shifted options key"; 3822262182Semaste case KEY_SPREVIOUS: return "shifted previous key"; 3823262182Semaste case KEY_SPRINT: return "shifted print key"; 3824262182Semaste case KEY_SREDO: return "shifted redo key"; 3825262182Semaste case KEY_SREPLACE: return "shifted replace key"; 3826262182Semaste case KEY_SRIGHT: return "shifted right-arrow key"; 3827262182Semaste case KEY_SRSUME: return "shifted resume key"; 3828262182Semaste case KEY_SSAVE: return "shifted save key"; 3829262182Semaste case KEY_SSUSPEND: return "shifted suspend key"; 3830262182Semaste case KEY_SUNDO: return "shifted undo key"; 3831262182Semaste case KEY_SUSPEND: return "suspend key"; 3832262182Semaste case KEY_UNDO: return "undo key"; 3833262182Semaste case KEY_MOUSE: return "Mouse event has occurred"; 3834262182Semaste case KEY_RESIZE: return "Terminal resize event"; 3835262182Semaste case KEY_EVENT: return "We were interrupted by an event"; 3836262182Semaste case KEY_RETURN: return "return"; 3837262182Semaste case ' ': return "space"; 3838262182Semaste case '\t': return "tab"; 3839262182Semaste case KEY_ESCAPE: return "escape"; 3840262182Semaste default: 3841262182Semaste if (isprint(ch)) 3842262182Semaste snprintf(g_desc, sizeof(g_desc), "%c", ch); 3843262182Semaste else 3844262182Semaste snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch); 3845262182Semaste return g_desc; 3846262182Semaste } 3847262182Semaste return NULL; 3848262182Semaste} 3849262182Semaste 3850262182SemasteHelpDialogDelegate::HelpDialogDelegate (const char *text, KeyHelp *key_help_array) : 3851262182Semaste m_text (), 3852262182Semaste m_first_visible_line (0) 3853262182Semaste{ 3854262182Semaste if (text && text[0]) 3855262182Semaste { 3856262182Semaste m_text.SplitIntoLines(text); 3857262182Semaste m_text.AppendString(""); 3858262182Semaste } 3859262182Semaste if (key_help_array) 3860262182Semaste { 3861262182Semaste for (KeyHelp *key = key_help_array; key->ch; ++key) 3862262182Semaste { 3863262182Semaste StreamString key_description; 3864262182Semaste key_description.Printf("%10s - %s", CursesKeyToCString(key->ch), key->description); 3865262182Semaste m_text.AppendString(std::move(key_description.GetString())); 3866262182Semaste } 3867262182Semaste } 3868262182Semaste} 3869262182Semaste 3870262182SemasteHelpDialogDelegate::~HelpDialogDelegate() 3871262182Semaste{ 3872262182Semaste} 3873262182Semaste 3874262182Semastebool 3875262182SemasteHelpDialogDelegate::WindowDelegateDraw (Window &window, bool force) 3876262182Semaste{ 3877262182Semaste window.Erase(); 3878262182Semaste const int window_height = window.GetHeight(); 3879262182Semaste int x = 2; 3880262182Semaste int y = 1; 3881262182Semaste const int min_y = y; 3882262182Semaste const int max_y = window_height - 1 - y; 3883262182Semaste const int num_visible_lines = max_y - min_y + 1; 3884262182Semaste const size_t num_lines = m_text.GetSize(); 3885262182Semaste const char *bottom_message; 3886262182Semaste if (num_lines <= num_visible_lines) 3887262182Semaste bottom_message = "Press any key to exit"; 3888262182Semaste else 3889262182Semaste bottom_message = "Use arrows to scroll, any other key to exit"; 3890262182Semaste window.DrawTitleBox(window.GetName(), bottom_message); 3891262182Semaste while (y <= max_y) 3892262182Semaste { 3893262182Semaste window.MoveCursor(x, y); 3894262182Semaste window.PutCStringTruncated(m_text.GetStringAtIndex(m_first_visible_line + y - min_y), 1); 3895262182Semaste ++y; 3896262182Semaste } 3897262182Semaste return true; 3898262182Semaste} 3899262182Semaste 3900262182SemasteHandleCharResult 3901262182SemasteHelpDialogDelegate::WindowDelegateHandleChar (Window &window, int key) 3902262182Semaste{ 3903262182Semaste bool done = false; 3904262182Semaste const size_t num_lines = m_text.GetSize(); 3905262182Semaste const size_t num_visible_lines = window.GetHeight() - 2; 3906262182Semaste 3907262182Semaste if (num_lines <= num_visible_lines) 3908262182Semaste { 3909262182Semaste done = true; 3910262182Semaste // If we have all lines visible and don't need scrolling, then any 3911262182Semaste // key press will cause us to exit 3912262182Semaste } 3913262182Semaste else 3914262182Semaste { 3915262182Semaste switch (key) 3916262182Semaste { 3917262182Semaste case KEY_UP: 3918262182Semaste if (m_first_visible_line > 0) 3919262182Semaste --m_first_visible_line; 3920262182Semaste break; 3921262182Semaste 3922262182Semaste case KEY_DOWN: 3923262182Semaste if (m_first_visible_line + num_visible_lines < num_lines) 3924262182Semaste ++m_first_visible_line; 3925262182Semaste break; 3926262182Semaste 3927262182Semaste case KEY_PPAGE: 3928262182Semaste case ',': 3929262182Semaste if (m_first_visible_line > 0) 3930262182Semaste { 3931262182Semaste if (m_first_visible_line >= num_visible_lines) 3932262182Semaste m_first_visible_line -= num_visible_lines; 3933262182Semaste else 3934262182Semaste m_first_visible_line = 0; 3935262182Semaste } 3936262182Semaste break; 3937262182Semaste case KEY_NPAGE: 3938262182Semaste case '.': 3939262182Semaste if (m_first_visible_line + num_visible_lines < num_lines) 3940262182Semaste { 3941262182Semaste m_first_visible_line += num_visible_lines; 3942262182Semaste if (m_first_visible_line > num_lines) 3943262182Semaste m_first_visible_line = num_lines - num_visible_lines; 3944262182Semaste } 3945262182Semaste break; 3946262182Semaste default: 3947262182Semaste done = true; 3948262182Semaste break; 3949262182Semaste } 3950262182Semaste } 3951262182Semaste if (done) 3952262182Semaste window.GetParent()->RemoveSubWindow(&window); 3953262182Semaste return eKeyHandled; 3954262182Semaste} 3955262182Semaste 3956262182Semasteclass ApplicationDelegate : 3957262182Semaste public WindowDelegate, 3958262182Semaste public MenuDelegate 3959262182Semaste{ 3960262182Semastepublic: 3961262182Semaste enum { 3962262182Semaste eMenuID_LLDB = 1, 3963262182Semaste eMenuID_LLDBAbout, 3964262182Semaste eMenuID_LLDBExit, 3965262182Semaste 3966262182Semaste eMenuID_Target, 3967262182Semaste eMenuID_TargetCreate, 3968262182Semaste eMenuID_TargetDelete, 3969262182Semaste 3970262182Semaste eMenuID_Process, 3971262182Semaste eMenuID_ProcessAttach, 3972262182Semaste eMenuID_ProcessDetach, 3973262182Semaste eMenuID_ProcessLaunch, 3974262182Semaste eMenuID_ProcessContinue, 3975262182Semaste eMenuID_ProcessHalt, 3976262182Semaste eMenuID_ProcessKill, 3977262182Semaste 3978262182Semaste eMenuID_Thread, 3979262182Semaste eMenuID_ThreadStepIn, 3980262182Semaste eMenuID_ThreadStepOver, 3981262182Semaste eMenuID_ThreadStepOut, 3982262182Semaste 3983262182Semaste eMenuID_View, 3984262182Semaste eMenuID_ViewBacktrace, 3985262182Semaste eMenuID_ViewRegisters, 3986262182Semaste eMenuID_ViewSource, 3987262182Semaste eMenuID_ViewVariables, 3988262182Semaste 3989262182Semaste eMenuID_Help, 3990262182Semaste eMenuID_HelpGUIHelp 3991262182Semaste }; 3992262182Semaste 3993262182Semaste ApplicationDelegate (Application &app, Debugger &debugger) : 3994262182Semaste WindowDelegate (), 3995262182Semaste MenuDelegate (), 3996262182Semaste m_app (app), 3997262182Semaste m_debugger (debugger) 3998262182Semaste { 3999262182Semaste } 4000262182Semaste 4001262182Semaste virtual 4002262182Semaste ~ApplicationDelegate () 4003262182Semaste { 4004262182Semaste } 4005262182Semaste virtual bool 4006262182Semaste WindowDelegateDraw (Window &window, bool force) 4007262182Semaste { 4008262182Semaste return false; // Drawing not handled, let standard window drawing happen 4009262182Semaste } 4010262182Semaste 4011262182Semaste virtual HandleCharResult 4012262182Semaste WindowDelegateHandleChar (Window &window, int key) 4013262182Semaste { 4014262182Semaste switch (key) 4015262182Semaste { 4016262182Semaste case '\t': 4017262182Semaste window.SelectNextWindowAsActive(); 4018262182Semaste return eKeyHandled; 4019262182Semaste 4020262182Semaste case 'h': 4021262182Semaste window.CreateHelpSubwindow(); 4022262182Semaste return eKeyHandled; 4023262182Semaste 4024262182Semaste case KEY_ESCAPE: 4025262182Semaste return eQuitApplication; 4026262182Semaste 4027262182Semaste default: 4028262182Semaste break; 4029262182Semaste } 4030262182Semaste return eKeyNotHandled; 4031262182Semaste } 4032262182Semaste 4033262182Semaste 4034262182Semaste virtual const char * 4035262182Semaste WindowDelegateGetHelpText () 4036262182Semaste { 4037262182Semaste return "Welcome to the LLDB curses GUI.\n\n" 4038262182Semaste "Press the TAB key to change the selected view.\n" 4039262182Semaste "Each view has its own keyboard shortcuts, press 'h' to open a dialog to display them.\n\n" 4040262182Semaste "Common key bindings for all views:"; 4041262182Semaste } 4042262182Semaste 4043262182Semaste virtual KeyHelp * 4044262182Semaste WindowDelegateGetKeyHelp () 4045262182Semaste { 4046262182Semaste static curses::KeyHelp g_source_view_key_help[] = { 4047262182Semaste { '\t', "Select next view" }, 4048262182Semaste { 'h', "Show help dialog with view specific key bindings" }, 4049262182Semaste { ',', "Page up" }, 4050262182Semaste { '.', "Page down" }, 4051262182Semaste { KEY_UP, "Select previous" }, 4052262182Semaste { KEY_DOWN, "Select next" }, 4053262182Semaste { KEY_LEFT, "Unexpand or select parent" }, 4054262182Semaste { KEY_RIGHT, "Expand" }, 4055262182Semaste { KEY_PPAGE, "Page up" }, 4056262182Semaste { KEY_NPAGE, "Page down" }, 4057262182Semaste { '\0', NULL } 4058262182Semaste }; 4059262182Semaste return g_source_view_key_help; 4060262182Semaste } 4061262182Semaste 4062262182Semaste virtual MenuActionResult 4063262182Semaste MenuDelegateAction (Menu &menu) 4064262182Semaste { 4065262182Semaste switch (menu.GetIdentifier()) 4066262182Semaste { 4067262182Semaste case eMenuID_ThreadStepIn: 4068262182Semaste { 4069262182Semaste ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); 4070262182Semaste if (exe_ctx.HasThreadScope()) 4071262182Semaste { 4072262182Semaste Process *process = exe_ctx.GetProcessPtr(); 4073262182Semaste if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true)) 4074262182Semaste exe_ctx.GetThreadRef().StepIn(true, true); 4075262182Semaste } 4076262182Semaste } 4077262182Semaste return MenuActionResult::Handled; 4078262182Semaste 4079262182Semaste case eMenuID_ThreadStepOut: 4080262182Semaste { 4081262182Semaste ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); 4082262182Semaste if (exe_ctx.HasThreadScope()) 4083262182Semaste { 4084262182Semaste Process *process = exe_ctx.GetProcessPtr(); 4085262182Semaste if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true)) 4086262182Semaste exe_ctx.GetThreadRef().StepOut(); 4087262182Semaste } 4088262182Semaste } 4089262182Semaste return MenuActionResult::Handled; 4090262182Semaste 4091262182Semaste case eMenuID_ThreadStepOver: 4092262182Semaste { 4093262182Semaste ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); 4094262182Semaste if (exe_ctx.HasThreadScope()) 4095262182Semaste { 4096262182Semaste Process *process = exe_ctx.GetProcessPtr(); 4097262182Semaste if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true)) 4098262182Semaste exe_ctx.GetThreadRef().StepOver(true); 4099262182Semaste } 4100262182Semaste } 4101262182Semaste return MenuActionResult::Handled; 4102262182Semaste 4103262182Semaste case eMenuID_ProcessContinue: 4104262182Semaste { 4105262182Semaste ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); 4106262182Semaste if (exe_ctx.HasProcessScope()) 4107262182Semaste { 4108262182Semaste Process *process = exe_ctx.GetProcessPtr(); 4109262182Semaste if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true)) 4110262182Semaste process->Resume(); 4111262182Semaste } 4112262182Semaste } 4113262182Semaste return MenuActionResult::Handled; 4114262182Semaste 4115262182Semaste case eMenuID_ProcessKill: 4116262182Semaste { 4117262182Semaste ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); 4118262182Semaste if (exe_ctx.HasProcessScope()) 4119262182Semaste { 4120262182Semaste Process *process = exe_ctx.GetProcessPtr(); 4121262182Semaste if (process && process->IsAlive()) 4122262182Semaste process->Destroy(); 4123262182Semaste } 4124262182Semaste } 4125262182Semaste return MenuActionResult::Handled; 4126262182Semaste 4127262182Semaste case eMenuID_ProcessHalt: 4128262182Semaste { 4129262182Semaste ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); 4130262182Semaste if (exe_ctx.HasProcessScope()) 4131262182Semaste { 4132262182Semaste Process *process = exe_ctx.GetProcessPtr(); 4133262182Semaste if (process && process->IsAlive()) 4134262182Semaste process->Halt(); 4135262182Semaste } 4136262182Semaste } 4137262182Semaste return MenuActionResult::Handled; 4138262182Semaste 4139262182Semaste case eMenuID_ProcessDetach: 4140262182Semaste { 4141262182Semaste ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); 4142262182Semaste if (exe_ctx.HasProcessScope()) 4143262182Semaste { 4144262182Semaste Process *process = exe_ctx.GetProcessPtr(); 4145262182Semaste if (process && process->IsAlive()) 4146262182Semaste process->Detach(false); 4147262182Semaste } 4148262182Semaste } 4149262182Semaste return MenuActionResult::Handled; 4150262182Semaste 4151262182Semaste case eMenuID_Process: 4152262182Semaste { 4153262182Semaste // Populate the menu with all of the threads if the process is stopped when 4154262182Semaste // the Process menu gets selected and is about to display its submenu. 4155262182Semaste Menus &submenus = menu.GetSubmenus(); 4156262182Semaste ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); 4157262182Semaste Process *process = exe_ctx.GetProcessPtr(); 4158262182Semaste if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true)) 4159262182Semaste { 4160262182Semaste if (submenus.size() == 7) 4161262182Semaste menu.AddSubmenu (MenuSP (new Menu(Menu::Type::Separator))); 4162262182Semaste else if (submenus.size() > 8) 4163262182Semaste submenus.erase (submenus.begin() + 8, submenus.end()); 4164262182Semaste 4165262182Semaste ThreadList &threads = process->GetThreadList(); 4166262182Semaste Mutex::Locker locker (threads.GetMutex()); 4167262182Semaste size_t num_threads = threads.GetSize(); 4168262182Semaste for (size_t i=0; i<num_threads; ++i) 4169262182Semaste { 4170262182Semaste ThreadSP thread_sp = threads.GetThreadAtIndex(i); 4171262182Semaste char menu_char = '\0'; 4172262182Semaste if (i < 9) 4173262182Semaste menu_char = '1' + i; 4174262182Semaste StreamString thread_menu_title; 4175262182Semaste thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID()); 4176262182Semaste const char *thread_name = thread_sp->GetName(); 4177262182Semaste if (thread_name && thread_name[0]) 4178262182Semaste thread_menu_title.Printf (" %s", thread_name); 4179262182Semaste else 4180262182Semaste { 4181262182Semaste const char *queue_name = thread_sp->GetQueueName(); 4182262182Semaste if (queue_name && queue_name[0]) 4183262182Semaste thread_menu_title.Printf (" %s", queue_name); 4184262182Semaste } 4185262182Semaste menu.AddSubmenu (MenuSP (new Menu(thread_menu_title.GetString().c_str(), NULL, menu_char, thread_sp->GetID()))); 4186262182Semaste } 4187262182Semaste } 4188262182Semaste else if (submenus.size() > 7) 4189262182Semaste { 4190262182Semaste // Remove the separator and any other thread submenu items 4191262182Semaste // that were previously added 4192262182Semaste submenus.erase (submenus.begin() + 7, submenus.end()); 4193262182Semaste } 4194262182Semaste // Since we are adding and removing items we need to recalculate the name lengths 4195262182Semaste menu.RecalculateNameLengths(); 4196262182Semaste } 4197262182Semaste return MenuActionResult::Handled; 4198262182Semaste 4199262182Semaste case eMenuID_ViewVariables: 4200262182Semaste { 4201262182Semaste WindowSP main_window_sp = m_app.GetMainWindow(); 4202262182Semaste WindowSP source_window_sp = main_window_sp->FindSubWindow("Source"); 4203262182Semaste WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables"); 4204262182Semaste WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers"); 4205262182Semaste const Rect source_bounds = source_window_sp->GetBounds(); 4206262182Semaste 4207262182Semaste if (variables_window_sp) 4208262182Semaste { 4209262182Semaste const Rect variables_bounds = variables_window_sp->GetBounds(); 4210262182Semaste 4211262182Semaste main_window_sp->RemoveSubWindow(variables_window_sp.get()); 4212262182Semaste 4213262182Semaste if (registers_window_sp) 4214262182Semaste { 4215262182Semaste // We have a registers window, so give all the area back to the registers window 4216262182Semaste Rect registers_bounds = variables_bounds; 4217262182Semaste registers_bounds.size.width = source_bounds.size.width; 4218262182Semaste registers_window_sp->SetBounds(registers_bounds); 4219262182Semaste } 4220262182Semaste else 4221262182Semaste { 4222262182Semaste // We have no registers window showing so give the bottom 4223262182Semaste // area back to the source view 4224262182Semaste source_window_sp->Resize (source_bounds.size.width, 4225262182Semaste source_bounds.size.height + variables_bounds.size.height); 4226262182Semaste } 4227262182Semaste } 4228262182Semaste else 4229262182Semaste { 4230262182Semaste Rect new_variables_rect; 4231262182Semaste if (registers_window_sp) 4232262182Semaste { 4233262182Semaste // We have a registers window so split the area of the registers 4234262182Semaste // window into two columns where the left hand side will be the 4235262182Semaste // variables and the right hand side will be the registers 4236262182Semaste const Rect variables_bounds = registers_window_sp->GetBounds(); 4237262182Semaste Rect new_registers_rect; 4238262182Semaste variables_bounds.VerticalSplitPercentage (0.50, new_variables_rect, new_registers_rect); 4239262182Semaste registers_window_sp->SetBounds (new_registers_rect); 4240262182Semaste } 4241262182Semaste else 4242262182Semaste { 4243262182Semaste // No variables window, grab the bottom part of the source window 4244262182Semaste Rect new_source_rect; 4245262182Semaste source_bounds.HorizontalSplitPercentage (0.70, new_source_rect, new_variables_rect); 4246262182Semaste source_window_sp->SetBounds (new_source_rect); 4247262182Semaste } 4248262182Semaste WindowSP new_window_sp = main_window_sp->CreateSubWindow ("Variables", 4249262182Semaste new_variables_rect, 4250262182Semaste false); 4251262182Semaste new_window_sp->SetDelegate (WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger))); 4252262182Semaste } 4253262182Semaste touchwin(stdscr); 4254262182Semaste } 4255262182Semaste return MenuActionResult::Handled; 4256262182Semaste 4257262182Semaste case eMenuID_ViewRegisters: 4258262182Semaste { 4259262182Semaste WindowSP main_window_sp = m_app.GetMainWindow(); 4260262182Semaste WindowSP source_window_sp = main_window_sp->FindSubWindow("Source"); 4261262182Semaste WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables"); 4262262182Semaste WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers"); 4263262182Semaste const Rect source_bounds = source_window_sp->GetBounds(); 4264262182Semaste 4265262182Semaste if (registers_window_sp) 4266262182Semaste { 4267262182Semaste if (variables_window_sp) 4268262182Semaste { 4269262182Semaste const Rect variables_bounds = variables_window_sp->GetBounds(); 4270262182Semaste 4271262182Semaste // We have a variables window, so give all the area back to the variables window 4272262182Semaste variables_window_sp->Resize (variables_bounds.size.width + registers_window_sp->GetWidth(), 4273262182Semaste variables_bounds.size.height); 4274262182Semaste } 4275262182Semaste else 4276262182Semaste { 4277262182Semaste // We have no variables window showing so give the bottom 4278262182Semaste // area back to the source view 4279262182Semaste source_window_sp->Resize (source_bounds.size.width, 4280262182Semaste source_bounds.size.height + registers_window_sp->GetHeight()); 4281262182Semaste } 4282262182Semaste main_window_sp->RemoveSubWindow(registers_window_sp.get()); 4283262182Semaste } 4284262182Semaste else 4285262182Semaste { 4286262182Semaste Rect new_regs_rect; 4287262182Semaste if (variables_window_sp) 4288262182Semaste { 4289262182Semaste // We have a variables window, split it into two columns 4290262182Semaste // where the left hand side will be the variables and the 4291262182Semaste // right hand side will be the registers 4292262182Semaste const Rect variables_bounds = variables_window_sp->GetBounds(); 4293262182Semaste Rect new_vars_rect; 4294262182Semaste variables_bounds.VerticalSplitPercentage (0.50, new_vars_rect, new_regs_rect); 4295262182Semaste variables_window_sp->SetBounds (new_vars_rect); 4296262182Semaste } 4297262182Semaste else 4298262182Semaste { 4299262182Semaste // No registers window, grab the bottom part of the source window 4300262182Semaste Rect new_source_rect; 4301262182Semaste source_bounds.HorizontalSplitPercentage (0.70, new_source_rect, new_regs_rect); 4302262182Semaste source_window_sp->SetBounds (new_source_rect); 4303262182Semaste } 4304262182Semaste WindowSP new_window_sp = main_window_sp->CreateSubWindow ("Registers", 4305262182Semaste new_regs_rect, 4306262182Semaste false); 4307262182Semaste new_window_sp->SetDelegate (WindowDelegateSP(new RegistersWindowDelegate(m_debugger))); 4308262182Semaste } 4309262182Semaste touchwin(stdscr); 4310262182Semaste } 4311262182Semaste return MenuActionResult::Handled; 4312262182Semaste 4313262182Semaste case eMenuID_HelpGUIHelp: 4314262182Semaste m_app.GetMainWindow ()->CreateHelpSubwindow(); 4315262182Semaste return MenuActionResult::Handled; 4316262182Semaste 4317262182Semaste default: 4318262182Semaste break; 4319262182Semaste } 4320262182Semaste 4321262182Semaste return MenuActionResult::NotHandled; 4322262182Semaste } 4323262182Semasteprotected: 4324262182Semaste Application &m_app; 4325262182Semaste Debugger &m_debugger; 4326262182Semaste}; 4327262182Semaste 4328262182Semaste 4329262182Semasteclass StatusBarWindowDelegate : public WindowDelegate 4330262182Semaste{ 4331262182Semastepublic: 4332262182Semaste StatusBarWindowDelegate (Debugger &debugger) : 4333262182Semaste m_debugger (debugger) 4334262182Semaste { 4335262182Semaste } 4336262182Semaste 4337262182Semaste virtual 4338262182Semaste ~StatusBarWindowDelegate () 4339262182Semaste { 4340262182Semaste } 4341262182Semaste virtual bool 4342262182Semaste WindowDelegateDraw (Window &window, bool force) 4343262182Semaste { 4344262182Semaste ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); 4345262182Semaste Process *process = exe_ctx.GetProcessPtr(); 4346262182Semaste Thread *thread = exe_ctx.GetThreadPtr(); 4347262182Semaste StackFrame *frame = exe_ctx.GetFramePtr(); 4348262182Semaste window.Erase(); 4349262182Semaste window.SetBackground(2); 4350262182Semaste window.MoveCursor (0, 0); 4351262182Semaste if (process) 4352262182Semaste { 4353262182Semaste const StateType state = process->GetState(); 4354262182Semaste window.Printf ("Process: %5" PRIu64 " %10s", process->GetID(), StateAsCString(state)); 4355262182Semaste 4356262182Semaste if (StateIsStoppedState(state, true)) 4357262182Semaste { 4358262182Semaste window.MoveCursor (40, 0); 4359262182Semaste if (thread) 4360262182Semaste window.Printf ("Thread: 0x%4.4" PRIx64, thread->GetID()); 4361262182Semaste 4362262182Semaste window.MoveCursor (60, 0); 4363262182Semaste if (frame) 4364262182Semaste window.Printf ("Frame: %3u PC = 0x%16.16" PRIx64, frame->GetFrameIndex(), frame->GetFrameCodeAddress().GetOpcodeLoadAddress (exe_ctx.GetTargetPtr())); 4365262182Semaste } 4366262182Semaste else if (state == eStateExited) 4367262182Semaste { 4368262182Semaste const char *exit_desc = process->GetExitDescription(); 4369262182Semaste const int exit_status = process->GetExitStatus(); 4370262182Semaste if (exit_desc && exit_desc[0]) 4371262182Semaste window.Printf (" with status = %i (%s)", exit_status, exit_desc); 4372262182Semaste else 4373262182Semaste window.Printf (" with status = %i", exit_status); 4374262182Semaste } 4375262182Semaste } 4376262182Semaste window.DeferredRefresh(); 4377262182Semaste return true; 4378262182Semaste } 4379262182Semaste 4380262182Semasteprotected: 4381262182Semaste Debugger &m_debugger; 4382262182Semaste}; 4383262182Semaste 4384262182Semasteclass SourceFileWindowDelegate : public WindowDelegate 4385262182Semaste{ 4386262182Semastepublic: 4387262182Semaste SourceFileWindowDelegate (Debugger &debugger) : 4388262182Semaste WindowDelegate (), 4389262182Semaste m_debugger (debugger), 4390262182Semaste m_sc (), 4391262182Semaste m_file_sp (), 4392262182Semaste m_disassembly_scope (NULL), 4393262182Semaste m_disassembly_sp (), 4394262182Semaste m_disassembly_range (), 4395262182Semaste m_line_width (4), 4396262182Semaste m_selected_line (0), 4397262182Semaste m_pc_line (0), 4398262182Semaste m_stop_id (0), 4399262182Semaste m_frame_idx (UINT32_MAX), 4400262182Semaste m_first_visible_line (0), 4401262182Semaste m_min_x (0), 4402262182Semaste m_min_y (0), 4403262182Semaste m_max_x (0), 4404262182Semaste m_max_y (0) 4405262182Semaste { 4406262182Semaste } 4407262182Semaste 4408262182Semaste 4409262182Semaste virtual 4410262182Semaste ~SourceFileWindowDelegate() 4411262182Semaste { 4412262182Semaste } 4413262182Semaste 4414262182Semaste void 4415262182Semaste Update (const SymbolContext &sc) 4416262182Semaste { 4417262182Semaste m_sc = sc; 4418262182Semaste } 4419262182Semaste 4420262182Semaste uint32_t 4421262182Semaste NumVisibleLines () const 4422262182Semaste { 4423262182Semaste return m_max_y - m_min_y; 4424262182Semaste } 4425262182Semaste 4426262182Semaste virtual const char * 4427262182Semaste WindowDelegateGetHelpText () 4428262182Semaste { 4429262182Semaste return "Source/Disassembly window keyboard shortcuts:"; 4430262182Semaste } 4431262182Semaste 4432262182Semaste virtual KeyHelp * 4433262182Semaste WindowDelegateGetKeyHelp () 4434262182Semaste { 4435262182Semaste static curses::KeyHelp g_source_view_key_help[] = { 4436262182Semaste { KEY_RETURN, "Run to selected line with one shot breakpoint" }, 4437262182Semaste { KEY_UP, "Select previous source line" }, 4438262182Semaste { KEY_DOWN, "Select next source line" }, 4439262182Semaste { KEY_PPAGE, "Page up" }, 4440262182Semaste { KEY_NPAGE, "Page down" }, 4441262182Semaste { 'b', "Set breakpoint on selected source/disassembly line" }, 4442262182Semaste { 'c', "Continue process" }, 4443262182Semaste { 'd', "Detach and resume process" }, 4444262182Semaste { 'D', "Detach with process suspended" }, 4445262182Semaste { 'h', "Show help dialog" }, 4446262182Semaste { 'k', "Kill process" }, 4447262182Semaste { 'n', "Step over (source line)" }, 4448262182Semaste { 'N', "Step over (single instruction)" }, 4449262182Semaste { 'o', "Step out" }, 4450262182Semaste { 's', "Step in (source line)" }, 4451262182Semaste { 'S', "Step in (single instruction)" }, 4452262182Semaste { ',', "Page up" }, 4453262182Semaste { '.', "Page down" }, 4454262182Semaste { '\0', NULL } 4455262182Semaste }; 4456262182Semaste return g_source_view_key_help; 4457262182Semaste } 4458262182Semaste 4459262182Semaste virtual bool 4460262182Semaste WindowDelegateDraw (Window &window, bool force) 4461262182Semaste { 4462262182Semaste ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); 4463262182Semaste Process *process = exe_ctx.GetProcessPtr(); 4464262182Semaste Thread *thread = NULL; 4465262182Semaste 4466262182Semaste bool update_location = false; 4467262182Semaste if (process) 4468262182Semaste { 4469262182Semaste StateType state = process->GetState(); 4470262182Semaste if (StateIsStoppedState(state, true)) 4471262182Semaste { 4472262182Semaste // We are stopped, so it is ok to 4473262182Semaste update_location = true; 4474262182Semaste } 4475262182Semaste } 4476262182Semaste 4477262182Semaste m_min_x = 1; 4478262182Semaste m_min_y = 1; 4479262182Semaste m_max_x = window.GetMaxX()-1; 4480262182Semaste m_max_y = window.GetMaxY()-1; 4481262182Semaste 4482262182Semaste const uint32_t num_visible_lines = NumVisibleLines(); 4483262182Semaste StackFrameSP frame_sp; 4484262182Semaste bool set_selected_line_to_pc = false; 4485262182Semaste 4486262182Semaste 4487262182Semaste if (update_location) 4488262182Semaste { 4489262182Semaste 4490262182Semaste const bool process_alive = process ? process->IsAlive() : false; 4491262182Semaste bool thread_changed = false; 4492262182Semaste if (process_alive) 4493262182Semaste { 4494262182Semaste thread = exe_ctx.GetThreadPtr(); 4495262182Semaste if (thread) 4496262182Semaste { 4497262182Semaste frame_sp = thread->GetSelectedFrame(); 4498262182Semaste auto tid = thread->GetID(); 4499262182Semaste thread_changed = tid != m_tid; 4500262182Semaste m_tid = tid; 4501262182Semaste } 4502262182Semaste else 4503262182Semaste { 4504262182Semaste if (m_tid != LLDB_INVALID_THREAD_ID) 4505262182Semaste { 4506262182Semaste thread_changed = true; 4507262182Semaste m_tid = LLDB_INVALID_THREAD_ID; 4508262182Semaste } 4509262182Semaste } 4510262182Semaste } 4511262182Semaste const uint32_t stop_id = process ? process->GetStopID() : 0; 4512262182Semaste const bool stop_id_changed = stop_id != m_stop_id; 4513262182Semaste bool frame_changed = false; 4514262182Semaste m_stop_id = stop_id; 4515262182Semaste if (frame_sp) 4516262182Semaste { 4517262182Semaste m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything); 4518262182Semaste const uint32_t frame_idx = frame_sp->GetFrameIndex(); 4519262182Semaste frame_changed = frame_idx != m_frame_idx; 4520262182Semaste m_frame_idx = frame_idx; 4521262182Semaste } 4522262182Semaste else 4523262182Semaste { 4524262182Semaste m_sc.Clear(true); 4525262182Semaste frame_changed = m_frame_idx != UINT32_MAX; 4526262182Semaste m_frame_idx = UINT32_MAX; 4527262182Semaste } 4528262182Semaste 4529262182Semaste const bool context_changed = thread_changed || frame_changed || stop_id_changed; 4530262182Semaste 4531262182Semaste if (process_alive) 4532262182Semaste { 4533262182Semaste if (m_sc.line_entry.IsValid()) 4534262182Semaste { 4535262182Semaste m_pc_line = m_sc.line_entry.line; 4536262182Semaste if (m_pc_line != UINT32_MAX) 4537262182Semaste --m_pc_line; // Convert to zero based line number... 4538262182Semaste // Update the selected line if the stop ID changed... 4539262182Semaste if (context_changed) 4540262182Semaste m_selected_line = m_pc_line; 4541262182Semaste 4542262182Semaste if (m_file_sp && m_file_sp->FileSpecMatches(m_sc.line_entry.file)) 4543262182Semaste { 4544262182Semaste // Same file, nothing to do, we should either have the 4545262182Semaste // lines or not (source file missing) 4546262182Semaste if (m_selected_line >= m_first_visible_line) 4547262182Semaste { 4548262182Semaste if (m_selected_line >= m_first_visible_line + num_visible_lines) 4549262182Semaste m_first_visible_line = m_selected_line - 10; 4550262182Semaste } 4551262182Semaste else 4552262182Semaste { 4553262182Semaste if (m_selected_line > 10) 4554262182Semaste m_first_visible_line = m_selected_line - 10; 4555262182Semaste else 4556262182Semaste m_first_visible_line = 0; 4557262182Semaste } 4558262182Semaste } 4559262182Semaste else 4560262182Semaste { 4561262182Semaste // File changed, set selected line to the line with the PC 4562262182Semaste m_selected_line = m_pc_line; 4563262182Semaste m_file_sp = m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file); 4564262182Semaste if (m_file_sp) 4565262182Semaste { 4566262182Semaste const size_t num_lines = m_file_sp->GetNumLines(); 4567262182Semaste int m_line_width = 1; 4568262182Semaste for (size_t n = num_lines; n >= 10; n = n / 10) 4569262182Semaste ++m_line_width; 4570262182Semaste 4571262182Semaste snprintf (m_line_format, sizeof(m_line_format), " %%%iu ", m_line_width); 4572262182Semaste if (num_lines < num_visible_lines || m_selected_line < num_visible_lines) 4573262182Semaste m_first_visible_line = 0; 4574262182Semaste else 4575262182Semaste m_first_visible_line = m_selected_line - 10; 4576262182Semaste } 4577262182Semaste } 4578262182Semaste } 4579262182Semaste else 4580262182Semaste { 4581262182Semaste m_file_sp.reset(); 4582262182Semaste } 4583262182Semaste 4584262182Semaste if (!m_file_sp || m_file_sp->GetNumLines() == 0) 4585262182Semaste { 4586262182Semaste // Show disassembly 4587262182Semaste bool prefer_file_cache = false; 4588262182Semaste if (m_sc.function) 4589262182Semaste { 4590262182Semaste if (m_disassembly_scope != m_sc.function) 4591262182Semaste { 4592262182Semaste m_disassembly_scope = m_sc.function; 4593262182Semaste m_disassembly_sp = m_sc.function->GetInstructions (exe_ctx, NULL, prefer_file_cache); 4594262182Semaste if (m_disassembly_sp) 4595262182Semaste { 4596262182Semaste set_selected_line_to_pc = true; 4597262182Semaste m_disassembly_range = m_sc.function->GetAddressRange(); 4598262182Semaste } 4599262182Semaste else 4600262182Semaste { 4601262182Semaste m_disassembly_range.Clear(); 4602262182Semaste } 4603262182Semaste } 4604262182Semaste else 4605262182Semaste { 4606262182Semaste set_selected_line_to_pc = context_changed; 4607262182Semaste } 4608262182Semaste } 4609262182Semaste else if (m_sc.symbol) 4610262182Semaste { 4611262182Semaste if (m_disassembly_scope != m_sc.symbol) 4612262182Semaste { 4613262182Semaste m_disassembly_scope = m_sc.symbol; 4614262182Semaste m_disassembly_sp = m_sc.symbol->GetInstructions (exe_ctx, NULL, prefer_file_cache); 4615262182Semaste if (m_disassembly_sp) 4616262182Semaste { 4617262182Semaste set_selected_line_to_pc = true; 4618262182Semaste m_disassembly_range.GetBaseAddress() = m_sc.symbol->GetAddress(); 4619262182Semaste m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize()); 4620262182Semaste } 4621262182Semaste else 4622262182Semaste { 4623262182Semaste m_disassembly_range.Clear(); 4624262182Semaste } 4625262182Semaste } 4626262182Semaste else 4627262182Semaste { 4628262182Semaste set_selected_line_to_pc = context_changed; 4629262182Semaste } 4630262182Semaste } 4631262182Semaste } 4632262182Semaste } 4633262182Semaste else 4634262182Semaste { 4635262182Semaste m_pc_line = UINT32_MAX; 4636262182Semaste } 4637262182Semaste } 4638262182Semaste 4639262182Semaste 4640262182Semaste window.Erase(); 4641262182Semaste window.DrawTitleBox ("Sources"); 4642262182Semaste 4643262182Semaste 4644262182Semaste Target *target = exe_ctx.GetTargetPtr(); 4645262182Semaste const size_t num_source_lines = GetNumSourceLines(); 4646262182Semaste if (num_source_lines > 0) 4647262182Semaste { 4648262182Semaste // Display source 4649262182Semaste BreakpointLines bp_lines; 4650262182Semaste if (target) 4651262182Semaste { 4652262182Semaste BreakpointList &bp_list = target->GetBreakpointList(); 4653262182Semaste const size_t num_bps = bp_list.GetSize(); 4654262182Semaste for (size_t bp_idx=0; bp_idx<num_bps; ++bp_idx) 4655262182Semaste { 4656262182Semaste BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx); 4657262182Semaste const size_t num_bps_locs = bp_sp->GetNumLocations(); 4658262182Semaste for (size_t bp_loc_idx=0; bp_loc_idx<num_bps_locs; ++bp_loc_idx) 4659262182Semaste { 4660262182Semaste BreakpointLocationSP bp_loc_sp = bp_sp->GetLocationAtIndex(bp_loc_idx); 4661262182Semaste LineEntry bp_loc_line_entry; 4662262182Semaste if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry (bp_loc_line_entry)) 4663262182Semaste { 4664262182Semaste if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file) 4665262182Semaste { 4666262182Semaste bp_lines.insert(bp_loc_line_entry.line); 4667262182Semaste } 4668262182Semaste } 4669262182Semaste } 4670262182Semaste } 4671262182Semaste } 4672262182Semaste 4673262182Semaste 4674262182Semaste const attr_t selected_highlight_attr = A_REVERSE; 4675262182Semaste const attr_t pc_highlight_attr = COLOR_PAIR(1); 4676262182Semaste 4677262182Semaste for (int i=0; i<num_visible_lines; ++i) 4678262182Semaste { 4679262182Semaste const uint32_t curr_line = m_first_visible_line + i; 4680262182Semaste if (curr_line < num_source_lines) 4681262182Semaste { 4682262182Semaste const int line_y = 1+i; 4683262182Semaste window.MoveCursor(1, line_y); 4684262182Semaste const bool is_pc_line = curr_line == m_pc_line; 4685262182Semaste const bool line_is_selected = m_selected_line == curr_line; 4686262182Semaste // Highlight the line as the PC line first, then if the selected line 4687262182Semaste // isn't the same as the PC line, highlight it differently 4688262182Semaste attr_t highlight_attr = 0; 4689262182Semaste attr_t bp_attr = 0; 4690262182Semaste if (is_pc_line) 4691262182Semaste highlight_attr = pc_highlight_attr; 4692262182Semaste else if (line_is_selected) 4693262182Semaste highlight_attr = selected_highlight_attr; 4694262182Semaste 4695262182Semaste if (bp_lines.find(curr_line+1) != bp_lines.end()) 4696262182Semaste bp_attr = COLOR_PAIR(2); 4697262182Semaste 4698262182Semaste if (bp_attr) 4699262182Semaste window.AttributeOn(bp_attr); 4700262182Semaste 4701262182Semaste window.Printf (m_line_format, curr_line + 1); 4702262182Semaste 4703262182Semaste if (bp_attr) 4704262182Semaste window.AttributeOff(bp_attr); 4705262182Semaste 4706262182Semaste window.PutChar(ACS_VLINE); 4707262182Semaste // Mark the line with the PC with a diamond 4708262182Semaste if (is_pc_line) 4709262182Semaste window.PutChar(ACS_DIAMOND); 4710262182Semaste else 4711262182Semaste window.PutChar(' '); 4712262182Semaste 4713262182Semaste if (highlight_attr) 4714262182Semaste window.AttributeOn(highlight_attr); 4715262182Semaste const uint32_t line_len = m_file_sp->GetLineLength(curr_line + 1, false); 4716262182Semaste if (line_len > 0) 4717262182Semaste window.PutCString(m_file_sp->PeekLineData(curr_line + 1), line_len); 4718262182Semaste 4719262182Semaste if (is_pc_line && frame_sp && frame_sp->GetConcreteFrameIndex() == 0) 4720262182Semaste { 4721262182Semaste StopInfoSP stop_info_sp; 4722262182Semaste if (thread) 4723262182Semaste stop_info_sp = thread->GetStopInfo(); 4724262182Semaste if (stop_info_sp) 4725262182Semaste { 4726262182Semaste const char *stop_description = stop_info_sp->GetDescription(); 4727262182Semaste if (stop_description && stop_description[0]) 4728262182Semaste { 4729262182Semaste size_t stop_description_len = strlen(stop_description); 4730262182Semaste int desc_x = window.GetWidth() - stop_description_len - 16; 4731262182Semaste window.Printf ("%*s", desc_x - window.GetCursorX(), ""); 4732262182Semaste //window.MoveCursor(window.GetWidth() - stop_description_len - 15, line_y); 4733262182Semaste window.Printf ("<<< Thread %u: %s ", thread->GetIndexID(), stop_description); 4734262182Semaste } 4735262182Semaste } 4736262182Semaste else 4737262182Semaste { 4738262182Semaste window.Printf ("%*s", window.GetWidth() - window.GetCursorX() - 1, ""); 4739262182Semaste } 4740262182Semaste } 4741262182Semaste if (highlight_attr) 4742262182Semaste window.AttributeOff(highlight_attr); 4743262182Semaste 4744262182Semaste } 4745262182Semaste else 4746262182Semaste { 4747262182Semaste break; 4748262182Semaste } 4749262182Semaste } 4750262182Semaste } 4751262182Semaste else 4752262182Semaste { 4753262182Semaste size_t num_disassembly_lines = GetNumDisassemblyLines(); 4754262182Semaste if (num_disassembly_lines > 0) 4755262182Semaste { 4756262182Semaste // Display disassembly 4757262182Semaste BreakpointAddrs bp_file_addrs; 4758262182Semaste Target *target = exe_ctx.GetTargetPtr(); 4759262182Semaste if (target) 4760262182Semaste { 4761262182Semaste BreakpointList &bp_list = target->GetBreakpointList(); 4762262182Semaste const size_t num_bps = bp_list.GetSize(); 4763262182Semaste for (size_t bp_idx=0; bp_idx<num_bps; ++bp_idx) 4764262182Semaste { 4765262182Semaste BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx); 4766262182Semaste const size_t num_bps_locs = bp_sp->GetNumLocations(); 4767262182Semaste for (size_t bp_loc_idx=0; bp_loc_idx<num_bps_locs; ++bp_loc_idx) 4768262182Semaste { 4769262182Semaste BreakpointLocationSP bp_loc_sp = bp_sp->GetLocationAtIndex(bp_loc_idx); 4770262182Semaste LineEntry bp_loc_line_entry; 4771262182Semaste const lldb::addr_t file_addr = bp_loc_sp->GetAddress().GetFileAddress(); 4772262182Semaste if (file_addr != LLDB_INVALID_ADDRESS) 4773262182Semaste { 4774262182Semaste if (m_disassembly_range.ContainsFileAddress(file_addr)) 4775262182Semaste bp_file_addrs.insert(file_addr); 4776262182Semaste } 4777262182Semaste } 4778262182Semaste } 4779262182Semaste } 4780262182Semaste 4781262182Semaste 4782262182Semaste const attr_t selected_highlight_attr = A_REVERSE; 4783262182Semaste const attr_t pc_highlight_attr = COLOR_PAIR(1); 4784262182Semaste 4785262182Semaste StreamString strm; 4786262182Semaste 4787262182Semaste InstructionList &insts = m_disassembly_sp->GetInstructionList(); 4788262182Semaste Address pc_address; 4789262182Semaste 4790262182Semaste if (frame_sp) 4791262182Semaste pc_address = frame_sp->GetFrameCodeAddress(); 4792262182Semaste const uint32_t pc_idx = pc_address.IsValid() ? insts.GetIndexOfInstructionAtAddress (pc_address) : UINT32_MAX; 4793262182Semaste if (set_selected_line_to_pc) 4794262182Semaste { 4795262182Semaste m_selected_line = pc_idx; 4796262182Semaste } 4797262182Semaste 4798262182Semaste const uint32_t non_visible_pc_offset = (num_visible_lines / 5); 4799262182Semaste if (m_first_visible_line >= num_disassembly_lines) 4800262182Semaste m_first_visible_line = 0; 4801262182Semaste 4802262182Semaste if (pc_idx < num_disassembly_lines) 4803262182Semaste { 4804262182Semaste if (pc_idx < m_first_visible_line || 4805262182Semaste pc_idx >= m_first_visible_line + num_visible_lines) 4806262182Semaste m_first_visible_line = pc_idx - non_visible_pc_offset; 4807262182Semaste } 4808262182Semaste 4809262182Semaste for (size_t i=0; i<num_visible_lines; ++i) 4810262182Semaste { 4811262182Semaste const uint32_t inst_idx = m_first_visible_line + i; 4812262182Semaste Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get(); 4813262182Semaste if (!inst) 4814262182Semaste break; 4815262182Semaste 4816262182Semaste window.MoveCursor(1, i+1); 4817262182Semaste const bool is_pc_line = frame_sp && inst_idx == pc_idx; 4818262182Semaste const bool line_is_selected = m_selected_line == inst_idx; 4819262182Semaste // Highlight the line as the PC line first, then if the selected line 4820262182Semaste // isn't the same as the PC line, highlight it differently 4821262182Semaste attr_t highlight_attr = 0; 4822262182Semaste attr_t bp_attr = 0; 4823262182Semaste if (is_pc_line) 4824262182Semaste highlight_attr = pc_highlight_attr; 4825262182Semaste else if (line_is_selected) 4826262182Semaste highlight_attr = selected_highlight_attr; 4827262182Semaste 4828262182Semaste if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) != bp_file_addrs.end()) 4829262182Semaste bp_attr = COLOR_PAIR(2); 4830262182Semaste 4831262182Semaste if (bp_attr) 4832262182Semaste window.AttributeOn(bp_attr); 4833262182Semaste 4834262182Semaste window.Printf (" 0x%16.16llx ", inst->GetAddress().GetLoadAddress(target)); 4835262182Semaste 4836262182Semaste if (bp_attr) 4837262182Semaste window.AttributeOff(bp_attr); 4838262182Semaste 4839262182Semaste window.PutChar(ACS_VLINE); 4840262182Semaste // Mark the line with the PC with a diamond 4841262182Semaste if (is_pc_line) 4842262182Semaste window.PutChar(ACS_DIAMOND); 4843262182Semaste else 4844262182Semaste window.PutChar(' '); 4845262182Semaste 4846262182Semaste if (highlight_attr) 4847262182Semaste window.AttributeOn(highlight_attr); 4848262182Semaste 4849262182Semaste const char *mnemonic = inst->GetMnemonic(&exe_ctx); 4850262182Semaste const char *operands = inst->GetOperands(&exe_ctx); 4851262182Semaste const char *comment = inst->GetComment(&exe_ctx); 4852262182Semaste 4853262182Semaste if (mnemonic && mnemonic[0] == '\0') 4854262182Semaste mnemonic = NULL; 4855262182Semaste if (operands && operands[0] == '\0') 4856262182Semaste operands = NULL; 4857262182Semaste if (comment && comment[0] == '\0') 4858262182Semaste comment = NULL; 4859262182Semaste 4860262182Semaste strm.Clear(); 4861262182Semaste 4862262182Semaste if (mnemonic && operands && comment) 4863262182Semaste strm.Printf ("%-8s %-25s ; %s", mnemonic, operands, comment); 4864262182Semaste else if (mnemonic && operands) 4865262182Semaste strm.Printf ("%-8s %s", mnemonic, operands); 4866262182Semaste else if (mnemonic) 4867262182Semaste strm.Printf ("%s", mnemonic); 4868262182Semaste 4869262182Semaste int right_pad = 1; 4870262182Semaste window.PutCStringTruncated(strm.GetString().c_str(), right_pad); 4871262182Semaste 4872262182Semaste if (is_pc_line && frame_sp && frame_sp->GetConcreteFrameIndex() == 0) 4873262182Semaste { 4874262182Semaste StopInfoSP stop_info_sp; 4875262182Semaste if (thread) 4876262182Semaste stop_info_sp = thread->GetStopInfo(); 4877262182Semaste if (stop_info_sp) 4878262182Semaste { 4879262182Semaste const char *stop_description = stop_info_sp->GetDescription(); 4880262182Semaste if (stop_description && stop_description[0]) 4881262182Semaste { 4882262182Semaste size_t stop_description_len = strlen(stop_description); 4883262182Semaste int desc_x = window.GetWidth() - stop_description_len - 16; 4884262182Semaste window.Printf ("%*s", desc_x - window.GetCursorX(), ""); 4885262182Semaste //window.MoveCursor(window.GetWidth() - stop_description_len - 15, line_y); 4886262182Semaste window.Printf ("<<< Thread %u: %s ", thread->GetIndexID(), stop_description); 4887262182Semaste } 4888262182Semaste } 4889262182Semaste else 4890262182Semaste { 4891262182Semaste window.Printf ("%*s", window.GetWidth() - window.GetCursorX() - 1, ""); 4892262182Semaste } 4893262182Semaste } 4894262182Semaste if (highlight_attr) 4895262182Semaste window.AttributeOff(highlight_attr); 4896262182Semaste } 4897262182Semaste } 4898262182Semaste } 4899262182Semaste window.DeferredRefresh(); 4900262182Semaste return true; // Drawing handled 4901262182Semaste } 4902262182Semaste 4903262182Semaste size_t 4904262182Semaste GetNumLines () 4905262182Semaste { 4906262182Semaste size_t num_lines = GetNumSourceLines(); 4907262182Semaste if (num_lines == 0) 4908262182Semaste num_lines = GetNumDisassemblyLines(); 4909262182Semaste return num_lines; 4910262182Semaste } 4911262182Semaste 4912262182Semaste size_t 4913262182Semaste GetNumSourceLines () const 4914262182Semaste { 4915262182Semaste if (m_file_sp) 4916262182Semaste return m_file_sp->GetNumLines(); 4917262182Semaste return 0; 4918262182Semaste } 4919262182Semaste size_t 4920262182Semaste GetNumDisassemblyLines () const 4921262182Semaste { 4922262182Semaste if (m_disassembly_sp) 4923262182Semaste return m_disassembly_sp->GetInstructionList().GetSize(); 4924262182Semaste return 0; 4925262182Semaste } 4926262182Semaste 4927262182Semaste virtual HandleCharResult 4928262182Semaste WindowDelegateHandleChar (Window &window, int c) 4929262182Semaste { 4930262182Semaste const uint32_t num_visible_lines = NumVisibleLines(); 4931262182Semaste const size_t num_lines = GetNumLines (); 4932262182Semaste 4933262182Semaste switch (c) 4934262182Semaste { 4935262182Semaste case ',': 4936262182Semaste case KEY_PPAGE: 4937262182Semaste // Page up key 4938262182Semaste if (m_first_visible_line > num_visible_lines) 4939262182Semaste m_first_visible_line -= num_visible_lines; 4940262182Semaste else 4941262182Semaste m_first_visible_line = 0; 4942262182Semaste m_selected_line = m_first_visible_line; 4943262182Semaste return eKeyHandled; 4944262182Semaste 4945262182Semaste case '.': 4946262182Semaste case KEY_NPAGE: 4947262182Semaste // Page down key 4948262182Semaste { 4949262182Semaste if (m_first_visible_line + num_visible_lines < num_lines) 4950262182Semaste m_first_visible_line += num_visible_lines; 4951262182Semaste else if (num_lines < num_visible_lines) 4952262182Semaste m_first_visible_line = 0; 4953262182Semaste else 4954262182Semaste m_first_visible_line = num_lines - num_visible_lines; 4955262182Semaste m_selected_line = m_first_visible_line; 4956262182Semaste } 4957262182Semaste return eKeyHandled; 4958262182Semaste 4959262182Semaste case KEY_UP: 4960262182Semaste if (m_selected_line > 0) 4961262182Semaste { 4962262182Semaste m_selected_line--; 4963262182Semaste if (m_first_visible_line > m_selected_line) 4964262182Semaste m_first_visible_line = m_selected_line; 4965262182Semaste } 4966262182Semaste return eKeyHandled; 4967262182Semaste 4968262182Semaste case KEY_DOWN: 4969262182Semaste if (m_selected_line + 1 < num_lines) 4970262182Semaste { 4971262182Semaste m_selected_line++; 4972262182Semaste if (m_first_visible_line + num_visible_lines < m_selected_line) 4973262182Semaste m_first_visible_line++; 4974262182Semaste } 4975262182Semaste return eKeyHandled; 4976262182Semaste 4977262182Semaste case '\r': 4978262182Semaste case '\n': 4979262182Semaste case KEY_ENTER: 4980262182Semaste // Set a breakpoint and run to the line using a one shot breakpoint 4981262182Semaste if (GetNumSourceLines() > 0) 4982262182Semaste { 4983262182Semaste ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); 4984262182Semaste if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive()) 4985262182Semaste { 4986262182Semaste BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (NULL, // Don't limit the breakpoint to certain modules 4987262182Semaste m_file_sp->GetFileSpec(), // Source file 4988262182Semaste m_selected_line + 1, // Source line number (m_selected_line is zero based) 4989262182Semaste eLazyBoolCalculate, // Check inlines using global setting 4990262182Semaste eLazyBoolCalculate, // Skip prologue using global setting, 4991262182Semaste false, // internal 4992262182Semaste false); // request_hardware 4993262182Semaste // Make breakpoint one shot 4994262182Semaste bp_sp->GetOptions()->SetOneShot(true); 4995262182Semaste exe_ctx.GetProcessRef().Resume(); 4996262182Semaste } 4997262182Semaste } 4998262182Semaste else if (m_selected_line < GetNumDisassemblyLines()) 4999262182Semaste { 5000262182Semaste const Instruction *inst = m_disassembly_sp->GetInstructionList().GetInstructionAtIndex(m_selected_line).get(); 5001262182Semaste ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); 5002262182Semaste if (exe_ctx.HasTargetScope()) 5003262182Semaste { 5004262182Semaste Address addr = inst->GetAddress(); 5005262182Semaste BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (addr, // lldb_private::Address 5006262182Semaste false, // internal 5007262182Semaste false); // request_hardware 5008262182Semaste // Make breakpoint one shot 5009262182Semaste bp_sp->GetOptions()->SetOneShot(true); 5010262182Semaste exe_ctx.GetProcessRef().Resume(); 5011262182Semaste } 5012262182Semaste } 5013262182Semaste return eKeyHandled; 5014262182Semaste 5015262182Semaste case 'b': // 'b' == toggle breakpoint on currently selected line 5016262182Semaste if (m_selected_line < GetNumSourceLines()) 5017262182Semaste { 5018262182Semaste ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); 5019262182Semaste if (exe_ctx.HasTargetScope()) 5020262182Semaste { 5021262182Semaste BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (NULL, // Don't limit the breakpoint to certain modules 5022262182Semaste m_file_sp->GetFileSpec(), // Source file 5023262182Semaste m_selected_line + 1, // Source line number (m_selected_line is zero based) 5024262182Semaste eLazyBoolCalculate, // Check inlines using global setting 5025262182Semaste eLazyBoolCalculate, // Skip prologue using global setting, 5026262182Semaste false, // internal 5027262182Semaste false); // request_hardware 5028262182Semaste } 5029262182Semaste } 5030262182Semaste else if (m_selected_line < GetNumDisassemblyLines()) 5031262182Semaste { 5032262182Semaste const Instruction *inst = m_disassembly_sp->GetInstructionList().GetInstructionAtIndex(m_selected_line).get(); 5033262182Semaste ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); 5034262182Semaste if (exe_ctx.HasTargetScope()) 5035262182Semaste { 5036262182Semaste Address addr = inst->GetAddress(); 5037262182Semaste BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (addr, // lldb_private::Address 5038262182Semaste false, // internal 5039262182Semaste false); // request_hardware 5040262182Semaste } 5041262182Semaste } 5042262182Semaste return eKeyHandled; 5043262182Semaste 5044262182Semaste case 'd': // 'd' == detach and let run 5045262182Semaste case 'D': // 'D' == detach and keep stopped 5046262182Semaste { 5047262182Semaste ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); 5048262182Semaste if (exe_ctx.HasProcessScope()) 5049262182Semaste exe_ctx.GetProcessRef().Detach(c == 'D'); 5050262182Semaste } 5051262182Semaste return eKeyHandled; 5052262182Semaste 5053262182Semaste case 'k': 5054262182Semaste // 'k' == kill 5055262182Semaste { 5056262182Semaste ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); 5057262182Semaste if (exe_ctx.HasProcessScope()) 5058262182Semaste exe_ctx.GetProcessRef().Destroy(); 5059262182Semaste } 5060262182Semaste return eKeyHandled; 5061262182Semaste 5062262182Semaste case 'c': 5063262182Semaste // 'c' == continue 5064262182Semaste { 5065262182Semaste ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); 5066262182Semaste if (exe_ctx.HasProcessScope()) 5067262182Semaste exe_ctx.GetProcessRef().Resume(); 5068262182Semaste } 5069262182Semaste return eKeyHandled; 5070262182Semaste 5071262182Semaste case 'o': 5072262182Semaste // 'o' == step out 5073262182Semaste { 5074262182Semaste ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); 5075262182Semaste if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true)) 5076262182Semaste { 5077262182Semaste exe_ctx.GetThreadRef().StepOut(); 5078262182Semaste } 5079262182Semaste } 5080262182Semaste return eKeyHandled; 5081262182Semaste case 'n': // 'n' == step over 5082262182Semaste case 'N': // 'N' == step over instruction 5083262182Semaste { 5084262182Semaste ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); 5085262182Semaste if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true)) 5086262182Semaste { 5087262182Semaste bool source_step = (c == 'n'); 5088262182Semaste exe_ctx.GetThreadRef().StepOver(source_step); 5089262182Semaste } 5090262182Semaste } 5091262182Semaste return eKeyHandled; 5092262182Semaste case 's': // 's' == step into 5093262182Semaste case 'S': // 'S' == step into instruction 5094262182Semaste { 5095262182Semaste ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); 5096262182Semaste if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true)) 5097262182Semaste { 5098262182Semaste bool source_step = (c == 's'); 5099262182Semaste bool avoid_code_without_debug_info = true; 5100262182Semaste exe_ctx.GetThreadRef().StepIn(source_step, avoid_code_without_debug_info); 5101262182Semaste } 5102262182Semaste } 5103262182Semaste return eKeyHandled; 5104262182Semaste 5105262182Semaste case 'h': 5106262182Semaste window.CreateHelpSubwindow (); 5107262182Semaste return eKeyHandled; 5108262182Semaste 5109262182Semaste default: 5110262182Semaste break; 5111262182Semaste } 5112262182Semaste return eKeyNotHandled; 5113262182Semaste } 5114262182Semaste 5115262182Semasteprotected: 5116262182Semaste typedef std::set<uint32_t> BreakpointLines; 5117262182Semaste typedef std::set<lldb::addr_t> BreakpointAddrs; 5118262182Semaste 5119262182Semaste Debugger &m_debugger; 5120262182Semaste SymbolContext m_sc; 5121262182Semaste SourceManager::FileSP m_file_sp; 5122262182Semaste SymbolContextScope *m_disassembly_scope; 5123262182Semaste lldb::DisassemblerSP m_disassembly_sp; 5124262182Semaste AddressRange m_disassembly_range; 5125262182Semaste lldb::user_id_t m_tid; 5126262182Semaste char m_line_format[8]; 5127262182Semaste int m_line_width; 5128262182Semaste uint32_t m_selected_line; // The selected line 5129262182Semaste uint32_t m_pc_line; // The line with the PC 5130262182Semaste uint32_t m_stop_id; 5131262182Semaste uint32_t m_frame_idx; 5132262182Semaste int m_first_visible_line; 5133262182Semaste int m_min_x; 5134262182Semaste int m_min_y; 5135262182Semaste int m_max_x; 5136262182Semaste int m_max_y; 5137262182Semaste 5138262182Semaste}; 5139262182Semaste 5140262182SemasteDisplayOptions ValueObjectListDelegate::g_options = { true }; 5141262182Semaste 5142262182SemasteIOHandlerCursesGUI::IOHandlerCursesGUI (Debugger &debugger) : 5143262182Semaste IOHandler (debugger) 5144262182Semaste{ 5145262182Semaste} 5146262182Semaste 5147262182Semastevoid 5148262182SemasteIOHandlerCursesGUI::Activate () 5149262182Semaste{ 5150262182Semaste IOHandler::Activate(); 5151262182Semaste if (!m_app_ap) 5152262182Semaste { 5153262182Semaste m_app_ap.reset (new Application (GetInputFILE(), GetOutputFILE())); 5154262182Semaste 5155262182Semaste 5156262182Semaste // This is both a window and a menu delegate 5157262182Semaste std::shared_ptr<ApplicationDelegate> app_delegate_sp(new ApplicationDelegate(*m_app_ap, m_debugger)); 5158262182Semaste 5159262182Semaste MenuDelegateSP app_menu_delegate_sp = std::static_pointer_cast<MenuDelegate>(app_delegate_sp); 5160262182Semaste MenuSP lldb_menu_sp(new Menu("LLDB" , "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB)); 5161262182Semaste MenuSP exit_menuitem_sp(new Menu("Exit", NULL, 'x', ApplicationDelegate::eMenuID_LLDBExit)); 5162262182Semaste exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit); 5163262182Semaste lldb_menu_sp->AddSubmenu (MenuSP (new Menu("About LLDB", NULL, 'a', ApplicationDelegate::eMenuID_LLDBAbout))); 5164262182Semaste lldb_menu_sp->AddSubmenu (MenuSP (new Menu(Menu::Type::Separator))); 5165262182Semaste lldb_menu_sp->AddSubmenu (exit_menuitem_sp); 5166262182Semaste 5167262182Semaste MenuSP target_menu_sp(new Menu("Target" ,"F2", KEY_F(2), ApplicationDelegate::eMenuID_Target)); 5168262182Semaste target_menu_sp->AddSubmenu (MenuSP (new Menu("Create", NULL, 'c', ApplicationDelegate::eMenuID_TargetCreate))); 5169262182Semaste target_menu_sp->AddSubmenu (MenuSP (new Menu("Delete", NULL, 'd', ApplicationDelegate::eMenuID_TargetDelete))); 5170262182Semaste 5171262182Semaste MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3), ApplicationDelegate::eMenuID_Process)); 5172262182Semaste process_menu_sp->AddSubmenu (MenuSP (new Menu("Attach" , NULL, 'a', ApplicationDelegate::eMenuID_ProcessAttach))); 5173262182Semaste process_menu_sp->AddSubmenu (MenuSP (new Menu("Detach" , NULL, 'd', ApplicationDelegate::eMenuID_ProcessDetach))); 5174262182Semaste process_menu_sp->AddSubmenu (MenuSP (new Menu("Launch" , NULL, 'l', ApplicationDelegate::eMenuID_ProcessLaunch))); 5175262182Semaste process_menu_sp->AddSubmenu (MenuSP (new Menu(Menu::Type::Separator))); 5176262182Semaste process_menu_sp->AddSubmenu (MenuSP (new Menu("Continue", NULL, 'c', ApplicationDelegate::eMenuID_ProcessContinue))); 5177262182Semaste process_menu_sp->AddSubmenu (MenuSP (new Menu("Halt" , NULL, 'h', ApplicationDelegate::eMenuID_ProcessHalt))); 5178262182Semaste process_menu_sp->AddSubmenu (MenuSP (new Menu("Kill" , NULL, 'k', ApplicationDelegate::eMenuID_ProcessKill))); 5179262182Semaste 5180262182Semaste MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4), ApplicationDelegate::eMenuID_Thread)); 5181262182Semaste thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step In" , NULL, 'i', ApplicationDelegate::eMenuID_ThreadStepIn))); 5182262182Semaste thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step Over", NULL, 'v', ApplicationDelegate::eMenuID_ThreadStepOver))); 5183262182Semaste thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step Out" , NULL, 'o', ApplicationDelegate::eMenuID_ThreadStepOut))); 5184262182Semaste 5185262182Semaste MenuSP view_menu_sp(new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View)); 5186262182Semaste view_menu_sp->AddSubmenu (MenuSP (new Menu("Backtrace", NULL, 'b', ApplicationDelegate::eMenuID_ViewBacktrace))); 5187262182Semaste view_menu_sp->AddSubmenu (MenuSP (new Menu("Registers", NULL, 'r', ApplicationDelegate::eMenuID_ViewRegisters))); 5188262182Semaste view_menu_sp->AddSubmenu (MenuSP (new Menu("Source" , NULL, 's', ApplicationDelegate::eMenuID_ViewSource))); 5189262182Semaste view_menu_sp->AddSubmenu (MenuSP (new Menu("Variables", NULL, 'v', ApplicationDelegate::eMenuID_ViewVariables))); 5190262182Semaste 5191262182Semaste MenuSP help_menu_sp(new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help)); 5192262182Semaste help_menu_sp->AddSubmenu (MenuSP (new Menu("GUI Help", NULL, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp))); 5193262182Semaste 5194262182Semaste m_app_ap->Initialize(); 5195262182Semaste WindowSP &main_window_sp = m_app_ap->GetMainWindow(); 5196262182Semaste 5197262182Semaste MenuSP menubar_sp(new Menu(Menu::Type::Bar)); 5198262182Semaste menubar_sp->AddSubmenu (lldb_menu_sp); 5199262182Semaste menubar_sp->AddSubmenu (target_menu_sp); 5200262182Semaste menubar_sp->AddSubmenu (process_menu_sp); 5201262182Semaste menubar_sp->AddSubmenu (thread_menu_sp); 5202262182Semaste menubar_sp->AddSubmenu (view_menu_sp); 5203262182Semaste menubar_sp->AddSubmenu (help_menu_sp); 5204262182Semaste menubar_sp->SetDelegate(app_menu_delegate_sp); 5205262182Semaste 5206262182Semaste Rect content_bounds = main_window_sp->GetFrame(); 5207262182Semaste Rect menubar_bounds = content_bounds.MakeMenuBar(); 5208262182Semaste Rect status_bounds = content_bounds.MakeStatusBar(); 5209262182Semaste Rect source_bounds; 5210262182Semaste Rect variables_bounds; 5211262182Semaste Rect threads_bounds; 5212262182Semaste Rect source_variables_bounds; 5213262182Semaste content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds, threads_bounds); 5214262182Semaste source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds, variables_bounds); 5215262182Semaste 5216262182Semaste WindowSP menubar_window_sp = main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false); 5217262182Semaste // Let the menubar get keys if the active window doesn't handle the 5218262182Semaste // keys that are typed so it can respond to menubar key presses. 5219262182Semaste menubar_window_sp->SetCanBeActive(false); // Don't let the menubar become the active window 5220262182Semaste menubar_window_sp->SetDelegate(menubar_sp); 5221262182Semaste 5222262182Semaste WindowSP source_window_sp (main_window_sp->CreateSubWindow("Source", 5223262182Semaste source_bounds, 5224262182Semaste true)); 5225262182Semaste WindowSP variables_window_sp (main_window_sp->CreateSubWindow("Variables", 5226262182Semaste variables_bounds, 5227262182Semaste false)); 5228262182Semaste WindowSP threads_window_sp (main_window_sp->CreateSubWindow("Threads", 5229262182Semaste threads_bounds, 5230262182Semaste false)); 5231262182Semaste WindowSP status_window_sp (main_window_sp->CreateSubWindow("Status", 5232262182Semaste status_bounds, 5233262182Semaste false)); 5234262182Semaste status_window_sp->SetCanBeActive(false); // Don't let the status bar become the active window 5235262182Semaste main_window_sp->SetDelegate (std::static_pointer_cast<WindowDelegate>(app_delegate_sp)); 5236262182Semaste source_window_sp->SetDelegate (WindowDelegateSP(new SourceFileWindowDelegate(m_debugger))); 5237262182Semaste variables_window_sp->SetDelegate (WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger))); 5238262182Semaste TreeDelegateSP thread_delegate_sp (new ThreadTreeDelegate(m_debugger)); 5239262182Semaste threads_window_sp->SetDelegate (WindowDelegateSP(new TreeWindowDelegate(m_debugger, thread_delegate_sp))); 5240262182Semaste status_window_sp->SetDelegate (WindowDelegateSP(new StatusBarWindowDelegate(m_debugger))); 5241262182Semaste 5242262182Semaste // Show the main help window once the first time the curses GUI is launched 5243262182Semaste static bool g_showed_help = false; 5244262182Semaste if (!g_showed_help) 5245262182Semaste { 5246262182Semaste g_showed_help = true; 5247262182Semaste main_window_sp->CreateHelpSubwindow(); 5248262182Semaste } 5249262182Semaste 5250262182Semaste init_pair (1, COLOR_WHITE , COLOR_BLUE ); 5251262182Semaste init_pair (2, COLOR_BLACK , COLOR_WHITE ); 5252262182Semaste init_pair (3, COLOR_MAGENTA , COLOR_WHITE ); 5253262182Semaste init_pair (4, COLOR_MAGENTA , COLOR_BLACK ); 5254262182Semaste init_pair (5, COLOR_RED , COLOR_BLACK ); 5255262182Semaste 5256262182Semaste } 5257262182Semaste} 5258262182Semaste 5259262182Semastevoid 5260262182SemasteIOHandlerCursesGUI::Deactivate () 5261262182Semaste{ 5262262182Semaste m_app_ap->Terminate(); 5263262182Semaste} 5264262182Semaste 5265262182Semastevoid 5266262182SemasteIOHandlerCursesGUI::Run () 5267262182Semaste{ 5268262182Semaste m_app_ap->Run(m_debugger); 5269262182Semaste SetIsDone(true); 5270262182Semaste} 5271262182Semaste 5272262182Semaste 5273262182SemasteIOHandlerCursesGUI::~IOHandlerCursesGUI () 5274262182Semaste{ 5275262182Semaste 5276262182Semaste} 5277262182Semaste 5278262182Semastevoid 5279262182SemasteIOHandlerCursesGUI::Hide () 5280262182Semaste{ 5281262182Semaste} 5282262182Semaste 5283262182Semaste 5284262182Semastevoid 5285262182SemasteIOHandlerCursesGUI::Refresh () 5286262182Semaste{ 5287262182Semaste} 5288262182Semaste 5289262500Semastevoid 5290262500SemasteIOHandlerCursesGUI::Cancel () 5291262500Semaste{ 5292262500Semaste} 5293262182Semaste 5294262182Semastevoid 5295262182SemasteIOHandlerCursesGUI::Interrupt () 5296262182Semaste{ 5297262182Semaste} 5298262182Semaste 5299262182Semaste 5300262182Semastevoid 5301262182SemasteIOHandlerCursesGUI::GotEOF() 5302262182Semaste{ 5303262182Semaste} 5304262182Semaste 5305262182Semaste#endif // #ifndef LLDB_DISABLE_CURSES