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.