1 // 2 // Copyright 2005 The Android Open Source Project 3 // 4 // Main window, menu bar, and associated goodies. 5 // 6 7 // For compilers that support precompilation, include "wx/wx.h". 8 #include "wx/wxprec.h" 9 10 // Otherwise, include all standard headers 11 #ifndef WX_PRECOMP 12 # include "wx/wx.h" 13 #endif 14 #include "wx/button.h" 15 #include "wx/help.h" 16 #include "wx/filedlg.h" 17 #include "wx/slider.h" 18 #include "wx/textctrl.h" 19 20 #include "MainFrame.h" 21 #include "MyApp.h" 22 #include "Resource.h" 23 #include "PhoneCollection.h" 24 #include "PhoneData.h" 25 #include "PhoneWindow.h" 26 #include "DeviceWindow.h" 27 #include "UserEventMessage.h" 28 #include "PrefsDialog.h" 29 30 #include "SimRuntime.h" 31 32 33 static wxString kStatusNotRunning = wxT("Idle"); 34 static wxString kStatusRunning = wxT("Run"); 35 36 static wxString kDeviceMenuString = wxT("&Device"); 37 38 static const wxString gStdJavaApps[] = { 39 wxT(""), 40 wxT("com.android.testharness.TestList"), 41 wxT("com.android.apps.contacts.ContactsList"), 42 wxT("mikeapp") 43 }; 44 45 46 BEGIN_EVENT_TABLE(MainFrame::MainFrame, wxFrame) 47 EVT_CLOSE(MainFrame::OnClose) 48 EVT_TIMER(kHalfSecondTimerId, MainFrame::OnTimer) 49 //EVT_IDLE(MainFrame::OnIdle) 50 51 EVT_ACTIVATE(MainFrame::OnActivate) 52 EVT_ACTIVATE_APP(MainFrame::OnActivate) 53 EVT_COMBOBOX(IDC_MODE_SELECT, MainFrame::OnComboBox) 54 EVT_COMBOBOX(IDC_JAVA_VM, MainFrame::OnComboBox) 55 EVT_CHECKBOX(IDC_USE_GDB, MainFrame::OnCheckBox) 56 EVT_CHECKBOX(IDC_USE_VALGRIND, MainFrame::OnCheckBox) 57 EVT_CHECKBOX(IDC_CHECK_JNI, MainFrame::OnCheckBox) 58 EVT_CHECKBOX(IDC_OVERLAY_ONION_SKIN, MainFrame::OnCheckBox) 59 EVT_TEXT(IDC_JAVA_APP_NAME, MainFrame::OnText) 60 EVT_TEXT_ENTER(IDC_ONION_SKIN_FILE_NAME, MainFrame::OnTextEnter) 61 EVT_BUTTON(IDC_ONION_SKIN_BUTTON, MainFrame::OnButton) 62 EVT_COMMAND_SCROLL(IDC_ONION_SKIN_ALPHA_VAL, MainFrame::OnSliderChange) 63 64 EVT_MENU(IDM_FILE_PREFERENCES, MainFrame::OnFilePreferences) 65 EVT_MENU(IDM_FILE_EXIT, MainFrame::OnFileExit) 66 EVT_MENU(IDM_RUNTIME_START, MainFrame::OnSimStart) 67 EVT_UPDATE_UI(IDM_RUNTIME_START, MainFrame::OnUpdateSimStart) 68 EVT_MENU(IDM_RUNTIME_STOP, MainFrame::OnSimStop) 69 EVT_UPDATE_UI(IDM_RUNTIME_STOP, MainFrame::OnUpdateSimStop) 70 EVT_MENU(IDM_RUNTIME_RESTART, MainFrame::OnSimRestart) 71 EVT_UPDATE_UI(IDM_RUNTIME_RESTART, MainFrame::OnUpdateSimRestart) 72 EVT_MENU(IDM_RUNTIME_KILL, MainFrame::OnSimKill) 73 EVT_UPDATE_UI(IDM_RUNTIME_KILL, MainFrame::OnUpdateSimKill) 74 EVT_MENU_RANGE(IDM_DEVICE_SEL0, IDM_DEVICE_SELN, 75 MainFrame::OnDeviceSelected) 76 EVT_MENU(IDM_DEVICE_RESCAN, MainFrame::OnDeviceRescan) 77 EVT_UPDATE_UI(IDM_DEBUG_SHOW_LOG, MainFrame::OnUpdateDebugShowLog) 78 EVT_MENU(IDM_DEBUG_SHOW_LOG, MainFrame::OnDebugShowLog) 79 EVT_MENU(IDM_HELP_CONTENTS, MainFrame::OnHelpContents) 80 EVT_MENU(IDM_HELP_ABOUT, MainFrame::OnHelpAbout) 81 82 EVT_USER_EVENT(MainFrame::OnUserEvent) 83 END_EVENT_TABLE() 84 85 86 /* 87 * Main window constructor. 88 * 89 * Creates menus and status bar. 90 */ 91 MainFrame::MainFrame(const wxString& title, const wxPoint& pos, 92 const wxSize& size, long style) 93 : wxFrame((wxFrame *)NULL, -1, title, pos, size, style), 94 mSimRunning(false), 95 mRestartRequested(false), 96 mpPhoneWindow(NULL), 97 mPhoneWindowPosn(wxDefaultPosition), 98 mTimer(this, kHalfSecondTimerId) 99 { 100 mSimAssetPath = ((MyApp*)wxTheApp)->GetSimAssetPath(); 101 mSimAssetPath += wxT("/simulator/default/default"); 102 103 Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); 104 int val; 105 106 val = mPhoneWindowPosn.x; 107 pPrefs->GetInt("window-device-x", &val); 108 mPhoneWindowPosn.x = val; 109 val = mPhoneWindowPosn.y; 110 pPrefs->GetInt("window-device-y", &val); 111 mPhoneWindowPosn.y = val; 112 113 /* 114 * Create main menu. 115 */ 116 ConstructMenu(); 117 118 /* 119 * Create the status bar. 120 */ 121 int widths[2] = { -1, 50 }; 122 CreateStatusBar(2, wxFULL_REPAINT_ON_RESIZE); // no wxST_SIZEGRIP 123 SetStatusWidths(2, widths); 124 SetStatusText(wxT("Ready")); 125 SetStatusText(kStatusNotRunning, 1); 126 127 /* 128 * Create main window controls. 129 */ 130 ConstructControls(); 131 132 #if 0 133 /* 134 * Use the standard window color for the main frame (which usually 135 * has a darker color). This has a dramatic effect under Windows. 136 */ 137 wxColour color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); 138 SetOwnBackgroundColour(color); 139 #endif 140 141 /* 142 * Create the log window. 143 */ 144 wxRect layout = LogWindow::GetPrefWindowRect(); 145 mpLogWindow = new LogWindow(this); 146 mpLogWindow->Move(layout.GetTopLeft()); 147 mpLogWindow->SetSize(layout.GetSize()); 148 bool showLogWindow = true; 149 pPrefs->GetBool("window-log-show", &showLogWindow); 150 if (showLogWindow) 151 mpLogWindow->Show(); 152 153 /* 154 * Set up a frequent timer. We use this to keep our "run/idle" 155 * display up to date. (Ideally this will go away.) 156 */ 157 mTimer.Start(400); // arg is delay in ms 158 159 /* 160 * Handle auto-power-on by sending ourselves an event. That way it 161 * gets handled after window initialization finishes. 162 */ 163 bool autoPowerOn = false; 164 pPrefs->GetBool("auto-power-on", &autoPowerOn); 165 if (autoPowerOn) { 166 printf("Sim: Auto power-up\n"); 167 wxCommandEvent startEvent(wxEVT_COMMAND_MENU_SELECTED, IDM_RUNTIME_START); 168 this->AddPendingEvent(startEvent); 169 } 170 171 /* 172 * wxThread wants these to be on the heap -- it will call delete on the 173 * object when the thread exits. 174 */ 175 mExternalRuntimeThread = new ExternalRuntime(); 176 mExternalRuntimeThread->StartThread(); 177 mPropertyServerThread = new PropertyServer(); 178 mPropertyServerThread->StartThread(); 179 } 180 181 /* 182 * Construct the main menu. Called from the constructor. 183 */ 184 void MainFrame::ConstructMenu(void) 185 { 186 Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); 187 188 /* 189 * Scan for available phones. 190 */ 191 PhoneCollection* pCollection = PhoneCollection::GetInstance(); 192 pCollection->ScanForPhones(mSimAssetPath.ToAscii()); 193 194 /* 195 * Create the "File" menu. 196 */ 197 wxMenu* menuFile = new wxMenu; 198 199 menuFile->Append(IDM_FILE_PREFERENCES, wxT("&Preferences..."), 200 wxT("Edit simulator preferences")); 201 menuFile->AppendSeparator(); 202 menuFile->Append(IDM_FILE_EXIT, wxT("E&xit\tCtrl-Q"), 203 wxT("Stop simulator and exit")); 204 205 /* 206 * Create the "Runtime" menu. 207 */ 208 wxMenu* menuRuntime = new wxMenu; 209 menuRuntime->Append(IDM_RUNTIME_START, wxT("&Power On\tCtrl-G"), 210 wxT("Start the device")); 211 // menuRuntime->Append(IDM_RUNTIME_STOP, wxT("Power &Off"), 212 // wxT("Stop the device")); 213 menuRuntime->AppendSeparator(); 214 // menuRuntime->Append(IDM_RUNTIME_RESTART, wxT("&Restart"), 215 // wxT("Restart the device")); 216 menuRuntime->Append(IDM_RUNTIME_KILL, wxT("&Kill\tCtrl-K"), 217 wxT("Kill the runtime processes")); 218 219 /* 220 * Create "Device" menu. 221 */ 222 wxString defaultDevice = wxT("Sooner"); 223 pPrefs->GetString("default-device", /*ref*/ defaultDevice); 224 wxMenu* menuDevice = CreateDeviceMenu(defaultDevice.ToAscii()); 225 226 /* 227 * Create "Debug" menu. 228 */ 229 wxMenu* menuDebug = new wxMenu; 230 menuDebug->AppendCheckItem(IDM_DEBUG_SHOW_LOG, wxT("View &Log Output"), 231 wxT("View log output window")); 232 233 /* 234 * Create the "Help" menu. 235 */ 236 wxMenu* menuHelp = new wxMenu; 237 menuHelp->Append(IDM_HELP_CONTENTS, wxT("&Contents...\tF1"), 238 wxT("Simulator help")); 239 menuHelp->AppendSeparator(); 240 menuHelp->Append(IDM_HELP_ABOUT, wxT("&About..."), 241 wxT("See the fabulous 'about' box")); 242 243 /* 244 * Create the menu bar. 245 */ 246 wxMenuBar *menuBar = new wxMenuBar; 247 menuBar->Append(menuFile, wxT("&File")); 248 menuBar->Append(menuDevice, kDeviceMenuString); 249 menuBar->Append(menuRuntime, wxT("&Runtime")); 250 menuBar->Append(menuDebug, wxT("&Debug")); 251 menuBar->Append(menuHelp, wxT("&Help")); 252 253 SetMenuBar(menuBar); 254 255 } 256 257 /* 258 * Construct the "device" menu from our phone collection. 259 */ 260 wxMenu* MainFrame::CreateDeviceMenu(const char* defaultItemName) 261 { 262 wxMenu* menuDevice = new wxMenu; 263 PhoneCollection* pCollection = PhoneCollection::GetInstance(); 264 int defaultModel = 0; 265 266 for (int i = 0; i < pCollection->GetPhoneCount(); i++) { 267 PhoneData* pPhoneData = pCollection->GetPhoneData(i); 268 assert(pPhoneData != NULL); 269 270 menuDevice->AppendRadioItem(IDM_DEVICE_SEL0 + i, 271 wxString::FromAscii(pPhoneData->GetTitle())); 272 273 // use this one as default if the string matches 274 if (strcasecmp(pPhoneData->GetName(), defaultItemName) == 0) 275 defaultModel = i; 276 } 277 278 menuDevice->Check(IDM_DEVICE_SEL0 + defaultModel, true); 279 280 menuDevice->AppendSeparator(); 281 menuDevice->Append(IDM_DEVICE_RESCAN, wxT("Re-scan")); 282 283 return menuDevice; 284 } 285 286 /* 287 * Create some controls in the main window. 288 * 289 * The main frame doesn't use the normal background color that you find 290 * in dialog windows, so we create a "panel" and put all the controls 291 * on that. 292 */ 293 void MainFrame::ConstructControls(void) 294 { 295 Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); 296 wxPanel* base = new wxPanel(this, wxID_ANY); 297 wxBoxSizer* masterSizer = new wxBoxSizer(wxVERTICAL); 298 wxBoxSizer* tmpSizer; 299 wxStaticBoxSizer* displayOptSizer; 300 wxStaticBoxSizer* runtimeOptSizer; 301 wxStaticBoxSizer* onionSkinOptSizer; 302 wxComboBox* pModeSelection; 303 wxCheckBox* pUseGDB; 304 wxCheckBox* pUseValgrind; 305 wxCheckBox* pCheckJni; 306 wxCheckBox* pOverlayOnionSkin; 307 308 displayOptSizer = new wxStaticBoxSizer(wxHORIZONTAL, base, 309 wxT("Configuration")); 310 runtimeOptSizer = new wxStaticBoxSizer(wxVERTICAL, base, 311 wxT("Runtime Options")); 312 onionSkinOptSizer = new wxStaticBoxSizer(wxVERTICAL, base, 313 wxT("Onion Skin Options")); 314 315 /* 316 * Set up the configuration sizer (nee "display options"). 317 */ 318 tmpSizer = new wxBoxSizer(wxHORIZONTAL); 319 displayOptSizer->Add(tmpSizer); 320 tmpSizer->Add( 321 new wxStaticText(base, wxID_ANY, wxT("Device mode:"), 322 wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT), 0, wxALIGN_CENTER_VERTICAL); 323 pModeSelection = new wxComboBox(base, IDC_MODE_SELECT, wxT(""), 324 wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); 325 tmpSizer->AddSpacer(kInterSpacing); 326 tmpSizer->Add(pModeSelection); 327 328 displayOptSizer->AddSpacer(kInterSpacing); 329 330 /* 331 * Configure the runtime options sizer. 332 */ 333 wxComboBox* pJavaAppName; 334 tmpSizer = new wxBoxSizer(wxHORIZONTAL); 335 pUseGDB = new wxCheckBox(base, IDC_USE_GDB, wxT("Use &debugger")); 336 tmpSizer->Add(pUseGDB); 337 tmpSizer->AddSpacer(kInterSpacing); 338 pUseValgrind = new wxCheckBox(base, IDC_USE_VALGRIND, wxT("Use &valgrind")); 339 tmpSizer->Add(pUseValgrind); 340 tmpSizer->AddSpacer(kInterSpacing); 341 pCheckJni = new wxCheckBox(base, IDC_CHECK_JNI, wxT("Check &JNI")); 342 tmpSizer->Add(pCheckJni); 343 344 pJavaAppName = new wxComboBox(base, IDC_JAVA_APP_NAME, wxT(""), 345 wxDefaultPosition, wxSize(320, -1), NELEM(gStdJavaApps), gStdJavaApps, 346 wxCB_DROPDOWN); 347 wxBoxSizer* javaAppSizer = new wxBoxSizer(wxHORIZONTAL); 348 javaAppSizer->Add( 349 new wxStaticText(base, wxID_ANY, 350 wxT("Java app:"), 351 wxDefaultPosition, wxDefaultSize, 352 wxALIGN_LEFT), 353 0, wxALIGN_CENTER_VERTICAL); 354 javaAppSizer->AddSpacer(kInterSpacing); 355 javaAppSizer->Add(pJavaAppName); 356 357 runtimeOptSizer->Add(tmpSizer); 358 359 runtimeOptSizer->AddSpacer(kInterSpacing); 360 runtimeOptSizer->Add(javaAppSizer); 361 runtimeOptSizer->AddSpacer(kInterSpacing); 362 363 wxString tmpStr; 364 SetCheckFromPref(pUseGDB, "debug", false); 365 SetCheckFromPref(pUseValgrind, "valgrind", false); 366 SetCheckFromPref(pCheckJni, "check-jni", false); 367 if (pPrefs->GetString("java-app-name", /*ref*/ tmpStr)) 368 pJavaAppName->SetValue(tmpStr); 369 370 /* 371 * Configure the onion skin options sizer. 372 */ 373 wxTextCtrl* pOnionSkinFileNameText; 374 wxButton* pOnionSkinFileButton; 375 wxSlider* pOnionSkinAlphaSlider; 376 tmpSizer = new wxBoxSizer(wxHORIZONTAL); 377 pOverlayOnionSkin = new wxCheckBox(base, 378 IDC_OVERLAY_ONION_SKIN, wxT("Overlay &onion skin")); 379 tmpSizer->Add(pOverlayOnionSkin); 380 381 pOnionSkinFileNameText = new wxTextCtrl(base, 382 IDC_ONION_SKIN_FILE_NAME, wxT(""), 383 wxDefaultPosition, wxSize(250, -1), 384 wxTE_PROCESS_ENTER); 385 pOnionSkinFileButton = new wxButton(base, IDC_ONION_SKIN_BUTTON, 386 wxT("Choose")); 387 388 wxBoxSizer* onionSkinFileNameSizer = new wxBoxSizer(wxHORIZONTAL); 389 onionSkinFileNameSizer->Add( 390 new wxStaticText(base, wxID_ANY, 391 wxT("Filename:"), 392 wxDefaultPosition, wxDefaultSize, 393 wxALIGN_LEFT), 394 0, wxALIGN_CENTER_VERTICAL); 395 onionSkinFileNameSizer->AddSpacer(kInterSpacing); 396 onionSkinFileNameSizer->Add(pOnionSkinFileNameText); 397 onionSkinFileNameSizer->Add(pOnionSkinFileButton); 398 399 wxBoxSizer * onionSkinAlphaSizer = new wxBoxSizer(wxHORIZONTAL); 400 int initialAlphaVal = 127; 401 pPrefs->GetInt("onion-skin-alpha-value", &initialAlphaVal); 402 pOnionSkinAlphaSlider = new wxSlider(base, IDC_ONION_SKIN_ALPHA_VAL, 403 initialAlphaVal, 0, 255, wxDefaultPosition, wxSize(150, 20)); 404 onionSkinAlphaSizer->Add( 405 new wxStaticText(base, wxID_ANY, 406 wxT("Transparency:"), 407 wxDefaultPosition, wxDefaultSize, 408 wxALIGN_LEFT), 409 0, wxALIGN_CENTER_VERTICAL); 410 onionSkinAlphaSizer->AddSpacer(kInterSpacing); 411 onionSkinAlphaSizer->Add(pOnionSkinAlphaSlider, 1, wxCENTRE | wxALL, 5); 412 413 onionSkinOptSizer->Add(tmpSizer); 414 onionSkinOptSizer->AddSpacer(kInterSpacing); 415 onionSkinOptSizer->Add(onionSkinFileNameSizer); 416 onionSkinOptSizer->Add(onionSkinAlphaSizer); 417 418 wxString tmpStr2; 419 SetCheckFromPref(pOverlayOnionSkin, "overlay-onion-skin", false); 420 if (pPrefs->GetString("onion-skin-file-name", /*ref*/ tmpStr2)) 421 pOnionSkinFileNameText->SetValue(tmpStr2); 422 423 /* 424 * Add the various components to the master sizer. 425 */ 426 masterSizer->Add(displayOptSizer); 427 masterSizer->AddSpacer(kInterSpacing * 2); 428 masterSizer->Add(runtimeOptSizer); 429 masterSizer->AddSpacer(kInterSpacing * 2); 430 masterSizer->Add(onionSkinOptSizer); 431 //masterSizer->AddSpacer(kInterSpacing); 432 433 /* 434 * I don't see a way to guarantee that the window is wide enough to 435 * show the entire menu bar, so just throw some pixels at it. 436 */ 437 wxBoxSizer* minWidthSizer = new wxBoxSizer(wxVERTICAL); 438 minWidthSizer->Add(300, kEdgeSpacing); // forces minimum width 439 minWidthSizer->Add(masterSizer); 440 minWidthSizer->AddSpacer(kInterSpacing * 2); 441 442 /* move us a few pixels in from the left */ 443 wxBoxSizer* indentSizer = new wxBoxSizer(wxHORIZONTAL); 444 indentSizer->AddSpacer(kEdgeSpacing); 445 indentSizer->Add(minWidthSizer); 446 indentSizer->AddSpacer(kEdgeSpacing); 447 448 base->SetSizer(indentSizer); 449 450 indentSizer->Fit(this); 451 indentSizer->SetSizeHints(this); 452 } 453 454 /* 455 * Set the value of a checkbox based on a value from the config file. 456 */ 457 void MainFrame::SetCheckFromPref(wxCheckBox* pControl, const char* prefStr, 458 bool defaultVal) 459 { 460 Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); 461 assert(pPrefs != NULL); 462 463 bool val = defaultVal; 464 pPrefs->GetBool(prefStr, &val); 465 466 pControl->SetValue(val); 467 } 468 469 /* 470 * Destructor. 471 */ 472 MainFrame::~MainFrame(void) 473 { 474 PhoneCollection::DestroyInstance(); 475 476 delete mExternalRuntimeThread; 477 delete mPropertyServerThread; 478 479 // don't touch mpModeSelection -- child of window 480 } 481 482 /* 483 * File->Quit or click on close box. 484 * 485 * If we want an "are you sure you want to quit" box, add it here. 486 */ 487 void MainFrame::OnClose(wxCloseEvent& event) 488 { 489 Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); 490 491 /* 492 if (event.CanVeto()) 493 printf("Closing (can veto)\n"); 494 else 495 printf("Closing (mandatory)\n"); 496 */ 497 498 /* 499 * Generally speaking, Close() is not guaranteed to close the window. 500 * However, we want to use it here because (a) our windows are 501 * guaranteed to close, and (b) it provides our windows an opportunity 502 * to tell others that they are about to vanish. 503 */ 504 if (mpPhoneWindow != NULL) 505 mpPhoneWindow->Close(true); 506 507 /* save position of main window */ 508 wxPoint pos = GetPosition(); 509 pPrefs->SetInt("window-main-x", pos.x); 510 pPrefs->SetInt("window-main-y", pos.y); 511 512 /* save default device selection */ 513 int idx = GetSelectedDeviceIndex(); 514 if (idx >= 0) { 515 PhoneCollection* pCollection = PhoneCollection::GetInstance(); 516 PhoneData* pPhoneData = pCollection->GetPhoneData(idx); 517 pPrefs->SetString("default-device", pPhoneData->GetName()); 518 } 519 520 if (mpLogWindow != NULL) 521 mpLogWindow->Close(true); 522 Destroy(); 523 } 524 525 /* 526 * File->Preferences 527 */ 528 void MainFrame::OnFilePreferences(wxCommandEvent& WXUNUSED(event)) 529 { 530 Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); 531 PrefsDialog dialog(this); 532 int result; 533 534 result = dialog.ShowModal(); 535 if (result == wxID_OK) { 536 /* 537 * The dialog handles writing changes to Preferences, so all we 538 * need to deal with here are changes that have an immediate 539 * impact on us. (which is currently nothing) 540 */ 541 } 542 } 543 544 /* 545 * File->Exit 546 */ 547 void MainFrame::OnFileExit(wxCommandEvent& WXUNUSED(event)) 548 { 549 Close(FALSE); // false means "allow veto" 550 } 551 552 /* 553 * Decide whether Simulator->Start should be enabled. 554 */ 555 void MainFrame::OnUpdateSimStart(wxUpdateUIEvent& event) 556 { 557 if (IsRuntimeRunning()) 558 event.Enable(FALSE); 559 else 560 event.Enable(TRUE); 561 } 562 563 /* 564 * Simulator->Start 565 */ 566 void MainFrame::OnSimStart(wxCommandEvent& WXUNUSED(event)) 567 { 568 // keyboard equivalents can still get here even if menu item disabled 569 if (IsRuntimeRunning()) 570 return; 571 572 int id = GetSelectedDeviceIndex(); 573 if (id < 0) { 574 fprintf(stderr, "Sim: could not identify currently selected device\n"); 575 return; 576 } 577 578 #if 0 579 static int foo = 0; 580 foo++; 581 if (foo == 2) { 582 Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); 583 584 pPrefs->SetBool("debug", true); 585 } 586 #endif 587 588 SetupPhoneUI(id, NULL); 589 if (mpPhoneWindow != NULL) 590 mpPhoneWindow->GetDeviceManager()->StartRuntime(); 591 } 592 593 /* 594 * Decide whether Simulator->Stop should be enabled. 595 */ 596 void MainFrame::OnUpdateSimStop(wxUpdateUIEvent& event) 597 { 598 if (IsRuntimeRunning()) 599 event.Enable(TRUE); 600 else 601 event.Enable(FALSE); 602 } 603 604 /* 605 * Simulator->Stop 606 */ 607 void MainFrame::OnSimStop(wxCommandEvent& WXUNUSED(event)) 608 { 609 if (!IsRuntimeRunning()) 610 return; 611 assert(mpPhoneWindow != NULL); 612 mpPhoneWindow->GetDeviceManager()->StopRuntime(); 613 } 614 615 /* 616 * Decide whether Simulator->Restart should be enabled. 617 */ 618 void MainFrame::OnUpdateSimRestart(wxUpdateUIEvent& event) 619 { 620 if (IsRuntimeRunning()) 621 event.Enable(TRUE); 622 else 623 event.Enable(FALSE); 624 } 625 626 /* 627 * Simulator->Restart - stop then start the device runtime. 628 */ 629 void MainFrame::OnSimRestart(wxCommandEvent& WXUNUSED(event)) 630 { 631 if (!IsRuntimeRunning()) 632 return; 633 634 printf("Restart requested\n"); 635 mpPhoneWindow->GetDeviceManager()->StopRuntime(); 636 637 mRestartRequested = true; 638 } 639 640 /* 641 * Decide whether Simulator->Kill should be enabled. 642 */ 643 void MainFrame::OnUpdateSimKill(wxUpdateUIEvent& event) 644 { 645 if (IsRuntimeKillable()) 646 event.Enable(TRUE); 647 else 648 event.Enable(FALSE); 649 } 650 651 /* 652 * Simulator->Kill 653 */ 654 void MainFrame::OnSimKill(wxCommandEvent& WXUNUSED(event)) 655 { 656 if (!IsRuntimeKillable()) 657 return; 658 assert(mpPhoneWindow != NULL); 659 mpPhoneWindow->GetDeviceManager()->KillRuntime(); 660 } 661 662 663 /* 664 * Device->[select] 665 */ 666 void MainFrame::OnDeviceSelected(wxCommandEvent& event) 667 { 668 wxBusyCursor busyc; 669 int id = event.GetId() - IDM_DEVICE_SEL0; 670 671 SetupPhoneUI(id, NULL); 672 } 673 674 /* 675 * Device->Rescan 676 */ 677 void MainFrame::OnDeviceRescan(wxCommandEvent& event) 678 { 679 wxBusyCursor busyc; 680 wxMenuBar* pMenuBar; 681 PhoneCollection* pCollection; 682 wxMenu* pOldMenu; 683 wxMenu* pNewMenu; 684 const char* curDevName = NULL; 685 int idx; 686 687 /* figure out the current device name */ 688 pCollection = PhoneCollection::GetInstance(); 689 idx = GetSelectedDeviceIndex(); 690 if (idx >= 0) { 691 PhoneData* pPhoneData; 692 693 pPhoneData = pCollection->GetPhoneData(idx); 694 curDevName = pPhoneData->GetName(); 695 printf("--- device name is '%s'\n", (const char*) curDevName); 696 } 697 698 /* reconstruct device menu with new data */ 699 #ifdef BEFORE_ASSET 700 pCollection->ScanForPhones(mSimAssetPath); 701 #else 702 pCollection->ScanForPhones(NULL); 703 #endif 704 705 pMenuBar = GetMenuBar(); 706 idx = pMenuBar->FindMenu(kDeviceMenuString); 707 if (idx == wxNOT_FOUND) { 708 fprintf(stderr, "Sim: couldn't find %s menu\n", (const char*) kDeviceMenuString.ToAscii()); 709 return; 710 } 711 712 pNewMenu = CreateDeviceMenu(curDevName); 713 714 pOldMenu = pMenuBar->Replace(idx, pNewMenu, kDeviceMenuString); 715 delete pOldMenu; 716 717 /* tell the PhoneWindow about it; may cause runtime to exit */ 718 if (mpPhoneWindow != NULL) 719 mpPhoneWindow->DevicesRescanned(); 720 } 721 722 /* 723 * Set checkbox on menu item. 724 */ 725 void MainFrame::OnUpdateDebugShowLog(wxUpdateUIEvent& event) 726 { 727 if (mpLogWindow == NULL) { 728 event.Enable(false); 729 } else { 730 event.Enable(true); 731 event.Check(mpLogWindow->IsShown()); 732 } 733 } 734 735 /* 736 * Debug->ShowLog toggle. 737 */ 738 void MainFrame::OnDebugShowLog(wxCommandEvent& WXUNUSED(event)) 739 { 740 mpLogWindow->Show(!mpLogWindow->IsShown()); 741 } 742 743 /* 744 * Help->Contents 745 */ 746 void MainFrame::OnHelpContents(wxCommandEvent& WXUNUSED(event)) 747 { 748 ((MyApp*)wxTheApp)->GetHelpController()->DisplayContents(); 749 } 750 751 /* 752 * Help->About 753 */ 754 void MainFrame::OnHelpAbout(wxCommandEvent& WXUNUSED(event)) 755 { 756 wxMessageBox(wxT("Android Simulator v0.1\n" 757 "Copyright 2006 The Android Open Source Project"), 758 wxT("About..."), wxOK | wxICON_INFORMATION, this); 759 } 760 761 /* 762 * Sent from phonewindow or when activated 763 */ 764 void MainFrame::OnActivate(wxActivateEvent& event) 765 { 766 #if 0 767 if (event.GetActive()) 768 { 769 if (mpPhoneWindow != NULL && 770 mpPhoneWindow->GetDeviceManager()->RefreshRuntime()) 771 { 772 wxString msg; 773 int sel; 774 775 msg = wxT("Newer runtime executable found. Would you like to reload the device?"); 776 777 sel = wxMessageBox(msg, wxT("Android Safety Patrol"), 778 wxYES | wxNO | wxICON_QUESTION, mpPhoneWindow); 779 //printf("BUTTON was %d (yes=%d)\n", sel, wxYES); 780 if (sel == wxYES) 781 { 782 mpPhoneWindow->GetDeviceManager()->StopRuntime(); 783 mpPhoneWindow->Close(); 784 mpPhoneWindow = NULL; 785 mRestartRequested = true; 786 } 787 else 788 { 789 mpPhoneWindow->GetDeviceManager()->UserCancelledRefresh(); 790 } 791 } 792 } 793 #endif 794 795 // let wxWidgets do whatever it needs to do 796 event.Skip(); 797 } 798 799 800 /* 801 * Device mode selection box. 802 */ 803 void MainFrame::OnComboBox(wxCommandEvent& event) 804 { 805 const char* pref; 806 Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); 807 assert(pPrefs != NULL); 808 809 if (IDC_MODE_SELECT == event.GetId()) 810 { 811 int id = GetSelectedDeviceIndex(); 812 if (id < 0) 813 return; 814 //printf("--- mode selected: '%s'\n", (const char*) event.GetString().ToAscii()); 815 816 /* 817 * Call the phone window's setup function. Don't call our SetupPhoneUI 818 * function from here -- updating the combo box from a combo box callback 819 * could cause problems. 820 */ 821 if (mpPhoneWindow != NULL) { 822 mpPhoneWindow->SetCurrentMode(event.GetString()); 823 mpPhoneWindow->Setup(id); 824 } 825 } else if (event.GetId() == IDC_JAVA_VM) { 826 wxComboBox* pBox = (wxComboBox*) FindWindow(IDC_JAVA_VM); 827 pPrefs->SetString("java-vm", pBox->GetValue().ToAscii()); 828 } 829 } 830 831 /* 832 * One of our option checkboxes has been changed. 833 * 834 * We update the prefs database so that the settings are retained when 835 * the simulator is next used. 836 */ 837 void MainFrame::OnCheckBox(wxCommandEvent& event) 838 { 839 const char* pref; 840 841 switch (event.GetId()) { 842 case IDC_USE_GDB: pref = "debug"; break; 843 case IDC_USE_VALGRIND: pref = "valgrind"; break; 844 case IDC_CHECK_JNI: pref = "check-jni"; break; 845 case IDC_OVERLAY_ONION_SKIN: pref = "overlay-onion-skin"; break; 846 default: 847 printf("Sim: unrecognized checkbox %d in OnCheckBox\n", event.GetId()); 848 return; 849 } 850 851 Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); 852 assert(pPrefs != NULL); 853 854 pPrefs->SetBool(pref, (bool) event.GetInt()); 855 //printf("--- set pref '%s' to %d\n", pref, (bool) event.GetInt()); 856 if (event.GetId() == IDC_OVERLAY_ONION_SKIN) { 857 BroadcastOnionSkinUpdate(); 858 } 859 if (event.GetId() == IDC_CHECK_JNI) { 860 const char* val = "0"; 861 if ((bool) event.GetInt()) 862 val = "1"; 863 mPropertyServerThread->SetProperty(PropertyServer::kPropCheckJni, val); 864 865 } 866 } 867 868 void MainFrame::BroadcastOnionSkinUpdate() { 869 if (mpPhoneWindow != NULL) { 870 // broadcast a user event indicating an onion skin update 871 UserEvent uev(0, (void*) -1); 872 mpPhoneWindow->GetDeviceManager()->BroadcastEvent(uev); 873 } 874 } 875 876 /* 877 * A text control on the main page is being updated. 878 * 879 * The current implementation updates the preferences database on every 880 * change, which is a bit silly but is easy to do. 881 */ 882 void MainFrame::OnText(wxCommandEvent& event) 883 { 884 const char* pref; 885 886 switch (event.GetId()) { 887 case IDC_JAVA_APP_NAME: pref = "java-app-name"; break; 888 default: 889 printf("Sim: unrecognized textctrl %d in OnText\n", event.GetId()); 890 return; 891 } 892 893 Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); 894 assert(pPrefs != NULL); 895 896 // event.GetString() does not work on Mac -- always blank 897 //pPrefs->SetString(pref, event.GetString()); 898 assert(event.GetId() == IDC_JAVA_APP_NAME); // fix if we add more 899 wxComboBox* pBox; 900 pBox = (wxComboBox*) FindWindow(IDC_JAVA_APP_NAME); 901 pPrefs->SetString(pref, pBox->GetValue().ToAscii()); 902 //printf("--- set pref '%s' to '%s'\n", pref,(const char*)pBox->GetValue()); 903 } 904 905 /* 906 * A user pressed enter in a text control on the main page. 907 * 908 * The current implementation updates the preferences database on every 909 * change, which is a bit silly but is easy to do. 910 */ 911 void MainFrame::OnTextEnter(wxCommandEvent& event) 912 { 913 const char* pref; 914 915 switch (event.GetId()) { 916 case IDC_ONION_SKIN_FILE_NAME: 917 pref = "onion-skin-file-name"; 918 break; 919 default: 920 printf("Sim: unrecognized textctrl %d in OnTextEnter\n", event.GetId()); 921 return; 922 } 923 924 Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); 925 assert(pPrefs != NULL); 926 927 assert(event.GetId() == IDC_ONION_SKIN_FILE_NAME); // fix if we add more 928 wxTextCtrl* pTextCtrl; 929 pTextCtrl = (wxTextCtrl*) FindWindow(IDC_ONION_SKIN_FILE_NAME); 930 wxString onionSkinFileNameWxString = pTextCtrl->GetValue(); 931 char* onionSkinFileName = ""; 932 if (onionSkinFileNameWxString.Len() > 0) { 933 onionSkinFileName = android::strdupNew(onionSkinFileNameWxString.ToAscii()); 934 } 935 pPrefs->SetString(pref, onionSkinFileName); 936 BroadcastOnionSkinUpdate(); 937 } 938 939 /* 940 * A user pressed a button on the main page 941 * 942 */ 943 void MainFrame::OnButton(wxCommandEvent& event) 944 { 945 wxWindow* base; 946 wxFileDialog* pOnionSkinFileChooser; 947 int retVal; 948 switch (event.GetId()) { 949 case IDC_ONION_SKIN_BUTTON: 950 base = FindWindow(IDC_ONION_SKIN_BUTTON)->GetParent(); 951 pOnionSkinFileChooser = new wxFileDialog(base, 952 wxT("Choose the onion skin image file."), 953 wxT(""), wxT(""), wxT("*.*"), 954 wxOPEN | wxFILE_MUST_EXIST); 955 retVal = pOnionSkinFileChooser->ShowModal(); 956 if (retVal == pOnionSkinFileChooser->GetAffirmativeId()) { 957 Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); 958 assert(pPrefs != NULL); 959 wxString fileNameWxString = pOnionSkinFileChooser->GetPath(); 960 const char* fileName = android::strdupNew(fileNameWxString.ToAscii()); 961 wxTextCtrl* fileTextCtrl = (wxTextCtrl*) FindWindow(IDC_ONION_SKIN_FILE_NAME); 962 fileTextCtrl->SetValue(fileNameWxString); 963 pPrefs->SetString("onion-skin-file-name", fileName); 964 BroadcastOnionSkinUpdate(); 965 } 966 break; 967 default: 968 printf("Sim: unrecognized button %d in OnButton\n", event.GetId()); 969 return; 970 } 971 } 972 973 /* 974 * The user moved a slider on the main page 975 */ 976 void MainFrame::OnSliderChange(wxScrollEvent& event) 977 { 978 wxSlider* pSlider; 979 Preferences* pPrefs; 980 switch (event.GetId()) { 981 case IDC_ONION_SKIN_ALPHA_VAL: 982 pSlider = (wxSlider*) FindWindow(IDC_ONION_SKIN_ALPHA_VAL); 983 pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); 984 assert(pPrefs != NULL); 985 pPrefs->SetInt("onion-skin-alpha-value", pSlider->GetValue()); 986 BroadcastOnionSkinUpdate(); 987 break; 988 default: 989 printf("Sim: unrecognized scroller or slider %d in OnSliderChange\n", event.GetId()); 990 return; 991 } 992 } 993 994 #if 0 995 /* 996 * Idle processing. Under wxWidgets this only called once after UI 997 * activity unless you call event.RequestMore(). 998 */ 999 void MainFrame::OnIdle(wxIdleEvent& event) 1000 { 1001 event.Skip(); // let base class handler do stuff 1002 } 1003 #endif 1004 1005 /* 1006 * Handle the timer. 1007 * 1008 * This is being called in the main thread, so multithreading with the 1009 * rest of MainFrame isn't a concern here. 1010 */ 1011 void MainFrame::OnTimer(wxTimerEvent& event) 1012 { 1013 bool status; 1014 1015 /* 1016 * Check to see if the runtime died without telling us. This can only 1017 * happen if we forcibly kill our thread. We shouldn't really be 1018 * doing that anymore, but keep this in for now just in case. 1019 */ 1020 status = IsRuntimeRunning(); 1021 1022 if (mSimRunning != status) { 1023 if (!status) { 1024 printf("Sim: fixed mSimRunning=%d actual=%d\n", 1025 mSimRunning, status); 1026 mSimRunning = status; 1027 1028 if (!status) 1029 HandleRuntimeStop(); 1030 } else { 1031 /* 1032 * This was happening when we were shutting down but the 1033 * device management thread hadn't completely gone away. The 1034 * simple IsRunning test passes, so we get a false positive. 1035 * Ignore it. 1036 */ 1037 } 1038 } 1039 1040 if (gWantToKill) { 1041 if (IsRuntimeRunning()) { 1042 printf("Sim: handling kill request\n"); 1043 mpPhoneWindow->GetDeviceManager()->KillRuntime(); 1044 } 1045 gWantToKill = false; 1046 1047 /* see if Ctrl-C should kill us too */ 1048 Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); 1049 bool die = false; 1050 1051 pPrefs->GetBool("trap-sigint-suicide", &die); 1052 if (die) { 1053 printf("Sim: goodbye cruel world!\n"); 1054 exit(0); 1055 } 1056 } 1057 } 1058 1059 /* 1060 * Determine whether or not the simulator is running. 1061 */ 1062 bool MainFrame::IsRuntimeRunning(void) 1063 { 1064 bool result; 1065 1066 if (mpPhoneWindow == NULL) 1067 result = false; 1068 else if (!mpPhoneWindow->IsReady()) 1069 result = false; 1070 else 1071 result = mpPhoneWindow->GetDeviceManager()->IsRunning(); 1072 1073 return result; 1074 } 1075 1076 /* 1077 * Determine whether or not the runtime can be killed. 1078 */ 1079 bool MainFrame::IsRuntimeKillable(void) 1080 { 1081 bool result; 1082 1083 result = IsRuntimeRunning(); 1084 if (result) 1085 result = mpPhoneWindow->GetDeviceManager()->IsKillable(); 1086 1087 return result; 1088 } 1089 1090 /* 1091 * Determine whether two devices are sufficiently compatible. 1092 */ 1093 bool MainFrame::CompatibleDevices(PhoneData* pData1, PhoneData* pData2) 1094 { 1095 int displayCount; 1096 1097 displayCount = pData1->GetNumDisplays(); 1098 if (pData2->GetNumDisplays() != displayCount) 1099 return false; 1100 1101 for (int i = 0; i < displayCount; i++) { 1102 PhoneDisplay* pDisplay1 = pData1->GetPhoneDisplay(i); 1103 PhoneDisplay* pDisplay2 = pData2->GetPhoneDisplay(i); 1104 1105 if (!PhoneDisplay::IsCompatible(pDisplay1, pDisplay2)) 1106 return false; 1107 } 1108 1109 return true; 1110 } 1111 1112 /* 1113 * (Re-)arrange the UI for the currently selected phone model. 1114 * 1115 * If the simulator is running, and the set of displays for the current 1116 * device are incompatible with the new device, we need to restart the 1117 * runtime. We need to ask for permission first though. 1118 */ 1119 void MainFrame::SetupPhoneUI(int idx, const char* defaultMode) 1120 { 1121 PhoneCollection* pCollection; 1122 PhoneData* pPhoneData; 1123 wxString* choices = NULL; 1124 int numChoices = 0; 1125 int numKeyboards = 0; 1126 bool haveDefaultMode = false; 1127 wxCharBuffer currentMode; 1128 int i; 1129 1130 pCollection = PhoneCollection::GetInstance(); 1131 pPhoneData = pCollection->GetPhoneData(idx); 1132 if (pPhoneData == NULL) { 1133 fprintf(stderr, "ERROR: device index %d not valid\n", idx); 1134 goto bail; 1135 } 1136 1137 /* 1138 * We have a window up. If the displays aren't compatible, we'll 1139 * need to recreate it. 1140 */ 1141 if (mpPhoneWindow != NULL) { 1142 PhoneData* pCurData = mpPhoneWindow->GetPhoneData(); 1143 1144 if (!CompatibleDevices(pCurData, pPhoneData)) { 1145 /* 1146 * We need to trash the window. This will also kill the 1147 * runtime. If it's running, ask permission. 1148 */ 1149 if (IsRuntimeRunning()) { 1150 wxString msg; 1151 int sel; 1152 1153 msg = wxT("Switching to the new device requires restarting the"); 1154 msg += wxT(" runtime. Continue?"); 1155 1156 sel = wxMessageBox(msg, wxT("Android Safety Patrol"), 1157 wxOK | wxCANCEL | wxICON_QUESTION, this); 1158 printf("BUTTON was %d (ok=%d)\n", sel, wxOK); 1159 if (sel == wxCANCEL) 1160 goto bail; 1161 1162 /* shut it down (politely), ask for an eventual restart */ 1163 mpPhoneWindow->GetDeviceManager()->StopRuntime(); 1164 mpPhoneWindow->Close(); 1165 mpPhoneWindow = NULL; 1166 mRestartRequested = true; 1167 goto bail; 1168 } else { 1169 /* not running, just trash the window and continue */ 1170 mpPhoneWindow->Close(); 1171 mpPhoneWindow = NULL; 1172 } 1173 } 1174 } 1175 1176 /* 1177 * Figure out the set of available modes. 1178 */ 1179 1180 numChoices = pPhoneData->GetNumModes(); 1181 if (numChoices > 0) { 1182 choices = new wxString[numChoices]; 1183 for (i = 0; i < numChoices; i++) { 1184 PhoneMode* pPhoneMode; 1185 pPhoneMode = pPhoneData->GetPhoneMode(i); 1186 choices[i] = wxString::FromAscii(pPhoneMode->GetName()); 1187 if (defaultMode != NULL && 1188 strcmp(defaultMode, pPhoneMode->GetName()) == 0) 1189 { 1190 haveDefaultMode = true; 1191 } 1192 } 1193 } 1194 1195 if (choices == NULL) { 1196 /* had a failure earlier; configure UI with default stuff */ 1197 choices = new wxString[1]; 1198 choices[0] = wxT("(none)"); 1199 } 1200 1201 if (!haveDefaultMode) { 1202 /* 1203 * Default mode wasn't found. If we specify it as the default 1204 * in the wxComboBox create call it shows up in the combo box 1205 * under Linux, even if it doesn't exist in the list. So, we 1206 * make sure that it doesn't get used if we can't find it. 1207 */ 1208 if (defaultMode != NULL) { 1209 printf("Sim: HEY: default mode '%s' not found in list\n", 1210 defaultMode); 1211 } 1212 currentMode = choices[0].ToAscii(); 1213 } else { 1214 currentMode = defaultMode; 1215 } 1216 1217 1218 /* 1219 * Create the window if necessary. 1220 */ 1221 if (mpPhoneWindow == NULL) { 1222 // create, setup, and then show window 1223 mpPhoneWindow = new PhoneWindow(this, mPhoneWindowPosn); 1224 mpPhoneWindow->SetCurrentMode((const char*)currentMode); 1225 if (!mpPhoneWindow->Setup(idx)) { 1226 delete mpPhoneWindow; 1227 mpPhoneWindow = NULL; 1228 } 1229 if (mpPhoneWindow != NULL) { 1230 mpPhoneWindow->Show(); 1231 //mpPhoneWindow->CheckPlacement(); 1232 } 1233 } else { 1234 // just set up for new device 1235 mpPhoneWindow->SetCurrentMode((const char*)currentMode); 1236 if (!mpPhoneWindow->Setup(idx)) { 1237 // it's in an uncertain state, blow it away 1238 delete mpPhoneWindow; 1239 mpPhoneWindow = NULL; 1240 } 1241 } 1242 1243 /* 1244 * Reconfigure mode selection box. 1245 */ 1246 wxComboBox* pModeSelection; 1247 pModeSelection = (wxComboBox*)FindWindow(IDC_MODE_SELECT); 1248 pModeSelection->Clear(); 1249 for (i = 0; i < numChoices; i++) 1250 pModeSelection->Append(choices[i]); 1251 pModeSelection->SetSelection(0); 1252 pModeSelection->Enable(numChoices > 1); 1253 1254 /* 1255 * configure qwerty keyboard attribute 1256 */ 1257 numKeyboards = pPhoneData->GetNumKeyboards(); 1258 if (numKeyboards > 0) { 1259 // only use the first keyboard for now 1260 PhoneKeyboard* pPhoneKeyboard; 1261 pPhoneKeyboard = pPhoneData->GetPhoneKeyboard(0); 1262 if (pPhoneKeyboard->getQwerty()) { 1263 printf("Sim: set 'qwerty' env\n"); 1264 setenv("qwerty", "true", true); 1265 } 1266 } 1267 1268 bail: 1269 delete[] choices; 1270 } 1271 1272 /* 1273 * Figure out which device is currently selected. 1274 * 1275 * The easiest way to do this is just run down the list of possible IDs 1276 * and stop when something claims to be checked. 1277 * 1278 * Returns -1 if it can't find a checked item (which can happen if no 1279 * device layouts were found). 1280 */ 1281 int MainFrame::GetSelectedDeviceIndex(void) 1282 { 1283 wxMenuBar* pMenuBar; 1284 wxMenu* pMenu; 1285 int idx; 1286 1287 pMenuBar = GetMenuBar(); 1288 idx = pMenuBar->FindMenu(kDeviceMenuString); 1289 if (idx == wxNOT_FOUND) { 1290 fprintf(stderr, "Sim: couldn't find %s menu\n", (const char*) kDeviceMenuString.ToAscii()); 1291 return -1; 1292 } 1293 1294 pMenu = pMenuBar->GetMenu(idx); 1295 1296 //printf("Menu.MenuItemCount = %d\n", pMenu->GetMenuItemCount()); 1297 for (int j = pMenu->GetMenuItemCount() -1; j >= 0; j--) { 1298 wxMenuItem* pItem; 1299 1300 pItem = pMenu->FindItemByPosition(j); 1301 //printf("ITEM %d: %s\n", j, (const char*) pItem->GetLabel()); 1302 if (pItem->IsChecked()) { 1303 printf("Sim: selected device is '%s'\n", 1304 (const char*) pItem->GetLabel().ToAscii()); 1305 return j; 1306 } 1307 } 1308 1309 return -1; 1310 } 1311 1312 /* 1313 * Receive a status message from the runtime thread. 1314 */ 1315 void MainFrame::OnUserEvent(UserEvent& event) 1316 { 1317 UserEventMessage* pUem; 1318 1319 pUem = (UserEventMessage*) event.GetData(); 1320 assert(pUem != NULL); 1321 1322 switch (pUem->GetType()) { 1323 case UserEventMessage::kRuntimeStarted: 1324 printf("Sim: runtime thread started!\n"); 1325 HandleRuntimeStart(); 1326 break; 1327 case UserEventMessage::kRuntimeStopped: 1328 printf("Sim: runtime thread stopped!\n"); 1329 HandleRuntimeStop(); 1330 break; 1331 case UserEventMessage::kErrorMessage: 1332 { 1333 wxString msg = pUem->GetString(); 1334 wxMessageBox(msg, wxT("Android Runtime Error"), 1335 wxOK | wxICON_WARNING, this); 1336 } 1337 break; 1338 case UserEventMessage::kLogMessage: 1339 mpLogWindow->AddLogMessage(pUem->GetLogMessage()); 1340 break; 1341 case UserEventMessage::kExternalRuntime: 1342 HandleExternalRuntime(pUem->GetReader(), pUem->GetWriter()); 1343 break; 1344 default: 1345 printf("Sim: MESSAGE: unknown UserEventMessage rcvd (type=%d)\n", 1346 pUem->GetType()); 1347 break; 1348 } 1349 1350 delete pUem; 1351 } 1352 1353 /* 1354 * The device management thread is up, so the runtime should be fully 1355 * running shortly. 1356 */ 1357 void MainFrame::HandleRuntimeStart(void) 1358 { 1359 mSimRunning = true; 1360 1361 SetStatusText(kStatusRunning, 1); 1362 } 1363 1364 /* 1365 * The device management thread is exiting, so the runtime must be dead. 1366 */ 1367 void MainFrame::HandleRuntimeStop(void) 1368 { 1369 mSimRunning = false; 1370 1371 SetStatusText(kStatusNotRunning, 1); 1372 1373 if (mRestartRequested) { 1374 printf("Sim: restarting runtime\n"); 1375 mRestartRequested = false; 1376 SetupPhoneUI(GetSelectedDeviceIndex(), NULL); 1377 if (mpPhoneWindow != NULL) 1378 mpPhoneWindow->GetDeviceManager()->StartRuntime(); 1379 } 1380 } 1381 1382 /* 1383 * Handle a connection from an external runtime. 1384 */ 1385 void MainFrame::HandleExternalRuntime(android::Pipe* reader, 1386 android::Pipe* writer) 1387 { 1388 android::MessageStream msgStream; 1389 android::Message msg; 1390 1391 if (IsRuntimeRunning()) { 1392 /* 1393 * Tell the new guy to go away. 1394 */ 1395 if (!msgStream.init(reader, writer, true)) { 1396 fprintf(stderr, "Sim: WARNING: unable to talk to remote runtime\n"); 1397 goto bail; 1398 } 1399 1400 printf("Sim: telling external runtime to go away\n"); 1401 msg.setCommand(android::Simulator::kCommandGoAway, 0); 1402 msgStream.send(&msg); 1403 } else { 1404 printf("Sim: new external runtime wants to talk to us\n"); 1405 1406 /* 1407 * Launch the pieces necessary to talk to this guy. 1408 */ 1409 int id = GetSelectedDeviceIndex(); 1410 if (id < 0) { 1411 fprintf(stderr, 1412 "Sim: could not identify currently selected device\n"); 1413 goto bail; 1414 } 1415 1416 /* kill existing window, so it pops up and reclaims focus */ 1417 if (mpPhoneWindow != NULL) { 1418 Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); 1419 bool okay; 1420 1421 if (pPrefs->GetBool("refocus-on-restart", &okay) && okay) { 1422 printf("Sim: inducing phone window refocus\n"); 1423 mpPhoneWindow->Close(TRUE); // no veto 1424 mpPhoneWindow = NULL; 1425 } 1426 } 1427 1428 SetupPhoneUI(id, NULL); 1429 if (mpPhoneWindow != NULL) { 1430 mpPhoneWindow->GetDeviceManager()->StartRuntime(reader, writer); 1431 } else { 1432 fprintf(stderr, "Sim: ERROR: unable to get runtime going\n"); 1433 goto bail; 1434 } 1435 1436 // we don't own these anymore 1437 reader = writer = NULL; 1438 } 1439 1440 bail: 1441 delete reader; 1442 delete writer; 1443 } 1444 1445 /* 1446 * The phone window is about to destroy itself. Get rid of our pointer 1447 * to it, and record its last position so we can create the new one in 1448 * the same place. 1449 */ 1450 void MainFrame::PhoneWindowClosing(int x, int y) 1451 { 1452 Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); 1453 1454 mpPhoneWindow = NULL; 1455 1456 mPhoneWindowPosn.x = x; 1457 mPhoneWindowPosn.y = y; 1458 1459 pPrefs->SetInt("window-device-x", x); 1460 pPrefs->SetInt("window-device-y", y); 1461 } 1462 1463