(sorry if you get this email twice - the first time I sent I attached a .js file so the mail got rejected, so I've changed the file extension to .txt)
On 25 August 2012 01:24, Florian Müllner <fmuell...@gnome.org> wrote: > On Fri, Aug 24, 2012 at 2:31 PM, Amy C <mathematical.cof...@gmail.com> wrote: >> So, how can I get a list of installed applications from a process >> outside of the gnome-shell one (no access to Shell, etc)? > > Use the GnomeMenu-3.0 API - ShellAppSystem is a rather thin wrapper > around that nowadays. Hi all, Thanks for pointing me to GnomeMenu and shell-app-system.c. Based on these I managed to get a list of installed applications. I attach a code sample I wrote to do so in case anyone wishes to use it. (I changed the filename to .txt because the email got rejected the first time - can't send .js as an attachment). It is very basic, feel free to tweak as you need - it is basically a very simple version of shell-app-system.c (with none of the search features, just the ability to list all the applications). The code snippet shows how to instantiate an object { app_desktop_id: GMenuTree.Entry } and keep that in sync with the apps list. The file can be run via ` ./gjs appsystem.js` and will print a list of installed apps to your terminal. (if you don't want it to run just comment out the `main();` at the very buttom of the file). ***USUAL DISCLAIMER about not running a script file blindly; view the code before running/don't run at all if you're concerned etc*** I am unsure of the etiquette concerning sending scripts around a mailing list so I'll outline the main points here in case someone in the future visits the archive and wants to know: // import GMenu const GMenu = imports.gi.GMenu; // make a GMenu.Tree containing the applications menu tree structure (Applictions -> {Accessories, Games, Graphics, ...} -> individual application) let appsTree = new GMenu.Tree({ menu_basename: 'applications.menu', flags: GMenu.TreeFlags.INCLUDE_NODISPLAY }); // you must do this on the tree before anything else, returns a boolean whether it // succeeded or not appsTree.load_sync(); // if you want to connect to changes in this (user installs/removes a program) appsTree.connect('changed', callback); Then to get the actual apps as a flat list, you create a TreeIter on appsTree and iterate through each item in it. If it is an entry, it is an app - add it to the list. If it is a directory, recurse into it until you extract the entries. This is the getNodes function in the attached file. let dir = appsTree.get_root_directory(); let iter = dir.iter(); // now just call iter.next() until it returns GMenu.TreeItemType.INVALID. // use iter.get_entry() to get the entry or iter.get_directory() to get the directory. // iter.next() returns the type of the current entry (ENTRY, DIRECTORY, ...) Hope that helps someone in the future, and thanks for the pointers everyone! (Florian, Yaa101, Milan Bouchet-Valat).
const GMenu = imports.gi.GMenu; /******* * Code * Example use. Getting a list of applications. * The GMenu.Tree is 'applications.menu', and the top-level children of it * are the application subclasses (like Accessories, Games, ...) * The children of these are applications. * * This piece of code simply loads the apps menu into a GMenu.Tree object * `appsTree`, and also populates an object `appsList` where the keys are the * application's desktop id (e.g. gnome-terminal.desktop) and the entries are * the GMenu.TreeEntry corresponding to the application. * * The code keeps `appsList` in sync with `appsTree` by monitoring changes * in `appsTree` and mirroring them to `appsList`. * * NOTE: shell-app-system.c has much more - for example, it deals with vendor * prefixes on desktop ids (e.g. gnome-xxx.desktop, fedora-yyy.desktop) and lets * you search with these prefixes removed. *******/ let appsTree = null, appsList = {}; function main() { // create a GMenu.Tree holding the applications menu. // (If you want the GNOME controle centre, use 'gnomecc.menu': see // /etc/xdg/menus) // This copies off shell-app-system.c shell_app_system_init appsTree = new GMenu.Tree({ menu_basename: 'applications.menu', flags: GMenu.TreeFlags.INCLUDE_NODISPLAY }); // monitor changes in the tree so we can update appsList (i.e. when this // gets called the installed apps have changed) appsTree.connect('changed', onTreeChanged); // update appsList onTreeChanged(appsTree); // now, appsList should be always up to date with the current application // list. for (let appid in appsList) { if (appsList.hasOwnProperty(appid)) { print(appid); } } } // callback: for example here we update our appsList dictionary to remain in // sync with the appsTree. function onTreeChanged(tree) { // first make sure that tree is appsTree (it should be, that's all we // connected this function to...) if (tree !== appsTree) { throw new Error("`tree` is not the apps tree!"); } // initialise appsTree if (!appsTree.load_sync()) { throw new Error("Failed to load apps..."); } // new apps tree let newApps = getTreeNodes(tree); // look for apps in appsList that are not in newApps and remove them for (let id in appsList) { if (!appsList.hasOwnProperty(id)) { continue; } if (!newApps[id]) { delete appsList[id]; } } // add/update appsList from newApps for (let id in newApps) { if (!newApps.hasOwnProperty(id)) { continue; } let entry = newApps[id]; // prefix = getPrefixForEntry(entry); // TODO <-- ?? if (appsList[id] !== entry) { // add the entry to appsList appsList[id] = entry; } } } /*************** * Helper functions. * * Note: * GMenu.Tree --> load_sync() to init, get_root_directory().iter() to iterate. * | Contains: * |- GMenu.TreeDirectory --> use .iter() to iterate through * | |- .iter() to iterate through * | |- .get_menu_id() * | |- .get_name() * | |- and so on... * |- GMenu.TreeEntry * | |- .get_app_info() * | |- .get_desktop_file_id() * | |- and so on... ***************/ /* Get all nodes of a directory * (shell-app-system.c: get_flattened_entires_recurse) * @dir: a GMenu.TreeDirectory (example, .get_root_directory() of a GMenu.Tree) * * returns: an object with key `id` and value GMenu.TreeEntry, where the id * is the entry's .get_desktop_file_id(). */ function getNodes(dir) { let entry, nodeType, entries, returnDict = {}, // dictionary. id: entry. iter = dir.iter(); // loop through the directory recursing down directories until we reach // items. while ((nodeType = iter.next()) !== GMenu.TreeItemType.INVALID) { switch (nodeType) { case GMenu.TreeItemType.ENTRY: // add it to the return object, overwriting duplicates (if any) entry = iter.get_entry(); print('adding entry: ' + entry.get_desktop_file_id()); returnDict[entry.get_desktop_file_id()] = entry; break; case GMenu.TreeItemType.DIRECTORY: // recurse into the directory, adding these results to our own // return object (any duplicates are overwritten): entries = getNodes(iter.get_directory()); for (entry in entries) { if (entries.hasOwnProperty(entry)) { returnDict[entry] = entries[entry]; } } break; default: // SEPARATOR, HEADER, ALIAS. skip for now. break; } } return returnDict; } /* get all nodes from a tree */ function getTreeNodes(tree) { return getNodes(tree.get_root_directory()); } /*****************************************************/ // run code sample main();
_______________________________________________ gnome-shell-list mailing list gnome-shell-list@gnome.org https://mail.gnome.org/mailman/listinfo/gnome-shell-list