• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
SignalHandler(int sigNum)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 */
IMPLEMENT_APP(MyApp)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  */
ChangeAssetDirectory(const wxString & dir)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  */
OnExit(void)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
find_last_slash(const wxString & s)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  */
SetDefaults()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  */
ParseArgs(int argc,char ** argv)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  */
AbsifyPath(wxString & dir)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  */
ProcessConfigFile(void)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  */
FindExe(const wxString & exeName,const wxString paths[],const wxString & defaultPath,wxString * pOut)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