aboutsummaryrefslogtreecommitdiffstats
path: root/android/skin/file.c
diff options
context:
space:
mode:
Diffstat (limited to 'android/skin/file.c')
-rw-r--r--android/skin/file.c693
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);
+ }
+}