Index: apps/plugins/battery_bench.c =================================================================== --- apps/plugins/battery_bench.c (revision 11988) +++ apps/plugins/battery_bench.c (working copy) @@ -99,7 +99,7 @@ /****************************** Plugin Entry Point ****************************/ static struct plugin_api* rb; int main(void); -bool exit_tsr(bool); +bool exit_tsr(bool,void*); void thread(void); @@ -122,10 +122,11 @@ struct event_queue thread_q; -bool exit_tsr(bool reenter) +bool exit_tsr(bool reenter,void* param) { bool exit = true; (void)reenter; + (void)param; rb->lcd_clear_display(); rb->lcd_puts_scroll(0, 0, "Batt.Bench is currently running."); rb->lcd_puts_scroll(0, 1, "Press OFF to cancel the test"); Index: apps/plugins/alpine_cdc.c =================================================================== --- apps/plugins/alpine_cdc.c (revision 11988) +++ apps/plugins/alpine_cdc.c (working copy) @@ -1120,8 +1120,9 @@ } /* callback to end the TSR plugin, called before a new one gets loaded */ -bool exit_tsr(bool reenter) +bool exit_tsr(bool reenter,void* param) { + (void)param; if (reenter) return false; /* dont let it start again */ gTread.exiting = true; /* tell the thread to end */ Index: apps/plugins/wps_extensions.c =================================================================== --- apps/plugins/wps_extensions.c (revision 0) +++ apps/plugins/wps_extensions.c (revision 0) @@ -0,0 +1,391 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: $ + * + * Copyright (C) 2007 Jonathan Gordon + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "plugin.h" +#include "lib/configfile.h" +PLUGIN_HEADER +static struct plugin_api* rb; +#define SETTINGS_VERSION 1 +#define SETTINGS_MIN_VERSION 1 +#define SETTINGS_FILENAME "wps_extensions.cfg" +static char* options_text[] = {"No", "Yes"}; + +#define CONFIG_ITEM(num,text) {TYPE_ENUM, 0, 2, \ + &extensions[num].enabled, text, options_text, NULL} + +/* To add a new tag.. + (1) Add the funtion to the list below + (2) Add the tag info to the entensions array + (3) Add the option to the config array + (4) Add the actual function +*/ + + +plugin_tag_cb runtime, volume_in_percent +#ifdef HAVE_LCD_BITMAP +,album_art_init,album_art_display +#endif +; + + +struct { + char tag[3]; + int enabled; + bool preload_tag; + plugin_tag_cb *function; +} extensions[] = { + {"rt",1,false,runtime}, + {"pv",0,false,volume_in_percent}, /* disable by default */ +#ifdef HAVE_LCD_BITMAP + {"Cl",1,true,album_art_init}, + {"C",1,false,album_art_display}, +#endif +}; + +static struct configdata config[] = +{ + CONFIG_ITEM(0,"Runtime (%rt)"), + CONFIG_ITEM(1,"Volume in % (%pv)"), +#ifdef HAVE_LCD_BITMAP + CONFIG_ITEM(2,"Album Art (preloaded-tag)(%Cl"), + CONFIG_ITEM(3,"Album Art (preloaded-tag)(%Cl"), +#endif +}; +#ifdef HAVE_LCD_BITMAP +#define NUM_EXTENSIONS 4 +#else +#define NUM_EXTENSIONS 2 +#endif + +#define UNUSED_PARAMS (void)wps_data;(void)cid3;(void)nid3; \ + (void)tag,(void)tag_len;(void)subline_time_mult; + +/* %rt */ +char* runtime(struct wps_data* wps_data, + struct mp3entry* cid3, + struct mp3entry* nid3, + const char* tag, + char* buf, + int buf_size, + unsigned char* tag_len, + unsigned short* subline_time_mult, + unsigned char* flags, + int *intval) +{ + UNUSED_PARAMS + int t = rb->global_settings->runtime; + rb->snprintf(buf,buf_size,"%dh %dm %ds", + t / 3600, (t % 3600) / 60, t % 60); + *flags |= WPS_REFRESH_DYNAMIC; + *intval = t; + return buf; +} + +/* %pv - shows the volume as a percentage instead of dB */ +char* volume_in_percent(struct wps_data* wps_data, + struct mp3entry* cid3, + struct mp3entry* nid3, + const char* tag, + char* buf, + int buf_size, + unsigned char* tag_len, + unsigned short* subline_time_mult, + unsigned char* flags, + int *intval) +{ + UNUSED_PARAMS + int volume = (100*(rb->global_settings->volume-rb->sound_min(SOUND_VOLUME)) / + (rb->sound_max(SOUND_VOLUME)-rb->sound_min(SOUND_VOLUME))); + *flags |= WPS_REFRESH_DYNAMIC; + rb->snprintf(buf, buf_size, "%d", volume); + *intval = volume; + return buf; +} +/* Album Art %C */ +#ifdef HAVE_LCD_BITMAP +#define AA_BUFSIZE (LCD_HEIGHT*LCD_WIDTH*LCD_DEPTH/8) +int aa_x, aa_y, aa_width = 0, aa_height = 0; +#define IMAGE_DIM(a,b) ((astrchr(tag,'|'); + while (s) + { + s++; /* skip over the | */ + switch (option) + { + case 0: + aa_x = rb->atoi(s); + break; + case 1: + aa_y = rb->atoi(s); + break; + case 2: + /* check for the letters */ + while (*s < '0' || *s > '9') + s++; + aa_width = rb->atoi(s); + break; + case 3: + /* check for the letters */ + while (*s < '0' || *s > '9') + s++; + aa_height = rb->atoi(s); + break; + } + option ++; + s = rb->strchr(s,'|'); + } + *tag_len = rb->strlen(tag); + loaded = true; + return (char*)1; +} + +char* album_art_display(struct wps_data* wps_data, + struct mp3entry* cid3, + struct mp3entry* nid3, + const char* tag, + char* buf, + int buf_size, + unsigned char* tag_len, + unsigned short* subline_time_mult, + unsigned char* flags, + int *intval) +{ + UNUSED_PARAMS + (void)buf;(void)buf_size;(void)flags;(void)intval; + static char img_buffer[AA_BUFSIZE]; + static char *img_buf_ptr = img_buffer; + static int img_buf_free = AA_BUFSIZE; + static struct bitmap bm; + static char track_filename[MAX_PATH] = {0}; + static bool art_loaded = false; + char img_filename[MAX_PATH]; + + if (!track_filename[0] || rb->strcmp(track_filename,cid3->path)) + { + char *s; + char path[MAX_PATH]; + int fd = -1; + art_loaded = false; + rb->strcpy(track_filename,cid3->path); + rb->strcpy(img_filename,cid3->path); + rb->strcpy(path,cid3->path); + s = rb->strrchr(path,'/'); + if (s) *s = '\0'; + else rb->strcpy(path,""); + /* filename priority + 1. ./filename .bmp - same filename as currently playing music file + 2. ./albumtitle .bmp - name of the album, found in metadata of the music file + 3. ./cover.bmp + 4. ../albumtitle .bmp + 5. ../cover.bmp + */ + /* (1) */ + s = rb->strrchr(img_filename,'.'); + if (s) + { + rb->strcpy(s+1,"bmp"); + fd = rb->open(img_filename,O_RDONLY); + } + /* (2) */ + if (fd<0) + { + rb->snprintf(img_filename,MAX_PATH,"%s/%s.bmp",path,cid3->album); + fd = rb->open(img_filename,O_RDONLY); + } + /* (3) */ + if (fd<0) + { + rb->snprintf(img_filename,MAX_PATH,"%s/cover.bmp",path); + fd = rb->open(img_filename,O_RDONLY); + } + s = rb->strrchr(path,'/'); + if (s) *s = '\0'; + else rb->strcpy(path,""); + /* (4) */ + if (fd<0) + { + rb->snprintf(img_filename,MAX_PATH,"%s/%s.bmp",path,cid3->album); + fd = rb->open(img_filename,O_RDONLY); + } + /* (5) */ + if (fd<0) + { + rb->snprintf(img_filename,MAX_PATH,"%s/cover.bmp",path); + fd = rb->open(img_filename,O_RDONLY); + } + /* now see if one of te files worked */ + if (fd >= 0) + { + int ret; + rb->close(fd); + bm.data = img_buf_ptr; + ret = rb->read_bmp_file(img_filename, &bm, + img_buf_free, + FORMAT_ANY|FORMAT_TRANSPARENT); + if (ret > 0) + { +#if LCD_DEPTH == 16 + if (ret % 2) ret++; + /* Always consume an even number of bytes */ +#endif + img_buf_ptr += ret; + img_buf_free -= ret; + art_loaded = true; + } + } + } + if (art_loaded) + { + rb->lcd_bitmap_transparent((fb_data*)bm.data, aa_x, aa_y, + IMAGE_DIM(aa_width,bm.width), + IMAGE_DIM(aa_height,bm.height)); + rb->lcd_update_rect(aa_x, aa_y, + IMAGE_DIM(aa_width,bm.width), + IMAGE_DIM(aa_height,bm.height)); + *intval = 1; + } + *intval = 2; + return NULL; +} + +#endif /* HAVE_LCD_BITMAP */ +/* shouldnt need to edit below here */ +/*ee|tag */ +char* is_extension_enabled(struct wps_data* wps_data, + struct mp3entry* cid3, + struct mp3entry* nid3, + const char* tag, + char* buf, + int buf_size, + unsigned char* tag_len, + unsigned short* subline_time_mult, + unsigned char* flags, + int *intval) +{ + UNUSED_PARAMS + (void)buf;(void)buf_size; + int i; + *intval = 0; + for (i=0; istrncmp(extensions[i].tag,&tag[3], + rb->strlen(extensions[i].tag)) && + extensions[i].function) + { + *intval = extensions[i].enabled?1:2; + } + } + *flags |= WPS_REFRESH_DYNAMIC; + return *intval==1?"yes":"no"; +} + +bool exit_callback(bool reenter,void* parameter) +{ + char* param = (char*)parameter; + if (!reenter || + (parameter == NULL) /* reloading from the browser */) + { + rb->wps_plugin_exiting(); + return true; + } + else if (param[0] == 'U') + return true; /* wps is forcing us to reload */ + return false; /* wps is trying to reload us */ +} + +bool extensions_menu(void) +{ + int m; + int result = 0; + int menu_quit=0; + + static const struct opt_items noyes[2] = { + { "Disabled", -1 }, + { "Enabled", -1 }, + }; + + static struct menu_item items[NUM_EXTENSIONS+2]; + for (m = 0; mmenu_init(items, NUM_EXTENSIONS+1, + NULL, NULL, NULL, NULL); + + rb->button_clear_queue(); + while (!menu_quit) { + result=rb->menu_show(m); + if (result >=0 && result < NUM_EXTENSIONS) + { + rb->set_option(config[result].name,config[result].val,INT, + noyes, 2, NULL); + } + else menu_quit = 1; + } + + rb->menu_exit(m); + + rb->lcd_clear_display(); + rb->lcd_update(); + configfile_save(SETTINGS_FILENAME,config,NUM_EXTENSIONS,SETTINGS_VERSION); + return (result==3); +} +/* this is the plugin entry point */ +enum plugin_status plugin_start(struct plugin_api* api, void* parameter) +{ + int i; + rb = api; + configfile_init(rb); + configfile_load(SETTINGS_FILENAME,config, + NUM_EXTENSIONS,SETTINGS_MIN_VERSION); + + if (parameter != NULL) + { + for (i=0;iwps_register_tag(extensions[i].tag, + extensions[i].preload_tag, + extensions[i].function); + } + rb->wps_register_tag("ee",false,is_extension_enabled); + rb->plugin_tsr(exit_callback); + } + else extensions_menu(); + + return PLUGIN_OK; +} Index: apps/plugins/SOURCES =================================================================== --- apps/plugins/SOURCES (revision 11988) +++ apps/plugins/SOURCES (working copy) @@ -18,6 +18,7 @@ stopwatch.c vbrfix.c viewer.c +wps_extensions.c /* plugins built for all targets, but not for the simulator */ #if !defined(SIMULATOR) Index: apps/gui/gwps-common.c =================================================================== --- apps/gui/gwps-common.c (revision 11988) +++ apps/gui/gwps-common.c (working copy) @@ -59,7 +59,16 @@ static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size); #endif +/* the variables in plugin_tag have the same meanings as in get_tag */ +#define MAX_PLUGIN_TAGS 32 +static struct { + char tag[2]; + bool preloaded; + plugin_tag_cb *function; +} plugin_tags[MAX_PLUGIN_TAGS]; +static bool wps_plugin_loaded = false; + #define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */ /* 3% of 30min file == 54s step size */ #define MIN_FF_REWIND_STEP 500 @@ -112,6 +121,7 @@ if(!data || !buf) return false; char c; + int i; #ifndef HAVE_LCD_BITMAP /* no bitmap-lcd == no bitmap loading */ (void)bmpdir; @@ -124,7 +134,22 @@ if('%' != *buf) return false; buf++; - + for (i=0;wps_plugin_loaded && i -1) + { + plugin_exists = 1; + close(fd); + } + else + { + plugin_exists = 0; + return; + } + /* if it exists fall through to opening it */ + case 1: + wps_plugin_loaded = + plugin_load(WPS_PLUGIN_FILENAME,"WPS") == PLUGIN_OK; + break; + } +} +int wps_register_tag(char *tag,bool is_preload_tag, plugin_tag_cb callback) +{ + int i; + for (i=0;i /* for size_t */ #include "gwps.h" +#ifndef PLUGIN void gui_wps_format_time(char* buf, int buf_size, long time); void fade(bool fade_in); @@ -36,5 +37,30 @@ bool wps_data_preload_tags(struct wps_data *data, char *buf, const char *bmpdir, size_t bmpdirlen); void display_keylock_text(bool locked); +#endif /* PLUGIN */ + +typedef char* plugin_tag_cb(struct wps_data* wps_data, + struct mp3entry* cid3, + struct mp3entry* nid3, + const char* tag, + char* buf, + int buf_size, + unsigned char* tag_len, + unsigned short* subline_time_mult, + unsigned char* flags, + int *intval); +/* if the tag is a preload one, then the above function type is used, + BUT, only wps_data, tag, buf and buf_size are used. + tag is buf from the wps file containng the string to parse + buf is the bmpdirectory for the wps + buf_size is the length of the buf string + The function returns (char*)1 if it was handled, or NULL if not. +*/ +#include "plugin.h" +void load_wps_plugin(void); +int wps_register_tag(char *tag,bool is_preload_tag, plugin_tag_cb callback); +void wps_unregister_tag(int tag); /* the tag is the return of register */ +void wps_plugin_exiting(void); +#define WPS_PLUGIN_FILENAME PLUGIN_DIR "/wps_extensions.rock" #endif Index: apps/gui/gwps.c =================================================================== --- apps/gui/gwps.c (revision 11988) +++ apps/gui/gwps.c (working copy) @@ -106,6 +106,8 @@ action_signalscreenchange(); wps_state_init(); + load_wps_plugin(); /* this shuold be changed to only + load if the wps wants it */ #ifdef HAVE_LCD_CHARCELLS status_set_audio(true); @@ -460,6 +462,7 @@ action_signalscreenchange(); if (main_menu()) return true; + load_wps_plugin(); /* reload the plugin if it was kicked out */ #if LCD_DEPTH > 1 show_wps_backdrop(); #endif Index: apps/plugin.c =================================================================== --- apps/plugin.c (revision 11988) +++ apps/plugin.c (working copy) @@ -59,7 +59,7 @@ /* for actual plugins only, not for codecs */ static bool plugin_loaded = false; static int plugin_size = 0; -static bool (*pfn_tsr_exit)(bool reenter) = NULL; /* TSR exit callback */ +static bool (*pfn_tsr_exit)(bool reenter,void* parameter) = NULL; /* TSR exit callback */ static char current_plugin[MAX_PATH]; static const struct plugin_api rockbox_api = { @@ -487,6 +487,9 @@ pcm_record_more, #endif create_thread_on_core, + wps_register_tag, + wps_unregister_tag, + wps_plugin_exiting }; int plugin_load(const char* plugin, void* parameter) @@ -517,7 +520,7 @@ if (pfn_tsr_exit != NULL) /* if we have a resident old plugin: */ { - if (pfn_tsr_exit(!strcmp(current_plugin,p)) == false ) + if (pfn_tsr_exit(!strcmp(current_plugin,p),parameter) == false ) { /* not allowing another plugin to load */ return PLUGIN_OK; @@ -718,7 +721,7 @@ /* The plugin wants to stay resident after leaving its main function, e.g. runs from timer or own thread. The callback is registered to later instruct it to free its resources before a new plugin gets loaded. */ -void plugin_tsr(bool (*exit_callback)(bool)) +void plugin_tsr(bool (*exit_callback)(bool,void*)) { pfn_tsr_exit = exit_callback; /* remember the callback for later */ } Index: apps/plugin.h =================================================================== --- apps/plugin.h (revision 11988) +++ apps/plugin.h (working copy) @@ -80,6 +80,9 @@ #include "lcd-remote.h" #endif +#include "gwps.h" +#include "gwps-common.h" + #ifdef PLUGIN #if defined(DEBUG) || defined(SIMULATOR) @@ -110,12 +113,12 @@ #define PLUGIN_MAGIC 0x526F634B /* RocK */ /* increase this every time the api struct changes */ -#define PLUGIN_API_VERSION 40 +#define PLUGIN_API_VERSION 41 /* update this to latest version if a change to the api struct breaks backwards compatibility (and please take the opportunity to sort in any new function which are "waiting" at the end of the function table) */ -#define PLUGIN_MIN_API_VERSION 38 +#define PLUGIN_MIN_API_VERSION 41 /* plugin return codes */ enum plugin_status { @@ -528,7 +531,7 @@ int (*set_time)(const struct tm *tm); void* (*plugin_get_buffer)(int* buffer_size); void* (*plugin_get_audio_buffer)(int* buffer_size); - void (*plugin_tsr)(bool (*exit_callback)(bool reenter)); + void (*plugin_tsr)(bool (*exit_callback)(bool reenter,void* parameter)); #if defined(DEBUG) || defined(SIMULATOR) void (*debugf)(const char *fmt, ...); #endif @@ -607,6 +610,9 @@ unsigned int core, void (*function)(void), void* stack, int stack_size, const char *name IF_PRIO(, int priority)); + int (*wps_register_tag)(char *tag,bool preloaded_tag, plugin_tag_cb callback); + void (*wps_unregister_tag)(int tag); + void (*wps_plugin_exiting)(void); }; /* plugin header */ @@ -664,7 +670,7 @@ /* plugin_tsr, callback returns true to allow the new plugin to load, reenter means the currently running plugin is being reloaded */ -void plugin_tsr(bool (*exit_callback)(bool reenter)); +void plugin_tsr(bool (*exit_callback)(bool reenter,void* parameter)); /* defined by the plugin */ enum plugin_status plugin_start(struct plugin_api* rockbox, void* parameter)