summaryrefslogtreecommitdiffstats
path: root/search-provider
diff options
context:
space:
mode:
Diffstat (limited to 'search-provider')
-rw-r--r--search-provider/cc-search-provider.c377
-rw-r--r--search-provider/cc-search-provider.h43
-rw-r--r--search-provider/control-center-search-provider.c151
-rw-r--r--search-provider/control-center-search-provider.h52
-rw-r--r--search-provider/meson.build47
-rw-r--r--search-provider/org.gnome.Settings.SearchProvider.service.in3
-rw-r--r--search-provider/org.gnome.Settings.search-provider.ini5
-rw-r--r--search-provider/org.gnome.ShellSearchProvider2.xml87
8 files changed, 765 insertions, 0 deletions
diff --git a/search-provider/cc-search-provider.c b/search-provider/cc-search-provider.c
new file mode 100644
index 0000000..8a8981a
--- /dev/null
+++ b/search-provider/cc-search-provider.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright (c) 2012 Giovanni Campagna <scampa.giovanni@gmail.com>
+ *
+ * The Control Center is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * The Control Center is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with the Control Center; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <config.h>
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <gio/gdesktopappinfo.h>
+#include <gtk/gtk.h>
+#include <string.h>
+
+#include <shell/cc-panel.h>
+#include <shell/cc-shell-model.h>
+#include <shell/cc-panel-loader.h>
+
+#include "cc-util.h"
+
+#include "control-center-search-provider.h"
+#include "cc-search-provider.h"
+
+struct _CcSearchProvider
+{
+ GObject parent;
+
+ CcShellSearchProvider2 *skeleton;
+
+ GHashTable *iter_table; /* COL_ID -> GtkTreeIter */
+};
+
+typedef enum {
+ MATCH_NONE,
+ MATCH_PREFIX,
+ MATCH_SUBSTRING
+} PanelSearchMatch;
+
+G_DEFINE_TYPE (CcSearchProvider, cc_search_provider, G_TYPE_OBJECT)
+
+static char **
+get_casefolded_terms (char **terms)
+{
+ char **casefolded_terms;
+ int i, n;
+
+ n = g_strv_length ((char**) terms);
+ casefolded_terms = g_new (char*, n + 1);
+
+ for (i = 0; i < n; i++)
+ casefolded_terms[i] = cc_util_normalize_casefold_and_unaccent (terms[i]);
+ casefolded_terms[n] = NULL;
+
+ return casefolded_terms;
+}
+
+static gboolean
+matches_all_terms (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ char **terms)
+{
+ int i;
+
+ for (i = 0; terms[i]; i++)
+ {
+ if (!cc_shell_model_iter_matches_search (CC_SHELL_MODEL (model),
+ iter,
+ terms[i]))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static GtkTreeModel *
+get_model (void)
+{
+ CcSearchProviderApp *app;
+
+ app = cc_search_provider_app_get ();
+ return GTK_TREE_MODEL (cc_search_provider_app_get_model (app));
+}
+
+static gchar **
+get_results (gchar **terms)
+{
+ g_auto(GStrv) casefolded_terms = NULL;
+ GtkTreeModel *model = get_model ();
+ GtkTreeIter iter;
+ GPtrArray *results;
+ gboolean ok;
+
+ casefolded_terms = get_casefolded_terms (terms);
+ results = g_ptr_array_new ();
+
+ cc_shell_model_set_sort_terms (CC_SHELL_MODEL (model), casefolded_terms);
+
+ ok = gtk_tree_model_get_iter_first (model, &iter);
+ while (ok)
+ {
+ if (matches_all_terms (model, &iter, casefolded_terms))
+ {
+ gchar *id;
+ gtk_tree_model_get (model, &iter, COL_ID, &id, -1);
+ g_ptr_array_add (results, id);
+ }
+
+ ok = gtk_tree_model_iter_next (model, &iter);
+ }
+
+ g_ptr_array_add (results, NULL);
+
+ return (char**) g_ptr_array_free (results, FALSE);
+}
+
+static gboolean
+handle_get_initial_result_set (CcShellSearchProvider2 *skeleton,
+ GDBusMethodInvocation *invocation,
+ char **terms,
+ CcSearchProvider *self)
+{
+ g_auto(GStrv) results = get_results (terms);
+ cc_shell_search_provider2_complete_get_initial_result_set (skeleton,
+ invocation,
+ (const char* const*) results);
+ return TRUE;
+}
+
+static gboolean
+handle_get_subsearch_result_set (CcShellSearchProvider2 *skeleton,
+ GDBusMethodInvocation *invocation,
+ char **previous_results,
+ char **terms,
+ CcSearchProvider *self)
+{
+ /* We ignore the previous results here since the model re-sorts for
+ * the new terms. This means that we're not really doing a subsearch
+ * but, on the other hand, the results are consistent with the
+ * control center's own search. In any case, the number of elements
+ * in the model is always small enough that we don't need to worry
+ * about this taking too long.
+ */
+ g_auto(GStrv) results = get_results (terms);
+ cc_shell_search_provider2_complete_get_subsearch_result_set (skeleton,
+ invocation,
+ (const char* const*) results);
+ return TRUE;
+}
+
+static GtkTreeIter *
+get_iter_for_result (CcSearchProvider *self,
+ const gchar *result)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gboolean ok;
+ gchar *id;
+
+ /* Caching GtkTreeIters in this way is only OK because the model is
+ * a GtkListStore which guarantees that while a row exists, the iter
+ * is persistent.
+ */
+ if (self->iter_table)
+ goto lookup;
+
+ self->iter_table = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, (GDestroyNotify) gtk_tree_iter_free);
+
+ model = get_model ();
+ ok = gtk_tree_model_get_iter_first (model, &iter);
+ while (ok)
+ {
+ gtk_tree_model_get (model, &iter, COL_ID, &id, -1);
+
+ g_hash_table_replace (self->iter_table, id, gtk_tree_iter_copy (&iter));
+
+ ok = gtk_tree_model_iter_next (model, &iter);
+ }
+
+ lookup:
+ return g_hash_table_lookup (self->iter_table, result);
+}
+
+static gboolean
+handle_get_result_metas (CcShellSearchProvider2 *skeleton,
+ GDBusMethodInvocation *invocation,
+ char **results,
+ CcSearchProvider *self)
+{
+ GtkTreeModel *model = get_model ();
+ GtkTreeIter *iter;
+ int i;
+ GVariantBuilder builder;
+ const char *id;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}"));
+
+ for (i = 0; results[i]; i++)
+ {
+ g_autofree gchar *description = NULL;
+ g_autofree gchar *name = NULL;
+ g_autoptr(GAppInfo) app = NULL;
+ g_autoptr(GIcon) icon = NULL;
+
+ iter = get_iter_for_result (self, results[i]);
+ if (!iter)
+ continue;
+
+ gtk_tree_model_get (model, iter,
+ COL_APP, &app,
+ COL_NAME, &name,
+ COL_GICON, &icon,
+ COL_DESCRIPTION, &description,
+ -1);
+ id = g_app_info_get_id (app);
+
+ g_variant_builder_open (&builder, G_VARIANT_TYPE ("a{sv}"));
+ g_variant_builder_add (&builder, "{sv}",
+ "id", g_variant_new_string (id));
+ g_variant_builder_add (&builder, "{sv}",
+ "name", g_variant_new_string (name));
+ g_variant_builder_add (&builder, "{sv}",
+ "icon", g_icon_serialize (icon));
+ g_variant_builder_add (&builder, "{sv}",
+ "description", g_variant_new_string (description));
+ g_variant_builder_close (&builder);
+ }
+
+ cc_shell_search_provider2_complete_get_result_metas (skeleton,
+ invocation,
+ g_variant_builder_end (&builder));
+ return TRUE;
+}
+
+static gboolean
+handle_activate_result (CcShellSearchProvider2 *skeleton,
+ GDBusMethodInvocation *invocation,
+ char *identifier,
+ char **results,
+ guint timestamp,
+ CcSearchProvider *self)
+{
+ GdkAppLaunchContext *launch_context;
+ g_autoptr(GError) error = NULL;
+ GAppInfo *app;
+
+ launch_context = gdk_display_get_app_launch_context (gdk_display_get_default ());
+ gdk_app_launch_context_set_timestamp (launch_context, timestamp);
+
+ app = G_APP_INFO (g_desktop_app_info_new (identifier));
+
+ if (!g_app_info_launch (app, NULL, G_APP_LAUNCH_CONTEXT (launch_context), &error))
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ else
+ cc_shell_search_provider2_complete_activate_result (skeleton, invocation);
+
+ return TRUE;
+}
+
+static gboolean
+handle_launch_search (CcShellSearchProvider2 *skeleton,
+ GDBusMethodInvocation *invocation,
+ char **terms,
+ guint timestamp,
+ CcSearchProvider *self)
+{
+ GdkAppLaunchContext *launch_context;
+ g_autoptr(GError) error = NULL;
+ char *joined_terms, *command_line;
+ GAppInfo *app;
+
+ launch_context = gdk_display_get_app_launch_context (gdk_display_get_default ());
+ gdk_app_launch_context_set_timestamp (launch_context, timestamp);
+
+ joined_terms = g_strjoinv (" ", terms);
+ command_line = g_strdup_printf ("gnome-control-center -s '%s'", joined_terms);
+ app = g_app_info_create_from_commandline (command_line,
+ "gnome-control-center.desktop",
+ G_APP_INFO_CREATE_SUPPORTS_STARTUP_NOTIFICATION,
+ &error);
+ if (!app)
+ {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ return TRUE;
+ }
+
+ if (!g_app_info_launch (app, NULL, G_APP_LAUNCH_CONTEXT (launch_context), &error))
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ else
+ cc_shell_search_provider2_complete_launch_search (skeleton, invocation);
+
+ return TRUE;
+}
+
+static void
+cc_search_provider_init (CcSearchProvider *self)
+{
+ self->skeleton = cc_shell_search_provider2_skeleton_new ();
+
+ g_signal_connect (self->skeleton, "handle-get-initial-result-set",
+ G_CALLBACK (handle_get_initial_result_set), self);
+ g_signal_connect (self->skeleton, "handle-get-subsearch-result-set",
+ G_CALLBACK (handle_get_subsearch_result_set), self);
+ g_signal_connect (self->skeleton, "handle-get-result-metas",
+ G_CALLBACK (handle_get_result_metas), self);
+ g_signal_connect (self->skeleton, "handle-activate-result",
+ G_CALLBACK (handle_activate_result), self);
+ g_signal_connect (self->skeleton, "handle-launch-search",
+ G_CALLBACK (handle_launch_search), self);
+}
+
+gboolean
+cc_search_provider_dbus_register (CcSearchProvider *self,
+ GDBusConnection *connection,
+ const gchar *object_path,
+ GError **error)
+{
+ GDBusInterfaceSkeleton *skeleton;
+
+ skeleton = G_DBUS_INTERFACE_SKELETON (self->skeleton);
+
+ return g_dbus_interface_skeleton_export (skeleton, connection, object_path, error);
+}
+
+void
+cc_search_provider_dbus_unregister (CcSearchProvider *self,
+ GDBusConnection *connection,
+ const gchar *object_path)
+{
+ GDBusInterfaceSkeleton *skeleton;
+
+ skeleton = G_DBUS_INTERFACE_SKELETON (self->skeleton);
+
+ if (g_dbus_interface_skeleton_has_connection (skeleton, connection))
+ g_dbus_interface_skeleton_unexport_from_connection (skeleton, connection);
+}
+
+static void
+cc_search_provider_dispose (GObject *object)
+{
+ CcSearchProvider *self;
+
+ self = CC_SEARCH_PROVIDER (object);
+
+ g_clear_object (&self->skeleton);
+ g_clear_pointer (&self->iter_table, g_hash_table_destroy);
+
+ G_OBJECT_CLASS (cc_search_provider_parent_class)->dispose (object);
+}
+
+static void
+cc_search_provider_class_init (CcSearchProviderClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = cc_search_provider_dispose;
+}
+
+CcSearchProvider *
+cc_search_provider_new (void)
+{
+ return g_object_new (CC_TYPE_SEARCH_PROVIDER, NULL);
+}
+
diff --git a/search-provider/cc-search-provider.h b/search-provider/cc-search-provider.h
new file mode 100644
index 0000000..0eb05b9
--- /dev/null
+++ b/search-provider/cc-search-provider.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2012 Giovanni Campagna <scampa.giovanni@gmail.com>
+ *
+ * The Control Center is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * The Control Center is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with the Control Center; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#pragma once
+
+#include <glib-object.h>
+#include <gio/gio.h>
+#include "cc-shell-search-provider-generated.h"
+#include <shell/cc-shell-model.h>
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_SEARCH_PROVIDER (cc_search_provider_get_type())
+
+G_DECLARE_FINAL_TYPE (CcSearchProvider, cc_search_provider, CC, SEARCH_PROVIDER, GObject)
+
+CcSearchProvider *cc_search_provider_new (void);
+
+gboolean cc_search_provider_dbus_register (CcSearchProvider *provider,
+ GDBusConnection *connection,
+ const char *object_path,
+ GError **error);
+void cc_search_provider_dbus_unregister (CcSearchProvider *provider,
+ GDBusConnection *connection,
+ const char *object_path);
+
+G_END_DECLS
diff --git a/search-provider/control-center-search-provider.c b/search-provider/control-center-search-provider.c
new file mode 100644
index 0000000..c49cd9a
--- /dev/null
+++ b/search-provider/control-center-search-provider.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2012 Giovanni Campagna <scampa.giovanni@gmail.com>
+ *
+ * The Control Center is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * The Control Center is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with the Control Center; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <stdlib.h>
+
+#include <gio/gio.h>
+
+#include <shell/cc-panel-loader.h>
+#include <shell/cc-shell-model.h>
+#include "cc-search-provider.h"
+#include "control-center-search-provider.h"
+
+G_DEFINE_TYPE (CcSearchProviderApp, cc_search_provider_app, GTK_TYPE_APPLICATION);
+
+#define INACTIVITY_TIMEOUT 60 * 1000 /* One minute, in milliseconds */
+
+static gboolean
+cc_search_provider_app_dbus_register (GApplication *application,
+ GDBusConnection *connection,
+ const gchar *object_path,
+ GError **error)
+{
+ CcSearchProviderApp *self;
+
+ if (!G_APPLICATION_CLASS (cc_search_provider_app_parent_class)->dbus_register (application,
+ connection,
+ object_path,
+ error))
+ return FALSE;
+
+ self = CC_SEARCH_PROVIDER_APP (application);
+
+ return cc_search_provider_dbus_register (self->search_provider, connection,
+ object_path, error);
+}
+
+static void
+cc_search_provider_app_dbus_unregister (GApplication *application,
+ GDBusConnection *connection,
+ const gchar *object_path)
+{
+ CcSearchProviderApp *self;
+
+ self = CC_SEARCH_PROVIDER_APP (application);
+ if (self->search_provider)
+ cc_search_provider_dbus_unregister (self->search_provider, connection, object_path);
+
+ G_APPLICATION_CLASS (cc_search_provider_app_parent_class)->dbus_unregister (application,
+ connection,
+ object_path);
+}
+
+static void
+cc_search_provider_app_dispose (GObject *object)
+{
+ CcSearchProviderApp *self;
+
+ self = CC_SEARCH_PROVIDER_APP (object);
+
+ g_clear_object (&self->model);
+ g_clear_object (&self->search_provider);
+
+ G_OBJECT_CLASS (cc_search_provider_app_parent_class)->dispose (object);
+}
+
+static void
+cc_search_provider_app_init (CcSearchProviderApp *self)
+{
+ self->search_provider = cc_search_provider_new ();
+ g_application_set_inactivity_timeout (G_APPLICATION (self),
+ INACTIVITY_TIMEOUT);
+
+ /* HACK: get the inactivity timeout started */
+ g_application_hold (G_APPLICATION (self));
+ g_application_release (G_APPLICATION (self));
+}
+
+static void
+cc_search_provider_app_startup (GApplication *application)
+{
+ CcSearchProviderApp *self;
+
+ self = CC_SEARCH_PROVIDER_APP (application);
+
+ G_APPLICATION_CLASS (cc_search_provider_app_parent_class)->startup (application);
+
+ self->model = cc_shell_model_new ();
+ cc_panel_loader_fill_model (self->model);
+}
+
+static void
+cc_search_provider_app_class_init (CcSearchProviderAppClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GApplicationClass *app_class = G_APPLICATION_CLASS (klass);
+
+ object_class->dispose = cc_search_provider_app_dispose;
+
+ app_class->dbus_register = cc_search_provider_app_dbus_register;
+ app_class->dbus_unregister = cc_search_provider_app_dbus_unregister;
+ app_class->startup = cc_search_provider_app_startup;
+}
+
+CcShellModel *
+cc_search_provider_app_get_model (CcSearchProviderApp *application)
+{
+ return application->model;
+}
+
+CcSearchProviderApp *
+cc_search_provider_app_get ()
+{
+ static CcSearchProviderApp *singleton;
+
+ if (singleton)
+ return singleton;
+
+ singleton = g_object_new (CC_TYPE_SEARCH_PROVIDER_APP,
+ "application-id", "org.gnome.Settings.SearchProvider",
+ "flags", G_APPLICATION_IS_SERVICE,
+ NULL);
+
+ return singleton;
+}
+
+int main (int argc, char **argv)
+{
+ GApplication *app;
+
+ app = G_APPLICATION (cc_search_provider_app_get ());
+ return g_application_run (app, argc, argv);
+}
diff --git a/search-provider/control-center-search-provider.h b/search-provider/control-center-search-provider.h
new file mode 100644
index 0000000..3dc058f
--- /dev/null
+++ b/search-provider/control-center-search-provider.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2012 Giovanni Campagna <scampa.giovanni@gmail.com>
+ *
+ * The Control Center is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * The Control Center is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with the Control Center; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+#include <shell/cc-shell-model.h>
+#include "cc-search-provider.h"
+
+G_BEGIN_DECLS
+
+typedef struct {
+ GtkApplication parent;
+
+ CcShellModel *model;
+ CcSearchProvider *search_provider;
+} CcSearchProviderApp;
+
+typedef struct {
+ GtkApplicationClass parent_class;
+} CcSearchProviderAppClass;
+
+#define CC_TYPE_SEARCH_PROVIDER_APP cc_search_provider_app_get_type ()
+
+#define CC_SEARCH_PROVIDER_APP(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ CC_TYPE_SEARCH_PROVIDER_APP, CcSearchProviderApp))
+
+GType cc_search_provider_app_get_type (void) G_GNUC_CONST;
+
+CcSearchProviderApp *cc_search_provider_app_get (void);
+
+CcShellModel *cc_search_provider_app_get_model (CcSearchProviderApp *application);
+
+G_END_DECLS
diff --git a/search-provider/meson.build b/search-provider/meson.build
new file mode 100644
index 0000000..624641a
--- /dev/null
+++ b/search-provider/meson.build
@@ -0,0 +1,47 @@
+service_conf = configuration_data()
+service_conf.set('libexecdir', control_center_libexecdir)
+
+service = 'org.gnome.Settings.SearchProvider.service'
+
+configure_file(
+ input : service + '.in',
+ output : service,
+ install : true,
+ install_dir : join_paths(control_center_datadir, 'dbus-1', 'services'),
+ configuration : service_conf
+)
+
+install_data(
+ 'org.gnome.Settings.search-provider.ini',
+ install_dir: join_paths(control_center_datadir, 'gnome-shell', 'search-providers')
+)
+
+sources = files(
+ 'cc-search-provider.c',
+ 'control-center-search-provider.c'
+)
+
+# The upstream for the DBus interface definition is
+# at http://git.gnome.org/browse/gnome-shell/plain/data/org.gnome.ShellSearchProvider2.xml
+sources += gnome.gdbus_codegen(
+ 'cc-shell-search-provider-generated',
+ 'org.gnome.ShellSearchProvider2.xml',
+ interface_prefix : 'org.gnome.',
+ namespace : 'Cc'
+)
+
+libs = [
+ libpanel_loader,
+ libshell
+]
+
+executable(
+ 'gnome-control-center-search-provider',
+ sources,
+ include_directories : top_inc,
+ dependencies : shell_deps,
+ c_args : cflags,
+ link_with : libs,
+ install : true,
+ install_dir : control_center_libexecdir
+)
diff --git a/search-provider/org.gnome.Settings.SearchProvider.service.in b/search-provider/org.gnome.Settings.SearchProvider.service.in
new file mode 100644
index 0000000..7cc98a3
--- /dev/null
+++ b/search-provider/org.gnome.Settings.SearchProvider.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.gnome.Settings.SearchProvider
+Exec=@libexecdir@/gnome-control-center-search-provider
diff --git a/search-provider/org.gnome.Settings.search-provider.ini b/search-provider/org.gnome.Settings.search-provider.ini
new file mode 100644
index 0000000..89dd949
--- /dev/null
+++ b/search-provider/org.gnome.Settings.search-provider.ini
@@ -0,0 +1,5 @@
+[Shell Search Provider]
+DesktopId=org.gnome.Settings.desktop
+BusName=org.gnome.Settings.SearchProvider
+ObjectPath=/org/gnome/Settings/SearchProvider
+Version=2
diff --git a/search-provider/org.gnome.ShellSearchProvider2.xml b/search-provider/org.gnome.ShellSearchProvider2.xml
new file mode 100644
index 0000000..26b213d
--- /dev/null
+++ b/search-provider/org.gnome.ShellSearchProvider2.xml
@@ -0,0 +1,87 @@
+<!DOCTYPE node PUBLIC
+'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
+'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
+<node>
+
+ <!--
+ org.gnome.Shell.SearchProvider2:
+ @short_description: Search provider interface
+
+ The interface used for integrating into GNOME Shell's search
+ interface (version 2).
+ -->
+ <interface name="org.gnome.Shell.SearchProvider2">
+
+ <!--
+ GetInitialResultSet:
+ @terms: Array of search terms, which the provider should treat as logical AND.
+ @results: 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 the user first begins a search.
+ -->
+ <method name="GetInitialResultSet">
+ <arg type="as" name="terms" direction="in" />
+ <arg type="as" name="results" direction="out" />
+ </method>
+
+ <!--
+ GetSubsearchResultSet:
+ @previous_results: Array of results previously returned by GetInitialResultSet().
+ @terms: Array of updated search terms, which the provider should treat as logical AND.
+ @results: 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.
+ -->
+ <method name="GetSubsearchResultSet">
+ <arg type="as" name="previous_results" direction="in" />
+ <arg type="as" name="terms" direction="in" />
+ <arg type="as" name="results" direction="out" />
+ </method>
+
+ <!--
+ GetResultMetas:
+ @identifiers: An array of result identifiers as returned by GetInitialResultSet() or GetSubsearchResultSet()
+ @metas: A dictionary describing the given search result, containing 'id' and 'name' (both strings). Optionally, either 'gicon' (a serialized GIcon) or 'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, data) can be specified if the result can be better served with a thumbnail of the content (such as with images). A 'description' field (string) may also be specified if more context would help the user find the desired result.
+
+ Return an array of meta data used to display each given result
+ -->
+ <method name="GetResultMetas">
+ <arg type="as" name="identifiers" direction="in" />
+ <arg type="aa{sv}" name="metas" direction="out" />
+ </method>
+
+ <!--
+ ActivateResult:
+ @identifier: A result identifier as returned by GetInitialResultSet() or GetSubsearchResultSet()
+ @terms: Array of search terms, which the provider should treat as logical AND.
+ @timestamp: A timestamp of the user interaction that triggered this call
+
+ Called when the users chooses a given result. The result should
+ be displayed in the application associated with the corresponding
+ provider. The provided search terms can be used to allow launching a full search in
+ the application.
+ -->
+ <method name="ActivateResult">
+ <arg type="s" name="identifier" direction="in" />
+ <arg type="as" name="terms" direction="in" />
+ <arg type="u" name="timestamp" direction="in" />
+ </method>
+
+ <!--
+ LaunchSearch:
+ @terms: Array of search terms, which the provider should treat as logical AND.
+ @timestamp: A timestamp of the user interaction that triggered this call
+
+ Asks the search provider to launch a full search in the application for the provided terms.
+ -->
+ <method name="LaunchSearch">
+ <arg type="as" name="terms" direction="in" />
+ <arg type="u" name="timestamp" direction="in" />
+ </method>
+ </interface>
+</node>