From: skeezix Date: Mon, 8 Mar 2010 21:05:29 +0000 (-0500) Subject: First checkin of Minimenu X-Git-Tag: Release-2010-05/1~76 X-Git-Url: https://git.openpandora.org/cgi-bin/gitweb.cgi?p=pandora-libraries.git;a=commitdiff_plain;h=2674535f2806b92169ce862bccd95206fff140e3 First checkin of Minimenu --- diff --git a/Makefile b/Makefile index d5b018b..93b567a 100644 --- a/Makefile +++ b/Makefile @@ -11,9 +11,9 @@ RANLIB = ${CROSSCOMPILE}ranlib RM = rm # environment -VPATH = lib test apps +VPATH = lib test apps minimenu CFLAG_SO = -fPIC #-fPIC not always needed, but good to have -CFLAGS = -Wall -I./include -g ${CFLAG_SO} +CFLAGS = -Wall -I./include -g ${CFLAG_SO} -I/usr/include/SDL CXXFLAGS = -Wall -I./include -g ${CFLAG_SO} # code @@ -23,10 +23,10 @@ SOLIB1 = libpnd.so.1.0.1 # versioned name XMLOBJ = lib/tinyxml/tinystr.o lib/tinyxml/tinyxml.o lib/tinyxml/tinyxmlerror.o lib/tinyxml/tinyxmlparser.o ALLOBJ = pnd_conf.o pnd_container.o pnd_discovery.o pnd_pxml.o pnd_notify.o pnd_locate.o pnd_tinyxml.o pnd_pndfiles.o pnd_apps.o pnd_utility.o pnd_desktop.o pnd_io_gpio.o pnd_logger.o pnd_dbusnotify.o pnd_device.o -all: ${SOLIB} ${LIB} conftest discotest notifytest pndnotifyd rawpxmltest pndvalidator loggertest dbusnotifytest pnd_run pndevmapperd pnd_info evtest +all: ${SOLIB} ${LIB} conftest discotest notifytest pndnotifyd rawpxmltest pndvalidator loggertest dbusnotifytest pnd_run pndevmapperd pnd_info evtest mmenu mmwrapper clean: - ${RM} -f ${ALLOBJ} ${XMLOBJ} ${LIB} ${SOLIB1} locatetest.o bin/locatetest conftest.o bin/conftest discotest.o bin/discotest dbusnotifytest.o bin/dbusnotifytest loggertest.o bin/loggertest bin/notifytest notifytest.o bin/rawpxmltest rawpxmltest.o bin/pnd_run pnd_run.o pnd_info.o bin/pnd_info bin/pndevmapperd pndevmapperd.o bin/pndnotifyd pndnotifyd.o ${SOLIB} testdata/dotdesktop/*.desktop testdata/menu/*.desktop testdata/apps/*.pnd testdata/dotdesktop/*.png deployment/usr/lib/libpnd* deployment/usr/bin/pndnotifyd deployment/usr/bin/pnd_run deployment/usr/bin/pnd_info deployment/usr/pandora/scripts/* deployment/etc/sudoers deployment/etc/init.d/pndnotifyd bin/pndvalidator pndvalidator.o deployment/usr/bin/pndevmapperd testdata/menuicons/* evtest.o bin/evtest + ${RM} -f ${ALLOBJ} ${XMLOBJ} ${LIB} ${SOLIB1} locatetest.o bin/locatetest conftest.o bin/conftest discotest.o bin/discotest dbusnotifytest.o bin/dbusnotifytest loggertest.o bin/loggertest bin/notifytest notifytest.o bin/rawpxmltest rawpxmltest.o bin/pnd_run pnd_run.o pnd_info.o bin/pnd_info bin/pndevmapperd pndevmapperd.o bin/pndnotifyd pndnotifyd.o ${SOLIB} testdata/dotdesktop/*.desktop testdata/menu/*.desktop testdata/apps/*.pnd testdata/dotdesktop/*.png deployment/usr/lib/libpnd* deployment/usr/bin/pndnotifyd deployment/usr/bin/pnd_run deployment/usr/bin/pnd_info deployment/usr/pandora/scripts/* deployment/etc/sudoers deployment/etc/init.d/pndnotifyd bin/pndvalidator pndvalidator.o deployment/usr/bin/pndevmapperd testdata/menuicons/* evtest.o bin/evtest bin/mmenu bin/mmwrapper mmenu.o mmwrapper.o deployment/usr/bin/mmenu deployment/usr/bin/mmwrapper mmcache.o mmui.o mmcat.o ${RM} -rf deployment/media find . -name "*~*" -exec rm {} \; -print @@ -60,6 +60,11 @@ pnd_info: pnd_info.o ${SOLIB1} pndevmapperd: pndevmapperd.o ${SOLIB1} ${CC} -lstdc++ -o bin/pndevmapperd pndevmapperd.o ${SOLIB1} +mmenu: mmenu.o mmui.o mmcache.o mmcat.o ${SOLIB1} + ${CC} -lstdc++ -o bin/mmenu mmenu.o mmui.o mmcache.o mmcat.o ${SOLIB1} -lSDL -lSDL_image -lSDL_ttf -lSDL_gfx +mmwrapper: mmwrapper.o ${SOLIB1} + ${CC} -lstdc++ -o bin/mmwrapper mmwrapper.o ${SOLIB1} + # deployment and assembly components # @@ -88,10 +93,15 @@ deploy: cp bin/pnd_info deployment/usr/bin cp testdata/scripts/* deployment/usr/pandora/scripts cp bin/pndevmapperd deployment/usr/bin + cp bin/mmenu deployment/usr/bin + cp bin/mmwrapper deployment/usr/bin # copy in freebee .pnd apps to /usr/pandora/apps # add pndnotify to etc/rc/startup-whatever cp testdata/sh/pndnotifyd deployment/etc/init.d/pndnotifyd cp testdata/sh/sudoers deployment/etc/sudoers + # minimenu + cp bin/mmenu /deployment/usr/bin + cp bin/mmwrapper /deployment/usr/bin # test tool targets # diff --git a/minimenu/TODO.txt b/minimenu/TODO.txt new file mode 100644 index 0000000..7bd579a --- /dev/null +++ b/minimenu/TODO.txt @@ -0,0 +1,39 @@ + +- libpnd: merge overrides, fix it up; + - full-replacement PXML is one option, another is + - apply override to all sections that match -- by unique-id, so apply override to all sub-apps that apply? + +- libpnd: appdata-dir-name + +- shoulders on panda + +- font! + +- menu + - zotmenu? + - rescan + - shutdown + - quit + +- deploy.. + - .desktop for deply + - Makefile change for building with sdl for djw? + - cp files to deply + - tell ED how to launch it + +- display + - preview pics + - battery indicator? + - status-line at bottom; number of categories, humber of apps found...? + - menu hint text (hit "menu") + +- touchscreen + - launch apps + - pick/rotate category + +- honor render_mask to know what to update +- defer icon or preview-pics + +- future + - add callback to pnd_disco_Search (maybe new func to not break cpas code), so can show number apps found so far + - note taking field diff --git a/minimenu/mmapps.c b/minimenu/mmapps.c new file mode 100644 index 0000000..9a82cbc --- /dev/null +++ b/minimenu/mmapps.c @@ -0,0 +1,159 @@ + +#include /* for FILE etc */ +#include /* for malloc */ +#define __USE_GNU /* for strcasestr */ +#include +#include /* for stat(2) */ +#include +#include +#include + +#include "../lib/pnd_pathiter.h" +#include "pnd_logger.h" +#include "pnd_pxml.h" +#include "pnd_container.h" +#include "pnd_conf.h" +#include "pnd_discovery.h" +#include "pnd_desktop.h" + +#include "mmapps.h" +#include "mmui.h" + +mm_app_t *apps_fullscan ( char *searchpath ) { + mm_app_t *apphead = NULL; + mm_app_t *n = NULL; + + SEARCHPATH_PRE + { + pnd_log ( pndn_debug, "Scanning path '%s'\n", buffer ); + + DIR *d = opendir ( buffer ); + struct dirent *de; + char *c; + char fullpath [ PATH_MAX ]; + + if ( d ) { + + while ( ( de = readdir ( d ) ) ) { + pnd_log ( pndn_debug, " Found file: '%s'\n", de -> d_name ); + + // candidate? + if ( ( c = strrchr ( de -> d_name, '.' ) ) && + ( strcasecmp ( c, ".desktop" ) == 0 ) ) + { + pnd_log ( pndn_debug, " ..filename suggests a .desktop\n" ); + + sprintf ( fullpath, "%s/%s", buffer, de -> d_name ); + + n = apps_fetch_from_dotdesktop ( fullpath ); + + if ( n ) { + // got an app, prepend to the applist + pnd_log ( pndn_rem, "Found application '%s': '%s'\n", n -> dispname, n -> exec ); + if ( ! apphead ) { + apphead = n; + apphead -> next = NULL; + } else { + n -> next = apphead; + apphead = n; + } + } else { + pnd_log ( pndn_debug, " No application found.\n" ); + } // if got an app back + + } else { + pnd_log ( pndn_debug, " ..filename suggests ignore\n" ); + } + + } // while + + closedir ( d ); + } else { + pnd_log ( pndn_warning, "WARN: Couldn't open directory '%s', skipping\n" ); + } + + + } + SEARCHPATH_POST + + return ( apphead ); +} + +mm_app_t *apps_fetch_from_dotdesktop ( char *path ) { + + FILE *f = fopen ( path, "r" ); + char buffer [ 1024 ]; + mm_app_t *p = (mm_app_t *) malloc ( sizeof(mm_app_t) ); + char *c; + + if ( ! f ) { + if ( p ) { + free ( p ); + } + return ( NULL ); + } + + if ( ! p ) { + fclose ( f ); + return ( NULL ); + } + + bzero ( p, sizeof(mm_app_t) ); + + unsigned char apptype = 0; + unsigned char pndcreated = 0; + + while ( fgets ( buffer, 1000, f ) ) { + char *equals; + + // chop + if ( ( c = strchr ( buffer, '\n' ) ) ) { + *c = '\0'; // truncate trailing newline + } + + //pnd_log ( pndn_debug, ".desktop line: '%s'\n", buffer ); + + if ( strcmp ( buffer, PND_DOTDESKTOP_SOURCE ) == 0 ) { + pndcreated = 1; + } + + if ( ( equals = strchr ( buffer, '=' ) ) ) { + *equals = '\0'; + } + + if ( strcasecmp ( buffer, "type" ) == 0 && + strcasecmp ( equals + 1, "application" ) == 0 ) + { + apptype = 1; + } + + if ( strcasecmp ( buffer, "name" ) == 0 ) { + p -> dispname = strdup ( equals + 1 ); + } + + if ( strcasecmp ( buffer, "exec" ) == 0 ) { + p -> exec = strdup ( equals + 1 ); + } + + if ( strcasecmp ( buffer, "icon" ) == 0 ) { + p -> iconpath = strdup ( equals + 1 ); + } + + } // while + + fclose ( f ); + + if ( ! apptype ) { + pnd_log ( pndn_debug, ".desktop is not an application; ignoring.\n" ); + free ( p ); + return ( NULL ); // not an application + } + + if ( ! pndcreated ) { + pnd_log ( pndn_debug, ".desktop is not from libpnd; ignoring.\n" ); + free ( p ); + return ( NULL ); // not created by libpnd + } + + return ( p ); +} diff --git a/minimenu/mmapps.h b/minimenu/mmapps.h new file mode 100644 index 0000000..85531fb --- /dev/null +++ b/minimenu/mmapps.h @@ -0,0 +1,19 @@ + +#ifndef h_mmapps_h +#define h_mmapps_h + +typedef struct _mm_app_t { + char *dispname; // name to display (already beft-match localized) + char *exec; // complete exec-line (ie: bin and all args) + char *iconpath; // path to icon + void *iconcache; // userdata: probably points to an icon cache + // structure + struct _mm_app_t *next; // next in linked list +} mm_app_t; + +// fullscan implies full searchpath walk and return; no merging new apps with existing list, etc +mm_app_t *apps_fullscan ( char *searchpath ); + +mm_app_t *apps_fetch_from_dotdesktop ( char *path ); + +#endif diff --git a/minimenu/mmcache.c b/minimenu/mmcache.c new file mode 100644 index 0000000..cd7c8b2 --- /dev/null +++ b/minimenu/mmcache.c @@ -0,0 +1,200 @@ + +#include + +#include "SDL.h" +#include "SDL_image.h" +#include "SDL_rotozoom.h" + +#include "pnd_pxml.h" +#include "pnd_utility.h" +#include "pnd_conf.h" +#include "pnd_container.h" +#include "pnd_discovery.h" +#include "pnd_logger.h" +#include "pnd_desktop.h" +#include "pnd_pndfiles.h" +#include "pnd_apps.h" + +#include "mmenu.h" +#include "mmapps.h" +#include "mmcache.h" + +extern pnd_conf_handle g_conf; + +mm_cache_t *g_icon_cache = NULL; +mm_cache_t *g_preview_cache = NULL; + +unsigned char cache_preview ( pnd_disco_t *app, unsigned char maxwidth, unsigned char maxheight ) { + SDL_Surface *s; + mm_cache_t *c; + + // does this sucker even have a preview? + if ( ! app -> preview_pic1 ) { + return ( 1 ); // nothing here, so thats fine + } + + // check if already cached + if ( ( c = cache_query_preview ( app -> unique_id ) ) ) { + return ( 1 ); // already got it + } + + // not cached, load it up + // + + // see if we can mount the pnd/dir + // does preview file exist? + // if so, load it up, size it, cache it + // if not, warning and bail + // unmount it + + // can we mount? + char fullpath [ PATH_MAX ]; + char filepath [ PATH_MAX ]; + + sprintf ( fullpath, "%s/%s", app -> object_path, app -> object_filename ); + + if ( ! pnd_pnd_mount ( pnd_run_script, fullpath, app -> unique_id ) ) { + pnd_log ( pndn_debug, "Couldn't mount '%s' for preview\n", fullpath ); + return ( 0 ); // couldn't mount?! + } + + sprintf ( filepath, "%s/%s/%s", PND_MOUNT_PATH, app -> unique_id, app -> preview_pic1 ); + s = IMG_Load ( filepath ); + + pnd_pnd_unmount ( pnd_run_script, fullpath, app -> unique_id ); + + if ( ! s ) { + pnd_log ( pndn_debug, "Couldn't open image '%s' for preview\n", filepath ); + return ( 0 ); + } + + pnd_log ( pndn_debug, "Image size is %u x %u (max %u x %u)\n", s -> w, s -> h, maxwidth, maxheight ); + + // scale + if ( s -> w < maxwidth ) { + SDL_Surface *scaled; + double scale = (double)maxwidth / (double)s -> w; + pnd_log ( pndn_debug, " Upscaling; scale factor %f\n", scale ); + scaled = rotozoomSurface ( s, 0 /* angle*/, scale /* scale */, 1 /* smooth==1*/ ); + SDL_FreeSurface ( s ); + s = scaled; + } else if ( s -> w > maxwidth ) { + SDL_Surface *scaled; + double scale = (double)maxwidth / (double)s -> w; + pnd_log ( pndn_debug, " Downscaling; scale factor %f\n", scale ); + scaled = rotozoomSurface ( s, 0 /* angle*/, scale /* scale */, 1 /* smooth==1*/ ); + SDL_FreeSurface ( s ); + s = scaled; + } + + // add to cache + c = (mm_cache_t*) malloc ( sizeof(mm_cache_t) ); + bzero ( c, sizeof(mm_cache_t) ); + + if ( ! g_preview_cache ) { + g_preview_cache = c; + } else { + c -> next = g_preview_cache; + g_preview_cache = c; + } + + strncpy ( c -> uniqueid, app -> unique_id, 1000 ); + c -> i = s; + + return ( 1 ); +} + +unsigned char cache_icon ( pnd_disco_t *app, unsigned char maxwidth, unsigned char maxheight ) { + SDL_Surface *s; + mm_cache_t *c; + + // check if already cached + if ( ( c = cache_query_icon ( app -> unique_id ) ) ) { + return ( 1 ); // already got it + } + + // not cached, load it up + // + + // pull icon into buffer + unsigned int buflen = 0; + unsigned char *iconbuf; + iconbuf = pnd_emit_icon_to_buffer ( app, &buflen ); + + if ( ! iconbuf ) { + return ( 0 ); + } + + // ready up a RWbuffer for SDL + SDL_RWops *rwops = SDL_RWFromMem ( iconbuf, buflen ); + + s = IMG_Load_RW ( rwops, 1 /* free the rwops */ ); + + if ( ! s ) { + return ( 0 ); + } + + free ( iconbuf ); // ditch the icon from ram + + pnd_log ( pndn_debug, "Image size is %u x %u (max %u x %u)\n", s -> w, s -> h, maxwidth, maxheight ); + + // scale the icon? + if ( s -> w < maxwidth ) { + SDL_Surface *scaled; + double scale = (double)maxwidth / (double)s -> w; + pnd_log ( pndn_debug, " Upscaling; scale factor %f\n", scale ); + scaled = rotozoomSurface ( s, 0 /* angle*/, scale /* scale */, 1 /* smooth==1*/ ); + SDL_FreeSurface ( s ); + s = scaled; + } else if ( s -> w > maxwidth ) { + SDL_Surface *scaled; + double scale = (double)maxwidth / (double)s -> w; + pnd_log ( pndn_debug, " Downscaling; scale factor %f\n", scale ); + scaled = rotozoomSurface ( s, 0 /* angle*/, scale /* scale */, 1 /* smooth==1*/ ); + SDL_FreeSurface ( s ); + s = scaled; + } + + // add to cache + c = (mm_cache_t*) malloc ( sizeof(mm_cache_t) ); + bzero ( c, sizeof(mm_cache_t) ); + + if ( ! g_icon_cache ) { + g_icon_cache = c; + } else { + c -> next = g_icon_cache; + g_icon_cache = c; + } + + strncpy ( c -> uniqueid, app -> unique_id, 1000 ); + c -> i = s; + + return ( 1 ); +} + +mm_cache_t *cache_query ( char *id, mm_cache_t *head ) { + mm_cache_t *iter = head; + + if ( ! id ) { + return ( NULL ); + } + + while ( iter ) { + if ( iter -> uniqueid && + strcasecmp ( iter -> uniqueid, id ) == 0 ) + { + return ( iter ); + } + iter = iter -> next; + } // while + + return ( NULL ); +} + +mm_cache_t *cache_query_icon ( char *id ) { + return ( cache_query ( id, g_icon_cache ) ); +} + +mm_cache_t *cache_query_preview ( char *id ) { + return ( cache_query ( id, g_preview_cache ) ); +} diff --git a/minimenu/mmcache.h b/minimenu/mmcache.h new file mode 100644 index 0000000..de12c44 --- /dev/null +++ b/minimenu/mmcache.h @@ -0,0 +1,27 @@ + +#ifndef h_mmcache_h +#define h_mmcache_h + +/* this cache is used, rather than just pointing right from the mm_app_t iconcache, since many apps + * may re-use the same icon, this lets the apps just cross-link to the same icon to save a bit of + * memory; probably irrelevent, but what the heck, I'm writing this quick ;) + */ + +/* the same structure can be used to contain preview pics, in a different list of same type + */ + +typedef struct _mm_cache_t { + char uniqueid [ 1024 ]; // pnd unique-id + void /*SDL_Surface*/ *i; + // structure + struct _mm_cache_t *next; // next in linked list +} mm_cache_t; + +unsigned char cache_icon ( pnd_disco_t *app, unsigned char maxwidth, unsigned char maxheight ); +unsigned char cache_preview ( pnd_disco_t *app, unsigned char maxwidth, unsigned char maxheight ); + +mm_cache_t *cache_query ( char *id, mm_cache_t *head ); +mm_cache_t *cache_query_icon ( char *id ); // specialized version +mm_cache_t *cache_query_preview ( char *id ); // specialized version + +#endif diff --git a/minimenu/mmcat.c b/minimenu/mmcat.c new file mode 100644 index 0000000..de4db3a --- /dev/null +++ b/minimenu/mmcat.c @@ -0,0 +1,136 @@ + +#include +#include +#include + +#include "pnd_conf.h" +#include "pnd_logger.h" +#include "pnd_pxml.h" +#include "pnd_container.h" +#include "pnd_discovery.h" + +#include "mmenu.h" +#include "mmcache.h" +#include "mmcat.h" + +mm_category_t g_categories [ MAX_CATS ]; +unsigned char g_categorycount = 0; + +unsigned char category_push ( char *catname, pnd_disco_t *app ) { + mm_category_t *c; + + // check category list; if found, append app to the end of it. + // if not found, add it to the category list and plop the app in there. + // app's are just app-refs, which contain links to the disco-t list -- thus removal is only in one place, and + // an app can be in multiple categories if we like.. + // + + // find or create category + // + + if ( ( c = category_query ( catname ) ) ) { + // category was found.. + } else { + // category wasn't found.. + pnd_log ( PND_LOG_DEFAULT, "New category '%s'\n", catname ); + g_categories [ g_categorycount ].catname = strdup ( catname ); + g_categories [ g_categorycount ].refs = NULL; + c = &(g_categories [ g_categorycount ]); + g_categorycount++; + } + + // alloc and populate appref + // + mm_appref_t *ar = malloc ( sizeof(mm_appref_t) ); + if ( ! ar ) { + return ( 0 ); + } + + bzero ( ar, sizeof(mm_appref_t) ); + + ar -> ref = app; + + // plug it into category + // and sort it on insert! +#if 0 // no sorting + ar -> next = c -> refs; + c -> refs = ar; +#else // with sorting + // if no refs at all, or new guy has no title, just stick it in at head + if ( c -> refs && ar -> ref -> title_en ) { + mm_appref_t *iter = c -> refs; + mm_appref_t *last = NULL; + + while ( iter ) { + + if ( iter -> ref -> title_en ) { + if ( strcmp ( ar -> ref -> title_en, iter -> ref -> title_en ) < 0 ) { + // new guy is smaller than the current guy! + break; + } + } else { + // since new guy must have a name by here, we're bigger than any guy who does not have a name + // --> continue + } + + last = iter; + iter = iter -> next; + } + + if ( iter ) { + // smaller than the current guy, so stitch in + if ( last ) { + ar -> next = iter; + last -> next = ar; + } else { + ar -> next = c -> refs; + c -> refs = ar; + } + } else { + // we're the biggest, just append to last + last -> next = ar; + } + + } else { + ar -> next = c -> refs; + c -> refs = ar; + } +#endif + c -> refcount++; + + return ( 1 ); +} + +mm_category_t *category_query ( char *catname ) { + unsigned char i; + + for ( i = 0; i < g_categorycount; i++ ) { + + if ( strcasecmp ( g_categories [ i ].catname, catname ) == 0 ) { + return ( &(g_categories [ i ]) ); + } + + } + + return ( NULL ); +} + +void category_dump ( void ) { + unsigned int i; + + // WHY AREN'T I SORTING ON INSERT? + + // dump + for ( i = 0; i < g_categorycount; i++ ) { + pnd_log ( PND_LOG_DEFAULT, "Category %u: '%s' * %u\n", i, g_categories [ i ].catname, g_categories [ i ].refcount ); + mm_appref_t *ar = g_categories [ i ].refs; + + while ( ar ) { + pnd_log ( PND_LOG_DEFAULT, " Appref %s\n", IFNULL(ar -> ref -> title_en,"No Name") ); + ar = ar -> next; + } + + } // for + + return; +} diff --git a/minimenu/mmcat.h b/minimenu/mmcat.h new file mode 100644 index 0000000..06605cf --- /dev/null +++ b/minimenu/mmcat.h @@ -0,0 +1,26 @@ + +#ifndef h_mmcat_h +#define h_mmcat_h + +typedef struct _mm_appref_t { + pnd_disco_t *ref; + // anything else? + struct _mm_appref_t *next; +} mm_appref_t; + +typedef struct { + char *catname; // name of the category + mm_appref_t *refs; // apps (from g_active_apps) that are in this category + unsigned int refcount; // how many apps in this category +} mm_category_t; + +#define MAX_CATS 100 + +#define CATEGORY_ALL "All" + +// try to populate as many cats as necessary +unsigned char category_push ( char *catname, pnd_disco_t *app ); // catname is not pulled from app, so we can make them up on the fly (ie: "All") +mm_category_t *category_query ( char *catname ); +void category_dump ( void ); // sort the apprefs + +#endif diff --git a/minimenu/mmenu.c b/minimenu/mmenu.c new file mode 100644 index 0000000..9ff0cdb --- /dev/null +++ b/minimenu/mmenu.c @@ -0,0 +1,270 @@ + +/* minimenu + * aka "2wm" - too weak menu, two week menu, akin to twm + * + * Craig wants a super minimal menu ASAP before launch, so lets see what I can put together in 2 weeks with not much + * free time ;) I'd like to do a fuller ('tiny', but with plugin support and a decent expansion and customizing design..) + * but later, baby! + * + */ + +/* mmenu - This is the actual menu + * The goal of this app is to show a application picker screen of some sort, allow the user to perform some useful + * activities (such as set clock speed, say), and request an app to run, or shutdown, etc. + * To keep the memory footprint down, when invoking an application, the menu _exits_, and simply spits out + * an operation for mmwrapper to perform. In the case of no wrapper, the menu will just exit, which is handy for + * debugging. + */ + +/* mmenu lifecycle: + * 1) determine app list (via pnd scan, .desktop scan, whatever voodoo) + * 2) show a menu, allow user to interact: + * a) user picks an application to run, or -> exit, pass shell run line to wrapper + * b) user requests menu shutdown -> exit, tell wrapper to exit as well + * c) user performsn some operation (set clock, copy files, whatever) -> probably stays within the menu + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pnd_logger.h" +#include "pnd_pxml.h" +#include "pnd_utility.h" +#include "pnd_conf.h" +#include "pnd_container.h" +#include "pnd_discovery.h" +#include "pnd_locate.h" +#include "pnd_device.h" + +#include "mmenu.h" +#include "mmwrapcmd.h" +#include "mmapps.h" +#include "mmcache.h" +#include "mmcat.h" +#include "mmui.h" + +pnd_box_handle *g_active_apps = NULL; +unsigned int g_active_appcount = 0; +char g_username [ 128 ]; // since we have to wait for login (!!), store username here +pnd_conf_handle g_conf = 0; + +char *pnd_run_script = NULL; + +int main ( int argc, char *argv[] ) { + int logall = -1; // -1 means normal logging rules; >=0 means log all! + int i; + + // boilerplate stuff from pndnotifyd and pndevmapperd + + /* iterate across args + */ + for ( i = 1; i < argc; i++ ) { + + if ( argv [ i ][ 0 ] == '-' && argv [ i ][ 1 ] == 'l' ) { + + if ( isdigit ( argv [ i ][ 2 ] ) ) { + unsigned char x = atoi ( argv [ i ] + 2 ); + if ( x >= 0 && + x < pndn_none ) + { + logall = x; + } + } else { + logall = 0; + } + + } else { + //printf ( "Unknown: %s\n", argv [ i ] ); + printf ( "%s [-l##]\n", argv [ 0 ] ); + printf ( "-l#\tLog-it; -l is 0-and-up (or all), and -l2 means 2-and-up (not all); l[0-3] for now. Log goes to /tmp/mmenu.log\n" ); + printf ( "-f\tFull path of frontend to run\n" ); + exit ( 0 ); + } + + } // for + + /* enable logging? + */ + pnd_log_set_pretext ( "mmenu" ); + pnd_log_set_flush ( 1 ); + + if ( logall == -1 ) { + // standard logging; non-daemon versus daemon + +#if 1 // HACK: set debug level to high on desktop, but not on pandora; just a convenience while devving, until the conf file is read + struct stat statbuf; + if ( stat ( PND_DEVICE_BATTERY_GAUGE_PERC, &statbuf ) == 0 ) { + // on pandora + pnd_log_set_filter ( pndn_error ); + } else { + pnd_log_set_filter ( pndn_debug ); + } +#endif + + pnd_log_to_stdout(); + + } else { + FILE *f; + + f = fopen ( "/tmp/mmenu.log", "w" ); + + if ( f ) { + pnd_log_set_filter ( logall ); + pnd_log_to_stream ( f ); + pnd_log ( pndn_rem, "logall mode - logging to /tmp/mmenu.log\n" ); + } + + if ( logall == pndn_debug ) { + pnd_log_set_buried_logging ( 1 ); // log the shit out of it + pnd_log ( pndn_rem, "logall mode 0 - turned on buried logging\n" ); + } + + } // logall + + pnd_log ( pndn_rem, "%s built %s %s", argv [ 0 ], __DATE__, __TIME__ ); + + pnd_log ( pndn_rem, "log level starting as %u", pnd_log_get_filter() ); + + // wait for a user to be logged in - we should probably get hupped when a user logs in, so we can handle + // log-out and back in again, with SDs popping in and out between.. + pnd_log ( pndn_rem, "Checking to see if a user is logged in\n" ); + while ( 1 ) { + if ( pnd_check_login ( g_username, 127 ) ) { + break; + } + pnd_log ( pndn_debug, " No one logged in yet .. spinning.\n" ); + sleep ( 2 ); + } // spin + pnd_log ( pndn_rem, "Looks like user '%s' is in, continue.\n", g_username ); + + /* conf file + */ + g_conf = pnd_conf_fetch_by_name ( MMENU_CONF, MMENU_CONF_SEARCHPATH ); + + if ( ! g_conf ) { + pnd_log ( pndn_error, "ERROR: Couldn't fetch conf file '%s'!\n", MMENU_CONF ); + emit_and_quit ( MM_QUIT ); + } + + // redo log filter + pnd_log_set_filter ( pnd_conf_get_as_int_d ( g_conf, "minimenu.loglevel", pndn_error ) ); + + /* setup + */ + + // pnd_run.sh + pnd_run_script = pnd_locate_filename ( pnd_conf_get_as_char ( g_conf, "minimenu.pndrun" ), "pnd_run.sh" ); + + if ( ! pnd_run_script ) { + pnd_log ( pndn_error, "ERROR: Couldn't locate pnd_run.sh!\n" ); + emit_and_quit ( MM_QUIT ); + } + + // attempt to set up UI + if ( ! ui_setup() ) { + pnd_log ( pndn_error, "ERROR: Couldn't set up the UI!\n" ); + emit_and_quit ( MM_QUIT ); + } + + // show load screen + ui_loadscreen(); + + // set up static image cache + if ( ! ui_imagecache ( pnd_conf_get_as_char ( g_conf, MMENU_ARTPATH ) ) ) { + pnd_log ( pndn_error, "ERROR: Couldn't set up static UI image cache!\n" ); + emit_and_quit ( MM_QUIT ); + } + + /* inhale applications, icons, categories, etc + */ + + // show disco screen + ui_discoverscreen ( 1 /* clear screen */ ); + + // determine current app list, cache icons + pnd_log ( pndn_debug, "Looking for pnd applications here: %s\n", pnd_conf_get_as_char ( g_conf, MMENU_APP_SEARCHPATH ) ); + g_active_apps = pnd_disco_search ( pnd_conf_get_as_char ( g_conf, MMENU_APP_SEARCHPATH ), NULL ); // ignore overrides for now + g_active_appcount = pnd_box_get_size ( g_active_apps ); + + unsigned char maxwidth, maxheight; + maxwidth = pnd_conf_get_as_int_d ( g_conf, MMENU_DISP_ICON_MAX_WIDTH, 50 ); + maxheight = pnd_conf_get_as_int_d ( g_conf, MMENU_DISP_ICON_MAX_HEIGHT, 50 ); + + // show cache screen + ui_cachescreen ( 1 /* clear screen */ ); + + pnd_log ( pndn_debug, "Found pnd applications, and caching icons:\n" ); + pnd_disco_t *iter = pnd_box_get_head ( g_active_apps ); + while ( iter ) { + pnd_log ( pndn_debug, " App: '%s'\n", IFNULL(iter->title_en,"No Name") ); + + // update cachescreen + ui_cachescreen ( 1 /* clear screen */ ); + + // cache the icon + if ( iter -> pnd_icon_pos && + ! cache_icon ( iter, maxwidth, maxheight ) ) + { + pnd_log ( pndn_warning, " Couldn't load icon: '%s'\n", IFNULL(iter->title_en,"No Name") ); + } + + // cache the preview --> SHOULD DEFER + if ( pnd_conf_get_as_int_d ( g_conf, "minimenu.load_previews_now", 0 ) > 0 ) { + // load the preview pics now! + if ( iter -> preview_pic1 && + ! cache_preview ( iter, pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_width", 200 ), pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_height", 180 ) ) ) + { + pnd_log ( pndn_warning, " Couldn't load preview pic: '%s' -> '%s'\n", IFNULL(iter->title_en,"No Name"), iter -> preview_pic1 ); + } + } // preview now? + + // push to All category + // we do this first, so first category is always All + if ( ! category_push ( CATEGORY_ALL, iter ) ) { + pnd_log ( pndn_warning, " Couldn't categorize to All: '%s'\n", IFNULL(iter -> title_en, "No Name") ); + } + + // push the categories + if ( iter -> main_category ) { + if ( ! category_push ( iter -> main_category, iter ) ) { + pnd_log ( pndn_warning, " Couldn't categorize to %s: '%s'\n", iter -> main_category, IFNULL(iter -> title_en, "No Name") ); + } + } + + iter = pnd_box_get_next ( iter ); + } // while + + // dump categories + category_dump(); + + /* actual work now + */ + + while ( 1 ) { // forever! + + // show the menu, or changes thereof + ui_render ( CHANGED_NOTHING ); + + // wait for input or time-based events (like animations) + // deal with inputs + ui_process_input ( 1 /* block */ ); + + // sleep? block? + usleep ( 5000 ); + + } // while + + return ( 0 ); +} + +void emit_and_quit ( char *s ) { + printf ( "%s\n", s ); + exit ( 0 ); +} diff --git a/minimenu/mmenu.conf b/minimenu/mmenu.conf new file mode 100644 index 0000000..ae3108e --- /dev/null +++ b/minimenu/mmenu.conf @@ -0,0 +1,95 @@ +# for the mmenu 'minimenu' +# + +[minimenu] +static_art_path ./minimenu/skin/default +font Vera.ttf +font_ptsize 24 +pndrun /usr/pandora/scripts:./testdata/scripts # searchpath to locate "pnd_run.sh"; why aren't I looking in /etc/pandora/conf/apps for this? +load_previews_now 0 # if >0, will try to load preview pics from pnds at boot time, not defer till later +loglevel 0 # 0 is debug, lots of crap; 3 is better, means 'errors only'. Output may screw up the wrapper! + +[apps] +searchpath ./testdata/app2:./testdata/app3:/media/*/pandora/desktop + +[display] +fullscreen 0 # 0 for windowed, >0 for fullscreen +screen_width 800 # for some calculations +detail_bg_alpha 100 # when rendering the detail panel background, how transparent? +font_rgba_r 220 # RGBA for the display text +font_rgba_g 220 # RGBA for the display text +font_rgba_b 220 # RGBA for the display text +font_rgba_a 20 # RGBA for the display text + +[tabs] +wraparound 0 # if 1, last tab wraps around to first when going right; going left from first tab goes to last +font Vera.ttf +font_ptsize 16 +tab_offset_x 4 # from left screen to first tab left +tab_offset_y 3 # from top of screen to first tab top +tab_width 132 # width of tab +tab_height 35 # height of tab +text_offset_x 10 # from left edge of tab to left edge of text +text_offset_y 10 # from top edge of tab to top edge of text +text_width 120 # clip text to this width + +[grid] +font Vera.ttf +font_ptsize 12 +icon_max_width 60 # scale icons to.. +icon_max_height 60 # scale icons to.. +grid_offset_x 17 # from left screen to first cell column +grid_offset_y 60 # from top screen to first cell row +icon_offset_x 12 # from left edge of cell to left edge of icon in cell +icon_offset_y 0 # from top edge of cell to top edge of icon in cell +text_offset_x 42 # from left edge of cell to center of text centering (ie: center of icon presumably) +text_offset_y 65 # from top of cell to top of text +text_width 75 # max width of the text +text_clip_x 5 # offset from cell edge to left edge of text, when the text width is being clipped to fit +cell_width 85 # cell location is grid_offset_x + ( cell_width * column_number ) +cell_height 92 # cell location is grid_offset_y + ( cell_height * column_number ) +col_max 5 # number of columns to render into grid +row_max 4 # number of rows to display before we stop rendering +text_hilite_offset_y 62 # from top of cell to top of hilight +scroll_increment 4 # number of rows to scroll when jumping up or down (recommend 1, or same as row_max for full page jump) +arrow_up_x 450 # left edge of up-arrow showing more icons scrolled away +arrow_up_y 80 # top edge of up-arrow showing more icons scrolled away +arrow_down_x 450 # left edge of down-arrow showing more icons scrolled away +arrow_down_y 380 # top edge of down-arrow showing more icons scrolled away +arrow_bar_x 455 # left edge of scrollbar +arrow_bar_y 100 # top edge of scrollbar +arrow_bar_clip_w 10 # clip scrollbar artwork to width-X +arrow_bar_clip_h 274 # clip scrollbar artwork to height-X + +[detailpane] +show 1 # if 0, don't show detail pane artwork at all +pane_offset_x 475 # left edge of detail pane graphic +pane_offset_y 60 # top edge of detail pane graphic + +[detailtext] +font Vera.ttf +font_ptsize 16 +cell_offset_x 488 # left edge of text cell +cell_offset_y 312 # top edge of text cell +cell_width 250 # width of cell (for text clipping) + +[previewpic] +cell_offset_x 480 # left edge of text cell +cell_offset_y 90 # top edge of text cell +cell_width 285 +cell_height 180 + +[graphics] +IMG_BACKGROUND_800480 800480_6.png +IMG_BACKGROUND_TABMASK tab1mask.png +IMG_DETAIL_PANEL detailpane2.png +IMG_DETAIL_BG 800480_4.png +IMG_SELECTED_ALPHAMASK select.png +IMG_SELECTED_HILITE hilite.png +IMG_TAB_SEL tab_sel_tall.png +IMG_TAB_UNSEL tab_unsel.png +IMG_ICON_MISSING pandora60.png +IMG_PREVIEW_MISSING pandora60.png +IMG_ARROW_UP arrowup.png +IMG_ARROW_DOWN arrowdown.png +IMG_ARROW_SCROLLBAR arrowscroller.png diff --git a/minimenu/mmenu.conf.6col b/minimenu/mmenu.conf.6col new file mode 100644 index 0000000..157267b --- /dev/null +++ b/minimenu/mmenu.conf.6col @@ -0,0 +1,70 @@ +# for the mmenu 'minimenu' +# + +[minimenu] +static_art_path ./minimenu/skin/default +font arial.ttf +font_ptsize 24 +pndrun ./testdata/scripts/pnd_run.sh + +[apps] +searchpath ./testdata/app2 + +[display] +screen_width 800 # for some calculations +detail_bg_alpha 100 # when rendering the detail panel background, how transparent? +font_rgba_r 220 # RGBA for the display text +font_rgba_g 220 # RGBA for the display text +font_rgba_b 220 # RGBA for the display text +font_rgba_a 20 # RGBA for the display text + +[tabs] +wraparound 0 # if 1, last tab wraps around to first when going right; going left from first tab goes to last +font arial.ttf +font_ptsize 16 +tab_offset_x 4 # from left screen to first tab left +tab_offset_y 3 # from top of screen to first tab top +tab_width 132 # width of tab +tab_height 33 # height of tab +text_offset_x 10 # from left edge of tab to left edge of text +text_offset_y 10 # from top edge of tab to top edge of text +text_width 120 # clip text to this width + +[grid] +font arial.ttf +font_ptsize 12 +icon_max_width 50 # scale icons to.. +icon_max_height 50 # scale icons to.. +grid_offset_x 15 # from left screen to first cell column +grid_offset_y 60 # from top screen to first cell row +icon_offset_x 10 # from left edge of cell to left edge of icon in cell +icon_offset_y 0 # from top edge of cell to top edge of icon in cell +text_offset_x 35 # from left edge of cell to center of text centering (ie: center of icon presumably) +text_offset_y 65 # from top of cell to top of text +cell_width 70 # cell location is grid_offset_x + ( cell_width * column_number ) +cell_height 90 # cell location is grid_offset_y + ( cell_height * column_number ) +col_max 6 # number of columns to render into grid +row_max 4 # number of rows to display before we stop rendering +text_hilite_offset_y 62 # from top of cell to top of hilight + +[detailtext] +font arial.ttf +font_ptsize 16 +cell_offset_x 488 # left edge of text cell +cell_offset_y 312 # top edge of text cell +cell_width 250 # width of cell (for text clipping) + +[previewpic] +cell_offset_x 480 # left edge of text cell +cell_offset_y 90 # top edge of text cell + +[graphics] +IMG_BACKGROUND_800480 800480_6.png +IMG_BACKGROUND_TABMASK tab1mask.png +IMG_DETAIL_PANEL detailpane.png +IMG_DETAIL_BG 800480_4.png +IMG_SELECTED_ALPHAMASK select.png +IMG_SELECTED_HILITE hilite.png +IMG_TAB_SEL tab_sel.png +IMG_TAB_UNSEL tab_unsel.png +IMG_ICON_MISSING pandora60.png diff --git a/minimenu/mmenu.h b/minimenu/mmenu.h new file mode 100644 index 0000000..a271891 --- /dev/null +++ b/minimenu/mmenu.h @@ -0,0 +1,35 @@ + +#ifndef h_mmenu_h +#define h_mmenu_h + +// utility +#define IFNULL(foo,bar) (foo)?(foo):(bar) +extern char *pnd_run_script; + +// base searchpath to locate the conf +#define MMENU_CONF "mmenu.conf" +#define MMENU_CONF_SEARCHPATH "/etc/pandora/conf:./minimenu" + +// keys +#define MMENU_ARTPATH "minimenu.static_art_path" +#define MMENU_APP_SEARCHPATH "apps.searchpath" + +#define MMENU_GRID_FONT "grid.font" +#define MMENU_GRID_FONTSIZE "grid.font_ptsize" + +#define MMENU_DISP_COLMAX "grid.col_max" +#define MMENU_DISP_ROWMAX "grid.row_max" +#define MMENU_DISP_ICON_MAX_WIDTH "grid.icon_max_width" +#define MMENU_DISP_ICON_MAX_HEIGHT "grid.icon_max_height" + +typedef enum { + pndn_debug = 0, + pndn_rem, // will set default log level to here, so 'debug' is omitted + pndn_warning, + pndn_error, + pndn_none +} pndnotify_loglevels_e; + +void emit_and_quit ( char *s ); + +#endif diff --git a/minimenu/mmtest.sh b/minimenu/mmtest.sh new file mode 100755 index 0000000..fddba59 --- /dev/null +++ b/minimenu/mmtest.sh @@ -0,0 +1,4 @@ +#!/bin/sh +#echo "run /bin/echo foo bar" +echo "quit" +exit diff --git a/minimenu/mmui.c b/minimenu/mmui.c new file mode 100644 index 0000000..9d6401b --- /dev/null +++ b/minimenu/mmui.c @@ -0,0 +1,1132 @@ + +#include +#include +#include +#include "SDL.h" +#include "SDL_audio.h" +#include "SDL_image.h" +#include "SDL_ttf.h" +#include "SDL_gfxPrimitives.h" +#include "SDL_rotozoom.h" + +#include "pnd_conf.h" +#include "pnd_logger.h" +#include "pnd_pxml.h" +#include "pnd_container.h" +#include "pnd_discovery.h" +#include "pnd_apps.h" + +#include "mmenu.h" +#include "mmcat.h" +#include "mmcache.h" +#include "mmui.h" +#include "mmwrapcmd.h" + +/* SDL + */ +SDL_Surface *sdl_realscreen = NULL; +unsigned int sdl_ticks = 0; + +/* app state + */ +unsigned short int g_scale = 1; // 1 == noscale + +SDL_Surface *g_imgcache [ IMG_MAX ]; + +TTF_Font *g_big_font = NULL; +TTF_Font *g_grid_font = NULL; +TTF_Font *g_detailtext_font = NULL; +TTF_Font *g_tab_font = NULL; + +extern pnd_conf_handle g_conf; + +/* current display state + */ +int ui_rows_scrolled_down = 0; // number of rows that should be missing from top of the display +mm_appref_t *ui_selected = NULL; +unsigned char ui_category = 0; // current category +unsigned char ui_catshift = 0; // how many cats are offscreen to the left + +extern mm_category_t g_categories [ MAX_CATS ]; +extern unsigned char g_categorycount; + +static SDL_Surface *ui_scale_image ( SDL_Surface *s, unsigned int maxwidth, int maxheight ); // height -1 means ignore +static int ui_selected_index ( void ); + +static unsigned int ui_timer ( unsigned int interval ) { + sdl_ticks++; + return ( interval ); +} + +unsigned char ui_setup ( void ) { + + /* set up SDL + */ + + SDL_Init ( SDL_INIT_EVERYTHING | SDL_INIT_NOPARACHUTE ); + + SDL_SetTimer ( 30, ui_timer ); // 30fps + + SDL_JoystickOpen ( 0 ); // turn on joy-0 + + SDL_WM_SetCaption ( "mmenu", "mmenu" ); + + // hide the mouse cursor if we can + if ( SDL_ShowCursor ( -1 ) == 1 ) { + SDL_ShowCursor ( 0 ); + } + + atexit ( SDL_Quit ); + + // open up a surface + unsigned int svm = SDL_SWSURFACE /*| SDL_FULLSCREEN*/ /* 0*/; + if ( pnd_conf_get_as_int_d ( g_conf, "display.fullscreen", 0 ) > 0 ) { + svm |= SDL_FULLSCREEN; + } + + sdl_realscreen = + SDL_SetVideoMode ( 800 * g_scale, 480 * g_scale, 16 /*bpp*/, svm ); + + if ( ! sdl_realscreen ) { + pnd_log ( pndn_error, "ERROR: Couldn't open SDL real screen; dieing." ); + return ( 0 ); + } + + pnd_log ( pndn_debug, "Pixel format:" ); + pnd_log ( pndn_debug, "bpp b: %u\n", sdl_realscreen -> format -> BitsPerPixel ); + pnd_log ( pndn_debug, "bpp B: %u\n", sdl_realscreen -> format -> BytesPerPixel ); + pnd_log ( pndn_debug, "R mask: %08x\n", sdl_realscreen -> format -> Rmask ); + pnd_log ( pndn_debug, "G mask: %08x\n", sdl_realscreen -> format -> Gmask ); + pnd_log ( pndn_debug, "B mask: %08x\n", sdl_realscreen -> format -> Bmask ); + +#if 0 // audio + { + SDL_AudioSpec fmt; + + /* Set 16-bit stereo audio at 22Khz */ + fmt.freq = 44100; //22050; + fmt.format = AUDIO_S16; //AUDIO_S16; + fmt.channels = 1; + fmt.samples = 2048; /* A good value for games */ + fmt.callback = mixaudio; + fmt.userdata = NULL; + + /* Open the audio device and start playing sound! */ + if ( SDL_OpenAudio ( &fmt, NULL ) < 0 ) { + zotlog ( "Unable to open audio: %s\n", SDL_GetError() ); + exit ( 1 ); + } + + SDL_PauseAudio ( 0 ); + } +#endif + + // images + //IMG_Init ( IMG_INIT_JPG | IMG_INIT_PNG ); + + /* fonts + */ + + // init + if ( TTF_Init() == -1 ) { + pnd_log ( pndn_error, "ERROR: Couldn't set up SDL TTF lib\n" ); + return ( 0 ); // couldn't set up SDL TTF + } + + char fullpath [ PATH_MAX ]; + // big font + sprintf ( fullpath, "%s/%s", pnd_conf_get_as_char ( g_conf, MMENU_ARTPATH ), pnd_conf_get_as_char ( g_conf, "minimenu.font" ) ); + g_big_font = TTF_OpenFont ( fullpath, pnd_conf_get_as_int_d ( g_conf, "minimenu.font_ptsize", 24 ) ); + if ( ! g_big_font ) { + pnd_log ( pndn_error, "ERROR: Couldn't load font '%s' for size %u\n", + pnd_conf_get_as_char ( g_conf, "minimenu.font" ), pnd_conf_get_as_int_d ( g_conf, "minimenu.font_ptsize", 24 ) ); + return ( 0 ); // couldn't set up SDL TTF + } + + // grid font + sprintf ( fullpath, "%s/%s", pnd_conf_get_as_char ( g_conf, MMENU_ARTPATH ), pnd_conf_get_as_char ( g_conf, MMENU_GRID_FONT ) ); + g_grid_font = TTF_OpenFont ( fullpath, pnd_conf_get_as_int_d ( g_conf, MMENU_GRID_FONTSIZE, 10 ) ); + if ( ! g_grid_font ) { + pnd_log ( pndn_error, "ERROR: Couldn't load font '%s' for size %u\n", + pnd_conf_get_as_char ( g_conf, MMENU_GRID_FONT ), pnd_conf_get_as_int_d ( g_conf, MMENU_GRID_FONTSIZE, 10 ) ); + return ( 0 ); // couldn't set up SDL TTF + } + + // detailtext font + sprintf ( fullpath, "%s/%s", pnd_conf_get_as_char ( g_conf, MMENU_ARTPATH ), pnd_conf_get_as_char ( g_conf, "detailtext.font" ) ); + g_detailtext_font = TTF_OpenFont ( fullpath, pnd_conf_get_as_int_d ( g_conf, "detailtext.font_ptsize", 10 ) ); + if ( ! g_detailtext_font ) { + pnd_log ( pndn_error, "ERROR: Couldn't load font '%s' for size %u\n", + pnd_conf_get_as_char ( g_conf, "detailtext.font" ), pnd_conf_get_as_int_d ( g_conf, "detailtext.font_ptsize", 10 ) ); + return ( 0 ); // couldn't set up SDL TTF + } + + // tab font + sprintf ( fullpath, "%s/%s", pnd_conf_get_as_char ( g_conf, MMENU_ARTPATH ), pnd_conf_get_as_char ( g_conf, "tabs.font" ) ); + g_tab_font = TTF_OpenFont ( fullpath, pnd_conf_get_as_int_d ( g_conf, "tabs.font_ptsize", 10 ) ); + if ( ! g_tab_font ) { + pnd_log ( pndn_error, "ERROR: Couldn't load font '%s' for size %u\n", + pnd_conf_get_as_char ( g_conf, "tabs.font" ), pnd_conf_get_as_int_d ( g_conf, "tabs.font_ptsize", 10 ) ); + return ( 0 ); // couldn't set up SDL TTF + } + + return ( 1 ); +} + +mm_imgcache_t g_imagecache [ IMG_TRUEMAX ] = { + { IMG_BACKGROUND_800480, "graphics.IMG_BACKGROUND_800480" }, + { IMG_BACKGROUND_TABMASK, "graphics.IMG_BACKGROUND_TABMASK" }, + { IMG_DETAIL_PANEL, "graphics.IMG_DETAIL_PANEL" }, + { IMG_DETAIL_BG, "graphics.IMG_DETAIL_BG" }, + { IMG_SELECTED_ALPHAMASK, "graphics.IMG_SELECTED_ALPHAMASK" }, + { IMG_TAB_SEL, "graphics.IMG_TAB_SEL" }, + { IMG_TAB_UNSEL, "graphics.IMG_TAB_UNSEL" }, + { IMG_ICON_MISSING, "graphics.IMG_ICON_MISSING" }, + { IMG_SELECTED_HILITE, "graphics.IMG_SELECTED_HILITE" }, + { IMG_PREVIEW_MISSING, "graphics.IMG_PREVIEW_MISSING" }, + { IMG_ARROW_UP, "graphics.IMG_ARROW_UP", }, + { IMG_ARROW_DOWN, "graphics.IMG_ARROW_DOWN", }, + { IMG_ARROW_SCROLLBAR, "graphics.IMG_ARROW_SCROLLBAR", }, + { IMG_MAX, NULL }, +}; + +unsigned char ui_imagecache ( char *basepath ) { + unsigned int i; + char fullpath [ PATH_MAX ]; + + // loaded + + for ( i = 0; i < IMG_MAX; i++ ) { + + if ( g_imagecache [ i ].id != i ) { + pnd_log ( pndn_error, "ERROR: Internal table mismatch during caching [%u]\n", i ); + exit ( -1 ); + } + + char *filename = pnd_conf_get_as_char ( g_conf, g_imagecache [ i ].confname ); + + if ( ! filename ) { + pnd_log ( pndn_error, "ERROR: Missing filename in conf for key: %s\n", g_imagecache [ i ].confname ); + return ( 0 ); + } + + sprintf ( fullpath, "%s/%s", basepath, filename ); + + if ( ! ( g_imagecache [ i ].i = IMG_Load ( fullpath ) ) ) { + pnd_log ( pndn_error, "ERROR: Couldn't load static cache image: %s\n", fullpath ); + return ( 0 ); + } + + } // for + + // generated + //g_imagecache [ IMG_SELECTED_ALPHAMASK ].i = SDL_CreateRGBSurface ( SDL_SWSURFACE, 60, 60, 32, 0xFF0000, 0x00FF00, 0xFF, 0xFF000000 ); + //boxRGBA ( g_imagecache [ IMG_SELECTED_ALPHAMASK ].i, 0, 0, 60, 60, 100, 100, 100, 250 ); + + // post processing + // + + // scale icons + g_imagecache [ IMG_SELECTED_ALPHAMASK ].i = ui_scale_image ( g_imagecache [ IMG_SELECTED_ALPHAMASK ].i, pnd_conf_get_as_int_d ( g_conf, "grid.icon_max_width", 50 ), -1 ); + g_imagecache [ IMG_ICON_MISSING ].i = ui_scale_image ( g_imagecache [ IMG_ICON_MISSING ].i, pnd_conf_get_as_int_d ( g_conf, "grid.icon_max_width", 50 ), -1 ); + // scale text hilight + g_imagecache [ IMG_SELECTED_HILITE ].i = ui_scale_image ( g_imagecache [ IMG_SELECTED_HILITE ].i, pnd_conf_get_as_int_d ( g_conf, "grid.text_width", 50 ), -1 ); + // scale preview no-pic + g_imagecache [ IMG_PREVIEW_MISSING ].i = ui_scale_image ( g_imagecache [ IMG_PREVIEW_MISSING ].i, + pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_width", 50 ), + pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_height", 50 ) ); + + // set alpha on detail panel + SDL_SetAlpha ( g_imagecache [ IMG_DETAIL_BG ].i, SDL_SRCALPHA, pnd_conf_get_as_int_d ( g_conf, "display.detail_bg_alpha", 50 ) ); + + return ( 1 ); +} // ui_imagecache + +void ui_render ( unsigned int render_mask ) { + + // 800x480: + // divide width: 550 / 250 + // divide left side: 5 columns == 110px width + // 20px buffer either side == 70px wide icon + 20 + 20? + + unsigned int icon_rows; + +#define MAXRECTS 200 + SDL_Rect rects [ MAXRECTS ], src; + SDL_Rect *dest = rects; + bzero ( dest, sizeof(SDL_Rect)*MAXRECTS ); + + unsigned int row, displayrow, col; + mm_appref_t *appiter; + + unsigned int screen_width = pnd_conf_get_as_int_d ( g_conf, "display.screen_width", 800 ); + + unsigned char row_max = pnd_conf_get_as_int_d ( g_conf, MMENU_DISP_ROWMAX, 4 ); + unsigned char col_max = pnd_conf_get_as_int_d ( g_conf, MMENU_DISP_COLMAX, 5 ); + + unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 ); + unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 ); + unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 ); + unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 ); + + unsigned int grid_offset_x = pnd_conf_get_as_int ( g_conf, "grid.grid_offset_x" ); + unsigned int grid_offset_y = pnd_conf_get_as_int ( g_conf, "grid.grid_offset_y" ); + + unsigned int icon_offset_x = pnd_conf_get_as_int ( g_conf, "grid.icon_offset_x" ); + unsigned int icon_offset_y = pnd_conf_get_as_int ( g_conf, "grid.icon_offset_y" ); + + unsigned int text_width = pnd_conf_get_as_int ( g_conf, "grid.text_width" ); + unsigned int text_clip_x = pnd_conf_get_as_int ( g_conf, "grid.text_clip_x" ); + unsigned int text_offset_x = pnd_conf_get_as_int ( g_conf, "grid.text_offset_x" ); + unsigned int text_offset_y = pnd_conf_get_as_int ( g_conf, "grid.text_offset_y" ); + + unsigned int cell_width = pnd_conf_get_as_int ( g_conf, "grid.cell_width" ); + unsigned int cell_height = pnd_conf_get_as_int ( g_conf, "grid.cell_height" ); + + // how many total rows do we need? + icon_rows = g_categories [ ui_category ].refcount / col_max; + if ( g_categories [ ui_category ].refcount % col_max > 0 ) { + icon_rows++; + } + + // if no selected app yet, select the first one +#if 0 + if ( ! ui_selected ) { + ui_selected = g_categories [ ui_category ].refs; + } +#endif + + // ensure selection is visible + if ( ui_selected ) { + + int index = ui_selected_index(); + int topleft = col_max * ui_rows_scrolled_down; + int botright = ( col_max * ( ui_rows_scrolled_down + row_max ) - 1 ); + + pnd_log ( PND_LOG_DEFAULT, "index %u tl %u br %u\n", index, topleft, botright ); + + if ( index < topleft ) { + ui_rows_scrolled_down -= pnd_conf_get_as_int_d ( g_conf, "grid.scroll_increment", 1 ); + } else if ( index > botright ) { + ui_rows_scrolled_down += pnd_conf_get_as_int_d ( g_conf, "grid.scroll_increment", 1 ); + } + + if ( ui_rows_scrolled_down < 0 ) { + ui_rows_scrolled_down = 0; + } else if ( ui_rows_scrolled_down > icon_rows ) { + ui_rows_scrolled_down = icon_rows; + } + + } // ensire visible + + // render background + if ( g_imagecache [ IMG_BACKGROUND_800480 ].i ) { + dest -> x = 0; + dest -> y = 0; + dest -> w = sdl_realscreen -> w; + dest -> h = sdl_realscreen -> h; + SDL_BlitSurface ( g_imagecache [ IMG_BACKGROUND_800480 ].i, NULL /* whole image */, sdl_realscreen, dest /* 0,0 */ ); + //SDL_UpdateRects ( sdl_realscreen, 1, &dest ); + dest++; + } + + // tabmask + if ( g_imagecache [ IMG_BACKGROUND_TABMASK ].i ) { + dest -> x = 0; + dest -> y = 0; + dest -> w = sdl_realscreen -> w; + dest -> h = sdl_realscreen -> h; + SDL_BlitSurface ( g_imagecache [ IMG_BACKGROUND_TABMASK ].i, NULL /* whole image */, sdl_realscreen, dest /* 0,0 */ ); + //SDL_UpdateRects ( sdl_realscreen, 1, &dest ); + dest++; + } + + // tabs + if ( g_imagecache [ IMG_TAB_SEL ].i && g_imagecache [ IMG_TAB_UNSEL ].i ) { + unsigned int tab_width = pnd_conf_get_as_int ( g_conf, "tabs.tab_width" ); + unsigned int tab_height = pnd_conf_get_as_int ( g_conf, "tabs.tab_height" ); + unsigned int tab_offset_x = pnd_conf_get_as_int ( g_conf, "tabs.tab_offset_x" ); + unsigned int tab_offset_y = pnd_conf_get_as_int ( g_conf, "tabs.tab_offset_y" ); + unsigned int text_offset_x = pnd_conf_get_as_int ( g_conf, "tabs.text_offset_x" ); + unsigned int text_offset_y = pnd_conf_get_as_int ( g_conf, "tabs.text_offset_y" ); + unsigned int text_width = pnd_conf_get_as_int ( g_conf, "tabs.text_width" ); + + for ( col = ui_catshift; + col < ( + ( screen_width / tab_width ) < g_categorycount ? ( screen_width / tab_width ) + ui_catshift : g_categorycount + ui_catshift + ); + col++ ) + { + + SDL_Surface *s; + if ( col == ui_category ) { + s = g_imagecache [ IMG_TAB_SEL ].i; + } else { + s = g_imagecache [ IMG_TAB_UNSEL ].i; + } + + // draw tab + src.x = 0; + src.y = 0; + src.w = tab_width; + src.h = tab_height; + dest -> x = tab_offset_x + ( col * tab_width ); + dest -> y = tab_offset_y; + //pnd_log ( pndn_debug, "tab %u at %ux%u\n", col, dest.x, dest.y ); + SDL_BlitSurface ( s, &src, sdl_realscreen, dest ); + //SDL_UpdateRects ( sdl_realscreen, 1, &dest ); + dest++; + + // draw text + SDL_Surface *rtext; + SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a }; + rtext = TTF_RenderText_Blended ( g_tab_font, g_categories [ col ].catname, tmpfontcolor ); + src.x = 0; + src.y = 0; + src.w = rtext -> w < text_width ? rtext -> w : text_width; + src.h = rtext -> h; + dest -> x = tab_offset_x + ( col * tab_width ) + text_offset_x; + dest -> y = tab_offset_y + text_offset_y; + SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest ); + //SDL_UpdateRects ( sdl_realscreen, 1, &dest ); + dest++; + + } // for + + } // tabs + + // scroll bars and arrows + { + unsigned char show_bar = 0; + + // up? + if ( ui_rows_scrolled_down && g_imagecache [ IMG_ARROW_UP ].i ) { + dest -> x = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_up_x", 450 ); + dest -> y = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_up_y", 80 ); + SDL_BlitSurface ( g_imagecache [ IMG_ARROW_UP ].i, NULL /* whole image */, sdl_realscreen, dest ); + //SDL_UpdateRects ( sdl_realscreen, 1, &dest ); + dest++; + + show_bar = 1; + } + + // down? + if ( ui_rows_scrolled_down + row_max < icon_rows && g_imagecache [ IMG_ARROW_DOWN ].i ) { + dest -> x = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_down_x", 450 ); + dest -> y = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_down_y", 80 ); + SDL_BlitSurface ( g_imagecache [ IMG_ARROW_DOWN ].i, NULL /* whole image */, sdl_realscreen, dest ); + //SDL_UpdateRects ( sdl_realscreen, 1, &dest ); + dest++; + + show_bar = 1; + } + + if ( show_bar ) { + // show scrollbar as well + src.x = 0; + src.y = 0; + src.w = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_bar_clip_w", 10 ); + src.h = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_bar_clip_h", 100 ); + dest -> x = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_bar_x", 450 ); + dest -> y = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_bar_y", 100 ); + SDL_BlitSurface ( g_imagecache [ IMG_ARROW_SCROLLBAR ].i, &src /* whole image */, sdl_realscreen, dest ); + //SDL_UpdateRects ( sdl_realscreen, 1, &dest ); + dest++; + } // bar + + } // scroll bars + + // render detail pane bg + if ( pnd_conf_get_as_int_d ( g_conf, "detailpane.show", 1 ) ) { + + if ( g_imagecache [ IMG_DETAIL_BG ].i ) { + src.x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ); + src.y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ); + src.w = ((SDL_Surface*)(g_imagecache [ IMG_DETAIL_PANEL ].i)) -> w; + src.h = ((SDL_Surface*)(g_imagecache [ IMG_DETAIL_PANEL ].i)) -> h; + dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ); + dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ); + SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest ); + //SDL_UpdateRects ( sdl_realscreen, 1, &dest ); + dest++; + } + + // render detail pane + if ( g_imagecache [ IMG_DETAIL_PANEL ].i ) { + dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ); + dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ); + SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_PANEL ].i, NULL /* whole image */, sdl_realscreen, dest ); + //SDL_UpdateRects ( sdl_realscreen, 1, &dest ); + dest++; + } + + } // detailpane frame/bg + + // anything to render? + if ( g_categories [ ui_category ].refs ) { + + appiter = g_categories [ ui_category ].refs; + row = 0; + displayrow = 0; + + // until we run out of apps, or run out of space + while ( appiter != NULL ) { + + for ( col = 0; col < col_max && appiter != NULL; col++ ) { + + // do we even need to render it? or are we suppressing it due to rows scrolled off the top? + if ( row >= ui_rows_scrolled_down ) { + + // selected? show hilights + if ( appiter == ui_selected ) { + // icon + dest -> x = grid_offset_x + ( col * cell_width ) + icon_offset_x; + dest -> y = grid_offset_y + ( displayrow * cell_height ) + icon_offset_y; + SDL_BlitSurface ( g_imagecache [ IMG_SELECTED_ALPHAMASK ].i, NULL /* all */, sdl_realscreen, dest ); + //SDL_UpdateRects ( sdl_realscreen, 1, &dest ); + dest++; + // text + dest -> x = grid_offset_x + ( col * cell_width ) + text_clip_x; + dest -> y = grid_offset_y + ( displayrow * cell_height ) + pnd_conf_get_as_int ( g_conf, "grid.text_hilite_offset_y" ); + SDL_BlitSurface ( g_imagecache [ IMG_SELECTED_HILITE ].i, NULL /* all */, sdl_realscreen, dest ); + //SDL_UpdateRects ( sdl_realscreen, 1, &dest ); + dest++; + } // selected? + + // show icon + mm_cache_t *ic = cache_query_icon ( appiter -> ref -> unique_id ); + SDL_Surface *iconsurface; + if ( ic ) { + iconsurface = ic -> i; + } else { + pnd_log ( pndn_warning, "WARNING: TBD: Need Missin-icon icon for '%s'\n", IFNULL(appiter -> ref -> title_en,"No Name") ); + iconsurface = g_imagecache [ IMG_ICON_MISSING ].i; + } + if ( iconsurface ) { + //pnd_log ( pndn_debug, "Got an icon for '%s'\n", IFNULL(appiter -> ref -> title_en,"No Name") ); + + src.x = 0; + src.y = 0; + src.w = 60; + src.h = 60; + dest -> x = grid_offset_x + ( col * cell_width ) + icon_offset_x; + dest -> y = grid_offset_y + ( displayrow * cell_height ) + icon_offset_y; + + SDL_BlitSurface ( iconsurface, &src, sdl_realscreen, dest ); + //SDL_UpdateRects ( sdl_realscreen, 1, &dest ); + dest++; + + } + + // show text + if ( appiter -> ref -> title_en ) { + SDL_Surface *rtext; + SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a }; + rtext = TTF_RenderText_Blended ( g_grid_font, appiter -> ref -> title_en, tmpfontcolor ); + src.x = 0; + src.y = 0; + src.w = text_width < rtext -> w ? text_width : rtext -> w; + src.h = rtext -> h; + if ( rtext -> w > text_width ) { + dest -> x = grid_offset_x + ( col * cell_width ) + text_clip_x; + } else { + dest -> x = grid_offset_x + ( col * cell_width ) + text_offset_x - ( rtext -> w / 2 ); + } + dest -> y = grid_offset_y + ( displayrow * cell_height ) + text_offset_y; + SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest ); + //SDL_UpdateRects ( sdl_realscreen, 1, &dest ); + dest++; + } + + } // display now? or scrolled away.. + + // next + appiter = appiter -> next; + + } // for column 1...X + + if ( row >= ui_rows_scrolled_down ) { + displayrow++; + } + + row ++; + + // are we done displaying rows? + if ( displayrow >= row_max ) { + break; + } + + } // while + + } else { + // no apps to render? + pnd_log ( pndn_rem, "No applications to render?\n" ); + } // apps to renser? + + // detail panel + if ( ui_selected ) { + + unsigned int cell_offset_x = pnd_conf_get_as_int ( g_conf, "detailtext.cell_offset_x" ); + unsigned int cell_offset_y = pnd_conf_get_as_int ( g_conf, "detailtext.cell_offset_y" ); + unsigned int cell_width = pnd_conf_get_as_int ( g_conf, "detailtext.cell_width" ); + + unsigned int desty = cell_offset_y; + + char buffer [ 256 ]; + + // full name + if ( ui_selected -> ref -> title_en ) { + SDL_Surface *rtext; + SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a }; + rtext = TTF_RenderText_Blended ( g_detailtext_font, ui_selected -> ref -> title_en, tmpfontcolor ); + src.x = 0; + src.y = 0; + src.w = rtext -> w < cell_width ? rtext -> w : cell_width; + src.h = rtext -> h; + dest -> x = cell_offset_x; + dest -> y = desty; + SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest ); + //SDL_UpdateRects ( sdl_realscreen, 1, &dest ); + dest++; + desty += src.h; + } + + // category + if ( ui_selected -> ref -> main_category ) { + + sprintf ( buffer, "Category: %s", ui_selected -> ref -> main_category ); + + SDL_Surface *rtext; + SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a }; + rtext = TTF_RenderText_Blended ( g_detailtext_font, buffer, tmpfontcolor ); + src.x = 0; + src.y = 0; + src.w = rtext -> w < cell_width ? rtext -> w : cell_width; + src.h = rtext -> h; + dest -> x = cell_offset_x; + dest -> y = desty; + SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest ); + //SDL_UpdateRects ( sdl_realscreen, 1, &dest ); + dest++; + desty += src.h; + } + + // clock + if ( ui_selected -> ref -> clockspeed ) { + + sprintf ( buffer, "CPU Clock: %s", ui_selected -> ref -> clockspeed ); + + SDL_Surface *rtext; + SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a }; + rtext = TTF_RenderText_Blended ( g_detailtext_font, buffer, tmpfontcolor ); + src.x = 0; + src.y = 0; + src.w = rtext -> w < cell_width ? rtext -> w : cell_width; + src.h = rtext -> h; + dest -> x = cell_offset_x; + dest -> y = desty; + SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest ); + //SDL_UpdateRects ( sdl_realscreen, 1, &dest ); + dest++; + desty += src.h; + } + + // preview pic + mm_cache_t *ic = cache_query_preview ( ui_selected -> ref -> unique_id ); + SDL_Surface *previewpic; + + if ( ic ) { + previewpic = ic -> i; + } else { + previewpic = g_imagecache [ IMG_PREVIEW_MISSING ].i; + } + + if ( previewpic ) { + dest -> x = pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_offset_x", 50 ) + + ( ( pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_width", 50 ) - previewpic -> w ) / 2 ); + dest -> y = pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_offset_y", 50 ); + SDL_BlitSurface ( previewpic, NULL /* whole image */, sdl_realscreen, dest ); + //SDL_UpdateRects ( sdl_realscreen, 1, &dest ); + dest++; + } + + } // selected? + + // update all the rects and send it all to sdl + SDL_UpdateRects ( sdl_realscreen, dest - rects, rects ); + +} // ui_render + +void ui_process_input ( unsigned char block_p ) { + SDL_Event event; + + unsigned char ui_event = 0; // if we get a ui event, flip to 1 and break + static ui_sdl_button_e ui_mask = uisb_none; // current buttons down + + while ( ! ui_event && + block_p ? SDL_WaitEvent ( &event ) : SDL_PollEvent ( &event ) ) + { + + switch ( event.type ) { + +#if 0 // joystick motion + case SDL_JOYAXISMOTION: + + pnd_log ( PND_LOG_DEFAULT, "joystick axis\n" ); + + if ( event.jaxis.axis == 0 ) { + // horiz + if ( event.jaxis.value < 0 ) { + ui_push_left(); + pnd_log ( PND_LOG_DEFAULT, "joystick axis - LEFT\n" ); + } else if ( event.jaxis.value > 0 ) { + ui_push_right(); + pnd_log ( PND_LOG_DEFAULT, "joystick axis - RIGHT\n" ); + } + } else if ( event.jaxis.axis == 1 ) { + // vert + if ( event.jaxis.value < 0 ) { + ui_push_up(); + } else if ( event.jaxis.value > 0 ) { + ui_push_down(); + } + } + + ui_event++; + + break; +#endif + +#if 0 // joystick buttons + case SDL_JOYBUTTONDOWN: + + pnd_log ( PND_LOG_DEFAULT, "joystick button down %u\n", event.jbutton.button ); + + if ( event.jbutton.button == 0 ) { // B + ui_mask |= uisb_b; + } else if ( event.jbutton.button == 1 ) { // Y + ui_mask |= uisb_y; + } else if ( event.jbutton.button == 2 ) { // X + ui_mask |= uisb_x; + } else if ( event.jbutton.button == 3 ) { // A + ui_mask |= uisb_a; + + } else if ( event.jbutton.button == 4 ) { // Select + ui_mask |= uisb_select; + } else if ( event.jbutton.button == 5 ) { // Start + ui_mask |= uisb_start; + + } else if ( event.jbutton.button == 7 ) { // L + ui_mask |= uisb_l; + } else if ( event.jbutton.button == 8 ) { // R + ui_mask |= uisb_r; + + } + + ui_event++; + + break; + + case SDL_JOYBUTTONUP: + + pnd_log ( PND_LOG_DEFAULT, "joystick button up %u\n", event.jbutton.button ); + + if ( event.jbutton.button == 0 ) { // B + ui_mask &= ~uisb_b; + ui_push_exec(); + } else if ( event.jbutton.button == 1 ) { // Y + ui_mask &= ~uisb_y; + } else if ( event.jbutton.button == 2 ) { // X + ui_mask &= ~uisb_x; + } else if ( event.jbutton.button == 3 ) { // A + ui_mask &= ~uisb_a; + + } else if ( event.jbutton.button == 4 ) { // Select + ui_mask &= ~uisb_select; + } else if ( event.jbutton.button == 5 ) { // Start + ui_mask &= ~uisb_start; + + } else if ( event.jbutton.button == 7 ) { // L + ui_mask &= ~uisb_l; + ui_push_ltrigger(); + } else if ( event.jbutton.button == 8 ) { // R + ui_mask &= ~uisb_r; + ui_push_rtrigger(); + + } + + ui_event++; + + break; +#endif + +#if 1 // keyboard events + case SDL_KEYUP: + + pnd_log ( pndn_debug, "key up %u\n", event.key.keysym.sym ); + + // directional + if ( event.key.keysym.sym == SDLK_RIGHT ) { + ui_push_right(); + ui_event++; + } else if ( event.key.keysym.sym == SDLK_LEFT ) { + ui_push_left(); + ui_event++; + } else if ( event.key.keysym.sym == SDLK_UP ) { + ui_push_up(); + ui_event++; + } else if ( event.key.keysym.sym == SDLK_DOWN ) { + ui_push_down(); + ui_event++; + } else if ( event.key.keysym.sym == SDLK_SPACE || event.key.keysym.sym == SDLK_END ) { + ui_push_exec(); + ui_event++; + } else if ( event.key.keysym.sym == SDLK_z ) { + ui_push_ltrigger(); + ui_event++; + } else if ( event.key.keysym.sym == SDLK_x ) { + ui_push_rtrigger(); + ui_event++; + } + + // extras + if ( event.key.keysym.sym == SDLK_q ) { + emit_and_quit ( MM_QUIT ); + } + + break; +#endif + +#if 0 // mouse / touchscreen + case SDL_MOUSEBUTTONDOWN: + if ( event.button.button == SDL_BUTTON_LEFT ) { + cb_pointer_press ( gc, event.button.x / g_scale, event.button.y / g_scale ); + ui_event++; + } + break; + + case SDL_MOUSEBUTTONUP: + if ( event.button.button == SDL_BUTTON_LEFT ) { + cb_pointer_release ( gc, event.button.x / g_scale, event.button.y / g_scale ); + retval |= STAT_pen; + ui_event++; + } + break; +#endif + + case SDL_QUIT: + exit ( 0 ); + break; + + default: + break; + + } // switch event type + + } // while poll + + return; +} + +void ui_push_left ( void ) { + + if ( ! ui_selected ) { + ui_push_right(); + return; + } + + // are we alreadt at first item? + if ( g_categories [ ui_category ].refs == ui_selected ) { + // can't go any more left, we're at the head + } else { + // figure out the previous item; yay for singly linked list :/ + mm_appref_t *i = g_categories [ ui_category ].refs; + while ( i ) { + if ( i -> next == ui_selected ) { + ui_selected = i; + break; + } + i = i -> next; + } + } + + return; +} + +void ui_push_right ( void ) { + + if ( ui_selected ) { + + if ( ui_selected -> next ) { + ui_selected = ui_selected -> next; + } + + } else { + ui_selected = g_categories [ ui_category ].refs; + } + + return; +} + +void ui_push_up ( void ) { + unsigned char col_max = pnd_conf_get_as_int ( g_conf, MMENU_DISP_COLMAX ); + + while ( col_max ) { + ui_push_left(); + col_max--; + } + + return; +} + +void ui_push_down ( void ) { + unsigned char col_max = pnd_conf_get_as_int ( g_conf, MMENU_DISP_COLMAX ); + + if ( ui_selected ) { + while ( col_max ) { + ui_push_right(); + col_max--; + } + } else { + ui_push_right(); + } + + return; +} + +void ui_push_exec ( void ) { + + if ( ui_selected ) { + char buffer [ PATH_MAX ]; + sprintf ( buffer, "%s/%s", ui_selected -> ref -> object_path, ui_selected -> ref -> object_filename ); + pnd_apps_exec ( pnd_run_script, + buffer, + ui_selected -> ref -> unique_id, + ui_selected -> ref -> exec, + ui_selected -> ref -> startdir, + ui_selected -> ref -> execargs, + atoi ( ui_selected -> ref -> clockspeed ), + PND_EXEC_OPTION_NORUN ); + sprintf ( buffer, "%s %s\n", MM_RUN, pnd_apps_exec_runline() ); + emit_and_quit ( buffer ); + } + + return; +} + +void ui_push_ltrigger ( void ) { + unsigned char oldcat = ui_category; + + if ( ui_category > 0 ) { + ui_category--; + } else { + if ( pnd_conf_get_as_int_d ( g_conf, "tabs.wraparound", 0 ) > 0 ) { + ui_category = g_categorycount - 1; + } + } + + if ( oldcat != ui_category ) { + ui_selected = NULL; + } + + return; +} + +void ui_push_rtrigger ( void ) { + unsigned char oldcat = ui_category; + + if ( ui_category < ( g_categorycount - 1 ) ) { + ui_category++; + } else { + if ( pnd_conf_get_as_int_d ( g_conf, "tabs.wraparound", 0 ) > 0 ) { + ui_category = 0; + } + } + + if ( oldcat != ui_category ) { + ui_selected = NULL; + } + + return; +} + +SDL_Surface *ui_scale_image ( SDL_Surface *s, unsigned int maxwidth, int maxheight ) { + double scale = 1000000.0; + double scalex = 1000000.0; + double scaley = 1000000.0; + SDL_Surface *scaled; + + scalex = (double)maxwidth / (double)s -> w; + + if ( maxheight == -1 ) { + scale = scalex; + } else { + scaley = (double)maxheight / (double)s -> h; + + if ( scaley < scalex ) { + scale = scaley; + } else { + scale = scalex; + } + + } + + pnd_log ( pndn_debug, " Upscaling; scale factor %f\n", scale ); + scaled = rotozoomSurface ( s, 0 /* angle*/, scale /* scale */, 1 /* smooth==1*/ ); + SDL_FreeSurface ( s ); + s = scaled; + + return ( s ); +} + +void ui_loadscreen ( void ) { + + SDL_Rect dest; + + unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 ); + unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 ); + unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 ); + unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 ); + + // clear the screen + SDL_FillRect( SDL_GetVideoSurface(), NULL, 0 ); + + // render text + SDL_Surface *rtext; + SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a }; + rtext = TTF_RenderText_Blended ( g_big_font, "Setting up menu...", tmpfontcolor ); + dest.x = 20; + dest.y = 20; + SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, &dest ); + SDL_UpdateRects ( sdl_realscreen, 1, &dest ); + + return; +} + +void ui_discoverscreen ( unsigned char clearscreen ) { + + SDL_Rect dest; + + unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 ); + unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 ); + unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 ); + unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 ); + + // clear the screen + if ( clearscreen ) { + SDL_FillRect( SDL_GetVideoSurface(), NULL, 0 ); + + // render background + if ( g_imagecache [ IMG_BACKGROUND_800480 ].i ) { + dest.x = 0; + dest.y = 0; + dest.w = sdl_realscreen -> w; + dest.h = sdl_realscreen -> h; + SDL_BlitSurface ( g_imagecache [ IMG_BACKGROUND_800480 ].i, NULL /* whole image */, sdl_realscreen, NULL /* 0,0 */ ); + SDL_UpdateRects ( sdl_realscreen, 1, &dest ); + } + + } + + // render text + SDL_Surface *rtext; + SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a }; + rtext = TTF_RenderText_Blended ( g_big_font, "Looking for applications...", tmpfontcolor ); + if ( clearscreen ) { + dest.x = 20; + dest.y = 20; + } else { + dest.x = 20; + dest.y = 40; + } + SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, &dest ); + SDL_UpdateRects ( sdl_realscreen, 1, &dest ); + + // render icon + if ( g_imagecache [ IMG_ICON_MISSING ].i ) { + dest.x = rtext -> w + 30; + dest.y = 20; + SDL_BlitSurface ( g_imagecache [ IMG_ICON_MISSING ].i, NULL, sdl_realscreen, &dest ); + SDL_UpdateRects ( sdl_realscreen, 1, &dest ); + } + + return; +} + +void ui_cachescreen ( unsigned char clearscreen ) { + + SDL_Rect dest; + + unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 ); + unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 ); + unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 ); + unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 ); + + static unsigned int stepx = 0; + + // clear the screen + if ( clearscreen ) { + SDL_FillRect( SDL_GetVideoSurface(), NULL, 0 ); + + // render background + if ( g_imagecache [ IMG_BACKGROUND_800480 ].i ) { + dest.x = 0; + dest.y = 0; + dest.w = sdl_realscreen -> w; + dest.h = sdl_realscreen -> h; + SDL_BlitSurface ( g_imagecache [ IMG_BACKGROUND_800480 ].i, NULL /* whole image */, sdl_realscreen, NULL /* 0,0 */ ); + SDL_UpdateRects ( sdl_realscreen, 1, &dest ); + } + + } + + // render text + SDL_Surface *rtext; + SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a }; + rtext = TTF_RenderText_Blended ( g_big_font, "Caching applications artwork...", tmpfontcolor ); + if ( clearscreen ) { + dest.x = 20; + dest.y = 20; + } else { + dest.x = 20; + dest.y = 40; + } + SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, &dest ); + SDL_UpdateRects ( sdl_realscreen, 1, &dest ); + + // render icon + if ( g_imagecache [ IMG_ICON_MISSING ].i ) { + dest.x = rtext -> w + 30 + stepx; + dest.y = 20; + SDL_BlitSurface ( g_imagecache [ IMG_ICON_MISSING ].i, NULL, sdl_realscreen, &dest ); + SDL_UpdateRects ( sdl_realscreen, 1, &dest ); + } + + // move across + stepx += 20; + + if ( stepx > 350 ) { + stepx = 0; + } + + return; +} + +int ui_selected_index ( void ) { + + if ( ! ui_selected ) { + return ( -1 ); // no index + } + + mm_appref_t *r = g_categories [ ui_category ].refs; + int counter = 0; + while ( r ) { + if ( r == ui_selected ) { + return ( counter ); + } + r = r -> next; + counter++; + } + + return ( -1 ); +} diff --git a/minimenu/mmui.h b/minimenu/mmui.h new file mode 100644 index 0000000..e22ee32 --- /dev/null +++ b/minimenu/mmui.h @@ -0,0 +1,86 @@ + +#ifndef h_mmui_h +#define h_mmui_h + +/* this code actually _does_ something; this way, at least all the IO routines are in one place, so + * I know what to replace with something sensible later :) + * ... ahh, to have time to make this in C++ as an actual abstract interface... + */ + +/* staticly cached stuff, for UI + */ + +typedef enum { + IMG_BACKGROUND_800480 = 0, + IMG_BACKGROUND_TABMASK, + IMG_DETAIL_PANEL, + IMG_DETAIL_BG, + IMG_SELECTED_ALPHAMASK, + IMG_TAB_SEL, + IMG_TAB_UNSEL, + IMG_ICON_MISSING, + IMG_SELECTED_HILITE, + IMG_PREVIEW_MISSING, + IMG_ARROW_UP, + IMG_ARROW_DOWN, + IMG_ARROW_SCROLLBAR, + IMG_MAX, // before this point is loaded; after is generated + IMG_TRUEMAX +} mm_imgcache_e; + +typedef struct { + mm_imgcache_e id; + char *confname; + void /*SDL_Surface*/ *i; +} mm_imgcache_t; + +/* ui stuff + */ + +typedef enum { + uisb_none = 0, + uisb_x = 1, + uisb_y = (1<<1), + uisb_a = (1<<2), + uisb_b = (1<<3), + uisb_l = (1<<4), + uisb_r = (1<<5), + uisb_start = (1<<6), + uisb_select = (1<<7), + uisb_max +} ui_sdl_button_e; + +unsigned char ui_setup ( void ); +unsigned char ui_imagecache ( char *basepath ); + +#define CHANGED_NOTHING (0) +#define CHANGED_CATEGORY (1<<0) /* changed to different category */ +#define CHANGED_SELECTION (1<<1) /* changed app selection */ +#define CHANGED_DATAUPDATE (1<<2) /* deferred preview pic or icon came in */ +#define CHANGED_APPRELOAD (1<<3) /* new set of applications entirely */ +#define CHANGED_EVERYTHING (0xFFFF) /* redraw it all! */ +void ui_render ( unsigned int render_mask ); + +void ui_loadscreen ( void ); // show screen while loading the menu +void ui_discoverscreen ( unsigned char clearscreen ); // screen to show while scanning for apps +void ui_cachescreen ( unsigned char clearscreen ); // while caching icons, categories and preview-pics-Now-mode + +/* internal functions follow + */ + +// change the focus +void ui_process_input ( unsigned char block_p ); +void ui_push_left ( void ); +void ui_push_right ( void ); +void ui_push_up ( void ); +void ui_push_down ( void ); +void ui_push_exec ( void ); +void ui_push_ltrigger ( void ); +void ui_push_rtrigger ( void ); + +// ui_render() can register tappable-areas which touchscreen code can then figure out if we made a hit +void ui_register_reset ( void ); +void ui_register_tab ( mm_category_t *category, unsigned int x, unsigned int y, unsigned int w, unsigned int h ); +void ui_register_app ( pnd_disco_t *app, unsigned int x, unsigned int y, unsigned int w, unsigned int h ); + +#endif diff --git a/minimenu/mmwrapcmd.h b/minimenu/mmwrapcmd.h new file mode 100644 index 0000000..6f8580b --- /dev/null +++ b/minimenu/mmwrapcmd.h @@ -0,0 +1,10 @@ + +#ifndef h_mmwrapcmd_h +#define h_mmwrapcmd_h + +// since only like 2 or 3 commands, no need for enum's/etc + +#define MM_QUIT "quit" +#define MM_RUN "run" + +#endif diff --git a/minimenu/mmwrapper.c b/minimenu/mmwrapper.c new file mode 100644 index 0000000..7ca7949 --- /dev/null +++ b/minimenu/mmwrapper.c @@ -0,0 +1,220 @@ + +/* minimenu + * aka "2wm" - too weak menu, two week menu, akin to twm + * + * Craig wants a super minimal menu ASAP before launch, so lets see what I can put together in 2 weeks with not much + * free time ;) I'd like to do a fuller ('tiny', but with plugin support and a decent expansion and customizing design..) + * but later, baby! + * + */ + +/* mmwrapper -- we probably want the menu to exeunt-with-alarums when running applications, to minimize the memory and + * performance footprint; it could be running in-X or atop a desktop-env, or purely on SDL or framebuffer with nothing.. + * so wrapper will actually do the running, and be tiny. + * Likewise, if the menu proper dies, the wrapper can fire it up again. + * A side effect is, people could use the wrappers abilities, and slap another cruddy menu on top of it .. yay for pure + * curses based or lynx-based menus ;) + */ + +/* mmwrapper's lifecycle: + * 1) launch 'frontend' (mmmenu, could be others) + * 2) when it exits, pick up the output... + * a) either a command (quit, run some app, pick new frontend) + * b) or a crash, in which case, back to (1) + * 3) execute command, if any + * 4) go to (1) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pnd_logger.h" + +#include "mmwrapcmd.h" + +unsigned char g_daemon_mode = 0; +char *g_frontend = NULL; + +typedef enum { + pndn_debug = 0, + pndn_rem, // will set default log level to here, so 'debug' is omitted + pndn_warning, + pndn_error, + pndn_none +} pndnotify_loglevels_e; + +int main ( int argc, char *argv[] ) { + int logall = -1; // -1 means normal logging rules; >=0 means log all! + int i; + + // pull conf, determine frontend; alternate is to check command line. + + // boilerplate stuff from pndnotifyd and pndevmapperd + + /* iterate across args + */ + for ( i = 1; i < argc; i++ ) { + + if ( argv [ i ][ 0 ] == '-' && argv [ i ][ 1 ] == 'd' ) { + //printf ( "Going daemon mode. Silent running.\n" ); + g_daemon_mode = 1; + } else if ( argv [ i ][ 0 ] == '-' && argv [ i ][ 1 ] == 'l' ) { + + if ( isdigit ( argv [ i ][ 2 ] ) ) { + unsigned char x = atoi ( argv [ i ] + 2 ); + if ( x >= 0 && + x < pndn_none ) + { + logall = x; + } + } else { + logall = 0; + } + + } else if ( argv [ i ][ 0 ] == '-' && argv [ i ][ 1 ] == 'f' && argv [ i ][ 2 ] != '\0' ) + { + g_frontend = argv [ i ] + 2; + } else { + //printf ( "Unknown: %s\n", argv [ i ] ); + printf ( "%s [-l[##]] [-d] [-fFRONTENDPATH]\n", argv [ 0 ] ); + printf ( "-d\tDaemon mode; detach from terminal, chdir to /tmp, suppress output. Optional.\n" ); + printf ( "-l#\tLog-it; -l is 0-and-up (or all), and -l2 means 2-and-up (not all); l[0-3] for now. Log goes to /tmp/mmwrapper.log\n" ); + printf ( "-f\tFull path of frontend to run\n" ); + exit ( 0 ); + } + + } // for + + /* enable logging? + */ + pnd_log_set_pretext ( "mmwrapper" ); + pnd_log_set_flush ( 1 ); + + if ( logall == -1 ) { + // standard logging; non-daemon versus daemon + + if ( g_daemon_mode ) { + // nada + } else { + pnd_log_set_filter ( pndn_rem ); + //pnd_log_set_filter ( pndn_debug ); + pnd_log_to_stdout(); + } + + } else { + FILE *f; + + f = fopen ( "/tmp/mmwrapper.log", "w" ); + + if ( f ) { + pnd_log_set_filter ( logall ); + pnd_log_to_stream ( f ); + pnd_log ( pndn_rem, "logall mode - logging to /tmp/mmwrapper.log\n" ); + } + + if ( logall == pndn_debug ) { + pnd_log_set_buried_logging ( 1 ); // log the shit out of it + pnd_log ( pndn_rem, "logall mode 0 - turned on buried logging\n" ); + } + + } // logall + + pnd_log ( pndn_rem, "%s built %s %s", argv [ 0 ], __DATE__, __TIME__ ); + + pnd_log ( pndn_rem, "log level starting as %u", pnd_log_get_filter() ); + + pnd_log ( pndn_rem, "Frontend is %s", g_frontend ); + + if ( g_daemon_mode ) { + + // set a CWD somewhere else +#if 0 + chdir ( "/tmp" ); +#endif + + // detach from terminal + if ( ( i = fork() ) < 0 ) { + pnd_log ( pndn_error, "ERROR: Couldn't fork()\n" ); + exit ( i ); + } + if ( i ) { + exit ( 0 ); // exit parent + } + setsid(); + + // umask + umask ( 022 ); // emitted files can be rwxr-xr-x + + } // set up daemon + + // check frontend + if ( ! g_frontend ) { + pnd_log ( pndn_error, "ERROR: No frontend specified!\n" ); + exit ( -1 ); + } + + /* actual work now + */ + + // invoke frontend + // wait for something to come back when it exits + + char cmdbuf [ 1024 ]; + char *c; + char *args; + + while ( 1 ) { + + // reset + bzero ( cmdbuf, 1024 ); + args = NULL; + + // invoke frontend + pnd_log ( pndn_debug, "Invoking frontend: %s\n", g_frontend ); + + FILE *fe = popen ( g_frontend, "r" ); + fgets ( cmdbuf, 1000, fe ); + pclose ( fe ); + + if ( ( c = strchr ( cmdbuf, '\n' ) ) ) { + *c = '\0'; // truncate trailing newline + } + + if ( ( c = strchr ( cmdbuf, ' ' ) ) ) { + *c = '\0'; + args = c + 1; + } + + pnd_log ( pndn_debug, "Command from frontend: '%s' args: '%s'\n", cmdbuf, args ? args : "none" ); + + // deal with command + if ( strcasecmp ( cmdbuf, MM_QUIT ) == 0 ) { + // time to die! + pnd_log ( pndn_rem, "Frontend requests shutdown.\n" ); + exit ( 0 ); + } else if ( strcasecmp ( cmdbuf, MM_RUN ) == 0 ) { + // shell out and run it + int retval = system ( args ); + + if ( retval < 0 ) { + pnd_log ( pndn_error, "ERROR: Couldn't invoke application specified by frontend: '%s'\n", args ); + } else { + pnd_log ( pndn_rem, "Invoked application returned %d\n", WEXITSTATUS(retval) ); + } + + } else { + pnd_log ( pndn_rem, "Unexpected exit of frontend; restarting it!\n" ); + // next time around the loop + } + + } // while + + return ( 0 ); +} // main diff --git a/minimenu/skin/default/800480_4.png b/minimenu/skin/default/800480_4.png new file mode 100644 index 0000000..9aa3653 Binary files /dev/null and b/minimenu/skin/default/800480_4.png differ diff --git a/minimenu/skin/default/800480_5.png b/minimenu/skin/default/800480_5.png new file mode 100644 index 0000000..3c8caf8 Binary files /dev/null and b/minimenu/skin/default/800480_5.png differ diff --git a/minimenu/skin/default/800480_6.png b/minimenu/skin/default/800480_6.png new file mode 100644 index 0000000..5d5d731 Binary files /dev/null and b/minimenu/skin/default/800480_6.png differ diff --git a/minimenu/skin/default/Vera.ttf b/minimenu/skin/default/Vera.ttf new file mode 100644 index 0000000..58cd6b5 Binary files /dev/null and b/minimenu/skin/default/Vera.ttf differ diff --git a/minimenu/skin/default/arrowdown.png b/minimenu/skin/default/arrowdown.png new file mode 100644 index 0000000..abbdbc0 Binary files /dev/null and b/minimenu/skin/default/arrowdown.png differ diff --git a/minimenu/skin/default/arrowscroller.png b/minimenu/skin/default/arrowscroller.png new file mode 100644 index 0000000..8ba9cc2 Binary files /dev/null and b/minimenu/skin/default/arrowscroller.png differ diff --git a/minimenu/skin/default/arrowup.png b/minimenu/skin/default/arrowup.png new file mode 100644 index 0000000..c1616aa Binary files /dev/null and b/minimenu/skin/default/arrowup.png differ diff --git a/minimenu/skin/default/detailpane.png b/minimenu/skin/default/detailpane.png new file mode 100644 index 0000000..58f4f83 Binary files /dev/null and b/minimenu/skin/default/detailpane.png differ diff --git a/minimenu/skin/default/detailpane2.png b/minimenu/skin/default/detailpane2.png new file mode 100644 index 0000000..97276f0 Binary files /dev/null and b/minimenu/skin/default/detailpane2.png differ diff --git a/minimenu/skin/default/hilite.png b/minimenu/skin/default/hilite.png new file mode 100644 index 0000000..e4675d8 Binary files /dev/null and b/minimenu/skin/default/hilite.png differ diff --git a/minimenu/skin/default/pandora60.png b/minimenu/skin/default/pandora60.png new file mode 100644 index 0000000..f3aa263 Binary files /dev/null and b/minimenu/skin/default/pandora60.png differ diff --git a/minimenu/skin/default/select.png b/minimenu/skin/default/select.png new file mode 100644 index 0000000..cdd15dc Binary files /dev/null and b/minimenu/skin/default/select.png differ diff --git a/minimenu/skin/default/tab1mask.png b/minimenu/skin/default/tab1mask.png new file mode 100644 index 0000000..8cdd900 Binary files /dev/null and b/minimenu/skin/default/tab1mask.png differ diff --git a/minimenu/skin/default/tab_sel.png b/minimenu/skin/default/tab_sel.png new file mode 100644 index 0000000..3a444a6 Binary files /dev/null and b/minimenu/skin/default/tab_sel.png differ diff --git a/minimenu/skin/default/tab_sel.png.bak b/minimenu/skin/default/tab_sel.png.bak new file mode 100644 index 0000000..3a444a6 Binary files /dev/null and b/minimenu/skin/default/tab_sel.png.bak differ diff --git a/minimenu/skin/default/tab_sel_tall.png b/minimenu/skin/default/tab_sel_tall.png new file mode 100644 index 0000000..d8583f3 Binary files /dev/null and b/minimenu/skin/default/tab_sel_tall.png differ diff --git a/minimenu/skin/default/tab_unsel.png b/minimenu/skin/default/tab_unsel.png new file mode 100644 index 0000000..d535a07 Binary files /dev/null and b/minimenu/skin/default/tab_unsel.png differ diff --git a/minimenu/skin/default/ttf-bitstream-vera-1.10.tar.bz2 b/minimenu/skin/default/ttf-bitstream-vera-1.10.tar.bz2 new file mode 100644 index 0000000..f9f2f8b Binary files /dev/null and b/minimenu/skin/default/ttf-bitstream-vera-1.10.tar.bz2 differ