]> git.cworth.org Git - xoboot/blob - xoboot.c
Add all basic stages of the animation
[xoboot] / xoboot.c
1 /* gcc -Wall -g $(pkg-config --cflags --libs gtk+-2.0 cairo) olpc-boot.c -o olpc-boot */
2
3 #include <gtk/gtk.h>
4 #include <cairo.h>
5 #include <math.h>
6
7 typedef enum {
8     ANIM_BLANK,
9     ANIM_HEAD,
10     ANIM_BODY,
11     ANIM_SPIN,
12     ANIM_SPIN_DONE,
13     ANIM_OUTLINE,
14     ANIM_FADE,
15     ANIM_DONE
16 } anim_state_t;
17
18 typedef struct _anim_stage {
19     double length;
20     anim_state_t state;
21 } anim_stage_t;
22
23 anim_stage_t timeline[] = {
24     { 0.5, ANIM_BLANK },
25     { 1.0, ANIM_HEAD },
26     { 2.0, ANIM_BODY },
27     { 6.0, ANIM_SPIN },
28     { 1.0, ANIM_SPIN_DONE },
29     { 1.0, ANIM_OUTLINE },
30     { 1.0, ANIM_FADE },
31     { 0.0, ANIM_DONE }
32 };
33
34 typedef struct _state {
35     GtkWidget *drawing_area;
36     double progress;
37     anim_state_t anim_state;
38 } state_t;
39
40 typedef struct _color {
41     double r;
42     double g;
43     double b;
44 } color_t;
45
46 #define HEX_COLOR(r,g,b) {(r) / 255.0, (g) / 255.0, (b) / 255.0}
47
48 color_t background[2] = {
49     HEX_COLOR (0x75, 0x75, 0x75),
50     HEX_COLOR (0xdb, 0xdc, 0xdf)
51 };
52
53 color_t xo_green   = HEX_COLOR (0x4f, 0xff, 0x12);
54 color_t xo_black   = HEX_COLOR (0x30, 0x30, 0x30);
55 color_t dot_gray   = HEX_COLOR (0xe5, 0xe5, 0xe5);
56 color_t ring_white = HEX_COLOR (0xf1, 0xf1, 0xf1);
57
58 #define LERP(a,b,t) ((a) + (t) * ((b) - (a)))
59 #define LERP_COLORS(c0, c1, t) LERP((c0).r, (c1).r, (t)), \
60                                LERP((c0).g, (c1).g, (t)), \
61                                LERP((c0).b, (c1).b, (t))
62
63 static void
64 set_color (cairo_t *cr, color_t *color)
65 {
66     cairo_set_source_rgb (cr, color->r, color->g, color->b);
67 }
68
69 #define XO_HEAD_CENTER_X        320
70 #define XO_HEAD_CENTER_Y        211.5
71 #define XO_HEAD_RADIUS          12.25
72 #define XO_BODY_CENTER_X        320
73 #define XO_BODY_CENTER_Y        250
74 #define XO_BODY_DELTA           20.5
75 #define XO_BODY_LINE_WIDTH      12.25
76 #define XO_OUTLINE_EXTRA        2.5
77 #define XO_OUTLINE_SHRINK       1.1
78 #define XO_DOTS_CENTER_X        320
79 #define XO_DOTS_CENTER_Y        240
80 #define XO_DOTS_POSITION_RADIUS 112.5
81 #define XO_DOTS_DOT_RADIUS      5.5
82 #define XO_DOTS_NUM_DOTS        30
83 #define XO_RING_CENTER_X        320
84 #define XO_RING_CENTER_Y        240
85 #define XO_RING_INNER_RADIUS    63
86 #define XO_RING_OUTER_RADIUS    118
87
88 static void
89 draw_head (cairo_t *cr, double extra)
90 {
91     cairo_arc (cr,
92                XO_HEAD_CENTER_X,
93                XO_HEAD_CENTER_Y,
94                XO_HEAD_RADIUS + extra,
95                0, 2 * M_PI);
96     cairo_fill (cr);
97 }
98
99 static void
100 draw_body (cairo_t *cr, double extra)
101 {
102     cairo_move_to (cr,
103                    XO_BODY_CENTER_X - XO_BODY_DELTA,
104                    XO_BODY_CENTER_Y - XO_BODY_DELTA);
105     cairo_rel_line_to (cr, XO_BODY_DELTA, XO_BODY_DELTA);
106     cairo_rel_line_to (cr, XO_BODY_DELTA, -XO_BODY_DELTA);
107     cairo_move_to (cr,
108                    XO_BODY_CENTER_X - XO_BODY_DELTA,
109                    XO_BODY_CENTER_Y + XO_BODY_DELTA);
110     cairo_rel_line_to (cr, XO_BODY_DELTA, -XO_BODY_DELTA);
111     cairo_rel_line_to (cr, XO_BODY_DELTA, XO_BODY_DELTA);
112     cairo_set_line_width (cr, XO_BODY_LINE_WIDTH + 2 * extra);
113     cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
114     cairo_stroke (cr);
115 }
116
117 static void
118 draw_dots (cairo_t *cr)
119 {
120     int i;
121
122     cairo_save (cr);
123
124     set_color (cr, &dot_gray);
125
126     cairo_translate (cr, XO_DOTS_CENTER_X, XO_DOTS_CENTER_Y);
127     cairo_rotate (cr, - M_PI_2);
128
129     for (i = 0; i < XO_DOTS_NUM_DOTS; i++) {
130         cairo_arc (cr,
131                    XO_DOTS_POSITION_RADIUS, 0,
132                    XO_DOTS_DOT_RADIUS,
133                    0, 2 * M_PI);
134         cairo_fill (cr);
135         cairo_rotate (cr, 2 * M_PI / XO_DOTS_NUM_DOTS);
136     }
137     
138     cairo_restore (cr);
139 }
140
141 static void
142 draw_ring (cairo_t *cr)
143 {
144     cairo_save (cr);
145
146     cairo_arc (cr,
147                XO_RING_CENTER_X,
148                XO_RING_CENTER_Y,
149                XO_RING_INNER_RADIUS,
150                0, 2 * M_PI);
151     cairo_arc (cr,
152                XO_RING_CENTER_X,
153                XO_RING_CENTER_Y,
154                XO_RING_OUTER_RADIUS,
155                0, 2 * M_PI);
156     set_color (cr, &ring_white);
157
158     cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
159     cairo_fill (cr);
160
161     cairo_restore (cr);
162 }
163
164 static gboolean
165 xoboot_expose_event (GtkWidget      *widget,
166                       GdkEventExpose *event,
167                       gpointer        closure)
168 {
169     state_t *state = closure;
170     anim_state_t anim_state = state->anim_state;
171     cairo_t *cr;
172     double shrink;
173
174     cr = gdk_cairo_create (widget->window);
175
176     if (anim_state < ANIM_FADE)
177         set_color (cr, &background[0]);
178     else
179         set_color (cr, &background[1]);
180
181     cairo_paint (cr);
182
183     if (anim_state == ANIM_BLANK)
184         goto DONE;
185
186     shrink = 0.0;
187     if (anim_state >= ANIM_OUTLINE) {
188         set_color (cr, &xo_black);
189         draw_head (cr, XO_OUTLINE_EXTRA);
190         draw_body (cr, XO_OUTLINE_EXTRA);
191         shrink = - XO_OUTLINE_SHRINK;
192     }
193
194     set_color (cr, &xo_green);
195
196     draw_head (cr, shrink);
197     if (anim_state == ANIM_HEAD)
198         goto DONE;
199
200     draw_body (cr, shrink);
201     if (anim_state == ANIM_BODY)
202         goto DONE;
203
204     if (anim_state < ANIM_FADE)
205         draw_dots (cr);
206     else
207         draw_ring (cr);
208
209   DONE:
210     cairo_destroy (cr);
211
212     return TRUE;
213 }
214
215 static GtkWidget *
216 create_window (state_t *state)
217 {
218     GtkWidget *window;
219     GtkWidget *drawing_area;
220
221     window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
222     gtk_window_set_title (GTK_WINDOW (window), "OLPC Boot Animation Demo");
223
224     g_signal_connect (window, "destroy",
225                       G_CALLBACK (gtk_main_quit), &window);
226
227     drawing_area = gtk_drawing_area_new ();
228
229     /* We want to force 640x480 to emulate the OLPC display */
230     gtk_widget_set_size_request (drawing_area, 640, 480);
231
232     gtk_container_add (GTK_CONTAINER (window), drawing_area);
233
234     g_signal_connect (drawing_area, "expose_event",
235                       G_CALLBACK (xoboot_expose_event), state);
236
237     return drawing_area;
238 }
239
240 static gint
241 timeout_callback (gpointer closure)
242 {
243     state_t *state = closure;
244
245     state->anim_state++;
246     if (state->anim_state > ANIM_DONE)
247         state->anim_state = ANIM_DONE;
248     else
249         gtk_widget_queue_draw (state->drawing_area);
250
251     return TRUE;
252 }
253
254 int
255 main (int argc, char *argv[])
256 {
257     state_t state;
258
259     gtk_init (&argc, &argv);
260
261     state.drawing_area = create_window (&state);
262     state.progress = 0.0;
263     state.anim_state = 0;
264
265     gtk_widget_show_all (gtk_widget_get_toplevel (state.drawing_area));
266
267     g_timeout_add (1000, timeout_callback, &state);
268
269     gtk_main ();
270
271     return 0;
272 }