/* Copyright (C) 2007-2008 The Android Open Source Project ** ** This software is licensed under the terms of the GNU General Public ** License version 2, as published by the Free Software Foundation, and ** may be copied, distributed, and modified under those terms. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. */ #include "android/skin/file.h" #include "android/utils/path.h" #include "android/charmap.h" #include "android/utils/bufprint.h" #include "android/utils/system.h" #include "android/utils/debug.h" //#include "qemu-common.h" /** UTILITY ROUTINES **/ static SkinImage* skin_image_find_in( const char* dirname, const char* filename ) { char buffer[1024]; char* p = buffer; char* end = p + sizeof(buffer); p = bufprint( p, end, "%s" PATH_SEP "%s", dirname, filename ); if (p >= end) return SKIN_IMAGE_NONE; return skin_image_find_simple(buffer); } /** SKIN BACKGROUND **/ static void skin_background_done( SkinBackground* background ) { if (background->image) skin_image_unref(&background->image); } static int skin_background_init_from( SkinBackground* background, AConfig* node, const char* basepath ) { const char* img = aconfig_str(node, "image", NULL); int x = aconfig_int(node, "x", 0); int y = aconfig_int(node, "y", 0); background->valid = 0; if (img == NULL) /* no background */ return -1; background->image = skin_image_find_in( basepath, img ); if (background->image == SKIN_IMAGE_NONE) { background->image = NULL; return -1; } background->rect.pos.x = x; background->rect.pos.y = y; background->rect.size.w = skin_image_w( background->image ); background->rect.size.h = skin_image_h( background->image ); background->valid = 1; return 0; } /** SKIN DISPLAY **/ static void skin_display_done( SkinDisplay* display ) { qframebuffer_done( display->qfbuff ); } static int skin_display_init_from( SkinDisplay* display, AConfig* node ) { display->rect.pos.x = aconfig_int(node, "x", 0); display->rect.pos.y = aconfig_int(node, "y", 0); display->rect.size.w = aconfig_int(node, "width", 0); display->rect.size.h = aconfig_int(node, "height", 0); display->rotation = aconfig_unsigned(node, "rotation", SKIN_ROTATION_0); display->valid = ( display->rect.size.w > 0 && display->rect.size.h > 0 ); if (display->valid) { SkinRect r; skin_rect_rotate( &r, &display->rect, -display->rotation ); qframebuffer_init( display->qfbuff, r.size.w, r.size.h, 0, QFRAME_BUFFER_RGB565 ); qframebuffer_fifo_add( display->qfbuff ); } return display->valid ? 0 : -1; } /** SKIN BUTTON **/ typedef struct { const char* name; AndroidKeyCode code; } KeyInfo; static KeyInfo _keyinfo_table[] = { { "dpad-up", kKeyCodeDpadUp }, { "dpad-down", kKeyCodeDpadDown }, { "dpad-left", kKeyCodeDpadLeft }, { "dpad-right", kKeyCodeDpadRight }, { "dpad-center", kKeyCodeDpadCenter }, { "soft-left", kKeyCodeSoftLeft }, { "soft-right", kKeyCodeSoftRight }, { "search", kKeyCodeSearch }, { "volume-up", kKeyCodeVolumeUp }, { "volume-down", kKeyCodeVolumeDown }, { "power", kKeyCodePower }, { "home", kKeyCodeHome }, { "back", kKeyCodeBack }, { "del", kKeyCodeDel }, { "0", kKeyCode0 }, { "1", kKeyCode1 }, { "2", kKeyCode2 }, { "3", kKeyCode3 }, { "4", kKeyCode4 }, { "5", kKeyCode5 }, { "6", kKeyCode6 }, { "7", kKeyCode7 }, { "8", kKeyCode8 }, { "9", kKeyCode9 }, { "star", kKeyCodeStar }, { "pound", kKeyCodePound }, { "phone-dial", kKeyCodeCall }, { "phone-hangup", kKeyCodeEndCall }, { "q", kKeyCodeQ }, { "w", kKeyCodeW }, { "e", kKeyCodeE }, { "r", kKeyCodeR }, { "t", kKeyCodeT }, { "y", kKeyCodeY }, { "u", kKeyCodeU }, { "i", kKeyCodeI }, { "o", kKeyCodeO }, { "p", kKeyCodeP }, { "a", kKeyCodeA }, { "s", kKeyCodeS }, { "d", kKeyCodeD }, { "f", kKeyCodeF }, { "g", kKeyCodeG }, { "h", kKeyCodeH }, { "j", kKeyCodeJ }, { "k", kKeyCodeK }, { "l", kKeyCodeL }, { "DEL", kKeyCodeDel }, { "z", kKeyCodeZ }, { "x", kKeyCodeX }, { "c", kKeyCodeC }, { "v", kKeyCodeV }, { "b", kKeyCodeB }, { "n", kKeyCodeN }, { "m", kKeyCodeM }, { "COMMA", kKeyCodeComma }, { "PERIOD", kKeyCodePeriod }, { "ENTER", kKeyCodeNewline }, { "AT", kKeyCodeAt }, { "SPACE", kKeyCodeSpace }, { "SLASH", kKeyCodeSlash }, { "CAP", kKeyCodeCapLeft }, { "SYM", kKeyCodeSym }, { "ALT", kKeyCodeAltLeft }, { "ALT2", kKeyCodeAltRight }, { "CAP2", kKeyCodeCapRight }, { 0, 0 }, }; static unsigned keyinfo_lookup_code(const char *name) { KeyInfo *ki = _keyinfo_table; while(ki->name) { if(!strcmp(name, ki->name)) return ki->code; ki++; } return 0; } static void skin_button_free( SkinButton* button ) { if (button) { skin_image_unref( &button->image ); AFREE(button); } } static SkinButton* skin_button_create_from( AConfig* node, const char* basepath ) { SkinButton* button; ANEW0(button); if (button) { const char* img = aconfig_str(node, "image", NULL); int x = aconfig_int(node, "x", 0); int y = aconfig_int(node, "y", 0); button->name = node->name; button->rect.pos.x = x; button->rect.pos.y = y; if (img != NULL) button->image = skin_image_find_in( basepath, img ); if (button->image == SKIN_IMAGE_NONE) { skin_button_free(button); return NULL; } button->rect.size.w = skin_image_w( button->image ); button->rect.size.h = skin_image_h( button->image ); button->keycode = keyinfo_lookup_code( button->name ); if (button->keycode == 0) { dprint( "Warning: skin file button uses unknown key name '%s'", button->name ); } } return button; } /** SKIN PART **/ static void skin_part_free( SkinPart* part ) { if (part) { skin_background_done( part->background ); skin_display_done( part->display ); SKIN_PART_LOOP_BUTTONS(part,button) skin_button_free(button); SKIN_PART_LOOP_END part->buttons = NULL; AFREE(part); } } static SkinLocation* skin_location_create_from_v2( AConfig* node, SkinPart* parts ) { const char* partname = aconfig_str(node, "name", NULL); int x = aconfig_int(node, "x", 0); int y = aconfig_int(node, "y", 0); SkinRotation rot = aconfig_int(node, "rotation", SKIN_ROTATION_0); SkinPart* part; SkinLocation* location; if (partname == NULL) { dprint( "### WARNING: ignoring part location without 'name' element" ); return NULL; } for (part = parts; part; part = part->next) if (!strcmp(part->name, partname)) break; if (part == NULL) { dprint( "### WARNING: ignoring part location with unknown name '%s'", partname ); return NULL; } ANEW0(location); location->part = part; location->anchor.x = x; location->anchor.y = y; location->rotation = rot; return location; } static SkinPart* skin_part_create_from_v1( AConfig* root, const char* basepath ) { SkinPart* part; AConfig* node; SkinBox box; ANEW0(part); part->name = root->name; node = aconfig_find(root, "background"); if (node) skin_background_init_from(part->background, node, basepath); node = aconfig_find(root, "display"); if (node) skin_display_init_from(part->display, node); node = aconfig_find(root, "button"); if (node) { for (node = node->first_child; node != NULL; node = node->next) { SkinButton* button = skin_button_create_from(node, basepath); if (button != NULL) { button->next = part->buttons; part->buttons = button; } } } skin_box_minmax_init( &box ); if (part->background->valid) skin_box_minmax_update( &box, &part->background->rect ); if (part->display->valid) skin_box_minmax_update( &box, &part->display->rect ); SKIN_PART_LOOP_BUTTONS(part, button) skin_box_minmax_update( &box, &button->rect ); SKIN_PART_LOOP_END if ( !skin_box_minmax_to_rect( &box, &part->rect ) ) { skin_part_free(part); part = NULL; } return part; } static SkinPart* skin_part_create_from_v2( AConfig* root, const char* basepath ) { SkinPart* part; AConfig* node; SkinBox box; ANEW0(part); part->name = root->name; node = aconfig_find(root, "background"); if (node) skin_background_init_from(part->background, node, basepath); node = aconfig_find(root, "display"); if (node) skin_display_init_from(part->display, node); node = aconfig_find(root, "buttons"); if (node) { for (node = node->first_child; node != NULL; node = node->next) { SkinButton* button = skin_button_create_from(node, basepath); if (button != NULL) { button->next = part->buttons; part->buttons = button; } } } skin_box_minmax_init( &box ); if (part->background->valid) skin_box_minmax_update( &box, &part->background->rect ); if (part->display->valid) skin_box_minmax_update( &box, &part->display->rect ); SKIN_PART_LOOP_BUTTONS(part, button) skin_box_minmax_update( &box, &button->rect ); SKIN_PART_LOOP_END if ( !skin_box_minmax_to_rect( &box, &part->rect ) ) { skin_part_free(part); part = NULL; } return part; } /** SKIN LAYOUT **/ static void skin_layout_free( SkinLayout* layout ) { if (layout) { SKIN_LAYOUT_LOOP_LOCS(layout,loc) AFREE(loc); SKIN_LAYOUT_LOOP_END layout->locations = NULL; AFREE(layout); } } SkinDisplay* skin_layout_get_display( SkinLayout* layout ) { SKIN_LAYOUT_LOOP_LOCS(layout,loc) SkinPart* part = loc->part; if (part->display->valid) { return part->display; } SKIN_LAYOUT_LOOP_END return NULL; } SkinRotation skin_layout_get_dpad_rotation( SkinLayout* layout ) { if (layout->has_dpad_rotation) return layout->dpad_rotation; SKIN_LAYOUT_LOOP_LOCS(layout, loc) SkinPart* part = loc->part; SKIN_PART_LOOP_BUTTONS(part,button) if (button->keycode == kKeyCodeDpadUp) return loc->rotation; SKIN_PART_LOOP_END SKIN_LAYOUT_LOOP_END return SKIN_ROTATION_0; } static int skin_layout_event_decode( const char* event, int *ptype, int *pcode, int *pvalue ) { typedef struct { const char* name; int value; } EventName; static const EventName _event_names[] = { { "EV_SW", 0x05 }, { NULL, 0 }, }; const char* x = strchr(event, ':'); const char* y = NULL; const EventName* ev = _event_names; if (x != NULL) y = strchr(x+1, ':'); if (x == NULL || y == NULL) { dprint( "### WARNING: invalid skin layout event format: '%s', should be '::'", event ); return -1; } for ( ; ev->name != NULL; ev++ ) if (!memcmp( event, ev->name, x - event ) && ev->name[x-event] == 0) break; if (!ev->name) { dprint( "### WARNING: unrecognized skin layout event name: %.*s", x-event, event ); return -1; } *ptype = ev->value; *pcode = strtol(x+1, NULL, 0); *pvalue = strtol(y+1, NULL, 0); return 0; } static SkinLayout* skin_layout_create_from_v2( AConfig* root, SkinPart* parts ) { SkinLayout* layout; int width, height; SkinLocation** ptail; AConfig* node; ANEW0(layout); width = aconfig_int( root, "width", 400 ); height = aconfig_int( root, "height", 400 ); node = aconfig_find( root, "event" ); if (node != NULL) { skin_layout_event_decode( node->value, &layout->event_type, &layout->event_code, &layout->event_value ); } else { layout->event_type = 0x05; /* close keyboard by default */ layout->event_code = 0; layout->event_value = 1; } layout->name = root->name; layout->color = aconfig_unsigned( root, "color", 0x808080 ) | 0xff000000; ptail = &layout->locations; node = aconfig_find( root, "dpad-rotation" ); if (node != NULL) { layout->dpad_rotation = aconfig_int( root, "dpad-rotation", 0 ); layout->has_dpad_rotation = 1; } for (node = root->first_child; node; node = node->next) { if (!memcmp(node->name, "part", 4)) { SkinLocation* location = skin_location_create_from_v2( node, parts ); if (location == NULL) { continue; } *ptail = location; ptail = &location->next; } } if (layout->locations == NULL) goto Fail; layout->size.w = width; layout->size.h = height; return layout; Fail: skin_layout_free(layout); return NULL; } /** SKIN FILE **/ static int skin_file_load_from_v1( SkinFile* file, AConfig* aconfig, const char* basepath ) { SkinPart* part; SkinLayout* layout; SkinLayout** ptail = &file->layouts; SkinLocation* location; int nn; file->parts = part = skin_part_create_from_v1( aconfig, basepath ); if (part == NULL) return -1; for (nn = 0; nn < 2; nn++) { ANEW0(layout); layout->color = 0xff808080; ANEW0(location); layout->event_type = 0x05; /* close keyboard by default */ layout->event_code = 0; layout->event_value = 1; location->part = part; switch (nn) { case 0: location->anchor.x = 0; location->anchor.y = 0; location->rotation = SKIN_ROTATION_0; layout->size = part->rect.size; break; #if 0 case 1: location->anchor.x = part->rect.size.h; location->anchor.y = 0; location->rotation = SKIN_ROTATION_90; layout->size.w = part->rect.size.h; layout->size.h = part->rect.size.w; layout->event_value = 0; break; case 2: location->anchor.x = part->rect.size.w; location->anchor.y = part->rect.size.h; location->rotation = SKIN_ROTATION_180; layout->size = part->rect.size; break; #endif default: location->anchor.x = 0; location->anchor.y = part->rect.size.w; location->rotation = SKIN_ROTATION_270; layout->size.w = part->rect.size.h; layout->size.h = part->rect.size.w; layout->event_value = 0; break; } layout->locations = location; *ptail = layout; ptail = &layout->next; } return 0; } static int skin_file_load_from_v2( SkinFile* file, AConfig* aconfig, const char* basepath ) { AConfig* node; /* first, load all parts */ node = aconfig_find(aconfig, "parts"); if (node == NULL) return -1; else { SkinPart** ptail = &file->parts; for (node = node->first_child; node != NULL; node = node->next) { SkinPart* part = skin_part_create_from_v2( node, basepath ); if (part == NULL) { dprint( "## WARNING: can't load part '%s' from skin\n", node->name ? "" : node->name ); continue; } part->next = NULL; *ptail = part; ptail = &part->next; } } if (file->parts == NULL) return -1; /* then load all layouts */ node = aconfig_find(aconfig, "layouts"); if (node == NULL) return -1; else { SkinLayout** ptail = &file->layouts; for (node = node->first_child; node != NULL; node = node->next) { SkinLayout* layout = skin_layout_create_from_v2( node, file->parts ); if (layout == NULL) { dprint( "## WARNING: ignoring layout in skin file" ); continue; } *ptail = layout; layout->next = NULL; ptail = &layout->next; } } if (file->layouts == NULL) return -1; return 0; } SkinFile* skin_file_create_from_aconfig( AConfig* aconfig, const char* basepath ) { SkinFile* file; ANEW0(file); if ( aconfig_find(aconfig, "parts") != NULL) { if (skin_file_load_from_v2( file, aconfig, basepath ) < 0) { skin_file_free( file ); file = NULL; } } else { if (skin_file_load_from_v1( file, aconfig, basepath ) < 0) { skin_file_free( file ); file = NULL; } } return file; } void skin_file_free( SkinFile* file ) { if (file) { SKIN_FILE_LOOP_LAYOUTS(file,layout) skin_layout_free(layout); SKIN_FILE_LOOP_END_LAYOUTS file->layouts = NULL; SKIN_FILE_LOOP_PARTS(file,part) skin_part_free(part); SKIN_FILE_LOOP_END_PARTS file->parts = NULL; AFREE(file); } }