// // 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; }