+/* 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));
+}
+
+
+/* return the next larger power of 2. */
+static int
+to_pow2 (int i)
+{
+ static unsigned int pow2[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
+ 2048, 4096, 8192, 16384, 32768, 65536 };
+ int j;
+ for (j = 0; j < countof(pow2); j++)
+ if (pow2[j] >= i) return pow2[j];
+ abort(); /* too big! */
+}
+
+
+/* Loads the given XImage into GL's texture memory.
+ The image may be of any size.
+ If mipmap_p is true, then make mipmaps instead of just a single texture.
+ Writes to stderr and returns False on error.
+ */
+static Bool
+ximage_to_texture (XImage *ximage,
+ GLint type, GLint format,
+ int *width_return,
+ int *height_return,
+ XRectangle *geometry,
+ Bool mipmap_p)
+{
+ int max_reduction = 7;
+ int err_count = 0;
+ GLenum err = 0;
+ int orig_width = ximage->width;
+ int orig_height = ximage->height;
+ int tex_width = 0;
+ int tex_height = 0;
+
+ AGAIN:
+
+ if (mipmap_p)
+ {
+ /* gluBuild2DMipmaps doesn't require textures to be a power of 2. */
+ tex_width = ximage->width;
+ tex_height = ximage->height;
+
+ if (debug_p)
+ fprintf (stderr, "%s: mipmap %d x %d\n",
+ progname, ximage->width, ximage->height);
+
+ gluBuild2DMipmaps (GL_TEXTURE_2D, 3, ximage->width, ximage->height,
+ format, type, ximage->data);
+ err = glGetError();
+ }
+ else
+ {
+ /* glTexImage2D() requires the texture sizes to be powers of 2.
+ So first, create a texture of that size (but don't write any
+ data into it.)
+ */
+ tex_width = to_pow2 (ximage->width);
+ tex_height = to_pow2 (ximage->height);
+
+ if (debug_p)
+ fprintf (stderr, "%s: texture %d x %d (%d x %d)\n",
+ progname, ximage->width, ximage->height,
+ tex_width, tex_height);
+
+ glTexImage2D (GL_TEXTURE_2D, 0, 3, tex_width, tex_height, 0,
+ format, type, 0);
+ err = glGetError();
+
+ /* Now load our non-power-of-2 image data into the existing texture. */
+ if (!err)
+ {
+ glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0,
+ ximage->width, ximage->height,
+ GL_RGBA, GL_UNSIGNED_BYTE, ximage->data);
+ err = glGetError();
+ }
+ }
+
+ if (err)
+ {
+ char buf[100];
+ const char *s = (char *) gluErrorString (err);
+
+ if (!s || !*s)
+ {
+ sprintf (buf, "unknown error %d", err);
+ s = buf;
+ }
+
+ while (glGetError() != GL_NO_ERROR)
+ ; /* clear any lingering errors */
+
+ if (++err_count > max_reduction)
+ {
+ fprintf (stderr,
+ "\n"
+ "%s: %dx%d texture failed, even after reducing to %dx%d:\n"
+ "%s: The error was: \"%s\".\n"
+ "%s: probably this means "
+ "\"your video card is worthless and weak\"?\n\n",
+ progname, orig_width, orig_height,
+ ximage->width, ximage->height,
+ progname, s,
+ progname);
+ return False;
+ }
+ else
+ {
+ if (debug_p)
+ fprintf (stderr, "%s: mipmap error (%dx%d): %s\n",
+ progname, ximage->width, ximage->height, s);
+ halve_image (ximage, geometry);
+ goto AGAIN;
+ }
+ }
+
+ if (width_return) *width_return = tex_width;
+ if (height_return) *height_return = tex_height;
+ return True;
+}
+
+
+static void screen_to_texture_async_cb (Screen *screen,
+ Window window, Drawable drawable,
+ const char *name, XRectangle *geometry,
+ void *closure);
+
+
+/* Grabs an image of the desktop (or another random image file) and
+ loads tht image into GL's texture memory.
+ Writes to stderr and returns False on error.
+ */
+Bool
+screen_to_texture (Screen *screen, Window window,
+ int desired_width, int desired_height,
+ Bool mipmap_p,
+ char **filename_return,
+ XRectangle *geometry_return,
+ int *image_width_return,
+ int *image_height_return,
+ int *texture_width_return,
+ int *texture_height_return)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ img_closure *data = (img_closure *) calloc (1, sizeof(*data));
+ XWindowAttributes xgwa;
+ char *filename = 0;
+ XRectangle geom = { 0, 0, 0, 0 };
+ int wret;
+
+ if (! image_width_return)
+ image_width_return = &wret;
+
+ if (debug_p)
+ data->load_time = double_time();
+
+ data->texid = -1;
+ data->mipmap_p = mipmap_p;
+ data->filename_return = filename_return;
+ data->geometry_return = geometry_return;
+ data->image_width_return = image_width_return;
+ data->image_height_return = image_height_return;
+ data->texture_width_return = texture_width_return;
+ data->texture_height_return = texture_height_return;
+
+ XGetWindowAttributes (dpy, window, &xgwa);
+ data->pix_width = xgwa.width;
+ data->pix_height = xgwa.height;
+ data->pix_depth = xgwa.depth;
+
+ if (desired_width && desired_width < xgwa.width)
+ data->pix_width = desired_width;
+ if (desired_height && desired_height < xgwa.height)
+ data->pix_height = desired_height;
+
+ data->pixmap = XCreatePixmap (dpy, window, data->pix_width, data->pix_height,
+ data->pix_depth);
+ load_random_image (screen, window, data->pixmap, &filename, &geom);
+ screen_to_texture_async_cb (screen, window, data->pixmap, filename, &geom,
+ data);
+
+ return (*image_width_return != 0);
+}
+
+
+/* Like the above, but the image is loaded in a background process,
+ and a callback is run when the loading is complete.
+ When the callback is called, the image data will have been loaded
+ into texture number `texid' (via glBindTexture.)
+
+ If an error occurred, width/height will be 0.
+ */
+void
+screen_to_texture_async (Screen *screen, Window window,
+ int desired_width, int desired_height,
+ Bool mipmap_p,
+ GLuint texid,
+ void (*callback) (const char *filename,
+ XRectangle *geometry,
+ int image_width,
+ int image_height,
+ int texture_width,
+ int texture_height,
+ void *closure),
+ void *closure)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ XWindowAttributes xgwa;
+ img_closure *data = (img_closure *) calloc (1, sizeof(*data));
+
+ if (debug_p)
+ data->load_time = double_time();
+
+ data->texid = texid;
+ data->mipmap_p = mipmap_p;
+ data->callback = callback;
+ data->closure = closure;
+
+ XGetWindowAttributes (dpy, window, &xgwa);
+ data->pix_width = xgwa.width;
+ data->pix_height = xgwa.height;
+ data->pix_depth = xgwa.depth;
+
+ if (desired_width && desired_width < xgwa.width)
+ data->pix_width = desired_width;
+ if (desired_height && desired_height < xgwa.height)
+ data->pix_height = desired_height;
+
+ data->pixmap = XCreatePixmap (dpy, window, data->pix_width, data->pix_height,
+ data->pix_depth);
+ fork_load_random_image (screen, window, data->pixmap,
+ screen_to_texture_async_cb, data);
+}
+
+
+/* Once we have an XImage, this loads it into GL.
+ This is used in both synchronous and asynchronous mode.
+ */
+static void
+screen_to_texture_async_cb (Screen *screen, Window window, Drawable drawable,
+ const char *name, XRectangle *geometry,
+ void *closure)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ Bool ok;
+ XImage *ximage;
+ GLint type, format;
+ int iw=0, ih=0, tw=0, th=0;
+ double cvt_time=0, tex_time=0, done_time=0;
+ img_closure *data = (img_closure *) closure;
+ /* copy closure data to stack and free the original before running cb */
+ img_closure dd = *data;
+ memset (data, 0, sizeof (*data));
+ free (data);
+ data = 0;
+
+ if (geometry->width <= 0 || geometry->height <= 0)
+ {
+ /* This can happen if an old version of xscreensaver-getimage
+ is installed. */
+ geometry->x = 0;
+ geometry->y = 0;
+ geometry->width = dd.pix_width;
+ geometry->height = dd.pix_height;
+ }
+
+ if (geometry->width <= 0 || geometry->height <= 0)
+ abort();
+
+ if (debug_p)
+ cvt_time = double_time();
+
+# ifdef REFORMAT_IMAGE_DATA
+ ximage = pixmap_to_gl_ximage (screen, window, dd.pixmap);
+ format = GL_RGBA;
+ type = GL_UNSIGNED_BYTE;
+
+#else /* ! REFORMAT_IMAGE_DATA */
+ {
+ Visual *visual = DefaultVisualOfScreen (screen);
+ GLint swap;
+
+ ximage = XCreateImage (dpy, visual, dd.pix_depth, ZPixmap, 0, 0,
+ dd.pix_width, dd.pix_height, 32, 0);
+
+ /* Note: height+2 in "to" to be to work around an array bounds overrun
+ in gluBuild2DMipmaps / gluScaleImage. */
+ ximage->data = (char *) calloc (ximage->height+2, ximage->bytes_per_line);
+
+ if (!ximage->data ||
+ !XGetSubImage (dpy, dd.pixmap, 0, 0, ximage->width, ximage->height,
+ ~0L, ximage->format, ximage, 0, 0))
+ {
+ XDestroyImage (ximage);
+ ximage = 0;
+ }
+
+ gl_settings_for_ximage (ximage, &type, &format, &swap);
+ glPixelStorei (GL_UNPACK_SWAP_BYTES, !swap);