diff --git a/data/Makefile.am b/data/Makefile.am
index cbc7b796d..4803081e8 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -17,6 +17,9 @@ dist_searchproviders_DATA = \
open-search-providers/google.xml \
open-search-providers/wikipedia.xml
+introspectiondir = $(datadir)/dbus-1/interfaces
+introspection_DATA = org.gnome.ShellSearchProvider.xml
+
themedir = $(pkgdatadir)/theme
dist_theme_DATA = \
theme/calendar-arrow-left.svg \
@@ -73,6 +76,7 @@ shaders_DATA = \
EXTRA_DIST = \
gnome-shell.desktop.in.in \
gnome-shell-extension-prefs.desktop.in.in \
+ $(introspection_DATA) \
$(menu_DATA) \
$(shaders_DATA) \
$(convert_DATA) \
diff --git a/data/org.gnome.ShellSearchProvider.xml b/data/org.gnome.ShellSearchProvider.xml
new file mode 100644
index 000000000..16fa9ad0a
--- /dev/null
+++ b/data/org.gnome.ShellSearchProvider.xml
@@ -0,0 +1,147 @@
+
+
+
+
+
+
+ The interface used for integrating into GNOME Shell's search
+ interface.
+
+
+
+
+
+
+
+
+ Called when the user first begins a search.
+
+
+
+
+
+
+
+ Array of search terms, which the provider should treat as
+ logical AND.
+
+
+
+
+
+
+
+
+ An array of result identifier strings representing items which
+ match the given search terms. Identifiers must be unique within
+ the provider's domain, but other than that may be chosen freely
+ by the provider.
+
+
+
+
+
+
+
+
+
+
+ Called when a search is performed which is a "subsearch" of
+ the previous search, e.g. the method may return less results, but
+ not more or different results.
+
+ This allows search providers to only search through the previous
+ result set, rather than possibly performing a full re-query.
+
+
+
+
+
+
+
+ Array of item identifiers
+
+
+
+
+
+
+
+
+ Array of updated search terms, which the provider should treat as
+ logical AND.
+
+
+
+
+
+
+
+
+ An array of result identifier strings representing items which
+ match the given search terms. Identifiers must be unique within
+ the provider's domain, but other than that may be chosen freely
+ by the provider.
+
+
+
+
+
+
+
+
+
+
+ Return an array of meta data used to display each given result
+
+
+
+
+
+
+
+ An array of result identifiers as returned by
+ GetInitialResultSet() or GetSubsearchResultSet()
+
+
+
+
+
+
+
+
+ A dictionary describing the given search result, containing
+ 'id', 'name' (both strings) and either 'icon' (a serialized
+ GIcon) or 'icon-data' (raw image data as (iiibiiay) - width,
+ height, rowstride, has-alpha, bits per sample, channels, data)
+
+
+
+
+
+
+
+
+
+
+ Called when the users chooses a given result. The result should
+ be displayed in the application associated with the corresponding
+ provider.
+
+
+
+
+
+
+
+ A result identifier as returned by GetInitialResultSet() or
+ GetSubsearchResultSet()
+
+
+
+
+
+
+
diff --git a/js/Makefile.am b/js/Makefile.am
index 98f7a4858..67806178e 100644
--- a/js/Makefile.am
+++ b/js/Makefile.am
@@ -75,6 +75,7 @@ nobase_dist_js_DATA = \
ui/placeDisplay.js \
ui/polkitAuthenticationAgent.js \
ui/popupMenu.js \
+ ui/remoteSearch.js \
ui/runDialog.js \
ui/scripting.js \
ui/search.js \
diff --git a/js/ui/remoteSearch.js b/js/ui/remoteSearch.js
new file mode 100644
index 000000000..18e57497a
--- /dev/null
+++ b/js/ui/remoteSearch.js
@@ -0,0 +1,131 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const Gio = imports.gi.Gio;
+const Lang = imports.lang;
+const St = imports.gi.St;
+
+const Search = imports.ui.search;
+
+const SearchProviderIface =
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+;
+
+var SearchProviderProxy = Gio.DBusProxy.makeProxyWrapper(SearchProviderIface);
+
+
+const RemoteSearchProvider = new Lang.Class({
+ Name: 'RemoteSearchProvider',
+ Extends: Search.SearchProvider,
+
+ _init: function(title, icon, dbusName, dbusPath) {
+ this._proxy = new SearchProviderProxy(Gio.DBus.session,
+ dbusName, dbusPath);
+
+ this.parent(title.toUpperCase());
+ this.async = true;
+ this._cancellable = new Gio.Cancellable();
+ },
+
+ createIcon: function(size, meta) {
+ if (meta['gicon']) {
+ return new St.Icon({ gicon: Gio.icon_new_for_string(meta['gicon']),
+ icon_size: size,
+ icon_type: St.IconType.FULLCOLOR });
+ } else if (meta['icon-data']) {
+ let [width, height, rowStride, hasAlpha,
+ bitsPerSample, nChannels, data] = meta['icon-data'];
+ let textureCache = St.TextureCache.get_default();
+ return textureCache.load_from_raw(data, hasAlpha,
+ width, height, rowStride, size);
+ }
+
+ // Ugh, but we want to fall back to something ...
+ return new St.Icon({ icon_name: 'text-x-generic',
+ icon_size: size,
+ icon_type: St.IconType.FULLCOLOR });
+ },
+
+ _getResultsFinished: function(results, error) {
+ if (error)
+ return;
+ this.searchSystem.pushResults(this, results[0]);
+ },
+
+ getInitialResultSetAsync: function(terms) {
+ this._cancellable.cancel();
+ this._cancellable.reset();
+ try {
+ this._proxy.GetInitialResultSetRemote(terms,
+ Lang.bind(this, this._getResultsFinished),
+ this._cancellable);
+ } catch(e) {
+ log('Error calling GetInitialResultSet for provider %s: %s'.format( this.title, e.toString()));
+ this.searchSystem.pushResults(this, []);
+ }
+ },
+
+ getSubsearchResultSetAsync: function(previousResults, newTerms) {
+ this._cancellable.cancel();
+ this._cancellable.reset();
+ try {
+ this._proxy.GetSubsearchResultSetRemote(previousResults, newTerms,
+ Lang.bind(this, this._getResultsFinished),
+ this._cancellable);
+ } catch(e) {
+ log('Error calling GetSubsearchResultSet for provider %s: %s'.format(this.title, e.toString()));
+ this.searchSystem.pushResults(this, []);
+ }
+ },
+
+ _getResultMetasFinished: function(results, error, callback) {
+ if (error) {
+ callback([]);
+ return;
+ }
+ let metas = results[0];
+ let resultMetas = [];
+ for (let i = 0; i < metas.length; i++) {
+ for (let prop in metas[i])
+ metas[i][prop] = metas[i][prop].deep_unpack();
+ resultMetas.push({ id: metas[i]['id'],
+ name: metas[i]['name'],
+ createIcon: Lang.bind(this,
+ this.createIcon, metas[i]) });
+ }
+ callback(resultMetas);
+ },
+
+ getResultMetasAsync: function(ids, callback) {
+ this._cancellable.cancel();
+ this._cancellable.reset();
+ try {
+ this._proxy.GetResultMetasRemote(ids,
+ Lang.bind(this, this._getResultMetasFinished, callback),
+ this._cancellable);
+ } catch(e) {
+ log('Error calling GetResultMetas for provider %s: %s'.format(this.title, e.toString()));
+ callback([]);
+ }
+ },
+
+ activateResult: function(id) {
+ this._proxy.ActivateResultRemote(id);
+ }
+});
+
+