blob: d947ec2b51647b3cd1a67e96fa1d606d132a54b7 [file] [log] [blame]
/* 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->bpp = aconfig_int(node, "bpp", 16);
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,
display->bpp == 32 ? QFRAME_BUFFER_RGBX_8888
: 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 },
{ "camera", kKeyCodeCamera },
{ "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 },
{ "tv", kKeyCodeTV },
{ "epg", kKeyCodeEPG },
{ "dvr", kKeyCodeDVR },
{ "prev", kKeyCodePrevious },
{ "next", kKeyCodeNext },
{ "play", kKeyCodePlay },
{ "pause", kKeyCodePause },
{ "stop", kKeyCodeStop },
{ "rev", kKeyCodeRewind },
{ "ffwd", kKeyCodeFastForward },
{ "bookmarks", kKeyCodeBookmarks },
{ "window", kKeyCodeCycleWindows },
{ "channel-up", kKeyCodeChannelUp },
{ "channel-down", kKeyCodeChannelDown },
{ 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 '<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;
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;
}
file->version = 1;
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;
file->version = 2;
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) {
goto BAD_FILE;
}
file->version = aconfig_int(aconfig, "version", 2);
/* The file version must be 1 or higher */
if (file->version <= 0) {
dprint( "## WARNING: invalid skin version: %d", file->version);
goto BAD_FILE;
}
}
else {
if (skin_file_load_from_v1( file, aconfig, basepath ) < 0) {
goto BAD_FILE;
}
file->version = 1;
}
return file;
BAD_FILE:
skin_file_free( file );
return NULL;
}
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);
}
}