+ENTRYPOINT ModeSpecOpt slideshow_opts = {countof(opts), opts, countof(vars), vars, NULL};
+
+
+static const char *
+blurb (void)
+{
+# ifdef HAVE_COCOA
+ return "GLSlideshow";
+# else
+ static char buf[255];
+ time_t now = time ((time_t *) 0);
+ char *ct = (char *) ctime (&now);
+ int n = strlen(progname);
+ if (n > 100) n = 99;
+ strncpy(buf, progname, n);
+ buf[n++] = ':';
+ buf[n++] = ' ';
+ strncpy(buf+n, ct+11, 8);
+ strcpy(buf+n+9, ": ");
+ return buf;
+# endif
+}
+
+
+/* Returns the current time in seconds as a double.
+ */
+static double
+double_time (void)
+{
+ struct timeval now;
+# ifdef GETTIMEOFDAY_TWO_ARGS
+ struct timezone tzp;
+ gettimeofday(&now, &tzp);
+# else
+ gettimeofday(&now);
+# endif
+
+ return (now.tv_sec + ((double) now.tv_usec * 0.000001));
+}
+
+
+static void image_loaded_cb (const char *filename, XRectangle *geom,
+ int image_width, int image_height,
+ int texture_width, int texture_height,
+ void *closure);
+
+
+/* Allocate an image structure and start a file loading in the background.
+ */
+static image *
+alloc_image (ModeInfo *mi)
+{
+ slideshow_state *ss = &sss[MI_SCREEN(mi)];
+ int wire = MI_IS_WIREFRAME(mi);
+ image *img = (image *) calloc (1, sizeof (*img));
+
+ img->id = ++ss->image_id;
+ img->loaded_p = False;
+ img->used_p = False;
+ img->mi = mi;
+
+ glGenTextures (1, &img->texid);
+ if (img->texid <= 0) abort();
+
+ ss->image_load_time = ss->now;
+
+ if (wire)
+ image_loaded_cb (0, 0, 0, 0, 0, 0, img);
+ else
+ load_texture_async (mi->xgwa.screen, mi->window, *ss->glx_context,
+ 0, 0, mipmap_p, img->texid, image_loaded_cb, img);
+
+ ss->images[ss->nimages++] = img;
+ if (ss->nimages >= countof(ss->images)) abort();
+
+ return img;
+}
+
+
+/* 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)
+{
+ image *img = (image *) closure;
+ ModeInfo *mi = img->mi;
+ /* slideshow_state *ss = &sss[MI_SCREEN(mi)]; */
+
+ int wire = MI_IS_WIREFRAME(mi);
+
+ if (wire)
+ {
+ img->w = MI_WIDTH (mi) * (0.5 + frand (1.0));
+ img->h = MI_HEIGHT (mi);
+ img->geom.width = img->w;
+ img->geom.height = img->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);
+
+ img->w = image_width;
+ img->h = image_height;
+ img->tw = texture_width;
+ img->th = texture_height;
+ img->geom = *geom;
+ img->title = (filename ? strdup (filename) : 0);
+
+ /* If the image's width doesn't come back as the width of the screen,
+ then the image must have been scaled down (due to insufficient
+ texture memory.) Scale up the coordinates to stretch the image
+ to fill the window.
+ */
+ if (img->w != MI_WIDTH(mi))
+ {
+ double scale = (double) MI_WIDTH(mi) / img->w;
+ img->w *= scale;
+ img->h *= scale;
+ img->tw *= scale;
+ img->th *= scale;
+ img->geom.x *= scale;
+ img->geom.y *= scale;
+ img->geom.width *= scale;
+ img->geom.height *= scale;
+ }
+
+ /* xscreensaver-getimage returns paths relative to the image directory
+ now, so leave the sub-directory part in. Unless it's an absolute path.
+ */
+ if (img->title && img->title[0] == '/')
+ {
+ /* strip filename to part between last "/" and last ".". */
+ char *s = strrchr (img->title, '/');
+ if (s) strcpy (img->title, s+1);
+ s = strrchr (img->title, '.');
+ if (s) *s = 0;
+ }
+
+ if (debug_p)
+ fprintf (stderr, "%s: loaded img %2d: \"%s\"\n",
+ blurb(), img->id, (img->title ? img->title : "(null)"));
+ DONE:
+
+ img->loaded_p = True;
+}