X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=xoboot.c;h=91a6d1c375fd657cd277182417414ccd41fd73e9;hb=d6020ec567d81d2f3dfdca2c611709fe33c028b5;hp=14cfd07bc737ff80a3138996416f216d786abf9e;hpb=b8d54384307f696e801ba39d0196543bed3ca7f5;p=xoboot diff --git a/xoboot.c b/xoboot.c index 14cfd07..91a6d1c 100644 --- a/xoboot.c +++ b/xoboot.c @@ -1,4 +1,40 @@ -/* gcc -Wall -g $(pkg-config --cflags --libs gtk+-2.0 cairo) olpc-boot.c -o olpc-boot */ +/* xoboot - A simple demonstration of what might be an XO boot animation + * + * gcc -Wall -g $(pkg-config --cflags --libs gtk+-2.0 cairo) xoboot.c -o xboot + * + * Copyright © 2007 Carl Worth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA." + */ + +/* This is simply a proof-of-concept for using cairo to implement a + * design idea for an OLPC boot-time animation. The animation here is + * implemented to mimic an animation originally provided as a + * Quicktime movie by Rebecca Allen . + * + * So far, this animation has been developed exclusively on an x86 + * laptop with GTK+ targeting the X Window System. No guarantees are + * provided as to how this might perform on an OLPC XO machine. What's + * likely desired is to write the application to use cairo image + * surfaces, and augment cairo to support targeting the XO's 565 image + * format directly. + * + * And obviously, to serve as a real boot-time animation, code will + * have to be written to advance the animation according to various + * system events, instead of using the hard-coded timeline below. + */ #include #include @@ -21,14 +57,17 @@ typedef struct _timeline { double transition; } timeline_t; +#define TIMELINE_ADVANCE_TIMER_PERIOD_MS 500 +#define TIMELINE_TRANSITION_FPS 20 + timeline_t timeline[] = { { 0.5, ANIM_BLANK, 0.0}, { 1.0, ANIM_HEAD, 0.0}, { 2.0, ANIM_BODY, 0.0}, { 7.0, ANIM_SPIN, 6.0}, { 1.0, ANIM_SPIN_DONE, 0.0}, - { 1.0, ANIM_OUTLINE, 0.0}, - { 1.0, ANIM_FADE, 0.5}, + { 1.0, ANIM_OUTLINE, 0.1}, + { 3.0, ANIM_FADE, 1.0}, { 0.0, ANIM_DONE } }; @@ -36,6 +75,11 @@ typedef struct _state { GtkWidget *drawing_area; double progress; anim_stage_t anim_stage; + int stage_ticks_remaining; + /* frames are used during a transition */ + int num_frames; + int frame; + double transition; } state_t; typedef struct _color { @@ -98,25 +142,35 @@ draw_head (cairo_t *cr, double extra) } static void -draw_body (cairo_t *cr, double extra) +draw_body (cairo_t *cr, double extra, double spin_transition) { + cairo_save (cr); + cairo_move_to (cr, XO_BODY_CENTER_X - XO_BODY_DELTA, - XO_BODY_CENTER_Y - XO_BODY_DELTA); - cairo_rel_line_to (cr, XO_BODY_DELTA, XO_BODY_DELTA); + XO_BODY_CENTER_Y + XO_BODY_DELTA); cairo_rel_line_to (cr, XO_BODY_DELTA, -XO_BODY_DELTA); + cairo_rel_line_to (cr, XO_BODY_DELTA, XO_BODY_DELTA); + + cairo_translate (cr, XO_HEAD_CENTER_X, XO_HEAD_CENTER_Y); + cairo_rotate (cr, 2 * M_PI * spin_transition); + cairo_translate (cr, -XO_HEAD_CENTER_X, -XO_HEAD_CENTER_Y); + cairo_move_to (cr, XO_BODY_CENTER_X - XO_BODY_DELTA, - XO_BODY_CENTER_Y + XO_BODY_DELTA); - cairo_rel_line_to (cr, XO_BODY_DELTA, -XO_BODY_DELTA); + XO_BODY_CENTER_Y - XO_BODY_DELTA); cairo_rel_line_to (cr, XO_BODY_DELTA, XO_BODY_DELTA); + cairo_rel_line_to (cr, XO_BODY_DELTA, -XO_BODY_DELTA); + cairo_set_line_width (cr, XO_BODY_LINE_WIDTH + 2 * extra); cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); cairo_stroke (cr); + + cairo_restore (cr); } static void -draw_dots (cairo_t *cr) +draw_dots (cairo_t *cr, double num_dots) { int i; @@ -125,14 +179,16 @@ draw_dots (cairo_t *cr) set_color (cr, &dot_gray); cairo_translate (cr, XO_DOTS_CENTER_X, XO_DOTS_CENTER_Y); - cairo_rotate (cr, - M_PI_2); - - for (i = 0; i < XO_DOTS_NUM_DOTS; i++) { - cairo_arc (cr, - XO_DOTS_POSITION_RADIUS, 0, - XO_DOTS_DOT_RADIUS, - 0, 2 * M_PI); - cairo_fill (cr); + cairo_rotate (cr, - 1.5 * M_PI); + + for (i = 0; i < num_dots; i++) { + if (i != 0) { + cairo_arc (cr, + XO_DOTS_POSITION_RADIUS, 0, + XO_DOTS_DOT_RADIUS, + 0, 2 * M_PI); + cairo_fill (cr); + } cairo_rotate (cr, 2 * M_PI / XO_DOTS_NUM_DOTS); } @@ -171,25 +227,33 @@ xoboot_expose_event (GtkWidget *widget, anim_stage_t anim_stage = state->anim_stage; cairo_t *cr; double shrink; + double spin_transition = 1.0; cr = gdk_cairo_create (widget->window); if (anim_stage < ANIM_FADE) set_color (cr, &background[0]); else - set_color (cr, &background[1]); + cairo_set_source_rgb (cr, LERP_COLORS (background[0], + background[1], + state->transition)); cairo_paint (cr); if (anim_stage == ANIM_BLANK) goto DONE; + if (anim_stage == ANIM_SPIN) + spin_transition = state->transition; + shrink = 0.0; if (anim_stage >= ANIM_OUTLINE) { set_color (cr, &xo_black); draw_head (cr, XO_OUTLINE_EXTRA); - draw_body (cr, XO_OUTLINE_EXTRA); + draw_body (cr, XO_OUTLINE_EXTRA, spin_transition); shrink = - XO_OUTLINE_SHRINK; + if (anim_stage == ANIM_OUTLINE) + shrink *= state->transition; } set_color (cr, &xo_green); @@ -198,14 +262,15 @@ xoboot_expose_event (GtkWidget *widget, if (anim_stage == ANIM_HEAD) goto DONE; - draw_body (cr, shrink); + draw_body (cr, shrink, spin_transition); if (anim_stage == ANIM_BODY) goto DONE; - if (anim_stage < ANIM_FADE) - draw_dots (cr); - else + if (anim_stage < ANIM_FADE) { + draw_dots (cr, (XO_DOTS_NUM_DOTS + 1) * spin_transition); + } else { draw_ring (cr); + } DONE: cairo_destroy (cr); @@ -238,17 +303,53 @@ create_window (state_t *state) return drawing_area; } +static gint +transition_advance (gpointer closure) +{ + state_t *state = closure; + + if (state->frame >= state->num_frames - 1) { + state->frame = state->num_frames - 1; + return FALSE; + } + + state->frame++; + state->transition = (double) state->frame / (state->num_frames - 1); + + gtk_widget_queue_draw (state->drawing_area); + + return TRUE; +} + static gint timeline_advance (gpointer closure) { state_t *state = closure; + timeline_t *tl; if (state->anim_stage == ANIM_DONE) return FALSE; + if (--state->stage_ticks_remaining) + return TRUE; + state->anim_stage++; + tl = &timeline[state->anim_stage]; + state->stage_ticks_remaining = (tl->duration * 1000 + / TIMELINE_ADVANCE_TIMER_PERIOD_MS); gtk_widget_queue_draw (state->drawing_area); + if (tl->transition > 1.0 / TIMELINE_TRANSITION_FPS) { + state->num_frames = tl->transition * TIMELINE_TRANSITION_FPS; + state->frame = 0; + state->transition = 0.0; + g_timeout_add (1000 / TIMELINE_TRANSITION_FPS, transition_advance, state); + } else { + state->num_frames = 1; + state->frame = 0; + state->transition = 1.0; + } + return TRUE; } @@ -262,10 +363,16 @@ main (int argc, char *argv[]) state.drawing_area = create_window (&state); state.progress = 0.0; state.anim_stage = 0; + state.num_frames = 1; + state.frame = 0; + state.transition = 1.0; + state.stage_ticks_remaining = (timeline[state.anim_stage].duration * 1000 + / TIMELINE_ADVANCE_TIMER_PERIOD_MS); gtk_widget_show_all (gtk_widget_get_toplevel (state.drawing_area)); - g_timeout_add (1000, timeline_advance, &state); + g_timeout_add (TIMELINE_ADVANCE_TIMER_PERIOD_MS, + timeline_advance, &state); gtk_main ();