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