1 /* gcc -Wall -g $(pkg-config --cflags --libs gtk+-2.0 cairo) olpc-boot.c -o olpc-boot */
18 typedef struct _timeline {
24 #define TIMELINE_ADVANCE_TIMER_PERIOD_MS 500
25 #define TIMELINE_TRANSITION_FPS 20
27 timeline_t timeline[] = {
28 { 0.5, ANIM_BLANK, 0.0},
29 { 1.0, ANIM_HEAD, 0.0},
30 { 2.0, ANIM_BODY, 0.0},
31 { 7.0, ANIM_SPIN, 6.0},
32 { 1.0, ANIM_SPIN_DONE, 0.0},
33 { 1.0, ANIM_OUTLINE, 0.1},
34 { 3.0, ANIM_FADE, 1.0},
38 typedef struct _state {
39 GtkWidget *drawing_area;
41 anim_stage_t anim_stage;
42 int stage_ticks_remaining;
43 /* frames are used during a transition */
49 typedef struct _color {
55 #define HEX_COLOR(r,g,b) {(r) / 255.0, (g) / 255.0, (b) / 255.0}
57 color_t background[2] = {
58 HEX_COLOR (0x75, 0x75, 0x75),
59 HEX_COLOR (0xdb, 0xdc, 0xdf)
62 color_t xo_green = HEX_COLOR (0x4f, 0xff, 0x12);
63 color_t xo_black = HEX_COLOR (0x30, 0x30, 0x30);
64 color_t dot_gray = HEX_COLOR (0xe5, 0xe5, 0xe5);
65 color_t ring_white = HEX_COLOR (0xf1, 0xf1, 0xf1);
67 #define LERP(a,b,t) ((a) + (t) * ((b) - (a)))
68 #define LERP_COLORS(c0, c1, t) LERP((c0).r, (c1).r, (t)), \
69 LERP((c0).g, (c1).g, (t)), \
70 LERP((c0).b, (c1).b, (t))
73 set_color (cairo_t *cr, color_t *color)
75 cairo_set_source_rgb (cr, color->r, color->g, color->b);
78 #define XO_HEAD_CENTER_X 320
79 #define XO_HEAD_CENTER_Y 211.5
80 #define XO_HEAD_RADIUS 12.25
81 #define XO_BODY_CENTER_X 320
82 #define XO_BODY_CENTER_Y 250
83 #define XO_BODY_DELTA 20.5
84 #define XO_BODY_LINE_WIDTH 12.25
85 #define XO_OUTLINE_EXTRA 2.5
86 #define XO_OUTLINE_SHRINK 1.1
87 #define XO_DOTS_CENTER_X 320
88 #define XO_DOTS_CENTER_Y 240
89 #define XO_DOTS_POSITION_RADIUS 112.5
90 #define XO_DOTS_DOT_RADIUS 5.5
91 #define XO_DOTS_NUM_DOTS 30
92 #define XO_RING_CENTER_X 320
93 #define XO_RING_CENTER_Y 240
94 #define XO_RING_INNER_RADIUS 63
95 #define XO_RING_OUTER_RADIUS 118
98 draw_head (cairo_t *cr, double extra)
103 XO_HEAD_RADIUS + extra,
109 draw_body (cairo_t *cr, double extra, double spin_transition)
114 XO_BODY_CENTER_X - XO_BODY_DELTA,
115 XO_BODY_CENTER_Y + XO_BODY_DELTA);
116 cairo_rel_line_to (cr, XO_BODY_DELTA, -XO_BODY_DELTA);
117 cairo_rel_line_to (cr, XO_BODY_DELTA, XO_BODY_DELTA);
119 cairo_translate (cr, XO_HEAD_CENTER_X, XO_HEAD_CENTER_Y);
120 cairo_rotate (cr, 2 * M_PI * spin_transition);
121 cairo_translate (cr, -XO_HEAD_CENTER_X, -XO_HEAD_CENTER_Y);
124 XO_BODY_CENTER_X - XO_BODY_DELTA,
125 XO_BODY_CENTER_Y - XO_BODY_DELTA);
126 cairo_rel_line_to (cr, XO_BODY_DELTA, XO_BODY_DELTA);
127 cairo_rel_line_to (cr, XO_BODY_DELTA, -XO_BODY_DELTA);
129 cairo_set_line_width (cr, XO_BODY_LINE_WIDTH + 2 * extra);
130 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
137 draw_dots (cairo_t *cr, double num_dots)
143 set_color (cr, &dot_gray);
145 cairo_translate (cr, XO_DOTS_CENTER_X, XO_DOTS_CENTER_Y);
146 cairo_rotate (cr, - 1.5 * M_PI);
148 for (i = 0; i < num_dots; i++) {
151 XO_DOTS_POSITION_RADIUS, 0,
156 cairo_rotate (cr, 2 * M_PI / XO_DOTS_NUM_DOTS);
163 draw_ring (cairo_t *cr)
170 XO_RING_INNER_RADIUS,
175 XO_RING_OUTER_RADIUS,
177 set_color (cr, &ring_white);
179 cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
186 xoboot_expose_event (GtkWidget *widget,
187 GdkEventExpose *event,
190 state_t *state = closure;
191 anim_stage_t anim_stage = state->anim_stage;
194 double spin_transition = 1.0;
196 cr = gdk_cairo_create (widget->window);
198 if (anim_stage < ANIM_FADE)
199 set_color (cr, &background[0]);
201 cairo_set_source_rgb (cr, LERP_COLORS (background[0],
207 if (anim_stage == ANIM_BLANK)
210 if (anim_stage == ANIM_SPIN)
211 spin_transition = state->transition;
214 if (anim_stage >= ANIM_OUTLINE) {
215 set_color (cr, &xo_black);
216 draw_head (cr, XO_OUTLINE_EXTRA);
217 draw_body (cr, XO_OUTLINE_EXTRA, spin_transition);
218 shrink = - XO_OUTLINE_SHRINK;
219 if (anim_stage == ANIM_OUTLINE)
220 shrink *= state->transition;
223 set_color (cr, &xo_green);
225 draw_head (cr, shrink);
226 if (anim_stage == ANIM_HEAD)
229 draw_body (cr, shrink, spin_transition);
230 if (anim_stage == ANIM_BODY)
233 if (anim_stage < ANIM_FADE) {
234 draw_dots (cr, (XO_DOTS_NUM_DOTS + 1) * spin_transition);
246 create_window (state_t *state)
249 GtkWidget *drawing_area;
251 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
252 gtk_window_set_title (GTK_WINDOW (window), "OLPC Boot Animation Demo");
254 g_signal_connect (window, "destroy",
255 G_CALLBACK (gtk_main_quit), &window);
257 drawing_area = gtk_drawing_area_new ();
259 /* We want to force 640x480 to emulate the OLPC display */
260 gtk_widget_set_size_request (drawing_area, 640, 480);
262 gtk_container_add (GTK_CONTAINER (window), drawing_area);
264 g_signal_connect (drawing_area, "expose_event",
265 G_CALLBACK (xoboot_expose_event), state);
271 transition_advance (gpointer closure)
273 state_t *state = closure;
275 if (state->frame >= state->num_frames - 1) {
276 state->frame = state->num_frames - 1;
281 state->transition = (double) state->frame / (state->num_frames - 1);
283 gtk_widget_queue_draw (state->drawing_area);
289 timeline_advance (gpointer closure)
291 state_t *state = closure;
294 if (state->anim_stage == ANIM_DONE)
297 if (--state->stage_ticks_remaining)
301 tl = &timeline[state->anim_stage];
302 state->stage_ticks_remaining = (tl->duration * 1000
303 / TIMELINE_ADVANCE_TIMER_PERIOD_MS);
304 gtk_widget_queue_draw (state->drawing_area);
306 if (tl->transition > 1.0 / TIMELINE_TRANSITION_FPS) {
307 state->num_frames = tl->transition * TIMELINE_TRANSITION_FPS;
309 state->transition = 0.0;
310 g_timeout_add (1000 / TIMELINE_TRANSITION_FPS, transition_advance, state);
312 state->num_frames = 1;
314 state->transition = 1.0;
321 main (int argc, char *argv[])
325 gtk_init (&argc, &argv);
327 state.drawing_area = create_window (&state);
328 state.progress = 0.0;
329 state.anim_stage = 0;
330 state.num_frames = 1;
332 state.transition = 1.0;
333 state.stage_ticks_remaining = (timeline[state.anim_stage].duration * 1000
334 / TIMELINE_ADVANCE_TIMER_PERIOD_MS);
336 gtk_widget_show_all (gtk_widget_get_toplevel (state.drawing_area));
338 g_timeout_add (TIMELINE_ADVANCE_TIMER_PERIOD_MS,
339 timeline_advance, &state);