Home | History | Annotate | Download | only in app
      1 //
      2 // Copyright 2005 The Android Open Source Project
      3 //
      4 // Application entry point.
      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/image.h"   // needed for Windows build
     15 #include "wx/fs_zip.h"
     16 
     17 #include "MainFrame.h"
     18 #include "MyApp.h"
     19 #include "executablepath.h"
     20 
     21 #include <stdio.h>
     22 #include <unistd.h>
     23 #include <getopt.h>
     24 #include <signal.h>
     25 
     26 #if defined(HAVE_WINDOWS_PATHS)
     27 # include <windows.h>
     28 #endif
     29 
     30 
     31 /* the name of our config file */
     32 static wxString kConfigFileName = wxT(".android.cf");
     33 
     34 #ifdef HAVE_WINDOWS_PATHS
     35 static wxString kExeSuffix = wxT(".exe");
     36 #else
     37 static wxString kExeSuffix = wxT("");
     38 #endif
     39 
     40 /* do we want to kill the runtime? */
     41 bool gWantToKill = false;
     42 
     43 /*
     44  * Signal handler for Ctrl-C.  Under Linux we seem to get hit twice,
     45  * possibly once for each thread.
     46  *
     47  * Avoid using LOG here -- it's not reentrant.  Actually, just avoid doing
     48  * anything here.
     49  *
     50  * Cygwin will ignore the signal but doesn't seem to call the signal
     51  * handler.  MinGW just kills the process.
     52  */
     53 static void SignalHandler(int sigNum)
     54 {
     55     printf("Sim: received signal %d (%s)\n", sigNum,
     56         sigNum == SIGINT ? "SIGINT" : "???");
     57     gWantToKill = true;
     58 }
     59 
     60 
     61 /* wxWidgets magic; creates appropriate main entry function */
     62 IMPLEMENT_APP(MyApp)
     63 
     64 /*
     65  * Application entry point.
     66  */
     67 bool MyApp::OnInit()
     68 {
     69     static wxString helpFilePath = wxT("simulator/help/unnamed.htb");
     70 
     71     /*
     72      * Parse args.
     73      */
     74 
     75     SetDefaults();
     76 
     77     char** cargv = (char**)malloc(argc * sizeof(char*));
     78     for (int i=0; i<argc; i++) {
     79 	wxCharBuffer tmp = wxString(argv[i]).ToAscii();
     80         cargv[i] = tmp.release();
     81     }
     82     if (!ParseArgs(argc, cargv)) {
     83 	for (int i=0; i<argc; i++)
     84 	    free(cargv[i]);
     85 	free(cargv);
     86         return FALSE;
     87     }
     88     for (int i=0; i<argc; i++)
     89         free(cargv[i]);
     90     free(cargv);
     91 
     92     if (!ProcessConfigFile())
     93         return FALSE;
     94 
     95     /*
     96      * (Try to) catch SIGINT (Ctrl-C).
     97      */
     98     bool trapInt = false;
     99     mPrefs.GetBool("trap-sigint", &trapInt);
    100     if (trapInt) {
    101         printf("Sim: catching SIGINT\n");
    102         signal(SIGINT, SignalHandler);
    103     }
    104 
    105     signal(SIGPIPE, SIG_IGN);
    106 
    107     /*
    108      * Set stdout to unbuffered.  This is needed for MinGW/MSYS.
    109      * Set stderr while we're at it.
    110      */
    111     setvbuf(stdout, NULL, _IONBF, 0);
    112     setvbuf(stderr, NULL, _IONBF, 0);
    113 
    114     /*
    115      * Initialize asset manager.
    116      */
    117     mpAssetManager = NULL;
    118     printf("Sim: looking in '%s' for my assets\n", (const char*) mSimAssetPath.ToAscii());
    119     ChangeAssetDirectory(mSimAssetPath);
    120 
    121     /*
    122      * Add JPEG and PNG image handlers.
    123      */
    124     ::wxInitAllImageHandlers();
    125 
    126     /*
    127      * Set up the help file browser.  We're using wxHtmlHelpController
    128      * because it seems to be the only "portable" version other than
    129      * the "use external browser" version.
    130      */
    131     wxFileSystem::AddHandler(new wxZipFSHandler);
    132     mHelpController = new wxHtmlHelpController;
    133 
    134     wxString helpFileName;
    135     helpFileName = mSimAssetPath;
    136     helpFileName += '/';
    137     helpFileName += helpFilePath;
    138     mHelpController->Initialize(helpFileName);
    139 
    140     /*
    141      * Create the main window, which just holds some of our UI.
    142      */
    143     wxPoint pos(wxDefaultPosition);
    144     mPrefs.GetInt("window-main-x", &pos.x);
    145     mPrefs.GetInt("window-main-y", &pos.y);
    146     mpMainFrame = new MainFrame(wxT("Android Simulator"), pos, wxDefaultSize,
    147         wxDEFAULT_FRAME_STYLE);
    148     mpMainFrame->Show(TRUE);
    149     SetTopWindow(mpMainFrame);
    150 
    151     return TRUE;
    152 }
    153 
    154 /*
    155  * Change our asset directory.  This requires deleting the existing
    156  * AssetManager and creating a new one.  Note that any open Assets will
    157  * still be valid.
    158  */
    159 void MyApp::ChangeAssetDirectory(const wxString& dir)
    160 {
    161     delete mpAssetManager;
    162     mpAssetManager = new android::AssetManager;
    163     android::String8 path(dir.ToAscii());
    164     path.appendPath("simulator.zip");
    165     mpAssetManager->addAssetPath(path, NULL);
    166     // mpAssetManager->setLocale(xxx);
    167     mpAssetManager->setVendor("google");
    168 }
    169 
    170 
    171 /*
    172  * App is shutting down.  Save the config file.
    173  */
    174 int MyApp::OnExit(void)
    175 {
    176     if (mPrefs.GetDirty()) {
    177         printf("Sim: writing config file to '%s'\n",
    178             (const char*) mConfigFile.ToAscii());
    179         if (!mPrefs.Save(mConfigFile.ToAscii())) {
    180             fprintf(stderr, "Sim: ERROR: prefs save to '%s' failed\n",
    181                 (const char*) mConfigFile.ToAscii());
    182         }
    183     }
    184 
    185     return 0;
    186 }
    187 
    188 static ssize_t
    189 find_last_slash(const wxString& s)
    190 {
    191     int slash = s.Last('/');
    192     if (slash < 0) {
    193         slash = s.Last('\\');
    194     }
    195     return slash;
    196 }
    197 
    198 
    199 /*
    200  * Set some default parameters
    201  */
    202 void MyApp::SetDefaults()
    203 {
    204     mDebuggerOption = false;
    205 
    206     /* Get the path to this executable, which should
    207      * end in something like "/host/linux-x86/bin/simulator".
    208      * (The full path may begin with something like "out"
    209      * or "out/debug".)
    210      */
    211     char exepath[PATH_MAX];
    212     executablepath(exepath);
    213     wxString out = wxString::FromAscii(exepath);
    214 
    215     /* Get the path to the root host directory;  e.g., "out/host".
    216      * We can do this by removing the last three slashes
    217      * and everything after/between them ("/linux-x86/bin/simulator").
    218      */
    219     for (int i = 0; i < 3; i++) {
    220         int slash = find_last_slash(out);
    221         assert(slash >= 0);
    222         out.Truncate(slash);
    223     }
    224 
    225     /* Get the location of the assets directory; something like
    226      * "out/host/common/sim-assets"
    227      */
    228     mSimAssetPath = out;
    229     mSimAssetPath.Append(wxT("/common/sim-assets"));
    230 
    231     /* Get the location of the simulated device filesystem.
    232      * We can't reliably predict this based on the executable
    233      * location, so try to get it from the environment.
    234      */
    235     char *envOut = getenv("ANDROID_PRODUCT_OUT");
    236     if (envOut == NULL) {
    237         fprintf(stderr,
    238                 "WARNING: $ANDROID_PRODUCT_OUT not set in environment\n");
    239         envOut = "";
    240     }
    241 
    242     // the root of the android stuff
    243     mAndroidRoot = wxString::FromAscii(envOut);
    244     mAndroidRoot.Append(wxT("/system"));
    245 
    246     // where runtime is
    247     mRuntimeExe = mAndroidRoot;
    248     mRuntimeExe.Append(wxT("/bin/runtime"));
    249     mRuntimeExe.Append(kExeSuffix);
    250 
    251     printf("mAndroidRoot='%s'\n", (const char*) mAndroidRoot.ToAscii());
    252     printf("mSimAssetPath='%s'\n", (const char*) mSimAssetPath.ToAscii());
    253 }
    254 
    255 
    256 /*
    257  * Parse command-line arguments.
    258  *
    259  * Returns "false" if we have a parsing error.
    260  */
    261 bool MyApp::ParseArgs(int argc, char** argv)
    262 {
    263     int ic;
    264 
    265     opterr = 0;     // don't complain about unrecognized options
    266 
    267     if (false) {
    268         printf("MyApp args:\n");
    269         for (int i = 0; i < argc; i++)
    270             printf("  %2d: '%s'\n", i, (const char*) argv[i]);
    271     }
    272 
    273     while (1) {
    274         ic = getopt(argc, argv, "tj:da:f:rx:");
    275         if (ic < 0)
    276             break;
    277 
    278         switch (ic) {
    279         case 'j':
    280             mAutoRunApp = wxString::FromAscii(optarg);
    281             break;
    282         case 't':
    283             mAutoRunApp = wxT("com.android.testharness.RunAll");
    284             break;
    285         case 'd':
    286             mDebuggerOption = true;
    287             break;
    288         case 'x':
    289             mDebuggerScript = wxString::FromAscii(optarg);
    290             mDebuggerOption = true;     // force debug if a script is being used
    291             break;
    292         case 'a':       // simulator asset dir
    293             mSimAssetPath = wxString::FromAscii(optarg);
    294             break;
    295         case 'f':       // simulator config file
    296             mConfigFile = wxString::FromAscii(optarg);
    297             break;
    298         case 'r':       // reset path-based options to defaults
    299             mResetPaths = true;
    300             break;
    301         default:
    302             fprintf(stderr, "WARNING: unknown sim option '%c'\n", ic);
    303             break;
    304         }
    305     }
    306 
    307     return true;
    308 }
    309 
    310 
    311 /*
    312  * Convert a path to absolute form, if needed.
    313  *
    314  * String manipulation would be more efficient than system calls, but
    315  * less reliable.
    316  *
    317  * We need to use GetCurrentDirectory() under Windows because, under
    318  * Cygwin, some wxWidgets features require "C:" paths rather than
    319  * local-rooted paths.  Probably needed for stand-alone MinGW too.
    320  */
    321 void MyApp::AbsifyPath(wxString& dir)
    322 {
    323     char oldDir[512], newDir[512];
    324     wxString newDirStr;
    325 
    326     // We still need to do this under Cygwin even if the path is
    327     // already absolute.
    328     //if (dir[0] == '/' || dir[0] == '\\')
    329     //    return;
    330 
    331     if (getcwd(oldDir, sizeof(oldDir)) == NULL) {
    332         fprintf(stderr, "getcwd() failed\n");
    333         return;
    334     }
    335 
    336     if (chdir(dir.ToAscii()) == 0) {
    337 #if defined(HAVE_WINDOWS_PATHS)
    338         DWORD dwRet;
    339         dwRet = GetCurrentDirectory(sizeof(newDir), newDir);
    340         if (dwRet == 0 || dwRet > sizeof(newDir))
    341             sprintf(newDir, "GET_DIR_FAILED %lu", dwRet);
    342 #else
    343         if (getcwd(newDir, sizeof(newDir)) == NULL)
    344             strcpy(newDir, "GET_DIR_FAILED");
    345 #endif
    346         newDirStr = wxString::FromAscii(newDir);
    347         chdir(oldDir);
    348     } else {
    349         fprintf(stderr, "WARNING: unable to chdir to '%s' from '%s'\n",
    350             (const char*) dir.ToAscii(), oldDir);
    351         newDirStr = dir;
    352     }
    353 
    354     //dir = "c:/dev/cygwin";
    355     //dir += newDirStr;
    356     dir = newDirStr;
    357 }
    358 
    359 
    360 /*
    361  * Load and process our configuration file.
    362  */
    363 bool MyApp::ProcessConfigFile(void)
    364 {
    365     wxString homeConfig;
    366     bool configLoaded = false;
    367 
    368     if (getenv("HOME") != NULL) {
    369         homeConfig = wxString::FromAscii(getenv("HOME"));
    370         homeConfig += '/';
    371         homeConfig += kConfigFileName;
    372     } else {
    373         homeConfig = wxT("./");
    374         homeConfig += kConfigFileName;
    375     }
    376 
    377     /*
    378      * Part 1: read the config file.
    379      */
    380 
    381     if (mConfigFile.Length() > 0) {
    382         /*
    383          * Read from specified config file.  We absolutify the path
    384          * first so that we're guaranteed to be hitting the same file
    385          * even if the cwd changes.
    386          */
    387         if (access(mConfigFile.ToAscii(), R_OK) != 0) {
    388             fprintf(stderr, "ERROR: unable to open '%s'\n",
    389                 (const char*) mConfigFile.ToAscii());
    390             return false;
    391         }
    392         if (!mPrefs.Load(mConfigFile.ToAscii())) {
    393             fprintf(stderr, "Failed loading config file '%s'\n",
    394                 (const char*) mConfigFile.ToAscii());
    395             return false;
    396         } else {
    397             configLoaded = true;
    398         }
    399     } else {
    400         /*
    401          * Try ./android.cf, then $HOME/android.cf.  If we find one and
    402          * read it successfully, save the name in mConfigFile.
    403          */
    404         {
    405             wxString fileName;
    406 
    407             fileName = wxT(".");
    408             AbsifyPath(fileName);
    409             fileName += wxT("/");
    410             fileName += kConfigFileName;
    411 
    412             if (access(fileName.ToAscii(), R_OK) == 0) {
    413                 if (mPrefs.Load(fileName.ToAscii())) {
    414                     mConfigFile = fileName;
    415                     configLoaded = true;
    416                 } else {
    417                     /* damaged config files are always fatal */
    418                     fprintf(stderr, "Failed loading config file '%s'\n",
    419                         (const char*) fileName.ToAscii());
    420                     return false;
    421                 }
    422             }
    423         }
    424         if (!configLoaded) {
    425             if (homeConfig.Length() > 0) {
    426                 if (access(homeConfig.ToAscii(), R_OK) == 0) {
    427                     if (mPrefs.Load(homeConfig.ToAscii())) {
    428                         mConfigFile = homeConfig;
    429                         configLoaded = true;
    430                     } else {
    431                         /* damaged config files are always fatal */
    432                         fprintf(stderr, "Failed loading config file '%s'\n",
    433                             (const char*) homeConfig.ToAscii());
    434                         return false;
    435                     }
    436                 }
    437             }
    438         }
    439 
    440     }
    441 
    442     /* if we couldn't find one to load, create a new one in $HOME */
    443     if (!configLoaded) {
    444         mConfigFile = homeConfig;
    445         if (!mPrefs.Create()) {
    446             fprintf(stderr, "prefs creation failed\n");
    447             return false;
    448         }
    449     }
    450 
    451     /*
    452      * Part 2: reset some entries if requested.
    453      *
    454      * If you want to reset local items (like paths to binaries) without
    455      * disrupting other options, specifying the "reset" flag will cause
    456      * some entries to be removed, and new defaults generated below.
    457      */
    458 
    459     if (mResetPaths) {
    460         if (mPrefs.RemovePref("debugger"))
    461             printf("  removed pref 'debugger'\n");
    462         if (mPrefs.RemovePref("valgrinder"))
    463             printf("  removed pref 'valgrinder'\n");
    464     }
    465 
    466     /*
    467      * Find GDB.
    468      */
    469     if (!mPrefs.Exists("debugger")) {
    470         static wxString paths[] = {
    471             wxT("/bin"), wxT("/usr/bin"), wxString()
    472         };
    473         wxString gdbPath;
    474 
    475         FindExe(wxT("gdb"), paths, wxT("/usr/bin/gdb"), &gdbPath);
    476         mPrefs.SetString("debugger", gdbPath.ToAscii());
    477     }
    478 
    479 
    480     /*
    481      * Find Valgrind.  It currently only exists in Linux, and is installed
    482      * in /usr/bin/valgrind by default on our systems.  The default version
    483      * is old and sometimes fails, so look for a newer version.
    484      */
    485     if (!mPrefs.Exists("valgrinder")) {
    486         static wxString paths[] = {
    487             wxT("/home/fadden/local/bin"), wxT("/usr/bin"), wxString()
    488         };
    489         wxString valgrindPath;
    490 
    491         FindExe(wxT("valgrind"), paths, wxT("/usr/bin/valgrind"), &valgrindPath);
    492         mPrefs.SetString("valgrinder", valgrindPath.ToAscii());
    493     }
    494 
    495     /*
    496      * Set misc options.
    497      */
    498     if (!mPrefs.Exists("auto-power-on"))
    499         mPrefs.SetBool("auto-power-on", true);
    500     if (!mPrefs.Exists("gamma"))
    501         mPrefs.SetDouble("gamma", 1.0);
    502 
    503     if (mPrefs.GetDirty()) {
    504         printf("Sim: writing config file to '%s'\n",
    505             (const char*) mConfigFile.ToAscii());
    506         if (!mPrefs.Save(mConfigFile.ToAscii())) {
    507             fprintf(stderr, "Sim: ERROR: prefs save to '%s' failed\n",
    508                 (const char*) mConfigFile.ToAscii());
    509         }
    510     }
    511 
    512     return true;
    513 }
    514 
    515 /*
    516  * Find an executable by searching in several places.
    517  */
    518 /*static*/ void MyApp::FindExe(const wxString& exeName, const wxString paths[],
    519     const wxString& defaultPath, wxString* pOut)
    520 {
    521     wxString exePath;
    522     wxString slashExe;
    523 
    524     slashExe = wxT("/");
    525     slashExe += exeName;
    526     slashExe += kExeSuffix;
    527 
    528     while (!(*paths).IsNull()) {
    529         wxString tmp;
    530 
    531         tmp = *paths;
    532         tmp += slashExe;
    533         if (access(tmp.ToAscii(), X_OK) == 0) {
    534             printf("Sim: Found '%s' in '%s'\n", (const char*) exeName.ToAscii(),
    535                     (const char*) tmp.ToAscii());
    536             *pOut = tmp;
    537             return;
    538         }
    539 
    540         paths++;
    541     }
    542 
    543     printf("Sim: Couldn't find '%s', defaulting to '%s'\n",
    544         (const char*) exeName.ToAscii(), (const char*) defaultPath.ToAscii());
    545     *pOut = defaultPath;
    546 }
    547 
    548