Back to index

indicator-appmenu  12.10.0
hud-service.c
Go to the documentation of this file.
00001 /*
00002  * Copyright © 2012 Canonical Ltd.
00003  *
00004  * This program is free software: you can redistribute it and/or modify it
00005  * under the terms of the GNU General Public License version 3, as
00006  * published by the Free Software Foundation.
00007  *
00008  * This program is distributed in the hope that it will be useful, but
00009  * WITHOUT ANY WARRANTY; without even the implied warranties of
00010  * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
00011  * PURPOSE.  See the GNU General Public License for more details.
00012  *
00013  * You should have received a copy of the GNU General Public License along
00014  * with this program.  If not, see <http://www.gnu.org/licenses/>.
00015  *
00016  * Author: Ryan Lortie <desrt@desrt.ca>
00017  */
00018 
00019 #define G_LOG_DOMAIN "hud-service"
00020 
00021 #include "config.h"
00022 
00023 #include <glib.h>
00024 #include <gio/gio.h>
00025 #include <stdlib.h>
00026 #include <locale.h>
00027 #include <libintl.h>
00028 
00029 #include "hudappindicatorsource.h"
00030 #include "hudindicatorsource.h"
00031 #include "hudwindowsource.h"
00032 #include "huddebugsource.h"
00033 #include "hudsourcelist.h"
00034 #include "hudsettings.h"
00035 
00036 #include "hud.interface.h"
00037 #include "shared-values.h"
00038 #include "hudquery.h"
00039 
00040 /* The return value of 'StartQuery' and the signal parameters for
00041  * 'UpdatedQuery' are the same, so use a utility function for both.
00042  */
00043 GVariant *
00044 describe_query (HudQuery *query)
00045 {
00046   GVariantBuilder builder;
00047   gint n, i;
00048 
00049   n = hud_query_get_n_results (query);
00050 
00051   g_variant_builder_init (&builder, G_VARIANT_TYPE ("(sa(sssssv)v)"));
00052 
00053   /* Target */
00054   g_variant_builder_add (&builder, "s", "");
00055 
00056   /* List of results */
00057   g_variant_builder_open (&builder, G_VARIANT_TYPE ("a(sssssv)"));
00058   for (i = 0; i < n; i++)
00059     {
00060       HudResult *result = hud_query_get_result_by_index (query, i);
00061       HudItem *item;
00062 
00063       item = hud_result_get_item (result);
00064 
00065       g_variant_builder_add (&builder, "(sssssv)",
00066                              hud_result_get_html_description (result),
00067                              hud_item_get_app_icon (item),
00068                              hud_item_get_item_icon (item),
00069                              "" /* complete text */ , "" /* accel */,
00070                              g_variant_new_uint64 (hud_item_get_id (item)));
00071     }
00072   g_variant_builder_close (&builder);
00073 
00074   /* Query key */
00075   g_variant_builder_add (&builder, "v", hud_query_get_query_key (query));
00076 
00077   return g_variant_builder_end (&builder);
00078 }
00079 
00080 static void
00081 query_changed (HudQuery *query,
00082                gpointer  user_data)
00083 {
00084   GDBusConnection *connection = user_data;
00085 
00086   g_debug ("emit UpdatedQuery signal");
00087 
00088   g_dbus_connection_emit_signal (connection, NULL, DBUS_PATH,
00089                                  DBUS_IFACE, "UpdatedQuery",
00090                                  describe_query (query), NULL);
00091 }
00092 
00093 static GVariant *
00094 unpack_platform_data (GVariant *parameters)
00095 {
00096   GVariant *platform_data;
00097   gchar *startup_id;
00098   guint32 timestamp;
00099 
00100   g_variant_get_child (parameters, 1, "u", &timestamp);
00101   startup_id = g_strdup_printf ("_TIME%u", timestamp);
00102   platform_data = g_variant_new_parsed ("{'desktop-startup-id': < %s >}", startup_id);
00103   g_free (startup_id);
00104 
00105   return g_variant_ref_sink (platform_data);
00106 }
00107 
00108 static gboolean
00109 drop_query_timeout (gpointer user_data)
00110 {
00111   g_object_unref (user_data);
00112 
00113   return G_SOURCE_REMOVE;
00114 }
00115 
00116 static void
00117 bus_method (GDBusConnection       *connection,
00118             const gchar           *sender,
00119             const gchar           *object_path,
00120             const gchar           *interface_name,
00121             const gchar           *method_name,
00122             GVariant              *parameters,
00123             GDBusMethodInvocation *invocation,
00124             gpointer               user_data)
00125 {
00126   HudSource *source = user_data;
00127 
00128   if (g_str_equal (method_name, "StartQuery"))
00129     {
00130       const gchar *search_string;
00131       gint num_results;
00132       HudQuery *query;
00133 
00134       g_variant_get (parameters, "(&si)", &search_string, &num_results);
00135       g_debug ("'StartQuery' from %s: '%s', %d", sender, search_string, num_results);
00136       query = hud_query_new (source, search_string, num_results);
00137       g_signal_connect_object (query, "changed", G_CALLBACK (query_changed), connection, 0);
00138       g_dbus_method_invocation_return_value (invocation, describe_query (query));
00139       g_object_unref (query);
00140     }
00141 
00142   else if (g_str_equal (method_name, "ExecuteQuery"))
00143     {
00144       GVariant *platform_data;
00145       GVariant *item_key;
00146       guint64 key_value;
00147       HudItem *item;
00148 
00149       g_variant_get_child (parameters, 0, "v", &item_key);
00150 
00151       if (!g_variant_is_of_type (item_key, G_VARIANT_TYPE_UINT64))
00152         {
00153           g_debug ("'ExecuteQuery' from %s: incorrect item key (not uint64)", sender);
00154           g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
00155                                                  "item key has invalid format");
00156           g_variant_unref (item_key);
00157           return;
00158         }
00159 
00160       key_value = g_variant_get_uint64 (item_key);
00161       g_variant_unref (item_key);
00162 
00163       item = hud_item_lookup (key_value);
00164       g_debug ("'ExecuteQuery' from %s, item #%"G_GUINT64_FORMAT": %p", sender, key_value, item);
00165 
00166       if (item == NULL)
00167         {
00168           g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
00169                                                  "item specified by item key does not exist");
00170           return;
00171         }
00172 
00173       platform_data = unpack_platform_data (parameters);
00174       hud_item_activate (item, platform_data);
00175       g_variant_unref (platform_data);
00176 
00177       g_dbus_method_invocation_return_value (invocation, NULL);
00178     }
00179 
00180   else if (g_str_equal (method_name, "CloseQuery"))
00181     {
00182       GVariant *query_key;
00183       HudQuery *query;
00184 
00185       g_debug ("Got 'CloseQuery' from %s", sender);
00186 
00187       g_variant_get (parameters, "(v)", &query_key);
00188       query = hud_query_lookup (query_key);
00189       g_variant_unref (query_key);
00190 
00191       if (query != NULL)
00192         {
00193           g_signal_handlers_disconnect_by_func (query, query_changed, connection);
00194           /* Unity does 'CloseQuery' immediately followed by
00195            * 'StartQuery' on every keystroke.  Delay the destruction of
00196            * the query for a moment just in case a 'StartQuery' is on the
00197            * way.
00198            *
00199            * That way we can avoid allowing the use count to drop to
00200            * zero only to be increased again back to 1.  This prevents a
00201            * bunch of dbusmenu "closed"/"opened" calls being sent.
00202            */
00203           g_timeout_add (1000, drop_query_timeout, g_object_ref (query));
00204           hud_query_close (query);
00205         }
00206 
00207       /* always success -- they may have just been closing a timed out query */
00208       g_dbus_method_invocation_return_value (invocation, NULL);
00209     }
00210 }
00211 
00212 static GMainLoop *mainloop = NULL;
00213 
00214 static GDBusInterfaceInfo *
00215 get_iface_info (void)
00216 {
00217   GDBusInterfaceInfo *iface_info;
00218   GDBusNodeInfo *node_info;
00219   GError *error = NULL;
00220 
00221   node_info = g_dbus_node_info_new_for_xml (hud_interface, &error);
00222   g_assert_no_error (error);
00223 
00224   iface_info = g_dbus_node_info_lookup_interface (node_info, DBUS_IFACE);
00225   g_assert (iface_info != NULL);
00226 
00227   g_dbus_interface_info_ref (iface_info);
00228   g_dbus_node_info_unref (node_info);
00229 
00230   return iface_info;
00231 }
00232 
00233 static void
00234 bus_acquired_cb (GDBusConnection *connection,
00235                  const gchar     *name,
00236                  gpointer         user_data)
00237 {
00238   HudSource *source = user_data;
00239   GDBusInterfaceVTable vtable = {
00240     bus_method
00241   };
00242   GError *error = NULL;
00243 
00244   g_debug ("Bus acquired (guid %s)", g_dbus_connection_get_guid (connection));
00245 
00246   if (!g_dbus_connection_register_object (connection, DBUS_PATH, get_iface_info (), &vtable, source, NULL, &error))
00247     {
00248       g_warning ("Unable to register path '"DBUS_PATH"': %s", error->message);
00249       g_main_loop_quit (mainloop);
00250       g_error_free (error);
00251     }
00252 }
00253 
00254 static void
00255 name_acquired_cb (GDBusConnection *connection,
00256                   const gchar     *name,
00257                   gpointer         user_data)
00258 {
00259   g_debug ("Acquired bus name '%s'", name);
00260 }
00261 
00262 static void
00263 name_lost_cb (GDBusConnection *connection,
00264               const gchar     *name,
00265               gpointer         user_data)
00266 {
00267   g_warning ("Unable to get name '%s'", name);
00268   g_main_loop_quit (mainloop);
00269 }
00270 
00271 int
00272 main (int argc, char **argv)
00273 {
00274   HudWindowSource *window_source;
00275   HudSourceList *source_list;
00276 
00277   g_type_init ();
00278 
00279   setlocale (LC_ALL, "");
00280   bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
00281   textdomain (GETTEXT_PACKAGE);
00282 
00283   hud_settings_init ();
00284 
00285   source_list = hud_source_list_new ();
00286 
00287   /* we will eventually pull GtkMenu out of this, so keep it around */
00288   window_source = hud_window_source_new ();
00289   hud_source_list_add (source_list, HUD_SOURCE (window_source));
00290 
00291   {
00292     HudIndicatorSource *source;
00293 
00294     source = hud_indicator_source_new ();
00295     hud_source_list_add (source_list, HUD_SOURCE (source));
00296     g_object_unref (source);
00297   }
00298 
00299   {
00300     HudAppIndicatorSource *source;
00301 
00302     source = hud_app_indicator_source_new ();
00303     hud_source_list_add (source_list, HUD_SOURCE (source));
00304     g_object_unref (source);
00305   }
00306 
00307   if (getenv ("HUD_DEBUG_SOURCE"))
00308     {
00309       HudDebugSource *source;
00310 
00311       source = hud_debug_source_new ();
00312       hud_source_list_add (source_list, HUD_SOURCE (source));
00313       g_object_unref (source);
00314     }
00315 
00316   g_bus_own_name (G_BUS_TYPE_SESSION, DBUS_NAME, G_BUS_NAME_OWNER_FLAGS_NONE,
00317                   bus_acquired_cb, name_acquired_cb, name_lost_cb, source_list, NULL);
00318 
00319   mainloop = g_main_loop_new (NULL, FALSE);
00320   g_main_loop_run (mainloop);
00321   g_main_loop_unref (mainloop);
00322 
00323   g_object_unref (window_source);
00324   g_object_unref (source_list);
00325 
00326   return 0;
00327 }