Initial Contribution
diff --git a/simulator/app/MainFrame.cpp b/simulator/app/MainFrame.cpp
new file mode 100644
index 0000000..b13caab
--- /dev/null
+++ b/simulator/app/MainFrame.cpp
@@ -0,0 +1,1462 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// Main window, menu bar, and associated goodies.
+//
+
+// For compilers that support precompilation, include "wx/wx.h".
+#include "wx/wxprec.h"
+
+// Otherwise, include all standard headers
+#ifndef WX_PRECOMP
+# include "wx/wx.h"
+#endif
+#include "wx/button.h"
+#include "wx/help.h"
+#include "wx/filedlg.h"
+#include "wx/slider.h"
+#include "wx/textctrl.h"
+
+#include "MainFrame.h"
+#include "MyApp.h"
+#include "Resource.h"
+#include "PhoneCollection.h"
+#include "PhoneData.h"
+#include "PhoneWindow.h"
+#include "DeviceWindow.h"
+#include "UserEventMessage.h"
+#include "PrefsDialog.h"
+
+#include "SimRuntime.h"
+
+
+static wxString kStatusNotRunning = wxT("Idle");
+static wxString kStatusRunning = wxT("Run");
+
+static wxString kDeviceMenuString = wxT("&Device");
+
+static const wxString gStdJavaApps[] = {
+    wxT(""),
+    wxT("com.android.testharness.TestList"),
+    wxT("com.android.apps.contacts.ContactsList"),
+    wxT("mikeapp")
+};
+
+
+BEGIN_EVENT_TABLE(MainFrame::MainFrame, wxFrame)
+    EVT_CLOSE(MainFrame::OnClose)
+    EVT_TIMER(kHalfSecondTimerId, MainFrame::OnTimer)
+    //EVT_IDLE(MainFrame::OnIdle)
+  
+    EVT_ACTIVATE(MainFrame::OnActivate)
+    EVT_ACTIVATE_APP(MainFrame::OnActivate)
+    EVT_COMBOBOX(IDC_MODE_SELECT, MainFrame::OnComboBox)
+    EVT_COMBOBOX(IDC_JAVA_VM, MainFrame::OnComboBox)
+    EVT_CHECKBOX(IDC_USE_GDB, MainFrame::OnCheckBox)
+    EVT_CHECKBOX(IDC_USE_VALGRIND, MainFrame::OnCheckBox)
+    EVT_CHECKBOX(IDC_CHECK_JNI, MainFrame::OnCheckBox)
+    EVT_CHECKBOX(IDC_OVERLAY_ONION_SKIN, MainFrame::OnCheckBox)
+    EVT_TEXT(IDC_JAVA_APP_NAME, MainFrame::OnText)
+    EVT_TEXT_ENTER(IDC_ONION_SKIN_FILE_NAME, MainFrame::OnTextEnter)
+    EVT_BUTTON(IDC_ONION_SKIN_BUTTON, MainFrame::OnButton)
+    EVT_COMMAND_SCROLL(IDC_ONION_SKIN_ALPHA_VAL, MainFrame::OnSliderChange)
+
+    EVT_MENU(IDM_FILE_PREFERENCES, MainFrame::OnFilePreferences)
+    EVT_MENU(IDM_FILE_EXIT, MainFrame::OnFileExit)
+    EVT_MENU(IDM_RUNTIME_START, MainFrame::OnSimStart)
+    EVT_UPDATE_UI(IDM_RUNTIME_START, MainFrame::OnUpdateSimStart)
+    EVT_MENU(IDM_RUNTIME_STOP, MainFrame::OnSimStop)
+    EVT_UPDATE_UI(IDM_RUNTIME_STOP, MainFrame::OnUpdateSimStop)
+    EVT_MENU(IDM_RUNTIME_RESTART, MainFrame::OnSimRestart)
+    EVT_UPDATE_UI(IDM_RUNTIME_RESTART, MainFrame::OnUpdateSimRestart)
+    EVT_MENU(IDM_RUNTIME_KILL, MainFrame::OnSimKill)
+    EVT_UPDATE_UI(IDM_RUNTIME_KILL, MainFrame::OnUpdateSimKill)
+    EVT_MENU_RANGE(IDM_DEVICE_SEL0, IDM_DEVICE_SELN,
+        MainFrame::OnDeviceSelected)
+    EVT_MENU(IDM_DEVICE_RESCAN, MainFrame::OnDeviceRescan)
+    EVT_UPDATE_UI(IDM_DEBUG_SHOW_LOG, MainFrame::OnUpdateDebugShowLog)
+    EVT_MENU(IDM_DEBUG_SHOW_LOG, MainFrame::OnDebugShowLog)
+    EVT_MENU(IDM_HELP_CONTENTS, MainFrame::OnHelpContents)
+    EVT_MENU(IDM_HELP_ABOUT, MainFrame::OnHelpAbout)
+
+    EVT_USER_EVENT(MainFrame::OnUserEvent)
+END_EVENT_TABLE()
+
+
+/*
+ * Main window constructor.
+ *
+ * Creates menus and status bar.
+ */
+MainFrame::MainFrame(const wxString& title, const wxPoint& pos,
+    const wxSize& size, long style)
+    : wxFrame((wxFrame *)NULL, -1, title, pos, size, style),
+      mSimRunning(false),
+      mRestartRequested(false),
+      mpPhoneWindow(NULL),
+      mPhoneWindowPosn(wxDefaultPosition),
+      mTimer(this, kHalfSecondTimerId)
+{
+    mSimAssetPath = ((MyApp*)wxTheApp)->GetSimAssetPath();
+    mSimAssetPath += wxT("/simulator/default/default");
+
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+    int val;
+
+    val = mPhoneWindowPosn.x;
+    pPrefs->GetInt("window-device-x", &val);
+    mPhoneWindowPosn.x = val;
+    val = mPhoneWindowPosn.y;
+    pPrefs->GetInt("window-device-y", &val);
+    mPhoneWindowPosn.y = val;
+
+    /*
+     * Create main menu.
+     */
+    ConstructMenu();
+
+    /*
+     * Create the status bar.
+     */
+    int widths[2] = { -1, 50 };
+    CreateStatusBar(2, wxFULL_REPAINT_ON_RESIZE);   // no wxST_SIZEGRIP
+    SetStatusWidths(2, widths);
+    SetStatusText(wxT("Ready"));
+    SetStatusText(kStatusNotRunning, 1);
+
+    /*
+     * Create main window controls.
+     */
+    ConstructControls();
+
+#if 0
+    /*
+     * Use the standard window color for the main frame (which usually
+     * has a darker color).  This has a dramatic effect under Windows.
+     */
+    wxColour color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
+    SetOwnBackgroundColour(color);
+#endif
+
+    /*
+     * Create the log window.
+     */
+    wxRect layout = LogWindow::GetPrefWindowRect();
+    mpLogWindow = new LogWindow(this);
+    mpLogWindow->Move(layout.GetTopLeft());
+    mpLogWindow->SetSize(layout.GetSize());
+    bool showLogWindow = true;
+    pPrefs->GetBool("window-log-show", &showLogWindow);
+    if (showLogWindow)
+        mpLogWindow->Show();
+
+    /*
+     * Set up a frequent timer.  We use this to keep our "run/idle"
+     * display up to date.  (Ideally this will go away.)
+     */
+    mTimer.Start(400);      // arg is delay in ms
+
+    /*
+     * Handle auto-power-on by sending ourselves an event.  That way it
+     * gets handled after window initialization finishes.
+     */
+    bool autoPowerOn = false;
+    pPrefs->GetBool("auto-power-on", &autoPowerOn);
+    if (autoPowerOn) {
+        printf("Sim: Auto power-up\n");
+        wxCommandEvent startEvent(wxEVT_COMMAND_MENU_SELECTED, IDM_RUNTIME_START);
+        this->AddPendingEvent(startEvent);
+    }
+
+    /*
+     * wxThread wants these to be on the heap -- it will call delete on the
+     * object when the thread exits.
+     */
+    mExternalRuntimeThread = new ExternalRuntime();
+    mExternalRuntimeThread->StartThread();
+    mPropertyServerThread = new PropertyServer();
+    mPropertyServerThread->StartThread();
+}
+
+/*
+ * Construct the main menu.  Called from the constructor.
+ */
+void MainFrame::ConstructMenu(void)
+{
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+
+    /*
+     * Scan for available phones.
+     */
+    PhoneCollection* pCollection = PhoneCollection::GetInstance();
+    pCollection->ScanForPhones(mSimAssetPath.ToAscii());
+
+    /*
+     * Create the "File" menu.
+     */
+    wxMenu* menuFile = new wxMenu;
+
+    menuFile->Append(IDM_FILE_PREFERENCES, wxT("&Preferences..."),
+        wxT("Edit simulator preferences"));
+    menuFile->AppendSeparator();
+    menuFile->Append(IDM_FILE_EXIT, wxT("E&xit\tCtrl-Q"),
+        wxT("Stop simulator and exit"));
+
+    /*
+     * Create the "Runtime" menu.
+     */
+    wxMenu* menuRuntime = new wxMenu;
+    menuRuntime->Append(IDM_RUNTIME_START, wxT("&Power On\tCtrl-G"),
+        wxT("Start the device"));
+//    menuRuntime->Append(IDM_RUNTIME_STOP, wxT("Power &Off"),
+//        wxT("Stop the device"));
+    menuRuntime->AppendSeparator();
+//    menuRuntime->Append(IDM_RUNTIME_RESTART, wxT("&Restart"),
+//        wxT("Restart the device"));
+    menuRuntime->Append(IDM_RUNTIME_KILL, wxT("&Kill\tCtrl-K"),
+        wxT("Kill the runtime processes"));
+
+    /*
+     * Create "Device" menu.
+     */
+    wxString defaultDevice = wxT("Sooner");
+    pPrefs->GetString("default-device", /*ref*/ defaultDevice);
+    wxMenu* menuDevice = CreateDeviceMenu(defaultDevice.ToAscii());
+
+    /*
+     * Create "Debug" menu.
+     */
+    wxMenu* menuDebug = new wxMenu;
+    menuDebug->AppendCheckItem(IDM_DEBUG_SHOW_LOG, wxT("View &Log Output"),
+        wxT("View log output window"));
+
+    /*
+     * Create the "Help" menu.
+     */
+    wxMenu* menuHelp = new wxMenu;
+    menuHelp->Append(IDM_HELP_CONTENTS, wxT("&Contents...\tF1"),
+        wxT("Simulator help"));
+    menuHelp->AppendSeparator();
+    menuHelp->Append(IDM_HELP_ABOUT, wxT("&About..."),
+        wxT("See the fabulous 'about' box"));
+
+    /*
+     * Create the menu bar.
+     */
+    wxMenuBar *menuBar = new wxMenuBar;
+    menuBar->Append(menuFile, wxT("&File"));
+    menuBar->Append(menuDevice, kDeviceMenuString);
+    menuBar->Append(menuRuntime, wxT("&Runtime"));
+    menuBar->Append(menuDebug, wxT("&Debug"));
+    menuBar->Append(menuHelp, wxT("&Help"));
+
+    SetMenuBar(menuBar);
+
+}
+
+/*
+ * Construct the "device" menu from our phone collection.
+ */
+wxMenu* MainFrame::CreateDeviceMenu(const char* defaultItemName)
+{
+    wxMenu* menuDevice = new wxMenu;
+    PhoneCollection* pCollection = PhoneCollection::GetInstance();
+    int defaultModel = 0;
+
+    for (int i = 0; i < pCollection->GetPhoneCount(); i++) {
+        PhoneData* pPhoneData = pCollection->GetPhoneData(i);
+        assert(pPhoneData != NULL);
+
+        menuDevice->AppendRadioItem(IDM_DEVICE_SEL0 + i,
+            wxString::FromAscii(pPhoneData->GetTitle()));
+
+        // use this one as default if the string matches
+        if (strcasecmp(pPhoneData->GetName(), defaultItemName) == 0)
+            defaultModel = i;
+    }
+
+    menuDevice->Check(IDM_DEVICE_SEL0 + defaultModel, true);
+
+    menuDevice->AppendSeparator();
+    menuDevice->Append(IDM_DEVICE_RESCAN, wxT("Re-scan"));
+
+    return menuDevice;
+}
+
+/*
+ * Create some controls in the main window.
+ *
+ * The main frame doesn't use the normal background color that you find
+ * in dialog windows, so we create a "panel" and put all the controls
+ * on that.
+ */
+void MainFrame::ConstructControls(void)
+{
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+    wxPanel* base = new wxPanel(this, wxID_ANY);
+    wxBoxSizer* masterSizer = new wxBoxSizer(wxVERTICAL);
+    wxBoxSizer* tmpSizer;
+    wxStaticBoxSizer* displayOptSizer;
+    wxStaticBoxSizer* runtimeOptSizer;
+    wxStaticBoxSizer* onionSkinOptSizer;
+    wxComboBox* pModeSelection;
+    wxCheckBox* pUseGDB;
+    wxCheckBox* pUseValgrind;
+    wxCheckBox* pCheckJni;
+    wxCheckBox* pOverlayOnionSkin;
+    
+    displayOptSizer = new wxStaticBoxSizer(wxHORIZONTAL, base,
+        wxT("Configuration"));
+    runtimeOptSizer = new wxStaticBoxSizer(wxVERTICAL, base,
+        wxT("Runtime Options"));
+    onionSkinOptSizer = new wxStaticBoxSizer(wxVERTICAL, base,
+        wxT("Onion Skin Options"));
+
+    /*
+     * Set up the configuration sizer (nee "display options").
+     */
+    tmpSizer = new wxBoxSizer(wxHORIZONTAL);
+    displayOptSizer->Add(tmpSizer);
+    tmpSizer->Add(
+            new wxStaticText(base, wxID_ANY, wxT("Device mode:"),
+            wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT), 0, wxALIGN_CENTER_VERTICAL);
+    pModeSelection = new wxComboBox(base, IDC_MODE_SELECT, wxT(""),
+            wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY);
+    tmpSizer->AddSpacer(kInterSpacing);
+    tmpSizer->Add(pModeSelection);
+
+    displayOptSizer->AddSpacer(kInterSpacing);
+
+    /*
+     * Configure the runtime options sizer.
+     */
+    wxComboBox* pJavaAppName;
+    tmpSizer = new wxBoxSizer(wxHORIZONTAL);
+    pUseGDB = new wxCheckBox(base, IDC_USE_GDB, wxT("Use &debugger"));
+    tmpSizer->Add(pUseGDB);
+    tmpSizer->AddSpacer(kInterSpacing);
+    pUseValgrind = new wxCheckBox(base, IDC_USE_VALGRIND, wxT("Use &valgrind"));
+    tmpSizer->Add(pUseValgrind);
+    tmpSizer->AddSpacer(kInterSpacing);
+    pCheckJni = new wxCheckBox(base, IDC_CHECK_JNI, wxT("Check &JNI"));
+    tmpSizer->Add(pCheckJni);
+
+    pJavaAppName = new wxComboBox(base, IDC_JAVA_APP_NAME, wxT(""),
+        wxDefaultPosition, wxSize(320, -1), NELEM(gStdJavaApps), gStdJavaApps,
+        wxCB_DROPDOWN);
+    wxBoxSizer* javaAppSizer = new wxBoxSizer(wxHORIZONTAL);
+    javaAppSizer->Add(
+            new wxStaticText(base, wxID_ANY,
+                wxT("Java app:"),
+                wxDefaultPosition, wxDefaultSize,
+                wxALIGN_LEFT),
+            0, wxALIGN_CENTER_VERTICAL);
+    javaAppSizer->AddSpacer(kInterSpacing);
+    javaAppSizer->Add(pJavaAppName);
+
+    runtimeOptSizer->Add(tmpSizer);
+
+    runtimeOptSizer->AddSpacer(kInterSpacing);
+    runtimeOptSizer->Add(javaAppSizer);
+    runtimeOptSizer->AddSpacer(kInterSpacing);
+
+    wxString tmpStr;
+    SetCheckFromPref(pUseGDB, "debug", false);
+    SetCheckFromPref(pUseValgrind, "valgrind", false);
+    SetCheckFromPref(pCheckJni, "check-jni", false);
+    if (pPrefs->GetString("java-app-name", /*ref*/ tmpStr))
+        pJavaAppName->SetValue(tmpStr);
+
+    /*
+     * Configure the onion skin options sizer.
+     */
+    wxTextCtrl* pOnionSkinFileNameText;
+    wxButton* pOnionSkinFileButton;
+    wxSlider* pOnionSkinAlphaSlider;
+    tmpSizer = new wxBoxSizer(wxHORIZONTAL);
+    pOverlayOnionSkin = new wxCheckBox(base, 
+        IDC_OVERLAY_ONION_SKIN, wxT("Overlay &onion skin"));
+    tmpSizer->Add(pOverlayOnionSkin);
+
+    pOnionSkinFileNameText = new wxTextCtrl(base, 
+        IDC_ONION_SKIN_FILE_NAME, wxT(""),
+        wxDefaultPosition, wxSize(250, -1),
+        wxTE_PROCESS_ENTER);
+    pOnionSkinFileButton = new wxButton(base, IDC_ONION_SKIN_BUTTON,
+        wxT("Choose"));
+
+    wxBoxSizer* onionSkinFileNameSizer = new wxBoxSizer(wxHORIZONTAL);
+    onionSkinFileNameSizer->Add(
+        new wxStaticText(base, wxID_ANY,
+            wxT("Filename:"),
+            wxDefaultPosition, wxDefaultSize,
+            wxALIGN_LEFT),
+        0, wxALIGN_CENTER_VERTICAL);
+    onionSkinFileNameSizer->AddSpacer(kInterSpacing);
+    onionSkinFileNameSizer->Add(pOnionSkinFileNameText);
+    onionSkinFileNameSizer->Add(pOnionSkinFileButton);
+
+    wxBoxSizer * onionSkinAlphaSizer = new wxBoxSizer(wxHORIZONTAL);
+    int initialAlphaVal = 127;
+    pPrefs->GetInt("onion-skin-alpha-value", &initialAlphaVal);
+    pOnionSkinAlphaSlider = new wxSlider(base, IDC_ONION_SKIN_ALPHA_VAL,
+        initialAlphaVal, 0, 255, wxDefaultPosition, wxSize(150, 20));
+    onionSkinAlphaSizer->Add(
+        new wxStaticText(base, wxID_ANY,
+            wxT("Transparency:"),
+            wxDefaultPosition, wxDefaultSize,
+            wxALIGN_LEFT),
+        0, wxALIGN_CENTER_VERTICAL);
+    onionSkinAlphaSizer->AddSpacer(kInterSpacing);
+    onionSkinAlphaSizer->Add(pOnionSkinAlphaSlider, 1, wxCENTRE | wxALL, 5);
+
+    onionSkinOptSizer->Add(tmpSizer);
+    onionSkinOptSizer->AddSpacer(kInterSpacing);
+    onionSkinOptSizer->Add(onionSkinFileNameSizer);
+    onionSkinOptSizer->Add(onionSkinAlphaSizer);
+
+    wxString tmpStr2;
+    SetCheckFromPref(pOverlayOnionSkin, "overlay-onion-skin", false);
+    if (pPrefs->GetString("onion-skin-file-name", /*ref*/ tmpStr2))
+        pOnionSkinFileNameText->SetValue(tmpStr2);
+
+    /*
+     * Add the various components to the master sizer.
+     */
+    masterSizer->Add(displayOptSizer);
+    masterSizer->AddSpacer(kInterSpacing * 2);
+    masterSizer->Add(runtimeOptSizer);
+    masterSizer->AddSpacer(kInterSpacing * 2);
+    masterSizer->Add(onionSkinOptSizer);
+    //masterSizer->AddSpacer(kInterSpacing);
+
+    /*
+     * I don't see a way to guarantee that the window is wide enough to
+     * show the entire menu bar, so just throw some pixels at it.
+     */
+    wxBoxSizer* minWidthSizer = new wxBoxSizer(wxVERTICAL);
+    minWidthSizer->Add(300, kEdgeSpacing);       // forces minimum width
+    minWidthSizer->Add(masterSizer);
+    minWidthSizer->AddSpacer(kInterSpacing * 2);
+
+    /* move us a few pixels in from the left */
+    wxBoxSizer* indentSizer = new wxBoxSizer(wxHORIZONTAL);
+    indentSizer->AddSpacer(kEdgeSpacing);
+    indentSizer->Add(minWidthSizer);
+    indentSizer->AddSpacer(kEdgeSpacing);
+
+    base->SetSizer(indentSizer);
+
+    indentSizer->Fit(this);
+    indentSizer->SetSizeHints(this);
+}
+
+/*
+ * Set the value of a checkbox based on a value from the config file.
+ */
+void MainFrame::SetCheckFromPref(wxCheckBox* pControl, const char* prefStr,
+    bool defaultVal)
+{
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+    assert(pPrefs != NULL);
+
+    bool val = defaultVal;
+    pPrefs->GetBool(prefStr, &val);
+
+    pControl->SetValue(val);
+}
+
+/*
+ * Destructor.
+ */
+MainFrame::~MainFrame(void)
+{
+    PhoneCollection::DestroyInstance();
+
+    delete mExternalRuntimeThread;
+    delete mPropertyServerThread;
+
+    // don't touch mpModeSelection -- child of window
+}
+
+/*
+ * File->Quit or click on close box.
+ *
+ * If we want an "are you sure you want to quit" box, add it here.
+ */
+void MainFrame::OnClose(wxCloseEvent& event)
+{
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+
+/*
+    if (event.CanVeto())
+        printf("Closing (can veto)\n");
+    else
+        printf("Closing (mandatory)\n");
+*/
+
+    /*
+     * Generally speaking, Close() is not guaranteed to close the window.
+     * However, we want to use it here because (a) our windows are
+     * guaranteed to close, and (b) it provides our windows an opportunity
+     * to tell others that they are about to vanish.
+     */
+    if (mpPhoneWindow != NULL)
+        mpPhoneWindow->Close(true);
+
+    /* save position of main window */
+    wxPoint pos = GetPosition();
+    pPrefs->SetInt("window-main-x", pos.x);
+    pPrefs->SetInt("window-main-y", pos.y);
+
+    /* save default device selection */
+    int idx = GetSelectedDeviceIndex();
+    if (idx >= 0) {
+        PhoneCollection* pCollection = PhoneCollection::GetInstance();
+        PhoneData* pPhoneData = pCollection->GetPhoneData(idx);
+        pPrefs->SetString("default-device", pPhoneData->GetName());
+    }
+
+    if (mpLogWindow != NULL)
+        mpLogWindow->Close(true);
+    Destroy();
+}
+
+/*
+ * File->Preferences
+ */
+void MainFrame::OnFilePreferences(wxCommandEvent& WXUNUSED(event))
+{
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+    PrefsDialog dialog(this);
+    int result;
+
+    result = dialog.ShowModal();
+    if (result == wxID_OK) {
+        /*
+         * The dialog handles writing changes to Preferences, so all we
+         * need to deal with here are changes that have an immediate
+         * impact on us. (which is currently nothing)
+         */
+    }
+}
+
+/*
+ * File->Exit
+ */
+void MainFrame::OnFileExit(wxCommandEvent& WXUNUSED(event))
+{
+    Close(FALSE);       // false means "allow veto"
+}
+
+/*
+ * Decide whether Simulator->Start should be enabled.
+ */
+void MainFrame::OnUpdateSimStart(wxUpdateUIEvent& event)
+{
+    if (IsRuntimeRunning())
+        event.Enable(FALSE);
+    else
+        event.Enable(TRUE);
+}
+
+/*
+ * Simulator->Start
+ */
+void MainFrame::OnSimStart(wxCommandEvent& WXUNUSED(event))
+{
+    // keyboard equivalents can still get here even if menu item disabled
+    if (IsRuntimeRunning())
+        return;
+
+    int id = GetSelectedDeviceIndex();
+    if (id < 0) {
+        fprintf(stderr, "Sim: could not identify currently selected device\n");
+        return;
+    }
+
+#if 0
+    static int foo = 0;
+    foo++;
+    if (foo == 2) {
+        Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+
+        pPrefs->SetBool("debug", true);
+    }
+#endif
+
+    SetupPhoneUI(id, NULL);
+    if (mpPhoneWindow != NULL)
+        mpPhoneWindow->GetDeviceManager()->StartRuntime();
+}
+
+/*
+ * Decide whether Simulator->Stop should be enabled.
+ */
+void MainFrame::OnUpdateSimStop(wxUpdateUIEvent& event)
+{
+    if (IsRuntimeRunning())
+        event.Enable(TRUE);
+    else
+        event.Enable(FALSE);
+}
+
+/*
+ * Simulator->Stop
+ */
+void MainFrame::OnSimStop(wxCommandEvent& WXUNUSED(event))
+{
+    if (!IsRuntimeRunning())
+        return;
+    assert(mpPhoneWindow != NULL);
+    mpPhoneWindow->GetDeviceManager()->StopRuntime();
+}
+
+/*
+ * Decide whether Simulator->Restart should be enabled.
+ */
+void MainFrame::OnUpdateSimRestart(wxUpdateUIEvent& event)
+{
+    if (IsRuntimeRunning())
+        event.Enable(TRUE);
+    else
+        event.Enable(FALSE);
+}
+
+/*
+ * Simulator->Restart - stop then start the device runtime.
+ */
+void MainFrame::OnSimRestart(wxCommandEvent& WXUNUSED(event))
+{
+    if (!IsRuntimeRunning())
+        return;
+
+    printf("Restart requested\n");
+    mpPhoneWindow->GetDeviceManager()->StopRuntime();
+
+    mRestartRequested = true;
+}
+
+/*
+ * Decide whether Simulator->Kill should be enabled.
+ */
+void MainFrame::OnUpdateSimKill(wxUpdateUIEvent& event)
+{
+    if (IsRuntimeKillable())
+        event.Enable(TRUE);
+    else
+        event.Enable(FALSE);
+}
+
+/*
+ * Simulator->Kill
+ */
+void MainFrame::OnSimKill(wxCommandEvent& WXUNUSED(event))
+{
+    if (!IsRuntimeKillable())
+        return;
+    assert(mpPhoneWindow != NULL);
+    mpPhoneWindow->GetDeviceManager()->KillRuntime();
+}
+
+
+/*
+ * Device->[select]
+ */
+void MainFrame::OnDeviceSelected(wxCommandEvent& event)
+{
+    wxBusyCursor busyc;
+    int id = event.GetId() - IDM_DEVICE_SEL0;
+
+    SetupPhoneUI(id, NULL);
+}
+
+/*
+ * Device->Rescan
+ */
+void MainFrame::OnDeviceRescan(wxCommandEvent& event)
+{
+    wxBusyCursor busyc;
+    wxMenuBar* pMenuBar;
+    PhoneCollection* pCollection;
+    wxMenu* pOldMenu;
+    wxMenu* pNewMenu;
+    const char* curDevName = NULL;
+    int idx;
+    
+    /* figure out the current device name */
+    pCollection = PhoneCollection::GetInstance();
+    idx = GetSelectedDeviceIndex();
+    if (idx >= 0) {
+        PhoneData* pPhoneData;
+
+        pPhoneData = pCollection->GetPhoneData(idx);
+        curDevName = pPhoneData->GetName();
+        printf("--- device name is '%s'\n", (const char*) curDevName);
+    }
+
+    /* reconstruct device menu with new data */
+#ifdef BEFORE_ASSET
+    pCollection->ScanForPhones(mSimAssetPath);
+#else
+    pCollection->ScanForPhones(NULL);
+#endif
+
+    pMenuBar = GetMenuBar();
+    idx = pMenuBar->FindMenu(kDeviceMenuString);
+    if (idx == wxNOT_FOUND) {
+        fprintf(stderr, "Sim: couldn't find %s menu\n", (const char*) kDeviceMenuString.ToAscii());
+        return;
+    }
+
+    pNewMenu = CreateDeviceMenu(curDevName);
+
+    pOldMenu = pMenuBar->Replace(idx, pNewMenu, kDeviceMenuString);
+    delete pOldMenu;
+
+    /* tell the PhoneWindow about it; may cause runtime to exit */
+    if (mpPhoneWindow != NULL)
+        mpPhoneWindow->DevicesRescanned();
+}
+
+/*
+ * Set checkbox on menu item.
+ */
+void MainFrame::OnUpdateDebugShowLog(wxUpdateUIEvent& event)
+{
+    if (mpLogWindow == NULL) {
+        event.Enable(false);
+    } else {
+        event.Enable(true);
+        event.Check(mpLogWindow->IsShown());
+    }
+}
+
+/*
+ * Debug->ShowLog toggle.
+ */
+void MainFrame::OnDebugShowLog(wxCommandEvent& WXUNUSED(event))
+{
+    mpLogWindow->Show(!mpLogWindow->IsShown());
+}
+
+/*
+ * Help->Contents
+ */
+void MainFrame::OnHelpContents(wxCommandEvent& WXUNUSED(event))
+{
+    ((MyApp*)wxTheApp)->GetHelpController()->DisplayContents();
+}
+
+/*
+ * Help->About
+ */
+void MainFrame::OnHelpAbout(wxCommandEvent& WXUNUSED(event))
+{
+    wxMessageBox(wxT("Android Simulator v0.1\n"
+                     "Copyright 2006 The Android Open Source Project"),
+        wxT("About..."), wxOK | wxICON_INFORMATION, this);
+}
+
+/*
+ * Sent from phonewindow or when activated
+ */
+void MainFrame::OnActivate(wxActivateEvent& event)
+{
+#if 0
+    if (event.GetActive())
+    {
+        if (mpPhoneWindow != NULL &&
+            mpPhoneWindow->GetDeviceManager()->RefreshRuntime())
+        {
+            wxString msg;
+            int sel;
+
+            msg = wxT("Newer runtime executable found. Would you like to reload the device?");
+
+            sel = wxMessageBox(msg, wxT("Android Safety Patrol"),
+                wxYES | wxNO | wxICON_QUESTION, mpPhoneWindow);
+            //printf("BUTTON was %d (yes=%d)\n", sel, wxYES);
+            if (sel == wxYES)
+            {
+                mpPhoneWindow->GetDeviceManager()->StopRuntime();
+                mpPhoneWindow->Close();
+                mpPhoneWindow = NULL;
+                mRestartRequested = true;
+            }
+            else
+            {
+                mpPhoneWindow->GetDeviceManager()->UserCancelledRefresh();
+            }
+        }
+    }
+#endif
+
+    // let wxWidgets do whatever it needs to do
+    event.Skip();
+}
+            
+
+/*
+ * Device mode selection box.
+ */
+void MainFrame::OnComboBox(wxCommandEvent& event)
+{
+    const char* pref;
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+    assert(pPrefs != NULL);
+
+    if (IDC_MODE_SELECT == event.GetId())
+    {
+        int id = GetSelectedDeviceIndex();
+        if (id < 0)
+            return;
+        //printf("--- mode selected: '%s'\n", (const char*) event.GetString().ToAscii());
+
+        /*
+         * Call the phone window's setup function.  Don't call our SetupPhoneUI
+         * function from here -- updating the combo box from a combo box callback
+         * could cause problems.
+         */
+        if (mpPhoneWindow != NULL) {
+            mpPhoneWindow->SetCurrentMode(event.GetString());
+            mpPhoneWindow->Setup(id);
+        }
+    } else if (event.GetId() == IDC_JAVA_VM) {
+        wxComboBox* pBox = (wxComboBox*) FindWindow(IDC_JAVA_VM);
+        pPrefs->SetString("java-vm", pBox->GetValue().ToAscii());
+    }
+}
+
+/*
+ * One of our option checkboxes has been changed.
+ *
+ * We update the prefs database so that the settings are retained when
+ * the simulator is next used.
+ */
+void MainFrame::OnCheckBox(wxCommandEvent& event)
+{
+    const char* pref;
+
+    switch (event.GetId()) {
+    case IDC_USE_GDB:               pref = "debug";                 break;
+    case IDC_USE_VALGRIND:          pref = "valgrind";              break;
+    case IDC_CHECK_JNI:             pref = "check-jni";             break;
+    case IDC_OVERLAY_ONION_SKIN:    pref = "overlay-onion-skin";    break; 
+    default:
+        printf("Sim: unrecognized checkbox %d in OnCheckBox\n", event.GetId());
+        return;
+    }
+
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+    assert(pPrefs != NULL);
+
+    pPrefs->SetBool(pref, (bool) event.GetInt());
+    //printf("--- set pref '%s' to %d\n", pref, (bool) event.GetInt());
+    if (event.GetId() == IDC_OVERLAY_ONION_SKIN) {
+        BroadcastOnionSkinUpdate();
+    }
+    if (event.GetId() == IDC_CHECK_JNI) {
+        const char* val = "0";
+        if ((bool) event.GetInt())
+            val = "1";
+        mPropertyServerThread->SetProperty(PropertyServer::kPropCheckJni, val);
+
+    }
+}
+
+void MainFrame::BroadcastOnionSkinUpdate() {
+    if (mpPhoneWindow != NULL) {
+        // broadcast a user event indicating an onion skin update
+        UserEvent uev(0, (void*) -1);
+        mpPhoneWindow->GetDeviceManager()->BroadcastEvent(uev);
+    }
+}
+
+/*
+ * A text control on the main page is being updated.
+ *
+ * The current implementation updates the preferences database on every
+ * change, which is a bit silly but is easy to do.
+ */
+void MainFrame::OnText(wxCommandEvent& event)
+{
+    const char* pref;
+
+    switch (event.GetId()) {
+    case IDC_JAVA_APP_NAME:     pref = "java-app-name"; break;
+    default:
+        printf("Sim: unrecognized textctrl %d in OnText\n", event.GetId());
+        return;
+    }
+
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+    assert(pPrefs != NULL);
+
+    // event.GetString() does not work on Mac -- always blank
+    //pPrefs->SetString(pref, event.GetString());
+    assert(event.GetId() == IDC_JAVA_APP_NAME); // fix if we add more
+    wxComboBox* pBox;
+    pBox = (wxComboBox*) FindWindow(IDC_JAVA_APP_NAME);
+    pPrefs->SetString(pref, pBox->GetValue().ToAscii());
+    //printf("--- set pref '%s' to '%s'\n", pref,(const char*)pBox->GetValue());
+}
+
+/*
+ * A user pressed enter in a text control on the main page.
+ *
+ * The current implementation updates the preferences database on every
+ * change, which is a bit silly but is easy to do.
+ */
+void MainFrame::OnTextEnter(wxCommandEvent& event)
+{
+    const char* pref;
+
+    switch (event.GetId()) {
+    case IDC_ONION_SKIN_FILE_NAME:
+        pref = "onion-skin-file-name";
+        break;
+    default:
+        printf("Sim: unrecognized textctrl %d in OnTextEnter\n", event.GetId());
+        return;
+    }
+
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+    assert(pPrefs != NULL);
+
+    assert(event.GetId() == IDC_ONION_SKIN_FILE_NAME); // fix if we add more
+    wxTextCtrl* pTextCtrl;
+    pTextCtrl = (wxTextCtrl*) FindWindow(IDC_ONION_SKIN_FILE_NAME);
+    wxString onionSkinFileNameWxString = pTextCtrl->GetValue();
+    char* onionSkinFileName = "";
+    if (onionSkinFileNameWxString.Len() > 0) {
+        onionSkinFileName = android::strdupNew(onionSkinFileNameWxString.ToAscii());
+    }
+    pPrefs->SetString(pref, onionSkinFileName);
+    BroadcastOnionSkinUpdate();
+}
+
+/*
+ * A user pressed a button on the main page
+ * 
+ */
+ void MainFrame::OnButton(wxCommandEvent& event)
+ {
+    wxWindow* base;
+    wxFileDialog* pOnionSkinFileChooser;
+    int retVal;
+    switch (event.GetId()) {
+    case IDC_ONION_SKIN_BUTTON:
+        base = FindWindow(IDC_ONION_SKIN_BUTTON)->GetParent();
+        pOnionSkinFileChooser = new wxFileDialog(base, 
+            wxT("Choose the onion skin image file."), 
+            wxT(""), wxT(""), wxT("*.*"),
+            wxOPEN | wxFILE_MUST_EXIST);
+        retVal = pOnionSkinFileChooser->ShowModal();
+        if (retVal == pOnionSkinFileChooser->GetAffirmativeId()) {
+            Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+            assert(pPrefs != NULL);
+            wxString fileNameWxString = pOnionSkinFileChooser->GetPath();
+            const char* fileName = android::strdupNew(fileNameWxString.ToAscii());
+            wxTextCtrl* fileTextCtrl = (wxTextCtrl*) FindWindow(IDC_ONION_SKIN_FILE_NAME);
+            fileTextCtrl->SetValue(fileNameWxString);
+            pPrefs->SetString("onion-skin-file-name", fileName);
+            BroadcastOnionSkinUpdate();
+        }
+        break;
+    default:
+        printf("Sim: unrecognized button %d in OnButton\n", event.GetId());
+        return;
+    }     
+ }
+ 
+ /*
+  * The user moved a slider on the main page
+  */
+ void MainFrame::OnSliderChange(wxScrollEvent& event)
+ {
+    wxSlider* pSlider;
+    Preferences* pPrefs;
+    switch (event.GetId()) {
+    case IDC_ONION_SKIN_ALPHA_VAL:
+        pSlider = (wxSlider*) FindWindow(IDC_ONION_SKIN_ALPHA_VAL);
+        pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+        assert(pPrefs != NULL);
+        pPrefs->SetInt("onion-skin-alpha-value", pSlider->GetValue());
+        BroadcastOnionSkinUpdate();
+        break;
+    default:
+        printf("Sim: unrecognized scroller or slider %d in OnSliderChange\n", event.GetId());
+        return;
+    }     
+ }
+
+#if 0
+/*
+ * Idle processing.  Under wxWidgets this only called once after UI
+ * activity unless you call event.RequestMore().
+ */
+void MainFrame::OnIdle(wxIdleEvent& event)
+{
+    event.Skip();       // let base class handler do stuff
+}
+#endif
+
+/*
+ * Handle the timer.
+ *
+ * This is being called in the main thread, so multithreading with the
+ * rest of MainFrame isn't a concern here.
+ */
+void MainFrame::OnTimer(wxTimerEvent& event)
+{
+    bool status;
+
+    /*
+     * Check to see if the runtime died without telling us.  This can only
+     * happen if we forcibly kill our thread.  We shouldn't really be
+     * doing that anymore, but keep this in for now just in case.
+     */
+    status = IsRuntimeRunning();
+
+    if (mSimRunning != status) {
+        if (!status) {
+            printf("Sim: fixed mSimRunning=%d actual=%d\n",
+                mSimRunning, status);
+            mSimRunning = status;
+
+            if (!status)
+                HandleRuntimeStop();
+        } else {
+            /*
+             * This was happening when we were shutting down but the
+             * device management thread hadn't completely gone away.  The
+             * simple IsRunning test passes, so we get a false positive.
+             * Ignore it.
+             */
+        }
+    }
+
+    if (gWantToKill) {
+        if (IsRuntimeRunning()) {
+            printf("Sim: handling kill request\n");
+            mpPhoneWindow->GetDeviceManager()->KillRuntime();
+        }
+        gWantToKill = false;
+
+        /* see if Ctrl-C should kill us too */
+        Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+        bool die = false;
+
+        pPrefs->GetBool("trap-sigint-suicide", &die);
+        if (die) {
+            printf("Sim: goodbye cruel world!\n");
+            exit(0);
+        }
+    }
+}
+
+/*
+ * Determine whether or not the simulator is running.
+ */
+bool MainFrame::IsRuntimeRunning(void)
+{
+    bool result;
+
+    if (mpPhoneWindow == NULL)
+        result = false;
+    else if (!mpPhoneWindow->IsReady())
+        result = false;
+    else
+        result = mpPhoneWindow->GetDeviceManager()->IsRunning();
+
+    return result;
+}
+
+/*
+ * Determine whether or not the runtime can be killed.
+ */
+bool MainFrame::IsRuntimeKillable(void)
+{
+    bool result;
+
+    result = IsRuntimeRunning();
+    if (result)
+        result = mpPhoneWindow->GetDeviceManager()->IsKillable();
+
+    return result;
+}
+
+/*
+ * Determine whether two devices are sufficiently compatible.
+ */
+bool MainFrame::CompatibleDevices(PhoneData* pData1, PhoneData* pData2)
+{
+    int displayCount;
+
+    displayCount = pData1->GetNumDisplays();
+    if (pData2->GetNumDisplays() != displayCount)
+        return false;
+
+    for (int i = 0; i < displayCount; i++) {
+        PhoneDisplay* pDisplay1 = pData1->GetPhoneDisplay(i);
+        PhoneDisplay* pDisplay2 = pData2->GetPhoneDisplay(i);
+
+        if (!PhoneDisplay::IsCompatible(pDisplay1, pDisplay2))
+            return false;
+    }
+
+    return true;
+}
+
+/*
+ * (Re-)arrange the UI for the currently selected phone model.
+ *
+ * If the simulator is running, and the set of displays for the current
+ * device are incompatible with the new device, we need to restart the
+ * runtime.  We need to ask for permission first though.
+ */
+void MainFrame::SetupPhoneUI(int idx, const char* defaultMode)
+{
+    PhoneCollection* pCollection;
+    PhoneData* pPhoneData;
+    wxString* choices = NULL;
+    int numChoices = 0;
+    int numKeyboards = 0;
+    bool haveDefaultMode = false;
+    wxCharBuffer currentMode;
+    int i;
+
+    pCollection = PhoneCollection::GetInstance();
+    pPhoneData = pCollection->GetPhoneData(idx);
+    if (pPhoneData == NULL) {
+        fprintf(stderr, "ERROR: device index %d not valid\n", idx);
+        goto bail;
+    }
+
+    /*
+     * We have a window up.  If the displays aren't compatible, we'll
+     * need to recreate it.
+     */
+    if (mpPhoneWindow != NULL) {
+        PhoneData* pCurData = mpPhoneWindow->GetPhoneData();
+
+        if (!CompatibleDevices(pCurData, pPhoneData)) {
+            /*
+             * We need to trash the window.  This will also kill the
+             * runtime.  If it's running, ask permission.
+             */
+            if (IsRuntimeRunning()) {
+                wxString msg;
+                int sel;
+
+                msg =  wxT("Switching to the new device requires restarting the");
+                msg += wxT(" runtime.  Continue?");
+
+                sel = wxMessageBox(msg, wxT("Android Safety Patrol"),
+                    wxOK | wxCANCEL | wxICON_QUESTION, this);
+                printf("BUTTON was %d (ok=%d)\n", sel, wxOK);
+                if (sel == wxCANCEL)
+                    goto bail;
+
+                /* shut it down (politely), ask for an eventual restart */
+                mpPhoneWindow->GetDeviceManager()->StopRuntime();
+                mpPhoneWindow->Close();
+                mpPhoneWindow = NULL;
+                mRestartRequested = true;
+                goto bail;
+            } else {
+                /* not running, just trash the window and continue */
+                mpPhoneWindow->Close();
+                mpPhoneWindow = NULL;
+            }
+        }
+    }
+
+    /*
+     * Figure out the set of available modes.
+     */
+
+    numChoices = pPhoneData->GetNumModes();
+    if (numChoices > 0) {
+        choices = new wxString[numChoices];
+        for (i = 0; i < numChoices; i++) {
+            PhoneMode* pPhoneMode;
+            pPhoneMode = pPhoneData->GetPhoneMode(i);
+            choices[i] = wxString::FromAscii(pPhoneMode->GetName());
+            if (defaultMode != NULL &&
+                strcmp(defaultMode, pPhoneMode->GetName()) == 0)
+            {
+                haveDefaultMode = true;
+            }
+        }
+    }
+
+    if (choices == NULL) {
+        /* had a failure earlier; configure UI with default stuff */
+        choices = new wxString[1];
+        choices[0] = wxT("(none)");
+    }
+
+    if (!haveDefaultMode) {
+        /*
+         * Default mode wasn't found.  If we specify it as the default
+         * in the wxComboBox create call it shows up in the combo box
+         * under Linux, even if it doesn't exist in the list.  So, we
+         * make sure that it doesn't get used if we can't find it.
+         */
+        if (defaultMode != NULL) {
+            printf("Sim: HEY: default mode '%s' not found in list\n",
+                defaultMode);
+        }
+        currentMode = choices[0].ToAscii();
+    } else {
+        currentMode = defaultMode;
+    }
+
+
+    /*
+     * Create the window if necessary.
+     */
+    if (mpPhoneWindow == NULL) {
+        // create, setup, and then show window
+        mpPhoneWindow = new PhoneWindow(this, mPhoneWindowPosn);
+        mpPhoneWindow->SetCurrentMode((const char*)currentMode);
+        if (!mpPhoneWindow->Setup(idx)) {
+            delete mpPhoneWindow;
+            mpPhoneWindow = NULL;
+        }
+        if (mpPhoneWindow != NULL) {
+            mpPhoneWindow->Show();
+            //mpPhoneWindow->CheckPlacement();
+        }
+    } else {
+        // just set up for new device
+        mpPhoneWindow->SetCurrentMode((const char*)currentMode);
+        if (!mpPhoneWindow->Setup(idx)) {
+            // it's in an uncertain state, blow it away
+            delete mpPhoneWindow;
+            mpPhoneWindow = NULL;
+        }
+    }
+
+    /*
+     * Reconfigure mode selection box.
+     */
+    wxComboBox* pModeSelection;
+    pModeSelection = (wxComboBox*)FindWindow(IDC_MODE_SELECT);
+    pModeSelection->Clear();
+    for (i = 0; i < numChoices; i++)
+        pModeSelection->Append(choices[i]);
+    pModeSelection->SetSelection(0);
+    pModeSelection->Enable(numChoices > 1);
+    
+    /*
+     * configure qwerty keyboard attribute
+     */
+    numKeyboards = pPhoneData->GetNumKeyboards();
+    if (numKeyboards > 0) {
+        // only use the first keyboard for now
+        PhoneKeyboard* pPhoneKeyboard;
+        pPhoneKeyboard = pPhoneData->GetPhoneKeyboard(0);
+        if (pPhoneKeyboard->getQwerty()) {
+            printf("Sim: set 'qwerty' env\n");
+            setenv("qwerty", "true", true);
+        }
+    }
+    
+bail:
+    delete[] choices;
+}
+
+/*
+ * Figure out which device is currently selected.
+ *
+ * The easiest way to do this is just run down the list of possible IDs
+ * and stop when something claims to be checked.
+ *
+ * Returns -1 if it can't find a checked item (which can happen if no
+ * device layouts were found).
+ */
+int MainFrame::GetSelectedDeviceIndex(void)
+{
+    wxMenuBar* pMenuBar;
+    wxMenu* pMenu;
+    int idx;
+    
+    pMenuBar = GetMenuBar();
+    idx = pMenuBar->FindMenu(kDeviceMenuString);
+    if (idx == wxNOT_FOUND) {
+        fprintf(stderr, "Sim: couldn't find %s menu\n", (const char*) kDeviceMenuString.ToAscii());
+        return -1;
+    }
+
+    pMenu = pMenuBar->GetMenu(idx);
+
+    //printf("Menu.MenuItemCount = %d\n", pMenu->GetMenuItemCount());
+    for (int j = pMenu->GetMenuItemCount() -1; j >= 0; j--) {
+        wxMenuItem* pItem;
+
+        pItem = pMenu->FindItemByPosition(j);
+        //printf("ITEM %d: %s\n", j, (const char*) pItem->GetLabel());
+        if (pItem->IsChecked()) {
+            printf("Sim: selected device is '%s'\n",
+                (const char*) pItem->GetLabel().ToAscii());
+            return j;
+        }
+    }
+
+    return -1;
+}
+
+/*
+ * Receive a status message from the runtime thread.
+ */
+void MainFrame::OnUserEvent(UserEvent& event)
+{
+    UserEventMessage* pUem;
+
+    pUem = (UserEventMessage*) event.GetData();
+    assert(pUem != NULL);
+
+    switch (pUem->GetType()) {
+    case UserEventMessage::kRuntimeStarted:
+        printf("Sim: runtime thread started!\n");
+        HandleRuntimeStart();
+        break;
+    case UserEventMessage::kRuntimeStopped:
+        printf("Sim: runtime thread stopped!\n");
+        HandleRuntimeStop();
+        break;
+    case UserEventMessage::kErrorMessage:
+        {
+            wxString msg = pUem->GetString();
+            wxMessageBox(msg, wxT("Android Runtime Error"),
+                wxOK | wxICON_WARNING, this);
+        }
+        break;
+    case UserEventMessage::kLogMessage:
+        mpLogWindow->AddLogMessage(pUem->GetLogMessage());
+        break;
+    case UserEventMessage::kExternalRuntime:
+        HandleExternalRuntime(pUem->GetReader(), pUem->GetWriter());
+        break;
+    default:
+        printf("Sim: MESSAGE: unknown UserEventMessage rcvd (type=%d)\n",
+            pUem->GetType());
+        break;
+    }
+
+    delete pUem;
+}
+
+/*
+ * The device management thread is up, so the runtime should be fully
+ * running shortly.
+ */
+void MainFrame::HandleRuntimeStart(void)
+{
+    mSimRunning = true;
+
+    SetStatusText(kStatusRunning, 1);
+}
+
+/*
+ * The device management thread is exiting, so the runtime must be dead.
+ */
+void MainFrame::HandleRuntimeStop(void)
+{
+    mSimRunning = false;
+
+    SetStatusText(kStatusNotRunning, 1);
+
+    if (mRestartRequested) {
+        printf("Sim: restarting runtime\n");
+        mRestartRequested = false;
+        SetupPhoneUI(GetSelectedDeviceIndex(), NULL);
+        if (mpPhoneWindow != NULL)
+            mpPhoneWindow->GetDeviceManager()->StartRuntime();
+    }
+}
+
+/*
+ * Handle a connection from an external runtime.
+ */
+void MainFrame::HandleExternalRuntime(android::Pipe* reader,
+    android::Pipe* writer)
+{
+    android::MessageStream msgStream;
+    android::Message msg;
+
+    if (IsRuntimeRunning()) {
+        /*
+         * Tell the new guy to go away.
+         */
+        if (!msgStream.init(reader, writer, true)) {
+            fprintf(stderr, "Sim: WARNING: unable to talk to remote runtime\n");
+            goto bail;
+        }
+
+        printf("Sim: telling external runtime to go away\n");
+        msg.setCommand(android::Simulator::kCommandGoAway, 0);
+        msgStream.send(&msg);
+    } else {
+        printf("Sim: new external runtime wants to talk to us\n");
+
+        /*
+         * Launch the pieces necessary to talk to this guy.
+         */
+        int id = GetSelectedDeviceIndex();
+        if (id < 0) {
+            fprintf(stderr,
+                "Sim: could not identify currently selected device\n");
+            goto bail;
+        }
+
+        /* kill existing window, so it pops up and reclaims focus */
+        if (mpPhoneWindow != NULL) {
+            Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+            bool okay;
+
+            if (pPrefs->GetBool("refocus-on-restart", &okay) && okay) {
+                printf("Sim: inducing phone window refocus\n");
+                mpPhoneWindow->Close(TRUE);     // no veto
+                mpPhoneWindow = NULL;
+            }
+        }
+
+        SetupPhoneUI(id, NULL);
+        if (mpPhoneWindow != NULL) {
+            mpPhoneWindow->GetDeviceManager()->StartRuntime(reader, writer);
+        } else {
+            fprintf(stderr, "Sim: ERROR: unable to get runtime going\n");
+            goto bail;
+        }
+
+        // we don't own these anymore
+        reader = writer = NULL;
+    }
+
+bail:
+    delete reader;
+    delete writer;
+}
+
+/*
+ * The phone window is about to destroy itself.  Get rid of our pointer
+ * to it, and record its last position so we can create the new one in
+ * the same place.
+ */
+void MainFrame::PhoneWindowClosing(int x, int y)
+{
+    Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
+
+    mpPhoneWindow = NULL;
+
+    mPhoneWindowPosn.x = x;
+    mPhoneWindowPosn.y = y;
+
+    pPrefs->SetInt("window-device-x", x);
+    pPrefs->SetInt("window-device-y", y);
+}
+