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