240ab28597264a15157677a4d481922527d449db
[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 "flashlight_applet.h"
34 #include "flashlight_lib.h"
35
36 #define MSG_FLASHLIGHT_ON _("On")
37 #define MSG_FLASHLIGHT_OFF _("Off")
38
39 #define ICON_FLASHLIGHT_ON "statusarea_flashlight_on"
40 #define ICON_FLASHLIGHT_OFF "statusarea_flashlight_off"
41
42 #define CAM_COVER_UDI "/org/freedesktop/Hal/devices/platform_cam_shutter"
43 #define CAM_COVER_STATE "button.state.value"
44
45 #define FLASHLIGHT_STATUS_PLUGIN_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE (obj,   \
46                             TYPE_FLASHLIGHT_STATUS_PLUGIN, FlashlightPluginPrivate))
47
48 struct _FlashlightPluginPrivate
49 {
50         GtkWidget *button;
51         guint status_timer;
52
53         FlashlightContext_t *flashlight;
54         DBusConnection *dbus_connection;
55         LibHalContext *hal;
56 };
57
58 HD_DEFINE_PLUGIN_MODULE (FlashlightPlugin, flashlight_status_plugin, HD_TYPE_STATUS_MENU_ITEM)
59
60 static gboolean flashlight_status_plugin_status (gpointer data);
61 static void flashlight_status_plugin_finalize (GObject *object);
62
63 static void
64 flashlight_status_plugin_show_notification (FlashlightPlugin *plugin,
65                                             const gchar *text)
66 {
67         FlashlightPluginPrivate *priv = FLASHLIGHT_STATUS_PLUGIN_GET_PRIVATE (plugin);
68         GtkWidget *banner;
69
70         g_return_if_fail (priv);
71         g_return_if_fail (priv->button);
72
73         banner = hildon_banner_show_information (GTK_WIDGET (priv->button), NULL, text);
74         hildon_banner_set_timeout (HILDON_BANNER (banner), 3000);
75 }
76
77 static void
78 flashlight_status_plugin_enable (FlashlightPlugin *plugin,
79                                  gboolean enable)
80 {
81         FlashlightPluginPrivate *priv = FLASHLIGHT_STATUS_PLUGIN_GET_PRIVATE (plugin);
82
83         g_return_if_fail (priv);
84         g_return_if_fail (priv->button);
85
86         if (enable) {
87                 if (flashlight_open (priv->flashlight, "/dev/video0") < 0) {
88                         flashlight_status_plugin_show_notification (plugin,
89                                 _("Unable to initialize flashlight.\nCamera in use by another application."));
90                         flashlight_close (priv->flashlight);
91                         return;
92                 }
93
94                 if (flashlight_set_intensity (priv->flashlight, 1) < 0) {
95                         flashlight_status_plugin_show_notification (plugin,
96                                 _("Unable to turn on flashlight."));
97                         flashlight_close (priv->flashlight);
98                         return;
99                 }
100
101                 hildon_button_set_value (HILDON_BUTTON (priv->button), MSG_FLASHLIGHT_ON);
102                 hildon_button_set_image (HILDON_BUTTON (priv->button),
103                                          gtk_image_new_from_icon_name (ICON_FLASHLIGHT_ON, -1));
104
105                 /* check status of controller every 1s */
106                 priv->status_timer = g_timeout_add (1000, flashlight_status_plugin_status, plugin);
107         } else {
108                 /* cancel status timer */
109                 if (priv->status_timer) {
110                         g_source_remove (priv->status_timer);
111                         priv->status_timer = 0;
112                 }
113
114                 /* set intensity to 0 */
115                 if (flashlight_set_intensity (priv->flashlight, 0) < 0) {
116                         flashlight_status_plugin_show_notification (plugin,
117                                 _("Unable to turn off flashlight."));
118                         return;
119                 }
120
121                 if (flashlight_close (priv->flashlight) < 0) {
122                         return;
123                 }
124
125                 hildon_button_set_value (HILDON_BUTTON (priv->button), MSG_FLASHLIGHT_OFF);
126                 hildon_button_set_image (HILDON_BUTTON (priv->button),
127                                          gtk_image_new_from_icon_name (ICON_FLASHLIGHT_OFF, -1));
128         }
129 }
130
131 static void
132 flashlight_status_plugin_on_hal_property_modified (LibHalContext *ctx,
133                                                    const char *udi,
134                                                    const char *key,
135                                                    dbus_bool_t is_removed,
136                                                    dbus_bool_t is_added)
137 {
138         FlashlightPlugin *plugin = libhal_ctx_get_user_data (ctx);
139         FlashlightPluginPrivate *priv = FLASHLIGHT_STATUS_PLUGIN_GET_PRIVATE (plugin);
140         gboolean is_open = FALSE;
141         int intensity = 0;
142
143         g_return_if_fail (priv);
144
145         if (strcmp (udi, CAM_COVER_UDI) != 0)
146                 return;
147
148         if (strcmp (key, CAM_COVER_STATE) != 0)
149                 return;
150
151         is_open = !libhal_device_get_property_bool (ctx, udi, key, NULL);
152
153         if (is_open) {
154                 /* show widget */
155                 gtk_widget_show_all (GTK_WIDGET (plugin));
156         } else {
157                 /* turn off flashlight if flashlight is enabled */
158                 if (flashlight_get_intensity (priv->flashlight, &intensity) == 0) {
159                         if (intensity > 0) {
160                                 flashlight_status_plugin_enable (plugin, FALSE);
161                         }
162                 }
163
164                 /* hide widget */
165                 gtk_widget_hide_all (GTK_WIDGET (plugin));
166         }
167 }
168
169 static gboolean
170 flashlight_status_plugin_status (gpointer data)
171 {
172         FlashlightPlugin *plugin = data;
173         FlashlightPluginPrivate *priv = FLASHLIGHT_STATUS_PLUGIN_GET_PRIVATE (plugin);
174         int status = 0;
175
176         if (flashlight_get_status (priv->flashlight, &status) < 0) {
177                 flashlight_status_plugin_show_notification (plugin,
178                                                             _("Unable to read status from driver."));
179                 return FALSE;
180         }
181
182         /* ops, something is wrong */
183         if (status) {
184                 /* turn off flashlight */
185                 flashlight_status_plugin_enable (plugin, FALSE);
186
187                 if (status & FLASHLIGHT_STATUS_SHORT_CIRCUT_FAULT) {
188                         flashlight_status_plugin_show_notification (plugin,
189                                 _("Short-circut fault detected!\nTurning off flashlight."));
190                 } else if (status & FLASHLIGHT_STATUS_OVERTEMPERATURE_FAULT) {
191                         flashlight_status_plugin_show_notification (plugin,
192                                 _("Overtemperature fault detected!\nTurning off flashlight."));
193                 } else if (status & FLASHLIGHT_STATUS_TIMEOUT_FAULT) {
194                         flashlight_status_plugin_show_notification (plugin,
195                                 _("Timeout fault detected!\nTurning off flashlight."));
196                 } else if (status & FLASHLIGHT_STATUS_OVERVOLTAGE_FAULT) {
197                         flashlight_status_plugin_show_notification (plugin,
198                                 _("Overvoltage fault detected!\nTurning off flashlight."));
199                 }
200         }
201
202         return TRUE;
203 }
204
205 static void
206 flashlight_status_plugin_on_clicked (HildonButton *button,
207                                      gpointer data)
208 {
209         FlashlightPlugin *plugin = data;
210         FlashlightPluginPrivate *priv = FLASHLIGHT_STATUS_PLUGIN_GET_PRIVATE (plugin);
211
212         g_return_if_fail (priv);
213
214         if (!strcmp (hildon_button_get_value (button), MSG_FLASHLIGHT_ON)) {
215                 flashlight_status_plugin_enable (plugin, FALSE);
216         } else {
217                 flashlight_status_plugin_enable (plugin, TRUE);
218         }
219 }
220
221 static GtkWidget *
222 flashlight_status_plugin_ui (FlashlightPlugin *plugin)
223 {
224         GtkWidget *button;
225
226         g_return_val_if_fail (plugin, NULL);
227
228         button = hildon_button_new (HILDON_SIZE_FINGER_HEIGHT, HILDON_BUTTON_ARRANGEMENT_VERTICAL);
229         gtk_button_set_alignment (GTK_BUTTON (button), 0.0, 0.5);
230         hildon_button_set_title (HILDON_BUTTON (button), _("Flashlight"));
231         hildon_button_set_value (HILDON_BUTTON (button), MSG_FLASHLIGHT_OFF);
232         hildon_button_set_image (HILDON_BUTTON (button),
233                                  gtk_image_new_from_icon_name (ICON_FLASHLIGHT_OFF, -1));
234         hildon_button_set_image_position (HILDON_BUTTON (button), GTK_POS_LEFT);
235
236         g_signal_connect (button, "clicked",
237                                         G_CALLBACK (flashlight_status_plugin_on_clicked), plugin);
238
239         return button;
240 }
241
242 static void
243 flashlight_status_plugin_class_init (FlashlightPluginClass *class)
244 {
245         GObjectClass *object_class = G_OBJECT_CLASS (class);
246
247         object_class->finalize = flashlight_status_plugin_finalize;
248
249         g_type_class_add_private (class, sizeof (FlashlightPluginPrivate));
250 }
251
252 static void
253 flashlight_status_plugin_class_finalize (FlashlightPluginClass *class)
254 {
255 }
256
257 static void
258 flashlight_status_plugin_init (FlashlightPlugin *plugin)
259 {
260         FlashlightPluginPrivate *priv = FLASHLIGHT_STATUS_PLUGIN_GET_PRIVATE (plugin);
261         DBusError error;
262
263         /* initialize dbus */
264         dbus_error_init (&error);
265         priv->dbus_connection = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
266         if (dbus_error_is_set (&error)) {
267                 g_critical ("flashlight_status_plugin_init: Could not get the system DBus connection, %s",
268                             error.message);
269                 dbus_error_free (&error);
270                 return;
271         }
272
273         /* initialize flashlight */
274         if (flashlight_init (&priv->flashlight) < 0) {
275                 g_critical ("flashlight_status_plugin_init: Unable to create Flashlight context\n");
276                 return;
277         }
278
279         /* initialize hal */
280         priv->hal = libhal_ctx_new ();
281         if (!priv->hal) {
282                 g_critical ("flashlight_status_plugin_init: Unable to create HAL context\n");
283                 return;
284         }
285
286         libhal_ctx_set_dbus_connection (priv->hal, priv->dbus_connection);
287         libhal_ctx_set_user_data (priv->hal, plugin);
288         libhal_ctx_set_device_property_modified (priv->hal,
289                                                  flashlight_status_plugin_on_hal_property_modified);
290
291         if (!libhal_ctx_init (priv->hal, &error)) {
292                 if (dbus_error_is_set (&error)) {
293                         g_critical ("Could not initialize the HAL context, %s",
294                                     error.message);
295                         dbus_error_free (&error);
296                 } else {
297                         g_critical ("Could not initialize the HAL context, "
298                                     "no error, is hald running?");
299                 }
300                 libhal_ctx_set_user_data (priv->hal, NULL);
301                 libhal_ctx_free (priv->hal);
302                 priv->hal = NULL;
303                 return;
304         }
305
306         libhal_device_add_property_watch (priv->hal, CAM_COVER_UDI, NULL);
307
308         /* create plugin ui */
309         priv->button = flashlight_status_plugin_ui (plugin);
310         gtk_container_add (GTK_CONTAINER (plugin), priv->button);
311
312         /* show widget if camera cover is open */
313         if ( !libhal_device_get_property_bool (priv->hal, CAM_COVER_UDI, CAM_COVER_STATE, NULL))
314                 gtk_widget_show_all (GTK_WIDGET (plugin));
315
316 }
317
318 static void
319 flashlight_status_plugin_finalize (GObject *object)
320 {
321         FlashlightPlugin *plugin = FLASHLIGHT_STATUS_PLUGIN (object);
322         FlashlightPluginPrivate *priv = FLASHLIGHT_STATUS_PLUGIN_GET_PRIVATE (plugin);
323
324         g_return_if_fail (priv);
325
326         /* deinitialize hal */
327         if (priv->hal) {
328                 libhal_device_remove_property_watch (priv->hal, CAM_COVER_UDI, NULL);
329                 libhal_ctx_set_user_data (priv->hal, NULL);
330                 libhal_ctx_shutdown (priv->hal, NULL);
331                 libhal_ctx_free (priv->hal);
332         }
333         priv->hal = NULL;
334
335         /* unreference dbus connection */
336         if (priv->dbus_connection) {
337                 dbus_connection_unref (priv->dbus_connection);
338         }
339         priv->dbus_connection = NULL;
340
341         /* cancel status timer */
342         if (priv->status_timer) {
343                 g_source_remove (priv->status_timer);
344         }
345         priv->status_timer = 0;
346
347         /* deinitialize flashlight */
348         if (priv->flashlight) {
349                 flashlight_deinit (priv->flashlight);
350         }
351         priv->flashlight = NULL;
352
353         G_OBJECT_CLASS (flashlight_status_plugin_parent_class)->finalize (object);
354 }