Support dynamic skin layout
This CL adds support for a skin whose layout section is
generated at runtime based on the hardware configuration.
Change-Id: I5f81f665b49ce0f3ec5795d922a08b30c1f34b98
diff --git a/android/cmdline-options.h b/android/cmdline-options.h
index 5e8d59f..16edeac 100644
--- a/android/cmdline-options.h
+++ b/android/cmdline-options.h
@@ -90,6 +90,7 @@
CFG_PARAM( skin, "<name>", "select a given skin" )
CFG_FLAG ( no_skin, "don't use any emulator skin" )
CFG_FLAG ( noskin, "same as -no-skin" )
+CFG_FLAG ( dynamic_skin, "dynamically construct a skin of given size, requires -skin WxH option" )
CFG_PARAM( memory, "<size>", "physical RAM size in MBs" )
OPT_PARAM( netspeed, "<speed>", "maximum network download/upload speeds" )
diff --git a/android/help.c b/android/help.c
index 1570160..0a9eed8 100644
--- a/android/help.c
+++ b/android/help.c
@@ -759,6 +759,15 @@
" specify an exact framebuffer size, without any visual ornaments.\n\n" );
}
+static void
+help_dynamic_skin(stralloc_t* out)
+{
+ PRINTF(
+ " use '-dynamic_skin' to dynamically generate a skin based on the settings\n"
+ " in the AVD. This option only has effect if the -skin WxH option is used\n"
+ " to specify the width and height of the framebuffer\n");
+}
+
/* default network settings for emulator */
#define DEFAULT_NETSPEED "full"
#define DEFAULT_NETDELAY "none"
diff --git a/android/main-common.c b/android/main-common.c
index 2d535c7..5c82005 100644
--- a/android/main-common.c
+++ b/android/main-common.c
@@ -321,6 +321,207 @@
#endif
}
+typedef struct part_properties part_properties;
+struct part_properties {
+ const char* name;
+ int width;
+ int height;
+ part_properties* next;
+};
+
+part_properties*
+read_all_part_properties(AConfig* parts)
+{
+ part_properties* head = NULL;
+ part_properties* prev = NULL;
+
+ AConfig *node = parts->first_child;
+ while (node) {
+ part_properties* t = calloc(1, sizeof(part_properties));
+ t->name = node->name;
+
+ AConfig* bg = aconfig_find(node, "background");
+ if (bg != NULL) {
+ t->width = aconfig_int(bg, "width", 0);
+ t->height = aconfig_int(bg, "height", 0);
+ }
+
+ if (prev == NULL) {
+ head = t;
+ } else {
+ prev->next = t;
+ }
+ prev = t;
+ node = node->next;
+ }
+
+ return head;
+}
+
+void
+free_all_part_properties(part_properties* head)
+{
+ part_properties* prev = head;
+ while (head) {
+ prev = head;
+ head = head->next;
+ free(prev);
+ }
+}
+
+part_properties*
+get_part_properties(part_properties* allparts, char *partname)
+{
+ part_properties* p;
+ for (p = allparts; p != NULL; p = p->next) {
+ if (!strcmp(partname, p->name))
+ return p;
+ }
+
+ return NULL;
+}
+
+void
+add_parts_to_layout(AConfig* layout,
+ char* parts[],
+ int n_parts,
+ part_properties *props,
+ int xoffset,
+ int x_margin,
+ int y_margin)
+{
+ int i;
+ int y = 10;
+ char tmp[512];
+ for (i = 0; i < n_parts; i++) {
+ part_properties *p = get_part_properties(props, parts[i]);
+ snprintf(tmp, sizeof tmp,
+ "part%d {\n \
+ name %s\n \
+ x %d\n \
+ y %d\n \
+ }",
+ i + 2, // layout already has the device part as part1, so start from part2
+ p->name,
+ xoffset + x_margin,
+ y
+ );
+ y += p->height + y_margin;
+ aconfig_load(layout, strdup(tmp));
+ }
+}
+
+int
+load_dynamic_skin(AndroidHwConfig* hwConfig,
+ const char* skinDirPath,
+ int width,
+ int height,
+ AConfig* root)
+{
+ char tmp[1024];
+ AConfig* node;
+ int i;
+ int max_part_width;
+
+ if (skinDirPath == NULL)
+ return 0;
+
+ snprintf(tmp, sizeof(tmp), "%s/dynamic", skinDirPath);
+ if (!path_exists(tmp))
+ return 0;
+
+ snprintf(tmp, sizeof(tmp), "%s/dynamic/layout", skinDirPath);
+ D("trying to load skin file '%s'", tmp);
+
+ if(aconfig_load_file(root, tmp) < 0) {
+ dwarning("could not load skin file '%s', won't use a skin\n", tmp);
+ return 0;
+ }
+
+ /* Fix the width and height specified for the "device" part in the layout */
+ node = aconfig_find(root, "parts");
+ if (node != NULL) {
+ node = aconfig_find(node, "device");
+ if (node != NULL) {
+ node = aconfig_find(node, "display");
+ if (node != NULL) {
+ snprintf(tmp, sizeof tmp, "%d", width);
+ aconfig_set(node, "width", strdup(tmp));
+ snprintf(tmp, sizeof tmp, "%d", height);
+ aconfig_set(node, "height", strdup(tmp));
+ }
+ }
+ }
+
+ /* The dynamic layout declares all the parts that are available statically
+ in the layout file. Now we need to dynamically generate the
+ appropriate layout based on the hardware config */
+
+ part_properties* props = read_all_part_properties(aconfig_find(root, "parts"));
+
+ const int N_PARTS = 3;
+ char* parts[N_PARTS];
+ parts[0] = hwConfig->hw_mainKeys ? "hwkeys_on" : "hwkeys_off";
+ parts[1] = hwConfig->hw_dPad ? "dpad_on" : "dpad_off";
+ parts[2] = hwConfig->hw_keyboard ? "keyboard_on" : "keyboard_off";
+
+ for (i = 0, max_part_width = 0; i < N_PARTS; i++) {
+ part_properties *p = get_part_properties(props, parts[i]);
+ if (p != NULL && p->width > max_part_width)
+ max_part_width = p->width;
+ }
+
+ int x_margin = 10;
+ int y_margin = 10;
+ snprintf(tmp, sizeof tmp,
+ "layouts {\n \
+ portrait {\n \
+ width %d\n \
+ height %d\n \
+ color 0x404040\n \
+ event EV_SW:0:1\n \
+ part1 {\n name device\n x 0\n y 0\n}\n \
+ }\n \
+ landscape {\n \
+ width %d\n \
+ height %d\n \
+ color 0x404040\n \
+ event EV_SW:0:0\n \
+ dpad-rotation 3\n \
+ part1 {\n name device\n x 0\n y %d\n rotation 3\n }\n \
+ }\n \
+ }\n \
+ }\n",
+ width + max_part_width + 2 * x_margin,
+ height,
+ height + max_part_width + 2 * x_margin,
+ width,
+ width);
+ aconfig_load(root, strdup(tmp));
+
+ /* Add parts to portrait orientation */
+ node = aconfig_find(root, "layouts");
+ if (node != NULL) {
+ node = aconfig_find(node, "portrait");
+ if (node != NULL) {
+ add_parts_to_layout(node, parts, N_PARTS, props, width, x_margin, y_margin);
+ }
+ }
+
+ /* Add parts to landscape orientation */
+ node = aconfig_find(root, "layouts");
+ if (node != NULL) {
+ node = aconfig_find(node, "landscape");
+ if (node != NULL) {
+ add_parts_to_layout(node, parts, N_PARTS, props, height, x_margin, y_margin);
+ }
+ }
+
+ free_all_part_properties(props);
+
+ return 1;
+}
+
/* list of skin aliases */
static const struct {
const char* name;
@@ -396,9 +597,19 @@
if (y && isdigit(y[1])) {
bpp = atoi(y+1);
}
+
+ if (opts->dynamic_skin) {
+ if (load_dynamic_skin(hwConfig, skinDirPath, width, height, root)) {
+ snprintf(tmp, sizeof tmp, "%s/dynamic/", skinDirPath);
+ path = tmp;
+ D("loaded dynamic skin width=%d height=%d bpp=%d\n", width, height, bpp);
+ goto FOUND_SKIN;
+ }
+ }
+
snprintf(tmp, sizeof tmp,
- "display {\n width %d\n height %d\n bpp %d}\n",
- width, height,bpp);
+ "display {\n width %d\n height %d\n bpp %d}\n",
+ width, height,bpp);
aconfig_load(root, strdup(tmp));
path = ":";
D("found magic skin width=%d height=%d bpp=%d\n", width, height, bpp);
diff --git a/docs/ANDROID-SKIN-FILES.TXT b/docs/ANDROID-SKIN-FILES.TXT
index 004f83d..edd7545 100644
--- a/docs/ANDROID-SKIN-FILES.TXT
+++ b/docs/ANDROID-SKIN-FILES.TXT
@@ -1,8 +1,10 @@
Android Emulator Skin File Specification:
=========================================
+Revisions:
+----------
Revision 2. Dated 2009-12-07
-
+ Dynamic Layout support added 10/2012
Introduction:
-------------
@@ -18,6 +20,9 @@
emulated handset.
This document specifies how to generate a new skin for the emulator.
+The emulator also supports dynamically creating the layout section of
+the skin, thereby removing the necessity to create skins for each new
+handset type. See the last section regarding info on dynamic controls.
General File Format:
--------------------
@@ -219,3 +224,16 @@
- 'network.delay':
Default network latency for this skin. Values correspond to the
-netdelay <delay> emulator command-line option.
+
+Dynamic Layouts:
+----------------
+
+The emulator also supports a skin that is dynamically generated. This
+skin (present in folder "dynamic" in the skins folder) follows the same
+format as other skins, except that it only defines the list of parts/controls
+that can be used in the skin. The layouts section is generated at
+runtime by the emulator. The parts section describes all the controls that
+can be used in any handset. All these parts have both an enabled and a
+disabled version defined in the skin, and at runtime the emulator reads
+the hardware definition and decides which of those two controls to actually
+use.