small fix to pupil coordinate calculation
[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   sina = nx / h;
85   cosa = ny / h;
86
87   temp = hypot ((eyes_applet->eye_width / 2) * sina, (eyes_applet->eye_height / 2) * cosa);
88   temp -= hypot ((eyes_applet->pupil_width / 2) * sina, (eyes_applet->pupil_height / 2) * cosa);
89   temp -= hypot ((eyes_applet->wall_thickness / 2) * sina, (eyes_applet->wall_thickness / 2) * cosa);
90
91   if ( hypot(temp*sina, temp*cosa) > hypot( nx , ny ) )
92     {
93       *pupil_x = nx + eyes_applet->eye_width / 2;
94       *pupil_y = ny + eyes_applet->eye_height / 2;
95     }
96   else 
97     {
98       *pupil_x = temp * sina + (eyes_applet->eye_width / 2);
99       *pupil_y = temp * cosa + (eyes_applet->eye_height / 2);
100     }
101 }
102
103 static void
104 draw_eye (EyesPluginContent *eyes,
105           gint    eye_num,
106           gint    pupil_x,
107           gint    pupil_y)
108 {
109   GdkPixbuf *pixbuf;
110   GdkRectangle rect, r1, r2;
111
112   pixbuf = gdk_pixbuf_copy (eyes->eye_image);
113   r1.x = pupil_x - eyes->pupil_width / 2;
114   r1.y = pupil_y - eyes->pupil_height / 2;
115   r1.width = eyes->pupil_width;
116   r1.height = eyes->pupil_height;
117   r2.x = 0;
118   r2.y = 0;
119   r2.width = eyes->eye_width;
120   r2.height = eyes->eye_height;
121   if (gdk_rectangle_intersect (&r1, &r2, &rect))
122     {
123       gdk_pixbuf_composite (eyes->pupil_image, pixbuf,
124                             rect.x,
125                             rect.y,
126                             rect.width,
127                             rect.height,
128                             pupil_x - eyes->pupil_width / 2,
129                             pupil_y - eyes->pupil_height / 2, 1.0, 1.0,
130                             GDK_INTERP_BILINEAR,
131                             255);
132       gtk_image_set_from_pixbuf (GTK_IMAGE (eyes->eyes[eye_num]),
133                                  pixbuf);
134     }
135   g_object_unref (G_OBJECT (pixbuf));
136 }
137
138 static gint
139 timer_cb(EyesPluginContent *eyes)
140 {
141   gint x, y, z;
142   gint pupil_x, pupil_y;
143   gint i;
144
145   for (i = 0; i < eyes->num_eyes; i++)
146     {
147       if (GTK_WIDGET_REALIZED(eyes->eyes[i]))
148         {
149           accel_read(&x, &y, &z);
150           calculate_pupil_xy (eyes, -x, -y, &pupil_x, &pupil_y, eyes->eyes[i]);
151
152           if ((pupil_x != eyes->pointer_last_x[i]) || (pupil_y != eyes->pointer_last_y[i]))
153             {
154
155               draw_eye (eyes, i, pupil_x, pupil_y);
156
157               eyes->pointer_last_x[i] = pupil_x;
158               eyes->pointer_last_y[i] = pupil_y;
159             }
160         }
161     }
162
163   return TRUE;
164 }
165
166 static void
167 properties_load(EyesPluginContent *eyes)
168 {
169   gchar *path;
170
171   if (eyes->active_theme)
172     path = g_build_filename(THEMESDIR, eyes->active_theme, NULL);
173   else
174     path = g_build_filename(THEMESDIR, DEFAULTTHEME, NULL);
175
176   load_theme(eyes, path);
177
178   g_free(path);
179 }
180
181
182
183 static void
184 setup_eyes(EyesPluginContent *eyes)
185 {
186   g_warning ("setup_eyes");
187   int i;
188
189   if (eyes->hbox != NULL)
190     {
191       gtk_widget_destroy(eyes->hbox);
192       eyes->hbox = NULL;
193     }
194
195   eyes->hbox = gtk_hbox_new(FALSE, 0);
196   gtk_container_add(GTK_CONTAINER(eyes->align), GTK_WIDGET(eyes->hbox));
197
198   eyes->eyes = g_new0 (GtkWidget *, eyes->num_eyes);
199   eyes->pointer_last_x = g_new0 (gint, eyes->num_eyes);
200   eyes->pointer_last_y = g_new0 (gint, eyes->num_eyes);
201
202   for (i = 0; i < eyes->num_eyes; i++)
203     {
204       eyes->eyes[i] = gtk_image_new ();
205
206       gtk_widget_set_size_request(GTK_WIDGET(eyes->eyes[i]),
207                                   eyes->eye_width,
208                                   eyes->eye_height);
209
210       gtk_widget_show(eyes->eyes[i]);
211
212       gtk_box_pack_start(GTK_BOX(eyes->hbox), eyes->eyes[i],
213                          FALSE, FALSE, 0);
214
215       if ((eyes->num_eyes != 1) && (i == 0))
216         gtk_misc_set_alignment (GTK_MISC (eyes->eyes[i]), 1.0, 0.5);
217       else if ((eyes->num_eyes != 1) && (i == eyes->num_eyes - 1))
218         gtk_misc_set_alignment (GTK_MISC (eyes->eyes[i]), 0.0, 0.5);
219       else
220         gtk_misc_set_alignment (GTK_MISC (eyes->eyes[i]), 0.5, 0.5);
221
222       eyes->pointer_last_x[i] = G_MAXINT;
223       eyes->pointer_last_y[i] = G_MAXINT;
224
225       draw_eye (eyes, i,
226                 eyes->eye_width / 2,
227                 eyes->eye_height / 2);
228     }
229
230   gtk_widget_show(eyes->hbox);
231 }
232
233
234
235 static gboolean
236 eyes_applet_fill(EyesPluginContent *eyes)
237 {
238   gtk_widget_show_all(GTK_WIDGET(eyes->align));
239
240   if (eyes->timeout_id == 0)
241     {
242       eyes->timeout_id = g_timeout_add (UPDATE_TIMEOUT,
243                                         (GtkFunction)timer_cb, eyes);
244     }
245
246   return TRUE;
247 }
248
249 /******************************
250  *** Panel Plugin Functions ***
251  ******************************/
252 static void
253 eyes_free_data(EyesPluginContent      *eyes)
254 {
255   g_warning("eyes_free_data");
256   g_return_if_fail(eyes != NULL);
257
258   if (eyes->timeout_id != 0)
259     g_source_remove (eyes->timeout_id);
260
261   g_free (eyes->eyes);
262   g_free (eyes->pointer_last_x);
263   g_free (eyes->pointer_last_y);
264
265   if (eyes->active_theme != NULL)
266     g_free (eyes->active_theme);
267
268   if (eyes->eye_image != NULL)
269     g_object_unref (G_OBJECT (eyes->eye_image));
270
271   if (eyes->pupil_image != NULL)
272     g_object_unref (G_OBJECT (eyes->pupil_image));
273
274   if (eyes->theme_dir != NULL)
275     g_free(eyes->theme_dir);
276
277   if (eyes->theme_name != NULL)
278     g_free(eyes->theme_name);
279
280   if (eyes->eye_filename != NULL)
281     g_free(eyes->eye_filename);
282
283   if (eyes->pupil_filename != NULL)
284     g_free(eyes->pupil_filename);
285
286   if (eyes->proxy != NULL)
287     dbus_g_proxy_disconnect_signal (eyes->proxy, MCE_DISPLAY_SIG, G_CALLBACK(eyes_check_display), eyes);
288   
289   g_free(eyes);
290 }
291
292 static void
293 eyes_read_rc_file (EyesPluginContent      *eyes)
294 {
295   g_warning ("eyes_read_rc");
296   if (eyes->active_theme == NULL)
297     eyes->active_theme = g_strdup (DEFAULTTHEME);
298 }
299
300 static EyesPluginContent *
301 eyes_plugin_new ()
302 {
303   g_warning ("eyes_plugin_new");
304   EyesPluginContent *eyes;
305
306   eyes = g_new0(EyesPluginContent, 1);
307
308   eyes->align = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
309
310   gtk_widget_show(GTK_WIDGET(eyes->align));
311
312   eyes_read_rc_file (eyes);
313
314   properties_load(eyes);
315   setup_eyes(eyes);
316   eyes_applet_fill(eyes);
317
318   eyes->visible = TRUE;
319
320   return eyes;
321 }
322
323 static void
324 eyes_check_desktop (GObject *gobject, GParamSpec *pspec, EyesPluginContent *eyes)
325
326 {
327   g_warning ("eyes_check_desktop");
328   gchar *name = pspec->name;
329   gboolean status;
330   g_object_get (gobject, name, &status, NULL);
331   if (status) {
332     eyes_applet_fill(eyes);
333     eyes->visible = TRUE;
334   } else if (eyes->timeout_id != 0) {
335     g_source_remove (eyes->timeout_id);
336     eyes->timeout_id = 0;
337     eyes->visible = FALSE;
338   }
339 }
340
341 static void
342 eyes_check_display(DBusGProxy *object, const char *status, EyesPluginContent *eyes)
343 {
344   if (strncmp(status, MCE_DISPLAY_ON_STRING,
345               strlen(MCE_DISPLAY_ON_STRING) + 1) == 0
346       && eyes-> visible)
347     {
348       eyes_applet_fill(eyes);
349     }
350   else if (strncmp(status, MCE_DISPLAY_OFF_STRING,
351                    strlen(MCE_DISPLAY_OFF_STRING) + 1) == 0)
352     {
353       if (eyes->timeout_id != 0)
354         {
355           g_source_remove (eyes->timeout_id);
356           eyes->timeout_id = 0;
357         }
358     }
359   
360 }
361
362 static void
363 eyes_plugin_dispose (GObject *object)
364 {
365   g_warning ("eyes_plugin_dispose");
366
367   G_OBJECT_CLASS (eyes_plugin_parent_class)->dispose (object);
368 }
369
370 static void eyes_plugin_finalize (GObject *object)
371 {
372   g_warning ("eyes_plugin_finalize");
373   EyesPlugin *self = EYES_HOME_PLUGIN (object);
374
375   eyes_free_data(self->priv);
376
377   G_OBJECT_CLASS (eyes_plugin_parent_class)->finalize (object);
378 }
379
380 static void
381 eyes_plugin_realize (GtkWidget *widget)
382 {
383   g_warning ("eyes_plugin_realize");
384   GdkScreen *screen = gtk_widget_get_screen (widget);
385   gtk_widget_set_colormap (widget, gdk_screen_get_rgba_colormap (screen));
386   gtk_widget_set_app_paintable (widget, TRUE);
387
388   GTK_WIDGET_CLASS (eyes_plugin_parent_class)->realize (widget);
389 }
390
391 static gboolean
392 eyes_plugin_expose_event (GtkWidget *widget, GdkEventExpose *event)
393 {
394   g_warning ("eyes_plugin_expose_event");
395   cairo_t *cr;
396
397   cr = gdk_cairo_create (GDK_DRAWABLE (widget->window));
398   gdk_cairo_region (cr, event->region);
399   cairo_clip (cr);
400
401   cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
402   cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.0);
403   cairo_paint (cr);
404   
405   cairo_destroy (cr);
406
407   return GTK_WIDGET_CLASS (eyes_plugin_parent_class)->expose_event (widget, event);
408 }
409
410 static void
411 eyes_plugin_init (EyesPlugin *desktop_plugin)
412 {
413   g_warning ("eyes_plugin_init");
414   EyesPluginContent *eyes;
415
416   eyes = eyes_plugin_new (desktop_plugin);
417   desktop_plugin->priv = eyes;
418
419   g_signal_connect (desktop_plugin, "notify::is-on-current-desktop",
420                     G_CALLBACK (eyes_check_desktop), eyes);
421
422   eyes->dbus_conn = NULL;
423   eyes->proxy = NULL;
424
425   eyes->dbus_conn = hd_home_plugin_item_get_dbus_g_connection ( &desktop_plugin->hitem, DBUS_BUS_SYSTEM, NULL );
426   if (eyes->dbus_conn != NULL)
427     {
428       eyes->proxy = dbus_g_proxy_new_for_name(eyes->dbus_conn, MCE_SERVICE, MCE_SIGNAL_PATH, MCE_SIGNAL_IF);
429       dbus_g_proxy_add_signal (eyes->proxy, MCE_DISPLAY_SIG, G_TYPE_STRING, G_TYPE_INVALID );
430       dbus_g_proxy_connect_signal(eyes->proxy, MCE_DISPLAY_SIG, G_CALLBACK(eyes_check_display),eyes,NULL);
431     }
432   
433   gtk_container_add (GTK_CONTAINER (desktop_plugin), eyes->align);
434
435
436 static void
437 eyes_plugin_class_init (EyesPluginClass *klass) {
438   g_warning ("eyes_plugin_class_init");
439   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
440   GObjectClass *object_class = G_OBJECT_CLASS (klass);
441
442   object_class->dispose = eyes_plugin_dispose;
443   object_class->finalize = eyes_plugin_finalize;
444
445   widget_class->realize = eyes_plugin_realize;
446   widget_class->expose_event = eyes_plugin_expose_event;
447   g_type_class_add_private (klass, sizeof (EyesPluginContent));
448
449
450 static void
451 eyes_plugin_class_finalize (EyesPluginClass *class) {} 
452