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