Initial Import
[profile/ivi/clutter-toys.git] / foofone / foofone.c
1 /* 
2  * foofone
3  *
4  * Foofone is a quick 3 hour hack to experiment with effects and create a
5  * dummy phone interface. Thats all it is.
6  *
7  * Copyright 2007 OpenedHand Ltd
8  * Authored by Matthew Allum <mallum@o-hand.com>
9  * Licensed under the GPL v2 or greater.
10  *
11  */
12
13 #include <clutter/clutter.h>
14 #include <stdlib.h>
15 #include <math.h>
16
17 #define CSW 240
18 #define CSH 320
19
20 typedef struct Button
21 {
22   ClutterActor     *actor;
23   gchar            *face;
24   gint              sx, sy;
25
26 Button;
27
28 typedef struct App
29 {
30   Button                *buttons[12];
31   ClutterActor          *dpy, *dpy_entry;
32   gint                   dpyx, dpyy;
33   ClutterActor          *screen_dialpad, *screen_dial, *dial_label;
34   gboolean               dialing_state;
35   ClutterTimeline       *dialing_timeline;
36
37 App;
38
39 /* An alpha function that goes from 0->1->0 along a sine. */
40 static gdouble
41 alpha_sine_func (ClutterAlpha *alpha,
42                  gpointer unused)
43 {
44   ClutterTimeline *timeline = clutter_alpha_get_timeline (alpha);
45   return sin(clutter_timeline_get_progress (timeline) * M_PI);
46 }
47
48 gulong ALPHA_SINE;
49
50 /* A boolean 'interpolator', switching from 'a' to 'b' when 'progress' = 0.5 */ 
51 static gboolean
52 boolean_progress (const GValue *a,
53                   const GValue *b,
54                   gdouble       progress,
55                   GValue       *retval)
56 {
57   gboolean ba = g_value_get_boolean (a);
58   gboolean bb = g_value_get_boolean (b);
59   gboolean res = (progress <= 0.5) ? ba : bb;
60   g_value_set_boolean (retval, res);
61   return TRUE;
62 }
63
64 void
65 on_call_deactivate_complete (ClutterTimeline *timeline,
66                              gpointer         user_data)
67 {
68   App *app = (App*)user_data;
69   /* reset the now hidden dialing screen */
70   clutter_actor_set_rotation (app->screen_dial, CLUTTER_Y_AXIS, 0, 0, 0, 0);
71 }
72
73 void
74 call_deactivate (App *app)
75 {
76   int               i;
77   ClutterAnimation *anim;
78   ClutterAlpha     *alpha;
79
80   /* stop the flashing text */
81   clutter_timeline_stop (app->dialing_timeline);
82
83   /* clear dialpad entry ready */
84   clutter_text_set_text (CLUTTER_TEXT(app->dpy_entry), "");
85
86   /* rotate screen_dial, and hide it at mid-animation */
87   clutter_actor_set_rotation (app->screen_dial, CLUTTER_Y_AXIS, 0.0, 0.0, 0.0, 0.0);
88   anim = clutter_actor_animate (app->screen_dial, CLUTTER_LINEAR, 150,
89                                 "rotation-angle-y", -180.0,
90                                 "visible", FALSE,
91                                 NULL);
92   alpha = clutter_animation_get_alpha (anim);
93
94   /* reset positions of dialer actors, needed back for flip */
95   for (i=0; i<12; i++)
96     {
97       clutter_actor_set_position (app->buttons[i]->actor,
98                                   app->buttons[i]->sx, app->buttons[i]->sy);
99       clutter_actor_set_opacity (app->buttons[i]->actor, 0xff);
100     }
101   clutter_actor_set_position (app->dpy, app->dpyx, app->dpyy);
102
103   /* rotate hidden screen_dialpad, and show it at mid-animation */
104   clutter_actor_set_rotation (app->screen_dialpad, CLUTTER_Y_AXIS, 180.0, 0.0, 0.0, 0.0);
105   clutter_actor_animate_with_alpha (app->screen_dialpad, alpha,
106                                     "rotation-angle-y", 0.0,
107                                     "visible", TRUE,
108                                     "signal-after::completed",
109                                       G_CALLBACK(on_call_deactivate_complete),
110                                       app,
111                                     NULL);
112
113   app->dialing_state = FALSE;
114 }
115
116 void
117 on_call_activate_complete (ClutterActor *actor,
118                            gpointer user_data)
119 {
120   ClutterAlpha     *alpha;
121   ClutterBehaviour *behave;
122   App *app = (App*)user_data;
123
124   clutter_actor_hide (app->screen_dialpad);
125
126   /* Setup the pulsing 'calling..' text if need be */
127   if (app->dialing_timeline == NULL)
128     {
129       app->dialing_timeline = clutter_timeline_new (1000);
130       clutter_timeline_set_loop (app->dialing_timeline, TRUE);
131       alpha = clutter_alpha_new_full (app->dialing_timeline, ALPHA_SINE);
132       behave = clutter_behaviour_opacity_new (alpha, 0xff, 0);
133       clutter_behaviour_apply (behave, app->dial_label);
134     }
135   clutter_timeline_start (app->dialing_timeline);
136
137   app->dialing_state = TRUE;
138 }
139
140 void
141 call_activate (App *app)
142 {
143   gint i;
144   gfloat x, y;
145   ClutterAnimation *anim;
146   ClutterAlpha     *alpha;
147
148   /* zoom in the dialing window */
149   clutter_actor_set_scale (app->screen_dial, 0.1, 0.1);
150   clutter_actor_set_opacity (app->screen_dial, 0x66);
151   clutter_actor_show_all (app->screen_dial);
152
153   anim = clutter_actor_animate (app->screen_dial, CLUTTER_EASE_OUT_SINE, 150,
154                                 "opacity", 0xff,
155                                 "scale-x", 1.0,
156                                 "scale-y", 1.0,
157                                 NULL);
158   alpha = clutter_animation_get_alpha (anim);
159
160   /* Set up effects to shoot everything offscreen, synchronized with screen_dial animation */
161   for (i=0; i<12; i++)
162     {
163       clutter_actor_set_position (app->buttons[i]->actor,
164                                   app->buttons[i]->sx,
165                                   app->buttons[i]->sy);
166
167       switch ((i+1) % 3)
168           {
169           case 0:
170             x = CSW + clutter_actor_get_width (app->buttons[i]->actor) / 2;
171             y = app->buttons[i]->sy;
172             break;
173           case 1:
174             x = -clutter_actor_get_width (app->buttons[i]->actor) / 2;
175             y = app->buttons[i]->sy;
176             break;
177           case 2:
178             x = app->buttons[i]->sx;
179             if (i < 3)
180               y = -clutter_actor_get_height (app->buttons[i]->actor) / 2;
181             else
182               y = CSH + clutter_actor_get_height (app->buttons[i]->actor) / 2;
183             break;
184           }
185
186       clutter_actor_animate_with_alpha (app->buttons[i]->actor, alpha,
187                                         "opacity", 0x00,
188                                         "x", x,
189                                         "y", y,
190                                         NULL);
191     }
192
193   clutter_actor_set_position (app->dpy, app->dpyx, app->dpyy);
194   clutter_actor_animate_with_alpha(app->dpy, alpha,
195                                    "x", (float)app->dpyx,
196                                    "y", -clutter_actor_get_height (app->dpy),
197                                    "signal-after::completed",
198                                      on_call_activate_complete,
199                                      app,
200                                    NULL);
201 }
202
203 void  
204 on_button_effect_complete (ClutterAnimation *animation,
205                                        gpointer user_data)
206 {
207   ClutterActor *actor = (ClutterActor*)user_data;
208
209   /* reset after effect */
210   clutter_actor_set_opacity (actor, 0xff);
211   clutter_actor_set_scale (actor, 1.0, 1.0);
212 }
213
214 void
215 button_activate (App *app, Button *b)
216 {
217   // Wait for the previous animation to end
218   if (clutter_actor_get_animation (b->actor))
219     return;
220
221   clutter_text_insert_text (CLUTTER_TEXT(app->dpy_entry), b->face, -1);
222
223   clutter_actor_set_opacity (b->actor, 0xff);
224   clutter_actor_set_scale (b->actor, 1.0, 1.0);
225   clutter_actor_animate (b->actor, CLUTTER_LINEAR, 50,
226                          "opacity", 0x00,
227                          "scale-x", 1.5,
228                          "scale-y", 1.5,
229                          "signal-after::completed", on_button_effect_complete, b->actor,
230                          NULL);
231 }
232
233 static gboolean 
234 on_input (ClutterStage *stage,
235           ClutterEvent *event,
236           gpointer      user_data)
237 {
238   App *app = (App*)user_data;
239
240   if (event->type == CLUTTER_BUTTON_PRESS)
241     {
242       ClutterActor *actor = clutter_event_get_source (event);
243       const gchar  *label = clutter_actor_get_name (actor);
244       int          label_val;
245
246       if (app->dialing_state == TRUE)
247             {
248               call_deactivate(app);
249               return TRUE;
250             }
251
252       /* retrieve button id (stored in the Actor's name) */
253       if ( !label )
254         return FALSE;
255       label_val = atoi(label);
256       if ( label_val < 1 || label_val > 12 )
257         return FALSE;
258       --label_val;
259
260       if (label_val == 11) /* 'dial' key */
261         call_activate (app);
262       else
263         button_activate (app, app->buttons[label_val]);
264
265       return TRUE;
266     }
267
268   return FALSE;
269 }
270
271 void
272 make_ui (App *app)
273 {
274   gint          i, xpad, ypad, x ,y, xinit, xdpy, ydpy;
275   ClutterActor *button_texture, *a;
276   ClutterColor  text_color = { 0xff, 0xff, 0xff, 0xff },
277                 rect_color = { 0, 0, 0, 0x99 },
278                 black_color = { 0, 0, 0, 0xff };
279
280   button_texture =  clutter_texture_new_from_file ("button.png", NULL);
281
282   xpad = (CSW-(3*clutter_actor_get_width(button_texture)))/4;
283   x = xinit = xpad;
284   ypad = xpad/2;
285   y = (CSH - (4 * (ypad + clutter_actor_get_height(button_texture))));
286
287   /*
288    * screen_dialpad (group)
289    *  +----dpy (group)
290    *        +---- (texture:display.png)
291    *        +----dpy_entry (text)
292    *        +----buttons[0:11]->actor (group)
293    *              +---- (texture:button.png)
294    *              +---- (text)
295    */
296
297   app->screen_dialpad = clutter_group_new();
298   clutter_actor_set_size (app->screen_dialpad, CSW, CSH);
299   clutter_actor_set_anchor_point_from_gravity (app->screen_dialpad, CLUTTER_GRAVITY_CENTER);
300   clutter_actor_set_position (app->screen_dialpad, CSW/2, CSH/2);
301
302   app->dpy = clutter_group_new();
303
304   a = clutter_texture_new_from_file ("display.png", NULL);
305   clutter_group_add (CLUTTER_GROUP(app->dpy), a);
306   app->dpyx = xdpy = x;
307   app->dpyy = ydpy = (y - clutter_actor_get_height(app->dpy))/2;
308   clutter_actor_set_position (app->dpy, xdpy, ydpy);
309
310   clutter_group_add(CLUTTER_GROUP(app->screen_dialpad), app->dpy);
311
312   app->dpy_entry = clutter_text_new_full ("Sans Bold 32px", "", &text_color);
313   clutter_text_set_editable (CLUTTER_TEXT(app->dpy_entry), TRUE);
314   clutter_actor_set_position (app->dpy_entry, 8, 8);
315   clutter_actor_set_size (app->dpy_entry, clutter_actor_get_width (app->dpy) - 16, 32);
316   clutter_group_add (CLUTTER_GROUP(app->dpy), app->dpy_entry);
317
318   for (i=0; i<12; i++)
319     {
320       gchar       buf[8];
321       gchar       label[8];
322
323       app->buttons[i] = g_new0(Button, 1);
324       app->buttons[i]->actor = clutter_group_new ();
325       g_snprintf (label, 8, "%d", i+1);
326       clutter_actor_set_name (app->buttons[i]->actor, label);
327       clutter_actor_set_reactive (app->buttons[i]->actor, TRUE);
328       clutter_actor_set_anchor_point_from_gravity (app->buttons[i]->actor,
329                                                    CLUTTER_GRAVITY_CENTER);
330       
331       if ( i == 0 )
332         a = button_texture;
333       else
334         a = clutter_clone_new(button_texture);
335       clutter_group_add(CLUTTER_GROUP(app->buttons[i]->actor), a);
336       
337       switch (i)
338         {
339         case 9:
340           g_snprintf(buf, 8, "#");
341           break;
342         case 10:
343           g_snprintf(buf, 8, "0");
344           break;
345         case 11:
346           g_snprintf(buf, 8, "*");
347           break;
348         default:
349           g_snprintf(buf, 8, "%i", i+1);
350           break;
351         }
352
353       a = clutter_text_new_full("Sans Bold 32px", buf, &text_color);
354       clutter_actor_set_position (a, 
355         (clutter_actor_get_width (button_texture)  - clutter_actor_get_width (a))/2,
356         (clutter_actor_get_height (button_texture) - clutter_actor_get_height (a))/2);
357       clutter_group_add (CLUTTER_GROUP (app->buttons[i]->actor), a);
358
359       clutter_group_add (CLUTTER_GROUP (app->screen_dialpad), app->buttons[i]->actor);
360
361       /* need to remember positions for anim - sucky */
362       app->buttons[i]->sx = x + clutter_actor_get_width (app->buttons[i]->actor)/2;
363       app->buttons[i]->sy = y + clutter_actor_get_height (app->buttons[i]->actor)/2;
364       clutter_actor_set_position (app->buttons[i]->actor,
365                                   app->buttons[i]->sx,
366                                   app->buttons[i]->sy);
367
368       /* Really we should use a Clutter*Box here.. */
369       if (i % 3 == 2)
370         {
371           x = xinit;
372           y += (ypad + clutter_actor_get_height (button_texture));
373         }
374       else
375         x += (xpad + clutter_actor_get_width(button_texture));
376
377       app->buttons[i]->face = g_strdup (buf);
378     }
379
380     /*
381      * screen_dial
382      *  +---- (rectangle:black)
383      *  +---- (texture:call-background.png)
384      *  +---- (rectangle:semi transparent)
385      *  +----dial_label (text:"Calling...")
386      */
387
388   app->screen_dial = clutter_group_new();
389   clutter_actor_set_anchor_point_from_gravity (app->screen_dial, CLUTTER_GRAVITY_CENTER);
390   clutter_actor_set_position (app->screen_dial, CSW/2, CSH/2);
391
392   a = clutter_rectangle_new_with_color (&black_color);
393   clutter_actor_set_size (a, CSW, CSH);
394   clutter_group_add (CLUTTER_GROUP(app->screen_dial), a);
395
396   a = clutter_texture_new_from_file ("call-background.png", NULL);
397   clutter_group_add (CLUTTER_GROUP(app->screen_dial), a);
398
399   a = clutter_rectangle_new_with_color (&rect_color);
400   clutter_actor_set_size (a, CSW, CSH/6);
401   clutter_actor_set_position (a, 0, (CSH - (CSH/6))/2);
402   clutter_group_add (CLUTTER_GROUP(app->screen_dial), a);
403
404   app->dial_label = clutter_text_new_full ("Sans Bold 32px", "Calling...", &text_color);
405   clutter_actor_set_position (app->dial_label, 10, (CSH - (CSH/6))/2 + 10);
406   clutter_group_add (CLUTTER_GROUP (app->screen_dial), app->dial_label);
407 }
408
409 int
410 main (int argc, char *argv[])
411 {
412   ClutterActor    *stage;
413   ClutterColor     stage_color = { 0x0, 0x0, 0x0, 0xff };
414
415   App             *app;
416
417   clutter_init (&argc, &argv);
418
419   ALPHA_SINE = clutter_alpha_register_func (alpha_sine_func, NULL);
420   clutter_interval_register_progress_func (G_TYPE_BOOLEAN, boolean_progress);
421
422   app = g_new0(App, 1);
423
424   stage = clutter_stage_get_default ();
425   clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
426   clutter_actor_set_size (stage, CSW, CSH);
427
428   make_ui (app);
429
430   clutter_group_add (CLUTTER_GROUP(stage), app->screen_dial);
431   clutter_group_add (CLUTTER_GROUP(stage), app->screen_dialpad);
432
433   clutter_actor_hide_all (app->screen_dial);
434   clutter_actor_show_all (stage);
435
436   g_signal_connect (stage, 
437                     "event",
438                     G_CALLBACK (on_input),
439                     app);
440
441   printf("\n..Press '*' to dial..\n\n");
442
443   clutter_main ();
444
445   return 0;
446 }