4592ca45e730f219827b6b35178b62ce73e2d13b
[eyes-widget] / src / eyes.c
1 /* $Id: eyes.c 2397 2007-01-17 17:46:35Z nick $
2  * 
3  * Copyright (c) Benedikt Meurer <benedikt.meurer@unix-ag.uni-siegen.de>>
4  * Copyright (c) Danny Milosavljevic <danny_milo@gmx.net>
5  * Copyright (c) Dave Camp
6  * Copyright (c) Davyd Madeley  <davyd@madeley.id.au>
7  * Copyright (c) Nick Schermer <nick@xfce.org>
8  * Copyright (c) Mikko Vartiainen <mvartiainen@gmail.com>
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU General Public License as published by the Free
12  * Software Foundation; either version 2 of the License, or (at your option)
13  * any later version.
14  *
15  * This program is distributed in the hope that it will be useful, but WITHOUT
16  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
18  * more details.
19  *
20  * You should have received a copy of the GNU General Public License along with
21  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
22  * Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #ifdef HAVE_MATH_H
30 #include <math.h>
31 #endif
32
33 #include <sys/types.h>
34 #include <dirent.h>
35 #include <string.h>
36
37 #include <libhildondesktop/libhildondesktop.h>
38 #include <mce/dbus-names.h>
39 #include <mce/mode-names.h>
40
41 #include "eyes.h"
42 #include "themes.h"
43 #include "accelerometer.h"
44
45 /* for xml: */
46 #define EYES_ROOT      "Eyes"
47 #define DEFAULTTHEME   "Default"
48 #define UPDATE_TIMEOUT 100
49
50 #define EYES_PLUGIN_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE (obj, \
51                                                                    EYES_TYPE_HOME_PLUGIN, \
52                                                                    EyesPluginContent))
53
54 HD_DEFINE_PLUGIN_MODULE (EyesPlugin, eyes_plugin,      HD_TYPE_HOME_PLUGIN_ITEM)
55
56 static void eyes_check_display(DBusGProxy *object, const char *status, EyesPluginContent *eyes);
57
58 /*****************************
59  *** Eyes Plugin Functions ***
60  *****************************/
61 static void
62 calculate_pupil_xy (EyesPluginContent *eyes_applet,
63                     gint x, gint y,
64                     gint *pupil_x, gint *pupil_y, GtkWidget* widget)
65 {
66   double sina;
67   double cosa;
68   double h;
69   double temp;
70   double nx, ny;
71
72   gfloat xalign, yalign;
73   gint width, height;
74
75   width = GTK_WIDGET(widget)->allocation.width;
76   height = GTK_WIDGET(widget)->allocation.height;
77   gtk_misc_get_alignment(GTK_MISC(widget),  &xalign, &yalign);
78
79   nx = width*x/1000.0;
80   ny = height*y/1000.0;
81
82   h = hypot (nx, ny);
83
84   if ( nx*nx/((eyes_applet->eye_width/2.0 - eyes_applet->pupil_width / 2)*(eyes_applet->eye_width/2.0 - eyes_applet->pupil_width/2)) + ny*ny/((eyes_applet->eye_height/2 - eyes_applet->pupil_height/2)*(eyes_applet->eye_height/2 - eyes_applet->pupil_height/2)) < 1 )
85     {
86       *pupil_x = nx + eyes_applet->eye_width / 2;
87       *pupil_y = ny + eyes_applet->eye_height / 2;
88       return;
89     }
90
91   sina = nx / h;
92   cosa = ny / h;
93
94   temp = hypot ((eyes_applet->eye_width / 2) * sina, (eyes_applet->eye_height / 2) * cosa);
95   temp -= hypot ((eyes_applet->pupil_width / 2) * sina, (eyes_applet->pupil_height / 2) * cosa);
96   temp -= hypot ((eyes_applet->wall_thickness / 2) * sina, (eyes_applet->wall_thickness / 2) * cosa);
97
98   *pupil_x = temp * sina + (eyes_applet->eye_width / 2);
99   *pupil_y = temp * cosa + (eyes_applet->eye_height / 2);
100 }
101
102 static void
103 draw_eye (EyesPluginContent *eyes,
104           gint    eye_num,
105           gint    pupil_x,
106           gint    pupil_y)
107 {
108   GdkPixbuf *pixbuf;
109   GdkRectangle rect, r1, r2;
110
111   pixbuf = gdk_pixbuf_copy (eyes->eye_image);
112   r1.x = pupil_x - eyes->pupil_width / 2;
113   r1.y = pupil_y - eyes->pupil_height / 2;
114   r1.width = eyes->pupil_width;
115   r1.height = eyes->pupil_height;
116   r2.x = 0;
117   r2.y = 0;
118   r2.width = eyes->eye_width;
119   r2.height = eyes->eye_height;
120   if (gdk_rectangle_intersect (&r1, &r2, &rect))
121     {
122       gdk_pixbuf_composite (eyes->pupil_image, pixbuf,
123                             rect.x,
124                             rect.y,
125                             rect.width,
126                             rect.height,
127                             pupil_x - eyes->pupil_width / 2,
128                             pupil_y - eyes->pupil_height / 2, 1.0, 1.0,
129                             GDK_INTERP_BILINEAR,
130                             255);
131       gtk_image_set_from_pixbuf (GTK_IMAGE (eyes->eyes[eye_num]),
132                                  pixbuf);
133     }
134   g_object_unref (G_OBJECT (pixbuf));
135 }
136
137 static gint
138 timer_cb(EyesPluginContent *eyes)
139 {
140   gint x, y, z;
141   gint pupil_x, pupil_y;
142   gint i;
143
144   for (i = 0; i < eyes->num_eyes; i++)
145     {
146       if (GTK_WIDGET_REALIZED(eyes->eyes[i]))
147         {
148           accel_read(&x, &y, &z);
149           x = -x;
150           y = -y;
151
152           if ((x != eyes->pointer_last_x[i]) || (y != eyes->pointer_last_y[i]))
153             {
154
155               calculate_pupil_xy (eyes, x, y, &pupil_x, &pupil_y, eyes->eyes[i]);
156               draw_eye (eyes, i, pupil_x, pupil_y);
157
158               eyes->pointer_last_x[i] = x;
159               eyes->pointer_last_y[i] = y;
160             }
161         }
162     }
163
164   return TRUE;
165 }
166
167 static void
168 properties_load(EyesPluginContent *eyes)
169 {
170   gchar *path;
171
172   if (eyes->active_theme)
173     path = g_build_filename(THEMESDIR, eyes->active_theme, NULL);
174   else
175     path = g_build_filename(THEMESDIR, DEFAULTTHEME, NULL);
176
177   load_theme(eyes, path);
178
179   g_free(path);
180 }
181
182
183
184 static void
185 setup_eyes(EyesPluginContent *eyes)
186 {
187   g_warning ("setup_eyes");
188   int i;
189
190   if (eyes->hbox != NULL)
191     {
192       gtk_widget_destroy(eyes->hbox);
193       eyes->hbox = NULL;
194     }
195
196   eyes->hbox = gtk_hbox_new(FALSE, 0);
197   gtk_container_add(GTK_CONTAINER(eyes->align), GTK_WIDGET(eyes->hbox));
198
199   eyes->eyes = g_new0 (GtkWidget *, eyes->num_eyes);
200   eyes->pointer_last_x = g_new0 (gint, eyes->num_eyes);
201   eyes->pointer_last_y = g_new0 (gint, eyes->num_eyes);
202
203   for (i = 0; i < eyes->num_eyes; i++)
204     {
205       eyes->eyes[i] = gtk_image_new ();
206
207       gtk_widget_set_size_request(GTK_WIDGET(eyes->eyes[i]),
208                                   eyes->eye_width,
209                                   eyes->eye_height);
210
211       gtk_widget_show(eyes->eyes[i]);
212
213       gtk_box_pack_start(GTK_BOX(eyes->hbox), eyes->eyes[i],
214                          FALSE, FALSE, 0);
215
216       if ((eyes->num_eyes != 1) && (i == 0))
217         gtk_misc_set_alignment (GTK_MISC (eyes->eyes[i]), 1.0, 0.5);
218       else if ((eyes->num_eyes != 1) && (i == eyes->num_eyes - 1))
219         gtk_misc_set_alignment (GTK_MISC (eyes->eyes[i]), 0.0, 0.5);
220       else
221         gtk_misc_set_alignment (GTK_MISC (eyes->eyes[i]), 0.5, 0.5);
222
223       eyes->pointer_last_x[i] = G_MAXINT;
224       eyes->pointer_last_y[i] = G_MAXINT;
225
226       draw_eye (eyes, i,
227                 eyes->eye_width / 2,
228                 eyes->eye_height / 2);
229     }
230
231   gtk_widget_show(eyes->hbox);
232 }
233
234
235
236 static gboolean
237 eyes_applet_fill(EyesPluginContent *eyes)
238 {
239   gtk_widget_show_all(GTK_WIDGET(eyes->align));
240
241   if (eyes->timeout_id == 0)
242     {
243       eyes->timeout_id = g_timeout_add (UPDATE_TIMEOUT,
244                                         (GtkFunction)timer_cb, eyes);
245     }
246
247   return TRUE;
248 }
249
250 /******************************
251  *** Panel Plugin Functions ***
252  ******************************/
253 static void
254 eyes_free_data(EyesPluginContent      *eyes)
255 {
256   g_warning("eyes_free_data");
257   g_return_if_fail(eyes != NULL);
258
259   if (eyes->timeout_id != 0)
260     g_source_remove (eyes->timeout_id);
261
262   g_free (eyes->eyes);
263   g_free (eyes->pointer_last_x);
264   g_free (eyes->pointer_last_y);
265
266   if (eyes->active_theme != NULL)
267     g_free (eyes->active_theme);
268
269   if (eyes->eye_image != NULL)
270     g_object_unref (G_OBJECT (eyes->eye_image));
271
272   if (eyes->pupil_image != NULL)
273     g_object_unref (G_OBJECT (eyes->pupil_image));
274
275   if (eyes->theme_dir != NULL)
276     g_free(eyes->theme_dir);
277
278   if (eyes->theme_name != NULL)
279     g_free(eyes->theme_name);
280
281   if (eyes->eye_filename != NULL)
282     g_free(eyes->eye_filename);
283
284   if (eyes->pupil_filename != NULL)
285     g_free(eyes->pupil_filename);
286
287   if (eyes->proxy != NULL)
288     dbus_g_proxy_disconnect_signal (eyes->proxy, MCE_DISPLAY_SIG, G_CALLBACK(eyes_check_display), eyes);
289   
290   g_free(eyes);
291 }
292
293 static void
294 eyes_read_rc_file (EyesPluginContent      *eyes)
295 {
296   g_warning ("eyes_read_rc");
297   if (eyes->active_theme == NULL)
298     eyes->active_theme = g_strdup (DEFAULTTHEME);
299 }
300
301 static EyesPluginContent *
302 eyes_plugin_new ()
303 {
304   g_warning ("eyes_plugin_new");
305   EyesPluginContent *eyes;
306
307   eyes = g_new0(EyesPluginContent, 1);
308
309   eyes->align = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
310
311   gtk_widget_show(GTK_WIDGET(eyes->align));
312
313   eyes_read_rc_file (eyes);
314
315   properties_load(eyes);
316   setup_eyes(eyes);
317   eyes_applet_fill(eyes);
318
319   eyes->visible = TRUE;
320
321   return eyes;
322 }
323
324 static void
325 eyes_check_desktop (GObject *gobject, GParamSpec *pspec, EyesPluginContent *eyes)
326
327 {
328   g_warning ("eyes_check_desktop");
329   gchar *name = pspec->name;
330   gboolean status;
331   g_object_get (gobject, name, &status, NULL);
332   if (status) {
333     eyes_applet_fill(eyes);
334     eyes->visible = TRUE;
335   } else if (eyes->timeout_id != 0) {
336     g_source_remove (eyes->timeout_id);
337     eyes->timeout_id = 0;
338     eyes->visible = FALSE;
339   }
340 }
341
342 static void
343 eyes_check_display(DBusGProxy *object, const char *status, EyesPluginContent *eyes)
344 {
345   if (strncmp(status, MCE_DISPLAY_ON_STRING,
346               strlen(MCE_DISPLAY_ON_STRING) + 1) == 0
347       && eyes-> visible)
348     {
349       eyes_applet_fill(eyes);
350     }
351   else if (strncmp(status, MCE_DISPLAY_OFF_STRING,
352                    strlen(MCE_DISPLAY_OFF_STRING) + 1) == 0)
353     {
354       if (eyes->timeout_id != 0)
355         {
356           g_source_remove (eyes->timeout_id);
357           eyes->timeout_id = 0;
358         }
359     }
360   
361 }
362
363 static void
364 eyes_plugin_dispose (GObject *object)
365 {
366   g_warning ("eyes_plugin_dispose");
367
368   G_OBJECT_CLASS (eyes_plugin_parent_class)->dispose (object);
369 }
370
371 static void eyes_plugin_finalize (GObject *object)
372 {
373   g_warning ("eyes_plugin_finalize");
374   EyesPlugin *self = EYES_HOME_PLUGIN (object);
375
376   eyes_free_data(self->priv);
377
378   G_OBJECT_CLASS (eyes_plugin_parent_class)->finalize (object);
379 }
380
381 static void
382 eyes_plugin_realize (GtkWidget *widget)
383 {
384   g_warning ("eyes_plugin_realize");
385   GdkScreen *screen = gtk_widget_get_screen (widget);
386   gtk_widget_set_colormap (widget, gdk_screen_get_rgba_colormap (screen));
387   gtk_widget_set_app_paintable (widget, TRUE);
388
389   GTK_WIDGET_CLASS (eyes_plugin_parent_class)->realize (widget);
390 }
391
392 static gboolean
393 eyes_plugin_expose_event (GtkWidget *widget, GdkEventExpose *event)
394 {
395   g_warning ("eyes_plugin_expose_event");
396   cairo_t *cr;
397
398   cr = gdk_cairo_create (GDK_DRAWABLE (widget->window));
399   gdk_cairo_region (cr, event->region);
400   cairo_clip (cr);
401
402   cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
403   cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.0);
404   cairo_paint (cr);
405   
406   cairo_destroy (cr);
407
408   return GTK_WIDGET_CLASS (eyes_plugin_parent_class)->expose_event (widget, event);
409 }
410
411 static void
412 eyes_plugin_init (EyesPlugin *desktop_plugin)
413 {
414   g_warning ("eyes_plugin_init");
415   EyesPluginContent *eyes;
416
417   eyes = eyes_plugin_new (desktop_plugin);
418   desktop_plugin->priv = eyes;
419
420   g_signal_connect (desktop_plugin, "notify::is-on-current-desktop",
421                     G_CALLBACK (eyes_check_desktop), eyes);
422
423   eyes->dbus_conn = NULL;
424   eyes->proxy = NULL;
425
426   eyes->dbus_conn = hd_home_plugin_item_get_dbus_g_connection ( &desktop_plugin->hitem, DBUS_BUS_SYSTEM, NULL );
427   if (eyes->dbus_conn != NULL)
428     {
429       eyes->proxy = dbus_g_proxy_new_for_name(eyes->dbus_conn, MCE_SERVICE, MCE_SIGNAL_PATH, MCE_SIGNAL_IF);
430       dbus_g_proxy_add_signal (eyes->proxy, MCE_DISPLAY_SIG, G_TYPE_STRING, G_TYPE_INVALID );
431       dbus_g_proxy_connect_signal(eyes->proxy, MCE_DISPLAY_SIG, G_CALLBACK(eyes_check_display),eyes,NULL);
432     }
433   
434   gtk_container_add (GTK_CONTAINER (desktop_plugin), eyes->align);
435
436
437 static void
438 eyes_plugin_class_init (EyesPluginClass *klass) {
439   g_warning ("eyes_plugin_class_init");
440   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
441   GObjectClass *object_class = G_OBJECT_CLASS (klass);
442
443   object_class->dispose = eyes_plugin_dispose;
444   object_class->finalize = eyes_plugin_finalize;
445
446   widget_class->realize = eyes_plugin_realize;
447   widget_class->expose_event = eyes_plugin_expose_event;
448   g_type_class_add_private (klass, sizeof (EyesPluginContent));
449
450
451 static void
452 eyes_plugin_class_finalize (EyesPluginClass *class) {} 
453