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