From: Thomas Martitz <thomas.martitz@student.htw-berlin.de>
Date: Tue, 13 Oct 2009 01:15:04 +0000 (+0200)
Subject: Rebase from statusbar
X-Git-Url: http://repo.or.cz/w/kugel-rb.git?a=commitdiff_plain;h=8728df88356755b64bc4d618d8d4162d708511a9;hp=08ce800150e5d78ba4c8f6e0b4fa9b83542ce560

Rebase from statusbar
---

diff --git a/apps/SOURCES b/apps/SOURCES
index bac3765..49d248d 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -78,6 +78,7 @@ gui/quickscreen.c
 #endif
 
 gui/wps.c
+gui/statusbar-skinned.c
 gui/scrollbar.c
 gui/splash.c
 gui/statusbar.c
diff --git a/apps/buffering.c b/apps/buffering.c
index 08590c9..e74aeb3 100644
--- a/apps/buffering.c
+++ b/apps/buffering.c
@@ -843,10 +843,15 @@ static bool fill_buffer(void)
 /* Given a file descriptor to a bitmap file, write the bitmap data to the
    buffer, with a struct bitmap and the actual data immediately following.
    Return value is the total size (struct + data). */
-static int load_image(int fd, const char *path)
+static int load_image(int fd, const char *path,
+                     bool (*size_func)(int *width, int *height))
 {
     int rc;
     struct bitmap *bmp = (struct bitmap *)&buffer[buf_widx];
+
+    /* get the desired image size */
+    if (!size_func || !size_func(&bmp->width, &bmp->height))
+        return 0;
     /* FIXME: alignment may be needed for the data buffer. */
     bmp->data = &buffer[buf_widx + sizeof(struct bitmap)];
 #ifndef HAVE_JPEG
@@ -859,8 +864,6 @@ static int load_image(int fd, const char *path)
     int free = (int)MIN(buffer_len - BUF_USED, buffer_len - buf_widx)
                                - sizeof(struct bitmap);
 
-    get_albumart_size(bmp);
-
 #ifdef HAVE_JPEG
     int pathlen = strlen(path);
     if (strcmp(path + pathlen - 4, ".bmp"))
@@ -897,11 +900,17 @@ management functions for all the actual handle management work.
    filename: name of the file to open
    offset: offset at which to start buffering the file, useful when the first
            (offset-1) bytes of the file aren't needed.
+   type: one of the data types supported (audio, image, cuesheet, others
+   user: user data passed possibly passed in subcalls specific to a data_type
    return value: <0 if the file cannot be opened, or one file already
    queued to be opened, otherwise the handle for the file in the buffer
 */
-int bufopen(const char *file, size_t offset, enum data_type type)
+int bufopen(const char *file, size_t offset, enum data_type type, void *user)
 {
+#ifndef HAVE_ALBUMART
+    /* currently only used for aa loading */
+    (void)user;
+#endif
     if (type == TYPE_ID3)
     {
         /* ID3 case: allocate space, init the handle and return. */
@@ -967,7 +976,7 @@ int bufopen(const char *file, size_t offset, enum data_type type)
         /* Bitmap file: we load the data instead of the file */
         int rc;
         mutex_lock(&llist_mod_mutex); /* Lock because load_bitmap yields */
-        rc = load_image(fd, file);
+        rc = load_image(fd, file, (bool (*)(int*w, int*h))user);
         mutex_unlock(&llist_mod_mutex);
         if (rc <= 0)
         {
diff --git a/apps/buffering.h b/apps/buffering.h
index d0e2dd7..5ad7bab 100644
--- a/apps/buffering.h
+++ b/apps/buffering.h
@@ -74,7 +74,7 @@ bool buffering_reset(char *buf, size_t buflen);
 
 #define BUF_MAX_HANDLES         256
 
-int bufopen(const char *file, size_t offset, enum data_type type);
+int bufopen(const char *file, size_t offset, enum data_type type, void *user);
 int bufalloc(const void *src, size_t size, enum data_type type);
 bool bufclose(int handle_id);
 int bufseek(int handle_id, size_t newpos);
diff --git a/apps/filetree.c b/apps/filetree.c
index 8fbc39f..793f278 100644
--- a/apps/filetree.c
+++ b/apps/filetree.c
@@ -477,7 +477,26 @@ int ft_enter(struct tree_context* c)
                 break;
 #endif
 
-
+#ifdef HAVE_LCD_BITMAP
+            case FILE_ATTR_SBS:
+                splash(0, ID2P(LANG_WAIT));
+                set_file(buf, (char *)global_settings.sbs_file,
+                         MAX_FILENAME);
+                global_settings.statusbar = STATUSBAR_CUSTOM;
+                settings_apply_skins();
+                viewportmanager_theme_changed(THEME_STATUSBAR);
+                break;
+#endif
+#ifdef HAVE_REMOTE_LCD
+            case FILE_ATTR_RSBS:
+                splash(0, ID2P(LANG_WAIT));
+                set_file(buf, (char *)global_settings.rsbs_file,
+                         MAX_FILENAME);
+                global_settings.remote_statusbar = STATUSBAR_CUSTOM;
+                settings_apply_skins();
+                viewportmanager_theme_changed(THEME_STATUSBAR);
+                break;
+#endif
                 /* wps config file */
             case FILE_ATTR_WPS:
                 splash(0, ID2P(LANG_WAIT));
diff --git a/apps/filetypes.c b/apps/filetypes.c
index ed3d938..2c737ce 100644
--- a/apps/filetypes.c
+++ b/apps/filetypes.c
@@ -108,6 +108,12 @@ static const struct filetype inbuilt_filetypes[] = {
 #endif
     { "bmark",FILE_ATTR_BMARK, Icon_Bookmark,  VOICE_EXT_BMARK },
     { "cue",  FILE_ATTR_CUE,   Icon_Bookmark,  VOICE_EXT_CUESHEET },
+#ifdef HAVE_LCD_BITMAP
+    { "sbs",  FILE_ATTR_SBS,  Icon_Wps,   VOICE_EXT_SBS },
+#endif
+#ifdef HAVE_REMOTE_LCD
+    { "rsbs", FILE_ATTR_RSBS, Icon_Wps,   VOICE_EXT_RSBS },
+#endif
 #ifdef BOOTFILE_EXT
     { BOOTFILE_EXT, FILE_ATTR_MOD, Icon_Firmware, VOICE_EXT_AJZ },
 #endif
diff --git a/apps/filetypes.h b/apps/filetypes.h
index f872cf8..d3846b0 100644
--- a/apps/filetypes.h
+++ b/apps/filetypes.h
@@ -41,6 +41,8 @@
 #define FILE_ATTR_KBD   0x0C00 /* keyboard file */
 #define FILE_ATTR_FMR   0x0D00 /* preset file */
 #define FILE_ATTR_CUE   0x0E00 /* cuesheet file */
+#define FILE_ATTR_SBS   0x0F00 /* statusbar file */
+#define FILE_ATTR_RSBS  0x1000 /* remote statusbar file */
 #define FILE_ATTR_MASK  0xFF00 /* which bits tree.c uses for file types */
 
 struct filetype {
diff --git a/apps/gui/list.c b/apps/gui/list.c
index d7ecdcc..0090f44 100644
--- a/apps/gui/list.c
+++ b/apps/gui/list.c
@@ -29,6 +29,7 @@
 #include "kernel.h"
 #include "system.h"
 
+#include "appevents.h"
 #include "action.h"
 #include "screen_access.h"
 #include "list.h"
diff --git a/apps/gui/skin_engine/skin_display.c b/apps/gui/skin_engine/skin_display.c
index cf1dbaa..04f76b4 100644
--- a/apps/gui/skin_engine/skin_display.c
+++ b/apps/gui/skin_engine/skin_display.c
@@ -64,6 +64,7 @@
 
 #include "wps_internals.h"
 #include "skin_engine.h"
+#include "statusbar-skinned.h"
 
 static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode);
 
@@ -108,7 +109,6 @@ bool gui_wps_display(struct gui_wps *gwps)
         vp->bg_pattern = display->get_background();
     }
 #endif
-    display->clear_display();
     display->backdrop_show(BACKDROP_SKIN_WPS);
     return skin_redraw(gwps, WPS_REFRESH_ALL);
 }
@@ -140,28 +140,30 @@ void skin_statusbar_changed(struct gui_wps *skin)
         return;
     struct wps_data *data = skin->data;
     const struct screen *display = skin->display;
+    const int   screen = display->screen_type;
 
     struct viewport *vp = &find_viewport(VP_DEFAULT_LABEL, data)->vp;
-    viewport_set_fullscreen(vp, display->screen_type);
+    viewport_set_fullscreen(vp, screen);
 
     if (data->wps_sb_tag)
     {   /* fix up the default viewport */
         if (data->show_sb_on_wps)
         {
-            bool bar_at_top =
-                statusbar_position(display->screen_type) != STATUSBAR_BOTTOM;
+            if (statusbar_position(screen) != STATUSBAR_OFF)
+                return;     /* vp is fixed already */
 
-            vp->y       = bar_at_top?STATUSBAR_HEIGHT:0;
+            vp->y       = STATUSBAR_HEIGHT;
             vp->height  = display->lcdheight - STATUSBAR_HEIGHT;
         }
         else
         {
-            vp->y       = 0;
+            if (statusbar_position(screen) == STATUSBAR_OFF)
+                return;     /* vp is fixed already */
+            vp->y       = vp->x = 0;
             vp->height  = display->lcdheight;
+            vp->width   = display->lcdwidth;
         }
     }
-
-
 }
 
 static void draw_progressbar(struct gui_wps *gwps,
@@ -170,6 +172,7 @@ static void draw_progressbar(struct gui_wps *gwps,
     struct screen *display = gwps->display;
     struct wps_state *state = gwps->state;
     struct progressbar *pb = wps_vp->pb;
+    struct mp3entry *id3 = state->id3;
     int y = pb->y;
 
     if (y < 0)
@@ -181,27 +184,37 @@ static void draw_progressbar(struct gui_wps *gwps,
         y = (-y -1)*line_height + (0 > center ? 0 : center);
     }
 
+    int elapsed, length;
+    if (id3)
+    {
+        elapsed = id3->elapsed;
+        length = id3->length;
+    }
+    else
+    {
+        elapsed = 0;
+        length = 0;
+    }
+
     if (pb->have_bitmap_pb)
         gui_bitmap_scrollbar_draw(display, pb->bm,
-                                  pb->x, y, pb->width, pb->bm.height,
-                                  state->id3->length ? state->id3->length : 1, 0,
-                                  state->id3->length ? state->id3->elapsed
-                                          + state->ff_rewind_count : 0,
-                                          HORIZONTAL);
+                                pb->x, y, pb->width, pb->bm.height,
+                                length ? length : 1, 0,
+                                length ? elapsed + state->ff_rewind_count : 0,
+                                HORIZONTAL);
     else
         gui_scrollbar_draw(display, pb->x, y, pb->width, pb->height,
-                           state->id3->length ? state->id3->length : 1, 0,
-                           state->id3->length ? state->id3->elapsed
-                                   + state->ff_rewind_count : 0,
-                                   HORIZONTAL);
+                           length ? length : 1, 0,
+                           length ? elapsed + state->ff_rewind_count : 0,
+                           HORIZONTAL);
 #ifdef AB_REPEAT_ENABLE
-    if ( ab_repeat_mode_enabled() && state->id3->length != 0 )
-        ab_draw_markers(display, state->id3->length,
+    if ( ab_repeat_mode_enabled() && length != 0 )
+        ab_draw_markers(display, length,
                         pb->x, pb->x + pb->width, y, pb->height);
 #endif
 
-    if (state->id3->cuesheet)
-        cue_draw_markers(display, state->id3->cuesheet, state->id3->length,
+    if (id3 && id3->cuesheet)
+        cue_draw_markers(display, state->id3->cuesheet, length,
                          pb->x, pb->x + pb->width, y+1, pb->height-2);
 }
 
@@ -273,7 +286,8 @@ static void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
     if (data->albumart && data->albumart->vp == vp 
 	    && data->albumart->draw)
     {
-        draw_album_art(gwps, audio_current_aa_hid(), false);
+        draw_album_art(gwps, playback_current_aa_hid(data->playback_aa_slot),
+                        false);
 		data->albumart->draw = false;
     }
 #endif
@@ -291,12 +305,20 @@ static bool draw_player_progress(struct gui_wps *gwps)
     int pos = 0;
     int i;
 
-    if (!state->id3)
-        return false;
+    int elapsed, length;
+    if (LIKELY(state->id3))
+    {
+        elapsed = state->id3->elapsed;
+        length = state->id3->length;
+    }
+    else
+    {
+        elapsed = 0;
+        length = 0;
+    }
 
-    if (state->id3->length)
-        pos = 36 * (state->id3->elapsed + state->ff_rewind_count)
-              / state->id3->length;
+    if (length)
+        pos = 36 * (elapsed + state->ff_rewind_count) / length;
 
     for (i = 0; i < 7; i++, pos -= 5)
     {
@@ -339,12 +361,24 @@ static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
     int digit, i, j;
     bool softchar;
 
-    if (!state->id3 || buf_size < 34) /* worst case: 11x UTF-8 char + \0 */
+    int elapsed, length;
+    if (LIKELY(state->id3))
+    {
+        elapsed = id3->elapsed;
+        length = id3->length;
+    }
+    else
+    {
+        elapsed = 0;
+        length = 0;
+    }
+
+    if (buf_size < 34) /* worst case: 11x UTF-8 char + \0 */
         return;
 
-    time = state->id3->elapsed + state->ff_rewind_count;
-    if (state->id3->length)
-        pos = 55 * time / state->id3->length;
+    time = elapsed + state->ff_rewind_count;
+    if (length)
+        pos = 55 * time / length;
 
     memset(timestr, 0, sizeof(timestr));
     format_time(timestr, sizeof(timestr)-2, time);
@@ -475,12 +509,18 @@ static bool evaluate_conditional(struct gui_wps *gwps, int *token_index)
 #ifdef HAVE_LCD_BITMAP
         /* clear all pictures in the conditional and nested ones */
         if (data->tokens[i].type == WPS_TOKEN_IMAGE_PRELOAD_DISPLAY)
-            clear_image_pos(gwps, find_image(data->tokens[i].value.i&0xFF, gwps->data));
+        {
+            struct gui_img *tmp = find_image(data->tokens[i].value.i&0xFF,
+                                                data);
+            if (tmp)
+                clear_image_pos(gwps, tmp);
+        }
 #endif
 #ifdef HAVE_ALBUMART
         if (data->albumart && data->tokens[i].type == WPS_TOKEN_ALBUMART_DISPLAY)
         {
-            draw_album_art(gwps, audio_current_aa_hid(), true);
+            draw_album_art(gwps,
+                    playback_current_aa_hid(data->playback_aa_slot), true);
             data->albumart->draw = false;
         }
 #endif
@@ -488,6 +528,7 @@ static bool evaluate_conditional(struct gui_wps *gwps, int *token_index)
 
     return true;
 }
+
 #ifdef HAVE_LCD_BITMAP
 struct gui_img* find_image(char label, struct wps_data *data)
 {
@@ -500,8 +541,8 @@ struct gui_img* find_image(char label, struct wps_data *data)
         list = list->next;
     }
     return NULL;
-}
-#endif
+}   
+#endif 
 
 struct skin_viewport* find_viewport(char label, struct wps_data *data)
 {
@@ -516,7 +557,6 @@ struct skin_viewport* find_viewport(char label, struct wps_data *data)
     return NULL;
 }
 
-
 /* Read a (sub)line to the given alignment format buffer.
    linebuf is the buffer where the data is actually stored.
    align is the alignment format that'll be used to display the text.
@@ -904,14 +944,8 @@ static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
 {
     struct wps_data *data = gwps->data;
     struct screen *display = gwps->display;
-    struct wps_state *state = gwps->state;
-
-    if (!data || !state || !display)
-        return false;
-
-    struct mp3entry *id3 = state->id3;
 
-    if (!id3)
+    if (!data || !display || !gwps->state)
         return false;
 
     unsigned flags;
@@ -944,15 +978,19 @@ static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
     if (refresh_mode == WPS_REFRESH_ALL)
     {
         struct skin_line *line;
+        struct skin_viewport *skin_viewport = find_viewport(VP_DEFAULT_LABEL, data);
 
-        display->set_viewport(&find_viewport(VP_DEFAULT_LABEL, data)->vp);
-        display->clear_viewport();
+        if (!(skin_viewport->hidden_flags & VP_NEVER_VISIBLE))
+        {
+            display->set_viewport(&skin_viewport->vp);
+            display->clear_viewport();
+        }
 
-        for (viewport_list = data->viewports;
+        for (viewport_list = data->viewports; 
              viewport_list; viewport_list = viewport_list->next)
         {
-            struct skin_viewport *skin_viewport =
-                            (struct skin_viewport *)viewport_list->token->value.data;
+            skin_viewport =
+                  (struct skin_viewport *)viewport_list->token->value.data;
             for(line = skin_viewport->lines; line; line = line->next)
             {
                 line->curr_subline = NULL;
@@ -975,6 +1013,10 @@ static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
     {
         struct skin_viewport *skin_viewport =
                         (struct skin_viewport *)viewport_list->token->value.data;
+        if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
+        {
+            continue;
+        }
         if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE)
         {
             if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN)
@@ -990,8 +1032,11 @@ static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
         struct skin_viewport *skin_viewport =
                         (struct skin_viewport *)viewport_list->token->value.data;
         unsigned vp_refresh_mode = refresh_mode;
+
         display->set_viewport(&skin_viewport->vp);
 
+        int hidden_vp = 0;
+
 #ifdef HAVE_LCD_BITMAP
         /* Set images to not to be displayed */
         struct skin_token_list *imglist = data->images;
@@ -1003,7 +1048,11 @@ static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
         }
 #endif
         /* dont redraw the viewport if its disabled */
-        if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
+        if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
+        {   /* don't draw anything into this one */
+            vp_refresh_mode = 0; hidden_vp = true;
+        }
+        else if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
         {
             if (!(skin_viewport->hidden_flags&VP_DRAW_WASHIDDEN))
                 display->scroll_stop(&skin_viewport->vp);
@@ -1017,6 +1066,7 @@ static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
             vp_refresh_mode = WPS_REFRESH_ALL;
             skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
         }
+        
         if (vp_refresh_mode == WPS_REFRESH_ALL)
         {
             display->clear_viewport();
@@ -1038,7 +1088,7 @@ static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
             flags = line->curr_subline->line_type;
 
             if (vp_refresh_mode == WPS_REFRESH_ALL || (flags & vp_refresh_mode)
-                || new_subline_refresh)
+                || new_subline_refresh || hidden_vp)
             {
                 /* get_line tells us if we need to update the line */
                 update_line = get_line(gwps, subline,
@@ -1084,7 +1134,7 @@ static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
             }
 #endif
 
-            if (update_line &&
+            if (update_line && !hidden_vp &&
                 /* conditionals clear the line which means if the %Vd is put into the default
                    viewport there will be a blank line.
                    To get around this we dont allow any actual drawing to happen in the
@@ -1103,7 +1153,6 @@ static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
                     write_line(display, &align, line_count, false);
             }
         }
-
 #ifdef HAVE_LCD_BITMAP
         /* progressbar */
         if (vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
@@ -1114,7 +1163,8 @@ static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
             }
         }
         /* Now display any images in this viewport */
-        wps_display_images(gwps, &skin_viewport->vp);
+        if (!hidden_vp)
+            wps_display_images(gwps, &skin_viewport->vp);
 #endif
     }
 
diff --git a/apps/gui/skin_engine/skin_engine.h b/apps/gui/skin_engine/skin_engine.h
index c52f720..704a785 100644
--- a/apps/gui/skin_engine/skin_engine.h
+++ b/apps/gui/skin_engine/skin_engine.h
@@ -27,7 +27,6 @@
 
 #include "wps_internals.h" /* TODO: remove this line.. shoudlnt be needed */
 
-
 #ifdef HAVE_TOUCHSCREEN
 int wps_get_touchaction(struct wps_data *data);
 #endif
@@ -44,7 +43,6 @@ bool skin_update(struct gui_wps *gwps, unsigned int update_type);
  * or from a skinfile (isfile = true)
  */
 bool skin_data_load(struct wps_data *wps_data,
-                    struct screen *display,
                     const char *buf,
                     bool isfile);
 
diff --git a/apps/gui/skin_engine/skin_parser.c b/apps/gui/skin_engine/skin_parser.c
index bd6199c..ba73cbb 100644
--- a/apps/gui/skin_engine/skin_parser.c
+++ b/apps/gui/skin_engine/skin_parser.c
@@ -356,6 +356,7 @@ static const struct wps_tag all_tags[] = {
                                                     parse_setting_and_lang },
                                                     
     { WPS_TOKEN_LASTTOUCH,                "Tl",  WPS_REFRESH_DYNAMIC, parse_timeout },
+    { WPS_TOKEN_CURRENT_SCREEN,           "cs",  WPS_REFRESH_DYNAMIC, NULL },
     { WPS_NO_TOKEN,                       "T",   0,    parse_touchregion      },
 
     { WPS_TOKEN_UNKNOWN,                  "",    0, NULL }
@@ -680,8 +681,15 @@ static int parse_viewport(const char *wps_bufptr,
     curr_line = NULL;
     if (!skin_start_new_line(skin_vp, wps_data->num_tokens))
         return WPS_ERROR_INVALID_PARAM;
-
-    if (*ptr == 'l')
+        
+    
+    if (*ptr == 'i')
+    {
+        skin_vp->label = VP_INFO_LABEL;
+        skin_vp->hidden_flags = VP_NEVER_VISIBLE;
+        ++ptr;
+    }
+    else if (*ptr == 'l')
     {
         if (*(ptr+1) == '|')
         {
@@ -991,6 +999,12 @@ static int parse_albumart_load(const char *wps_bufptr,
     if (!aa)
         return skip_end_of_line(wps_bufptr);
 
+    wps_data->playback_aa_slot
+            = playback_claim_aa_slot(wps_data->get_aa_size);
+
+    if (wps_data->playback_aa_slot < 0)
+        return skip_end_of_line(wps_bufptr);
+
     /* reset albumart info in wps */
     aa->width = -1;
     aa->height = -1;
@@ -1587,8 +1601,14 @@ static void wps_reset(struct wps_data *data)
 #ifdef HAVE_REMOTE_LCD
     bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */
 #endif
+    bool (*size_func)(int*,int*) = data->get_aa_size;
+    playback_release_aa_slot(data->playback_aa_slot);
     memset(data, 0, sizeof(*data));
     skin_data_init(data);
+#ifdef HAVE_ALBUMART
+    data->get_aa_size = size_func;
+    data->playback_aa_slot = -1;
+#endif
 #ifdef HAVE_REMOTE_LCD
     data->remote_wps = rwps;
 #endif
@@ -1625,7 +1645,6 @@ static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char
     else
 	{
         /* Abort if we can't load an image */
-        DEBUGF("ERR: Failed to load image - %s\n",img_path);
 		loaded = false;
     }
 	return loaded;
@@ -1684,7 +1703,6 @@ static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
 /* to setup up the wps-data from a format-buffer (isfile = false)
    from a (wps-)file (isfile = true)*/
 bool skin_data_load(struct wps_data *wps_data,
-                   struct screen *display,
                    const char *buf,
                    bool isfile)
 {
@@ -1706,6 +1724,7 @@ bool skin_data_load(struct wps_data *wps_data,
 
     wps_reset(wps_data);
 
+    /* alloc default viewport, will be fixed up later */
     curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
     if (!curr_vp)
         return false;
@@ -1717,9 +1736,6 @@ bool skin_data_load(struct wps_data *wps_data,
 
     /* Initialise the first (default) viewport */
     curr_vp->label         = VP_DEFAULT_LABEL;
-    curr_vp->vp.x          = 0;
-    curr_vp->vp.width      = display->getwidth();
-    curr_vp->vp.height     = display->getheight();
     curr_vp->pb            = NULL;
     curr_vp->hidden_flags  = 0;
     curr_vp->lines         = NULL;
@@ -1728,31 +1744,6 @@ bool skin_data_load(struct wps_data *wps_data,
     if (!skin_start_new_line(curr_vp, 0))
         return false;
 
-    switch (statusbar_position(display->screen_type))
-    {
-        case STATUSBAR_OFF:
-            curr_vp->vp.y      = 0;
-            break;
-        case STATUSBAR_TOP:
-            curr_vp->vp.y       = STATUSBAR_HEIGHT;
-            curr_vp->vp.height -= STATUSBAR_HEIGHT;
-            break;
-        case STATUSBAR_BOTTOM:
-            curr_vp->vp.y       = 0;
-            curr_vp->vp.height -= STATUSBAR_HEIGHT;
-            break;
-    }
-#ifdef HAVE_LCD_BITMAP
-    curr_vp->vp.font       = FONT_UI;
-    curr_vp->vp.drawmode   = DRMODE_SOLID;
-#endif
-#if LCD_DEPTH > 1
-    if (display->depth > 1)
-    {
-        curr_vp->vp.fg_pattern = display->get_foreground();
-        curr_vp->vp.bg_pattern = display->get_background();
-    }
-#endif
     if (!isfile)
     {
         return wps_parse(wps_data, buf, false);
diff --git a/apps/gui/skin_engine/skin_tokens.c b/apps/gui/skin_engine/skin_tokens.c
index e0d4595..4a1b97c 100644
--- a/apps/gui/skin_engine/skin_tokens.c
+++ b/apps/gui/skin_engine/skin_tokens.c
@@ -56,10 +56,11 @@
 
 #include "wps_internals.h"
 #include "wps.h"
+#include "root_menu.h"
 
 static char* get_codectype(const struct mp3entry* id3)
 {
-    if (id3->codectype < AFMT_NUM_CODECS) {
+    if (id3 && id3->codectype < AFMT_NUM_CODECS) {
         return (char*)audio_formats[id3->codectype].label;
     } else {
         return NULL;
@@ -115,6 +116,28 @@ static char* get_dir(char* buf, int buf_size, const char* path, int level)
    and the original value of *intval, inclusive).
    When not treating a conditional/enum, intval should be NULL.
 */
+
+/* a few macros for the id3 == NULL case */
+
+#define HANDLE_NULL_ID3(field) (LIKELY(id3) ? id3->field : na)
+
+#define HANDLE_NULL_ID3_NUM_ZERO { if (UNLIKELY(!id3)) return zero; }
+
+#define HANDLE_NULL_ID3_NUM_NULL { if (UNLIKELY(!id3)) return zero; }
+
+#define HANDLE_NULL_ID3_NUM_INTVAL(field) \
+            do { \
+                if (intval) { \
+                    *intval =  (LIKELY(id3) ? id3->field + 1 : 0); \
+                } \
+            if (LIKELY(id3)) \
+            { \
+                snprintf(buf, buf_size, "%ld", id3->field); \
+                return buf; \
+            } \
+            return zero; \
+            } while (0)
+
 const char *get_token_value(struct gui_wps *gwps,
                            struct wps_token *token,
                            char *buf, int buf_size,
@@ -125,6 +148,9 @@ const char *get_token_value(struct gui_wps *gwps,
 
     struct wps_data *data = gwps->data;
     struct wps_state *state = gwps->state;
+    int elapsed, length;
+    static const char * const na = "n/a";
+    static const char * const zero = "0";
 
     if (!data || !state)
         return NULL;
@@ -136,8 +162,16 @@ const char *get_token_value(struct gui_wps *gwps,
     else
         id3 = state->id3;
 
-    if (!id3)
-        return NULL;
+    if (id3)
+    {
+        elapsed = id3->elapsed;
+        length = id3->length;
+    }
+    else
+    {
+        elapsed = 0;
+        length = 0;
+    }
 
 #if CONFIG_RTC
     struct tm* tm = NULL;
@@ -176,17 +210,17 @@ const char *get_token_value(struct gui_wps *gwps,
 
         case WPS_TOKEN_TRACK_TIME_ELAPSED:
             format_time(buf, buf_size,
-                        id3->elapsed + state->ff_rewind_count);
+                        elapsed + state->ff_rewind_count);
             return buf;
 
         case WPS_TOKEN_TRACK_TIME_REMAINING:
             format_time(buf, buf_size,
-                        id3->length - id3->elapsed -
+                        length - elapsed -
                         state->ff_rewind_count);
             return buf;
 
         case WPS_TOKEN_TRACK_LENGTH:
-            format_time(buf, buf_size, id3->length);
+            format_time(buf, buf_size, length);
             return buf;
 
         case WPS_TOKEN_PLAYLIST_ENTRIES:
@@ -233,37 +267,38 @@ const char *get_token_value(struct gui_wps *gwps,
             return buf;
 
         case WPS_TOKEN_TRACK_ELAPSED_PERCENT:
-            if (id3->length <= 0)
+            if (length <= 0)
                 return NULL;
 
             if (intval)
             {
-                *intval = limit * (id3->elapsed + state->ff_rewind_count)
-                          / id3->length + 1;
+                *intval = limit * (elapsed + state->ff_rewind_count)
+                          / length + 1;
             }
             snprintf(buf, buf_size, "%d",
-                     100*(id3->elapsed + state->ff_rewind_count) / id3->length);
+                     100*(elapsed + state->ff_rewind_count) / length);
             return buf;
 
         case WPS_TOKEN_METADATA_ARTIST:
-            return id3->artist;
+            return HANDLE_NULL_ID3(artist);
 
         case WPS_TOKEN_METADATA_COMPOSER:
-            return id3->composer;
+            return HANDLE_NULL_ID3(composer);
 
         case WPS_TOKEN_METADATA_ALBUM:
-            return id3->album;
+            return HANDLE_NULL_ID3(album);
 
         case WPS_TOKEN_METADATA_ALBUM_ARTIST:
-            return id3->albumartist;
+            return HANDLE_NULL_ID3(albumartist);
 
         case WPS_TOKEN_METADATA_GROUPING:
-            return id3->grouping;
+            return HANDLE_NULL_ID3(grouping);
 
         case WPS_TOKEN_METADATA_GENRE:
-            return id3->genre_string;
+            return HANDLE_NULL_ID3(genre_string);
 
         case WPS_TOKEN_METADATA_DISC_NUMBER:
+            HANDLE_NULL_ID3_NUM_NULL;
             if (id3->disc_string)
                 return id3->disc_string;
             if (id3->discnum) {
@@ -273,6 +308,7 @@ const char *get_token_value(struct gui_wps *gwps,
             return NULL;
 
         case WPS_TOKEN_METADATA_TRACK_NUMBER:
+            HANDLE_NULL_ID3_NUM_NULL;
             if (id3->track_string)
                 return id3->track_string;
 
@@ -283,9 +319,10 @@ const char *get_token_value(struct gui_wps *gwps,
             return NULL;
 
         case WPS_TOKEN_METADATA_TRACK_TITLE:
-            return id3->title;
+            return HANDLE_NULL_ID3(title);
 
         case WPS_TOKEN_METADATA_VERSION:
+            HANDLE_NULL_ID3_NUM_NULL;
             switch (id3->id3version)
             {
                 case ID3_VER_1_0:
@@ -308,6 +345,7 @@ const char *get_token_value(struct gui_wps *gwps,
             }
 
         case WPS_TOKEN_METADATA_YEAR:
+            HANDLE_NULL_ID3_NUM_NULL;
             if( id3->year_string )
                 return id3->year_string;
 
@@ -318,12 +356,13 @@ const char *get_token_value(struct gui_wps *gwps,
             return NULL;
 
         case WPS_TOKEN_METADATA_COMMENT:
-            return id3->comment;
+            return HANDLE_NULL_ID3(comment);
 
 #ifdef HAVE_ALBUMART
         case WPS_TOKEN_ALBUMART_FOUND:
-            if (data->albumart && audio_current_aa_hid() >= 0) {
-                return "C";
+            if (data->albumart) {
+                if (playback_current_aa_hid(data->playback_aa_slot) >= 0)
+                    return "C";
             }
             return NULL;
             
@@ -336,7 +375,7 @@ const char *get_token_value(struct gui_wps *gwps,
 #endif
 
         case WPS_TOKEN_FILE_BITRATE:
-            if(id3->bitrate)
+            if(id3 && id3->bitrate)
                 snprintf(buf, buf_size, "%d", id3->bitrate);
             else
                 return "?";
@@ -345,7 +384,9 @@ const char *get_token_value(struct gui_wps *gwps,
         case WPS_TOKEN_FILE_CODEC:
             if (intval)
             {
-                if(id3->codectype == AFMT_UNKNOWN)
+                if (UNLIKELY(!id3))
+                    *intval = 0;
+                else if(id3->codectype == AFMT_UNKNOWN)
                     *intval = AFMT_NUM_CODECS;
                 else
                     *intval = id3->codectype;
@@ -353,10 +394,12 @@ const char *get_token_value(struct gui_wps *gwps,
             return get_codectype(id3);
 
         case WPS_TOKEN_FILE_FREQUENCY:
+            HANDLE_NULL_ID3_NUM_ZERO;
             snprintf(buf, buf_size, "%ld", id3->frequency);
             return buf;
 
         case WPS_TOKEN_FILE_FREQUENCY_KHZ:
+            HANDLE_NULL_ID3_NUM_ZERO;
             /* ignore remainders < 100, so 22050 Hz becomes just 22k */
             if ((id3->frequency % 1000) < 100)
                 snprintf(buf, buf_size, "%ld", id3->frequency / 1000);
@@ -367,6 +410,7 @@ const char *get_token_value(struct gui_wps *gwps,
             return buf;
 
         case WPS_TOKEN_FILE_NAME:
+            if (!id3) return na;
             if (get_dir(buf, buf_size, id3->path, 0)) {
                 /* Remove extension */
                 char* sep = strrchr(buf, '.');
@@ -380,20 +424,24 @@ const char *get_token_value(struct gui_wps *gwps,
             }
 
         case WPS_TOKEN_FILE_NAME_WITH_EXTENSION:
+            if (!id3) return na;
             return get_dir(buf, buf_size, id3->path, 0);
 
         case WPS_TOKEN_FILE_PATH:
-            return id3->path;
+            return HANDLE_NULL_ID3(path);
 
         case WPS_TOKEN_FILE_SIZE:
+            HANDLE_NULL_ID3_NUM_ZERO;
             snprintf(buf, buf_size, "%ld", id3->filesize / 1024);
             return buf;
 
         case WPS_TOKEN_FILE_VBR:
-            return id3->vbr ? "(avg)" : NULL;
+            return (LIKELY(id3) && id3->vbr) ? "(avg)" : NULL;
 
         case WPS_TOKEN_FILE_DIRECTORY:
-            return get_dir(buf, buf_size, id3->path, token->value.i);
+            if (LIKELY(id3))
+                return get_dir(buf, buf_size, id3->path, token->value.i);
+            return na;
 
         case WPS_TOKEN_BATTERY_PERCENT:
         {
@@ -467,17 +515,31 @@ const char *get_token_value(struct gui_wps *gwps,
 
         case WPS_TOKEN_PLAYBACK_STATUS:
         {
-            int status = audio_status();
-            int mode = 1;
-            if (status == AUDIO_STATUS_PLAY)
-                mode = 2;
-            if (is_wps_fading() ||
-               (status & AUDIO_STATUS_PAUSE && !status_get_ffmode()))
-                mode = 3;
-            if (status_get_ffmode() == STATUS_FASTFORWARD)
-                mode = 4;
-            if (status_get_ffmode() == STATUS_FASTBACKWARD)
-                mode = 5;
+            int status = current_playmode();
+            /* music */
+            int mode = 1; /* stop */
+            if (status == STATUS_PLAY)
+                mode = 2; /* play */
+            if (is_wps_fading() || 
+               (status == STATUS_PAUSE  && !status_get_ffmode()))
+                mode = 3; /* pause */
+            else
+            {   /* ff / rwd */
+                if (status_get_ffmode() == STATUS_FASTFORWARD)
+                    mode = 4;
+                if (status_get_ffmode() == STATUS_FASTBACKWARD)
+                    mode = 5;
+            }
+            /* recording */
+            if (status == STATUS_RECORD)
+                mode = 6;
+            else if (status == STATUS_RECORD_PAUSE)
+                mode = 7;
+            /* radio */
+            if (status == STATUS_RADIO)
+                mode = 8;
+            else if (status == STATUS_RADIO_PAUSE)
+                mode = 9;
 
             if (intval) {
                 *intval = mode;
@@ -642,27 +704,16 @@ const char *get_token_value(struct gui_wps *gwps,
             return buf;
 #endif
 
+                
 #ifdef HAVE_TAGCACHE
         case WPS_TOKEN_DATABASE_PLAYCOUNT:
-            if (intval) {
-                *intval = id3->playcount + 1;
-            }
-            snprintf(buf, buf_size, "%ld", id3->playcount);
-            return buf;
+            HANDLE_NULL_ID3_NUM_INTVAL(playcount);
 
         case WPS_TOKEN_DATABASE_RATING:
-            if (intval) {
-                *intval = id3->rating + 1;
-            }
-            snprintf(buf, buf_size, "%d", id3->rating);
-            return buf;
+            HANDLE_NULL_ID3_NUM_INTVAL(rating);
 
         case WPS_TOKEN_DATABASE_AUTOSCORE:
-            if (intval)
-                *intval = id3->score + 1;
-
-            snprintf(buf, buf_size, "%d", id3->score);
-            return buf;
+            HANDLE_NULL_ID3_NUM_INTVAL(score);
 #endif
 
 #if (CONFIG_CODEC == SWCODEC)
@@ -680,9 +731,13 @@ const char *get_token_value(struct gui_wps *gwps,
                 val = 1; /* off */
             else
             {
-                int type =
-                    get_replaygain_mode(id3->track_gain_string != NULL,
+                int type;
+                if (LIKELY(id3))
+                    type = get_replaygain_mode(id3->track_gain_string != NULL,
                                         id3->album_gain_string != NULL);
+                else
+                    type = -1;
+                                        
                 if (type < 0)
                     val = 6;    /* no tag */
                 else
@@ -701,6 +756,7 @@ const char *get_token_value(struct gui_wps *gwps,
                 case 6:
                     return "+0.00 dB";
                     break;
+                /* due to above, coming here with !id3 shouldn't be possible */
                 case 2:
                 case 4:
                     strlcpy(buf, id3->track_gain_string, buf_size);
@@ -808,6 +864,36 @@ const char *get_token_value(struct gui_wps *gwps,
             cfg_to_string(token->value.i,buf,buf_size);
             return buf;
         }
+        case WPS_TOKEN_CURRENT_SCREEN:
+        {
+            int curr_screen = current_screen();
+            switch (curr_screen)
+            {
+                case GO_TO_WPS:
+                    curr_screen = 2;
+                    break;
+#ifdef HAVE_RECORDING
+                case GO_TO_RECSCREEN:
+                    curr_screen = 3;
+                    break;
+#endif
+#if CONFIG_TUNER
+                case GO_TO_FM:
+                    curr_screen = 4;
+                    break;
+#endif
+                default: /* lists */
+                    curr_screen = 1;
+                    break;
+            }
+            if (intval)
+            {
+                
+                *intval = curr_screen;
+            }
+            snprintf(buf, buf_size, "%d", curr_screen);
+            return buf;
+        }
 
         default:
             return NULL;
diff --git a/apps/gui/skin_engine/skin_tokens.h b/apps/gui/skin_engine/skin_tokens.h
index 8642542..6d78355 100644
--- a/apps/gui/skin_engine/skin_tokens.h
+++ b/apps/gui/skin_engine/skin_tokens.h
@@ -192,6 +192,7 @@ enum wps_token_type {
 
     /* Setting option */
     WPS_TOKEN_SETTING,
+    WPS_TOKEN_CURRENT_SCREEN,
 };
 
 struct wps_token {
diff --git a/apps/gui/skin_engine/wps_internals.h b/apps/gui/skin_engine/wps_internals.h
index 177a376..21c2f7a 100644
--- a/apps/gui/skin_engine/wps_internals.h
+++ b/apps/gui/skin_engine/wps_internals.h
@@ -191,11 +191,14 @@ struct skin_line {
     struct skin_line *next;
 };
 
-#define VP_DRAW_HIDEABLE 0x1
-#define VP_DRAW_HIDDEN   0x2
-#define VP_DRAW_WASHIDDEN  0x4
-#define VP_DEFAULT_LABEL '|'
-#define VP_NO_LABEL      '-'
+#define VP_DRAW_HIDEABLE    0x1
+#define VP_DRAW_HIDDEN      0x2
+#define VP_DRAW_WASHIDDEN   0x4
+/* these are never drawn, nor cleared, i.e. just ignored */
+#define VP_NEVER_VISIBLE    0x8
+#define VP_DEFAULT_LABEL    '|'
+#define VP_NO_LABEL         '-'
+#define VP_INFO_LABEL       '_'
 struct skin_viewport {
     struct viewport vp;   /* The LCD viewport struct */
     struct progressbar *pb;
@@ -253,6 +256,8 @@ struct wps_data
     bool peak_meter_enabled;
 #ifdef HAVE_ALBUMART
     struct skin_albumart *albumart;
+    int    playback_aa_slot;
+    bool    (*get_aa_size)(int *width, int *height);
 #endif
 
 #else /*HAVE_LCD_CHARCELLS */
diff --git a/apps/gui/statusbar-skinned.c b/apps/gui/statusbar-skinned.c
new file mode 100644
index 0000000..ebcb3f5
--- /dev/null
+++ b/apps/gui/statusbar-skinned.c
@@ -0,0 +1,170 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2009 Thomas Martitz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "config.h"
+
+#include "system.h"
+#include "settings.h"
+#include "appevents.h"
+#include "screens.h"
+#include "screen_access.h"
+#include "skin_engine/skin_engine.h"
+#include "skin_engine/wps_internals.h"
+#include "viewport.h"
+#include "statusbar.h"
+#include "debug.h"
+
+
+/* currently only one wps_state is needed */
+extern struct wps_state wps_state;
+static struct gui_wps sb_skin[NB_SCREENS];
+static struct wps_data sb_skin_data[NB_SCREENS];
+
+/* initial setup of wps_data  */
+static void sb_skin_update(void*);
+static bool loaded_ok[NB_SCREENS] = { false };
+static int skinbars = 0;
+
+void sb_skin_data_load(enum screen_type screen, const char *buf, bool isfile)
+{
+    struct wps_data *data = sb_skin[screen].data;
+
+    int success;
+    success = buf && skin_data_load(data, buf, isfile);
+
+    if (success)
+    {  /* hide the sb's default viewport because it has nasty effect with stuff
+        * not part of the statusbar,
+        * hence .sbs's without any other vps are unsupported*/
+        struct skin_viewport *vp = find_viewport(VP_DEFAULT_LABEL, data);
+        struct skin_token_list *next_vp = data->viewports->next;
+
+        if (!next_vp)
+        {    /* no second viewport, let parsing fail */
+            success = false;
+        }
+        /* hide this viewport, forever */
+        vp->hidden_flags = VP_NEVER_VISIBLE;
+    }
+
+    if (!success)
+        remove_event(GUI_EVENT_ACTIONUPDATE, sb_skin_update);
+
+#ifdef HAVE_REMOVE_LCD
+    data->remote_wps = !(screen == SCREEN_MAIN);
+#endif
+    loaded_ok[screen] = success;
+}
+
+void sb_skin_data_init(enum screen_type screen)
+{
+    skin_data_init(sb_skin[screen].data);
+}
+
+struct viewport *sb_skin_get_info_vp(enum screen_type screen)
+{
+    return &find_viewport(VP_INFO_LABEL, sb_skin[screen].data)->vp;
+}
+
+inline bool sb_skin_get_state(enum screen_type screen)
+{
+    return loaded_ok[screen] && (skinbars & VP_SB_ONSCREEN(screen));
+}
+
+void sb_skin_set_state(int state, enum screen_type screen)
+{
+    sb_skin[screen].state->do_full_update = true;
+    if (state)
+    {
+        skinbars |= VP_SB_ONSCREEN(screen);
+    }
+    else
+    {
+        skinbars &= ~VP_SB_ONSCREEN(screen);
+    }
+
+    if (skinbars)
+        add_event(GUI_EVENT_ACTIONUPDATE, false, sb_skin_update);
+    else
+        remove_event(GUI_EVENT_ACTIONUPDATE, sb_skin_update);
+}
+
+static void sb_skin_update(void* param)
+{
+    static long next_update = 0;
+    int i;
+    int forced_draw = param ||  sb_skin[SCREEN_MAIN].state->do_full_update;
+    if (TIME_AFTER(current_tick, next_update) || forced_draw)
+    {
+        FOR_NB_SCREENS(i)
+        {
+            if (sb_skin_get_state(i))
+            {
+                skin_update(&sb_skin[i], forced_draw?
+                        WPS_REFRESH_ALL : WPS_REFRESH_NON_STATIC);
+            }
+        }
+        next_update = current_tick + HZ/7; /* don't update too often */
+        sb_skin[SCREEN_MAIN].state->do_full_update = false;
+    }
+}
+#ifdef HAVE_ALBUMART
+static bool sb_uses_albumart(int *width, int *height)
+{
+    int  i;
+    FOR_NB_SCREENS(i) {
+        struct gui_wps *gwps = &sb_skin[i];
+        struct skin_albumart *aa = gwps->data->albumart;
+        if (aa && (aa->state != WPS_ALBUMART_NONE))
+        {
+            if (width)
+                *width = aa->width;
+            if (height)
+                *height = aa->height;
+            return true;
+        }
+    }
+    return false;
+}
+#endif
+
+void sb_skin_init(void)
+{
+    int i;
+    FOR_NB_SCREENS(i)
+    {
+        skin_data_init(&sb_skin_data[i]);
+#ifdef HAVE_ALBUMART
+        sb_skin_data[i].albumart = NULL;
+        sb_skin_data[i].playback_aa_slot = -1;
+        sb_skin_data[i].get_aa_size = sb_uses_albumart;
+#endif
+#ifdef HAVE_REMOTE_LCD
+        sb_skin_data[i].remote_wps = (i == SCREEN_REMOTE);
+#endif
+        sb_skin[i].data = &sb_skin_data[i];
+        sb_skin[i].display = &screens[i];
+        /* Currently no seperate wps_state needed/possible
+           so use the only available ( "global" ) one */
+        sb_skin[i].state = &wps_state;
+        sb_skin[i].statusbars = &skinbars;
+    }
+}
diff --git a/apps/gui/statusbar-skinned.h b/apps/gui/statusbar-skinned.h
new file mode 100644
index 0000000..ca27211
--- /dev/null
+++ b/apps/gui/statusbar-skinned.h
@@ -0,0 +1,43 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2009 Thomas Martitz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "config.h"
+
+#ifndef __STATUSBAR_SKINNED_H__
+#define __STATUSBAR_SKINNED_H__
+
+
+void sb_skin_data_load(enum screen_type screen, const char *buf, bool isfile);
+void sb_skin_data_init(enum screen_type screen);
+
+/* probably temporary, to shut the classic statusbar up */
+bool sb_skin_get_state(enum screen_type screen);
+void sb_skin_set_state(int state, enum screen_type screen);
+void sb_skin_init(void);
+struct viewport *sb_skin_get_info_vp(enum screen_type screen);
+
+#ifdef HAVE_ALBUMART
+bool sb_skin_uses_statusbar(int *width, int *height);
+#endif
+
+#endif /* __STATUSBAR_SKINNED_H__ */
diff --git a/apps/gui/statusbar.c b/apps/gui/statusbar.c
index 5aac200..4710eba 100644
--- a/apps/gui/statusbar.c
+++ b/apps/gui/statusbar.c
@@ -188,6 +188,9 @@ void gui_statusbar_draw(struct gui_statusbar * bar, bool force_redraw)
 {
     struct screen * display = bar->display;
 
+    if (!display)
+        return;
+
 #ifdef HAVE_LCD_CHARCELLS
     int val;
     (void)force_redraw; /* The Player always has "redraw" */
diff --git a/apps/gui/statusbar.h b/apps/gui/statusbar.h
index 84d3785..b25edbf 100644
--- a/apps/gui/statusbar.h
+++ b/apps/gui/statusbar.h
@@ -55,7 +55,8 @@ struct status_info {
 };
 
 /* statusbar visibility/position, used for settings also */
-enum statusbar_values { STATUSBAR_OFF = 0, STATUSBAR_TOP, STATUSBAR_BOTTOM };
+enum statusbar_values { STATUSBAR_OFF = 0, STATUSBAR_TOP, STATUSBAR_BOTTOM,
+                        STATUSBAR_CUSTOM };
 
 struct gui_statusbar
 {
diff --git a/apps/gui/viewport.c b/apps/gui/viewport.c
index fdd06bd..46cf0f9 100644
--- a/apps/gui/viewport.c
+++ b/apps/gui/viewport.c
@@ -58,6 +58,9 @@
 #ifdef HAVE_LCD_BITMAP
 #include "language.h"
 #endif
+#include "statusbar-skinned.h"
+#include "debug.h"
+
 
 static int statusbar_enabled = 0;
 
@@ -93,8 +96,9 @@ static bool showing_bars(enum screen_type screen)
     if (statusbar_enabled & VP_SB_ONSCREEN(screen))
     {
 #ifdef HAVE_LCD_BITMAP
-        bool ignore = statusbar_enabled & VP_SB_IGNORE_SETTING(screen);
-        return ignore || (statusbar_position(screen));
+        int ignore;
+        ignore = statusbar_enabled & VP_SB_IGNORE_SETTING(screen);
+        return ignore || (statusbar_position(screen) != STATUSBAR_OFF);
 #else
         return true;
 #endif
@@ -109,22 +113,30 @@ void viewport_set_fullscreen(struct viewport *vp,
     vp->width = screens[screen].lcdwidth;
 
 #ifdef HAVE_LCD_BITMAP
-    set_default_align_flags(vp);
-    vp->drawmode = DRMODE_SOLID;
-    vp->font = FONT_UI; /* default to UI to discourage SYSFONT use */
-        
-    vp->height = screens[screen].lcdheight;
-    if (statusbar_position(screen) != STATUSBAR_BOTTOM
-            && showing_bars(screen))
-        vp->y = STATUSBAR_HEIGHT;
-    else 
-        vp->y = 0;
+    struct viewport *sb_skin_vp = sb_skin_get_info_vp(screen);
+    if (sb_skin_vp && sb_skin_get_state(screen)
+            && statusbar_enabled & VP_SB_ONSCREEN(screen))
+    {
+        *vp = *sb_skin_vp;
+    }
+    else
+    {
+        if (statusbar_position(screen) != STATUSBAR_BOTTOM && showing_bars(screen))
+            vp->y = STATUSBAR_HEIGHT;
+        else 
+            vp->y = 0;
 #else
-    vp->y = 0;
+    {
+        vp->y = 0;
 #endif
-    vp->height = screens[screen].lcdheight
-                        - (showing_bars(screen)?STATUSBAR_HEIGHT:0);
+        vp->height = screens[screen].lcdheight
+                - (showing_bars(screen)?STATUSBAR_HEIGHT:0);
+    }
 
+#ifdef HAVE_LCD_BITMAP
+    set_default_align_flags(vp);
+    vp->font = FONT_UI; /* default to UI to discourage SYSFONT use */
+    vp->drawmode = DRMODE_SOLID;
 #if LCD_DEPTH > 1
 #ifdef HAVE_REMOTE_LCD
     /* We only need this test if there is a remote LCD */
@@ -144,7 +156,7 @@ void viewport_set_fullscreen(struct viewport *vp,
         vp->bg_pattern = LCD_REMOTE_DEFAULT_BG;
     }
 #endif
-
+#endif
 }
 
 void viewport_set_defaults(struct viewport *vp,
@@ -180,20 +192,26 @@ int viewportmanager_get_statusbar(void)
 int viewportmanager_set_statusbar(const int enabled)
 {
     int old = statusbar_enabled;
+    int i;
+    
     statusbar_enabled = enabled;
-    if (enabled)
+
+    FOR_NB_SCREENS(i)
     {
-        int i;
-        FOR_NB_SCREENS(i)
+        if (showing_bars(i)
+                && statusbar_position(i) != STATUSBAR_CUSTOM)
         {
-            if (showing_bars(i))
-                gui_statusbar_draw(&statusbars.statusbars[i], true);
+            add_event(GUI_EVENT_ACTIONUPDATE, false, viewportmanager_redraw);
+            gui_statusbar_draw(&statusbars.statusbars[i], true);
         }
-        add_event(GUI_EVENT_ACTIONUPDATE, false, viewportmanager_redraw);
+        else
+            remove_event(GUI_EVENT_ACTIONUPDATE, viewportmanager_redraw);
     }
-    else
+
+    FOR_NB_SCREENS(i)
     {
-        remove_event(GUI_EVENT_ACTIONUPDATE, viewportmanager_redraw);
+        sb_skin_set_state(showing_bars(i)
+                        && statusbar_position(i) == STATUSBAR_CUSTOM, i);
     }
     return old;
 }
@@ -204,7 +222,8 @@ static void viewportmanager_redraw(void* data)
 
     FOR_NB_SCREENS(i)
     {
-        if (showing_bars(i))
+        if (showing_bars(i)
+                && statusbar_position(i) != STATUSBAR_CUSTOM)
             gui_statusbar_draw(&statusbars.statusbars[i], NULL != data);
     }
 }
@@ -234,11 +253,6 @@ void viewportmanager_theme_changed(const int which)
         /* reset the ui viewport */
         FOR_NB_SCREENS(i)
             ui_vp_info.active[i] = retval & BIT_N(i);
-
-        if (retval != 0)
-            add_event(GUI_EVENT_REFRESH, false, viewportmanager_ui_vp_changed);
-        else
-            remove_event(GUI_EVENT_REFRESH, viewportmanager_ui_vp_changed);
         /* and point to it */
         ui_vp_info.vp = custom_vp;
     }
@@ -249,18 +263,14 @@ void viewportmanager_theme_changed(const int which)
     }
     if (which & THEME_STATUSBAR)
     {
-        statusbar_enabled = VP_SB_HIDE_ALL;
-
+        statusbar_enabled = 0;
         FOR_NB_SCREENS(i)
         {
             if (statusbar_position(i) != STATUSBAR_OFF)
                 statusbar_enabled  |= VP_SB_ONSCREEN(i);
         }
 
-        if (statusbar_enabled != VP_SB_HIDE_ALL)
-            add_event(GUI_EVENT_ACTIONUPDATE, false, viewportmanager_redraw);
-        else
-            remove_event(GUI_EVENT_ACTIONUPDATE, viewportmanager_redraw);
+        viewportmanager_set_statusbar(statusbar_enabled);
 
         /* reposition viewport to fit statusbar, only if not using the ui vp */
         
@@ -270,6 +280,19 @@ void viewportmanager_theme_changed(const int which)
                 viewport_set_fullscreen(&custom_vp[i], i);
         }
     }
+
+    int event_add = 0;
+    FOR_NB_SCREENS(i)
+    {
+        event_add |= ui_vp_info.active[i];
+        event_add |= (statusbar_position(i) == STATUSBAR_CUSTOM);
+    }
+
+    if (event_add)
+        add_event(GUI_EVENT_REFRESH, false, viewportmanager_ui_vp_changed);
+    else
+        remove_event(GUI_EVENT_REFRESH, viewportmanager_ui_vp_changed);
+
     send_event(GUI_EVENT_THEME_CHANGED, NULL);
 }
 
@@ -283,10 +306,10 @@ static void viewportmanager_ui_vp_changed(void *param)
     FOR_NB_SCREENS(i)
         screens[i].clear_display();
     /* redraw the statusbar if it was enabled */
-    viewportmanager_set_statusbar(statusbar_enabled);
+    send_event(GUI_EVENT_ACTIONUPDATE, (void*)true);
     /* call the passed function which will redraw the content of
      * the current screen */
-    if (param != NULL)
+    if (draw_func != NULL)
         draw_func();
     FOR_NB_SCREENS(i)
         screens[i].update();
diff --git a/apps/gui/viewport.h b/apps/gui/viewport.h
index cffc7fb..0e5ad30 100644
--- a/apps/gui/viewport.h
+++ b/apps/gui/viewport.h
@@ -56,16 +56,16 @@ void viewport_set_defaults(struct viewport *vp,
  */
 
 
-#define THEME_STATUSBAR     (BIT_N(0))
-#define THEME_UI_VIEWPORT   (BIT_N(1))
-#define THEME_BUTTONBAR     (BIT_N(2))
-#define THEME_LANGUAGE      (BIT_N(3))
-#define THEME_ALL           (~(0u))
-
-#define VP_SB_HIDE_ALL 0
-#define VP_SB_ONSCREEN(screen) BIT_N(screen)
-#define VP_SB_IGNORE_SETTING(screen) BIT_N(4+screen)
-#define VP_SB_ALLSCREENS (VP_SB_ONSCREEN(0)|VP_SB_ONSCREEN(1))
+#define THEME_STATUSBAR             (BIT_N(0))
+#define THEME_UI_VIEWPORT           (BIT_N(1))
+#define THEME_BUTTONBAR             (BIT_N(2))
+#define THEME_LANGUAGE              (BIT_N(3))
+#define THEME_ALL                   (~(0u))
+
+#define VP_SB_HIDE_ALL                          0
+#define VP_SB_ONSCREEN(screen)                  BIT_N(screen)
+#define VP_SB_IGNORE_SETTING(screen)            BIT_N(4+screen)
+#define VP_SB_ALLSCREENS            (VP_SB_ONSCREEN(0)|VP_SB_ONSCREEN(1))
 
 #ifndef __PCTOOL__
 /*
diff --git a/apps/gui/wps.c b/apps/gui/wps.c
index 2529686..96769e3 100644
--- a/apps/gui/wps.c
+++ b/apps/gui/wps.c
@@ -31,7 +31,6 @@
 #include "action.h"
 #include "kernel.h"
 #include "filetypes.h"
-#include "debug.h"
 #include "sprintf.h"
 #include "settings.h"
 #include "skin_engine/skin_engine.h"
@@ -81,7 +80,7 @@
 /* this is for the viewportmanager */
 static int wpsbars;
 /* currently only one wps_state is needed */
-static struct wps_state wps_state;
+struct wps_state wps_state;
 static struct gui_wps gui_wps[NB_SCREENS];
 static struct wps_data wps_datas[NB_SCREENS];
 
@@ -128,8 +127,8 @@ void wps_data_load(enum screen_type screen, const char *buf, bool isfile)
 
 #endif /* __PCTOOL__ */
 
-    loaded_ok = buf && skin_data_load(gui_wps[screen].data,
-                                        &screens[screen], buf, isfile);
+    loaded_ok = buf && skin_data_load(gui_wps[screen].data, buf, isfile);
+
     if (!loaded_ok) /* load the hardcoded default */
     {
         char *skin_buf[NB_SCREENS] = {
@@ -152,11 +151,11 @@ void wps_data_load(enum screen_type screen, const char *buf, bool isfile)
             "%pb\n",
 #endif
         };
-        skin_data_load(gui_wps[screen].data, &screens[screen],
-                        skin_buf[screen], false);
+        skin_data_load(gui_wps[screen].data, skin_buf[screen], false);
     }
 #ifdef HAVE_REMOVE_LCD
     gui_wps[screen].data->remote_wps = !(screen == SCREEN_MAIN);
+
 #endif
 }
 
@@ -730,7 +729,6 @@ long gui_wps_show(void)
 #endif
             }
         }
-
 #ifdef HAVE_LCD_BITMAP
         /* when the peak meter is enabled we want to have a
             few extra updates to make it look smooth. On the
@@ -1283,6 +1281,26 @@ static void statusbar_toggle_handler(void *data)
 }
 #endif
 
+
+#ifdef HAVE_ALBUMART
+static bool wps_uses_albumart(int *width, int *height)
+{
+    int  i;
+    FOR_NB_SCREENS(i) {
+        struct gui_wps *gwps = &gui_wps[i];
+        struct skin_albumart *aa = gwps->data->albumart;
+        if (aa && (aa->state != WPS_ALBUMART_NONE))
+        {
+            if (width)
+                *width = aa->width;
+            if (height)
+                *height = aa->height;
+            return true;
+        }
+    }
+    return false;
+}
+#endif
 void gui_sync_wps_init(void)
 {
     int i;
@@ -1291,6 +1309,8 @@ void gui_sync_wps_init(void)
         skin_data_init(&wps_datas[i]);
 #ifdef HAVE_ALBUMART
         wps_datas[i].albumart = NULL;
+        wps_datas[i].playback_aa_slot = -1;
+        wps_datas[i].get_aa_size = wps_uses_albumart;
 #endif
 #ifdef HAVE_REMOTE_LCD
         wps_datas[i].remote_wps = (i == SCREEN_REMOTE);
@@ -1310,26 +1330,6 @@ void gui_sync_wps_init(void)
 #endif
 }
 
-#ifdef HAVE_ALBUMART
-bool wps_uses_albumart(int *width, int *height)
-{
-    int  i;
-    FOR_NB_SCREENS(i) {
-        struct gui_wps *gwps = &gui_wps[i];
-        struct skin_albumart *aa = gwps->data->albumart;
-        if (aa && (aa->state != WPS_ALBUMART_NONE))
-        {
-            if (width)
-                *width = aa->width;
-            if (height)
-                *height = aa->height;
-            return true;
-        }
-    }
-    return false;
-}
-#endif
-
 
 #ifdef IPOD_ACCESSORY_PROTOCOL
 int wps_get_ff_rewind_count(void)
diff --git a/apps/gui/wps.h b/apps/gui/wps.h
index f2f4485..c813c5a 100644
--- a/apps/gui/wps.h
+++ b/apps/gui/wps.h
@@ -39,16 +39,6 @@ void display_keylock_text(bool locked);
 
 bool is_wps_fading(void);
 
-
-#ifdef HAVE_ALBUMART
-/*
- * Returns true if at least one of the gui_wps screens has an album art
- * tag in its wps structure and writes the width and height into the passed
- * pointers
- */
-bool wps_uses_albumart(int*, int*);
-#endif
-
 #ifdef IPOD_ACCESSORY_PROTOCOL
 /* return length of the current ff or rewin action, IAP needs this */
 int wps_get_ff_rewind_count(void);
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 8be7380..7669401 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -13075,3 +13075,45 @@
     swcodec: "Release Time"
   </voice>
 </phrase>
+<phrase>
+  id: LANG_STATUSBAR_CUSTOM
+  desc: in Settings -> General -> Display -> statusbar
+  user: core
+  <source>
+    *: "Custom"
+  </source>
+  <dest>
+    *: "Custom"
+  </dest>
+  <voice>
+    *: "Custom"
+  </voice>
+</phrase>
+<phrase>
+  id: VOICE_EXT_SBS
+  desc: spoken only, for file extension
+  user: core
+  <source>
+    *: ""
+  </source>
+  <dest>
+    *: ""
+  </dest>
+  <voice>
+    *: "statusbar skin"
+  </voice>
+</phrase>
+<phrase>
+  id: VOICE_EXT_RSBS
+  desc: spoken only, for file extension
+  user: core
+  <source>
+    *: ""
+  </source>
+  <dest>
+    *: ""
+  </dest>
+  <voice>
+    *: "remote statusbar skin"
+  </voice>
+</phrase>
diff --git a/apps/main.c b/apps/main.c
index 58ff138..9b999b4 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -73,6 +73,7 @@
 #include "scrobbler.h"
 #include "icon.h"
 #include "viewport.h"
+#include "statusbar-skinned.h"
 
 #ifdef IPOD_ACCESSORY_PROTOCOL
 #include "iap.h"
@@ -322,6 +323,7 @@ static void init(void)
     settings_reset();
     settings_load(SETTINGS_ALL);
     gui_sync_wps_init();
+    sb_skin_init();
     settings_apply(true);
     init_dircache(true);
     init_dircache(false);
@@ -550,6 +552,7 @@ static void init(void)
     }
 
     gui_sync_wps_init();
+    sb_skin_init();
     settings_apply(true);
     init_dircache(false);
 #ifdef HAVE_TAGCACHE
@@ -564,7 +567,6 @@ static void init(void)
         eeprom_settings_store();
     }
 #endif
-    status_init();
     playlist_init();
     tree_mem_init();
     filetype_init();
diff --git a/apps/menus/theme_menu.c b/apps/menus/theme_menu.c
index a0e4f02..704bbe6 100644
--- a/apps/menus/theme_menu.c
+++ b/apps/menus/theme_menu.c
@@ -162,12 +162,16 @@ static int statusbar_callback_ex(int action,const struct menu_item_ex *this_item
             old_bar[screen] = statusbar_position(screen);
             break;
         case ACTION_EXIT_MENUITEM:
-            gui_statusbar_changed(screen, old_bar[screen]);
             send_event(GUI_EVENT_STATUSBAR_TOGGLE, NULL);
             send_event(GUI_EVENT_ACTIONUPDATE, (void*)true);
+            if ((old_bar[screen] == STATUSBAR_CUSTOM)
+                || (statusbar_position(screen) == STATUSBAR_CUSTOM))
+                send_event(GUI_EVENT_REFRESH, NULL);
+            else
+                gui_statusbar_changed(screen, old_bar[screen]);
             break;
     }
-    return action;
+    return ACTION_REDRAW;
 }
 
 #ifdef HAVE_REMOTE_LCD
diff --git a/apps/playback.c b/apps/playback.c
index c69bf39..87326af 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -135,6 +135,10 @@ enum {
     Q_AUDIO_POSTINIT,
     Q_AUDIO_FILL_BUFFER,
     Q_AUDIO_FINISH_LOAD,
+#ifdef HAVE_ALBUMART
+    Q_AUDIO_HANDOUT_SLOT,
+    Q_AUDIO_RELEASE_SLOT,
+#endif
     Q_CODEC_REQUEST_COMPLETE,
     Q_CODEC_REQUEST_FAILED,
 
@@ -158,6 +162,7 @@ enum filling_state {
 };
 
 #define MAX_TRACK       128
+#define MAX_MULTIPLE_AA 3
 
 #define MAX_TRACK_MASK  (MAX_TRACK-1)
 
@@ -206,7 +211,7 @@ struct track_info {
     int id3_hid;               /* The ID for the track's metadata handle */
     int codec_hid;             /* The ID for the track's codec handle */
 #ifdef HAVE_ALBUMART
-    int aa_hid;                /* The ID for the track's album art handle */
+    int aa_hid[MAX_MULTIPLE_AA];/* The ID for the track's album art handle */
 #endif
     int cuesheet_hid;          /* The ID for the track's parsed cueesheet handle */
 
@@ -215,6 +220,19 @@ struct track_info {
     bool taginfo_ready;        /* Is metadata read */
 };
 
+
+#ifdef HAVE_ALBUMART
+struct albumart_slot {
+    bool (*get_aa_size)(int *width, int *height);
+    union {
+        int slot_id; /* only used in claim_aa_slot */
+        bool used;   /* for everywhere else */
+    };
+} albumart_slots[MAX_MULTIPLE_AA+1];
+
+#define FOREACH_AA_SLOT(i) for(i = 0;i < MAX_MULTIPLE_AA; i++)
+#endif
+
 static struct track_info tracks[MAX_TRACK];
 static volatile int track_ridx = 0;  /* Track being decoded (A/C-) */
 static int track_widx = 0;           /* Track being buffered (A) */
@@ -367,11 +385,17 @@ static bool clear_track_info(struct track_info *track)
     }
 
 #ifdef HAVE_ALBUMART
-    if (track->aa_hid >= 0) {
-        if (bufclose(track->aa_hid))
-            track->aa_hid = -1;
-        else
-            return false;
+    {
+        int i;
+        for (i = 0; i < MAX_MULTIPLE_AA; i++)
+        {
+            if (track->aa_hid[i] >= 0) {
+                if (bufclose(track->aa_hid[i]))
+                    track->aa_hid[i] = -1;
+                else
+                    return false;
+            }   
+        }
     }
 #endif
 
@@ -538,18 +562,6 @@ void audio_remove_encoder(void)
 
 #endif /* HAVE_RECORDING */
 
-#ifdef HAVE_ALBUMART
-int audio_current_aa_hid(void)
-{
-    int cur_idx;
-    int offset = ci.new_track + wps_offset;
-
-    cur_idx = track_ridx + offset;
-    cur_idx &= MAX_TRACK_MASK;
-
-    return tracks[cur_idx].aa_hid;
-}
-#endif
 
 struct mp3entry* audio_current_track(void)
 {
@@ -673,6 +685,79 @@ struct mp3entry* audio_next_track(void)
     return NULL;
 }
 
+#ifdef HAVE_ALBUMART
+int playback_current_aa_hid(int slot)
+{
+    if (slot < 0)
+        return -1;
+    int cur_idx;
+    int offset = ci.new_track + wps_offset;
+
+    cur_idx = track_ridx + offset;
+    cur_idx &= MAX_TRACK_MASK;
+
+    return tracks[cur_idx].aa_hid[slot];
+}
+
+static void claim_aa_slot(intptr_t *param)
+{
+    struct albumart_slot *tmp = (struct albumart_slot*)param;
+
+    int i;
+    FOREACH_AA_SLOT(i)
+    {
+        if (!albumart_slots[i].used)
+        {
+            albumart_slots[i].used = true;
+            albumart_slots[i].get_aa_size = tmp->get_aa_size;
+            tmp->slot_id = i;
+            return;
+        }
+    }
+    tmp->slot_id = -1;
+}
+
+int playback_claim_aa_slot(bool (*size_func)(int*,int*))
+{
+    /* the handler will write the slot number into tmp,
+     * DO NOT RETURN BEFORE THE AUDIO THREAD HANDLED IT,
+     * i.e. don't change queue_send */
+    struct albumart_slot tmp;
+    tmp.get_aa_size = size_func;
+    tmp.slot_id = -1;
+
+    if (LIKELY(audio_is_initialized))
+    {
+        LOGFQUEUE("audio >| audio Q_AUDIO_HANDOUT_SLOT");
+        queue_send(&audio_queue, Q_AUDIO_HANDOUT_SLOT, (intptr_t)&tmp);
+    }
+    else
+    {
+        claim_aa_slot((intptr_t*)&tmp);
+    }
+
+    return tmp.slot_id;
+}
+
+static void release_aa_slot(int slot)
+{
+    albumart_slots[slot].used = false;
+}
+
+void playback_release_aa_slot(int slot)
+{
+    if (LIKELY(audio_is_initialized))
+    {
+        LOGFQUEUE("audio >| audio Q_AUDIO_RELEASE_SLOT");
+        queue_send(&audio_queue, Q_AUDIO_RELEASE_SLOT, (intptr_t)slot);
+    }
+    else
+    {
+        albumart_slots[slot].used = false;
+    }
+}
+
+#endif
 void audio_play(long offset)
 {
     logf("audio_play");
@@ -1671,7 +1756,7 @@ static bool audio_loadcodec(bool start_play)
 
     codec_get_full_path(codec_path, codec_fn);
 
-    tracks[track_widx].codec_hid = bufopen(codec_path, 0, TYPE_CODEC);
+    tracks[track_widx].codec_hid = bufopen(codec_path, 0, TYPE_CODEC, NULL);
     if (tracks[track_widx].codec_hid < 0)
         return false;
 
@@ -1757,7 +1842,7 @@ static bool audio_load_track(size_t offset, bool start_play)
     /* Get track metadata if we don't already have it. */
     if (tracks[track_widx].id3_hid < 0)
     {
-        tracks[track_widx].id3_hid = bufopen(trackname, 0, TYPE_ID3);
+        tracks[track_widx].id3_hid = bufopen(trackname, 0, TYPE_ID3, NULL);
 
         if (tracks[track_widx].id3_hid < 0)
         {
@@ -1859,26 +1944,35 @@ static void audio_finish_load_track(void)
         }
     }
 #ifdef HAVE_ALBUMART
-    if (tracks[track_widx].aa_hid < 0)
     {
+        int i;
         char aa_path[MAX_PATH];
-        /* find_albumart will error out if the wps doesn't have AA */
-        if (find_albumart(track_id3, aa_path, sizeof(aa_path)))
+        FOREACH_AA_SLOT(i)
         {
-            tracks[track_widx].aa_hid = bufopen(aa_path, 0, TYPE_BITMAP);
-
-            if(tracks[track_widx].aa_hid == ERR_BUFFER_FULL)
+            if (tracks[track_widx].aa_hid[i] >= 0 || !albumart_slots[i].used)
+                continue;
+            /* find_albumart will error out if the wps doesn't have AA */
+            if (find_albumart(track_id3, aa_path, sizeof(aa_path),
+                               albumart_slots[i].get_aa_size))
             {
-                filling = STATE_FULL;
-                logf("buffer is full for now");
-                return;  /* No space for track's album art, not an error */
-            }
-            else if (tracks[track_widx].aa_hid < 0)
-            {
-                /* another error, ignore AlbumArt */
-                logf("Album art loading failed");
+                int aa_hid = bufopen(aa_path, 0, TYPE_BITMAP,
+                                                albumart_slots[i].get_aa_size);
+
+                if(aa_hid == ERR_BUFFER_FULL)
+                {
+                    filling = STATE_FULL;
+                    logf("buffer is full for now");
+                    return;  /* No space for track's album art, not an error */
+                }
+                else if (aa_hid < 0)
+                {
+                    /* another error, ignore AlbumArt */
+                    logf("Album art loading failed");
+                }
+                tracks[track_widx].aa_hid[i] = aa_hid;
             }
         }
+        
     }
 #endif
 
@@ -1954,7 +2048,8 @@ static void audio_finish_load_track(void)
     else
         file_offset = 0;
 
-    tracks[track_widx].audio_hid = bufopen(track_id3->path, file_offset, type);
+    tracks[track_widx].audio_hid = bufopen(track_id3->path, file_offset, type,
+                                            NULL);
 
     /* No space left, not an error */
     if (tracks[track_widx].audio_hid == ERR_BUFFER_FULL)
@@ -2558,7 +2653,16 @@ static void audio_thread(void)
                 LOGFQUEUE("audio < Q_AUDIO_TRACK_CHANGED");
                 audio_finalise_track_change();
                 break;
-
+#ifdef HAVE_ALBUMART
+            case Q_AUDIO_HANDOUT_SLOT:
+                LOGFQUEUE("audio < Q_AUDIO_HANDOUT_SLOT");
+                claim_aa_slot((intptr_t*)ev.data);
+                break;
+            case Q_AUDIO_RELEASE_SLOT:
+                LOGFQUEUE("audio < Q_AUDIO_RELEASE_SLOT");
+                release_aa_slot(ev.data);
+                break;
+#endif
 #ifndef SIMULATOR
             case SYS_USB_CONNECTED:
                 LOGFQUEUE("audio < SYS_USB_CONNECTED");
@@ -2681,11 +2785,18 @@ void audio_init(void)
         tracks[i].audio_hid = -1;
         tracks[i].id3_hid = -1;
         tracks[i].codec_hid = -1;
-#ifdef HAVE_ALBUMART
-        tracks[i].aa_hid = -1;
-#endif
         tracks[i].cuesheet_hid = -1;
     }
+#ifdef HAVE_ALBUMART
+    for (i = 0; i < MAX_MULTIPLE_AA; i++)
+    {
+        int j;
+        for (j = 0; j < MAX_TRACK; j++)
+        {
+            tracks[j].aa_hid[i] = -1;
+        }
+    }
+#endif
 
     add_event(BUFFER_EVENT_REBUFFER, false, buffering_handle_rebuffer_callback);
     add_event(BUFFER_EVENT_FINISHED, false, buffering_handle_finished_callback);
diff --git a/apps/playback.h b/apps/playback.h
index 378d5ef..2b376ac 100644
--- a/apps/playback.h
+++ b/apps/playback.h
@@ -25,6 +25,14 @@
 #include <stdbool.h>
 #include "config.h"
 
+#ifdef HAVE_ALBUMART
+int playback_current_aa_hid(int slot);
+
+int playback_claim_aa_slot(bool (*size_func)(int*,int*));
+
+void playback_release_aa_slot(int slot);
+#endif
+
 /* Functions */
 const char *get_codec_filename(int cod_spec);
 void voice_wait(void);
@@ -45,9 +53,6 @@ bool audio_restore_playback(int type); /* Restores the audio buffer to handle th
 void codec_thread_do_callback(void (*fn)(void),
                               unsigned int *codec_thread_id);
 
-#ifdef HAVE_ALBUMART
-int audio_current_aa_hid(void);
-#endif
 
 #if CONFIG_CODEC == SWCODEC /* This #ifdef is better here than gui/wps.c */
 extern void audio_next_dir(void);
diff --git a/apps/plugin.c b/apps/plugin.c
index a205926..77fb925 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -802,8 +802,6 @@ int plugin_load(const char* plugin, const void* parameter)
 #ifdef HAVE_TOUCHSCREEN
     touchscreen_set_mode(global_settings.touch_mode);
 #endif
-    
-    viewportmanager_set_statusbar(oldbars);
 
     button_clear_queue();
 
@@ -832,13 +830,14 @@ int plugin_load(const char* plugin, const void* parameter)
 #endif
 #endif
 
+    viewportmanager_set_statusbar(oldbars);
+    viewport_set_current_vp(NULL);
+
     if (rc != PLUGIN_GOTO_WPS)
     {
         send_event(GUI_EVENT_REFRESH, NULL);
     }
 
-    viewportmanager_set_statusbar(oldbars);
-    viewport_set_current_vp(NULL);
     if (pfn_tsr_exit == NULL)
         plugin_loaded = false;
 
diff --git a/apps/plugin.h b/apps/plugin.h
index b060104..f58585f 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -789,7 +789,8 @@ struct plugin_api {
 
 #if (CONFIG_CODEC == SWCODEC)
     /* buffering API */
-    int (*bufopen)(const char *file, size_t offset, enum data_type type);
+    int (*bufopen)(const char *file, size_t offset, enum data_type type,
+                   void *user);
     int (*bufalloc)(const void *src, size_t size, enum data_type type);
     bool (*bufclose)(int handle_id);
     int (*bufseek)(int handle_id, size_t newpos);
diff --git a/apps/recorder/albumart.c b/apps/recorder/albumart.c
index bb2fae4..492d0f4 100644
--- a/apps/recorder/albumart.c
+++ b/apps/recorder/albumart.c
@@ -272,9 +272,11 @@ bool search_albumart_files(const struct mp3entry *id3, const char *size_string,
 
 #ifndef PLUGIN
 /* Look for albumart bitmap in the same dir as the track and in its parent dir.
+ * Calls size_func to get the dimensions to look for
  * Stores the found filename in the buf parameter.
  * Returns true if a bitmap was found, false otherwise */
-bool find_albumart(const struct mp3entry *id3, char *buf, int buflen)
+bool find_albumart(const struct mp3entry *id3, char *buf, int buflen,
+                    bool (*size_func)(int *w, int *h))
 {
     if (!id3 || !buf)
         return false;
@@ -282,7 +284,7 @@ bool find_albumart(const struct mp3entry *id3, char *buf, int buflen)
     char size_string[9];
     int width = 0, height = 0;
 
-    if (!wps_uses_albumart(&width, &height))
+    if (!size_func(&width, &height))
         return false;
 
     logf("Looking for album art for %s", id3->path);
@@ -376,17 +378,4 @@ void draw_album_art(struct gui_wps *gwps, int handle_id, bool clear)
     }
 }
 
-void get_albumart_size(struct bitmap *bmp)
-{
-    /* FIXME: What should we do with albumart on remote? */
-    int width, height;
-
-    if (!wps_uses_albumart(&width, &height))
-    {
-        width = 0; height = 0;
-    }
-
-    bmp->width = width;
-    bmp->height = height;
-}
 #endif /* PLUGIN */
diff --git a/apps/recorder/albumart.h b/apps/recorder/albumart.h
index d1c2dfa..40070e5 100644
--- a/apps/recorder/albumart.h
+++ b/apps/recorder/albumart.h
@@ -29,9 +29,11 @@
 #include "skin_engine/skin_engine.h"
 
 /* Look for albumart bitmap in the same dir as the track and in its parent dir.
+ * Calls size_func to get the dimensions to look for
  * Stores the found filename in the buf parameter.
  * Returns true if a bitmap was found, false otherwise */
-bool find_albumart(const struct mp3entry *id3, char *buf, int buflen);
+bool find_albumart(const struct mp3entry *id3, char *buf, int buflen,
+                    bool (*size_func)(int *w, int *h));
 
 /* Draw the album art bitmap from the given handle ID onto the given WPS.
    Call with clear = true to clear the bitmap instead of drawing it. */
diff --git a/apps/root_menu.c b/apps/root_menu.c
index feff126..302e889 100644
--- a/apps/root_menu.c
+++ b/apps/root_menu.c
@@ -73,6 +73,9 @@ struct root_items {
 static int last_screen = GO_TO_ROOT; /* unfortunatly needed so we can resume
                                         or goto current track based on previous
                                         screen */
+                                        
+int next_screen = GO_TO_ROOT;
+
 static char current_track_path[MAX_PATH];
 static void rootmenu_track_changed_callback(void* param)
 {
@@ -504,10 +507,14 @@ void previous_music_is_wps(void)
     previous_music = GO_TO_WPS;
 }
 
+int current_screen(void)
+{
+    return next_screen;
+}
+
 void root_menu(void)
 {
     int previous_browser = GO_TO_FILEBROWSER;
-    int next_screen = GO_TO_ROOT;
     int selected = 0;
 
     if (global_settings.start_in_screen == 0)
diff --git a/apps/root_menu.h b/apps/root_menu.h
index 2ab3aae..584e048 100644
--- a/apps/root_menu.h
+++ b/apps/root_menu.h
@@ -58,4 +58,6 @@ extern const struct menu_item_ex root_menu_;
 
 extern void previous_music_is_wps(void);
 
+extern int current_screen(void);
+
 #endif /* __ROOT_MENU_H__ */
diff --git a/apps/settings.c b/apps/settings.c
index ce888c4..90b6bc9 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -71,6 +71,7 @@
 #include "wps.h"
 #include "skin_engine/skin_engine.h"
 #include "viewport.h"
+#include "statusbar-skinned.h"
 
 #if CONFIG_CODEC == MAS3507D
 void dac_line_in(bool enable);
@@ -740,6 +741,17 @@ void settings_apply_skins(void)
     char buf[MAX_PATH];
     /* re-initialize the skin buffer before we start reloading skins */
     skin_buffer_init();
+    if ( global_settings.sbs_file[0] &&
+        global_settings.sbs_file[0] != 0xff ) {
+        snprintf(buf, sizeof buf, WPS_DIR "/%s.sbs",
+                global_settings.sbs_file);
+        sb_skin_data_load(SCREEN_MAIN, buf, true);
+    }
+    else
+    {
+        sb_skin_data_init(SCREEN_MAIN);
+        sb_skin_data_load(SCREEN_MAIN, NULL, true);
+    }
     if ( global_settings.wps_file[0] &&
         global_settings.wps_file[0] != 0xff ) {
         snprintf(buf, sizeof buf, WPS_DIR "/%s.wps",
@@ -752,6 +764,17 @@ void settings_apply_skins(void)
         wps_data_load(SCREEN_MAIN, NULL, true);
     }
 #if defined(HAVE_REMOTE_LCD) && (NB_SCREENS > 1)
+    if ( global_settings.rsbs_file[0] &&
+        global_settings.rsbs_file[0] != 0xff ) {
+        snprintf(buf, sizeof buf, WPS_DIR "/%s.rsbs",
+                global_settings.rsbs_file);
+        sb_skin_data_load(SCREEN_REMOTE, buf, true);
+    }
+    else
+    {
+        sb_skin_data_init(SCREEN_REMOTE);
+        sb_skin_data_load(SCREEN_REMOTE, NULL, true);
+    }
     if ( global_settings.rwps_file[0]) {
         snprintf(buf, sizeof buf, WPS_DIR "/%s.rwps",
                 global_settings.rwps_file);
@@ -763,6 +786,7 @@ void settings_apply_skins(void)
         wps_data_load(SCREEN_REMOTE, NULL, true);
     }
 #endif
+    viewportmanager_theme_changed(THEME_STATUSBAR);
 }
 
 void settings_apply(bool read_disk)
@@ -997,7 +1021,10 @@ void settings_apply(bool read_disk)
 #if defined(HAVE_RECORDING) && CONFIG_CODEC == SWCODEC
     enc_global_settings_apply();
 #endif
-    viewportmanager_theme_changed(THEME_ALL);
+#ifdef HAVE_LCD_BITMAP
+    /* already called with THEME_STATUSBAR in settings_apply_skins() */
+    viewportmanager_theme_changed(THEME_UI_VIEWPORT|THEME_LANGUAGE);
+#endif
 }
 
 
diff --git a/apps/settings.h b/apps/settings.h
index ad250ed..09bf605 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -60,6 +60,7 @@ struct opt_items {
 #define FONT_DIR    ROCKBOX_DIR "/fonts"
 #define LANG_DIR    ROCKBOX_DIR "/langs"
 #define WPS_DIR     ROCKBOX_DIR "/wps"
+#define SB_DIR      ROCKBOX_DIR "/statusbar"
 #define THEME_DIR   ROCKBOX_DIR "/themes"
 #define ICON_DIR    ROCKBOX_DIR "/icons"
 
@@ -548,6 +549,7 @@ struct user_settings
     int peak_meter_max; /* range maximum */
 
     unsigned char wps_file[MAX_FILENAME+1];  /* last wps */
+    unsigned char sbs_file[MAX_FILENAME+1];  /* last statusbar skin */
     unsigned char lang_file[MAX_FILENAME+1]; /* last language */
     unsigned char playlist_catalog_dir[MAX_FILENAME+1];
     int skip_length; /* skip length */
@@ -723,6 +725,7 @@ struct user_settings
     unsigned char remote_icon_file[MAX_FILENAME+1];
     unsigned char remote_viewers_icon_file[MAX_FILENAME+1];
     unsigned char rwps_file[MAX_FILENAME+1];  /* last remote-wps */
+    unsigned char rsbs_file[MAX_FILENAME+1];  /* last remote statusbar skin */
 #ifdef HAS_REMOTE_BUTTON_HOLD
     int remote_backlight_on_button_hold; /* what to do with remote backlight when hold
                                             switch is on */
diff --git a/apps/settings_list.c b/apps/settings_list.c
index 62a9351..9499ecf 100644
--- a/apps/settings_list.c
+++ b/apps/settings_list.c
@@ -199,6 +199,7 @@ static const char graphic_numeric[] = "graphic,numeric";
 
 /* Default theme settings */
 #define DEFAULT_WPSNAME  "cabbiev2"
+#define DEFAULT_SBS_NAME DEFAULT_WPSNAME
 
 #ifdef HAVE_LCD_BITMAP
 
@@ -631,14 +632,14 @@ const struct settings_list settings[] = {
                     ID2P(LANG_INVERT_CURSOR_BAR)),
  #endif
     CHOICE_SETTING(F_THEMESETTING|F_TEMPVAR, statusbar,
-                  LANG_STATUS_BAR, STATUSBAR_TOP, "statusbar","off,top,bottom",
-                  NULL, 3, ID2P(LANG_OFF), ID2P(LANG_STATUSBAR_TOP),
-                  ID2P(LANG_STATUSBAR_BOTTOM)),
+                  LANG_STATUS_BAR, STATUSBAR_TOP, "statusbar","off,top,bottom,custom",
+                  NULL, 4, ID2P(LANG_OFF), ID2P(LANG_STATUSBAR_TOP),
+                  ID2P(LANG_STATUSBAR_BOTTOM), ID2P(LANG_STATUSBAR_CUSTOM)),
 #ifdef HAVE_REMOTE_LCD
     CHOICE_SETTING(F_THEMESETTING|F_TEMPVAR, remote_statusbar,
-                  LANG_REMOTE_STATUSBAR, STATUSBAR_TOP, "remote statusbar","off,top,bottom",
-                  NULL, 3, ID2P(LANG_OFF), ID2P(LANG_STATUSBAR_TOP),
-                  ID2P(LANG_STATUSBAR_BOTTOM)),
+                  LANG_REMOTE_STATUSBAR, STATUSBAR_TOP, "remote statusbar","off,top,bottom,custom",
+                  NULL, 4, ID2P(LANG_OFF), ID2P(LANG_STATUSBAR_TOP),
+                  ID2P(LANG_STATUSBAR_BOTTOM), ID2P(LANG_STATUSBAR_CUSTOM)),
 #endif
     CHOICE_SETTING(F_THEMESETTING|F_TEMPVAR, scrollbar,
                   LANG_SCROLL_BAR, SCROLLBAR_LEFT, "scrollbar","off,left,right",
@@ -1439,9 +1440,13 @@ const struct settings_list settings[] = {
                      DEFAULT_FONTNAME, FONT_DIR "/", ".fnt"),
 #endif
     TEXT_SETTING(F_THEMESETTING,wps_file, "wps",
-                     DEFAULT_WPSNAME, WPS_DIR "/", ".wps"),
+                     DEFAULT_SBS_NAME, WPS_DIR "/", ".wps"),
+    TEXT_SETTING(F_THEMESETTING,sbs_file, "sbs",
+                     DEFAULT_WPSNAME, WPS_DIR "/", ".sbs"),
     TEXT_SETTING(0,lang_file,"lang","",LANG_DIR "/",".lng"),
 #ifdef HAVE_REMOTE_LCD
+    TEXT_SETTING(F_THEMESETTING,rsbs_file, "rsbs",
+                     DEFAULT_SBS_NAME, WPS_DIR "/", ".rsbs"),
     TEXT_SETTING(F_THEMESETTING,rwps_file,"rwps",
                      DEFAULT_WPSNAME, WPS_DIR "/", ".rwps"),
 #endif
diff --git a/apps/status.c b/apps/status.c
index 92f29c1..485ca0a 100644
--- a/apps/status.c
+++ b/apps/status.c
@@ -27,12 +27,7 @@
 #include "radio.h"
 #endif
 
-static enum playmode ff_mode;
-
-void status_init(void)
-{
-    ff_mode = 0;
-}
+static enum playmode ff_mode = 0;
 
 void status_set_ffmode(enum playmode mode)
 {
diff --git a/apps/status.h b/apps/status.h
index 8a7a69b..36b723e 100644
--- a/apps/status.h
+++ b/apps/status.h
@@ -41,7 +41,6 @@ enum playmode
     STATUS_RADIO_PAUSE
 };
 
-void status_init(void);
 void status_set_ffmode(enum playmode mode);
 enum playmode status_get_ffmode(void);
 int current_playmode(void);
diff --git a/wps/WPSLIST b/wps/WPSLIST
index 662fb82..9b52416 100644
--- a/wps/WPSLIST
+++ b/wps/WPSLIST
@@ -44,6 +44,8 @@ selector type: bar (inverse)
 <wps>
 Name: rockbox_default.wps
 RWPS: rockbox_default.rwps
+SBS:
+RSBS:
 Author: Rockbox team
 Font: 08-Schumacher-Clean.fnt
 Font.11x2x1:
@@ -59,6 +61,8 @@ selector type: bar (inverse)
 <wps>
 Name: boxes.wps
 rwps: boxes.rwps
+SBS:
+RSBS:
 Author: Christi Scarborough (after Magnus Westerlund)
 Font: 08-Schumacher-Clean.fnt
 Statusbar: top
@@ -76,6 +80,8 @@ remote ui viewport: -
 <wps>
 Name: engineeer2.wps
 RWPS: engineeer2.rwps
+SBS:
+RSBS:
 Author: Magnus Westerlund
 Font: 08-Nedore.fnt
 Statusbar: top
@@ -90,6 +96,8 @@ remote ui viewport: -
 <wps>
 Name: iAmp.wps
 RWPS: iAmp.rwps
+SBS:
+RSBS:
 Author: Raymond Hoh
 Font: 12-Nimbus.fnt
 backdrop:
@@ -104,6 +112,8 @@ remote ui viewport: -
 <wps>
 Name: zezayer.wps
 RWPS: zezayer.rwps
+SBS:
+RSBS:
 Author: Jake Owen
 Font: 08-Nedore.fnt
 Statusbar: top
@@ -153,6 +163,8 @@ Statusbar: top
 <wps>
 Name: DancePuffDuo.wps
 RWPS: DancePuffDuo.rwps
+SBS:
+RSBS:
 Author: Chris Oxtoby
 Font: 13-Nimbus.fnt
 Statusbar: top
@@ -176,6 +188,8 @@ Statusbar: top
 <wps>
 Name: iCatcher.wps
 RWPS: iCatcher.rwps
+SBS:
+RSBS:
 Author: Ioannis Koutoulakis
 Font.112x64x1: 08-Rockfont.fnt
 Font.128x64x1: 08-Rockfont.fnt
@@ -212,6 +226,8 @@ Statusbar: top
 <wps>
 Name: UniCatcher.wps
 RWPS: UniCatcher.rwps
+SBS:
+RSBS:
 Author: Ioannis Koutoulakis
 Font: 16-GNU-Unifont.fnt
 Foreground Color: 000000
@@ -235,6 +251,8 @@ Statusbar: top
 <wps>
 Name: Rockboxed.wps
 RWPS: rockbox_default.rwps
+SBS:
+RSBS:
 Author: Roan Horning
 Font.112x64x1: 08-Schumacher-Clean.fnt
 Font.128x64x1: 08-Schumacher-Clean.fnt
@@ -268,6 +286,8 @@ Author: Jens Arnold
 <wps>
 Name: cabbiev2.wps
 RWPS: cabbiev2.rwps
+SBS:
+RSBS:
 # Real name of the creator of the WPS
 Author: Johannes Voggenthaler, Apoo Maha, Marc Guay, Alex Vanderpol, Jerry Lange, Keith Perri, Mark Fawcus, and Marianne Arnold with support from Rockbox developers and forums. Based on Cabbie by Yohann Misquitta.
 
@@ -349,6 +369,7 @@ viewers iconset.240x400x16: /.rockbox/icons/tango_small_viewers.bmp
 
 # Whether the WPS is designed to have the statusbar on or off
 Statusbar: top
+remote statusbar: top
 
 # list & remote ui viewports
 ui viewport: -
diff --git a/wps/wpsbuild.pl b/wps/wpsbuild.pl
index e9e5c29..7378f30 100755
--- a/wps/wpsbuild.pl
+++ b/wps/wpsbuild.pl
@@ -32,9 +32,18 @@ my $cppdef = $target;
 my @depthlist = ( 16, 8, 4, 2, 1 );
 
 # These parameters are filled in as we parse wpslist
+my $req_size;
+my $req_g_wps;
+my $req_t;
+my $req_t_wps;
 my $wps;
 my $wps_prefix;
+my $sbs_prefix;
 my $rwps;
+my $sbs;
+my $sbs_w_size;
+my $rsbs;
+my $rsbs_w_size;
 my $width;
 my $height;
 my $font;
@@ -42,9 +51,6 @@ my $fgcolor;
 my $bgcolor;
 my $statusbar;
 my $author;
-my $req_g;
-my $req_g_wps;
-my $req_t_wps;
 my $backdrop;
 my $lineselectstart;
 my $lineselectend;
@@ -191,12 +197,26 @@ sub copywps
     my $dir;
     my @filelist;
     my $file;
+    my $__sb;
 
     if($wpslist =~ /(.*)WPSLIST/) {
         $dir = $1;
-#        system("cp $dir/$wps .rockbox/wps/");
-        #print "$req_t_wps $req_g_wps\n";
+        $__sb = $sbs_prefix . "." . $req_size . ".sbs";
+        #print "$req_t_wps $req_g_wps $sbs_prefix\n";
+        #print "$dir/$__sb\n";
 
+#        system("cp $dir/$wps .rockbox/wps/");
+        # check for <name>.WIDTHxHEIGHTxDEPTH.sbs
+        if (-e "$dir/$__sb") {
+            system("cp $dir/$__sb $rbdir/wps/$sbs");
+        }
+        # check for <name>.WIDTHxHEIGHTxDEPTH.<model>.sbs and overwrite the
+        # previous sb if needed
+        $__sb = $sbs_prefix . "." . $req_size . "." . $modelname . ".sbs";
+        if (-e "$dir/$__sb") {
+            system("cp $dir/$__sb $rbdir/wps/$sbs");
+        }
+        
         if (-e "$dir/$req_t_wps" ) {
           system("cp $dir/$req_t_wps $rbdir/wps/$wps");
 
@@ -210,9 +230,9 @@ sub copywps
            close(WPSFILE);
 
            if ($#filelist >= 0) {
-              if (-e "$dir/$wps_prefix/$req_g") {
+              if (-e "$dir/$wps_prefix/$req_size") {
                  foreach $file (@filelist) {
-                     system("cp $dir/$wps_prefix/$req_g/$file $rbdir/wps/$wps_prefix/");
+                     system("cp $dir/$wps_prefix/$req_size/$file $rbdir/wps/$wps_prefix/");
                  }
               }
               elsif (-e "$dir/$wps_prefix") {
@@ -235,7 +255,7 @@ sub copywps
 
 sub buildcfg {
     my $cfg = $wps;
-    my @out;
+    my @out;    
 
     $cfg =~ s/\.(r|)wps/.cfg/;
 
@@ -247,6 +267,20 @@ sub buildcfg {
 wps: /$rbdir/wps/$wps
 MOO
 ;
+    if(defined($sbs)) {
+        if ($sbs eq '') {
+            push @out, "sbs: -\n";
+        } else {
+            push @out, "sbs: /$rbdir/wps/$sbs\n";
+        }
+    }
+    if(defined($sbs)  && $has_remote) {
+        if ($rsbs eq '') {
+            push @out, "rsbs: -\n";
+        } else {
+            push @out, "rsbs: /$rbdir/wps/$rsbs\n";
+        }
+    }
     if($font) {
         push @out, "font: /$rbdir/fonts/$font\n";
     }
@@ -264,7 +298,7 @@ MOO
     }
     if(defined($backdrop)) {
         if ($backdrop eq '') {
-            push @out, "backdrop:\n";
+            push @out, "backdrop: -\n";
         } else {
             # clip resolution from filename
             $backdrop =~ s/(\.[0-9]*x[0-9]*x[0-9]*)//;
@@ -338,6 +372,8 @@ while(<WPS>) {
         undef $wps;
         undef $wps_prefix;
         undef $rwps;
+        undef $sbs;
+        undef $rsbs;
         undef $width;
         undef $height;
         undef $font;
@@ -386,23 +422,24 @@ while(<WPS>) {
             foreach my $d (@depthlist) {
                 next if ($d > $rdepth);
 
-                $req_g = $rwidth . "x" . $rheight . "x" . $d;
+                $req_size = $rwidth . "x" . $rheight . "x" . $d;
 
                 # check for model specific wps
-                $req_g_wps = $wps_prefix . "." . $req_g . "." . $modelname . ".wps";
+                $req_g_wps = $wps_prefix . "." . $req_size . "." . $modelname . ".wps";
                 last if (-e "$wpsdir/$req_g_wps");
 
-                $req_g_wps = $wps_prefix . "." . $req_g . ".wps";
+                # check for normal wps (with WIDTHxHEIGHTxDEPTH)
+                $req_g_wps = $wps_prefix . "." . $req_size . ".wps";
                 last if (-e "$wpsdir/$req_g_wps");
 
                 if ($isrwps) {
-                    $req_g = $req_g . "." . $main_width . "x" . $main_height . "x" . "$main_depth";
+                    $req_size = $req_size . "." . $main_width . "x" . $main_height . "x" . "$main_depth";
 
-                    $req_g_wps = $wps_prefix . "." . $req_g . ".wps";
+                    $req_g_wps = $wps_prefix . "." . $req_size . ".wps";
                     last if (-e "$wpsdir/$req_g_wps");
                 }
             }
-            $req_t_wps = $wps_prefix . ".txt" . ".wps";
+            $req_t_wps = $wps_prefix . $req_t . ".wps";
 
             #print "LCD: $wps wants $width x $height\n";
             #print "LCD: is $rwidth x $rheight\n";
@@ -454,6 +491,20 @@ while(<WPS>) {
         elsif($l =~ /^RWPS\.${main_width}x${main_height}x$main_depth: *(.*)/i) {
             $rwps = $1;
         }
+        elsif($l =~ /^SBS: *(.*)/i) {
+            $sbs = $sbs_prefix = $1;
+            $sbs_prefix =~ s/\.(r|)sbs//;
+        }
+        elsif($l =~ /^SBS\.${main_width}x${main_height}x$main_depth: *(.*)/i) {
+            $sbs = $sbs_prefix = $1;
+            $sbs_prefix =~ s/\.(r|)sbs//;
+        }
+        elsif($l =~ /^RSBS: *(.*)/i) {
+            $rsbs = $1;
+        }
+        elsif($l =~ /^RSBS\.${main_width}x${main_height}x$main_depth: *(.*)/i) {
+            $rsbs = $1;
+        }
         elsif($l =~ /^Author: *(.*)/i) {
             $author = $1;
         }
@@ -526,9 +577,15 @@ while(<WPS>) {
         elsif($l =~ /^ui viewport: *(.*)/i) {
             $listviewport = $1;
         }
+        elsif($l =~ /^ui viewport\.${main_width}x${main_height}x$main_depth: *(.*)/i) {
+            $listviewport = $1;
+        }
         elsif($l =~ /^remote ui viewport: *(.*)/i) {
             $remotelistviewport = $1;
         }
+        elsif($l =~ /^remote ui viewport\.${main_width}x${main_height}x$main_depth: *(.*)/i) {
+            $remotelistviewport = $1;
+        }
         else{
             #print "Unknown line:  $l!\n";
         }
