Fixed bug 6003: On/Of" text under word Flashlight should be secondary colour.
[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_style (HILDON_BUTTON (button), HILDON_BUTTON_STYLE_PICKER);
290         hildon_button_set_title (HILDON_BUTTON (button), _("Flashlight"));
291         hildon_button_set_value (HILDON_BUTTON (button), MSG_FLASHLIGHT_OFF);
292         hildon_button_set_image (HILDON_BUTTON (button),
293                                  gtk_image_new_from_icon_name (ICON_FLASHLIGHT_OFF, -1));
294         hildon_button_set_image_position (HILDON_BUTTON (button), GTK_POS_LEFT);
295
296         g_signal_connect (button, "clicked",
297                                         G_CALLBACK (flashlight_status_plugin_on_clicked), plugin);
298
299         return button;
300 }
301
302 static void
303 flashlight_status_plugin_class_init (FlashlightPluginClass *class)
304 {
305         GObjectClass *object_class = G_OBJECT_CLASS (class);
306
307         object_class->finalize = flashlight_status_plugin_finalize;
308
309         g_type_class_add_private (class, sizeof (FlashlightPluginPrivate));
310 }
311
312 static void
313 flashlight_status_plugin_class_finalize (FlashlightPluginClass *class)
314 {
315 }
316
317 static void
318 flashlight_status_plugin_init (FlashlightPlugin *plugin)
319 {
320         FlashlightPluginPrivate *priv = FLASHLIGHT_STATUS_PLUGIN_GET_PRIVATE (plugin);
321         DBusError error;
322
323         /* initialize osso */
324         priv->osso_context = osso_initialize (PACKAGE, VERSION, TRUE, NULL);
325         if (!priv->osso_context) {
326                 g_critical ("flashlight_status_plugin_init: Could not initialize OSSO\n");
327                 return;
328         }
329
330         if (osso_rpc_set_cb_f (priv->osso_context,
331                                FLASHLIGHT_APPLET_SERVICE,
332                                FLASHLIGHT_APPLET_OBJECT,
333                                FLASHLIGHT_APPLET_IFACE,
334                                flashlight_applet_dbus_request_handler, plugin) != OSSO_OK) {
335                 g_critical ("flashlight_status_plugin_init: Unable to register D-BUS request handler\n");
336                 return;
337         }
338
339         /* initialize dbus */
340         dbus_error_init (&error);
341         priv->dbus_connection = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
342         if (dbus_error_is_set (&error)) {
343                 g_critical ("flashlight_status_plugin_init: Could not get the system DBus connection, %s",
344                             error.message);
345                 dbus_error_free (&error);
346                 return;
347         }
348
349         /* initialize flashlight */
350         if (flashlight_init (&priv->flashlight) < 0) {
351                 g_critical ("flashlight_status_plugin_init: Unable to create Flashlight context\n");
352                 return;
353         }
354
355         /* initialize hal */
356         priv->hal = libhal_ctx_new ();
357         if (!priv->hal) {
358                 g_critical ("flashlight_status_plugin_init: Unable to create HAL context\n");
359                 return;
360         }
361
362         libhal_ctx_set_dbus_connection (priv->hal, priv->dbus_connection);
363         libhal_ctx_set_user_data (priv->hal, plugin);
364         libhal_ctx_set_device_property_modified (priv->hal,
365                                                  flashlight_status_plugin_on_hal_property_modified);
366
367         if (!libhal_ctx_init (priv->hal, &error)) {
368                 if (dbus_error_is_set (&error)) {
369                         g_critical ("Could not initialize the HAL context, %s",
370                                     error.message);
371                         dbus_error_free (&error);
372                 } else {
373                         g_critical ("Could not initialize the HAL context, "
374                                     "no error, is hald running?");
375                 }
376                 libhal_ctx_set_user_data (priv->hal, NULL);
377                 libhal_ctx_free (priv->hal);
378                 priv->hal = NULL;
379                 return;
380         }
381
382         libhal_device_add_property_watch (priv->hal, CAM_COVER_UDI, NULL);
383
384         /* create plugin ui */
385         priv->button = flashlight_status_plugin_ui (plugin);
386         gtk_container_add (GTK_CONTAINER (plugin), priv->button);
387
388         /* show widget if camera cover is open */
389         if ( !libhal_device_get_property_bool (priv->hal, CAM_COVER_UDI, CAM_COVER_STATE, NULL))
390                 gtk_widget_show_all (GTK_WIDGET (plugin));
391
392 }
393
394 static void
395 flashlight_status_plugin_finalize (GObject *object)
396 {
397         FlashlightPlugin *plugin = FLASHLIGHT_STATUS_PLUGIN (object);
398         FlashlightPluginPrivate *priv = FLASHLIGHT_STATUS_PLUGIN_GET_PRIVATE (plugin);
399
400         g_return_if_fail (priv);
401
402         /* deinitialize hal */
403         if (priv->hal) {
404                 libhal_device_remove_property_watch (priv->hal, CAM_COVER_UDI, NULL);
405                 libhal_ctx_set_user_data (priv->hal, NULL);
406                 libhal_ctx_shutdown (priv->hal, NULL);
407                 libhal_ctx_free (priv->hal);
408         }
409         priv->hal = NULL;
410
411         /* unreference dbus connection */
412         if (priv->dbus_connection) {
413                 dbus_connection_unref (priv->dbus_connection);
414         }
415         priv->dbus_connection = NULL;
416
417         /* cancel status timer */
418         if (priv->status_timer) {
419                 g_source_remove (priv->status_timer);
420         }
421         priv->status_timer = 0;
422
423         /* deinitialize flashlight */
424         if (priv->flashlight) {
425                 flashlight_deinit (priv->flashlight);
426         }
427         priv->flashlight = NULL;
428
429         /* deinitialize osso */
430         if (priv->osso_context) {
431                 osso_deinitialize (priv->osso_context);
432         }
433         priv->osso_context = NULL;
434
435         G_OBJECT_CLASS (flashlight_status_plugin_parent_class)->finalize (object);
436 }