2 * glslideshow - takes a snapshot of the screen and smoothly scans around
5 * Copyright (c) 2002, 2003 Mike Oliphant (oliphant@gtk.org)
7 * Framework based on flipscreen3d
8 * Copyright (C) 2001 Ben Buxton (bb@cactii.net)
10 * Smooth transitions between multiple files added by
11 * Jamie Zawinski <jwz@jwz.org>
13 * Permission to use, copy, modify, distribute, and sell this software and its
14 * documentation for any purpose is hereby granted without fee, provided that
15 * the above copyright notice appear in all copies and that both that
16 * copyright notice and this permission notice appear in supporting
17 * documentation. No representations are made about the suitability of this
18 * software for any purpose. It is provided "as is" without express or
23 #include <X11/Intrinsic.h>
26 # define PROGCLASS "GLSlideshow"
27 # define HACK_INIT init_slideshow
28 # define HACK_DRAW draw_slideshow
29 # define HACK_RESHAPE reshape_slideshow
30 # define slideshow_opts xlockmore_opts
32 # define DEF_FADE "True"
33 # define DEF_DURATION "30"
34 # define DEF_ZOOM "75"
36 #define DEFAULTS "*delay: 20000 \n" \
37 "*fade: " DEF_FADE "\n" \
38 "*duration: " DEF_DURATION "\n" \
39 "*zoom: " DEF_ZOOM "\n" \
40 "*wireframe: False \n" \
41 "*showFPS: False \n" \
42 "*fpsSolid: True \n" \
43 "*desktopGrabber: xscreensaver-getimage -no-desktop %s\n"
45 # include "xlockmore.h"
47 #define RRAND(range) (random()%(range))
49 #define countof(x) (sizeof((x))/sizeof((*x)))
58 #include "grab-ximage.h"
61 #define QW 12.4 /* arbitrary size of the textured quads we render */
66 #define NQUADS 2 /* sometimes we draw 2 at once */
69 GLXContext *glx_context;
72 int tw, th; /* texture width, height */
73 GLfloat max_tx, max_ty;
75 GLfloat qw, qh; /* q? are for the quad we'll draw */
76 GLfloat qx[NQUADS], qy[NQUADS], qz[NQUADS];
77 GLfloat dx[NQUADS], dy[NQUADS], dz[NQUADS];
79 GLuint texids[NQUADS]; /* two textures: current img, incoming img */
81 time_t start_time; /* when we started displaying this image */
85 int in_transition; /* true while we're drawing overlapping imgs */
86 int in_file_transition; /* ...plus loading a new image */
87 int frames; /* how many frames we've drawn in this pan */
91 static slideshow_state *sss = NULL;
94 /* Command-line arguments
96 int fade; /* If true, transitions between pans (and between images) will
97 be translucent; otherwise, they will be jump-cuts. */
98 int duration; /* how many seconds until loading a new image */
99 int zoom; /* how far in to zoom when panning, in percent of image size:
100 that is, 75 means "when zoomed all the way in, 75% of the
101 image will be on screen." */
104 /* blah, apparently other magic numbers elsewhere in the file also
105 affect this... can't just change these to speed up / slow down...
107 static int frames_per_pan = 300;
108 static int frames_per_fade = 100;
111 static XrmOptionDescRec opts[] = {
112 {"+fade", ".slideshow.fade", XrmoptionNoArg, (caddr_t) "False" },
113 {"-fade", ".slideshow.fade", XrmoptionNoArg, (caddr_t) "True" },
114 {"-duration", ".slideshow.duration", XrmoptionSepArg, 0},
115 {"-zoom", ".slideshow.zoom", XrmoptionSepArg, 0}
118 static argtype vars[] = {
119 {(caddr_t *) &fade, "fade", "Fade", DEF_FADE, t_Bool},
120 {(caddr_t *) &duration, "duration", "Duration", DEF_DURATION, t_Int},
121 {(caddr_t *) &zoom, "zoom", "Zoom", DEF_ZOOM, t_Int}
124 ModeSpecOpt slideshow_opts = {countof(opts), opts, countof(vars), vars, NULL};
127 /* draw the texture mapped quad.
128 `screen' specifies which of the independently-moving images to draw.
131 showscreen (ModeInfo *mi, int wire, int screen, int texid_index)
133 slideshow_state *ss = &sss[MI_SCREEN(mi)];
134 static GLfloat r = 1, g = 1, b = 1, a = 1;
138 if (screen >= NQUADS) abort();
139 qxw = ss->qx[screen] + ss->qw;
140 qyh = ss->qy[screen] - ss->qh;
146 ss->qx[screen] += ss->dx[screen];
147 ss->qy[screen] -= ss->dy[screen];
148 ss->qz[screen] += ss->dz[screen];
150 glTranslatef(0, 0, ss->qz[screen]);
152 if (ss->in_transition) {
153 a = 1 - (ss->frames/100.0);
155 if (screen != ss->curr_screen) {
163 glColor4f(r, g, b, a);
166 glEnable(GL_TEXTURE_2D);
168 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
169 glDepthMask(GL_FALSE);
170 glBindTexture (GL_TEXTURE_2D, ss->texids[texid_index]);
173 glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
177 glTexCoord2f(0, ss->max_ty);
180 glTexCoord2f(ss->max_tx, ss->max_ty);
183 glTexCoord2f(ss->max_tx, 0);
195 for (i = x; i < w; i += ((w-x)/k))
200 for (i = y; i >= h; i -= ((y-h)/k))
212 glDisable(GL_TEXTURE_2D);
213 glDepthMask(GL_TRUE);
215 glBegin(GL_LINE_LOOP);
223 glTranslatef(0, 0, -ss->qz[screen]);
228 display (ModeInfo *mi)
230 slideshow_state *ss = &sss[MI_SCREEN(mi)];
231 int wire = MI_IS_WIREFRAME(mi);
232 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
239 showscreen (mi, wire, ss->curr_screen, ss->curr_texid_index);
241 if (ss->in_transition)
242 showscreen (mi, wire, 1-ss->curr_screen,
243 (ss->in_file_transition
244 ? 1 - ss->curr_texid_index
245 : ss->curr_texid_index));
252 reshape_slideshow (ModeInfo *mi, int width, int height)
254 glViewport(0,0,(GLint)width, (GLint) height);
255 glMatrixMode(GL_PROJECTION);
257 gluPerspective(45, 1, 2.0, 85);
258 glMatrixMode(GL_MODELVIEW);
262 reset (ModeInfo *mi, int screen)
264 slideshow_state *ss = &sss[MI_SCREEN(mi)];
267 if (screen >= NQUADS) abort();
268 ss->dz[screen] = (-.02+(RRAND(400)/10000.0)) * (GLfloat) zoom/100.0;
270 if (ss->dz[screen] < 0.0) {
271 ss->qz[screen] = 6.0 + RRAND(300)/100.0;
274 ss->qz[screen] = 1.0 + RRAND(300)/100.0;
277 ss->qz[screen] *= (GLfloat) zoom/100.0;
279 ss->dx[screen] = -.02 + RRAND(400)/10000.0;
280 ss->dy[screen] =- .01 + RRAND(200)/10000.0;
282 ss->dx[screen] *= ss->qz[screen]/12.0;
283 ss->dy[screen] *= ss->qz[screen]/12.0;
285 ss->qx[screen] = QX - ss->dx[screen] * 40.0 * ss->qz[screen];
286 ss->qy[screen] = QY + ss->dy[screen] * 40.0 * ss->qz[screen];
291 getSnapshot (ModeInfo *mi, int into_texid)
293 slideshow_state *ss = &sss[MI_SCREEN(mi)];
297 if(MI_IS_WIREFRAME(mi)) return;
302 ximage = screen_to_ximage (mi->xgwa.screen, mi->window);
304 ss->tw = mi->xgwa.width;
305 ss->th = mi->xgwa.height;
306 /* ss->tw = ximage->width; */
307 /* ss->th = ximage->height; */
309 ss->qw *= (GLfloat) ss->tw / MI_WIDTH(mi);
310 ss->qh *= (GLfloat) ss->th / MI_HEIGHT(mi);
312 ss->max_tx = (GLfloat) ss->tw / (GLfloat) ximage->width;
313 ss->max_ty = (GLfloat) ss->th / (GLfloat) ximage->height;
315 glBindTexture (GL_TEXTURE_2D, into_texid);
317 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
318 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
319 GL_LINEAR_MIPMAP_LINEAR);
322 status = gluBuild2DMipmaps(GL_TEXTURE_2D, 3,
323 ximage->width, ximage->height,
324 GL_RGBA, GL_UNSIGNED_BYTE, ximage->data);
326 if(!status && glGetError())
327 /* Some implementations of gluBuild2DMipmaps(), but set a GL error anyway.
328 We could just call check_gl_error(), but that would exit. */
332 const char *s = gluErrorString (status);
334 fprintf(stderr, "%s: error mipmapping %dx%d texture: %s\n",
335 progname, ximage->width, ximage->height,
336 (s ? s : "(unknown)"));
337 fprintf(stderr, "%s: turning on -wireframe.\n", progname);
338 MI_IS_WIREFRAME(mi) = 1;
342 check_gl_error("mipmapping"); /* should get a return code instead of a
343 GL error, but just in case... */
347 XDestroyImage(ximage);
349 ss->start_time = time ((time_t *) 0);
353 init_slideshow (ModeInfo *mi)
355 int screen = MI_SCREEN(mi);
359 if((sss = (slideshow_state *)
360 calloc(MI_NUM_SCREENS(mi), sizeof(slideshow_state))) == NULL)
365 ss->window = MI_WINDOW(mi);
369 if((ss->glx_context = init_GL(mi)) != NULL) {
370 reshape_slideshow(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
375 glClearColor(0.0,0.0,0.0,0.0);
377 if(! MI_IS_WIREFRAME(mi)) {
378 glShadeModel(GL_SMOOTH);
379 glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
380 glEnable(GL_DEPTH_TEST);
381 glEnable(GL_CULL_FACE);
382 glCullFace(GL_FRONT);
383 glDisable(GL_LIGHTING);
385 glGenTextures (1, &ss->texids[0]); /* texture for image A */
386 glGenTextures (1, &ss->texids[1]); /* texture for image B */
389 reset(mi, ss->curr_screen);
390 ss->curr_texid_index = 0;
391 getSnapshot(mi, ss->texids[ss->curr_texid_index]);
395 draw_slideshow (ModeInfo *mi)
397 slideshow_state *ss = &sss[MI_SCREEN(mi)];
398 Window w = MI_WINDOW(mi);
399 Display *disp = MI_DISPLAY(mi);
401 if(!ss->glx_context) return;
403 glXMakeCurrent(disp, w, *(ss->glx_context));
405 if (ss->frames == frames_per_pan) {
407 time_t now = time ((time_t *) 0);
410 ss->in_transition = 1;
411 reset (mi, 1 - ss->curr_screen);
413 if (ss->start_time + duration <= now) {
414 ss->in_file_transition = 1;
415 getSnapshot(mi, ss->texids[1 - ss->curr_texid_index]);
419 reset(mi, ss->curr_screen);
421 if (ss->start_time + duration <= now)
422 getSnapshot(mi, ss->texids[ss->curr_texid_index]);
426 if (fade && ss->in_transition && ss->frames == frames_per_fade) {
427 ss->in_transition = 0;
428 ss->curr_screen = 1 - ss->curr_screen;
430 if (ss->in_file_transition) {
431 ss->in_file_transition = 0;
432 ss->curr_texid_index = 1 - ss->curr_texid_index;
440 if(mi->fps_p) do_fps(mi);
443 glXSwapBuffers(disp, w);
447 release_slideshow (ModeInfo *mi)
450 (void) free((void *) sss);