4d735bc3f24747793143b3049fc9a25bf7e885c9
[cl-launcher] / src / cl-launcher.c
1 /*
2  *  Camera Launcher for Maemo.
3  *  Copyright (C) 2009 Roman Moravcik
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include <gconf/gconf-client.h>
29
30 #include <glib.h>
31 #include <glib/gi18n-lib.h>
32
33 #include <gtk/gtk.h>
34 #include <hildon/hildon.h>
35
36 #include <libosso.h>
37
38 #include <libhal.h>
39 #include <dbus/dbus.h>
40
41 #include "cl-utils.h"
42
43 #define CAM_COVER_UDI "/org/freedesktop/Hal/devices/platform_cam_shutter"
44 #define CAM_FOCUS_UDI "/org/freedesktop/Hal/devices/platform_cam_focus"
45 #define CAM_COVER_STATE "button.state.value"
46
47 #define CAMERA_APPLICATION "camera-ui.desktop"
48
49 typedef struct _CLLauncherData CLLauncherData;
50 struct _CLLauncherData {
51         GtkWidget *dialog;
52
53         CLLauncherAction action;
54         gchar *application;
55
56         osso_context_t *osso_context;
57         GConfClient *gconf_client;
58 };
59
60 static void launcher_popup_show (CLLauncherData *data);
61 static void launcher_popup_hide (CLLauncherData *data);
62
63 static void
64 run_application (CLLauncherData *data, DesktopFileInfo *application)
65 {
66         g_return_if_fail (data);
67
68         if (application->osso_service) {
69                 if (strcmp (application->osso_service, "")) {
70                         if (osso_rpc_run_with_defaults (data->osso_context,
71                                                         application->osso_service,
72                                                         "top_application",
73                                                         NULL,
74                                                         DBUS_TYPE_INVALID) != OSSO_OK) {
75                         }
76                 }
77         } else if (application->exec) {
78                 if (strcmp (application->exec, "")) {
79                         if (!g_spawn_command_line_async (application->exec, NULL)) {
80                         }
81                 }
82         } else {
83         }
84 }
85
86 static void
87 kill_camera_application (void)
88 {
89         system ("killall -9 camera-ui");
90 }
91
92 static void
93 launcher_popup_selected (GtkTreeSelection *selection,
94                          gpointer user_data)
95 {
96         CLLauncherData *data = (CLLauncherData *) user_data;
97         GtkTreeModel *model;
98         GtkTreeIter iter;
99
100         g_return_if_fail (data);
101
102         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
103                 DesktopFileInfo application;
104
105                 gtk_tree_model_get (model, &iter,
106                                     SELECTOR_COLUMN_NAME, &application.name,
107                                     SELECTOR_COLUMN_OSSO_SERVICE, &application.osso_service,
108                                     SELECTOR_COLUMN_EXEC, &application.exec,
109                                     -1);
110
111                 run_application (data, &application);
112         }
113
114         /* destroy selector popup window */
115         launcher_popup_hide (data);
116 }
117
118 static void
119 launcher_popup_response (GtkDialog *dialog,
120                          gint response_id,
121                          gpointer user_data)
122 {
123         CLLauncherData *data = (CLLauncherData *) user_data;
124
125         g_return_if_fail (data);
126
127         gtk_widget_hide_all (data->dialog);
128         gtk_widget_destroy (data->dialog);
129         data->dialog = NULL;
130 }
131
132 static void
133 launcher_popup_hide (CLLauncherData *data)
134 {
135         g_return_if_fail (data);
136
137         if (data->dialog) {
138                 gtk_widget_hide_all (data->dialog);
139                 gtk_widget_destroy (data->dialog);
140                 data->dialog = NULL;
141         }
142 }
143
144 static void
145 launcher_popup_show (CLLauncherData *data)
146 {
147         GtkWidget *label, *alignment, *pannable, *tree_view;
148         GtkListStore *application_list;
149         GtkTreeViewColumn *column;
150         GtkCellRenderer *renderer;
151         GtkTreeSelection *selection;
152
153         g_return_if_fail (data);
154
155         /* popup dialog */
156         data->dialog = gtk_dialog_new ();
157         gtk_window_set_title (GTK_WINDOW (data->dialog), _("Select application"));
158         gtk_widget_set_size_request (GTK_WIDGET (GTK_DIALOG (data->dialog)->vbox), -1, 270);
159         g_signal_connect (G_OBJECT (data->dialog), "response", G_CALLBACK (launcher_popup_response), data);
160
161         /* create application list */
162         application_list = gtk_list_store_new (NUM_COLS,
163                                                GDK_TYPE_PIXBUF, /* SELECTOR_COLUMN_ICON */
164                                                G_TYPE_STRING,   /* SELECTOR_COLUMN_NAME */
165                                                G_TYPE_STRING,   /* SELECTOR_COLUMN_OSSO_SERVICE */
166                                                G_TYPE_STRING);  /* SELECTOR_COLUMN_EXEC */
167
168         /* get application list */
169         if (get_application_list (application_list)) {
170                 GtkTreeSelection *selection;
171
172                 /* sort list of calendars */
173                 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (application_list),
174                                                       SELECTOR_COLUMN_NAME, GTK_SORT_ASCENDING);
175
176                 /* alignment */
177                 alignment = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
178                 gtk_alignment_set_padding (GTK_ALIGNMENT (alignment),
179                                            0, 0, HILDON_MARGIN_DEFAULT, 0);
180                 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (data->dialog)->vbox), alignment);
181
182                 /* pannable */
183                 pannable = hildon_pannable_area_new ();
184                 gtk_container_add (GTK_CONTAINER (alignment), pannable);
185
186                 /* tree view */
187                 tree_view = hildon_gtk_tree_view_new_with_model (HILDON_UI_MODE_EDIT,
188                                                                  GTK_TREE_MODEL (application_list));
189                 gtk_container_add (GTK_CONTAINER (pannable), tree_view);
190
191                 /* application icon */
192                 column = gtk_tree_view_column_new ();
193                 gtk_tree_view_column_set_fixed_width (column, 75);
194                 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
195                 renderer = gtk_cell_renderer_pixbuf_new ();
196                 g_object_set(G_OBJECT(renderer), "xpad", 10, NULL);
197                 gtk_tree_view_column_pack_start (column, renderer, FALSE);
198                 gtk_tree_view_column_set_attributes (column, renderer,
199                                                      "pixbuf", SELECTOR_COLUMN_ICON,
200                                                      NULL);
201                 gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
202
203                 /* application name */
204                 column = gtk_tree_view_column_new ();
205                 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
206                 renderer = gtk_cell_renderer_text_new ();
207                 gtk_tree_view_column_pack_end (column, renderer, TRUE);
208                 gtk_tree_view_column_set_attributes (column, renderer, "text",
209                                                      SELECTOR_COLUMN_NAME,
210                                                      NULL);
211                 gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
212         } else {
213                 label = gtk_label_new (_("No applications"));
214                 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (data->dialog)->vbox), label);
215         }
216
217         /* unref application list */
218         g_object_unref (application_list);
219
220         /* show selector popup */
221         gtk_widget_show_all (data->dialog);
222
223         /* unselect all entries */
224         if (tree_view) {
225                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
226                 gtk_tree_selection_unselect_all (selection);
227                 g_signal_connect (G_OBJECT (selection), "changed", G_CALLBACK (launcher_popup_selected), data);
228         }
229 }
230
231 static void
232 camera_launcher_on_gconf_changed (GConfClient *client,
233                                   guint cnxn_id,
234                                   GConfEntry *entry,
235                                   CLLauncherData *data)
236 {
237         const gchar *key;
238         GConfValue *value;
239         CLLauncherAction action;
240         const gchar *application;
241
242         key = gconf_entry_get_key (entry);
243         value = gconf_entry_get_value (entry);
244
245         g_return_if_fail (data);
246         g_return_if_fail (value);
247
248         /* Only key without absolute path is required */
249         key += strlen (GCONF_CL_LAUNCHER) + 1;
250
251         if (!strcmp (key, "action")) {
252                 action = gconf_value_get_int (value);
253                 if ((action < CL_LAUNCHER_ACTION_DO_NOTHING) ||
254                     (action > CL_LAUNCHER_ACTION_SHOW_SELECTOR_POPUP)) {
255                         g_warning("camera_launcher_on_gconf_changed: Wrong value %d of key %s/%s", action, GCONF_CL_LAUNCHER, key);
256                 } else {
257                         data->action = action;
258                 }
259         } else if (!strcmp (key, "application")) {
260                 application = gconf_value_get_string (value);
261                 if (strcmp (application, "") &&
262                     g_str_has_suffix (application, ".desktop"))
263                 {
264                         if (data->application)
265                                 g_free (data->application);
266                         data->application = g_strdup (application);
267                 } else {
268                         g_warning("camera_launcher_on_gconf_changed: Wrong value %s of key %s/%s", application, GCONF_CL_LAUNCHER, key);
269                 }
270         } else {
271                 g_warning("camera_launcher_on_gconf_changed: Wrong %s key, %s", GCONF_CL_LAUNCHER, key);
272         }
273         gconf_value_free (value);
274 }
275
276 static void
277 camera_launcher_on_hal_property_modified (LibHalContext *ctx,
278                                           const char *udi,
279                                           const char *key,
280                                           dbus_bool_t is_removed,
281                                           dbus_bool_t is_added)
282 {
283         CLLauncherData *data = libhal_ctx_get_user_data (ctx);
284         gboolean state;
285         DesktopFileInfo *application;
286
287         g_return_if_fail (data);
288
289         if (strcmp (key, CAM_COVER_STATE) != 0)
290                 return;
291
292         state = !libhal_device_get_property_bool (ctx, udi, key, NULL);
293
294         if (!strcmp (udi, CAM_COVER_UDI)) {
295                 if (state) {
296                         switch (data->action) {
297                                 case CL_LAUNCHER_ACTION_DO_NOTHING:
298                                         /* just only kill camera application if it's running */
299                                         kill_camera_application ();
300                                         break;
301
302                                 case CL_LAUNCHER_ACTION_RUN_PREFERED_APPLICATION:
303                                         /* kill camera application only if it's not selected as prefered */
304                                         if (strcmp (data->application, CAMERA_APPLICATION))
305                                                 kill_camera_application ();
306
307                                         /* run prefered application */
308                                         application = get_desktop_file_info (data->application);
309                                         if (application) {
310                                                 run_application (data, application);
311                                                 g_free (application);
312                                         }
313                                         break;
314
315                                 case CL_LAUNCHER_ACTION_SHOW_SELECTOR_POPUP:
316                                         /* kill camera application if it's running */
317                                         kill_camera_application ();
318
319                                         /* create selector popup window */
320                                         launcher_popup_show (data);
321                                         break;
322                         }
323                 } else {
324                         switch (data->action) {
325                                 case CL_LAUNCHER_ACTION_DO_NOTHING:
326                                 case CL_LAUNCHER_ACTION_RUN_PREFERED_APPLICATION:
327                                         /* do nothing */
328                                         break;
329
330                                 case CL_LAUNCHER_ACTION_SHOW_SELECTOR_POPUP:
331                                         /* destroy selector popup window */
332                                         launcher_popup_hide (data);
333                                         break;
334                         }
335                 }
336         } else if (!strcmp (udi, CAM_FOCUS_UDI)) {
337                 if (state) {
338                         /* run camera application when focus key was pressed */
339                         application = get_desktop_file_info (CAMERA_APPLICATION);
340                         if (application) {
341                                 run_application (data, application);
342                                 g_free (application);
343                         }
344                 }
345         }
346 }
347
348 int
349 main (int argc, char **argv)
350 {
351         CLLauncherData *data;
352         DBusConnection *dbus_connection;
353         LibHalContext *hal;
354         DBusError dbus_error;
355         GError *error = NULL;
356
357         hildon_gtk_init (&argc, &argv);
358
359         /* create CLLauncherData */
360         data = g_new0 (CLLauncherData, 1);
361         data->dialog = NULL;
362 //      data->action = CL_LAUNCHER_ACTION_RUN_PREFERED_APPLICATION;
363         data->action = CL_LAUNCHER_ACTION_SHOW_SELECTOR_POPUP;
364         data->application = g_strdup (CAMERA_APPLICATION);
365
366         /* initialize osso */
367         data->osso_context = osso_initialize (PACKAGE, VERSION, TRUE, NULL);
368
369         /* initialize gconf */
370         data->gconf_client = gconf_client_get_default ();
371         gconf_client_add_dir (data->gconf_client, GCONF_CL_LAUNCHER, GCONF_CLIENT_PRELOAD_ONELEVEL, &error);
372         if (error) {
373                 g_warning ("camera-launcher: Unable to add GConf client, %s", error->message);
374                 g_error_free (error);
375                 error = NULL;
376         }
377
378         gconf_client_notify_add (data->gconf_client, GCONF_CL_LAUNCHER,
379                                  (GConfClientNotifyFunc) camera_launcher_on_gconf_changed, data, NULL, &error);
380         if (error) {
381                 g_warning ("camera-launcher: Unable to add GConf client notification, %s", error->message);
382                 g_error_free (error);
383         }
384
385         gconf_client_notify (data->gconf_client, GCONF_CL_LAUNCHER "/action");
386         gconf_client_notify (data->gconf_client, GCONF_CL_LAUNCHER "/application");
387
388         /* initialize dbus */
389         dbus_error_init (&dbus_error);
390         dbus_connection = dbus_bus_get (DBUS_BUS_SYSTEM, &dbus_error);
391         if (dbus_error_is_set (&dbus_error)) {
392                 g_critical ("camera-launcher: Could not get the system DBus connection, %s",
393                             dbus_error.message);
394                 dbus_error_free (&dbus_error);
395                 goto osso_error;
396         }
397
398         /* initialize hal */
399         hal = libhal_ctx_new ();
400         if (!hal) {
401                 g_critical ("camera-launcher: Unable to create HAL context\n");
402                 goto osso_error;
403         }
404
405         libhal_ctx_set_dbus_connection (hal, dbus_connection);
406         libhal_ctx_set_user_data (hal, data);
407         libhal_ctx_set_device_property_modified (hal, camera_launcher_on_hal_property_modified);
408
409         if (!libhal_ctx_init (hal, &dbus_error)) {
410                 if (dbus_error_is_set (&dbus_error)) {
411                         g_critical ("camera-launcher: Could not initialize the HAL context, %s",
412                                     dbus_error.message);
413                         dbus_error_free (&dbus_error);
414                 } else {
415                         g_critical ("camera-launcher: Could not initialize the HAL context, "
416                                     "no error, is hald running?");
417                 }
418                 goto hal_error;
419         }
420
421         libhal_device_add_property_watch (hal, CAM_COVER_UDI, NULL);
422         libhal_device_add_property_watch (hal, CAM_FOCUS_UDI, NULL);
423
424         gtk_main ();
425
426         /* deinitialize hal */
427         if (hal) {
428                 libhal_device_remove_property_watch (hal, CAM_COVER_UDI, NULL);
429                 libhal_device_remove_property_watch (hal, CAM_FOCUS_UDI, NULL);
430 hal_error:
431                 libhal_ctx_set_user_data (hal, NULL);
432                 libhal_ctx_shutdown (hal, NULL);
433                 libhal_ctx_free (hal);
434         }
435
436 osso_error:
437         /* deinitialize osso */
438         if (data->osso_context) {
439                 osso_deinitialize (data->osso_context);
440         }
441
442         return 0;
443 }