90772d2c18c9aad566371ef366ba77725bc9f690
[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
39 #include "eyes.h"
40 #include "themes.h"
41 #include "accelerometer.h"
42
43 /* for xml: */
44 #define EYES_ROOT      "Eyes"
45 #define DEFAULTTHEME   "Default"
46 #define UPDATE_TIMEOUT 100
47
48 #define EYES_PLUGIN_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE (obj,\
49                                                                                  EYES_TYPE_HOME_PLUGIN,\
50                                                                                  EyesPluginContent))
51
52 HD_DEFINE_PLUGIN_MODULE (EyesPlugin, eyes_plugin,      HD_TYPE_HOME_PLUGIN_ITEM)
53
54 /*****************************
55  *** Eyes Plugin Functions ***
56  *****************************/
57 static void
58 calculate_pupil_xy (EyesPluginContent *eyes_applet,
59                     gint x, gint y,
60                     gint *pupil_x, gint *pupil_y, GtkWidget* widget)
61 {
62         double sina;
63         double cosa;
64         double h;
65         double temp;
66         double nx, ny;
67
68         gfloat xalign, yalign;
69         gint width, height;
70
71         width = GTK_WIDGET(widget)->allocation.width;
72         height = GTK_WIDGET(widget)->allocation.height;
73         gtk_misc_get_alignment(GTK_MISC(widget),  &xalign, &yalign);
74
75     nx = width*x/1000.0;
76     ny = height*y/1000.0;
77
78         h = hypot (nx, ny);
79
80     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 )
81         {
82                         *pupil_x = nx + eyes_applet->eye_width / 2;
83                         *pupil_y = ny + eyes_applet->eye_height / 2;
84                         return;
85         }
86
87         sina = nx / h;
88         cosa = ny / h;
89
90         temp = hypot ((eyes_applet->eye_width / 2) * sina, (eyes_applet->eye_height / 2) * cosa);
91         temp -= hypot ((eyes_applet->pupil_width / 2) * sina, (eyes_applet->pupil_height / 2) * cosa);
92         temp -= hypot ((eyes_applet->wall_thickness / 2) * sina, (eyes_applet->wall_thickness / 2) * cosa);
93
94         *pupil_x = temp * sina + (eyes_applet->eye_width / 2);
95         *pupil_y = temp * cosa + (eyes_applet->eye_height / 2);
96 }
97
98
99
100 static void
101 draw_eye (EyesPluginContent *eyes,
102           gint    eye_num,
103           gint    pupil_x,
104           gint    pupil_y)
105 {
106     GdkPixbuf *pixbuf;
107     GdkRectangle rect, r1, r2;
108
109     pixbuf = gdk_pixbuf_copy (eyes->eye_image);
110     r1.x = pupil_x - eyes->pupil_width / 2;
111     r1.y = pupil_y - eyes->pupil_height / 2;
112     r1.width = eyes->pupil_width;
113     r1.height = eyes->pupil_height;
114     r2.x = 0;
115     r2.y = 0;
116     r2.width = eyes->eye_width;
117     r2.height = eyes->eye_height;
118     if (gdk_rectangle_intersect (&r1, &r2, &rect))
119     {
120         gdk_pixbuf_composite (eyes->pupil_image, pixbuf,
121                            rect.x,
122                            rect.y,
123                            rect.width,
124                                  rect.height,
125                                  pupil_x - eyes->pupil_width / 2,
126                            pupil_y - eyes->pupil_height / 2, 1.0, 1.0,
127                                  GDK_INTERP_BILINEAR,
128                                255);
129         gtk_image_set_from_pixbuf (GTK_IMAGE (eyes->eyes[eye_num]),
130                               pixbuf);
131     }
132     g_object_unref (G_OBJECT (pixbuf));
133 }
134
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
168
169 static void
170 properties_load(EyesPluginContent *eyes)
171 {
172     gchar *path;
173
174     if (eyes->active_theme)
175         path = g_build_filename(THEMESDIR, eyes->active_theme, NULL);
176     else
177         path = g_build_filename(THEMESDIR, DEFAULTTHEME, NULL);
178
179     load_theme(eyes, path);
180
181     g_free(path);
182 }
183
184
185
186 static void
187 setup_eyes(EyesPluginContent *eyes)
188 {
189     g_warning ("setup_eyes");
190     int i;
191
192     if (eyes->hbox != NULL)
193     {
194         gtk_widget_destroy(eyes->hbox);
195         eyes->hbox = NULL;
196     }
197
198     eyes->hbox = gtk_hbox_new(FALSE, 0);
199     gtk_container_add(GTK_CONTAINER(eyes->align), GTK_WIDGET(eyes->hbox));
200
201     eyes->eyes = g_new0 (GtkWidget *, eyes->num_eyes);
202         eyes->pointer_last_x = g_new0 (gint, eyes->num_eyes);
203         eyes->pointer_last_y = g_new0 (gint, eyes->num_eyes);
204
205     for (i = 0; i < eyes->num_eyes; i++)
206     {
207         eyes->eyes[i] = gtk_image_new ();
208
209         gtk_widget_set_size_request(GTK_WIDGET(eyes->eyes[i]),
210                                     eyes->eye_width,
211                                     eyes->eye_height);
212
213         gtk_widget_show(eyes->eyes[i]);
214
215         gtk_box_pack_start(GTK_BOX(eyes->hbox), eyes->eyes[i],
216                            FALSE, FALSE, 0);
217
218                 if ((eyes->num_eyes != 1) && (i == 0))
219             gtk_misc_set_alignment (GTK_MISC (eyes->eyes[i]), 1.0, 0.5);
220                 else if ((eyes->num_eyes != 1) && (i == eyes->num_eyes - 1))
221                         gtk_misc_set_alignment (GTK_MISC (eyes->eyes[i]), 0.0, 0.5);
222                 else
223                         gtk_misc_set_alignment (GTK_MISC (eyes->eyes[i]), 0.5, 0.5);
224
225                 eyes->pointer_last_x[i] = G_MAXINT;
226                 eyes->pointer_last_y[i] = G_MAXINT;
227
228                 draw_eye (eyes, i,
229                               eyes->eye_width / 2,
230                   eyes->eye_height / 2);
231     }
232
233     gtk_widget_show(eyes->hbox);
234 }
235
236
237
238 static gboolean
239 eyes_applet_fill(EyesPluginContent *eyes)
240 {
241     gtk_widget_show_all(GTK_WIDGET(eyes->align));
242
243     if (eyes->timeout_id == 0)
244     {
245         eyes->timeout_id = g_timeout_add (UPDATE_TIMEOUT,
246                                           (GtkFunction)timer_cb, eyes);
247     }
248
249     return TRUE;
250 }
251
252
253
254
255 /*
256 static void
257 combobox_changed (GtkComboBox    *combobox,
258                   EyesPluginContent     *eyes)
259 {
260         gchar *selected = gtk_combo_box_get_active_text (combobox);
261
262         if (eyes->active_theme)
263                 g_free (eyes->active_theme);
264
265         eyes->active_theme = g_strdup (selected);
266         g_free (selected);
267
268         properties_load(eyes);
269     setup_eyes(eyes);
270     eyes_applet_fill(eyes);
271 }
272 */
273
274 /*
275 static void
276 eyes_properties_dialog (XfcePanelPlugin *plugin,
277                         EyesPluginContent      *eyes)
278 {
279         GtkWidget   *dlg, *hbox, *label, *combobox;
280         GDir        *dir;
281         gint         i;
282         gchar       *current;
283         const gchar *entry;
284
285         xfce_panel_plugin_block_menu (plugin);
286
287         dlg = xfce_titled_dialog_new_with_buttons (_("Eyes"),
288                                                   GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (plugin))),
289                                                   GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
290                                                   GTK_STOCK_CLOSE, GTK_RESPONSE_OK,
291                                                   NULL);
292
293     gtk_window_set_position   (GTK_WINDOW (dlg), GTK_WIN_POS_CENTER);
294     gtk_window_set_icon_name  (GTK_WINDOW (dlg), "xfce4-settings");
295
296     g_signal_connect (dlg, "response", G_CALLBACK (eyes_properties_dialog_response),
297                       eyes);
298
299         hbox = gtk_hbox_new(FALSE, 6);
300         gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), hbox, FALSE, FALSE, 0);
301         gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);
302
303         label = gtk_label_new_with_mnemonic (_("_Select a theme:"));
304         gtk_box_pack_start (GTK_BOX(hbox), label, FALSE, FALSE, 0);
305
306         combobox = gtk_combo_box_new_text ();
307     gtk_box_pack_start (GTK_BOX (hbox), combobox, FALSE, TRUE, 0);
308
309     if (eyes->active_theme)
310                 current = g_strdup (eyes->active_theme);
311         else
312                 current = g_strdup (DEFAULTTHEME);
313
314     if ((dir = g_dir_open(THEMESDIR, 0, NULL)) == NULL)
315     {
316         gtk_combo_box_append_text (GTK_COMBO_BOX (combobox), current);
317     }
318     else
319     {
320         for (i = 0; (entry = g_dir_read_name(dir)) != NULL; i++)
321         {
322             gtk_combo_box_append_text (GTK_COMBO_BOX (combobox), entry);
323
324             if (strcmp (entry, current) == 0)
325                                 gtk_combo_box_set_active (GTK_COMBO_BOX(combobox), i);
326                 }
327
328         g_dir_close(dir);
329         }
330
331         g_free (current);
332
333         gtk_label_set_mnemonic_widget (GTK_LABEL (label), combobox);
334
335     g_signal_connect(G_OBJECT(combobox), "changed",
336             G_CALLBACK(combobox_changed), eyes);
337
338     gtk_widget_show_all (dlg);
339 }
340 */
341
342
343 /******************************
344  *** Panel Plugin Functions ***
345  ******************************/
346 static void
347 eyes_free_data(EyesPluginContent      *eyes)
348 {
349     g_warning("eyes_free_data");
350     g_return_if_fail(eyes != NULL);
351
352     if (eyes->timeout_id != 0)
353         g_source_remove (eyes->timeout_id);
354
355     g_free (eyes->eyes);
356         g_free (eyes->pointer_last_x);
357         g_free (eyes->pointer_last_y);
358
359         if (eyes->active_theme != NULL)
360                 g_free (eyes->active_theme);
361
362     if (eyes->eye_image != NULL)
363         g_object_unref (G_OBJECT (eyes->eye_image));
364
365     if (eyes->pupil_image != NULL)
366         g_object_unref (G_OBJECT (eyes->pupil_image));
367
368     if (eyes->theme_dir != NULL)
369         g_free(eyes->theme_dir);
370
371     if (eyes->theme_name != NULL)
372         g_free(eyes->theme_name);
373
374     if (eyes->eye_filename != NULL)
375         g_free(eyes->eye_filename);
376
377     if (eyes->pupil_filename != NULL)
378         g_free(eyes->pupil_filename);
379
380     g_free(eyes);
381 }
382
383 static void
384 eyes_read_rc_file (EyesPluginContent      *eyes)
385 {
386     g_warning ("eyes_read_rc");
387 /*    XfceRc      *rc;
388     gchar       *file;
389     gchar const *tmp;
390
391     if (eyes->active_theme != NULL)
392     {
393         g_free (eyes->active_theme);
394         eyes->active_theme = NULL;
395     }
396
397     if ((file = xfce_panel_plugin_lookup_rc_file (plugin)) != NULL)
398     {
399         rc = xfce_rc_simple_open (file, TRUE);
400         g_free (file);
401
402         if (rc != NULL)
403         {
404             tmp = xfce_rc_read_entry (rc, "theme", DEFAULTTHEME);
405
406             if (tmp != NULL)
407                 eyes->active_theme = g_strdup (tmp);
408
409             xfce_rc_close (rc);
410         }
411     }
412 */
413     if (eyes->active_theme == NULL)
414                 eyes->active_theme = g_strdup (DEFAULTTHEME);
415 }
416
417 /*
418 static void
419 eyes_write_rc_file (XfcePanelPlugin *plugin,
420                     EyesPluginContent      *eyes)
421 {
422     gchar  *file;
423     XfceRc *rc;
424
425     if (!(file = xfce_panel_plugin_save_location (plugin, TRUE)))
426         return;
427
428     rc = xfce_rc_simple_open (file, FALSE);
429     g_free (file);
430
431     if (!rc)
432         return;
433
434     if (eyes->active_theme != NULL)
435         xfce_rc_write_entry (rc, "theme", eyes->active_theme);
436
437     xfce_rc_close (rc);
438 }
439 */
440
441 static EyesPluginContent *
442 eyes_plugin_new ()
443 {
444     g_warning ("eyes_plugin_new");
445     EyesPluginContent *eyes;
446
447     eyes = g_new0(EyesPluginContent, 1);
448
449     eyes->align = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
450
451     gtk_widget_show(GTK_WIDGET(eyes->align));
452
453     eyes_read_rc_file (eyes);
454
455     properties_load(eyes);
456     setup_eyes(eyes);
457     eyes_applet_fill(eyes);
458
459     return eyes;
460 }
461
462 static void eyes_check_desktop (GObject *gobject, GParamSpec *pspec, EyesPluginContent *eyes)
463 {
464         g_warning ("eyes_check_desktop");
465         gchar *name = pspec->name;
466         gboolean status;
467         g_object_get (gobject, name, &status, NULL);
468         if (status) {
469             eyes_applet_fill(eyes);
470         } else if (eyes->timeout_id != 0) {
471         g_source_remove (eyes->timeout_id);
472                 eyes->timeout_id = 0;
473         }
474 }
475
476 static void eyes_plugin_dispose (GObject *object)
477 {
478         g_warning ("eyes_plugin_dispose");
479
480         G_OBJECT_CLASS (eyes_plugin_parent_class)->dispose (object);
481 }
482
483 static void eyes_plugin_finalize (GObject *object)
484 {
485         g_warning ("eyes_plugin_finalize");
486         EyesPlugin *self = EYES_HOME_PLUGIN (object);
487
488     eyes_free_data(self->priv);
489
490         G_OBJECT_CLASS (eyes_plugin_parent_class)->finalize (object);
491 }
492
493 static void
494 eyes_plugin_realize (GtkWidget *widget)
495 {
496     g_warning ("eyes_plugin_realize");
497     GdkScreen *screen = gtk_widget_get_screen (widget);
498     gtk_widget_set_colormap (widget, gdk_screen_get_rgba_colormap (screen));
499     gtk_widget_set_app_paintable (widget, TRUE);
500
501     GTK_WIDGET_CLASS (eyes_plugin_parent_class)->realize (widget);
502 }
503
504 static gboolean
505 eyes_plugin_expose_event (GtkWidget *widget, GdkEventExpose *event)
506 {
507     g_warning ("eyes_plugin_expose_event");
508     cairo_t *cr;
509
510     cr = gdk_cairo_create (GDK_DRAWABLE (widget->window));
511     gdk_cairo_region (cr, event->region);
512     cairo_clip (cr);
513
514     cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
515     cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.0);
516     cairo_paint (cr);
517   
518     cairo_destroy (cr);
519
520     return GTK_WIDGET_CLASS (eyes_plugin_parent_class)->expose_event (widget, event);
521 }
522
523 static void
524 eyes_plugin_init (EyesPlugin *desktop_plugin)
525 {
526     g_warning ("eyes_plugin_init");
527     EyesPluginContent *eyes;
528
529     eyes = eyes_plugin_new (desktop_plugin);
530     desktop_plugin->priv = eyes;
531
532     g_signal_connect (desktop_plugin, "notify::is-on-current-desktop",
533         G_CALLBACK (eyes_check_desktop), eyes);
534
535     gtk_container_add (GTK_CONTAINER (desktop_plugin), eyes->align);
536
537
538 static void
539 eyes_plugin_class_init (EyesPluginClass *klass) {
540     g_warning ("eyes_plugin_class_init");
541     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
542     GObjectClass *object_class = G_OBJECT_CLASS (klass);
543
544         object_class->dispose = eyes_plugin_dispose;
545         object_class->finalize = eyes_plugin_finalize;
546
547     widget_class->realize = eyes_plugin_realize;
548     widget_class->expose_event = eyes_plugin_expose_event;
549     g_type_class_add_private (klass, sizeof (EyesPluginContent));
550
551
552 static void
553 eyes_plugin_class_finalize (EyesPluginClass *class) {} 
554