548 lines
15 KiB
C++
548 lines
15 KiB
C++
//
|
|
// Copyright 2005 The Android Open Source Project
|
|
//
|
|
// Application entry point.
|
|
//
|
|
|
|
// 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/image.h" // needed for Windows build
|
|
#include "wx/fs_zip.h"
|
|
|
|
#include "MainFrame.h"
|
|
#include "MyApp.h"
|
|
#include "executablepath.h"
|
|
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <getopt.h>
|
|
#include <signal.h>
|
|
|
|
#if defined(HAVE_WINDOWS_PATHS)
|
|
# include <windows.h>
|
|
#endif
|
|
|
|
|
|
/* the name of our config file */
|
|
static wxString kConfigFileName = wxT(".android.cf");
|
|
|
|
#ifdef HAVE_WINDOWS_PATHS
|
|
static wxString kExeSuffix = wxT(".exe");
|
|
#else
|
|
static wxString kExeSuffix = wxT("");
|
|
#endif
|
|
|
|
/* do we want to kill the runtime? */
|
|
bool gWantToKill = false;
|
|
|
|
/*
|
|
* Signal handler for Ctrl-C. Under Linux we seem to get hit twice,
|
|
* possibly once for each thread.
|
|
*
|
|
* Avoid using LOG here -- it's not reentrant. Actually, just avoid doing
|
|
* anything here.
|
|
*
|
|
* Cygwin will ignore the signal but doesn't seem to call the signal
|
|
* handler. MinGW just kills the process.
|
|
*/
|
|
static void SignalHandler(int sigNum)
|
|
{
|
|
printf("Sim: received signal %d (%s)\n", sigNum,
|
|
sigNum == SIGINT ? "SIGINT" : "???");
|
|
gWantToKill = true;
|
|
}
|
|
|
|
|
|
/* wxWidgets magic; creates appropriate main entry function */
|
|
IMPLEMENT_APP(MyApp)
|
|
|
|
/*
|
|
* Application entry point.
|
|
*/
|
|
bool MyApp::OnInit()
|
|
{
|
|
static wxString helpFilePath = wxT("simulator/help/unnamed.htb");
|
|
|
|
/*
|
|
* Parse args.
|
|
*/
|
|
|
|
SetDefaults();
|
|
|
|
char** cargv = (char**)malloc(argc * sizeof(char*));
|
|
for (int i=0; i<argc; i++) {
|
|
wxCharBuffer tmp = wxString(argv[i]).ToAscii();
|
|
cargv[i] = tmp.release();
|
|
}
|
|
if (!ParseArgs(argc, cargv)) {
|
|
for (int i=0; i<argc; i++)
|
|
free(cargv[i]);
|
|
free(cargv);
|
|
return FALSE;
|
|
}
|
|
for (int i=0; i<argc; i++)
|
|
free(cargv[i]);
|
|
free(cargv);
|
|
|
|
if (!ProcessConfigFile())
|
|
return FALSE;
|
|
|
|
/*
|
|
* (Try to) catch SIGINT (Ctrl-C).
|
|
*/
|
|
bool trapInt = false;
|
|
mPrefs.GetBool("trap-sigint", &trapInt);
|
|
if (trapInt) {
|
|
printf("Sim: catching SIGINT\n");
|
|
signal(SIGINT, SignalHandler);
|
|
}
|
|
|
|
signal(SIGPIPE, SIG_IGN);
|
|
|
|
/*
|
|
* Set stdout to unbuffered. This is needed for MinGW/MSYS.
|
|
* Set stderr while we're at it.
|
|
*/
|
|
setvbuf(stdout, NULL, _IONBF, 0);
|
|
setvbuf(stderr, NULL, _IONBF, 0);
|
|
|
|
/*
|
|
* Initialize asset manager.
|
|
*/
|
|
mpAssetManager = NULL;
|
|
printf("Sim: looking in '%s' for my assets\n", (const char*) mSimAssetPath.ToAscii());
|
|
ChangeAssetDirectory(mSimAssetPath);
|
|
|
|
/*
|
|
* Add JPEG and PNG image handlers.
|
|
*/
|
|
::wxInitAllImageHandlers();
|
|
|
|
/*
|
|
* Set up the help file browser. We're using wxHtmlHelpController
|
|
* because it seems to be the only "portable" version other than
|
|
* the "use external browser" version.
|
|
*/
|
|
wxFileSystem::AddHandler(new wxZipFSHandler);
|
|
mHelpController = new wxHtmlHelpController;
|
|
|
|
wxString helpFileName;
|
|
helpFileName = mSimAssetPath;
|
|
helpFileName += '/';
|
|
helpFileName += helpFilePath;
|
|
mHelpController->Initialize(helpFileName);
|
|
|
|
/*
|
|
* Create the main window, which just holds some of our UI.
|
|
*/
|
|
wxPoint pos(wxDefaultPosition);
|
|
mPrefs.GetInt("window-main-x", &pos.x);
|
|
mPrefs.GetInt("window-main-y", &pos.y);
|
|
mpMainFrame = new MainFrame(wxT("Android Simulator"), pos, wxDefaultSize,
|
|
wxDEFAULT_FRAME_STYLE);
|
|
mpMainFrame->Show(TRUE);
|
|
SetTopWindow(mpMainFrame);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Change our asset directory. This requires deleting the existing
|
|
* AssetManager and creating a new one. Note that any open Assets will
|
|
* still be valid.
|
|
*/
|
|
void MyApp::ChangeAssetDirectory(const wxString& dir)
|
|
{
|
|
delete mpAssetManager;
|
|
mpAssetManager = new android::AssetManager;
|
|
android::String8 path(dir.ToAscii());
|
|
path.appendPath("simulator.zip");
|
|
mpAssetManager->addAssetPath(path, NULL);
|
|
// mpAssetManager->setLocale(xxx);
|
|
mpAssetManager->setVendor("google");
|
|
}
|
|
|
|
|
|
/*
|
|
* App is shutting down. Save the config file.
|
|
*/
|
|
int MyApp::OnExit(void)
|
|
{
|
|
if (mPrefs.GetDirty()) {
|
|
printf("Sim: writing config file to '%s'\n",
|
|
(const char*) mConfigFile.ToAscii());
|
|
if (!mPrefs.Save(mConfigFile.ToAscii())) {
|
|
fprintf(stderr, "Sim: ERROR: prefs save to '%s' failed\n",
|
|
(const char*) mConfigFile.ToAscii());
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t
|
|
find_last_slash(const wxString& s)
|
|
{
|
|
int slash = s.Last('/');
|
|
if (slash < 0) {
|
|
slash = s.Last('\\');
|
|
}
|
|
return slash;
|
|
}
|
|
|
|
|
|
/*
|
|
* Set some default parameters
|
|
*/
|
|
void MyApp::SetDefaults()
|
|
{
|
|
mDebuggerOption = false;
|
|
|
|
/* Get the path to this executable, which should
|
|
* end in something like "/host/linux-x86/bin/simulator".
|
|
* (The full path may begin with something like "out"
|
|
* or "out/debug".)
|
|
*/
|
|
char exepath[PATH_MAX];
|
|
executablepath(exepath);
|
|
wxString out = wxString::FromAscii(exepath);
|
|
|
|
/* Get the path to the root host directory; e.g., "out/host".
|
|
* We can do this by removing the last three slashes
|
|
* and everything after/between them ("/linux-x86/bin/simulator").
|
|
*/
|
|
for (int i = 0; i < 3; i++) {
|
|
int slash = find_last_slash(out);
|
|
assert(slash >= 0);
|
|
out.Truncate(slash);
|
|
}
|
|
|
|
/* Get the location of the assets directory; something like
|
|
* "out/host/common/sim-assets"
|
|
*/
|
|
mSimAssetPath = out;
|
|
mSimAssetPath.Append(wxT("/common/sim-assets"));
|
|
|
|
/* Get the location of the simulated device filesystem.
|
|
* We can't reliably predict this based on the executable
|
|
* location, so try to get it from the environment.
|
|
*/
|
|
char *envOut = getenv("ANDROID_PRODUCT_OUT");
|
|
if (envOut == NULL) {
|
|
fprintf(stderr,
|
|
"WARNING: $ANDROID_PRODUCT_OUT not set in environment\n");
|
|
envOut = "";
|
|
}
|
|
|
|
// the root of the android stuff
|
|
mAndroidRoot = wxString::FromAscii(envOut);
|
|
mAndroidRoot.Append(wxT("/system"));
|
|
|
|
// where runtime is
|
|
mRuntimeExe = mAndroidRoot;
|
|
mRuntimeExe.Append(wxT("/bin/runtime"));
|
|
mRuntimeExe.Append(kExeSuffix);
|
|
|
|
printf("mAndroidRoot='%s'\n", (const char*) mAndroidRoot.ToAscii());
|
|
printf("mSimAssetPath='%s'\n", (const char*) mSimAssetPath.ToAscii());
|
|
}
|
|
|
|
|
|
/*
|
|
* Parse command-line arguments.
|
|
*
|
|
* Returns "false" if we have a parsing error.
|
|
*/
|
|
bool MyApp::ParseArgs(int argc, char** argv)
|
|
{
|
|
int ic;
|
|
|
|
opterr = 0; // don't complain about unrecognized options
|
|
|
|
if (false) {
|
|
printf("MyApp args:\n");
|
|
for (int i = 0; i < argc; i++)
|
|
printf(" %2d: '%s'\n", i, (const char*) argv[i]);
|
|
}
|
|
|
|
while (1) {
|
|
ic = getopt(argc, argv, "tj:da:f:rx:");
|
|
if (ic < 0)
|
|
break;
|
|
|
|
switch (ic) {
|
|
case 'j':
|
|
mAutoRunApp = wxString::FromAscii(optarg);
|
|
break;
|
|
case 't':
|
|
mAutoRunApp = wxT("com.android.testharness.RunAll");
|
|
break;
|
|
case 'd':
|
|
mDebuggerOption = true;
|
|
break;
|
|
case 'x':
|
|
mDebuggerScript = wxString::FromAscii(optarg);
|
|
mDebuggerOption = true; // force debug if a script is being used
|
|
break;
|
|
case 'a': // simulator asset dir
|
|
mSimAssetPath = wxString::FromAscii(optarg);
|
|
break;
|
|
case 'f': // simulator config file
|
|
mConfigFile = wxString::FromAscii(optarg);
|
|
break;
|
|
case 'r': // reset path-based options to defaults
|
|
mResetPaths = true;
|
|
break;
|
|
default:
|
|
fprintf(stderr, "WARNING: unknown sim option '%c'\n", ic);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
* Convert a path to absolute form, if needed.
|
|
*
|
|
* String manipulation would be more efficient than system calls, but
|
|
* less reliable.
|
|
*
|
|
* We need to use GetCurrentDirectory() under Windows because, under
|
|
* Cygwin, some wxWidgets features require "C:" paths rather than
|
|
* local-rooted paths. Probably needed for stand-alone MinGW too.
|
|
*/
|
|
void MyApp::AbsifyPath(wxString& dir)
|
|
{
|
|
char oldDir[512], newDir[512];
|
|
wxString newDirStr;
|
|
|
|
// We still need to do this under Cygwin even if the path is
|
|
// already absolute.
|
|
//if (dir[0] == '/' || dir[0] == '\\')
|
|
// return;
|
|
|
|
if (getcwd(oldDir, sizeof(oldDir)) == NULL) {
|
|
fprintf(stderr, "getcwd() failed\n");
|
|
return;
|
|
}
|
|
|
|
if (chdir(dir.ToAscii()) == 0) {
|
|
#if defined(HAVE_WINDOWS_PATHS)
|
|
DWORD dwRet;
|
|
dwRet = GetCurrentDirectory(sizeof(newDir), newDir);
|
|
if (dwRet == 0 || dwRet > sizeof(newDir))
|
|
sprintf(newDir, "GET_DIR_FAILED %lu", dwRet);
|
|
#else
|
|
if (getcwd(newDir, sizeof(newDir)) == NULL)
|
|
strcpy(newDir, "GET_DIR_FAILED");
|
|
#endif
|
|
newDirStr = wxString::FromAscii(newDir);
|
|
chdir(oldDir);
|
|
} else {
|
|
fprintf(stderr, "WARNING: unable to chdir to '%s' from '%s'\n",
|
|
(const char*) dir.ToAscii(), oldDir);
|
|
newDirStr = dir;
|
|
}
|
|
|
|
//dir = "c:/dev/cygwin";
|
|
//dir += newDirStr;
|
|
dir = newDirStr;
|
|
}
|
|
|
|
|
|
/*
|
|
* Load and process our configuration file.
|
|
*/
|
|
bool MyApp::ProcessConfigFile(void)
|
|
{
|
|
wxString homeConfig;
|
|
bool configLoaded = false;
|
|
|
|
if (getenv("HOME") != NULL) {
|
|
homeConfig = wxString::FromAscii(getenv("HOME"));
|
|
homeConfig += '/';
|
|
homeConfig += kConfigFileName;
|
|
} else {
|
|
homeConfig = wxT("./");
|
|
homeConfig += kConfigFileName;
|
|
}
|
|
|
|
/*
|
|
* Part 1: read the config file.
|
|
*/
|
|
|
|
if (mConfigFile.Length() > 0) {
|
|
/*
|
|
* Read from specified config file. We absolutify the path
|
|
* first so that we're guaranteed to be hitting the same file
|
|
* even if the cwd changes.
|
|
*/
|
|
if (access(mConfigFile.ToAscii(), R_OK) != 0) {
|
|
fprintf(stderr, "ERROR: unable to open '%s'\n",
|
|
(const char*) mConfigFile.ToAscii());
|
|
return false;
|
|
}
|
|
if (!mPrefs.Load(mConfigFile.ToAscii())) {
|
|
fprintf(stderr, "Failed loading config file '%s'\n",
|
|
(const char*) mConfigFile.ToAscii());
|
|
return false;
|
|
} else {
|
|
configLoaded = true;
|
|
}
|
|
} else {
|
|
/*
|
|
* Try ./android.cf, then $HOME/android.cf. If we find one and
|
|
* read it successfully, save the name in mConfigFile.
|
|
*/
|
|
{
|
|
wxString fileName;
|
|
|
|
fileName = wxT(".");
|
|
AbsifyPath(fileName);
|
|
fileName += wxT("/");
|
|
fileName += kConfigFileName;
|
|
|
|
if (access(fileName.ToAscii(), R_OK) == 0) {
|
|
if (mPrefs.Load(fileName.ToAscii())) {
|
|
mConfigFile = fileName;
|
|
configLoaded = true;
|
|
} else {
|
|
/* damaged config files are always fatal */
|
|
fprintf(stderr, "Failed loading config file '%s'\n",
|
|
(const char*) fileName.ToAscii());
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
if (!configLoaded) {
|
|
if (homeConfig.Length() > 0) {
|
|
if (access(homeConfig.ToAscii(), R_OK) == 0) {
|
|
if (mPrefs.Load(homeConfig.ToAscii())) {
|
|
mConfigFile = homeConfig;
|
|
configLoaded = true;
|
|
} else {
|
|
/* damaged config files are always fatal */
|
|
fprintf(stderr, "Failed loading config file '%s'\n",
|
|
(const char*) homeConfig.ToAscii());
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/* if we couldn't find one to load, create a new one in $HOME */
|
|
if (!configLoaded) {
|
|
mConfigFile = homeConfig;
|
|
if (!mPrefs.Create()) {
|
|
fprintf(stderr, "prefs creation failed\n");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Part 2: reset some entries if requested.
|
|
*
|
|
* If you want to reset local items (like paths to binaries) without
|
|
* disrupting other options, specifying the "reset" flag will cause
|
|
* some entries to be removed, and new defaults generated below.
|
|
*/
|
|
|
|
if (mResetPaths) {
|
|
if (mPrefs.RemovePref("debugger"))
|
|
printf(" removed pref 'debugger'\n");
|
|
if (mPrefs.RemovePref("valgrinder"))
|
|
printf(" removed pref 'valgrinder'\n");
|
|
}
|
|
|
|
/*
|
|
* Find GDB.
|
|
*/
|
|
if (!mPrefs.Exists("debugger")) {
|
|
static wxString paths[] = {
|
|
wxT("/bin"), wxT("/usr/bin"), wxString()
|
|
};
|
|
wxString gdbPath;
|
|
|
|
FindExe(wxT("gdb"), paths, wxT("/usr/bin/gdb"), &gdbPath);
|
|
mPrefs.SetString("debugger", gdbPath.ToAscii());
|
|
}
|
|
|
|
|
|
/*
|
|
* Find Valgrind. It currently only exists in Linux, and is installed
|
|
* in /usr/bin/valgrind by default on our systems. The default version
|
|
* is old and sometimes fails, so look for a newer version.
|
|
*/
|
|
if (!mPrefs.Exists("valgrinder")) {
|
|
static wxString paths[] = {
|
|
wxT("/home/fadden/local/bin"), wxT("/usr/bin"), wxString()
|
|
};
|
|
wxString valgrindPath;
|
|
|
|
FindExe(wxT("valgrind"), paths, wxT("/usr/bin/valgrind"), &valgrindPath);
|
|
mPrefs.SetString("valgrinder", valgrindPath.ToAscii());
|
|
}
|
|
|
|
/*
|
|
* Set misc options.
|
|
*/
|
|
if (!mPrefs.Exists("auto-power-on"))
|
|
mPrefs.SetBool("auto-power-on", true);
|
|
if (!mPrefs.Exists("gamma"))
|
|
mPrefs.SetDouble("gamma", 1.0);
|
|
|
|
if (mPrefs.GetDirty()) {
|
|
printf("Sim: writing config file to '%s'\n",
|
|
(const char*) mConfigFile.ToAscii());
|
|
if (!mPrefs.Save(mConfigFile.ToAscii())) {
|
|
fprintf(stderr, "Sim: ERROR: prefs save to '%s' failed\n",
|
|
(const char*) mConfigFile.ToAscii());
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Find an executable by searching in several places.
|
|
*/
|
|
/*static*/ void MyApp::FindExe(const wxString& exeName, const wxString paths[],
|
|
const wxString& defaultPath, wxString* pOut)
|
|
{
|
|
wxString exePath;
|
|
wxString slashExe;
|
|
|
|
slashExe = wxT("/");
|
|
slashExe += exeName;
|
|
slashExe += kExeSuffix;
|
|
|
|
while (!(*paths).IsNull()) {
|
|
wxString tmp;
|
|
|
|
tmp = *paths;
|
|
tmp += slashExe;
|
|
if (access(tmp.ToAscii(), X_OK) == 0) {
|
|
printf("Sim: Found '%s' in '%s'\n", (const char*) exeName.ToAscii(),
|
|
(const char*) tmp.ToAscii());
|
|
*pOut = tmp;
|
|
return;
|
|
}
|
|
|
|
paths++;
|
|
}
|
|
|
|
printf("Sim: Couldn't find '%s', defaulting to '%s'\n",
|
|
(const char*) exeName.ToAscii(), (const char*) defaultPath.ToAscii());
|
|
*pOut = defaultPath;
|
|
}
|
|
|