From d3a98cf6cbc3bd0b9efc570f58e8812c03931c18 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 16 Oct 2018 10:08:48 +0200 Subject: Original 5.40 --- hacks/glx/photopile.c | 833 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 833 insertions(+) create mode 100644 hacks/glx/photopile.c (limited to 'hacks/glx/photopile.c') diff --git a/hacks/glx/photopile.c b/hacks/glx/photopile.c new file mode 100644 index 0000000..e060f25 --- /dev/null +++ b/hacks/glx/photopile.c @@ -0,0 +1,833 @@ +/* photopile, Copyright (c) 2008-2018 Jens Kilian + * Based on carousel, Copyright (c) 2005-2008 Jamie Zawinski + * Loads a sequence of images and shuffles them into a pile. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + */ + +#if defined(HAVE_COCOA) || defined(HAVE_ANDROID) +# define DEF_FONT "OCR A Std 48, Lucida Console 48, Monaco 48" +#elif 0 /* real X11, XQueryFont() */ +# define DEF_FONT "-*-helvetica-bold-r-normal-*-*-480-*-*-*-*-*-*" +#else /* real X11, load_font_retry() */ +# define DEF_FONT "-*-ocr a std-medium-r-*-*-*-480-*-*-m-*-*-*" +#endif + +#define DEFAULTS "*count: 7 \n" \ + "*delay: 10000 \n" \ + "*wireframe: False \n" \ + "*showFPS: False \n" \ + "*fpsSolid: True \n" \ + "*useSHM: True \n" \ + "*font: " DEF_FONT "\n" \ + "*desktopGrabber: xscreensaver-getimage -no-desktop %s\n" \ + "*grabDesktopImages: False \n" \ + "*chooseRandomImages: True \n" \ + "*suppressRotationAnimation: True\n" \ + +# define free_photopile 0 +# define release_photopile 0 +# define photopile_handle_event xlockmore_no_events + +#undef countof +#define countof(x) (sizeof((x))/sizeof((*x))) + +#ifndef HAVE_JWXYZ +# include /* for XrmDatabase in -debug mode */ +#endif +#include + +#include "xlockmore.h" +#include "grab-ximage.h" +#include "texfont.h" +#include "dropshadow.h" + +#ifdef USE_GL + +# define DEF_SCALE "0.4" +# define DEF_MAX_TILT "50" +# define DEF_SPEED "1.0" +# define DEF_DURATION "5" +# define DEF_MIPMAP "True" +# define DEF_TITLES "True" +# define DEF_POLAROID "True" +# define DEF_CLIP "True" +# define DEF_SHADOWS "True" +# define DEF_DEBUG "False" + +#define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3) + +typedef struct { + GLfloat x, y; /* position on screen */ + GLfloat angle; /* rotation angle */ + +} position; + +typedef struct { + Bool loaded_p; /* true if image can be drawn */ + + char *title; /* the filename of this image */ + int w, h; /* size in pixels of the image */ + int tw, th; /* size in pixels of the texture */ + XRectangle geom; /* where in the image the bits are */ + + position pos[4]; /* control points for calculating position */ + + GLuint texid; /* GL texture ID */ + +} image; + + +typedef enum { EARLY, SHUFFLE, NORMAL, LOADING } fade_mode; +static int fade_ticks = 60; + +typedef struct { + ModeInfo *mi; + GLXContext *glx_context; + + image *frames; /* pointer to array of images */ + int nframe; /* image being (resp. next to be) loaded */ + + GLuint shadow; + texture_font_data *texfont; + int loading_sw, loading_sh; + + time_t last_time, now; + int draw_tick; + fade_mode mode; + int mode_tick; + +} photopile_state; + +static photopile_state *sss = NULL; + + +/* Command-line arguments + */ +static GLfloat scale; /* Scale factor for loading images. */ +static GLfloat max_tilt; /* Maximum angle from vertical. */ +static GLfloat speed; /* Animation speed scale factor. */ +static int duration; /* Reload images after this long. */ +static Bool mipmap_p; /* Use mipmaps instead of single textures. */ +static Bool titles_p; /* Display image titles. */ +static Bool polaroid_p; /* Use instant-film look for images. */ +static Bool clip_p; /* Clip images instead of scaling for -polaroid. */ +static Bool shadows_p; /* Draw drop shadows. */ +static Bool debug_p; /* Be loud and do weird things. */ + + +static XrmOptionDescRec opts[] = { + {"-scale", ".scale", XrmoptionSepArg, 0 }, + {"-maxTilt", ".maxTilt", XrmoptionSepArg, 0 }, + {"-speed", ".speed", XrmoptionSepArg, 0 }, + {"-duration", ".duration", XrmoptionSepArg, 0 }, + {"-mipmaps", ".mipmap", XrmoptionNoArg, "True" }, + {"-no-mipmaps", ".mipmap", XrmoptionNoArg, "False" }, + {"-titles", ".titles", XrmoptionNoArg, "True" }, + {"-no-titles", ".titles", XrmoptionNoArg, "False" }, + {"-polaroid", ".polaroid", XrmoptionNoArg, "True" }, + {"-no-polaroid", ".polaroid", XrmoptionNoArg, "False" }, + {"-clip", ".clip", XrmoptionNoArg, "True" }, + {"-no-clip", ".clip", XrmoptionNoArg, "False" }, + {"-shadows", ".shadows", XrmoptionNoArg, "True" }, + {"-no-shadows", ".shadows", XrmoptionNoArg, "False" }, + {"-debug", ".debug", XrmoptionNoArg, "True" }, + {"-font", ".font", XrmoptionSepArg, 0 }, +}; + +static argtype vars[] = { + { &scale, "scale", "Scale", DEF_SCALE, t_Float}, + { &max_tilt, "maxTilt", "MaxTilt", DEF_MAX_TILT, t_Float}, + { &speed, "speed", "Speed", DEF_SPEED, t_Float}, + { &duration, "duration", "Duration", DEF_DURATION, t_Int}, + { &mipmap_p, "mipmap", "Mipmap", DEF_MIPMAP, t_Bool}, + { &titles_p, "titles", "Titles", DEF_TITLES, t_Bool}, + { &polaroid_p, "polaroid", "Polaroid", DEF_POLAROID, t_Bool}, + { &clip_p, "clip", "Clip", DEF_CLIP, t_Bool}, + { &shadows_p, "shadows", "Shadows", DEF_SHADOWS, t_Bool}, + { &debug_p, "debug", "Debug", DEF_DEBUG, t_Bool}, +}; + +ENTRYPOINT ModeSpecOpt photopile_opts = {countof(opts), opts, countof(vars), vars, NULL}; + + +/* Functions to interpolate between image positions. + */ +static position +add_pos(position p, position q) +{ + p.x += q.x; + p.y += q.y; + p.angle += q.angle; + return p; +} + +static position +scale_pos(GLfloat t, position p) +{ + p.x *= t; + p.y *= t; + p.angle *= t; + return p; +} + +static position +linear_combination(GLfloat t, position p, position q) +{ + return add_pos(scale_pos(1.0 - t, p), scale_pos(t, q)); +} + +static position +interpolate(GLfloat t, position p[4]) +{ + /* de Casteljau's algorithm, 4 control points */ + position p10 = linear_combination(t, p[0], p[1]); + position p11 = linear_combination(t, p[1], p[2]); + position p12 = linear_combination(t, p[2], p[3]); + + position p20 = linear_combination(t, p10, p11); + position p21 = linear_combination(t, p11, p12); + + return linear_combination(t, p20, p21); +} + +static position +offset_pos(position p, GLfloat th, GLfloat r) +{ + p.x += cos(th) * r; + p.y += sin(th) * r; + p.angle = (frand(2.0) - 1.0) * max_tilt; + return p; +} + +/* Calculate new positions for all images. + */ +static void +set_new_positions(photopile_state *ss) +{ + ModeInfo *mi = ss->mi; + int i; + + for (i = 0; i < MI_COUNT(mi)+1; ++i) + { + image *frame = ss->frames + i; + GLfloat w = frame->w; + GLfloat h = frame->h; + GLfloat d = sqrt(w*w + h*h); + GLfloat leave = frand(M_PI * 2.0); + GLfloat enter = frand(M_PI * 2.0); + + /* start position */ + frame->pos[0] = frame->pos[3]; + + /* end position */ + frame->pos[3].x = BELLRAND(MI_WIDTH(mi)); + frame->pos[3].y = BELLRAND(MI_HEIGHT(mi)); + frame->pos[3].angle = (frand(2.0) - 1.0) * max_tilt; + + /* Try to keep the images mostly inside the screen bounds */ + frame->pos[3].x = MAX(0.5*w, MIN(MI_WIDTH(mi)-0.5*w, frame->pos[3].x)); + frame->pos[3].y = MAX(0.5*h, MIN(MI_HEIGHT(mi)-0.5*h, frame->pos[3].y)); + + /* intermediate points */ + frame->pos[1] = offset_pos(frame->pos[0], leave, d * (0.5 + frand(1.0))); + frame->pos[2] = offset_pos(frame->pos[3], enter, d * (0.5 + frand(1.0))); + } +} + +/* Callback that tells us that the texture has been loaded. + */ +static void +image_loaded_cb (const char *filename, XRectangle *geom, + int image_width, int image_height, + int texture_width, int texture_height, + void *closure) +{ + photopile_state *ss = (photopile_state *) closure; + ModeInfo *mi = ss->mi; + int wire = MI_IS_WIREFRAME(mi); + image *frame = ss->frames + ss->nframe; + + if (wire) + { + if (random() % 2) + { + frame->w = (int)(MI_WIDTH(mi) * scale) - 1; + frame->h = (int)(MI_HEIGHT(mi) * scale) - 1; + } + else + { + frame->w = (int)(MI_HEIGHT(mi) * scale) - 1; + frame->h = (int)(MI_WIDTH(mi) * scale) - 1; + } + if (frame->w <= 10) frame->w = 10; + if (frame->h <= 10) frame->h = 10; + frame->geom.width = frame->w; + frame->geom.height = frame->h; + goto DONE; + } + + if (image_width == 0 || image_height == 0) + exit (1); + + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + mipmap_p ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR); + + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + frame->w = image_width; + frame->h = image_height; + frame->tw = texture_width; + frame->th = texture_height; + frame->geom = *geom; + + if (frame->title) + free (frame->title); + frame->title = (filename ? strdup (filename) : 0); + + /* xscreensaver-getimage returns paths relative to the image directory + now, so leave the sub-directory part in. Unless it's an absolute path. + */ + if (frame->title && frame->title[0] == '/') + { + /* strip filename to part after last /. */ + char *s = strrchr (frame->title, '/'); + if (s) strcpy (frame->title, s+1); + } + + if (debug_p) + fprintf (stderr, "%s: loaded %4d x %-4d %4d x %-4d \"%s\"\n", + progname, + frame->geom.width, + frame->geom.height, + frame->tw, frame->th, + (frame->title ? frame->title : "(null)")); + + DONE: + frame->loaded_p = True; +} + + +/* Load a new file. + */ +static void +load_image (ModeInfo *mi) +{ + photopile_state *ss = &sss[MI_SCREEN(mi)]; + int wire = MI_IS_WIREFRAME(mi); + image *frame = ss->frames + ss->nframe; + + if (debug_p && !wire && frame->w != 0) + fprintf (stderr, "%s: dropped %4d x %-4d %4d x %-4d \"%s\"\n", + progname, + frame->geom.width, + frame->geom.height, + frame->tw, frame->th, + (frame->title ? frame->title : "(null)")); + + frame->loaded_p = False; + + if (wire) + image_loaded_cb (0, 0, 0, 0, 0, 0, ss); + else + { + int w = MI_WIDTH(mi); + int h = MI_HEIGHT(mi); + int size = (int)((w > h ? w : h) * scale); + if (size <= 10) size = 10; + load_texture_async (mi->xgwa.screen, mi->window, *ss->glx_context, + size, size, + mipmap_p, frame->texid, + image_loaded_cb, ss); + } +} + + +static void +loading_msg (ModeInfo *mi) +{ + photopile_state *ss = &sss[MI_SCREEN(mi)]; + int wire = MI_IS_WIREFRAME(mi); + const char text[] = "Loading..."; + + if (wire) return; + + if (ss->loading_sw == 0) /* only do this once */ + { + XCharStruct e; + texture_string_metrics (ss->texfont, text, &e, 0, 0); + ss->loading_sw = e.width; + ss->loading_sh = e.ascent + e.descent; + } + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0, MI_WIDTH(mi), 0, MI_HEIGHT(mi), -1, 1); + + glTranslatef ((MI_WIDTH(mi) - ss->loading_sw) / 2, + (MI_HEIGHT(mi) - ss->loading_sh) / 2, + 0); + glColor3f (1, 1, 0); + glEnable (GL_TEXTURE_2D); + glDisable (GL_DEPTH_TEST); + print_texture_string (ss->texfont, text); + glEnable (GL_DEPTH_TEST); + glPopMatrix(); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + + glMatrixMode(GL_MODELVIEW); + + glFinish(); + glXSwapBuffers (MI_DISPLAY (mi), MI_WINDOW(mi)); +} + + +static Bool +loading_initial_image (ModeInfo *mi) +{ + photopile_state *ss = &sss[MI_SCREEN(mi)]; + + if (ss->frames[ss->nframe].loaded_p) + { + /* The initial image has been fully loaded, start fading it in. */ + int i; + + for (i = 0; i < ss->nframe; ++i) + { + ss->frames[i].pos[3].x = MI_WIDTH(mi) * 0.5; + ss->frames[i].pos[3].y = MI_HEIGHT(mi) * 0.5; + ss->frames[i].pos[3].angle = 0.0; + } + set_new_positions(ss); + + ss->mode = SHUFFLE; + ss->mode_tick = fade_ticks / speed; + } + else + { + loading_msg(mi); + } + + return (ss->mode == EARLY); +} + + +ENTRYPOINT void +reshape_photopile (ModeInfo *mi, int width, int height) +{ + glViewport (0, 0, (GLint) width, (GLint) height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glOrtho(0, MI_WIDTH(mi), 0, MI_HEIGHT(mi), -1, 1); + +# ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */ + { + GLfloat h = MI_HEIGHT(mi) / (GLfloat) MI_WIDTH(mi); + int o = (int) current_device_rotation(); + if (o != 0 && o != 180 && o != -180) + glScalef (1/h, h, 1); + } +# endif + + glClear(GL_COLOR_BUFFER_BIT); +} + + +/* Kludge to add "-v" to invocation of "xscreensaver-getimage" in -debug mode + */ +static void +hack_resources (Display *dpy) +{ +# ifndef HAVE_JWXYZ + char *res = "desktopGrabber"; + char *val = get_string_resource (dpy, res, "DesktopGrabber"); + char buf1[255]; + char buf2[255]; + XrmValue value; + XrmDatabase db = XtDatabase (dpy); + sprintf (buf1, "%.100s.%.100s", progname, res); + sprintf (buf2, "%.200s -v", val); + value.addr = buf2; + value.size = strlen(buf2); + XrmPutResource (&db, buf1, "String", &value); +# endif /* !HAVE_JWXYZ */ +} + + +ENTRYPOINT void +init_photopile (ModeInfo *mi) +{ + int screen = MI_SCREEN(mi); + photopile_state *ss; + int wire = MI_IS_WIREFRAME(mi); + + MI_INIT (mi, sss); + ss = &sss[screen]; + ss->mi = mi; + + if ((ss->glx_context = init_GL(mi)) != NULL) { + reshape_photopile (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); + clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */ + } else { + MI_CLEARWINDOW(mi); + } + + ss->shadow = init_drop_shadow(); + ss->texfont = load_texture_font (MI_DISPLAY(mi), "font"); + + if (debug_p) + hack_resources (MI_DISPLAY (mi)); + + ss->frames = (image *)calloc (MI_COUNT(mi) + 1, sizeof(image)); + ss->nframe = 0; + if (!wire) + { + int i; + for (i = 0; i < MI_COUNT(mi) + 1; ++i) + { + glGenTextures (1, &(ss->frames[i].texid)); + if (ss->frames[i].texid <= 0) abort(); + } + } + + ss->mode = EARLY; + load_image(mi); /* start loading the first image */ +} + + +static void +draw_image (ModeInfo *mi, int i, GLfloat t, GLfloat s, GLfloat z) +{ + int wire = MI_IS_WIREFRAME(mi); + photopile_state *ss = &sss[MI_SCREEN(mi)]; + image *frame = ss->frames + i; + + position pos = interpolate(t, frame->pos); + GLfloat w = frame->geom.width * 0.5; + GLfloat h = frame->geom.height * 0.5; + GLfloat z1 = z - 0.25 / (MI_COUNT(mi) + 1); + GLfloat z2 = z - 0.5 / (MI_COUNT(mi) + 1); + GLfloat w1 = w; + GLfloat h1 = h; + GLfloat h2 = h; + + if (polaroid_p) + { + GLfloat minSize = MIN(w, h); + GLfloat maxSize = MAX(w, h); + + /* Clip or scale image to fit in the frame. + */ + if (clip_p) + { + w = h = minSize; + } + else + { + GLfloat scale = minSize / maxSize; + w *= scale; + h *= scale; + } + + w1 = minSize * 1.16; /* enlarge frame border */ + h1 = minSize * 1.5; + h2 = w1; + s /= 1.5; /* compensate for border size */ + } + + glPushMatrix(); + + /* Position and scale this image. + */ + glTranslatef (pos.x, pos.y, 0); + glRotatef (pos.angle, 0, 0, 1); + glScalef (s, s, 1); + + /* Draw the drop shadow. */ + if (shadows_p && !wire) + { + glColor3f (0, 0, 0); + draw_drop_shadow(ss->shadow, -w1, -h1, z2, 2.0 * w1, h1 + h2, 20.0); + glDisable (GL_BLEND); + } + + glDisable (GL_LIGHTING); + glEnable (GL_DEPTH_TEST); + glDisable (GL_CULL_FACE); + + /* Draw the retro instant-film frame. + */ + if (polaroid_p) + { + if (! wire) + { + glShadeModel (GL_SMOOTH); + glEnable (GL_LINE_SMOOTH); + glHint (GL_LINE_SMOOTH_HINT, GL_NICEST); + + glColor3f (1, 1, 1); + glBegin (GL_QUADS); + glVertex3f (-w1, -h1, z2); + glVertex3f ( w1, -h1, z2); + glVertex3f ( w1, h2, z2); + glVertex3f (-w1, h2, z2); + glEnd(); + } + + glLineWidth (1.0); + glColor3f (0.5, 0.5, 0.5); + glBegin (GL_LINE_LOOP); + glVertex3f (-w1, -h1, z); + glVertex3f ( w1, -h1, z); + glVertex3f ( w1, h2, z); + glVertex3f (-w1, h2, z); + glEnd(); + } + + /* Draw the image quad. + */ + if (! wire) + { + GLfloat texw = w / frame->tw; + GLfloat texh = h / frame->th; + GLfloat texx = (frame->geom.x + 0.5 * frame->geom.width) / frame->tw; + GLfloat texy = (frame->geom.y + 0.5 * frame->geom.height) / frame->th; + + glBindTexture (GL_TEXTURE_2D, frame->texid); + glEnable (GL_TEXTURE_2D); + glColor3f (1, 1, 1); + glBegin (GL_QUADS); + glTexCoord2f (texx - texw, texy + texh); glVertex3f (-w, -h, z1); + glTexCoord2f (texx + texw, texy + texh); glVertex3f ( w, -h, z1); + glTexCoord2f (texx + texw, texy - texh); glVertex3f ( w, h, z1); + glTexCoord2f (texx - texw, texy - texh); glVertex3f (-w, h, z1); + glEnd(); + glDisable (GL_TEXTURE_2D); + } + + /* Draw a box around it. + */ + if (! wire) + { + glShadeModel (GL_SMOOTH); + glEnable (GL_LINE_SMOOTH); + glHint (GL_LINE_SMOOTH_HINT, GL_NICEST); + } + glLineWidth (1.0); + glColor3f (0.5, 0.5, 0.5); + glBegin (GL_LINE_LOOP); + glVertex3f (-w, -h, z); + glVertex3f ( w, -h, z); + glVertex3f ( w, h, z); + glVertex3f (-w, h, z); + glEnd(); + + /* Draw a title under the image. + */ + if (titles_p) + { + int sw = 0, sh = 0; + int ascent, descent; + GLfloat tw = w * 2; + GLfloat th = h1 - h; + GLfloat scale = 1; + const char *title = frame->title ? frame->title : "(untitled)"; + XCharStruct e; + + texture_string_metrics (ss->texfont, title, &e, &ascent, &descent); + sw = e.width; + sh = ascent; /* + descent; */ + + /* Scale the text to match the pixel size of the photo */ + scale *= w / 150.0; + +# if defined(HAVE_COCOA) + scale /= 2; + if (MI_WIDTH(mi) > 2560) scale /= 2; /* Retina displays */ +# endif + +# if defined(HAVE_MOBILE) + scale /= 2; +# endif + + /* Clip characters off the left end of the string until it fits. */ + if (clip_p || polaroid_p) + while (sw * scale > tw && strlen (title) > 10) + { + title++; + texture_string_metrics (ss->texfont, title, &e, &ascent, &descent); + sw = e.width; + } + + if (th <= 0) /* Non-polaroid */ + th = -sh * 1.2; + + glTranslatef (-w, -h1, 0); + glTranslatef ((tw - sw*scale) / 2, (th - sh*scale) / 2, 0); + + glScalef (scale, scale, 1); + + if (wire || !polaroid_p) + { + glColor3f (1, 1, 1); + } + else + { + glColor3f (0.5, 0.5, 0.5); + } + + if (!wire) + { + glEnable (GL_TEXTURE_2D); + glEnable (GL_BLEND); + glDisable (GL_DEPTH_TEST); + print_texture_string (ss->texfont, title); + glEnable (GL_DEPTH_TEST); + } + else + { + glBegin (GL_LINE_LOOP); + glVertex3f (0, 0, 0); + glVertex3f (sw, 0, 0); + glVertex3f (sw, sh, 0); + glVertex3f (0, sh, 0); + glEnd(); + } + + } + + glPopMatrix(); +} + + +ENTRYPOINT void +draw_photopile (ModeInfo *mi) +{ + photopile_state *ss = &sss[MI_SCREEN(mi)]; + int i; + + if (!ss->glx_context) + return; + + glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(ss->glx_context)); + + if (ss->mode == EARLY) + if (loading_initial_image (mi)) + return; + + /* Only check the wall clock every 10 frames */ + if (ss->now == 0 || ss->draw_tick++ > 10) + { + ss->now = time((time_t *) 0); + if (ss->last_time == 0) ss->last_time = ss->now; + ss->draw_tick = 0; + } + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + { + GLfloat t; + + glPushMatrix(); + glTranslatef (MI_WIDTH(mi)/2, MI_HEIGHT(mi)/2, 0); + glRotatef(current_device_rotation(), 0, 0, 1); + glTranslatef (-MI_WIDTH(mi)/2, -MI_HEIGHT(mi)/2, 0); + + /* Handle state transitions. */ + switch (ss->mode) + { + case SHUFFLE: + if (--ss->mode_tick <= 0) + { + ss->nframe = (ss->nframe+1) % (MI_COUNT(mi)+1); + + ss->mode = NORMAL; + ss->last_time = time((time_t *) 0); + } + break; + case NORMAL: + if (ss->now - ss->last_time > duration) + { + ss->mode = LOADING; + load_image(mi); + } + break; + case LOADING: + if (ss->frames[ss->nframe].loaded_p) + { + set_new_positions(ss); + ss->mode = SHUFFLE; + ss->mode_tick = fade_ticks / speed; + } + break; + default: + abort(); + } + + t = 1.0 - ss->mode_tick / (fade_ticks / speed); + t = 0.5 * (1.0 - cos(M_PI * t)); + + /* Draw the images. */ + for (i = 0; i < MI_COUNT(mi) + (ss->mode == SHUFFLE); ++i) + { + int j = (ss->nframe + i + 1) % (MI_COUNT(mi) + 1); + + if (ss->frames[j].loaded_p) + { + GLfloat s = 1.0; + GLfloat z = (GLfloat)i / (MI_COUNT(mi) + 1); + + switch (ss->mode) + { + case SHUFFLE: + if (i == MI_COUNT(mi)) + { + s *= t; + } + else if (i == 0) + { + s *= 1.0 - t; + } + break; + case NORMAL: + case LOADING: + t = 1.0; + break; + default: + abort(); + } + + draw_image(mi, j, t, s, z); + } + } + glPopMatrix(); + } + + if (mi->fps_p) do_fps (mi); + glFinish(); + glXSwapBuffers (MI_DISPLAY (mi), MI_WINDOW(mi)); +} + +XSCREENSAVER_MODULE ("Photopile", photopile) + +#endif /* USE_GL */ -- cgit v1.2.3-55-g7522