From 39a64f23697324606435d2d75b373919134c72d4 Mon Sep 17 00:00:00 2001 From: skeezix Date: Thu, 3 Nov 2011 13:38:13 -0400 Subject: [PATCH] mmenu can now load .desktops (better at libpnd generated ones) default is to only accept libpnd origin .desktops --- deployment/etc/pandora/conf/mmenu.conf | 3 + include/pnd_desktop.h | 2 +- lib/pnd_desktop.c | 95 +++++++++-- minimenu/mmenu.c | 227 +++++++++++++++++-------- minimenu/mmenu.conf | 3 + testdata/conf/apps | 3 +- testdata/conf/desktop | 6 +- testdata/doticons/jeff.sample.3.png | Bin 14866 -> 0 bytes testdata/doticons/skype-96fad0c.png | Bin 5121 -> 0 bytes 9 files changed, 251 insertions(+), 88 deletions(-) delete mode 100644 testdata/doticons/jeff.sample.3.png delete mode 100644 testdata/doticons/skype-96fad0c.png diff --git a/deployment/etc/pandora/conf/mmenu.conf b/deployment/etc/pandora/conf/mmenu.conf index ba2700a..e56afed 100644 --- a/deployment/etc/pandora/conf/mmenu.conf +++ b/deployment/etc/pandora/conf/mmenu.conf @@ -24,6 +24,9 @@ defer_icon_us 100000 # when background loading icons (load_icons_later), time b threaded_preview 0 # if 1, will try to load the preview in background, to avoid slowing up navigation loglevel 0 # 0 is debug, lots of crap; 3 is better, means 'errors only'. Output may screw up the wrapper! x11_present_sh /bin/pidof X # command to invoke to determine if X11 is running or not; expects a number on X is present. +disco_pnds 1 # if nonzero, will do application discovery on pnd-files +disco_dotdesktop 0 # if nonzero, will do application discovery on .desktop files +disco_dotdesktop_all 0 # if nonzero, will include non-libpnd .desktop; if 0, just libpnd (pnd-found by pndnotifyd) will be included desktop_apps 1 # search the pnd standard desktop searchpath for apps menu_apps 1 # search the pnd standard menu searchpath for apps aux_searchpath /media/*/pandora/mmenu:/usr/pandora/mmenu # if something here, also search this path; can be used for mmenu-only apps? diff --git a/include/pnd_desktop.h b/include/pnd_desktop.h index 5c53812..6c8d0ab 100644 --- a/include/pnd_desktop.h +++ b/include/pnd_desktop.h @@ -16,7 +16,7 @@ extern "C" { unsigned char pnd_emit_dotdesktop ( char *targetpath, char *pndrun, pnd_disco_t *p ); #define PND_DOTDESKTOP_LIBPND_ONLY 1 /* convenience flag; caller can do this himself as well */ -pnd_disco_t *pnd_parse_dotdesktop ( char *ddpath, unsigned int flags ); // sets object_flag CUSTOM1 for non-libpnd-origin +pnd_disco_t *pnd_parse_dotdesktop ( char *ddpath, unsigned int flags ); // sets object_flag CUSTOM1 for libpnd-origin // emit_dotinfo() will spit out a .desktop 'info entry', similar to the way emit_dotdesktop does its thing // - rather than slide this into emit_dotdesktop(), we wish to allow apps to do this or not by calling this diff --git a/lib/pnd_desktop.c b/lib/pnd_desktop.c index dc7aa63..77d363a 100644 --- a/lib/pnd_desktop.c +++ b/lib/pnd_desktop.c @@ -748,6 +748,22 @@ pnd_disco_t *pnd_parse_dotdesktop ( char *ddpath, unsigned int flags ) { // - clockspeed // - categories + char pndpath [ 1024 ]; + bzero ( pndpath, 1024 ); + + // filter on filename? + if ( flags & PND_DOTDESKTOP_LIBPND_ONLY ) { + // too bad we didn't put some libpnd token at the front of the filename or something + // hell, we should cleanse unique-id to ensure its not full of special chars like '*' and '..'.. eep! + if ( strrchr ( ddpath, '#' ) == NULL ) { // but if requiring libpnd, we can at least check for #subapp-number + return ( NULL ); + } + } + + if ( strstr ( ddpath, ".desktop" ) == NULL ) { + return ( NULL ); + } + // determine file length struct stat statbuf; @@ -784,6 +800,8 @@ pnd_disco_t *pnd_parse_dotdesktop ( char *ddpath, unsigned int flags ) { if ( strncmp ( dd, "Name=", 5 ) == 0 ) { p -> title_en = strdup ( dd + 5 ); + } else if ( strncmp ( dd, "Name[en]=", 9 ) == 0 ) { + p -> title_en = strdup ( dd + 9 ); } else if ( strncmp ( dd, "Icon=", 5 ) == 0 ) { p -> icon = strdup ( dd + 5 ); } else if ( strcmp ( dd, PND_DOTDESKTOP_SOURCE ) == 0 ) { @@ -792,21 +810,39 @@ pnd_disco_t *pnd_parse_dotdesktop ( char *ddpath, unsigned int flags ) { p -> unique_id = strdup ( dd + 14 ); } else if ( strncmp ( dd, "Comment=", 8 ) == 0 ) { p -> desc_en = strdup ( dd + 8 ); + } else if ( strncmp ( dd, "Comment[en]=", 12 ) == 0 ) { + p -> desc_en = strdup ( dd + 12 ); } else if ( strncmp ( dd, "Exec=", 5 ) == 0 ) { char *e = strstr ( dd, " -e " ); + if ( e ) { - e += 5; + // probably libpnd app - char *space = strchr ( e, ' ' ); - p -> exec = strndup ( e, space - e - 1 ); - } + if ( e ) { + e += 5; - char *b = strstr ( dd, " -b " ); - if ( b ) { - b += 5; - char *space = strchr ( b, '\0' ); - p -> appdata_dirname = strndup ( b, space - b - 1 ); + char *space = strchr ( e, ' ' ); + p -> exec = strndup ( e, space - e - 1 ); + } + + char *b = strstr ( dd, " -b " ); + if ( b ) { + b += 5; + char *space = strchr ( b, '\0' ); + p -> appdata_dirname = strndup ( b, space - b - 1 ); + } + + char *p = strstr ( dd, " -p " ); + if ( p ) { + p += 5; + char *space = strchr ( p, ' ' ); + strncpy ( pndpath, p, space - p - 1 ); + } + + } else { + // probably not libpnd app + p -> exec = strdup ( dd + 5 ); } } else if ( strncmp ( dd, "Categories=", 11 ) == 0 ) { @@ -832,7 +868,6 @@ pnd_disco_t *pnd_parse_dotdesktop ( char *ddpath, unsigned int flags ) { // filter if ( ! libpnd_origin ) { - p -> object_flags |= PND_DISCO_CUSTOM1; // so caller can do something if it wishes // convenience flag if ( flags & PND_DOTDESKTOP_LIBPND_ONLY ) { @@ -841,19 +876,53 @@ pnd_disco_t *pnd_parse_dotdesktop ( char *ddpath, unsigned int flags ) { return ( NULL ); } + } else { + p -> object_flags |= PND_DISCO_CUSTOM1; // so caller can do something if it wishes + } + + // filter on content + if ( ( ! p -> title_en ) || + ( ! p -> exec ) + ) + { + pnd_disco_destroy ( p ); + free ( p ); + return ( NULL ); + } + + if ( ! p -> unique_id ) { + if ( flags & PND_DOTDESKTOP_LIBPND_ONLY ) { + pnd_disco_destroy ( p ); + free ( p ); + return ( NULL ); + } else { + char hack [ 100 ]; + snprintf ( hack, 100, "inode-%lu", statbuf.st_ino ); + p -> unique_id = strdup ( hack ); + } } // additional p -> object_type = pnd_object_type_pnd; - char *slash = strrchr ( ddpath, '/' ); + + char *source; + if ( pndpath [ 0 ] ) { + source = pndpath; + } else { + source = ddpath; + } + + char *slash = strrchr ( source, '/' ); if ( slash ) { - p -> object_path = strndup ( ddpath, slash - ddpath ); + p -> object_path = strndup ( source, slash - source ); p -> object_filename = strdup ( slash + 1 ); } else { p -> object_path = "./"; - p -> object_filename = strdup ( ddpath ); + p -> object_filename = strdup ( source ); } + fprintf ( stderr, "object %s /// src %s /// %s /// %s\n", ddpath, source, p -> object_path, p -> object_filename ); + // return disco-t return ( p ); } diff --git a/minimenu/mmenu.c b/minimenu/mmenu.c index be38fb0..27b4e49 100644 --- a/minimenu/mmenu.c +++ b/minimenu/mmenu.c @@ -51,6 +51,7 @@ #include "pnd_notify.h" #include "pnd_dbusnotify.h" #include "pnd_apps.h" +#include "pnd_desktop.h" #include "mmenu.h" #include "mmwrapcmd.h" @@ -495,87 +496,158 @@ void applications_scan ( void ) { g_active_apps = 0; pnd_box_handle merge_apps = 0; - // desktop apps? - if ( pnd_conf_get_as_int_d ( g_conf, "minimenu.desktop_apps", 1 ) ) { - pnd_log ( pndn_debug, "Looking for pnd applications here: %s\n", - pnd_conf_get_as_char ( g_desktopconf, "desktop.searchpath" ) ); - g_active_apps = pnd_disco_search ( pnd_conf_get_as_char ( g_desktopconf, "desktop.searchpath" ), NULL ); - } + // boy I wish I built a plugin system here + // - // menu apps? - if ( pnd_conf_get_as_int_d ( g_conf, "minimenu.menu_apps", 1 ) ) { - pnd_log ( pndn_debug, "Looking for pnd applications here: %s\n", - pnd_conf_get_as_char ( g_desktopconf, "menu.searchpath" ) ); - merge_apps = pnd_disco_search ( pnd_conf_get_as_char ( g_desktopconf, "menu.searchpath" ), NULL ); - } + // perform application discovery for pnd-files? + // + if ( pnd_conf_get_as_int_d ( g_conf, "minimenu.disco_pnds", 1 ) ) { - // merge lists - if ( merge_apps ) { - if ( g_active_apps ) { - // the key from pnd_disco_search() is the _path_, so easy to look for duplicates - // this is pretty inefficient, being linked lists; perhaps should switch to hash tables when - // we expect thousands of apps.. or at least an index or something. - void *a = pnd_box_get_head ( merge_apps ); - void *nexta = NULL; - while ( a ) { - nexta = pnd_box_get_next ( a ); - - // if the key for the node is also found in active apps, toss out the merging one - if ( pnd_box_find_by_key ( g_active_apps, pnd_box_get_key ( a ) ) ) { - //fprintf ( stderr, "Merging app id '%s' is duplicate; discarding it.\n", pnd_box_get_key ( a ) ); - pnd_box_delete_node ( merge_apps, a ); + // desktop apps? + if ( pnd_conf_get_as_int_d ( g_conf, "minimenu.desktop_apps", 1 ) ) { + pnd_log ( pndn_debug, "Looking for pnd applications here: %s\n", + pnd_conf_get_as_char ( g_desktopconf, "desktop.searchpath" ) ); + g_active_apps = pnd_disco_search ( pnd_conf_get_as_char ( g_desktopconf, "desktop.searchpath" ), NULL ); + } + + // menu apps? + if ( pnd_conf_get_as_int_d ( g_conf, "minimenu.menu_apps", 1 ) ) { + pnd_log ( pndn_debug, "Looking for pnd applications here: %s\n", + pnd_conf_get_as_char ( g_desktopconf, "menu.searchpath" ) ); + merge_apps = pnd_disco_search ( pnd_conf_get_as_char ( g_desktopconf, "menu.searchpath" ), NULL ); + } + + // merge lists + if ( merge_apps ) { + if ( g_active_apps ) { + // the key from pnd_disco_search() is the _path_, so easy to look for duplicates + // this is pretty inefficient, being linked lists; perhaps should switch to hash tables when + // we expect thousands of apps.. or at least an index or something. + void *a = pnd_box_get_head ( merge_apps ); + void *nexta = NULL; + while ( a ) { + nexta = pnd_box_get_next ( a ); + + // if the key for the node is also found in active apps, toss out the merging one + if ( pnd_box_find_by_key ( g_active_apps, pnd_box_get_key ( a ) ) ) { + //fprintf ( stderr, "Merging app id '%s' is duplicate; discarding it.\n", pnd_box_get_key ( a ) ); + pnd_box_delete_node ( merge_apps, a ); + } + + a = nexta; } - a = nexta; + // got menu apps, and got desktop apps, merge + pnd_box_append ( g_active_apps, merge_apps ); + } else { + // got menu apps, had no desktop apps, so just assign + g_active_apps = merge_apps; } - - // got menu apps, and got desktop apps, merge - pnd_box_append ( g_active_apps, merge_apps ); - } else { - // got menu apps, had no desktop apps, so just assign - g_active_apps = merge_apps; } - } - // aux apps? - char *aux_apps = NULL; - merge_apps = 0; - aux_apps = pnd_conf_get_as_char ( g_conf, "minimenu.aux_searchpath" ); - if ( aux_apps && aux_apps [ 0 ] ) { - pnd_log ( pndn_debug, "Looking for pnd applications here: %s\n", aux_apps ); - merge_apps = pnd_disco_search ( aux_apps, NULL ); - } + // aux apps? + char *aux_apps = NULL; + merge_apps = 0; + aux_apps = pnd_conf_get_as_char ( g_conf, "minimenu.aux_searchpath" ); + if ( aux_apps && aux_apps [ 0 ] ) { + pnd_log ( pndn_debug, "Looking for pnd applications here: %s\n", aux_apps ); + merge_apps = pnd_disco_search ( aux_apps, NULL ); + } - // merge aux apps - if ( merge_apps ) { - if ( g_active_apps ) { - - // LAME: snipped from above; should just catenate the 3 sets of searchpaths into a - // master searchpath, possibly removing duplicate paths _then_, and keep all this much - // more efficient - - // the key from pnd_disco_search() is the _path_, so easy to look for duplicates - // this is pretty inefficient, being linked lists; perhaps should switch to hash tables when - // we expect thousands of apps.. or at least an index or something. - void *a = pnd_box_get_head ( merge_apps ); - void *nexta = NULL; - while ( a ) { - nexta = pnd_box_get_next ( a ); - - // if the key for the node is also found in active apps, toss out the merging one - if ( pnd_box_find_by_key ( g_active_apps, pnd_box_get_key ( a ) ) ) { - fprintf ( stderr, "Merging app id '%s' is duplicate; discarding it.\n", pnd_box_get_key ( a ) ); - pnd_box_delete_node ( merge_apps, a ); + // merge aux apps + if ( merge_apps ) { + if ( g_active_apps ) { + + // LAME: snipped from above; should just catenate the 3 sets of searchpaths into a + // master searchpath, possibly removing duplicate paths _then_, and keep all this much + // more efficient + + // the key from pnd_disco_search() is the _path_, so easy to look for duplicates + // this is pretty inefficient, being linked lists; perhaps should switch to hash tables when + // we expect thousands of apps.. or at least an index or something. + void *a = pnd_box_get_head ( merge_apps ); + void *nexta = NULL; + while ( a ) { + nexta = pnd_box_get_next ( a ); + + // if the key for the node is also found in active apps, toss out the merging one + if ( pnd_box_find_by_key ( g_active_apps, pnd_box_get_key ( a ) ) ) { + fprintf ( stderr, "Merging app id '%s' is duplicate; discarding it.\n", pnd_box_get_key ( a ) ); + pnd_box_delete_node ( merge_apps, a ); + } + + a = nexta; } - a = nexta; + pnd_box_append ( g_active_apps, merge_apps ); + } else { + g_active_apps = merge_apps; } + } - pnd_box_append ( g_active_apps, merge_apps ); - } else { - g_active_apps = merge_apps; + } // app discovery on pnd-files? + + // perform app discovery on .desktop files? + // + if ( pnd_conf_get_as_int_d ( g_conf, "minimenu.disco_dotdesktop", 0 ) ) { + char *chunks[5] = { + pnd_conf_get_as_char ( g_desktopconf, "desktop.dotdesktoppath" ), + pnd_conf_get_as_char ( g_desktopconf, "menu.dotdesktoppath" ), + //"/usr/share/applications", + NULL + }; + char ddpath [ 1024 ]; + unsigned int flags = PND_DOTDESKTOP_LIBPND_ONLY; + + if ( pnd_conf_get_as_int_d ( g_conf, "minimenu.disco_dotdesktop_all", 0 ) ) { + flags = 0; // get all } - } + + // app box? + if ( ! g_active_apps ) { + g_active_apps = pnd_box_new ( "discovery-dotdesktop" ); + } + + // for each searchpath.. + unsigned char i; + for ( i = 0; i < 5; i++ ) { + + if ( ! chunks [ i ] ) { + break; + } + + DIR *d = opendir ( chunks [ i ] ); + + if ( d ) { + struct dirent *de = readdir ( d ); + + // for each filename found + while ( de ) { + + if ( strcmp ( de -> d_name, "." ) == 0 ) { + // irrelevent + } else if ( strcmp ( de -> d_name, ".." ) == 0 ) { + // irrelevent + } else { + snprintf ( ddpath, 1024, "%s/%s", chunks [ i ], de -> d_name ); + pnd_disco_t *p = pnd_parse_dotdesktop ( ddpath, flags ); + if ( p ) { + pnd_disco_t *ai = pnd_box_allocinsert ( g_active_apps, ddpath, sizeof(pnd_disco_t) ); + memmove ( ai, p, sizeof(pnd_disco_t) ); + } + } + + // next! + de = readdir ( d ); + } + + closedir ( d ); + + } // for each dir + + } // for each searchpath + + } // app discovery in .desktops // do it g_active_appcount = pnd_box_get_size ( g_active_apps ); @@ -640,11 +712,21 @@ void applications_scan ( void ) { // cache the icon, unless deferred if ( pnd_conf_get_as_int_d ( g_conf, "minimenu.load_icons_later", 0 ) == 0 ) { - if ( iter -> pnd_icon_pos && - ! cache_icon ( iter, maxwidth, maxheight ) ) + + // if app was from a pnd and has an icon-pos (we've already found where it is in the binary), + // OR its a .desktop and we've got a path + // THEN go try to cache/load the icon + if ( ( iter -> pnd_icon_pos ) || + ( iter -> icon && iter -> object_flags & PND_DISCO_CUSTOM1 ) + ) { - pnd_log ( pndn_warning, " Couldn't load icon: '%s'\n", IFNULL(iter->title_en,"No Name") ); + + if ( ! cache_icon ( iter, maxwidth, maxheight ) ) { + pnd_log ( pndn_warning, " WARNING: Couldn't load icon: '%s'\n", IFNULL(iter->title_en,"No Name") ); + } + } + } // cache the preview --> SHOULD DEFER @@ -739,6 +821,9 @@ void applications_scan ( void ) { // let deferred icon cache go now ui_post_scan(); + // log completion + pnd_log ( pndn_debug, "Applications scan done.\n" ); + return; } diff --git a/minimenu/mmenu.conf b/minimenu/mmenu.conf index 6b212c4..64aa4c5 100644 --- a/minimenu/mmenu.conf +++ b/minimenu/mmenu.conf @@ -24,6 +24,9 @@ defer_icon_us 100000 # when background loading icons (load_icons_later), time b threaded_preview 0 # if 1, will try to load the preview in background, to avoid slowing up navigation loglevel 0 # 0 is debug, lots of crap; 3 is better, means 'errors only'. Output may screw up the wrapper! x11_present_sh /bin/pidof X # command to invoke to determine if X11 is running or not; expects a number on X is present. +disco_pnds 0 # if nonzero, will do application discovery on pnd-files +disco_dotdesktop 1 # if nonzero, will do application discovery on .desktop files +disco_dotdesktop_all 0 # if nonzero, will include non-libpnd .desktop; if 0, just libpnd (pnd-found by pndnotifyd) will be included desktop_apps 1 # search the pnd standard desktop searchpath for apps menu_apps 1 # search the pnd standard menu searchpath for apps aux_searchpath /media/*/pandora/mmenu # if something here, also search this path; can be used for mmenu-only apps? diff --git a/testdata/conf/apps b/testdata/conf/apps index f8e1c07..1602d18 100644 --- a/testdata/conf/apps +++ b/testdata/conf/apps @@ -4,7 +4,8 @@ [autodiscovery] searchpath /mnt/sd?/pandora/apps:./testdata/app? # path to depth-search for PXMLs -notifypath /media:/media/*/pandora/apps:./testdata/app?:./testdata/menuapp? +#notifypath /media:/media/*/pandora/apps:./testdata/app?:./testdata/menuapp? +notifypath /mediaZZZ:/media/*/pandora/apps:./testdata/app?:./testdata/menuapp? # PXMLs may be overridden .. ie: overrides are a subset of PXML, where the values are copied over the full PXML [overrides] diff --git a/testdata/conf/desktop b/testdata/conf/desktop index c3c8863..f241b83 100644 --- a/testdata/conf/desktop +++ b/testdata/conf/desktop @@ -3,12 +3,14 @@ # Desktop configuration [desktop] -searchpath /media/*/pandora/desktop:./testdata/app? # path to depth-search for PXMLs and pnd-files +#searchpath /media/*/pandora/desktop:./testdata/app? # path to depth-search for PXMLs and pnd-files +searchpath /mediaZZZ/*/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 /media/*/pandora/menu:./testdata/app?:./testdata/menuapps # path to depth-search for PXMLs and pnd-files +#searchpath /media/*/pandora/menu:./testdata/app?:./testdata/menuapps # path to depth-search for PXMLs and pnd-files +searchpath /mediaZZZ/*/pandora/menu:./testdata/app?:./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) diff --git a/testdata/doticons/jeff.sample.3.png b/testdata/doticons/jeff.sample.3.png deleted file mode 100644 index 140a39386bfe401a5c2d8406bfd0caf568318f08..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 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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 -- 2.39.2