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