1/* Copyright (C) 2007-2008 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 "android/skin/file.h" 13#include "android/utils/path.h" 14#include "android/charmap.h" 15#include "android/utils/bufprint.h" 16#include "android/utils/system.h" 17#include "android/utils/debug.h" 18 19//#include "qemu-common.h" 20 21/** UTILITY ROUTINES 22 **/ 23static SkinImage* 24skin_image_find_in( const char* dirname, const char* filename ) 25{ 26 char buffer[1024]; 27 char* p = buffer; 28 char* end = p + sizeof(buffer); 29 30 p = bufprint( p, end, "%s" PATH_SEP "%s", dirname, filename ); 31 if (p >= end) 32 return SKIN_IMAGE_NONE; 33 34 return skin_image_find_simple(buffer); 35} 36 37/** SKIN BACKGROUND 38 **/ 39 40static void 41skin_background_done( SkinBackground* background ) 42{ 43 if (background->image) 44 skin_image_unref(&background->image); 45} 46 47static int 48skin_background_init_from( SkinBackground* background, 49 AConfig* node, 50 const char* basepath ) 51{ 52 const char* img = aconfig_str(node, "image", NULL); 53 int x = aconfig_int(node, "x", 0); 54 int y = aconfig_int(node, "y", 0); 55 56 background->valid = 0; 57 58 if (img == NULL) /* no background */ 59 return -1; 60 61 background->image = skin_image_find_in( basepath, img ); 62 if (background->image == SKIN_IMAGE_NONE) { 63 background->image = NULL; 64 return -1; 65 } 66 67 background->rect.pos.x = x; 68 background->rect.pos.y = y; 69 background->rect.size.w = skin_image_w( background->image ); 70 background->rect.size.h = skin_image_h( background->image ); 71 72 background->valid = 1; 73 74 return 0; 75} 76 77/** SKIN DISPLAY 78 **/ 79 80static void 81skin_display_done( SkinDisplay* display ) 82{ 83 qframebuffer_done( display->qfbuff ); 84} 85 86static int 87skin_display_init_from( SkinDisplay* display, AConfig* node ) 88{ 89 display->rect.pos.x = aconfig_int(node, "x", 0); 90 display->rect.pos.y = aconfig_int(node, "y", 0); 91 display->rect.size.w = aconfig_int(node, "width", 0); 92 display->rect.size.h = aconfig_int(node, "height", 0); 93 display->rotation = aconfig_unsigned(node, "rotation", SKIN_ROTATION_0); 94 display->bpp = aconfig_int(node, "bpp", 16); 95 96 display->valid = ( display->rect.size.w > 0 && display->rect.size.h > 0 ); 97 98 if (display->valid) { 99 SkinRect r; 100 skin_rect_rotate( &r, &display->rect, -display->rotation ); 101 qframebuffer_init( display->qfbuff, 102 r.size.w, 103 r.size.h, 104 0, 105 display->bpp == 32 ? QFRAME_BUFFER_RGBX_8888 106 : QFRAME_BUFFER_RGB565 ); 107 108 qframebuffer_fifo_add( display->qfbuff ); 109 } 110 return display->valid ? 0 : -1; 111} 112 113/** SKIN BUTTON 114 **/ 115 116typedef struct 117{ 118 const char* name; 119 AndroidKeyCode code; 120} KeyInfo; 121 122static KeyInfo _keyinfo_table[] = { 123 { "dpad-up", kKeyCodeDpadUp }, 124 { "dpad-down", kKeyCodeDpadDown }, 125 { "dpad-left", kKeyCodeDpadLeft }, 126 { "dpad-right", kKeyCodeDpadRight }, 127 { "dpad-center", kKeyCodeDpadCenter }, 128 { "soft-left", kKeyCodeSoftLeft }, 129 { "soft-right", kKeyCodeSoftRight }, 130 { "search", kKeyCodeSearch }, 131 { "camera", kKeyCodeCamera }, 132 { "volume-up", kKeyCodeVolumeUp }, 133 { "volume-down", kKeyCodeVolumeDown }, 134 { "power", kKeyCodePower }, 135 { "home", kKeyCodeHome }, 136 { "back", kKeyCodeBack }, 137 { "del", kKeyCodeDel }, 138 { "0", kKeyCode0 }, 139 { "1", kKeyCode1 }, 140 { "2", kKeyCode2 }, 141 { "3", kKeyCode3 }, 142 { "4", kKeyCode4 }, 143 { "5", kKeyCode5 }, 144 { "6", kKeyCode6 }, 145 { "7", kKeyCode7 }, 146 { "8", kKeyCode8 }, 147 { "9", kKeyCode9 }, 148 { "star", kKeyCodeStar }, 149 { "pound", kKeyCodePound }, 150 { "phone-dial", kKeyCodeCall }, 151 { "phone-hangup", kKeyCodeEndCall }, 152 { "q", kKeyCodeQ }, 153 { "w", kKeyCodeW }, 154 { "e", kKeyCodeE }, 155 { "r", kKeyCodeR }, 156 { "t", kKeyCodeT }, 157 { "y", kKeyCodeY }, 158 { "u", kKeyCodeU }, 159 { "i", kKeyCodeI }, 160 { "o", kKeyCodeO }, 161 { "p", kKeyCodeP }, 162 { "a", kKeyCodeA }, 163 { "s", kKeyCodeS }, 164 { "d", kKeyCodeD }, 165 { "f", kKeyCodeF }, 166 { "g", kKeyCodeG }, 167 { "h", kKeyCodeH }, 168 { "j", kKeyCodeJ }, 169 { "k", kKeyCodeK }, 170 { "l", kKeyCodeL }, 171 { "DEL", kKeyCodeDel }, 172 { "z", kKeyCodeZ }, 173 { "x", kKeyCodeX }, 174 { "c", kKeyCodeC }, 175 { "v", kKeyCodeV }, 176 { "b", kKeyCodeB }, 177 { "n", kKeyCodeN }, 178 { "m", kKeyCodeM }, 179 { "COMMA", kKeyCodeComma }, 180 { "PERIOD", kKeyCodePeriod }, 181 { "ENTER", kKeyCodeNewline }, 182 { "AT", kKeyCodeAt }, 183 { "SPACE", kKeyCodeSpace }, 184 { "SLASH", kKeyCodeSlash }, 185 { "CAP", kKeyCodeCapLeft }, 186 { "SYM", kKeyCodeSym }, 187 { "ALT", kKeyCodeAltLeft }, 188 { "ALT2", kKeyCodeAltRight }, 189 { "CAP2", kKeyCodeCapRight }, 190 { "tv", kKeyCodeTV }, 191 { "epg", kKeyCodeEPG }, 192 { "dvr", kKeyCodeDVR }, 193 { "prev", kKeyCodePrevious }, 194 { "next", kKeyCodeNext }, 195 { "play", kKeyCodePlay }, 196 { "pause", kKeyCodePause }, 197 { "stop", kKeyCodeStop }, 198 { "rev", kKeyCodeRewind }, 199 { "ffwd", kKeyCodeFastForward }, 200 { "bookmarks", kKeyCodeBookmarks }, 201 { "window", kKeyCodeCycleWindows }, 202 { "channel-up", kKeyCodeChannelUp }, 203 { "channel-down", kKeyCodeChannelDown }, 204 { 0, 0 }, 205}; 206 207static unsigned 208keyinfo_lookup_code(const char *name) 209{ 210 KeyInfo *ki = _keyinfo_table; 211 while(ki->name) { 212 if(!strcmp(name, ki->name)) 213 return ki->code; 214 ki++; 215 } 216 return 0; 217} 218 219 220static void 221skin_button_free( SkinButton* button ) 222{ 223 if (button) { 224 skin_image_unref( &button->image ); 225 AFREE(button); 226 } 227} 228 229static SkinButton* 230skin_button_create_from( AConfig* node, const char* basepath ) 231{ 232 SkinButton* button; 233 ANEW0(button); 234 if (button) { 235 const char* img = aconfig_str(node, "image", NULL); 236 int x = aconfig_int(node, "x", 0); 237 int y = aconfig_int(node, "y", 0); 238 239 button->name = node->name; 240 button->rect.pos.x = x; 241 button->rect.pos.y = y; 242 243 if (img != NULL) 244 button->image = skin_image_find_in( basepath, img ); 245 246 if (button->image == SKIN_IMAGE_NONE) { 247 skin_button_free(button); 248 return NULL; 249 } 250 251 button->rect.size.w = skin_image_w( button->image ); 252 button->rect.size.h = skin_image_h( button->image ); 253 254 button->keycode = keyinfo_lookup_code( button->name ); 255 if (button->keycode == 0) { 256 dprint( "Warning: skin file button uses unknown key name '%s'", button->name ); 257 } 258 } 259 return button; 260} 261 262/** SKIN PART 263 **/ 264 265static void 266skin_part_free( SkinPart* part ) 267{ 268 if (part) { 269 skin_background_done( part->background ); 270 skin_display_done( part->display ); 271 272 SKIN_PART_LOOP_BUTTONS(part,button) 273 skin_button_free(button); 274 SKIN_PART_LOOP_END 275 part->buttons = NULL; 276 AFREE(part); 277 } 278} 279 280static SkinLocation* 281skin_location_create_from_v2( AConfig* node, SkinPart* parts ) 282{ 283 const char* partname = aconfig_str(node, "name", NULL); 284 int x = aconfig_int(node, "x", 0); 285 int y = aconfig_int(node, "y", 0); 286 SkinRotation rot = aconfig_int(node, "rotation", SKIN_ROTATION_0); 287 SkinPart* part; 288 SkinLocation* location; 289 290 if (partname == NULL) { 291 dprint( "### WARNING: ignoring part location without 'name' element" ); 292 return NULL; 293 } 294 295 for (part = parts; part; part = part->next) 296 if (!strcmp(part->name, partname)) 297 break; 298 299 if (part == NULL) { 300 dprint( "### WARNING: ignoring part location with unknown name '%s'", partname ); 301 return NULL; 302 } 303 304 ANEW0(location); 305 location->part = part; 306 location->anchor.x = x; 307 location->anchor.y = y; 308 location->rotation = rot; 309 310 return location; 311} 312 313static SkinPart* 314skin_part_create_from_v1( AConfig* root, const char* basepath ) 315{ 316 SkinPart* part; 317 AConfig* node; 318 SkinBox box; 319 320 ANEW0(part); 321 part->name = root->name; 322 323 node = aconfig_find(root, "background"); 324 if (node) 325 skin_background_init_from(part->background, node, basepath); 326 327 node = aconfig_find(root, "display"); 328 if (node) 329 skin_display_init_from(part->display, node); 330 331 node = aconfig_find(root, "button"); 332 if (node) { 333 for (node = node->first_child; node != NULL; node = node->next) 334 { 335 SkinButton* button = skin_button_create_from(node, basepath); 336 337 if (button != NULL) { 338 button->next = part->buttons; 339 part->buttons = button; 340 } 341 } 342 } 343 344 skin_box_minmax_init( &box ); 345 346 if (part->background->valid) 347 skin_box_minmax_update( &box, &part->background->rect ); 348 349 if (part->display->valid) 350 skin_box_minmax_update( &box, &part->display->rect ); 351 352 SKIN_PART_LOOP_BUTTONS(part, button) 353 skin_box_minmax_update( &box, &button->rect ); 354 SKIN_PART_LOOP_END 355 356 if ( !skin_box_minmax_to_rect( &box, &part->rect ) ) { 357 skin_part_free(part); 358 part = NULL; 359 } 360 361 return part; 362} 363 364static SkinPart* 365skin_part_create_from_v2( AConfig* root, const char* basepath ) 366{ 367 SkinPart* part; 368 AConfig* node; 369 SkinBox box; 370 371 ANEW0(part); 372 part->name = root->name; 373 374 node = aconfig_find(root, "background"); 375 if (node) 376 skin_background_init_from(part->background, node, basepath); 377 378 node = aconfig_find(root, "display"); 379 if (node) 380 skin_display_init_from(part->display, node); 381 382 node = aconfig_find(root, "buttons"); 383 if (node) { 384 for (node = node->first_child; node != NULL; node = node->next) 385 { 386 SkinButton* button = skin_button_create_from(node, basepath); 387 388 if (button != NULL) { 389 button->next = part->buttons; 390 part->buttons = button; 391 } 392 } 393 } 394 395 skin_box_minmax_init( &box ); 396 397 if (part->background->valid) 398 skin_box_minmax_update( &box, &part->background->rect ); 399 400 if (part->display->valid) 401 skin_box_minmax_update( &box, &part->display->rect ); 402 403 SKIN_PART_LOOP_BUTTONS(part, button) 404 skin_box_minmax_update( &box, &button->rect ); 405 SKIN_PART_LOOP_END 406 407 if ( !skin_box_minmax_to_rect( &box, &part->rect ) ) { 408 skin_part_free(part); 409 part = NULL; 410 } 411 return part; 412} 413 414/** SKIN LAYOUT 415 **/ 416 417static void 418skin_layout_free( SkinLayout* layout ) 419{ 420 if (layout) { 421 SKIN_LAYOUT_LOOP_LOCS(layout,loc) 422 AFREE(loc); 423 SKIN_LAYOUT_LOOP_END 424 layout->locations = NULL; 425 if (layout->onion_image) { 426 skin_image_unref( &layout->onion_image ); 427 } 428 AFREE(layout); 429 } 430} 431 432SkinDisplay* 433skin_layout_get_display( SkinLayout* layout ) 434{ 435 SKIN_LAYOUT_LOOP_LOCS(layout,loc) 436 SkinPart* part = loc->part; 437 if (part->display->valid) { 438 return part->display; 439 } 440 SKIN_LAYOUT_LOOP_END 441 return NULL; 442} 443 444SkinRotation 445skin_layout_get_dpad_rotation( SkinLayout* layout ) 446{ 447 if (layout->has_dpad_rotation) 448 return layout->dpad_rotation; 449 450 SKIN_LAYOUT_LOOP_LOCS(layout, loc) 451 SkinPart* part = loc->part; 452 SKIN_PART_LOOP_BUTTONS(part,button) 453 if (button->keycode == kKeyCodeDpadUp) 454 return loc->rotation; 455 SKIN_PART_LOOP_END 456 SKIN_LAYOUT_LOOP_END 457 458 return SKIN_ROTATION_0; 459} 460 461 462static int 463skin_layout_event_decode( const char* event, int *ptype, int *pcode, int *pvalue ) 464{ 465 typedef struct { 466 const char* name; 467 int value; 468 } EventName; 469 470 static const EventName _event_names[] = { 471 { "EV_SW", 0x05 }, 472 { NULL, 0 }, 473 }; 474 475 const char* x = strchr(event, ':'); 476 const char* y = NULL; 477 const EventName* ev = _event_names; 478 479 if (x != NULL) 480 y = strchr(x+1, ':'); 481 482 if (x == NULL || y == NULL) { 483 dprint( "### WARNING: invalid skin layout event format: '%s', should be '<TYPE>:<CODE>:<VALUE>'", event ); 484 return -1; 485 } 486 487 for ( ; ev->name != NULL; ev++ ) 488 if (!memcmp( event, ev->name, x - event ) && ev->name[x-event] == 0) 489 break; 490 491 if (!ev->name) { 492 dprint( "### WARNING: unrecognized skin layout event name: %.*s", x-event, event ); 493 return -1; 494 } 495 496 *ptype = ev->value; 497 *pcode = strtol(x+1, NULL, 0); 498 *pvalue = strtol(y+1, NULL, 0); 499 return 0; 500} 501 502static SkinLayout* 503skin_layout_create_from_v2( AConfig* root, SkinPart* parts, const char* basepath ) 504{ 505 SkinLayout* layout; 506 int width, height; 507 SkinLocation** ptail; 508 AConfig* node; 509 510 ANEW0(layout); 511 512 width = aconfig_int( root, "width", 400 ); 513 height = aconfig_int( root, "height", 400 ); 514 515 node = aconfig_find( root, "event" ); 516 if (node != NULL) { 517 skin_layout_event_decode( node->value, 518 &layout->event_type, 519 &layout->event_code, 520 &layout->event_value ); 521 } else { 522 layout->event_type = 0x05; /* close keyboard by default */ 523 layout->event_code = 0; 524 layout->event_value = 1; 525 } 526 527 layout->name = root->name; 528 layout->color = aconfig_unsigned( root, "color", 0x808080 ) | 0xff000000; 529 ptail = &layout->locations; 530 531 node = aconfig_find( root, "dpad-rotation" ); 532 if (node != NULL) { 533 layout->dpad_rotation = aconfig_int( root, "dpad-rotation", 0 ); 534 layout->has_dpad_rotation = 1; 535 } 536 537 node = aconfig_find( root, "onion" ); 538 if (node != NULL) { 539 const char* img = aconfig_str(node, "image", NULL); 540 layout->onion_image = skin_image_find_in( basepath, img ); 541 if (layout->onion_image == SKIN_IMAGE_NONE) { 542 layout->onion_image = NULL; 543 } 544 // In layout file, alpha is specified in range 0-100. Convert to 545 // internal range 0-256 with default=128. 546 int alpha = aconfig_int( node, "alpha", 50 ); 547 layout->onion_alpha = (256*alpha)/100; 548 layout->onion_rotation = aconfig_int( node, "rotation", 0 ); 549 } 550 551 for (node = root->first_child; node; node = node->next) 552 { 553 if (!memcmp(node->name, "part", 4)) { 554 SkinLocation* location = skin_location_create_from_v2( node, parts ); 555 if (location == NULL) { 556 continue; 557 } 558 *ptail = location; 559 ptail = &location->next; 560 } 561 } 562 563 if (layout->locations == NULL) 564 goto Fail; 565 566 layout->size.w = width; 567 layout->size.h = height; 568 569 return layout; 570 571Fail: 572 skin_layout_free(layout); 573 return NULL; 574} 575 576/** SKIN FILE 577 **/ 578 579static int 580skin_file_load_from_v1( SkinFile* file, AConfig* aconfig, const char* basepath ) 581{ 582 SkinPart* part; 583 SkinLayout* layout; 584 SkinLayout** ptail = &file->layouts; 585 SkinLocation* location; 586 int nn; 587 588 file->parts = part = skin_part_create_from_v1( aconfig, basepath ); 589 if (part == NULL) 590 return -1; 591 592 for (nn = 0; nn < 2; nn++) 593 { 594 ANEW0(layout); 595 596 layout->color = 0xff808080; 597 598 ANEW0(location); 599 600 layout->event_type = 0x05; /* close keyboard by default */ 601 layout->event_code = 0; 602 layout->event_value = 1; 603 604 location->part = part; 605 switch (nn) { 606 case 0: 607 location->anchor.x = 0; 608 location->anchor.y = 0; 609 location->rotation = SKIN_ROTATION_0; 610 layout->size = part->rect.size; 611 break; 612 613#if 0 614 case 1: 615 location->anchor.x = part->rect.size.h; 616 location->anchor.y = 0; 617 location->rotation = SKIN_ROTATION_90; 618 layout->size.w = part->rect.size.h; 619 layout->size.h = part->rect.size.w; 620 layout->event_value = 0; 621 break; 622 623 case 2: 624 location->anchor.x = part->rect.size.w; 625 location->anchor.y = part->rect.size.h; 626 location->rotation = SKIN_ROTATION_180; 627 layout->size = part->rect.size; 628 break; 629#endif 630 default: 631 location->anchor.x = 0; 632 location->anchor.y = part->rect.size.w; 633 location->rotation = SKIN_ROTATION_270; 634 layout->size.w = part->rect.size.h; 635 layout->size.h = part->rect.size.w; 636 layout->event_value = 0; 637 break; 638 } 639 layout->locations = location; 640 641 *ptail = layout; 642 ptail = &layout->next; 643 } 644 file->version = 1; 645 return 0; 646} 647 648static int 649skin_file_load_from_v2( SkinFile* file, AConfig* aconfig, const char* basepath ) 650{ 651 AConfig* node; 652 653 /* first, load all parts */ 654 node = aconfig_find(aconfig, "parts"); 655 if (node == NULL) 656 return -1; 657 else 658 { 659 SkinPart** ptail = &file->parts; 660 for (node = node->first_child; node != NULL; node = node->next) 661 { 662 SkinPart* part = skin_part_create_from_v2( node, basepath ); 663 if (part == NULL) { 664 dprint( "## WARNING: can't load part '%s' from skin\n", node->name ? "<NULL>" : node->name ); 665 continue; 666 } 667 part->next = NULL; 668 *ptail = part; 669 ptail = &part->next; 670 } 671 } 672 673 if (file->parts == NULL) 674 return -1; 675 676 /* then load all layouts */ 677 node = aconfig_find(aconfig, "layouts"); 678 if (node == NULL) 679 return -1; 680 else 681 { 682 SkinLayout** ptail = &file->layouts; 683 for (node = node->first_child; node != NULL; node = node->next) 684 { 685 SkinLayout* layout = skin_layout_create_from_v2( node, file->parts, basepath ); 686 if (layout == NULL) { 687 dprint( "## WARNING: ignoring layout in skin file" ); 688 continue; 689 } 690 *ptail = layout; 691 layout->next = NULL; 692 ptail = &layout->next; 693 } 694 } 695 if (file->layouts == NULL) 696 return -1; 697 698 file->version = 2; 699 return 0; 700} 701 702SkinFile* 703skin_file_create_from_aconfig( AConfig* aconfig, const char* basepath ) 704{ 705 SkinFile* file; 706 707 ANEW0(file); 708 709 if ( aconfig_find(aconfig, "parts") != NULL) { 710 if (skin_file_load_from_v2( file, aconfig, basepath ) < 0) { 711 goto BAD_FILE; 712 } 713 file->version = aconfig_int(aconfig, "version", 2); 714 /* The file version must be 1 or higher */ 715 if (file->version <= 0) { 716 dprint( "## WARNING: invalid skin version: %d", file->version); 717 goto BAD_FILE; 718 } 719 } 720 else { 721 if (skin_file_load_from_v1( file, aconfig, basepath ) < 0) { 722 goto BAD_FILE; 723 } 724 file->version = 1; 725 } 726 return file; 727 728BAD_FILE: 729 skin_file_free( file ); 730 return NULL; 731} 732 733void 734skin_file_free( SkinFile* file ) 735{ 736 if (file) { 737 SKIN_FILE_LOOP_LAYOUTS(file,layout) 738 skin_layout_free(layout); 739 SKIN_FILE_LOOP_END_LAYOUTS 740 file->layouts = NULL; 741 742 SKIN_FILE_LOOP_PARTS(file,part) 743 skin_part_free(part); 744 SKIN_FILE_LOOP_END_PARTS 745 file->parts = NULL; 746 747 AFREE(file); 748 } 749} 750