From 92f24e9074fc8b9975311753fa4dbdd990ce80d5 Mon Sep 17 00:00:00 2001 From: skeezix Date: Tue, 8 Dec 2009 16:35:19 -0500 Subject: [PATCH] Hacked around pndnotifyd so it supports two sets of paths now -- menu path and desktop path ie: apps are now in SD /pandora/desktop and /pandora/menu and go to their configured .desktop emit paths TODO: fix up discotest and conftest to not look for apps.autodiscovery.searchpath but left it in for now --- Makefile | 2 +- apps/pnd_run.c | 7 - apps/pndnotifyd.c | 416 ++++++++++++--------- deployment/etc/pandora/conf/apps | 5 +- deployment/etc/pandora/conf/desktop | 11 +- include/pnd_apps.h | 15 +- testdata/conf/apps | 2 +- testdata/conf/desktop | 13 +- testdata/doticons/jeff.sample.3.png | Bin 0 -> 14866 bytes testdata/doticons/skype-96fad0c.png | Bin 0 -> 5121 bytes testdata/menuapps/sampleapp9/PXML.xml | 41 ++ testdata/menuapps/sampleapp9/program.exe | 0 testdata/menuapps/sampleapp9/zeldaicon.png | Bin 0 -> 14866 bytes 13 files changed, 311 insertions(+), 201 deletions(-) create mode 100644 testdata/doticons/jeff.sample.3.png create mode 100644 testdata/doticons/skype-96fad0c.png create mode 100644 testdata/menuapps/sampleapp9/PXML.xml create mode 100644 testdata/menuapps/sampleapp9/program.exe create mode 100644 testdata/menuapps/sampleapp9/zeldaicon.png diff --git a/Makefile b/Makefile index e17b2a1..fa52006 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ ALLOBJ = pnd_conf.o pnd_container.o pnd_discovery.o pnd_pxml.o pnd_notify.o pnd_ all: ${SOLIB} ${LIB} conftest discotest notifytest pndnotifyd rawpxmltest pndvalidator loggertest pnd_run clean: - ${RM} -f ${ALLOBJ} ${XMLOBJ} ${LIB} ${SOLIB1} locatetest.o bin/locatetest conftest.o bin/conftest discotest.o bin/discotest loggertest.o bin/loggertest bin/notifytest notifytest.o bin/rawpxmltest rawpxmltest.o bin/pnd_run pnd_run.o bin/pndnotifyd pndnotifyd.o ${SOLIB} testdata/dotdesktop/*.desktop testdata/apps/*.pnd testdata/dotdesktop/*.png deployment/usr/lib/libpnd* deployment/usr/bin/pndnotifyd deployment/usr/pandora/scripts/* deployment/etc/sudoers deployment/etc/init.d/pndnotifyd bin/pndvalidator pndvalidator.o + ${RM} -f ${ALLOBJ} ${XMLOBJ} ${LIB} ${SOLIB1} locatetest.o bin/locatetest conftest.o bin/conftest discotest.o bin/discotest loggertest.o bin/loggertest bin/notifytest notifytest.o bin/rawpxmltest rawpxmltest.o bin/pnd_run pnd_run.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/pandora/scripts/* deployment/etc/sudoers deployment/etc/init.d/pndnotifyd bin/pndvalidator pndvalidator.o ${RM} -rf deployment/media find . -name "*~*" -exec rm {} \; -print diff --git a/apps/pnd_run.c b/apps/pnd_run.c index 98b9bc7..c012ac6 100644 --- a/apps/pnd_run.c +++ b/apps/pnd_run.c @@ -55,7 +55,6 @@ int main ( int argc, char *argv[] ) { // ---> cribbed right out of discotest :/ copypaste ftw! if ( ! pnd_run ) { char *configpath; - char *appspath; char *overridespath; // attempt to fetch a sensible default searchpath for configs @@ -67,11 +66,6 @@ int main ( int argc, char *argv[] ) { apph = pnd_conf_fetch_by_id ( pnd_conf_apps, configpath ); if ( apph ) { - appspath = pnd_conf_get_as_char ( apph, PND_APPS_KEY ); - - if ( ! appspath ) { - appspath = PND_APPS_SEARCHPATH; - } overridespath = pnd_conf_get_as_char ( apph, PND_PXML_OVERRIDE_KEY ); @@ -81,7 +75,6 @@ int main ( int argc, char *argv[] ) { } else { // couldn't find a useful app search path so use the default - appspath = PND_APPS_SEARCHPATH; overridespath = PND_PXML_OVERRIDE_SEARCHPATH; } diff --git a/apps/pndnotifyd.c b/apps/pndnotifyd.c index 822c8f9..4d3d701 100644 --- a/apps/pndnotifyd.c +++ b/apps/pndnotifyd.c @@ -48,13 +48,18 @@ typedef enum { // like discotest char *configpath; -char *appspath; char *overridespath; // daemon stuff char *searchpath = NULL; -char *dotdesktoppath = NULL; -char *iconpath = NULL; char *notifypath = NULL; +time_t createtime = 0; // all 'new' .destops are created at or after this time; prev are old. +// dotfiles; this used to be a single pai .. now two pairs, a little unwieldy; pnd_box it up? +char *desktop_dotdesktoppath = NULL; +char *desktop_iconpath = NULL; +char *desktop_appspath = NULL; +char *menu_dotdesktoppath = NULL; +char *menu_iconpath = NULL; +char *menu_appspath = NULL; // pnd runscript char *run_searchpath; // searchpath to find pnd_run.sh char *run_script; // name of pnd_run.sh script from config @@ -70,6 +75,9 @@ pnd_notify_handle nh = 0; void consume_configuration ( void ); void setup_notifications ( void ); void sighup_handler ( int n ); +void process_discoveries ( pnd_box_handle applist, char *emitdesktoppath, char *emiticonpath ); +unsigned char perform_discoveries ( char *appspath, char *overridespath, + char *emitdesktoppath, char *emiticonpath ); int main ( int argc, char *argv[] ) { // behaviour @@ -145,12 +153,19 @@ int main ( int argc, char *argv[] ) { /* startup */ - pnd_log ( pndn_rem, "Apps searchpath is '%s'\n", appspath ); pnd_log ( pndn_rem, "PXML overrides searchpath is '%s'\n", overridespath ); - pnd_log ( pndn_rem, ".desktop files emit to '%s'\n", dotdesktoppath ); - pnd_log ( pndn_rem, ".desktop icon files emit to '%s'\n", iconpath ); pnd_log ( pndn_rem, "Notify searchpath is '%s'\n", notifypath ); + pnd_log ( pndn_rem, "Desktop apps ---------------------------------\n" ); + pnd_log ( pndn_rem, "Apps searchpath is '%s'\n", desktop_appspath ); + pnd_log ( pndn_rem, ".desktop files emit to '%s'\n", desktop_dotdesktoppath ); + pnd_log ( pndn_rem, ".desktop icon files emit to '%s'\n", desktop_iconpath ); + + pnd_log ( pndn_rem, "Menu apps ---------------------------------\n" ); + pnd_log ( pndn_rem, "Apps searchpath is '%s'\n", menu_appspath ); + pnd_log ( pndn_rem, ".desktop files emit to '%s'\n", menu_dotdesktoppath ); + pnd_log ( pndn_rem, ".desktop icon files emit to '%s'\n", menu_iconpath ); + /* set up signal handler */ sigset_t ss; @@ -180,8 +195,7 @@ int main ( int argc, char *argv[] ) { if ( scanonlaunch || pnd_notify_rediscover_p ( nh ) ) { - pnd_box_handle applist; - time_t createtime = time ( NULL ); // all 'new' .destops are created at or after this time; prev are old. + createtime = time ( NULL ); // all 'new' .destops are created at or after this time; prev are old. // if this was a forced scan, lets not do that next iteration if ( scanonlaunch ) { @@ -194,153 +208,18 @@ int main ( int argc, char *argv[] ) { pnd_log ( pndn_rem, "------------------------------------------------------\n" ); pnd_log ( pndn_rem, "Changes within watched paths .. performing re-discover!\n" ); - // run the discovery - applist = pnd_disco_search ( appspath, overridespath ); - - // list the found apps (if any) - if ( applist ) { - pnd_disco_t *d = pnd_box_get_head ( applist ); - - while ( d ) { - - pnd_log ( pndn_rem, "Found app: %s\n", pnd_box_get_key ( d ) ); - - // check if icon already exists (from a previous extraction say); if so, we needn't - // do it again - char existingpath [ FILENAME_MAX ]; - sprintf ( existingpath, "%s/%s.png", iconpath, d -> unique_id ); - - struct stat dirs; - if ( stat ( existingpath, &dirs ) == 0 ) { - // icon seems to exist, so just crib the location into the .desktop - - pnd_log ( pndn_rem, " Found icon already existed, so reusing it! %s\n", existingpath ); - - if ( d -> icon ) { - free ( d -> icon ); - } - d -> icon = strdup ( existingpath ); - - } else { - // icon seems unreadable or does not exist; lets try to create it.. - - pnd_log ( pndn_rem, " Icon not already present, so trying to write it! %s\n", existingpath ); - - // attempt to create icon files; if successful, alter the disco struct to contain new - // path, otherwise leave it alone (since it could be a generic icon reference..) - if ( pnd_emit_icon ( iconpath, d ) ) { - // success; fix up icon path to new one.. - if ( d -> icon ) { - free ( d -> icon ); - } - d -> icon = strdup ( existingpath ); - } else { - pnd_log ( pndn_rem, " WARN: Couldn't write out icon %s\n", existingpath ); - } - - } // icon already exists? - - // create the .desktop file - if ( pnd_emit_dotdesktop ( dotdesktoppath, pndrun, d ) ) { - // add a watch onto the newly created .desktop? -#if 0 - char buffer [ FILENAME_MAX ]; - sprintf ( buffer, "%s/%s", dotdesktoppath, d -> unique_id ); - pnd_notify_watch_path ( nh, buffer, PND_NOTIFY_RECURSE ); -#endif - } else { - pnd_log ( pndn_rem, "ERROR: Error creating .desktop file for app: %s\n", pnd_box_get_key ( d ) ); - } - - // next! - d = pnd_box_get_next ( d ); - - } // while applist - - } else { - - pnd_log ( pndn_rem, "No applications found in search path\n" ); - - } // got apps? - - // run a clean up, to remove any dotdesktop files that we didn't - // just now create (that seem to have been created by pndnotifyd - // previously.) This allows SD eject (or .pnd remove) to remove - // an app from the launcher - // NOTE: Could opendir and iterate across all .desktop files, - // removing any that have Source= something else, and that the - // app name is not in the list found in applist box above. But - // a cheesy simple way right now is to just remove .desktop files - // that have a last mod time prior to the time we stored above. - { - DIR *dir; - - if ( ( dir = opendir ( dotdesktoppath ) ) ) { - struct dirent *dirent; - struct stat dirs; - char buffer [ FILENAME_MAX ]; - - while ( ( dirent = readdir ( dir ) ) ) { - - // file is a .desktop? - if ( strstr ( dirent -> d_name, ".desktop" ) == NULL ) { - continue; - } - - // figure out full path - sprintf ( buffer, "%s/%s", dotdesktoppath, dirent -> d_name ); - - // file was previously created by libpnd; check Source= line - // logic: default to 'yes' (in case we can't open the file for some reason) - // if we can open the file, default to no and look for the source flag we added; if - // that matches then we know its libpnd created, otherwise assume not. - unsigned char source_libpnd = 1; - { - char line [ 256 ]; - FILE *grep = fopen ( buffer, "r" ); - if ( grep ) { - source_libpnd = 0; - while ( fgets ( line, 255, grep ) ) { - if ( strcasestr ( line, PND_DOTDESKTOP_SOURCE ) ) { - source_libpnd = 2; - } - } // while - fclose ( grep ); - } - } - if ( source_libpnd ) { -#if 0 - pnd_log ( pndn_rem, - "File '%s' appears to have been created by libpnd so candidate for delete: %u\n", buffer, source_libpnd ); -#endif - } else { -#if 0 - pnd_log ( pndn_rem, "File '%s' appears NOT to have been created by libpnd, so leave it alone\n", buffer ); -#endif - continue; // skip deleting it - } - - // file is 'new'? - if ( stat ( buffer, &dirs ) == 0 ) { - if ( dirs.st_mtime >= createtime ) { -#if 0 - pnd_log ( pndn_rem, "File '%s' seems 'new', so leave it alone.\n", buffer ); -#endif - continue; // skip deleting it - } - } + /* run the discovery + */ - // by this point, the .desktop file must be 'old' and created by pndnotifyd - // previously, so can remove it - pnd_log ( pndn_rem, "File '%s' seems nolonger relevent; removing it.\n", dirent -> d_name ); - unlink ( buffer ); - - } // while getting filenames from dir - - closedir ( dir ); - } + pnd_log ( pndn_rem, "Scanning desktop paths----------------------------\n" ); + if ( ! perform_discoveries ( desktop_appspath, overridespath, desktop_dotdesktoppath, desktop_iconpath ) ) { + pnd_log ( pndn_rem, "No applications found in desktop search path\n" ); + } - } // purge old .desktop files + pnd_log ( pndn_rem, "Scanning menu paths----------------------------\n" ); + if ( ! perform_discoveries ( menu_appspath, overridespath, menu_dotdesktoppath, menu_iconpath ) ) { + pnd_log ( pndn_rem, "No applications found in menu search path\n" ); + } // if we've got a hup script located, lets invoke it if ( pndhup ) { @@ -380,12 +259,6 @@ void consume_configuration ( void ) { if ( apph ) { - appspath = pnd_conf_get_as_char ( apph, PND_APPS_KEY ); - - if ( ! appspath ) { - appspath = PND_APPS_SEARCHPATH; - } - overridespath = pnd_conf_get_as_char ( apph, PND_PXML_OVERRIDE_KEY ); if ( ! overridespath ) { @@ -400,34 +273,47 @@ void consume_configuration ( void ) { } else { // couldn't find a useful app search path so use the default - appspath = PND_APPS_SEARCHPATH; overridespath = PND_PXML_OVERRIDE_SEARCHPATH; notifypath = PND_APPS_NOTIFYPATH; } - // attempt to figure out where to drop dotfiles + // attempt to figure out where to drop dotfiles .. now that we're going + // multi-target we see the limit of my rudimentary conf-file parser; should + // just parse to an array of targets, rather that hardcoding two, but + // on the other hand, don't likely see the need for more than two? (famous + // last words.) pnd_conf_handle desktoph; desktoph = pnd_conf_fetch_by_id ( pnd_conf_desktop, configpath ); + // for 'desktop' main applications if ( desktoph ) { - dotdesktoppath = pnd_conf_get_as_char ( desktoph, PND_DOTDESKTOP_KEY ); + desktop_dotdesktoppath = pnd_conf_get_as_char ( desktoph, PND_DESKTOP_DOTDESKTOP_PATH_KEY ); + desktop_iconpath = pnd_conf_get_as_char ( desktoph, PND_DESKTOP_ICONS_PATH_KEY ); + desktop_appspath = pnd_conf_get_as_char ( desktoph, PND_DESKTOP_SEARCH_KEY ); + } - if ( ! dotdesktoppath ) { - dotdesktoppath = PND_DOTDESKTOP_DEFAULT; - } + if ( ! desktop_dotdesktoppath ) { + desktop_dotdesktoppath = PND_DESKTOP_DOTDESKTOP_PATH_DEFAULT; + } - iconpath = pnd_conf_get_as_char ( desktoph, PND_DOTDESKTOPICONS_KEY ); + if ( ! desktop_iconpath ) { + desktop_iconpath = PND_DESKTOP_ICONS_PATH_DEFAULT; + } - if ( ! iconpath ) { - iconpath = PND_DOTDESKTOPICONS_DEFAULT; - } + if ( ! desktop_appspath ) { + desktop_appspath = PND_DESKTOP_SEARCH_PATH_DEFAULT; + } - } else { - dotdesktoppath = PND_DOTDESKTOPICONS_DEFAULT; + // for 'menu' applications + if ( desktoph ) { + menu_dotdesktoppath = pnd_conf_get_as_char ( desktoph, PND_MENU_DOTDESKTOP_PATH_KEY ); + menu_iconpath = pnd_conf_get_as_char ( desktoph, PND_MENU_ICONS_PATH_KEY ); + menu_appspath = pnd_conf_get_as_char ( desktoph, PND_MENU_SEARCH_KEY ); } - // try to locate a runscript and optional hupscript + /* try to locate a runscript and optional hupscript + */ if ( apph ) { run_searchpath = pnd_conf_get_as_char ( apph, PND_PNDRUN_SEARCHPATH_KEY ); @@ -491,13 +377,19 @@ void consume_configuration ( void ) { /* handle globbing or variable substitution */ - dotdesktoppath = pnd_expand_tilde ( strdup ( dotdesktoppath ) ); - iconpath = pnd_expand_tilde ( strdup ( iconpath ) ); - - /* validate paths - */ - mkdir ( dotdesktoppath, 0777 ); - mkdir ( iconpath, 0777 ); + desktop_dotdesktoppath = pnd_expand_tilde ( strdup ( desktop_dotdesktoppath ) ); + desktop_iconpath = pnd_expand_tilde ( strdup ( desktop_iconpath ) ); + mkdir ( desktop_dotdesktoppath, 0777 ); + mkdir ( desktop_iconpath, 0777 ); + + if ( menu_dotdesktoppath ) { + menu_dotdesktoppath = pnd_expand_tilde ( strdup ( menu_dotdesktoppath ) ); + mkdir ( menu_dotdesktoppath, 0777 ); + } + if ( menu_iconpath ) { + menu_iconpath = pnd_expand_tilde ( strdup ( menu_iconpath ) ); + mkdir ( menu_iconpath, 0777 ); + } // done return; @@ -554,3 +446,169 @@ void sighup_handler ( int n ) { return; } + +// This very recently was inline code; just slight refactor to functionize it so that it can be +// reused in a couple of places. Simple code with simple design quickly became too large for +// its simple design; should revisit a lot of these little things.. +void process_discoveries ( pnd_box_handle applist, char *emitdesktoppath, char *emiticonpath ) { + pnd_disco_t *d = pnd_box_get_head ( applist ); + + while ( d ) { + + pnd_log ( pndn_rem, "Found app: %s\n", pnd_box_get_key ( d ) ); + + // check if icon already exists (from a previous extraction say); if so, we needn't + // do it again + char existingpath [ FILENAME_MAX ]; + sprintf ( existingpath, "%s/%s.png", emiticonpath, d -> unique_id ); + + struct stat dirs; + if ( stat ( existingpath, &dirs ) == 0 ) { + // icon seems to exist, so just crib the location into the .desktop + + pnd_log ( pndn_rem, " Found icon already existed, so reusing it! %s\n", existingpath ); + + if ( d -> icon ) { + free ( d -> icon ); + } + d -> icon = strdup ( existingpath ); + + } else { + // icon seems unreadable or does not exist; lets try to create it.. + + pnd_log ( pndn_debug, " Icon not already present, so trying to write it! %s\n", existingpath ); + + // attempt to create icon files; if successful, alter the disco struct to contain new + // path, otherwise leave it alone (since it could be a generic icon reference..) + if ( pnd_emit_icon ( emiticonpath, d ) ) { + // success; fix up icon path to new one.. + if ( d -> icon ) { + free ( d -> icon ); + } + d -> icon = strdup ( existingpath ); + } else { + pnd_log ( pndn_debug, " WARN: Couldn't write out icon %s\n", existingpath ); + } + + } // icon already exists? + + // create the .desktop file + if ( pnd_emit_dotdesktop ( emitdesktoppath, pndrun, d ) ) { + // add a watch onto the newly created .desktop? +#if 0 + char buffer [ FILENAME_MAX ]; + sprintf ( buffer, "%s/%s", emitdesktoppath, d -> unique_id ); + pnd_notify_watch_path ( nh, buffer, PND_NOTIFY_RECURSE ); +#endif + } else { + pnd_log ( pndn_rem, "ERROR: Error creating .desktop file for app: %s\n", pnd_box_get_key ( d ) ); + } + + // next! + d = pnd_box_get_next ( d ); + + } // while applist + + return; +} + +// returns true if any applications were found +unsigned char perform_discoveries ( char *appspath, char *overridespath, // args to do discovery + char *emitdesktoppath, char *emiticonpath ) // args to do emitting +{ + pnd_box_handle applist; + + // attempt to auto-discover applications in the given path + applist = pnd_disco_search ( appspath, overridespath ); + + if ( ! applist ) { + return ( 0 ); + } + + process_discoveries ( applist, emitdesktoppath, emiticonpath ); + + // run a clean up, to remove any dotdesktop files that we didn't + // just now create (that seem to have been created by pndnotifyd + // previously.) This allows SD eject (or .pnd remove) to remove + // an app from the launcher + // NOTE: Could opendir and iterate across all .desktop files, + // removing any that have Source= something else, and that the + // app name is not in the list found in applist box above. But + // a cheesy simple way right now is to just remove .desktop files + // that have a last mod time prior to the time we stored above. + { + DIR *dir; + + if ( ( dir = opendir ( emitdesktoppath ) ) ) { + struct dirent *dirent; + struct stat dirs; + char buffer [ FILENAME_MAX ]; + + while ( ( dirent = readdir ( dir ) ) ) { + + // file is a .desktop? + if ( strstr ( dirent -> d_name, ".desktop" ) == NULL ) { + continue; + } + + // figure out full path + sprintf ( buffer, "%s/%s", emitdesktoppath, dirent -> d_name ); + + // file was previously created by libpnd; check Source= line + // logic: default to 'yes' (in case we can't open the file for some reason) + // if we can open the file, default to no and look for the source flag we added; if + // that matches then we know its libpnd created, otherwise assume not. + unsigned char source_libpnd = 1; + { + char line [ 256 ]; + FILE *grep = fopen ( buffer, "r" ); + if ( grep ) { + source_libpnd = 0; + while ( fgets ( line, 255, grep ) ) { + if ( strcasestr ( line, PND_DOTDESKTOP_SOURCE ) ) { + source_libpnd = 2; + } + } // while + fclose ( grep ); + } + } + if ( source_libpnd ) { +#if 0 + pnd_log ( pndn_rem, + "File '%s' appears to have been created by libpnd so candidate for delete: %u\n", buffer, source_libpnd ); +#endif + } else { +#if 0 + pnd_log ( pndn_rem, "File '%s' appears NOT to have been created by libpnd, so leave it alone\n", buffer ); +#endif + continue; // skip deleting it + } + + // file is 'new'? + if ( stat ( buffer, &dirs ) == 0 ) { + if ( dirs.st_mtime >= createtime ) { +#if 0 + pnd_log ( pndn_rem, "File '%s' seems 'new', so leave it alone.\n", buffer ); +#endif + continue; // skip deleting it + } + } + + // by this point, the .desktop file must be 'old' and created by pndnotifyd + // previously, so can remove it + pnd_log ( pndn_rem, "File '%s' seems nolonger relevent; removing it.\n", dirent -> d_name ); + unlink ( buffer ); + + } // while getting filenames from dir + + closedir ( dir ); + } + + } // purge old .desktop files + + //WARN: MEMORY LEAK HERE + pnd_log ( pndn_debug, "pndnotifyd - memory leak here - perform_discoveries()\n" ); + pnd_box_delete ( applist ); // does not free the disco_t contents! + + return ( 1 ); +} diff --git a/deployment/etc/pandora/conf/apps b/deployment/etc/pandora/conf/apps index 9040efe..ecf98e4 100644 --- a/deployment/etc/pandora/conf/apps +++ b/deployment/etc/pandora/conf/apps @@ -7,7 +7,7 @@ searchpath /media/*/pandora/apps:/usr/pandora/apps # notifypath is a list of paths to monitor; if anything in those paths changes, the searchpath is rescanned # note that for each path chunk, all current subdirs of that path are also watched) -notifypath /media:/media/*/pandora/apps:/usr/pandora/apps:./testdata/app? +notifypath /media:/media/*/pandora/apps:/usr/pandora/apps # PXMLs may be overridden .. ie: overrides are a subset of PXML, where the values are copied over the full PXML [overrides] @@ -16,7 +16,8 @@ searchpath ~/pxml-overrides # [pnd] defines where to locate the pnd support scripts, so the user may override pnd_run.sh without clobbering built in [pnd] -searchpath /media/*/pandora/scripts:/usr/pandora/scripts +# if you wish to make pnd_run.sh etc be findable on SD, prepend "/media/*/pandora/scripts:" to the 'searchpath' +searchpath /usr/pandora/scripts runscript pnd_run.sh # [pndnotifyd] sets some extra pndnotifyd specific items diff --git a/deployment/etc/pandora/conf/desktop b/deployment/etc/pandora/conf/desktop index b45fccf..55f3975 100644 --- a/deployment/etc/pandora/conf/desktop +++ b/deployment/etc/pandora/conf/desktop @@ -2,10 +2,15 @@ # Open Pandora # Desktop configuration -[dotfiles] -#(~/Desktop for xfce, /usr/share/applications for WMs that actually follow spec) +[desktop] +searchpath /mnt/sd?/pandora/desktop:/usr/pandora/apps # path to depth-search for PXMLs and pnd-files dotdesktoppath ~/Desktop/ # path for pndnotifyd to spit .desktop files into (run as root) -iconpath /tmp # path for pndnotifyd to drop icons into (can be same as .desktop if WM permits) +iconpath /tmp # path for pndnotifyd to drop icons into (can be same as dotdesktoppath if WM permits) + +[menu] +searchpath /mnt/sd?/pandora/menu # path to depth-search for PXMLs and pnd-files +dotdesktoppath /tmp/menu # path for pndnotifyd to spit .desktop files into +iconpath /tmp/menuicons # path for pndnotifyd to drop icons into (can be same as dotdesktoppath if WM permits) [launcher] # if hupscript is commented out entirely, pndnotifyd will not try to find/run the hup diff --git a/include/pnd_apps.h b/include/pnd_apps.h index 51939f9..9e56333 100644 --- a/include/pnd_apps.h +++ b/include/pnd_apps.h @@ -25,10 +25,17 @@ extern "C" { #define PND_MOUNT_PATH "/mnt/pnd/" /* all mounted PND images should be here.. /mnt/apps/UNIQUE-ID/... */ // .desktop support -#define PND_DOTDESKTOP_KEY "dotfiles.dotdesktoppath" -#define PND_DOTDESKTOPICONS_KEY "dotfiles.iconpath" -#define PND_DOTDESKTOP_DEFAULT "~/.applications" -#define PND_DOTDESKTOPICONS_DEFAULT "~/.applications" +#define PND_DESKTOP_DOTDESKTOP_PATH_KEY "desktop.dotdesktoppath" +#define PND_DESKTOP_ICONS_PATH_KEY "desktop.iconpath" +#define PND_DESKTOP_SEARCH_KEY "desktop.searchpath" + +#define PND_MENU_DOTDESKTOP_PATH_KEY "menu.dotdesktoppath" +#define PND_MENU_ICONS_PATH_KEY "menu.iconpath" +#define PND_MENU_SEARCH_KEY "menu.searchpath" + +#define PND_DESKTOP_DOTDESKTOP_PATH_DEFAULT "~/.applications" +#define PND_DESKTOP_ICONS_PATH_DEFAULT "~/.applications" +#define PND_DESKTOP_SEARCH_PATH_DEFAULT "/media/*/pandora/desktop:/usr/pandora/apps" // apps #define PND_DEFAULT_WORKDIR "./" diff --git a/testdata/conf/apps b/testdata/conf/apps index f7ab2c2..4f9a01e 100644 --- a/testdata/conf/apps +++ b/testdata/conf/apps @@ -18,4 +18,4 @@ runscript pnd_run.sh # [pndnotifyd] sets some extra pndnotifyd specific items [pndnotifyd] # logging level 0 means to include debug; level 1 (regular), 2 (warnings), 3 (errors) -loglevel 0 +loglevel 1 diff --git a/testdata/conf/desktop b/testdata/conf/desktop index 4b1e994..6e3d6ca 100644 --- a/testdata/conf/desktop +++ b/testdata/conf/desktop @@ -2,10 +2,15 @@ # Open Pandora # Desktop configuration -[dotfiles] -#dotdesktoppath ./testdata/dotdesktop # path for pndnotifyd to spit .desktop files into -dotdesktoppath ~/.applications # path for pndnotifyd to spit .desktop files into -iconpath ./testdata/dotdesktop # path for pndnotifyd to drop icons into (can be same as .desktop if WM permits) +[desktop] +searchpath /mnt/sd?/pandora/desktop:./testdata/app? # path to depth-search for PXMLs and pnd-files +dotdesktoppath ./testdata/dotdesktop # path for pndnotifyd to spit .desktop files into +iconpath ./testdata/doticons # path for pndnotifyd to drop icons into (can be same as dotdesktoppath if WM permits) + +[menu] +searchpath /mnt/sd?/pandora/menu:./testdata/menuapps # path to depth-search for PXMLs and pnd-files +dotdesktoppath ./testdata/menu # path for pndnotifyd to spit .desktop files into +iconpath ./testdata/menuicons # path for pndnotifyd to drop icons into (can be same as dotdesktoppath if WM permits) [launcher] hupscript pnd_hup.sh diff --git a/testdata/doticons/jeff.sample.3.png b/testdata/doticons/jeff.sample.3.png new file mode 100644 index 0000000000000000000000000000000000000000..140a39386bfe401a5c2d8406bfd0caf568318f08 GIT binary patch literal 14866 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 #<|oyxycV^Z26=k++2w7qH+;7A97fyhppC3v)rE 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

v;u8cxhX+l~3hEj$>wWI`e}tM2dAlDCK>6pd z32Gw4Xm)fGqhSCDf-%G-fSm#hVJA(~mLQ^l4?<|16c7qpAw42M5KU=BJ0VIdHbN&V zT!)qkahw2n4H$t;kR@n=(2N{$B7>@eN&yTGjImbPEUlV07M!ou^^Xl5g(}; z7oiywt{D{u_BBF-;5CF1KR*p0A4(%�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 literal 0 HcmV?d00001 diff --git a/testdata/menuapps/sampleapp9/PXML.xml b/testdata/menuapps/sampleapp9/PXML.xml new file mode 100644 index 0000000..2d59722 --- /dev/null +++ b/testdata/menuapps/sampleapp9/PXML.xml @@ -0,0 +1,41 @@ + + + Sample App 9 + Sample App 9 - German + + + + This is the English Description of the file. + This would be the German description. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ......kfujl........ + ......qwe......fwer..... + diff --git a/testdata/menuapps/sampleapp9/program.exe b/testdata/menuapps/sampleapp9/program.exe new file mode 100644 index 0000000..e69de29 diff --git a/testdata/menuapps/sampleapp9/zeldaicon.png b/testdata/menuapps/sampleapp9/zeldaicon.png new file mode 100644 index 0000000000000000000000000000000000000000..140a39386bfe401a5c2d8406bfd0caf568318f08 GIT binary patch literal 14866 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 #<|oyxycV^Z26=k++2w7qH+;7A97fyhppC3v)rE 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

v;u8cxhX+l~3hEj$>wWI`e}tM2dAlDCK>6pd z32Gw4Xm)fGqhSCDf-%G-fSm#hVJA(~mLQ^l4?<|16c7qpAw42M5KU=BJ0VIdHbN&V zT!)qkahw2n4H$t;kR@n=(2N{$B7>@eN&yTGjImbPEUlV07M!ou^^Xl5g(}; z7oiywt{D{u_BBF-;5CF1KR*p0A4(%�`vKtA}`Dug(XoLrPuM38z|z|mj~fUi0fN`(@V3J4>vBt=l9VEm3cK;5Lbhiz@p zZF*dBK!(|hjQ1T-v$O)7L&F&+F3tzdO%7X{AGIK)Y>C6>W=E|ESx^(?bJ)fbIATXQ z!ejPXm>;mUM)`+w=#Z1Wy1NU)GZ{LBJzsBxK&@bZt&jlCut3d-AV4EBSThPSC`>yp zTqhpBM!DNqX4Kzy|ta zgpY>$W2hk_6+tjtBRXs|Hbg<|0(3xeA^x;FAVb_u&D#Sw;^~e`iag878PQGM(TNU! z1U5Ds(x%3LJG->)ZFlPH68YHKq1g^CEp|!a-#_^Xvt5R@R^W23#s zhLG3+BO`p>XJqJ=5C_U|ANUvi`5=7C-WC&6bRF!#4B()LD<+%(vCYQ=rXmsn#OzG~ zeH-xcKr=K}^Kd)t?t0kO<&cXr3Icd5frA|$0XpzFnvMgOW&lKuVFnO4w;O5tJWRl+ zj*ZPubq#iY!42x4ZNW>&8Qe^UfrI^SLp{>M*cjGsiNnUmJ2W&Lf`arM92ooeYuH%p zIy!(OFm=ep#LUA3cNdP0>N`7uAe^UrNk==^@L4Wtv^!M-(-20%*#M9@HgR09mVwi|DBRl~|^ucl^TR3w(ru(5Yzc{#tLoPmG# zR^Q%kr@jtC21XuMZHWU01|}{p?|kyf*x9oTH8m|88^)nSXb(Adg~ju(e1&~8He0v<%0O^v7l4I~<1pP~K%1A^`B z+Gk*7>F)964f4-reRb_i7B7pjXQ!T%17q(_hOVs*LstvD;Nrdd`di|Vf#Cs^fP3~b zjvUpnu-LD!ze`K&kdYBXTSLvtoS}L2u%#J3)hx_*>FEF}(QOQ!Dr}jWAqws{CqQ=G zZ)t%d==;EV6-YC4b6kbm`hbb)L1O}yU(j@@Ms$yX9*v>91AHTo-``=hh zzkdB%3f_6y#n7?0W*F$~)lg&T*`U1B!r;SM_UY+u3H)kHfS@gL$M~S%8=w{bz39GO zbfjB;-wU?0bGv>X&@()2WQwa8+FIc8kf|}_&_QiW%isOs_vE$kh54BDG{grr6O%pK zdYa}|JNNHp=weQ-t+`7_2Nv$p(b*DkoVx;8R$;RZdkpjec=a9FF0O5EMqj&uP50mY ztnJ42u%Qv-=utW{?$puRtE+QRSNG}PzKj>qzr68HS{9daaQ}W?-J@n^`*aOe|sSVfG)!nJ7iHM8 z|95<;y1PqP`wr~Z(;@gSkE*X2*v;6ZgB}uWXa5iMA2zEQ86DC?_B=Q+I!^ulva-Il zb;rR2hf#m&=Jo4D%2aX;+04W?*M=hppZN*SO$~dBS$iM&&MR?K2 zd`Uwc{oAjlvmG!08@@JQ3zIh6NgqR}+dp?}UhcM^gp_^zjBIQ_`1oUZ&>!D<$16C1 zv2PdQMMDEB198Yu$moncd-iE*ZSaD#2uKICb+^;ue|CPaw)P#MToosEzK1(o(idW= z00{XGeE-h$8Mwff0FRiy{QOr>;8j@6fD$uUsNDAJX(B7_R#!h_Yz(EqkHE_zL%oB# z+Ju)wM~^Ue?I1IP-8;y^|1EZ6(tuEh6u{WMd;gImjGa3fyLO?V+Hr6nJ{f!WV8j@E z_mcB__W##9vy;(1*pF8m@p^