diff options
Diffstat (limited to 'android/skin/file.c')
-rw-r--r-- | android/skin/file.c | 693 |
1 files changed, 693 insertions, 0 deletions
diff --git a/android/skin/file.c b/android/skin/file.c new file mode 100644 index 0000000..b0cb9cf --- /dev/null +++ b/android/skin/file.c @@ -0,0 +1,693 @@ +/* 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 }, + { "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 ) +{ + 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 '<TYPE>:<CODE>:<VALUE>'", 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; + + 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 ? "<NULL>" : 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); + } +} |