http://www.mirrorservice.org/sites/master.us.finkmirrors.net/distfiles/md5/fa43fdd68d...
[xscreensaver] / hacks / glx / glslideshow.c
1 /*
2  * glslideshow - takes a snapshot of the screen and smoothly scans around
3  *               in it
4  *
5  * Copyright (c) 2002, 2003 Mike Oliphant (oliphant@gtk.org)
6  *
7  * Framework based on flipscreen3d
8  *   Copyright (C) 2001 Ben Buxton (bb@cactii.net)
9  *
10  * Smooth transitions between multiple files added by
11  * Jamie Zawinski <jwz@jwz.org>
12  *
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
19  * implied warranty.
20  *
21  */
22
23 #include <X11/Intrinsic.h>
24
25
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
31
32 # define DEF_FADE     "True"
33 # define DEF_DURATION "30"
34 # define DEF_ZOOM     "75"
35
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"
44
45 # include "xlockmore.h"
46
47 #define RRAND(range) (random()%(range))
48 #undef countof
49 #define countof(x) (sizeof((x))/sizeof((*x)))
50
51 #ifdef USE_GL
52
53 #include <GL/glu.h>
54 #include <math.h>
55 #include <sys/time.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include "grab-ximage.h"
59
60
61 #define QW 12.4   /* arbitrary size of the textured quads we render */
62 #define QH 12.4
63 #define QX -6.2
64 #define QY  6.2
65
66 #define NQUADS 2  /* sometimes we draw 2 at once */
67
68 typedef struct {
69   GLXContext *glx_context;
70   Window window;
71
72   int tw, th;                   /* texture width, height */
73   GLfloat max_tx, max_ty;
74
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];
78
79   GLuint texids[NQUADS];        /* two textures: current img, incoming img */
80
81   time_t start_time;            /* when we started displaying this image */
82
83   int curr_screen;
84   int curr_texid_index;
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 */
88
89 } slideshow_state;
90
91 static slideshow_state *sss = NULL;
92
93
94 /* Command-line arguments
95  */
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."  */
102
103
104 /* blah, apparently other magic numbers elsewhere in the file also
105    affect this...   can't just change these to speed up / slow down...
106  */
107 static int frames_per_pan  = 300;
108 static int frames_per_fade = 100;
109
110
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}
116 };
117
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}
122 };
123
124 ModeSpecOpt slideshow_opts = {countof(opts), opts, countof(vars), vars, NULL};
125
126
127 /* draw the texture mapped quad.
128    `screen' specifies which of the independently-moving images to draw.
129  */
130 static void
131 showscreen (ModeInfo *mi, int wire, int screen, int texid_index)
132 {
133   slideshow_state *ss = &sss[MI_SCREEN(mi)];
134   static GLfloat r = 1, g = 1, b = 1, a = 1;
135   GLfloat qxw, qyh;
136   GLfloat x, y, w, h;
137
138   if (screen >= NQUADS) abort();
139   qxw = ss->qx[screen] + ss->qw;
140   qyh = ss->qy[screen] - ss->qh;
141   x = ss->qx[screen];
142   y = ss->qy[screen];
143   w = qxw;
144   h = qyh;
145
146   ss->qx[screen] += ss->dx[screen];
147   ss->qy[screen] -= ss->dy[screen];
148   ss->qz[screen] += ss->dz[screen];
149
150   glTranslatef(0, 0, ss->qz[screen]);
151
152   if (ss->in_transition) {
153     a = 1 - (ss->frames/100.0);
154
155     if (screen != ss->curr_screen) {
156       a = 1-a;
157     }
158   }
159   else {
160     a = 1;
161   }
162
163   glColor4f(r, g, b, a);
164
165   if(!wire) {
166     glEnable(GL_TEXTURE_2D);
167     glEnable(GL_BLEND);
168     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
169     glDepthMask(GL_FALSE);
170     glBindTexture (GL_TEXTURE_2D, ss->texids[texid_index]);
171   }
172
173   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
174
175   glNormal3f(0, 0, 1);
176
177   glTexCoord2f(0, ss->max_ty);
178   glVertex3f(x, y, 0);
179
180   glTexCoord2f(ss->max_tx, ss->max_ty);
181   glVertex3f(w, y, 0);
182
183   glTexCoord2f(ss->max_tx, 0);
184   glVertex3f(w, h, 0);
185
186   glTexCoord2f(0, 0);
187   glVertex3f(x, h, 0);
188
189   glEnd();
190
191   if (wire) {
192     GLfloat i;
193     int k = 10;
194     glBegin(GL_LINES);
195     for (i = x; i < w; i += ((w-x)/k))
196       {
197         glVertex3f(i, y, 0);
198         glVertex3f(i, h, 0);
199       }
200     for (i = y; i >= h; i -= ((y-h)/k))
201       {
202         glVertex3f(x, i, 0);
203         glVertex3f(w, i, 0);
204       }
205     glVertex3f(x, y, 0);
206     glVertex3f(w, h, 0);
207     glVertex3f(x, h, 0);
208     glVertex3f(w, y, 0);
209     glEnd();
210   }
211
212   glDisable(GL_TEXTURE_2D);
213   glDepthMask(GL_TRUE);
214   
215   glBegin(GL_LINE_LOOP);
216   glVertex3f(x, y, 0);
217   glVertex3f(x, h, 0);
218   glVertex3f(w, h, 0);
219   glVertex3f(w, y, 0);
220   glEnd();
221   glDisable(GL_BLEND);
222
223   glTranslatef(0, 0, -ss->qz[screen]);
224 }
225
226
227 static void
228 display (ModeInfo *mi)
229 {
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);
233   glLoadIdentity();
234   gluLookAt(0, 0, 15,
235             0, 0, 0,
236             0, 1, 0);
237   glPushMatrix();
238
239   showscreen (mi, wire, ss->curr_screen, ss->curr_texid_index);
240
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));
246
247   glPopMatrix();
248   glFlush();
249 }
250
251 void
252 reshape_slideshow (ModeInfo *mi, int width, int height)
253 {
254   glViewport(0,0,(GLint)width, (GLint) height);
255   glMatrixMode(GL_PROJECTION);
256   glLoadIdentity();
257   gluPerspective(45, 1, 2.0, 85);
258   glMatrixMode(GL_MODELVIEW);
259 }
260
261 static void
262 reset (ModeInfo *mi, int screen)
263 {
264   slideshow_state *ss = &sss[MI_SCREEN(mi)];
265   ss->frames = 0;
266
267   if (screen >= NQUADS) abort();
268   ss->dz[screen] = (-.02+(RRAND(400)/10000.0)) * (GLfloat) zoom/100.0;
269
270   if (ss->dz[screen] < 0.0) {
271     ss->qz[screen] = 6.0 + RRAND(300)/100.0;
272   }
273   else {
274     ss->qz[screen] = 1.0 + RRAND(300)/100.0;
275   }
276
277   ss->qz[screen] *= (GLfloat) zoom/100.0;
278
279   ss->dx[screen] = -.02 + RRAND(400)/10000.0;
280   ss->dy[screen] =- .01 + RRAND(200)/10000.0;
281
282   ss->dx[screen] *= ss->qz[screen]/12.0;
283   ss->dy[screen] *= ss->qz[screen]/12.0;
284
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];  
287 }
288
289
290 static void
291 getSnapshot (ModeInfo *mi, int into_texid)
292 {
293   slideshow_state *ss = &sss[MI_SCREEN(mi)];
294   XImage *ximage;
295   int status;
296   
297   if(MI_IS_WIREFRAME(mi)) return;
298
299   ss->qw = QW;
300   ss->qh = QH;
301
302   ximage = screen_to_ximage (mi->xgwa.screen, mi->window);
303
304   ss->tw = ximage->width;
305   ss->th = ximage->height;
306   
307   ss->qw *= (GLfloat) ss->tw / MI_WIDTH(mi);
308   ss->qh *= (GLfloat) ss->th / MI_HEIGHT(mi);
309   
310   ss->max_tx = (GLfloat) ss->tw / (GLfloat) ximage->width;
311   ss->max_ty = (GLfloat) ss->th / (GLfloat) ximage->height;
312
313   glBindTexture (GL_TEXTURE_2D, into_texid);
314
315   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
316   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
317                   GL_LINEAR_MIPMAP_LINEAR);
318   
319   clear_gl_error();
320   status = gluBuild2DMipmaps(GL_TEXTURE_2D, 3,
321                              ximage->width, ximage->height,
322                              GL_RGBA, GL_UNSIGNED_BYTE, ximage->data);
323   
324   if(!status && glGetError())
325    /* Some implementations of gluBuild2DMipmaps(), but set a GL error anyway.
326       We could just call check_gl_error(), but that would exit. */
327     status = -1;
328
329   if(status) {
330     const char *s = gluErrorString (status);
331
332     fprintf(stderr, "%s: error mipmapping %dx%d texture: %s\n",
333             progname, ximage->width, ximage->height,
334             (s ? s : "(unknown)"));
335     fprintf(stderr, "%s: turning on -wireframe.\n", progname);
336     MI_IS_WIREFRAME(mi) = 1;
337     clear_gl_error();
338   }
339
340   check_gl_error("mipmapping");  /* should get a return code instead of a
341                                     GL error, but just in case... */
342   
343   free(ximage->data);
344   ximage->data = 0;
345   XDestroyImage(ximage);
346
347   ss->start_time = time ((time_t *) 0);
348 }
349
350 void
351 init_slideshow (ModeInfo *mi)
352 {
353   int screen = MI_SCREEN(mi);
354   slideshow_state *ss;
355   
356   if(sss == NULL) {
357     if((sss = (slideshow_state *)
358         calloc(MI_NUM_SCREENS(mi), sizeof(slideshow_state))) == NULL)
359       return;
360   }
361
362   ss = &sss[screen];
363   ss->window = MI_WINDOW(mi);
364   ss->qw = QW;
365   ss->qh = QH;
366
367   if((ss->glx_context = init_GL(mi)) != NULL) {
368     reshape_slideshow(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
369   } else {
370     MI_CLEARWINDOW(mi);
371   }
372
373   glClearColor(0.0,0.0,0.0,0.0);
374   
375   if(! MI_IS_WIREFRAME(mi)) {
376     glShadeModel(GL_SMOOTH);
377     glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
378     glEnable(GL_DEPTH_TEST);
379     glEnable(GL_CULL_FACE);
380     glCullFace(GL_FRONT);
381     glDisable(GL_LIGHTING);
382
383     glGenTextures (1, &ss->texids[0]);  /* texture for image A */
384     glGenTextures (1, &ss->texids[1]);  /* texture for image B */
385   }
386   
387   reset(mi, ss->curr_screen);
388   ss->curr_texid_index = 0;
389   getSnapshot(mi, ss->texids[ss->curr_texid_index]);
390 }
391
392 void
393 draw_slideshow (ModeInfo *mi)
394 {
395   slideshow_state *ss = &sss[MI_SCREEN(mi)];
396   Window w = MI_WINDOW(mi);
397   Display *disp = MI_DISPLAY(mi);
398
399   if(!ss->glx_context) return;
400
401   glXMakeCurrent(disp, w, *(ss->glx_context));
402   
403   if (ss->frames == frames_per_pan) {
404
405     time_t now = time ((time_t *) 0);
406
407     if(fade) {
408       ss->in_transition = 1;
409       reset (mi, 1 - ss->curr_screen);
410
411       if (ss->start_time + duration <= now) {
412         ss->in_file_transition = 1;
413         getSnapshot(mi, ss->texids[1 - ss->curr_texid_index]);
414       }
415
416     } else {
417       reset(mi, ss->curr_screen);
418
419       if (ss->start_time + duration <= now)
420         getSnapshot(mi, ss->texids[ss->curr_texid_index]);
421     }
422   }
423
424   if (fade && ss->in_transition && ss->frames == frames_per_fade) {
425     ss->in_transition = 0;
426     ss->curr_screen = 1 - ss->curr_screen;
427
428     if (ss->in_file_transition) {
429       ss->in_file_transition = 0;
430       ss->curr_texid_index = 1 - ss->curr_texid_index;
431     }
432   }
433
434   display(mi);
435   
436   ss->frames++;
437
438   if(mi->fps_p) do_fps(mi);
439
440   glFinish(); 
441   glXSwapBuffers(disp, w);
442 }
443
444 void
445 release_slideshow (ModeInfo *mi)
446 {
447   if(sss != NULL) {
448     (void) free((void *) sss);
449     sss = NULL;
450   }
451
452   FreeAllGL(MI);
453 }
454
455 #endif