import load-applet 0.46-1
[cpumem-applet] / src / item.c
1 /*
2   Load applet - Maemo5 edition
3
4 */
5
6 #include <gtk/gtk.h>
7 #include <hildon/hildon.h>
8 #include <glib/gerror.h>
9 #include <glib.h>
10 #include <glib/gstdio.h>
11 #include <string.h>
12 #include <osso-log.h>
13 #include <unistd.h>
14 #include <stdlib.h>
15 #include <signal.h>
16 #include <dbus/dbus.h>
17 #include <dbus/dbus.h>
18 #include <dbus/dbus-glib.h>
19 #include <dbus/dbus-glib-lowlevel.h>
20 #include <sched.h>
21 #include "item.h"
22
23 #define LOAD_APPLET_STATUS_MENU_ITEM_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE (obj, LOAD_APPLET_TYPE_STATUS_MENU_ITEM, LoadAppletStatusMenuItemPrivate))
24
25 struct _LoadAppletStatusMenuItemPrivate {
26         gulong screenshot_h;
27         gulong video_h;
28         pid_t rec_pid;
29         gboolean recording;
30
31         DBusError error;
32         GMainLoop *loop;
33         DBusConnection *conn;
34         guint timeout;
35
36 };
37
38 HD_DEFINE_PLUGIN_MODULE (LoadAppletStatusMenuItem, load_applet_status_menu_item, HD_TYPE_STATUS_MENU_ITEM)
39
40
41
42 #define SHUTTER_SOUND "/usr/share/sounds/camera_snd_title_1.wav"
43 #define BEEP_SOUND    "/usr/share/sounds/ui-default_beep.wav" 
44 #define MAX_SSHOT_NAME_LEN 64
45 static gchar *sshotfilename = NULL;
46 static char sshotname[MAX_SSHOT_NAME_LEN];
47 static guint screenshot_delay = 20;
48 static guint beeps = 0;
49 static gint sshotn = 0;
50 static gint scastn = 0;
51 static gboolean taking = FALSE;
52
53 #define MATCH_CAMERA_KEY "type='signal',"         \
54         "interface='org.freedesktop.Hal.Device'," 
55
56
57 static void
58 load_applet_status_menu_item_class_finalize(LoadAppletStatusMenuItemClass * klass)
59 {
60 }
61
62 static void
63 load_applet_status_menu_item_dispose(GObject * object)
64 {
65         G_OBJECT_CLASS(load_applet_status_menu_item_parent_class)->dispose(object);
66 }
67
68 static void
69 load_applet_status_menu_item_class_init(LoadAppletStatusMenuItemClass * klass)
70 {
71         GObjectClass *object_class = G_OBJECT_CLASS(klass);
72
73         object_class->dispose = load_applet_status_menu_item_dispose;
74
75         g_type_class_add_private(klass, sizeof(LoadAppletStatusMenuItemPrivate));
76 }
77
78 /*
79  * Get the filename for next screen-shot. Start with nr.
80  * 
81  */
82 static int
83 get_next_sshot(gint nr, const gchar *suffix)
84 {
85         FILE *f;
86         gint n;
87
88         n = nr;
89         f = NULL;
90         do {
91                 g_snprintf (sshotname, MAX_SSHOT_NAME_LEN-1, "%s/MyDocs/.images/%s%02d.%s", getenv("HOME"), sshotfilename, n, suffix);
92                 f = g_fopen(sshotname, "r");
93                 if (f) {
94                         fclose(f); 
95                         n++;
96                 }
97         }
98         while(f != NULL);
99                 
100         return n;
101 }
102
103
104 /*
105  * Get the filename for next screen-cast. Start with nr.
106  * 
107  */
108 static int
109 get_next_scast(gint nr, const gchar *suffix)
110 {
111         FILE *f;
112         gint n;
113
114         n = nr;
115         f = NULL;
116         do {
117                 g_snprintf (sshotname, MAX_SSHOT_NAME_LEN-1, "%s/MyDocs/.videos/%s%02d.%s", getenv("HOME"), "screencast", n, suffix);
118                 f = g_fopen(sshotname, "r");
119                 if (f) {
120                         fclose(f); 
121                         n++;
122                 }
123         }
124         while(f != NULL);
125                 
126         return n;
127 }
128
129
130
131 /*
132  * Close the menu, make sure it is closed and redrawn
133  */
134 static void
135 do_little_dance (void)
136 {
137         while (gtk_events_pending())
138         {
139                 gtk_main_iteration();
140         }
141
142         usleep(1000);
143         sched_yield();
144 }
145
146
147 /*
148  *  Take screeshot using the root window drawable
149  */
150 static gboolean
151 take_screenshot(void)
152 {
153         int width, height;
154         GdkDrawable *root_window;
155         GdkPixbuf *pixbuf;
156         gboolean ret;
157         GError *error = NULL;
158         
159         do_little_dance();
160         sshotn = get_next_sshot(sshotn, "png");
161
162
163         root_window = gdk_get_default_root_window();
164         gdk_drawable_get_size(root_window, &width, &height);
165         pixbuf = gdk_pixbuf_get_from_drawable(NULL,
166                                            root_window,
167                                            gdk_drawable_get_colormap (root_window),
168                                            0, 0,
169                                            0, 0,
170                                            width, height);
171       
172
173         ret = gdk_pixbuf_save(pixbuf, sshotname, "png", &error, NULL);
174         g_object_unref(pixbuf);
175
176         //DEBUG
177         //printf("\tSaving %s\n", sshotname);
178
179         return FALSE;
180 }
181
182
183 /*
184  * Either beep or play shutter sound and make the screenshot
185  */
186 static gboolean
187 beep_or_shoot(gpointer data)
188 {
189         beeps++;
190         if (beeps >= screenshot_delay) {
191                 hildon_play_system_sound(SHUTTER_SOUND);
192                 take_screenshot();
193                 return FALSE; 
194         } else {
195                 hildon_play_system_sound(BEEP_SOUND);
196                 return TRUE;  
197         }
198 }
199
200
201 static DBusHandlerResult
202 handle_dbus_events(DBusConnection *connection, DBusMessage *msg, void *user_data)
203 {
204         if (taking == FALSE) 
205                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
206
207         gint message_type = -1;
208         const gchar *interface = dbus_message_get_interface(msg);
209         const gchar *method = dbus_message_get_member(msg);
210         LoadAppletStatusMenuItem *item = (LoadAppletStatusMenuItem*)user_data;
211         item->priv = LOAD_APPLET_STATUS_MENU_ITEM_GET_PRIVATE (item);
212
213         if (!(message_type = dbus_message_get_type(msg)) || !interface || !method) 
214                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
215
216         switch (message_type) {
217         case DBUS_MESSAGE_TYPE_SIGNAL:
218                 if ((g_strcmp0(interface, "org.freedesktop.Hal.Device") == 0) &&
219                     (g_strcmp0(method, "PropertyModified") == 0)) {
220                         //Camera key pressed. Take screenshot, cancel the waiting loop, remove D-Bus listener
221                         gtk_timeout_remove(item->priv->timeout);
222                         taking = FALSE;
223                         hildon_play_system_sound(SHUTTER_SOUND);
224                         take_screenshot();
225                         dbus_bus_remove_match(item->priv->conn, MATCH_CAMERA_KEY, &item->priv->error);
226                         dbus_connection_unref(item->priv->conn);
227                         item->priv->conn = 0;
228                         g_main_loop_unref(item->priv->loop);
229                         item->priv->loop = NULL;
230                 }
231                 break;
232         default:
233                 break;
234         }
235         return DBUS_HANDLER_RESULT_HANDLED;
236 }
237
238
239 #ifdef SHOW_PROCESS_INFO
240 /*
241  * Show the details dialog (FAKE)
242  *
243  */
244 static void
245 activate_status_item(GtkMenuItem *item, gpointer data)
246 {
247         GtkWidget *dialog;
248         GtkWidget *selector;
249         GtkWidget *content_area;
250         gint i;
251
252         dialog = gtk_dialog_new();
253
254         selector = hildon_touch_selector_new_text ();
255         hildon_touch_selector_set_column_selection_mode (HILDON_TOUCH_SELECTOR (selector),HILDON_TOUCH_SELECTOR_SELECTION_MODE_SINGLE);
256
257         for (i = 1; i <= 10 ; i++) {
258                 gchar *label = g_strdup_printf ("Item %d", i);
259                 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector), label);
260                 g_free (label);
261     }
262
263         hildon_touch_selector_unselect_all(HILDON_TOUCH_SELECTOR (selector), 0);
264
265         content_area = GTK_DIALOG(dialog)->vbox;
266         gtk_container_add(GTK_CONTAINER(content_area), selector);
267         gtk_window_set_default_size(GTK_WINDOW(dialog), -1, 480);
268         
269         gtk_window_set_title(GTK_WINDOW(dialog), "Select process to kill - FAKE FEATURE");
270         gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
271         gtk_dialog_add_button(GTK_DIALOG(dialog), "Kill it", 1);
272         
273         gtk_widget_show_all(GTK_WIDGET(dialog));
274         //gtk_dialog_run(GTK_DIALOG(dialog)); <- blocks and won't close status menu
275 }
276 #endif
277
278
279
280 /*
281  * Answer the callback by setting delayed screenshot
282  */
283 static void
284 activate_delayed_screenshot_item(GtkMenuItem *item, gpointer data)
285 {
286         if (taking)
287                 return;
288         
289         LoadAppletStatusMenuItem *menu_item = (LoadAppletStatusMenuItem*)data;
290         menu_item->priv = LOAD_APPLET_STATUS_MENU_ITEM_GET_PRIVATE (menu_item);
291
292         hildon_banner_show_information(NULL, NULL, "You have 20s to press the camera capture key to take screenshot.");
293         beeps = 0;
294         taking = TRUE;
295         menu_item->priv->timeout = gtk_timeout_add(1000, beep_or_shoot, NULL);
296
297         menu_item->priv->loop = g_main_loop_new(NULL, FALSE);
298         dbus_error_init(&menu_item->priv->error);
299         if ((menu_item->priv->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &menu_item->priv->error)) == NULL) {
300                 DLOG_CRIT("LA:Failed to open connection to system bus");
301                 dbus_error_free(&menu_item->priv->error);
302                 return;
303         }
304         if (!dbus_connection_add_filter(menu_item->priv->conn, handle_dbus_events, (void *)data, 0)) {
305                 ULOG_CRIT("LA:Failed to add dbus filter");
306                 dbus_error_free(&menu_item->priv->error);
307                 return;
308         }
309         dbus_bus_add_match(menu_item->priv->conn, MATCH_CAMERA_KEY, &menu_item->priv->error);
310         if (dbus_error_is_set(&menu_item->priv->error)) {
311                 ULOG_WARN("LA:Unable to add match for my key");
312                 dbus_error_free(&menu_item->priv->error);
313         }
314         dbus_connection_setup_with_g_main(menu_item->priv->conn, NULL);
315 }
316
317
318 /*
319  * Answer the callback on video button
320  * If idle, start recording
321  * If already recording, stop
322  */
323 static void
324 activate_video_item(GtkMenuItem *item, gpointer data)
325 {       
326         LoadAppletStatusMenuItem *la_item = (LoadAppletStatusMenuItem*)data;
327         pid_t rec_pid;
328         gchar *savefile;
329                 
330         if (la_item->priv->recording == FALSE) {
331                 // We are not recording. Start recording by exec
332                 scastn = get_next_scast(scastn, "avi");
333                 savefile = g_strdup_printf("location=%s", sshotname);
334                 rec_pid = fork();
335                 if (rec_pid == 0) {
336                         execl("/usr/bin/gst-launch", "/usr/bin/gst-launch", "ximagewsrc", "!", "video/x-raw-rgb,framerate=5/1", "!", "ffmpegcolorspace", "!", "jpegenc", "quality=30", "!", "queue", "!", "mux.", "autoaudiosrc", "!", "audioconvert", "!", "audioresample", "!", "queue", "!", "mux.", "avimux", "name=mux", "!", "filesink", savefile, NULL); 
337                         exit(0);
338                 } else {
339                         la_item->priv->recording = TRUE;
340                         la_item->priv->rec_pid = rec_pid;
341                 }
342         } else {
343                 // We are already recording     
344                 kill(la_item->priv->rec_pid, SIGTERM);
345                 la_item->priv->recording = FALSE;
346         }
347 }
348
349
350
351
352 static void
353 load_applet_status_menu_item_init (LoadAppletStatusMenuItem * menu_item)
354 {
355         #define ICON_DIR        "/usr/share/pixmaps/"
356         #define ICON_VIDEO      ICON_DIR"la_video.png"
357         #define ICON_PICTURE    ICON_DIR"la_picture.png"
358         #define ICON_PROCESS    ICON_DIR"la_process.png"        
359
360         GtkWidget *hbox, *icon, *b;
361
362         menu_item->priv = LOAD_APPLET_STATUS_MENU_ITEM_GET_PRIVATE (menu_item);
363
364         hbox = gtk_hbox_new(FALSE, 0);
365
366         sshotfilename = g_strdup("screenshot");
367
368 #ifdef SHOW_PROCESS_INFO
369         // Button status
370         b = hildon_button_new(HILDON_SIZE_FINGER_HEIGHT, HILDON_BUTTON_ARRANGEMENT_VERTICAL); 
371         hildon_button_set_style(HILDON_BUTTON(b), HILDON_BUTTON_STYLE_PICKER);
372         gtk_widget_show(b);
373         icon = gtk_image_new_from_file(ICON_PROCESS);
374         gtk_widget_show(icon);
375         gtk_button_set_image(GTK_BUTTON(b), GTK_WIDGET(icon));
376         gtk_box_pack_start(GTK_BOX(hbox), b, TRUE, TRUE, 1);
377         g_signal_connect(b, "clicked", G_CALLBACK(activate_status_item), menu_item);
378 #endif
379         // Button Video
380         b = gtk_toggle_button_new();
381         gtk_widget_show(b);
382         icon = gtk_image_new_from_file(ICON_VIDEO);
383         gtk_widget_show(icon);
384         gtk_button_set_image(GTK_BUTTON(b), GTK_WIDGET(icon));
385         gtk_box_pack_start(GTK_BOX(hbox), b, TRUE, TRUE, 1);
386         menu_item->priv->screenshot_h = g_signal_connect(b, "clicked", G_CALLBACK(activate_video_item), menu_item);
387         // Button Picture
388         b = hildon_button_new(HILDON_SIZE_FINGER_HEIGHT, HILDON_BUTTON_ARRANGEMENT_VERTICAL);
389         gtk_widget_show(b);
390         icon = gtk_image_new_from_file(ICON_PICTURE);
391         gtk_widget_show(icon);
392         gtk_button_set_image(GTK_BUTTON(b), GTK_WIDGET(icon));
393         gtk_box_pack_start(GTK_BOX(hbox), b, TRUE, TRUE, 1);  
394         menu_item->priv->screenshot_h = g_signal_connect(b, "clicked", G_CALLBACK(activate_delayed_screenshot_item), menu_item);
395
396         // Pack widgets to menu
397         gtk_widget_show(hbox);
398         gtk_container_add(GTK_CONTAINER(menu_item), hbox);
399         gtk_widget_show(GTK_WIDGET(menu_item));
400         
401         // More init
402         menu_item->priv->recording = FALSE;
403 }
404
405
406