8e6ba0096b623c0a6d5601f660fe908902a049d0
[flashlight-appl] / src / flashlight_applet.c
1 /*
2  *  Flashlight applet (widget) for Maemo.
3  *  Copyright (C) 2009, 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 <glib.h>
27 #include <gtk/gtk.h>
28 #include <hildon/hildon.h>
29 #include <glib/gi18n-lib.h>
30 #include <libhal.h>
31 #include <dbus/dbus.h>
32
33 #include <libosso.h>
34
35 #include "flashlight_applet.h"
36 #include "flashlight_lib.h"
37
38 #define MSG_FLASHLIGHT_ON _("On")
39 #define MSG_FLASHLIGHT_OFF _("Off")
40
41 #define ICON_FLASHLIGHT_ON "statusarea_flashlight_on"
42 #define ICON_FLASHLIGHT_OFF "statusarea_flashlight_off"
43
44 #define CAM_COVER_UDI "/org/freedesktop/Hal/devices/platform_cam_shutter"
45 #define CAM_COVER_STATE "button.state.value"
46
47 #define FLASHLIGHT_APPLET_SERVICE "org.maemo.flashlight_applet"
48 #define FLASHLIGHT_APPLET_OBJECT "/org/maemo/flashlight_applet"
49 #define FLASHLIGHT_APPLET_IFACE "org.maemo.flashlight_applet"
50
51 #define FLASHLIGHT_APPLET_METHOD_ENABLE "enable"
52 #define FLASHLIGHT_APPLET_METHOD_DISABLE "disable"
53
54 #define _CAMERAUI(str) dgettext("osso-camera-ui",str)
55
56 #define FLASHLIGHT_STATUS_PLUGIN_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE (obj,   \
57                             TYPE_FLASHLIGHT_STATUS_PLUGIN, FlashlightPluginPrivate))
58
59 struct _FlashlightPluginPrivate
60 {
61         GtkWidget *button;
62         guint status_timer;
63
64         FlashlightContext_t *flashlight;
65         DBusConnection *dbus_connection;
66         LibHalContext *hal;
67         osso_context_t *osso_context;
68 };
69
70 HD_DEFINE_PLUGIN_MODULE (FlashlightPlugin, flashlight_status_plugin, HD_TYPE_STATUS_MENU_ITEM)
71
72 static gboolean flashlight_status_plugin_status (gpointer data);
73 static void flashlight_status_plugin_finalize (GObject *object);
74
75 static void
76 flashlight_status_plugin_show_notification (FlashlightPlugin *plugin,
77                                             const gchar *text)
78 {
79         FlashlightPluginPrivate *priv = FLASHLIGHT_STATUS_PLUGIN_GET_PRIVATE (plugin);
80         GtkWidget *banner;
81
82         g_return_if_fail (priv);
83         g_return_if_fail (priv->button);
84
85         banner = hildon_banner_show_information (GTK_WIDGET (priv->button), NULL, text);
86         hildon_banner_set_timeout (HILDON_BANNER (banner), 3000);
87 }
88
89 static void
90 flashlight_status_plugin_enable (FlashlightPlugin *plugin,
91                                  gboolean enable)
92 {
93         FlashlightPluginPrivate *priv = FLASHLIGHT_STATUS_PLUGIN_GET_PRIVATE (plugin);
94
95         g_return_if_fail (priv);
96         g_return_if_fail (priv->button);
97
98         if (enable) {
99                 if (flashlight_open (priv->flashlight, "/dev/video0") < 0) {
100                         flashlight_status_plugin_show_notification (plugin,
101                                 _("Unable to initialize flashlight.\nCamera in use by another application."));
102                         flashlight_close (priv->flashlight);
103                         return;
104                 }
105
106                 if (flashlight_set_intensity (priv->flashlight, 1) < 0) {
107                         flashlight_status_plugin_show_notification (plugin,
108                                 _("Unable to turn on flashlight."));
109                         flashlight_close (priv->flashlight);
110                         return;
111                 }
112
113                 hildon_button_set_value (HILDON_BUTTON (priv->button), MSG_FLASHLIGHT_ON);
114                 hildon_button_set_image (HILDON_BUTTON (priv->button),
115                                          gtk_image_new_from_icon_name (ICON_FLASHLIGHT_ON, -1));
116
117                 /* check status of controller every 1s */
118                 priv->status_timer = g_timeout_add (1000, flashlight_status_plugin_status, plugin);
119         } else {
120                 /* cancel status timer */
121                 if (priv->status_timer) {
122                         g_source_remove (priv->status_timer);
123                         priv->status_timer = 0;
124                 }
125
126                 /* set intensity to 0 */
127                 if (flashlight_set_intensity (priv->flashlight, 0) < 0) {
128                         flashlight_status_plugin_show_notification (plugin,
129                                 _("Unable to turn off flashlight."));
130                         return;
131                 }
132
133                 if (flashlight_close (priv->flashlight) < 0) {
134                         return;
135                 }
136
137                 hildon_button_set_value (HILDON_BUTTON (priv->button), MSG_FLASHLIGHT_OFF);
138                 hildon_button_set_image (HILDON_BUTTON (priv->button),
139                                          gtk_image_new_from_icon_name (ICON_FLASHLIGHT_OFF, -1));
140         }
141 }
142
143 static gint
144 flashlight_applet_dbus_request_handler (const gchar *interface,
145                                         const gchar *method,
146                                         GArray *arguments,
147                                         gpointer data,
148                                         osso_rpc_t *retval)
149 {
150         FlashlightPlugin *plugin = data;
151         FlashlightPluginPrivate *priv = FLASHLIGHT_STATUS_PLUGIN_GET_PRIVATE (plugin);
152         gboolean is_open = FALSE;
153
154         g_return_val_if_fail (interface, OSSO_ERROR);
155         g_return_val_if_fail (method, OSSO_ERROR);
156
157         g_return_val_if_fail (priv, OSSO_ERROR);
158         g_return_val_if_fail (priv->hal, OSSO_ERROR);
159         g_return_val_if_fail (priv->button, OSSO_ERROR);
160
161         if (strcmp (interface, FLASHLIGHT_APPLET_IFACE))
162                 return OSSO_ERROR;
163
164         is_open = !libhal_device_get_property_bool (priv->hal, CAM_COVER_UDI, CAM_COVER_STATE, NULL);
165
166         if (!strcmp (method, FLASHLIGHT_APPLET_METHOD_ENABLE)) {
167                 /* check if cover is open */
168                 if (is_open) {
169                         /* try to enable flashlight, only if it's disabled */
170                         if (!strcmp (hildon_button_get_value (HILDON_BUTTON (priv->button)), MSG_FLASHLIGHT_OFF)) {
171                                 flashlight_status_plugin_enable (plugin, TRUE);
172                         }
173                 } else {
174                         flashlight_status_plugin_show_notification (plugin, _CAMERAUI ("camera_ia_open_lens_cover"));
175                 }
176         } else if (!strcmp (method, FLASHLIGHT_APPLET_METHOD_DISABLE)) {
177                 if (is_open) {
178                         /* try to disable flashlight, only if it's enabled */
179                         if (!strcmp (hildon_button_get_value (HILDON_BUTTON (priv->button)), MSG_FLASHLIGHT_ON)) {
180                                 flashlight_status_plugin_enable (plugin, FALSE);
181                         }
182                 }
183         } else {
184                 return OSSO_ERROR;
185         }
186
187         return OSSO_OK;
188 }
189
190 static void
191 flashlight_status_plugin_on_hal_property_modified (LibHalContext *ctx,
192                                                    const char *udi,
193                                                    const char *key,
194                                                    dbus_bool_t is_removed,
195                                                    dbus_bool_t is_added)
196 {
197         FlashlightPlugin *plugin = libhal_ctx_get_user_data (ctx);
198         FlashlightPluginPrivate *priv = FLASHLIGHT_STATUS_PLUGIN_GET_PRIVATE (plugin);
199         gboolean is_open = FALSE;
200         int intensity = 0;
201
202         g_return_if_fail (priv);
203
204         if (strcmp (udi, CAM_COVER_UDI) != 0)
205                 return;
206
207         if (strcmp (key, CAM_COVER_STATE) != 0)
208                 return;
209
210         is_open = !libhal_device_get_property_bool (ctx, udi, key, NULL);
211
212         if (is_open) {
213                 /* show widget */
214                 gtk_widget_show_all (GTK_WIDGET (plugin));
215         } else {
216                 /* turn off flashlight if flashlight is enabled */
217                 if (flashlight_get_intensity (priv->flashlight, &intensity) == 0) {
218                         if (intensity > 0) {
219                                 flashlight_status_plugin_enable (plugin, FALSE);
220                         }
221                 }
222
223                 /* hide widget */
224                 gtk_widget_hide_all (GTK_WIDGET (plugin));
225         }
226 }
227
228 static gboolean
229 flashlight_status_plugin_status (gpointer data)
230 {
231         FlashlightPlugin *plugin = data;
232         FlashlightPluginPrivate *priv = FLASHLIGHT_STATUS_PLUGIN_GET_PRIVATE (plugin);
233         int status = 0;
234
235         if (flashlight_get_status (priv->flashlight, &status) < 0) {
236                 flashlight_status_plugin_show_notification (plugin,
237                                                             _("Unable to read status from driver."));
238                 return FALSE;
239         }
240
241         /* ops, something is wrong */
242         if (status) {
243                 /* turn off flashlight */
244                 flashlight_status_plugin_enable (plugin, FALSE);
245
246                 if (status & FLASHLIGHT_STATUS_SHORT_CIRCUT_FAULT) {
247                         flashlight_status_plugin_show_notification (plugin,
248                                 _("Short-circut fault detected!\nTurning off flashlight."));
249                 } else if (status & FLASHLIGHT_STATUS_OVERTEMPERATURE_FAULT) {
250                         flashlight_status_plugin_show_notification (plugin,
251                                 _("Overtemperature fault detected!\nTurning off flashlight."));
252                 } else if (status & FLASHLIGHT_STATUS_TIMEOUT_FAULT) {
253                         flashlight_status_plugin_show_notification (plugin,
254                                 _("Timeout fault detected!\nTurning off flashlight."));
255                 } else if (status & FLASHLIGHT_STATUS_OVERVOLTAGE_FAULT) {
256                         flashlight_status_plugin_show_notification (plugin,
257                                 _("Overvoltage fault detected!\nTurning off flashlight."));
258                 }
259         }
260
261         return TRUE;
262 }
263
264 static void
265 flashlight_status_plugin_on_clicked (HildonButton *button,
266                                      gpointer data)
267 {
268         FlashlightPlugin *plugin = data;
269         FlashlightPluginPrivate *priv = FLASHLIGHT_STATUS_PLUGIN_GET_PRIVATE (plugin);
270
271         g_return_if_fail (priv);
272
273         if (!strcmp (hildon_button_get_value (button), MSG_FLASHLIGHT_ON)) {
274                 flashlight_status_plugin_enable (plugin, FALSE);
275         } else {
276                 flashlight_status_plugin_enable (plugin, TRUE);
277         }
278 }
279
280 static GtkWidget *
281 flashlight_status_plugin_ui (FlashlightPlugin *plugin)
282 {
283         GtkWidget *button;
284
285         g_return_val_if_fail (plugin, NULL);
286
287         button = hildon_button_new (HILDON_SIZE_FINGER_HEIGHT, HILDON_BUTTON_ARRANGEMENT_VERTICAL);
288         gtk_button_set_alignment (GTK_BUTTON (button), 0.0, 0.5);
289         hildon_button_set_title (HILDON_BUTTON (button), _("Flashlight"));
290         hildon_button_set_value (HILDON_BUTTON (button), MSG_FLASHLIGHT_OFF);
291         hildon_button_set_image (HILDON_BUTTON (button),
292                                  gtk_image_new_from_icon_name (ICON_FLASHLIGHT_OFF, -1));
293         hildon_button_set_image_position (HILDON_BUTTON (button), GTK_POS_LEFT);
294
295         g_signal_connect (button, "clicked",
296                                         G_CALLBACK (flashlight_status_plugin_on_clicked), plugin);
297
298         return button;
299 }
300
301 static void
302 flashlight_status_plugin_class_init (FlashlightPluginClass *class)
303 {
304         GObjectClass *object_class = G_OBJECT_CLASS (class);
305
306         object_class->finalize = flashlight_status_plugin_finalize;
307
308         g_type_class_add_private (class, sizeof (FlashlightPluginPrivate));
309 }
310
311 static void
312 flashlight_status_plugin_class_finalize (FlashlightPluginClass *class)
313 {
314 }
315
316 static void
317 flashlight_status_plugin_init (FlashlightPlugin *plugin)
318 {
319         FlashlightPluginPrivate *priv = FLASHLIGHT_STATUS_PLUGIN_GET_PRIVATE (plugin);
320         DBusError error;
321
322         /* initialize osso */
323         priv->osso_context = osso_initialize (PACKAGE, VERSION, TRUE, NULL);
324         if (!priv->osso_context) {
325                 g_critical ("flashlight_status_plugin_init: Could not initialize OSSO\n");
326                 return;
327         }
328
329         if (osso_rpc_set_cb_f (priv->osso_context,
330                                FLASHLIGHT_APPLET_SERVICE,
331                                FLASHLIGHT_APPLET_OBJECT,
332                                FLASHLIGHT_APPLET_IFACE,
333                                flashlight_applet_dbus_request_handler, plugin) != OSSO_OK) {
334                 g_critical ("flashlight_status_plugin_init: Unable to register D-BUS request handler\n");
335                 return;
336         }
337
338         /* initialize dbus */
339         dbus_error_init (&error);
340         priv->dbus_connection = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
341         if (dbus_error_is_set (&error)) {
342                 g_critical ("flashlight_status_plugin_init: Could not get the system DBus connection, %s",
343                             error.message);
344                 dbus_error_free (&error);
345                 return;
346         }
347
348         /* initialize flashlight */
349         if (flashlight_init (&priv->flashlight) < 0) {
350                 g_critical ("flashlight_status_plugin_init: Unable to create Flashlight context\n");
351                 return;
352         }
353
354         /* initialize hal */
355         priv->hal = libhal_ctx_new ();
356         if (!priv->hal) {
357                 g_critical ("flashlight_status_plugin_init: Unable to create HAL context\n");
358                 return;
359         }
360
361         libhal_ctx_set_dbus_connection (priv->hal, priv->dbus_connection);
362         libhal_ctx_set_user_data (priv->hal, plugin);
363         libhal_ctx_set_device_property_modified (priv->hal,
364                                                  flashlight_status_plugin_on_hal_property_modified);
365
366         if (!libhal_ctx_init (priv->hal, &error)) {
367                 if (dbus_error_is_set (&error)) {
368                         g_critical ("Could not initialize the HAL context, %s",
369                                     error.message);
370                         dbus_error_free (&error);
371                 } else {
372                         g_critical ("Could not initialize the HAL context, "
373                                     "no error, is hald running?");
374                 }
375                 libhal_ctx_set_user_data (priv->hal, NULL);
376                 libhal_ctx_free (priv->hal);
377                 priv->hal = NULL;
378                 return;
379         }
380
381         libhal_device_add_property_watch (priv->hal, CAM_COVER_UDI, NULL);
382
383         /* create plugin ui */
384         priv->button = flashlight_status_plugin_ui (plugin);
385         gtk_container_add (GTK_CONTAINER (plugin), priv->button);
386
387         /* show widget if camera cover is open */
388         if ( !libhal_device_get_property_bool (priv->hal, CAM_COVER_UDI, CAM_COVER_STATE, NULL))
389                 gtk_widget_show_all (GTK_WIDGET (plugin));
390
391 }
392
393 static void
394 flashlight_status_plugin_finalize (GObject *object)
395 {
396         FlashlightPlugin *plugin = FLASHLIGHT_STATUS_PLUGIN (object);
397         FlashlightPluginPrivate *priv = FLASHLIGHT_STATUS_PLUGIN_GET_PRIVATE (plugin);
398
399         g_return_if_fail (priv);
400
401         /* deinitialize hal */
402         if (priv->hal) {
403                 libhal_device_remove_property_watch (priv->hal, CAM_COVER_UDI, NULL);
404                 libhal_ctx_set_user_data (priv->hal, NULL);
405                 libhal_ctx_shutdown (priv->hal, NULL);
406                 libhal_ctx_free (priv->hal);
407         }
408         priv->hal = NULL;
409
410         /* unreference dbus connection */
411         if (priv->dbus_connection) {
412                 dbus_connection_unref (priv->dbus_connection);
413         }
414         priv->dbus_connection = NULL;
415
416         /* cancel status timer */
417         if (priv->status_timer) {
418                 g_source_remove (priv->status_timer);
419         }
420         priv->status_timer = 0;
421
422         /* deinitialize flashlight */
423         if (priv->flashlight) {
424                 flashlight_deinit (priv->flashlight);
425         }
426         priv->flashlight = NULL;
427
428         /* deinitialize osso */
429         if (priv->osso_context) {
430                 osso_deinitialize (priv->osso_context);
431         }
432         priv->osso_context = NULL;
433
434         G_OBJECT_CLASS (flashlight_status_plugin_parent_class)->finalize (object);
435 }