David 'Digit' Turner | f845627 | 2011-02-02 12:34:14 +0100 | [diff] [blame] | 1 | /* Copyright (C) 2011 The Android Open Source Project |
| 2 | ** |
| 3 | ** This software is licensed under the terms of the GNU General Public |
| 4 | ** License version 2, as published by the Free Software Foundation, and |
| 5 | ** may be copied, distributed, and modified under those terms. |
| 6 | ** |
| 7 | ** This program is distributed in the hope that it will be useful, |
| 8 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 9 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 10 | ** GNU General Public License for more details. |
| 11 | */ |
| 12 | #include <signal.h> |
| 13 | #include <unistd.h> |
| 14 | #include <string.h> |
| 15 | #include <sys/time.h> |
| 16 | #include <errno.h> |
| 17 | #include <fcntl.h> |
| 18 | #ifdef _WIN32 |
| 19 | #include <process.h> |
| 20 | #endif |
| 21 | |
| 22 | #include <SDL.h> |
| 23 | #include <SDL_syswm.h> |
| 24 | |
| 25 | #include "console.h" |
| 26 | |
| 27 | #include "android/utils/debug.h" |
| 28 | #include "android/utils/path.h" |
| 29 | #include "android/utils/bufprint.h" |
Vladimir Chtchetkine | 83ffd66 | 2011-02-11 12:40:59 -0800 | [diff] [blame] | 30 | #include "android/utils/dirscanner.h" |
David 'Digit' Turner | f845627 | 2011-02-02 12:34:14 +0100 | [diff] [blame] | 31 | #include "android/main-common.h" |
| 32 | #include "android/globals.h" |
| 33 | #include "android/resource.h" |
| 34 | #include "android/user-config.h" |
Xavier Ducrohet | fc8ed80 | 2011-02-09 18:04:23 -0800 | [diff] [blame] | 35 | #include "android/qemulator.h" |
David 'Digit' Turner | f845627 | 2011-02-02 12:34:14 +0100 | [diff] [blame] | 36 | #include "android/display.h" |
Xavier Ducrohet | fc8ed80 | 2011-02-09 18:04:23 -0800 | [diff] [blame] | 37 | #include "android/skin/image.h" |
| 38 | #include "android/skin/trackball.h" |
| 39 | #include "android/skin/keyboard.h" |
| 40 | #include "android/skin/file.h" |
| 41 | #include "android/skin/window.h" |
David 'Digit' Turner | f845627 | 2011-02-02 12:34:14 +0100 | [diff] [blame] | 42 | |
| 43 | |
| 44 | |
| 45 | /***********************************************************************/ |
| 46 | /***********************************************************************/ |
| 47 | /***** *****/ |
| 48 | /***** U T I L I T Y R O U T I N E S *****/ |
| 49 | /***** *****/ |
| 50 | /***********************************************************************/ |
| 51 | /***********************************************************************/ |
| 52 | |
| 53 | #define D(...) do { if (VERBOSE_CHECK(init)) dprint(__VA_ARGS__); } while (0) |
| 54 | |
| 55 | /*** CONFIGURATION |
| 56 | ***/ |
| 57 | |
| 58 | static AUserConfig* userConfig; |
| 59 | |
| 60 | void |
David 'Digit' Turner | 755811e | 2011-02-07 13:38:25 +0100 | [diff] [blame] | 61 | user_config_init( void ) |
David 'Digit' Turner | f845627 | 2011-02-02 12:34:14 +0100 | [diff] [blame] | 62 | { |
| 63 | userConfig = auserConfig_new( android_avdInfo ); |
| 64 | } |
| 65 | |
| 66 | /* only call this function on normal exits, so that ^C doesn't save the configuration */ |
| 67 | void |
David 'Digit' Turner | 755811e | 2011-02-07 13:38:25 +0100 | [diff] [blame] | 68 | user_config_done( void ) |
David 'Digit' Turner | f845627 | 2011-02-02 12:34:14 +0100 | [diff] [blame] | 69 | { |
| 70 | int win_x, win_y; |
| 71 | |
| 72 | if (!userConfig) { |
| 73 | D("no user configuration?"); |
| 74 | return; |
| 75 | } |
| 76 | |
| 77 | SDL_WM_GetPos( &win_x, &win_y ); |
| 78 | auserConfig_setWindowPos(userConfig, win_x, win_y); |
| 79 | auserConfig_save(userConfig); |
| 80 | } |
| 81 | |
| 82 | void |
David 'Digit' Turner | 755811e | 2011-02-07 13:38:25 +0100 | [diff] [blame] | 83 | user_config_get_window_pos( int *window_x, int *window_y ) |
David 'Digit' Turner | f845627 | 2011-02-02 12:34:14 +0100 | [diff] [blame] | 84 | { |
| 85 | *window_x = *window_y = 10; |
| 86 | |
| 87 | if (userConfig) |
| 88 | auserConfig_getWindowPos(userConfig, window_x, window_y); |
| 89 | } |
| 90 | |
| 91 | unsigned convertBytesToMB( uint64_t size ) |
| 92 | { |
| 93 | if (size == 0) |
| 94 | return 0; |
| 95 | |
| 96 | size = (size + ONE_MB-1) >> 20; |
| 97 | if (size > UINT_MAX) |
| 98 | size = UINT_MAX; |
| 99 | |
| 100 | return (unsigned) size; |
| 101 | } |
| 102 | |
| 103 | uint64_t convertMBToBytes( unsigned megaBytes ) |
| 104 | { |
| 105 | return ((uint64_t)megaBytes << 20); |
| 106 | } |
| 107 | |
| 108 | |
| 109 | /***********************************************************************/ |
| 110 | /***********************************************************************/ |
| 111 | /***** *****/ |
| 112 | /***** K E Y S E T R O U T I N E S *****/ |
| 113 | /***** *****/ |
| 114 | /***********************************************************************/ |
| 115 | /***********************************************************************/ |
| 116 | |
| 117 | #define KEYSET_FILE "default.keyset" |
| 118 | |
| 119 | SkinKeyset* android_keyset = NULL; |
| 120 | |
| 121 | static int |
| 122 | load_keyset(const char* path) |
| 123 | { |
| 124 | if (path_can_read(path)) { |
| 125 | AConfig* root = aconfig_node("",""); |
| 126 | if (!aconfig_load_file(root, path)) { |
| 127 | android_keyset = skin_keyset_new(root); |
| 128 | if (android_keyset != NULL) { |
| 129 | D( "keyset loaded from: %s", path); |
| 130 | return 0; |
| 131 | } |
| 132 | } |
| 133 | } |
| 134 | return -1; |
| 135 | } |
| 136 | |
| 137 | void |
| 138 | parse_keyset(const char* keyset, AndroidOptions* opts) |
| 139 | { |
| 140 | char kname[MAX_PATH]; |
| 141 | char temp[MAX_PATH]; |
| 142 | char* p; |
| 143 | char* end; |
| 144 | |
| 145 | /* append .keyset suffix if needed */ |
| 146 | if (strchr(keyset, '.') == NULL) { |
| 147 | p = kname; |
| 148 | end = p + sizeof(kname); |
| 149 | p = bufprint(p, end, "%s.keyset", keyset); |
| 150 | if (p >= end) { |
| 151 | derror( "keyset name too long: '%s'\n", keyset); |
| 152 | exit(1); |
| 153 | } |
| 154 | keyset = kname; |
| 155 | } |
| 156 | |
| 157 | /* look for a the keyset file */ |
| 158 | p = temp; |
| 159 | end = p + sizeof(temp); |
| 160 | p = bufprint_config_file(p, end, keyset); |
| 161 | if (p < end && load_keyset(temp) == 0) |
| 162 | return; |
| 163 | |
| 164 | p = temp; |
| 165 | p = bufprint(p, end, "%s" PATH_SEP "keysets" PATH_SEP "%s", opts->sysdir, keyset); |
| 166 | if (p < end && load_keyset(temp) == 0) |
| 167 | return; |
| 168 | |
| 169 | p = temp; |
| 170 | p = bufprint_app_dir(p, end); |
| 171 | p = bufprint(p, end, PATH_SEP "keysets" PATH_SEP "%s", keyset); |
| 172 | if (p < end && load_keyset(temp) == 0) |
| 173 | return; |
| 174 | |
| 175 | return; |
| 176 | } |
| 177 | |
| 178 | void |
| 179 | write_default_keyset( void ) |
| 180 | { |
| 181 | char path[MAX_PATH]; |
| 182 | |
| 183 | bufprint_config_file( path, path+sizeof(path), KEYSET_FILE ); |
| 184 | |
| 185 | /* only write if there is no file here */ |
| 186 | if ( !path_exists(path) ) { |
| 187 | int fd = open( path, O_WRONLY | O_CREAT, 0666 ); |
| 188 | int ret; |
| 189 | const char* ks = skin_keyset_get_default(); |
| 190 | |
| 191 | |
| 192 | D( "writing default keyset file to %s", path ); |
| 193 | |
| 194 | if (fd < 0) { |
| 195 | D( "%s: could not create file: %s", __FUNCTION__, strerror(errno) ); |
| 196 | return; |
| 197 | } |
| 198 | CHECKED(ret, write(fd, ks, strlen(ks))); |
| 199 | close(fd); |
| 200 | } |
| 201 | } |
| 202 | |
Xavier Ducrohet | fc8ed80 | 2011-02-09 18:04:23 -0800 | [diff] [blame] | 203 | |
David 'Digit' Turner | f845627 | 2011-02-02 12:34:14 +0100 | [diff] [blame] | 204 | |
| 205 | /***********************************************************************/ |
| 206 | /***********************************************************************/ |
| 207 | /***** *****/ |
| 208 | /***** S D L S U P P O R T *****/ |
| 209 | /***** *****/ |
| 210 | /***********************************************************************/ |
| 211 | /***********************************************************************/ |
| 212 | |
| 213 | void *readpng(const unsigned char* base, size_t size, unsigned *_width, unsigned *_height); |
| 214 | |
| 215 | #ifdef CONFIG_DARWIN |
| 216 | # define ANDROID_ICON_PNG "android_icon_256.png" |
| 217 | #else |
| 218 | # define ANDROID_ICON_PNG "android_icon_16.png" |
| 219 | #endif |
| 220 | |
| 221 | static void |
| 222 | sdl_set_window_icon( void ) |
| 223 | { |
| 224 | static int window_icon_set; |
| 225 | |
| 226 | if (!window_icon_set) |
| 227 | { |
| 228 | #ifdef _WIN32 |
| 229 | HANDLE handle = GetModuleHandle( NULL ); |
| 230 | HICON icon = LoadIcon( handle, MAKEINTRESOURCE(1) ); |
| 231 | SDL_SysWMinfo wminfo; |
| 232 | |
| 233 | SDL_GetWMInfo(&wminfo); |
| 234 | |
| 235 | SetClassLong( wminfo.window, GCL_HICON, (LONG)icon ); |
| 236 | #else /* !_WIN32 */ |
| 237 | unsigned icon_w, icon_h; |
| 238 | size_t icon_bytes; |
| 239 | const unsigned char* icon_data; |
| 240 | void* icon_pixels; |
| 241 | |
| 242 | window_icon_set = 1; |
| 243 | |
| 244 | icon_data = android_icon_find( ANDROID_ICON_PNG, &icon_bytes ); |
| 245 | if ( !icon_data ) |
| 246 | return; |
| 247 | |
| 248 | icon_pixels = readpng( icon_data, icon_bytes, &icon_w, &icon_h ); |
| 249 | if ( !icon_pixels ) |
| 250 | return; |
| 251 | |
| 252 | /* the data is loaded into memory as RGBA bytes by libpng. we want to manage |
| 253 | * the values as 32-bit ARGB pixels, so swap the bytes accordingly depending |
| 254 | * on our CPU endianess |
| 255 | */ |
| 256 | { |
| 257 | unsigned* d = icon_pixels; |
| 258 | unsigned* d_end = d + icon_w*icon_h; |
| 259 | |
| 260 | for ( ; d < d_end; d++ ) { |
| 261 | unsigned pix = d[0]; |
| 262 | #if HOST_WORDS_BIGENDIAN |
| 263 | /* R,G,B,A read as RGBA => ARGB */ |
| 264 | pix = ((pix >> 8) & 0xffffff) | (pix << 24); |
| 265 | #else |
| 266 | /* R,G,B,A read as ABGR => ARGB */ |
| 267 | pix = (pix & 0xff00ff00) | ((pix >> 16) & 0xff) | ((pix & 0xff) << 16); |
| 268 | #endif |
| 269 | d[0] = pix; |
| 270 | } |
| 271 | } |
| 272 | |
| 273 | SDL_Surface* icon = sdl_surface_from_argb32( icon_pixels, icon_w, icon_h ); |
| 274 | if (icon != NULL) { |
| 275 | SDL_WM_SetIcon(icon, NULL); |
| 276 | SDL_FreeSurface(icon); |
| 277 | free( icon_pixels ); |
| 278 | } |
| 279 | #endif /* !_WIN32 */ |
| 280 | } |
| 281 | } |
| 282 | |
| 283 | /***********************************************************************/ |
| 284 | /***********************************************************************/ |
| 285 | /***** *****/ |
| 286 | /***** S K I N S U P P O R T *****/ |
| 287 | /***** *****/ |
| 288 | /***********************************************************************/ |
| 289 | /***********************************************************************/ |
| 290 | |
| 291 | const char* skin_network_speed = NULL; |
| 292 | const char* skin_network_delay = NULL; |
| 293 | |
Xavier Ducrohet | fc8ed80 | 2011-02-09 18:04:23 -0800 | [diff] [blame] | 294 | |
David 'Digit' Turner | f845627 | 2011-02-02 12:34:14 +0100 | [diff] [blame] | 295 | static void sdl_at_exit(void) |
| 296 | { |
David 'Digit' Turner | 755811e | 2011-02-07 13:38:25 +0100 | [diff] [blame] | 297 | user_config_done(); |
David 'Digit' Turner | f845627 | 2011-02-02 12:34:14 +0100 | [diff] [blame] | 298 | qemulator_done(qemulator_get()); |
| 299 | SDL_Quit(); |
| 300 | } |
| 301 | |
| 302 | |
| 303 | void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) |
| 304 | { |
| 305 | QEmulator* emulator = qemulator_get(); |
| 306 | SkinDisplay* disp = skin_layout_get_display(emulator->layout); |
| 307 | int width, height; |
| 308 | char buf[128]; |
| 309 | |
| 310 | if (disp->rotation & 1) { |
| 311 | width = disp->rect.size.h; |
| 312 | height = disp->rect.size.w; |
| 313 | } else { |
| 314 | width = disp->rect.size.w; |
| 315 | height = disp->rect.size.h; |
| 316 | } |
| 317 | |
| 318 | snprintf(buf, sizeof buf, "width=%d,height=%d", width, height); |
David 'Digit' Turner | 07db349 | 2011-02-02 17:36:34 +0100 | [diff] [blame] | 319 | #if !defined(CONFIG_STANDALONE_UI) && !defined(CONFIG_STANDALONE_CORE) |
David 'Digit' Turner | f845627 | 2011-02-02 12:34:14 +0100 | [diff] [blame] | 320 | android_display_init(ds, qframebuffer_fifo_get()); |
David 'Digit' Turner | 07db349 | 2011-02-02 17:36:34 +0100 | [diff] [blame] | 321 | #endif |
David 'Digit' Turner | f845627 | 2011-02-02 12:34:14 +0100 | [diff] [blame] | 322 | } |
| 323 | |
| 324 | /* list of skin aliases */ |
| 325 | static const struct { |
| 326 | const char* name; |
| 327 | const char* alias; |
| 328 | } skin_aliases[] = { |
| 329 | { "QVGA-L", "320x240" }, |
| 330 | { "QVGA-P", "240x320" }, |
| 331 | { "HVGA-L", "480x320" }, |
| 332 | { "HVGA-P", "320x480" }, |
| 333 | { "QVGA", "320x240" }, |
| 334 | { "HVGA", "320x480" }, |
| 335 | { NULL, NULL } |
| 336 | }; |
| 337 | |
| 338 | /* this is used by hw/events_device.c to send the charmap name to the system */ |
| 339 | const char* android_skin_keycharmap = NULL; |
| 340 | |
David 'Digit' Turner | 74d7ace | 2011-02-02 13:21:03 +0100 | [diff] [blame] | 341 | void |
| 342 | parse_skin_files(const char* skinDirPath, |
| 343 | const char* skinName, |
| 344 | AndroidOptions* opts, |
David 'Digit' Turner | 2507cab | 2011-02-10 16:29:17 +0100 | [diff] [blame] | 345 | AndroidHwConfig* hwConfig, |
David 'Digit' Turner | 74d7ace | 2011-02-02 13:21:03 +0100 | [diff] [blame] | 346 | AConfig* *skinConfig, |
| 347 | char* *skinPath) |
David 'Digit' Turner | f845627 | 2011-02-02 12:34:14 +0100 | [diff] [blame] | 348 | { |
| 349 | char tmp[1024]; |
| 350 | AConfig* root; |
David 'Digit' Turner | 74d7ace | 2011-02-02 13:21:03 +0100 | [diff] [blame] | 351 | const char* path = NULL; |
David 'Digit' Turner | f845627 | 2011-02-02 12:34:14 +0100 | [diff] [blame] | 352 | AConfig* n; |
David 'Digit' Turner | 74d7ace | 2011-02-02 13:21:03 +0100 | [diff] [blame] | 353 | |
| 354 | root = aconfig_node("", ""); |
| 355 | |
| 356 | if (skinName == NULL) |
| 357 | goto DEFAULT_SKIN; |
| 358 | |
| 359 | /* Support skin aliases like QVGA-H QVGA-P, etc... |
| 360 | But first we check if it's a directory that exist before applying |
| 361 | the alias */ |
| 362 | int checkAlias = 1; |
| 363 | |
| 364 | if (skinDirPath != NULL) { |
| 365 | bufprint(tmp, tmp+sizeof(tmp), "%s/%s", skinDirPath, skinName); |
| 366 | if (path_exists(tmp)) { |
| 367 | checkAlias = 0; |
| 368 | } else { |
| 369 | D("there is no '%s' skin in '%s'", skinName, skinDirPath); |
| 370 | } |
| 371 | } |
| 372 | |
| 373 | if (checkAlias) { |
| 374 | int nn; |
| 375 | |
| 376 | for (nn = 0; ; nn++ ) { |
| 377 | const char* skin_name = skin_aliases[nn].name; |
| 378 | const char* skin_alias = skin_aliases[nn].alias; |
| 379 | |
| 380 | if (!skin_name) |
| 381 | break; |
| 382 | |
| 383 | if (!strcasecmp( skin_name, skinName )) { |
| 384 | D("skin name '%s' aliased to '%s'", skinName, skin_alias); |
| 385 | skinName = skin_alias; |
| 386 | break; |
| 387 | } |
| 388 | } |
| 389 | } |
| 390 | |
| 391 | /* Magically support skins like "320x240" or "320x240x16" */ |
| 392 | if(isdigit(skinName[0])) { |
| 393 | char *x = strchr(skinName, 'x'); |
| 394 | if(x && isdigit(x[1])) { |
| 395 | int width = atoi(skinName); |
| 396 | int height = atoi(x+1); |
| 397 | int bpp = 16; |
| 398 | char* y = strchr(x+1, 'x'); |
| 399 | if (y && isdigit(y[1])) { |
| 400 | bpp = atoi(y+1); |
| 401 | } |
| 402 | snprintf(tmp, sizeof tmp, |
| 403 | "display {\n width %d\n height %d\n bpp %d}\n", |
| 404 | width, height,bpp); |
| 405 | aconfig_load(root, strdup(tmp)); |
| 406 | path = ":"; |
| 407 | D("found magic skin width=%d height=%d bpp=%d\n", width, height, bpp); |
| 408 | goto FOUND_SKIN; |
| 409 | } |
| 410 | } |
| 411 | |
| 412 | if (skinDirPath == NULL) { |
| 413 | derror("unknown skin name '%s'", skinName); |
| 414 | exit(1); |
| 415 | } |
| 416 | |
| 417 | snprintf(tmp, sizeof tmp, "%s/%s/layout", skinDirPath, skinName); |
| 418 | D("trying to load skin file '%s'", tmp); |
| 419 | |
| 420 | if(aconfig_load_file(root, tmp) < 0) { |
| 421 | dwarning("could not load skin file '%s', using built-in one\n", |
| 422 | tmp); |
| 423 | goto DEFAULT_SKIN; |
| 424 | } |
| 425 | |
| 426 | snprintf(tmp, sizeof tmp, "%s/%s/", skinDirPath, skinName); |
| 427 | path = tmp; |
| 428 | goto FOUND_SKIN; |
| 429 | |
| 430 | FOUND_SKIN: |
| 431 | /* the default network speed and latency can now be specified by the device skin */ |
| 432 | n = aconfig_find(root, "network"); |
| 433 | if (n != NULL) { |
| 434 | skin_network_speed = aconfig_str(n, "speed", 0); |
| 435 | skin_network_delay = aconfig_str(n, "delay", 0); |
| 436 | } |
| 437 | |
David 'Digit' Turner | 2507cab | 2011-02-10 16:29:17 +0100 | [diff] [blame] | 438 | /* extract framebuffer information from the skin. |
| 439 | * |
| 440 | * for version 1 of the skin format, they are in the top-level |
| 441 | * 'display' element. |
| 442 | * |
| 443 | * for version 2 of the skin format, they are under parts.device.display |
| 444 | */ |
| 445 | n = aconfig_find(root, "display"); |
| 446 | if (n == NULL) { |
| 447 | n = aconfig_find(root, "parts"); |
| 448 | if (n != NULL) { |
David 'Digit' Turner | 50142ee | 2011-03-25 16:18:58 +0100 | [diff] [blame] | 449 | n = aconfig_find(n, "device"); |
David 'Digit' Turner | 2507cab | 2011-02-10 16:29:17 +0100 | [diff] [blame] | 450 | if (n != NULL) { |
David 'Digit' Turner | 50142ee | 2011-03-25 16:18:58 +0100 | [diff] [blame] | 451 | n = aconfig_find(n, "display"); |
David 'Digit' Turner | 2507cab | 2011-02-10 16:29:17 +0100 | [diff] [blame] | 452 | } |
| 453 | } |
| 454 | } |
| 455 | |
| 456 | if (n != NULL) { |
| 457 | int width = aconfig_int(n, "width", hwConfig->hw_lcd_width); |
| 458 | int height = aconfig_int(n, "height", hwConfig->hw_lcd_height); |
| 459 | int depth = aconfig_int(n, "bpp", hwConfig->hw_lcd_depth); |
| 460 | |
| 461 | if (width > 0 && height > 0) { |
| 462 | /* The emulated framebuffer wants sizes that are multiples of 4 */ |
| 463 | if (((width|height) & 3) != 0) { |
| 464 | width = (width+3) & ~3; |
| 465 | height = (height+3) & ~3; |
| 466 | D("adjusting LCD dimensions to (%dx%dx)", width, height); |
| 467 | } |
| 468 | |
| 469 | /* only depth values of 16 and 32 are correct. 16 is the default. */ |
| 470 | if (depth != 32 && depth != 16) { |
| 471 | depth = 16; |
| 472 | D("adjusting LCD bit depth to %d", depth); |
| 473 | } |
| 474 | |
| 475 | hwConfig->hw_lcd_width = width; |
| 476 | hwConfig->hw_lcd_height = height; |
| 477 | hwConfig->hw_lcd_depth = depth; |
| 478 | } |
| 479 | else { |
| 480 | D("ignoring invalid skin LCD dimensions (%dx%dx%d)", |
| 481 | width, height, depth); |
| 482 | } |
| 483 | } |
| 484 | |
David 'Digit' Turner | 74d7ace | 2011-02-02 13:21:03 +0100 | [diff] [blame] | 485 | *skinConfig = root; |
| 486 | *skinPath = strdup(path); |
| 487 | return; |
| 488 | |
| 489 | DEFAULT_SKIN: |
| 490 | { |
| 491 | const unsigned char* layout_base; |
| 492 | size_t layout_size; |
| 493 | char* base; |
| 494 | |
| 495 | skinName = "<builtin>"; |
| 496 | |
| 497 | layout_base = android_resource_find( "layout", &layout_size ); |
| 498 | if (layout_base == NULL) { |
| 499 | fprintf(stderr, "Couldn't load builtin skin\n"); |
| 500 | exit(1); |
| 501 | } |
| 502 | base = malloc( layout_size+1 ); |
| 503 | memcpy( base, layout_base, layout_size ); |
| 504 | base[layout_size] = 0; |
| 505 | |
| 506 | D("parsing built-in skin layout file (%d bytes)", (int)layout_size); |
| 507 | aconfig_load(root, base); |
| 508 | path = ":"; |
| 509 | } |
| 510 | goto FOUND_SKIN; |
| 511 | } |
| 512 | |
| 513 | |
| 514 | void |
| 515 | init_sdl_ui(AConfig* skinConfig, |
| 516 | const char* skinPath, |
| 517 | AndroidOptions* opts) |
| 518 | { |
| 519 | int win_x, win_y, flags; |
David 'Digit' Turner | f845627 | 2011-02-02 12:34:14 +0100 | [diff] [blame] | 520 | |
| 521 | signal(SIGINT, SIG_DFL); |
| 522 | #ifndef _WIN32 |
| 523 | signal(SIGQUIT, SIG_DFL); |
| 524 | #endif |
| 525 | |
| 526 | /* we're not a game, so allow the screensaver to run */ |
| 527 | putenv("SDL_VIDEO_ALLOW_SCREENSAVER=1"); |
| 528 | |
| 529 | flags = SDL_INIT_NOPARACHUTE; |
| 530 | if (!opts->no_window) |
| 531 | flags |= SDL_INIT_VIDEO; |
| 532 | |
| 533 | if(SDL_Init(flags)){ |
| 534 | fprintf(stderr, "SDL init failure, reason is: %s\n", SDL_GetError() ); |
| 535 | exit(1); |
| 536 | } |
| 537 | |
| 538 | if (!opts->no_window) { |
| 539 | SDL_EnableUNICODE(!opts->raw_keys); |
| 540 | SDL_EnableKeyRepeat(0,0); |
| 541 | |
| 542 | sdl_set_window_icon(); |
| 543 | } |
| 544 | else |
| 545 | { |
| 546 | #ifndef _WIN32 |
| 547 | /* prevent SIGTTIN and SIGTTOUT from stopping us. this is necessary to be |
| 548 | * able to run the emulator in the background (e.g. "emulator &"). |
| 549 | * despite the fact that the emulator should not grab input or try to |
| 550 | * write to the output in normal cases, we're stopped on some systems |
| 551 | * (e.g. OS X) |
| 552 | */ |
| 553 | signal(SIGTTIN, SIG_IGN); |
| 554 | signal(SIGTTOU, SIG_IGN); |
| 555 | #endif |
| 556 | } |
| 557 | atexit(sdl_at_exit); |
| 558 | |
David 'Digit' Turner | 755811e | 2011-02-07 13:38:25 +0100 | [diff] [blame] | 559 | user_config_get_window_pos(&win_x, &win_y); |
David 'Digit' Turner | f845627 | 2011-02-02 12:34:14 +0100 | [diff] [blame] | 560 | |
David 'Digit' Turner | 74d7ace | 2011-02-02 13:21:03 +0100 | [diff] [blame] | 561 | if ( qemulator_init(qemulator_get(), skinConfig, skinPath, win_x, win_y, opts) < 0 ) { |
| 562 | fprintf(stderr, "### Error: could not load emulator skin from '%s'\n", skinPath); |
David 'Digit' Turner | f845627 | 2011-02-02 12:34:14 +0100 | [diff] [blame] | 563 | exit(1); |
| 564 | } |
| 565 | |
| 566 | android_skin_keycharmap = skin_keyboard_charmap_name(qemulator_get()->keyboard); |
| 567 | |
David 'Digit' Turner | f845627 | 2011-02-02 12:34:14 +0100 | [diff] [blame] | 568 | /* add an onion overlay image if needed */ |
| 569 | if (opts->onion) { |
| 570 | SkinImage* onion = skin_image_find_simple( opts->onion ); |
| 571 | int alpha, rotate; |
| 572 | |
| 573 | if ( opts->onion_alpha && 1 == sscanf( opts->onion_alpha, "%d", &alpha ) ) { |
| 574 | alpha = (256*alpha)/100; |
| 575 | } else |
| 576 | alpha = 128; |
| 577 | |
| 578 | if ( opts->onion_rotation && 1 == sscanf( opts->onion_rotation, "%d", &rotate ) ) { |
| 579 | rotate &= 3; |
| 580 | } else |
| 581 | rotate = SKIN_ROTATION_0; |
| 582 | |
| 583 | qemulator_get()->onion = onion; |
| 584 | qemulator_get()->onion_alpha = alpha; |
| 585 | qemulator_get()->onion_rotation = rotate; |
| 586 | } |
| 587 | } |
Vladimir Chtchetkine | 83ffd66 | 2011-02-11 12:40:59 -0800 | [diff] [blame] | 588 | |
| 589 | /* this function is used to perform auto-detection of the |
| 590 | * system directory in the case of a SDK installation. |
| 591 | * |
| 592 | * we want to deal with several historical usages, hence |
| 593 | * the slightly complicated logic. |
| 594 | * |
| 595 | * NOTE: the function returns the path to the directory |
| 596 | * containing 'fileName'. this is *not* the full |
| 597 | * path to 'fileName'. |
| 598 | */ |
| 599 | static char* |
| 600 | _getSdkImagePath( const char* fileName ) |
| 601 | { |
| 602 | char temp[MAX_PATH]; |
| 603 | char* p = temp; |
| 604 | char* end = p + sizeof(temp); |
| 605 | char* q; |
| 606 | char* app; |
| 607 | |
| 608 | static const char* const searchPaths[] = { |
| 609 | "", /* program's directory */ |
| 610 | "/lib/images", /* this is for SDK 1.0 */ |
| 611 | "/../platforms/android-1.1/images", /* this is for SDK 1.1 */ |
| 612 | NULL |
| 613 | }; |
| 614 | |
| 615 | app = bufprint_app_dir(temp, end); |
| 616 | if (app >= end) |
| 617 | return NULL; |
| 618 | |
| 619 | do { |
| 620 | int nn; |
| 621 | |
| 622 | /* first search a few well-known paths */ |
| 623 | for (nn = 0; searchPaths[nn] != NULL; nn++) { |
| 624 | p = bufprint(app, end, "%s", searchPaths[nn]); |
| 625 | q = bufprint(p, end, "/%s", fileName); |
| 626 | if (q < end && path_exists(temp)) { |
| 627 | *p = 0; |
| 628 | goto FOUND_IT; |
| 629 | } |
| 630 | } |
| 631 | |
| 632 | /* hmmm. let's assume that we are in a post-1.1 SDK |
| 633 | * scan ../platforms if it exists |
| 634 | */ |
| 635 | p = bufprint(app, end, "/../platforms"); |
| 636 | if (p < end) { |
| 637 | DirScanner* scanner = dirScanner_new(temp); |
| 638 | if (scanner != NULL) { |
| 639 | int found = 0; |
| 640 | const char* subdir; |
| 641 | |
| 642 | for (;;) { |
| 643 | subdir = dirScanner_next(scanner); |
| 644 | if (!subdir) break; |
| 645 | |
| 646 | q = bufprint(p, end, "/%s/images/%s", subdir, fileName); |
| 647 | if (q >= end || !path_exists(temp)) |
| 648 | continue; |
| 649 | |
| 650 | found = 1; |
| 651 | p = bufprint(p, end, "/%s/images", subdir); |
| 652 | break; |
| 653 | } |
| 654 | dirScanner_free(scanner); |
| 655 | if (found) |
| 656 | break; |
| 657 | } |
| 658 | } |
| 659 | |
| 660 | /* I'm out of ideas */ |
| 661 | return NULL; |
| 662 | |
| 663 | } while (0); |
| 664 | |
| 665 | FOUND_IT: |
| 666 | //D("image auto-detection: %s/%s", temp, fileName); |
| 667 | return android_strdup(temp); |
| 668 | } |
| 669 | |
| 670 | static char* |
| 671 | _getSdkImage( const char* path, const char* file ) |
| 672 | { |
| 673 | char temp[MAX_PATH]; |
| 674 | char *p = temp, *end = p + sizeof(temp); |
| 675 | |
| 676 | p = bufprint(temp, end, "%s/%s", path, file); |
| 677 | if (p >= end || !path_exists(temp)) |
| 678 | return NULL; |
| 679 | |
| 680 | return android_strdup(temp); |
| 681 | } |
| 682 | |
| 683 | static char* |
| 684 | _getSdkSystemImage( const char* path, const char* optionName, const char* file ) |
| 685 | { |
| 686 | char* image = _getSdkImage(path, file); |
| 687 | |
| 688 | if (image == NULL) { |
| 689 | derror("Your system directory is missing the '%s' image file.\n" |
| 690 | "Please specify one with the '%s <filepath>' option", |
| 691 | file, optionName); |
| 692 | exit(2); |
| 693 | } |
| 694 | return image; |
| 695 | } |
| 696 | |
David 'Digit' Turner | 25eb655 | 2011-02-25 15:07:11 +0100 | [diff] [blame] | 697 | void sanitizeOptions( AndroidOptions* opts ) |
Vladimir Chtchetkine | 83ffd66 | 2011-02-11 12:40:59 -0800 | [diff] [blame] | 698 | { |
Vladimir Chtchetkine | 83ffd66 | 2011-02-11 12:40:59 -0800 | [diff] [blame] | 699 | /* legacy support: we used to use -system <dir> and -image <file> |
| 700 | * instead of -sysdir <dir> and -system <file>, so handle this by checking |
| 701 | * whether the options point to directories or files. |
| 702 | */ |
| 703 | if (opts->image != NULL) { |
| 704 | if (opts->system != NULL) { |
| 705 | if (opts->sysdir != NULL) { |
| 706 | derror( "You can't use -sysdir, -system and -image at the same time.\n" |
| 707 | "You should probably use '-sysdir <path> -system <file>'.\n" ); |
| 708 | exit(2); |
| 709 | } |
| 710 | } |
| 711 | dwarning( "Please note that -image is obsolete and that -system is now used to point\n" |
| 712 | "to the system image. Next time, try using '-sysdir <path> -system <file>' instead.\n" ); |
| 713 | opts->sysdir = opts->system; |
| 714 | opts->system = opts->image; |
| 715 | opts->image = NULL; |
| 716 | } |
| 717 | else if (opts->system != NULL && path_is_dir(opts->system)) { |
| 718 | if (opts->sysdir != NULL) { |
| 719 | derror( "Option -system should now be followed by a file path, not a directory one.\n" |
| 720 | "Please use '-sysdir <path>' to point to the system directory.\n" ); |
| 721 | exit(1); |
| 722 | } |
| 723 | dwarning( "Please note that the -system option should now be used to point to the initial\n" |
| 724 | "system image (like the obsolete -image option). To point to the system directory\n" |
| 725 | "please now use '-sysdir <path>' instead.\n" ); |
| 726 | |
| 727 | opts->sysdir = opts->system; |
| 728 | opts->system = NULL; |
| 729 | } |
| 730 | |
David 'Digit' Turner | 25eb655 | 2011-02-25 15:07:11 +0100 | [diff] [blame] | 731 | if (opts->nojni) { |
| 732 | opts->no_jni = opts->nojni; |
| 733 | opts->nojni = 0; |
| 734 | } |
| 735 | |
| 736 | if (opts->nocache) { |
| 737 | opts->no_cache = opts->nocache; |
| 738 | opts->nocache = 0; |
| 739 | } |
| 740 | |
| 741 | if (opts->noaudio) { |
| 742 | opts->no_audio = opts->noaudio; |
| 743 | opts->noaudio = 0; |
| 744 | } |
| 745 | |
| 746 | if (opts->noskin) { |
| 747 | opts->no_skin = opts->noskin; |
| 748 | opts->noskin = 0; |
| 749 | } |
| 750 | |
| 751 | /* If -no-cache is used, ignore any -cache argument */ |
| 752 | if (opts->no_cache) { |
| 753 | opts->cache = 0; |
| 754 | } |
| 755 | |
| 756 | /* the purpose of -no-audio is to disable sound output from the emulator, |
| 757 | * not to disable Audio emulation. So simply force the 'none' backends */ |
| 758 | if (opts->no_audio) |
| 759 | opts->audio = "none"; |
| 760 | |
| 761 | /* we don't accept -skindir without -skin now |
| 762 | * to simplify the autoconfig stuff with virtual devices |
| 763 | */ |
| 764 | if (opts->no_skin) { |
| 765 | opts->skin = "320x480"; |
| 766 | opts->skindir = NULL; |
| 767 | } |
| 768 | |
| 769 | if (opts->skindir) { |
| 770 | if (!opts->skin) { |
| 771 | derror( "the -skindir <path> option requires a -skin <name> option"); |
| 772 | exit(1); |
| 773 | } |
| 774 | } |
David 'Digit' Turner | 0b01949 | 2011-03-01 14:02:42 +0100 | [diff] [blame] | 775 | |
| 776 | if (opts->bootchart) { |
| 777 | char* end; |
| 778 | int timeout = strtol(opts->bootchart, &end, 10); |
| 779 | if (timeout == 0) |
| 780 | opts->bootchart = NULL; |
| 781 | else if (timeout < 0 || timeout > 15*60) { |
| 782 | derror( "timeout specified for -bootchart option is invalid.\n" |
| 783 | "please use integers between 1 and 900\n"); |
| 784 | exit(1); |
| 785 | } |
| 786 | } |
David 'Digit' Turner | 25eb655 | 2011-02-25 15:07:11 +0100 | [diff] [blame] | 787 | } |
| 788 | |
| 789 | AvdInfo* createAVD(AndroidOptions* opts, int* inAndroidBuild) |
| 790 | { |
| 791 | AvdInfo* ret = NULL; |
| 792 | char tmp[MAX_PATH]; |
| 793 | char* tmpend = tmp + sizeof(tmp); |
| 794 | char* android_build_root = NULL; |
| 795 | char* android_build_out = NULL; |
| 796 | |
Vladimir Chtchetkine | 83ffd66 | 2011-02-11 12:40:59 -0800 | [diff] [blame] | 797 | /* If no AVD name was given, try to find the top of the |
| 798 | * Android build tree |
| 799 | */ |
| 800 | if (opts->avd == NULL) { |
| 801 | do { |
| 802 | char* out = getenv("ANDROID_PRODUCT_OUT"); |
| 803 | |
| 804 | if (out == NULL || out[0] == 0) |
| 805 | break; |
| 806 | |
| 807 | if (!path_exists(out)) { |
| 808 | derror("Can't access ANDROID_PRODUCT_OUT as '%s'\n" |
| 809 | "You need to build the Android system before launching the emulator", |
| 810 | out); |
| 811 | exit(2); |
| 812 | } |
| 813 | |
| 814 | android_build_root = path_parent( out, 4 ); |
| 815 | if (android_build_root == NULL || !path_exists(android_build_root)) { |
| 816 | derror("Can't find the Android build root from '%s'\n" |
| 817 | "Please check the definition of the ANDROID_PRODUCT_OUT variable.\n" |
| 818 | "It should point to your product-specific build output directory.\n", |
| 819 | out ); |
| 820 | exit(2); |
| 821 | } |
| 822 | android_build_out = out; |
| 823 | D( "found Android build root: %s", android_build_root ); |
| 824 | D( "found Android build out: %s", android_build_out ); |
| 825 | } while (0); |
| 826 | } |
| 827 | /* if no virtual device name is given, and we're not in the |
| 828 | * Android build system, we'll need to perform some auto-detection |
| 829 | * magic :-) |
| 830 | */ |
| 831 | if (opts->avd == NULL && !android_build_out) |
| 832 | { |
| 833 | char dataDirIsSystem = 0; |
| 834 | |
| 835 | if (!opts->sysdir) { |
| 836 | opts->sysdir = _getSdkImagePath("system.img"); |
| 837 | if (!opts->sysdir) { |
| 838 | derror( |
| 839 | "You did not specify a virtual device name, and the system\n" |
| 840 | "directory could not be found.\n\n" |
| 841 | "If you are an Android SDK user, please use '@<name>' or '-avd <name>'\n" |
| 842 | "to start a given virtual device (see -help-avd for details).\n\n" |
| 843 | |
| 844 | "Otherwise, follow the instructions in -help-disk-images to start the emulator\n" |
| 845 | ); |
| 846 | exit(2); |
| 847 | } |
| 848 | D("autoconfig: -sysdir %s", opts->sysdir); |
| 849 | } |
| 850 | |
| 851 | if (!opts->system) { |
| 852 | opts->system = _getSdkSystemImage(opts->sysdir, "-image", "system.img"); |
| 853 | D("autoconfig: -image %s", opts->image); |
| 854 | } |
| 855 | |
| 856 | if (!opts->kernel) { |
| 857 | opts->kernel = _getSdkSystemImage(opts->sysdir, "-kernel", "kernel-qemu"); |
| 858 | D("autoconfig: -kernel %s", opts->kernel); |
| 859 | } |
| 860 | |
| 861 | if (!opts->ramdisk) { |
| 862 | opts->ramdisk = _getSdkSystemImage(opts->sysdir, "-ramdisk", "ramdisk.img"); |
| 863 | D("autoconfig: -ramdisk %s", opts->ramdisk); |
| 864 | } |
| 865 | |
| 866 | /* if no data directory is specified, use the system directory */ |
| 867 | if (!opts->datadir) { |
| 868 | opts->datadir = android_strdup(opts->sysdir); |
| 869 | dataDirIsSystem = 1; |
| 870 | D("autoconfig: -datadir %s", opts->sysdir); |
| 871 | } |
| 872 | |
| 873 | if (!opts->data) { |
| 874 | /* check for userdata-qemu.img in the data directory */ |
| 875 | bufprint(tmp, tmpend, "%s/userdata-qemu.img", opts->datadir); |
| 876 | if (!path_exists(tmp)) { |
| 877 | derror( |
| 878 | "You did not provide the name of an Android Virtual Device\n" |
| 879 | "with the '-avd <name>' option. Read -help-avd for more information.\n\n" |
| 880 | |
| 881 | "If you *really* want to *NOT* run an AVD, consider using '-data <file>'\n" |
| 882 | "to specify a data partition image file (I hope you know what you're doing).\n" |
| 883 | ); |
| 884 | exit(2); |
| 885 | } |
| 886 | |
| 887 | opts->data = android_strdup(tmp); |
| 888 | D("autoconfig: -data %s", opts->data); |
| 889 | } |
| 890 | |
Vladimir Chtchetkine | 83ffd66 | 2011-02-11 12:40:59 -0800 | [diff] [blame] | 891 | if (!opts->snapstorage && opts->datadir) { |
| 892 | bufprint(tmp, tmpend, "%s/snapshots.img", opts->datadir); |
| 893 | if (path_exists(tmp)) { |
| 894 | opts->snapstorage = android_strdup(tmp); |
| 895 | D("autoconfig: -snapstorage %s", opts->snapstorage); |
| 896 | } |
| 897 | } |
Vladimir Chtchetkine | 83ffd66 | 2011-02-11 12:40:59 -0800 | [diff] [blame] | 898 | } |
| 899 | |
Vladimir Chtchetkine | 83ffd66 | 2011-02-11 12:40:59 -0800 | [diff] [blame] | 900 | /* setup the virtual device differently depending on whether |
| 901 | * we are in the Android build system or not |
| 902 | */ |
| 903 | if (opts->avd != NULL) |
| 904 | { |
| 905 | ret = avdInfo_new( opts->avd, android_avdParams ); |
| 906 | if (ret == NULL) { |
| 907 | /* an error message has already been printed */ |
| 908 | dprint("could not find virtual device named '%s'", opts->avd); |
| 909 | exit(1); |
| 910 | } |
| 911 | } |
| 912 | else |
| 913 | { |
| 914 | if (!android_build_out) { |
| 915 | android_build_out = android_build_root = opts->sysdir; |
| 916 | } |
| 917 | ret = avdInfo_newForAndroidBuild( |
| 918 | android_build_root, |
| 919 | android_build_out, |
| 920 | android_avdParams ); |
| 921 | |
| 922 | if(ret == NULL) { |
| 923 | D("could not start virtual device\n"); |
| 924 | exit(1); |
| 925 | } |
| 926 | } |
| 927 | |
| 928 | if (android_build_out) { |
| 929 | *inAndroidBuild = 1; |
| 930 | } else { |
| 931 | *inAndroidBuild = 0; |
| 932 | } |
| 933 | |
| 934 | return ret; |
| 935 | } |
| 936 | |
David 'Digit' Turner | bdb6f2d | 2011-02-23 15:57:25 +0100 | [diff] [blame] | 937 | |
| 938 | |
| 939 | |
| 940 | #ifdef CONFIG_STANDALONE_UI |
| 941 | |
| 942 | #include "android/protocol/core-connection.h" |
| 943 | #include "android/protocol/fb-updates-impl.h" |
| 944 | #include "android/protocol/user-events-proxy.h" |
| 945 | #include "android/protocol/core-commands-proxy.h" |
| 946 | #include "android/protocol/ui-commands-impl.h" |
| 947 | #include "android/protocol/attach-ui-impl.h" |
| 948 | |
| 949 | /* Emulator's core port. */ |
| 950 | int android_base_port = 0; |
| 951 | |
| 952 | // Base console port |
| 953 | #define CORE_BASE_PORT 5554 |
| 954 | |
| 955 | // Maximum number of core porocesses running simultaneously on a machine. |
| 956 | #define MAX_CORE_PROCS 16 |
| 957 | |
| 958 | // Socket timeout in millisec (set to 5 seconds) |
| 959 | #define CORE_PORT_TIMEOUT_MS 5000 |
| 960 | |
| 961 | #include "android/async-console.h" |
| 962 | |
| 963 | typedef struct { |
| 964 | LoopIo io[1]; |
| 965 | int port; |
| 966 | int ok; |
| 967 | AsyncConsoleConnector connector[1]; |
| 968 | } CoreConsole; |
| 969 | |
| 970 | static void |
| 971 | coreconsole_io_func(void* opaque, int fd, unsigned events) |
| 972 | { |
| 973 | CoreConsole* cc = opaque; |
| 974 | AsyncStatus status; |
David 'Digit' Turner | f9e333a | 2011-03-17 14:57:51 +0100 | [diff] [blame] | 975 | status = asyncConsoleConnector_run(cc->connector); |
David 'Digit' Turner | bdb6f2d | 2011-02-23 15:57:25 +0100 | [diff] [blame] | 976 | if (status == ASYNC_COMPLETE) { |
| 977 | cc->ok = 1; |
| 978 | } |
| 979 | } |
| 980 | |
| 981 | static void |
| 982 | coreconsole_init(CoreConsole* cc, const SockAddress* address, Looper* looper) |
| 983 | { |
| 984 | int fd = socket_create_inet(SOCKET_STREAM); |
| 985 | AsyncStatus status; |
| 986 | cc->port = sock_address_get_port(address); |
| 987 | cc->ok = 0; |
| 988 | loopIo_init(cc->io, looper, fd, coreconsole_io_func, cc); |
| 989 | if (fd >= 0) { |
| 990 | status = asyncConsoleConnector_connect(cc->connector, address, cc->io); |
| 991 | if (status == ASYNC_ERROR) { |
| 992 | cc->ok = 0; |
| 993 | } |
| 994 | } |
| 995 | } |
| 996 | |
| 997 | static void |
| 998 | coreconsole_done(CoreConsole* cc) |
| 999 | { |
| 1000 | socket_close(cc->io->fd); |
| 1001 | loopIo_done(cc->io); |
| 1002 | } |
| 1003 | |
| 1004 | /* List emulator core processes running on the given machine. |
| 1005 | * This routine is called from main() if -list-cores parameter is set in the |
| 1006 | * command line. |
| 1007 | * Param: |
| 1008 | * host Value passed with -list-core parameter. Must be either "localhost", or |
| 1009 | * an IP address of a machine where core processes must be enumerated. |
| 1010 | */ |
| 1011 | static void |
| 1012 | list_running_cores(const char* host) |
| 1013 | { |
| 1014 | Looper* looper; |
| 1015 | CoreConsole cores[MAX_CORE_PROCS]; |
| 1016 | SockAddress address; |
| 1017 | int nn, found; |
| 1018 | |
| 1019 | if (sock_address_init_resolve(&address, host, CORE_BASE_PORT, 0) < 0) { |
| 1020 | derror("Unable to resolve hostname %s: %s", host, errno_str); |
| 1021 | return; |
| 1022 | } |
| 1023 | |
| 1024 | looper = looper_newGeneric(); |
| 1025 | |
| 1026 | for (nn = 0; nn < MAX_CORE_PROCS; nn++) { |
| 1027 | int port = CORE_BASE_PORT + nn*2; |
| 1028 | sock_address_set_port(&address, port); |
| 1029 | coreconsole_init(&cores[nn], &address, looper); |
| 1030 | } |
| 1031 | |
| 1032 | looper_runWithTimeout(looper, CORE_PORT_TIMEOUT_MS*2); |
| 1033 | |
| 1034 | found = 0; |
| 1035 | for (nn = 0; nn < MAX_CORE_PROCS; nn++) { |
| 1036 | int port = CORE_BASE_PORT + nn*2; |
| 1037 | if (cores[nn].ok) { |
| 1038 | if (found == 0) { |
| 1039 | fprintf(stdout, "Running emulator core processes:\n"); |
| 1040 | } |
| 1041 | fprintf(stdout, "Emulator console port %d\n", port); |
| 1042 | found++; |
| 1043 | } |
| 1044 | coreconsole_done(&cores[nn]); |
| 1045 | } |
| 1046 | looper_free(looper); |
| 1047 | |
| 1048 | if (found == 0) { |
| 1049 | fprintf(stdout, "There were no running emulator core processes found on %s.\n", |
| 1050 | host); |
| 1051 | } |
| 1052 | } |
| 1053 | |
| 1054 | /* Attaches starting UI to a running core process. |
| 1055 | * This routine is called from main() when -attach-core parameter is set, |
| 1056 | * indicating that this UI instance should attach to a running core, rather than |
| 1057 | * start a new core process. |
| 1058 | * Param: |
| 1059 | * opts Android options containing non-NULL attach_core. |
| 1060 | * Return: |
| 1061 | * 0 on success, or -1 on failure. |
| 1062 | */ |
| 1063 | static int |
| 1064 | attach_to_core(AndroidOptions* opts) { |
| 1065 | int iter; |
| 1066 | SockAddress console_socket; |
| 1067 | SockAddress** sockaddr_list; |
| 1068 | QEmulator* emulator; |
| 1069 | |
| 1070 | // Parse attach_core param extracting the host name, and the port name. |
| 1071 | char* console_address = strdup(opts->attach_core); |
| 1072 | char* host_name = console_address; |
| 1073 | char* port_num = strchr(console_address, ':'); |
| 1074 | if (port_num == NULL) { |
| 1075 | // The host name is ommited, indicating the localhost |
| 1076 | host_name = "localhost"; |
| 1077 | port_num = console_address; |
| 1078 | } else if (port_num == console_address) { |
| 1079 | // Invalid. |
| 1080 | derror("Invalid value %s for -attach-core parameter\n", |
| 1081 | opts->attach_core); |
| 1082 | return -1; |
| 1083 | } else { |
| 1084 | *port_num = '\0'; |
| 1085 | port_num++; |
| 1086 | if (*port_num == '\0') { |
| 1087 | // Invalid. |
| 1088 | derror("Invalid value %s for -attach-core parameter\n", |
| 1089 | opts->attach_core); |
| 1090 | return -1; |
| 1091 | } |
| 1092 | } |
| 1093 | |
| 1094 | /* Create socket address list for the given address, and pull appropriate |
| 1095 | * address to use for connection. Note that we're fine copying that address |
| 1096 | * out of the list, since INET and IN6 will entirely fit into SockAddress |
| 1097 | * structure. */ |
| 1098 | sockaddr_list = |
| 1099 | sock_address_list_create(host_name, port_num, SOCKET_LIST_FORCE_INET); |
| 1100 | free(console_address); |
| 1101 | if (sockaddr_list == NULL) { |
| 1102 | derror("Unable to resolve address %s: %s\n", |
| 1103 | opts->attach_core, errno_str); |
| 1104 | return -1; |
| 1105 | } |
| 1106 | for (iter = 0; sockaddr_list[iter] != NULL; iter++) { |
| 1107 | if (sock_address_get_family(sockaddr_list[iter]) == SOCKET_INET || |
| 1108 | sock_address_get_family(sockaddr_list[iter]) == SOCKET_IN6) { |
| 1109 | memcpy(&console_socket, sockaddr_list[iter], sizeof(SockAddress)); |
| 1110 | break; |
| 1111 | } |
| 1112 | } |
| 1113 | if (sockaddr_list[iter] == NULL) { |
| 1114 | derror("Unable to resolve address %s. Note that 'port' parameter passed to -attach-core\n" |
| 1115 | "must be resolvable into an IP address.\n", opts->attach_core); |
| 1116 | sock_address_list_free(sockaddr_list); |
| 1117 | return -1; |
| 1118 | } |
| 1119 | sock_address_list_free(sockaddr_list); |
| 1120 | |
| 1121 | if (attachUiImpl_create(&console_socket)) { |
| 1122 | return -1; |
| 1123 | } |
| 1124 | |
| 1125 | // Save core's port, and set the title. |
| 1126 | android_base_port = sock_address_get_port(&console_socket); |
| 1127 | emulator = qemulator_get(); |
| 1128 | qemulator_set_title(emulator); |
| 1129 | |
| 1130 | return 0; |
| 1131 | } |
| 1132 | |
| 1133 | |
| 1134 | void handle_ui_options( AndroidOptions* opts ) |
| 1135 | { |
| 1136 | // Lets see if user just wants to list core process. |
| 1137 | if (opts->list_cores) { |
| 1138 | fprintf(stdout, "Enumerating running core processes.\n"); |
| 1139 | list_running_cores(opts->list_cores); |
| 1140 | exit(0); |
| 1141 | } |
| 1142 | } |
| 1143 | |
| 1144 | int attach_ui_to_core( AndroidOptions* opts ) |
| 1145 | { |
| 1146 | // Lets see if we're attaching to a running core process here. |
| 1147 | if (opts->attach_core) { |
| 1148 | if (attach_to_core(opts)) { |
| 1149 | return -1; |
| 1150 | } |
| 1151 | // Connect to the core's UI control services. |
| 1152 | if (coreCmdProxy_create(attachUiImpl_get_console_socket())) { |
| 1153 | return -1; |
| 1154 | } |
| 1155 | // Connect to the core's user events service. |
| 1156 | if (userEventsProxy_create(attachUiImpl_get_console_socket())) { |
| 1157 | return -1; |
| 1158 | } |
| 1159 | } |
| 1160 | return 0; |
| 1161 | } |
| 1162 | |
| 1163 | #else /* !CONFIG_STANDALONE_UI */ |
| 1164 | |
| 1165 | void handle_ui_options( AndroidOptions* opts ) |
| 1166 | { |
| 1167 | return; |
| 1168 | } |
| 1169 | |
| 1170 | int attach_ui_to_core( AndroidOptions* opts ) |
| 1171 | { |
| 1172 | return 0; |
| 1173 | } |
| 1174 | |
| 1175 | #endif /* CONFIG_STANDALONE_UI */ |