516e93c71ede0d85f34e39591e9b22581f51aaaa
[cl-launcher] / src / cl-launcher.c
1 /*
2  *  Camera Launcher for Maemo.
3  *  Copyright (C) 2010 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 typedef struct _CLLauncherData CLLauncherData;
48 struct _CLLauncherData {
49         GtkWidget *dialog;
50
51         CLLauncherAction action;
52         gchar *prefered_application;
53         GtkListStore *application_list;
54         gboolean application_list_empty;
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         g_return_if_fail (data->osso_context);
68
69         if (application->osso_service) {
70                 if (strcmp (application->osso_service, "")) {
71                         if (osso_rpc_run_with_defaults (data->osso_context,
72                                                         application->osso_service,
73                                                         "top_application",
74                                                         NULL,
75                                                         DBUS_TYPE_INVALID) != OSSO_OK) {
76                         }
77                 }
78         } else if (application->exec) {
79                 if (strcmp (application->exec, "")) {
80                         if (!g_spawn_command_line_async (application->exec, NULL)) {
81                         }
82                 }
83         } else {
84         }
85 }
86
87 static void
88 kill_camera_application (void)
89 {
90         system ("killall -9 camera-ui");
91 }
92
93 static void
94 launcher_popup_selected (GtkTreeSelection *selection,
95                          gpointer user_data)
96 {
97         CLLauncherData *data = (CLLauncherData *) user_data;
98         GtkTreeModel *model;
99         GtkTreeIter iter;
100
101         g_return_if_fail (data);
102
103         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
104                 DesktopFileInfo application;
105
106                 gtk_tree_model_get (model, &iter,
107                                     SELECTOR_COLUMN_NAME, &application.name,
108                                     SELECTOR_COLUMN_OSSO_SERVICE, &application.osso_service,
109                                     SELECTOR_COLUMN_EXEC, &application.exec,
110                                     -1);
111
112                 run_application (data, &application);
113         }
114
115         /* destroy selector popup window */
116         launcher_popup_hide (data);
117 }
118
119 static void
120 launcher_popup_response (GtkDialog *dialog,
121                          gint response_id,
122                          gpointer user_data)
123 {
124         CLLauncherData *data = (CLLauncherData *) user_data;
125
126         g_return_if_fail (data);
127         g_return_if_fail (data->dialog);
128
129         gtk_widget_hide_all (data->dialog);
130         gtk_widget_destroy (data->dialog);
131         data->dialog = NULL;
132 }
133
134 static void
135 launcher_popup_hide (CLLauncherData *data)
136 {
137         g_return_if_fail (data);
138
139         if (data->dialog) {
140                 gtk_widget_hide_all (data->dialog);
141                 gtk_widget_destroy (data->dialog);
142                 data->dialog = NULL;
143         }
144 }
145
146 static void
147 launcher_popup_show (CLLauncherData *data)
148 {
149         GtkWidget *label, *alignment, *pannable, *tree_view;
150         GtkTreeViewColumn *column;
151         GtkCellRenderer *renderer;
152         GtkTreeSelection *selection;
153
154         g_return_if_fail (data);
155
156         /* popup dialog */
157         data->dialog = gtk_dialog_new ();
158         gtk_window_set_title (GTK_WINDOW (data->dialog), _("Select application"));
159         gtk_widget_set_size_request (GTK_WIDGET (GTK_DIALOG (data->dialog)->vbox), -1, 292);
160         hildon_gtk_window_set_portrait_flags (GTK_WINDOW (data->dialog), HILDON_PORTRAIT_MODE_SUPPORT);
161         g_signal_connect (G_OBJECT (data->dialog), "response", G_CALLBACK (launcher_popup_response), data);
162
163         /* check if application list is empty */
164         if (!data->application_list_empty) {
165                 GtkTreeSelection *selection;
166
167                 /* sort list of applications */
168                 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (data->application_list),
169                                                       SELECTOR_COLUMN_NAME, GTK_SORT_ASCENDING);
170
171                 /* alignment */
172                 alignment = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
173                 gtk_alignment_set_padding (GTK_ALIGNMENT (alignment),
174                                            0, 0, HILDON_MARGIN_DEFAULT, 0);
175                 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (data->dialog)->vbox), alignment);
176
177                 /* pannable */
178                 pannable = hildon_pannable_area_new ();
179                 gtk_container_add (GTK_CONTAINER (alignment), pannable);
180
181                 /* tree view */
182                 tree_view = hildon_gtk_tree_view_new_with_model (HILDON_UI_MODE_EDIT,
183                                                                  GTK_TREE_MODEL (data->application_list));
184                 gtk_container_add (GTK_CONTAINER (pannable), tree_view);
185
186                 /* application icon */
187                 column = gtk_tree_view_column_new ();
188                 gtk_tree_view_column_set_fixed_width (column, 75);
189                 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
190                 renderer = gtk_cell_renderer_pixbuf_new ();
191                 g_object_set(G_OBJECT(renderer), "xpad", 10, NULL);
192                 gtk_tree_view_column_pack_start (column, renderer, FALSE);
193                 gtk_tree_view_column_set_attributes (column, renderer,
194                                                      "pixbuf", SELECTOR_COLUMN_ICON,
195                                                      NULL);
196                 gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
197
198                 /* application name */
199                 column = gtk_tree_view_column_new ();
200                 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
201                 renderer = gtk_cell_renderer_text_new ();
202                 gtk_tree_view_column_pack_end (column, renderer, TRUE);
203                 gtk_tree_view_column_set_attributes (column, renderer, "text",
204                                                      SELECTOR_COLUMN_NAME,
205                                                      NULL);
206                 gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
207         } else {
208                 label = gtk_label_new (_("No applications"));
209                 hildon_helper_set_logical_color (label, GTK_RC_FG, GTK_STATE_NORMAL,
210                                                  "SecondaryTextColor");
211                 hildon_helper_set_logical_font (label, "LargeSystemFont");
212                 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (data->dialog)->vbox), label);
213         }
214
215         /* show selector popup */
216         gtk_widget_show_all (data->dialog);
217
218         /* unselect all entries */
219         if (tree_view) {
220                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
221                 gtk_tree_selection_unselect_all (selection);
222                 g_signal_connect (G_OBJECT (selection), "changed", G_CALLBACK (launcher_popup_selected), data);
223         }
224 }
225
226 static void
227 camera_launcher_on_gconf_changed (GConfClient *client,
228                                   guint cnxn_id,
229                                   GConfEntry *entry,
230                                   CLLauncherData *data)
231 {
232         const gchar *key;
233         GConfValue *value;
234         CLLauncherAction action;
235         const gchar *application;
236         const GSList *application_list;
237
238         g_return_if_fail (data);
239
240         key = gconf_entry_get_key (entry);
241         value = gconf_entry_get_value (entry);
242
243         g_return_if_fail (value);
244
245         /* Only key without absolute path is required */
246         key += strlen (GCONF_CL_LAUNCHER) + 1;
247
248         if (!strcmp (key, "action")) {
249                 action = gconf_value_get_int (value);
250                 if ((action < CL_LAUNCHER_ACTION_RUN_PREFERED_APPLICATION) ||
251                     (action > CL_LAUNCHER_ACTION_DO_NOTHING)) {
252                         g_warning("camera_launcher_on_gconf_changed: Wrong value %d of key %s/%s", action,
253                                   GCONF_CL_LAUNCHER, key);
254                 } else {
255                         data->action = action;
256                 }
257         } else if (!strcmp (key, "prefered_application")) {
258                 application = gconf_value_get_string (value);
259                 if (strcmp (application, "") &&
260                     g_str_has_suffix (application, ".desktop"))
261                 {
262                         if (data->prefered_application)
263                                 g_free (data->prefered_application);
264                         data->prefered_application = g_strdup (application);
265                 } else {
266                         g_warning("camera_launcher_on_gconf_changed: Wrong value %s of key %s/%s", application,
267                                   GCONF_CL_LAUNCHER, key);
268                 }
269         } else if (!strcmp (key, "application_list")) {
270                 if (data->application_list) {
271                         if (gconf_value_get_list_type (value) == GCONF_VALUE_STRING) {
272                                 application_list = gconf_value_get_list (value);
273
274                                 /* clear previous application list */
275                                 gtk_list_store_clear (data->application_list);
276
277                                 /* fill application list */
278                                 data->application_list_empty = get_application_list_from_list (data->application_list,
279                                                                                                application_list);
280                         }
281                 }
282         } else {
283                 g_warning("camera_launcher_on_gconf_changed: Wrong %s key, %s", GCONF_CL_LAUNCHER, key);
284         }
285 }
286
287 static void
288 camera_launcher_on_hal_property_modified (LibHalContext *ctx,
289                                           const char *udi,
290                                           const char *key,
291                                           dbus_bool_t is_removed,
292                                           dbus_bool_t is_added)
293 {
294         CLLauncherData *data = libhal_ctx_get_user_data (ctx);
295         gboolean state;
296         DesktopFileInfo *application;
297
298         g_return_if_fail (data);
299
300         if (strcmp (key, CAM_COVER_STATE) != 0)
301                 return;
302
303         state = !libhal_device_get_property_bool (ctx, udi, key, NULL);
304
305         if (!strcmp (udi, CAM_COVER_UDI)) {
306                 if (state) {
307                         switch (data->action) {
308                                 case CL_LAUNCHER_ACTION_DO_NOTHING:
309                                         /* just only kill camera application if it's running */
310                                         kill_camera_application ();
311                                         break;
312
313                                 case CL_LAUNCHER_ACTION_RUN_PREFERED_APPLICATION:
314                                         /* kill camera application only if it's not selected as prefered */
315                                         if (strcmp (data->prefered_application, CAMERA_APPLICATION_DESKTOP_FILE))
316                                                 kill_camera_application ();
317
318                                         /* run prefered application */
319                                         application = get_desktop_file_info (data->prefered_application);
320                                         if (application) {
321                                                 run_application (data, application);
322                                                 g_free (application);
323                                         }
324                                         break;
325
326                                 case CL_LAUNCHER_ACTION_SHOW_SELECTOR_POPUP:
327                                         /* kill camera application if it's running */
328                                         kill_camera_application ();
329
330                                         /* create selector popup window */
331                                         launcher_popup_show (data);
332                                         break;
333                         }
334                 } else {
335                         switch (data->action) {
336                                 case CL_LAUNCHER_ACTION_DO_NOTHING:
337                                 case CL_LAUNCHER_ACTION_RUN_PREFERED_APPLICATION:
338                                         /* do nothing */
339                                         break;
340
341                                 case CL_LAUNCHER_ACTION_SHOW_SELECTOR_POPUP:
342                                         /* destroy selector popup window */
343                                         launcher_popup_hide (data);
344                                         break;
345                         }
346                 }
347         } else if (!strcmp (udi, CAM_FOCUS_UDI)) {
348                 if (state) {
349                         /* run camera application when focus key was pressed */
350                         application = get_desktop_file_info (CAMERA_APPLICATION_DESKTOP_FILE);
351                         if (application) {
352                                 run_application (data, application);
353                                 g_free (application);
354                         }
355                 }
356         }
357 }
358
359 int
360 main (int argc, char **argv)
361 {
362         CLLauncherData *data;
363         DBusConnection *dbus_connection;
364         LibHalContext *hal;
365         DBusError dbus_error;
366         GError *error = NULL;
367
368         hildon_gtk_init (&argc, &argv);
369
370         /* allocate cllauncherdata */
371         data = g_new0 (CLLauncherData, 1);
372         data->dialog = NULL;
373         data->action = CL_LAUNCHER_ACTION_RUN_PREFERED_APPLICATION;
374         data->prefered_application = g_strdup (CAMERA_APPLICATION_DESKTOP_FILE);
375         data->application_list = gtk_list_store_new (NUM_COLS,
376                                                      G_TYPE_STRING,     /* SELECTOR_COLUMN_FILENAME */
377                                                      GDK_TYPE_PIXBUF,   /* SELECTOR_COLUMN_ICON */
378                                                      G_TYPE_STRING,     /* SELECTOR_COLUMN_NAME */
379                                                      G_TYPE_STRING,     /* SELECTOR_COLUMN_OSSO_SERVICE */
380                                                      G_TYPE_STRING);    /* SELECTOR_COLUMN_EXEC */
381         data->application_list_empty = TRUE;
382
383         /* initialize osso */
384         data->osso_context = osso_initialize (PACKAGE, VERSION, TRUE, NULL);
385
386         /* initialize gconf */
387         data->gconf_client = gconf_client_get_default ();
388         gconf_client_add_dir (data->gconf_client, GCONF_CL_LAUNCHER, GCONF_CLIENT_PRELOAD_ONELEVEL, &error);
389         if (error) {
390                 g_warning ("camera-launcher: Unable to add GConf client, %s", error->message);
391                 g_error_free (error);
392                 error = NULL;
393         }
394
395         gconf_client_notify_add (data->gconf_client, GCONF_CL_LAUNCHER,
396                                  (GConfClientNotifyFunc) camera_launcher_on_gconf_changed, data, NULL, &error);
397         if (error) {
398                 g_warning ("camera-launcher: Unable to add GConf client notification, %s", error->message);
399                 g_error_free (error);
400         }
401
402         gconf_client_notify (data->gconf_client, GCONF_CL_LAUNCHER "/action");
403         gconf_client_notify (data->gconf_client, GCONF_CL_LAUNCHER "/prefered_application");
404         gconf_client_notify (data->gconf_client, GCONF_CL_LAUNCHER "/application_list");
405
406         /* initialize dbus */
407         dbus_error_init (&dbus_error);
408         dbus_connection = dbus_bus_get (DBUS_BUS_SYSTEM, &dbus_error);
409         if (dbus_error_is_set (&dbus_error)) {
410                 g_critical ("camera-launcher: Could not get the system DBus connection, %s",
411                             dbus_error.message);
412                 dbus_error_free (&dbus_error);
413                 goto osso_error;
414         }
415
416         /* initialize hal */
417         hal = libhal_ctx_new ();
418         if (!hal) {
419                 g_critical ("camera-launcher: Unable to create HAL context\n");
420                 goto osso_error;
421         }
422
423         libhal_ctx_set_dbus_connection (hal, dbus_connection);
424         libhal_ctx_set_user_data (hal, data);
425         libhal_ctx_set_device_property_modified (hal, camera_launcher_on_hal_property_modified);
426
427         if (!libhal_ctx_init (hal, &dbus_error)) {
428                 if (dbus_error_is_set (&dbus_error)) {
429                         g_critical ("camera-launcher: Could not initialize the HAL context, %s",
430                                     dbus_error.message);
431                         dbus_error_free (&dbus_error);
432                 } else {
433                         g_critical ("camera-launcher: Could not initialize the HAL context, "
434                                     "no error, is hald running?");
435                 }
436                 goto hal_error;
437         }
438
439         libhal_device_add_property_watch (hal, CAM_COVER_UDI, NULL);
440         /* libhal_device_add_property_watch (hal, CAM_FOCUS_UDI, NULL); */
441
442         gtk_main ();
443
444         /* deinitialize hal */
445         if (hal) {
446                 libhal_device_remove_property_watch (hal, CAM_COVER_UDI, NULL);
447                 /* libhal_device_remove_property_watch (hal, CAM_FOCUS_UDI, NULL); */
448 hal_error:
449                 libhal_ctx_set_user_data (hal, NULL);
450                 libhal_ctx_shutdown (hal, NULL);
451                 libhal_ctx_free (hal);
452         }
453
454         /* unreference dbus connection */
455         if (dbus_connection)
456                 dbus_connection_unref (dbus_connection);
457
458 osso_error:
459         /* deinitialize osso */
460         if (data->osso_context) {
461                 osso_deinitialize (data->osso_context);
462         }
463
464         /* free cllauncherdata */
465         if (data) {
466                 if (data->prefered_application)
467                         g_free (data->prefered_application);
468
469                 /* unref application list */
470                 g_object_unref (data->application_list);
471
472                 g_free (data);
473         }
474
475
476         return 0;
477 }