ftp://ftp.krokus.ru/pub/OpenBSD/distfiles/xscreensaver-4.21.tar.gz
[xscreensaver] / hacks / glx / mirrorblob.c
1 /* mirrorblob  Copyright (c) 2003 Jon Dowdall <jon.dowdall@bigpond.com> */
2 /*
3  * Permission to use, copy, modify, and distribute this software and its
4  * documentation for any purpose and without fee is hereby granted,
5  * provided that the above copyright notice appear in all copies and that
6  * both that copyright notice and this permission notice appear in
7  * supporting documentation.
8  *
9  * This file is provided AS IS with no warranties of any kind.  The author
10  * shall have no liability with respect to the infringement of copyrights,
11  * trade secrets or any patents by this file or any part thereof.  In no
12  * event will the author be liable for any lost revenue or profits or
13  * other special, indirect and consequential damages.
14  *
15  * Revision History:
16  * 23-Sep-2003:  jon.dowdall@bigpond.com  Created module "blob"
17  * 19-Oct-2003:  jon.dowdall@bigpond.com  Added texturing
18  * 21-Oct-2003:                           Renamed to mirrorblob
19  * 10-Feb-2004:  jon.dowdall@bigpond.com  Added motion blur
20  *
21  * The mirrorblob screensaver draws a pulsing blob on the screen.  Options
22  * include adding a background (via screen_to_texture), texturing the blob,
23  * making the blob semi-transparent and varying the resolution of the blob
24  * tessellation.
25  *
26  * The blob was inspired by a lavalamp is in no way a simulation.  The code is
27  * just an attempt to generate some eye-candy.
28  *
29  * Much of xscreensaver code framework is taken from the pulsar module by David
30  * Konerding and the glslideshow by Mike Oliphant and Jamie Zawinski.
31  *
32  */
33
34 #include <math.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <sys/time.h>
38
39 #ifdef STANDALONE
40 # define PROGCLASS         "Screensaver"
41 # define HACK_INIT         init_screensaver
42 # define HACK_DRAW         draw_screensaver
43 # define HACK_RESHAPE      reshape_screensaver
44 # define screensaver_opts  xlockmore_opts
45
46 #define DEF_DELAY            "10000"
47 #define DEF_FPS              "False"
48 #define DEF_WIRE             "False"
49 #define DEF_BLEND            "False"
50 #define DEF_FOG              "False"
51 #define DEF_ANTIALIAS        "False"
52 #define DEF_WALLS            "True"
53 #define DEF_COLOUR           "False"
54 #define DEF_TEXTURE          "True"
55 #define DEF_OFFSET_TEXTURE   "False"
56 #define DEF_PAINT_BACKGROUND "True"
57 #define DEF_X_RES            "60"
58 #define DEF_Y_RES            "32"
59 #define DEF_FIELD_POINTS     "5"
60 #define DEF_MOTION_BLUR      "0"
61 #define DEF_INCREMENTAL      "0"
62 #define DEF_HOLD_TIME        "30"
63 #define DEF_FADE_TIME        "5"
64
65 #define DEFAULTS \
66     "*delay:             " DEF_DELAY "\n" \
67     "*showFPS:           " DEF_FPS   "\n" \
68     "*useSHM:              True      \n"
69
70 # include "xlockmore.h"    /* from the xscreensaver distribution */
71 #else /* !STANDALONE */
72 # include "xlock.h"        /* from the xlockmore distribution */
73 #endif /* !STANDALONE */
74
75 #ifdef USE_GL /* whole file */
76
77 #ifdef HAVE_XMU
78 # ifndef VMS
79 #  include <X11/Xmu/Drawing.h>
80 #else  /* VMS */
81 #  include <Xmu/Drawing.h>
82 # endif /* VMS */
83 #endif
84
85 #include <GL/gl.h>
86 #include <GL/glu.h>
87 #include "GL/glx.h"
88
89 #include <stdlib.h>
90 #include <stdio.h>
91 /*#include <string.h>*/
92 #include "grab-ximage.h"
93
94 #undef countof
95 #define countof(x) (sizeof((x)) / sizeof((*x)))
96
97 #define PI  3.1415926535897
98
99 /* */
100 static int do_wire;
101 static int do_blend;
102 static int do_fog;
103 static int do_antialias;
104 static int do_walls;
105 static int do_texture;
106 static int do_paint_background;
107 static int do_colour;
108 static int offset_texture;
109 static int x_resolution;
110 static int y_resolution;
111 static int field_points;
112 static int motion_blur;
113 static int incremental;
114 static int fade_time;
115 static int hold_time;
116
117 static XrmOptionDescRec opts[] = {
118     {"-wire",             ".blob.wire",             XrmoptionNoArg, "true" },
119     {"+wire",             ".blob.wire",             XrmoptionNoArg, "false" },
120     {"-blend",            ".blob.blend",            XrmoptionNoArg, "true" },
121     {"+blend",            ".blob.blend",            XrmoptionNoArg, "false" },
122     {"-fog",              ".blob.fog",              XrmoptionNoArg, "true" },
123     {"+fog",              ".blob.fog",              XrmoptionNoArg, "false" },
124     {"-antialias",        ".blob.antialias",        XrmoptionNoArg, "true" },
125     {"+antialias",        ".blob.antialias",        XrmoptionNoArg, "false" },
126     {"-walls",            ".blob.walls",            XrmoptionNoArg, "true" },
127     {"+walls",            ".blob.walls",            XrmoptionNoArg, "false" },
128     {"-texture",          ".blob.texture",          XrmoptionNoArg, "true" },
129     {"+texture",          ".blob.texture",          XrmoptionNoArg, "false" },
130     {"-colour",           ".blob.colour",           XrmoptionNoArg, "true" },
131     {"+colour",           ".blob.colour",           XrmoptionNoArg, "false" },
132     {"-offset_texture",   ".blob.offset_texture",   XrmoptionNoArg, "true" },
133     {"+offset_texture",   ".blob.offset_texture",   XrmoptionNoArg, "false" },
134     {"-paint_background", ".blob.paint_background", XrmoptionNoArg, "true" },
135     {"+paint_background", ".blob.paint_background", XrmoptionNoArg, "false" },
136     {"-x_res",            ".blob.x_res",            XrmoptionSepArg, NULL },
137     {"-y_res",            ".blob.y_res",            XrmoptionSepArg, NULL },
138     {"-field_points",     ".blob.field_points",     XrmoptionSepArg, NULL },
139     {"-motion_blur",      ".blob.motion_blur",      XrmoptionSepArg, NULL },
140     {"-incremental",      ".blob.incremental",      XrmoptionSepArg, NULL },
141     {"-fade_time",        ".blob.fade_time",        XrmoptionSepArg, NULL },
142     {"-hold_time",        ".blob.hold_time",        XrmoptionSepArg, NULL },
143 };
144
145 static argtype vars[] = {
146     {&do_wire,      "wire",         "Wire",      DEF_WIRE,      t_Bool},
147     {&do_blend,     "blend",        "Blend",     DEF_BLEND,     t_Bool},
148     {&do_fog,       "fog",          "Fog",       DEF_FOG,       t_Bool},
149     {&do_antialias, "antialias",    "Antialias", DEF_ANTIALIAS, t_Bool},
150     {&do_walls,     "walls",        "Walls",     DEF_WALLS,     t_Bool},
151     {&do_texture,   "texture",      "Texture",   DEF_TEXTURE,   t_Bool},
152     {&do_colour,    "colour",       "Colour",    DEF_COLOUR,   t_Bool},
153     {&offset_texture, "offset_texture","Offset_Texture", DEF_OFFSET_TEXTURE, t_Bool},
154     {&do_paint_background,"paint_background","Paint_Background", DEF_PAINT_BACKGROUND, t_Bool},
155     {&x_resolution, "x_res",        "X_Res",        DEF_X_RES,        t_Int},
156     {&y_resolution, "y_res",        "Y_Res",        DEF_Y_RES,        t_Int},
157     {&field_points, "field_points", "Field_Points", DEF_FIELD_POINTS, t_Int},
158     {&motion_blur,  "motion_blur",  "Motion_Blur",  DEF_MOTION_BLUR,  t_Int},
159     {&incremental,  "incremental",  "Incremental",  DEF_INCREMENTAL,  t_Int},
160     {&fade_time,    "fade_time",    "Fade_Time",    DEF_FADE_TIME,    t_Int},
161     {&hold_time,    "hold_time",    "Hold_Time",    DEF_HOLD_TIME,    t_Int},
162 };
163
164
165 static OptionStruct desc[] =
166 {
167     {"-/+ wire", "whether to do use wireframe instead of filled (faster)"},
168     {"-/+ blend", "whether to do enable blending (slower)"},
169     {"-/+ fog", "whether to do enable fog (slower)"},
170     {"-/+ antialias", "whether to do enable antialiased lines (slower)"},
171     {"-/+ walls", "whether to add walls to the blob space (slower)"},
172     {"-/+ texture", "whether to add a texture to the blob (slower)"},
173     {"-/+ colour", "whether to colour the blob"},
174     {"-/+ offset_texture", "whether to offset texture co-ordinates"},
175     {"-/+ paint_background", "whether to display a background texture (slower)"},
176     {"-x_res", "Blob resolution in x direction"},
177     {"-y_res", "Blob resolution in y direction"},
178     {"-field_points", "Number of field points used to disturb blob"},
179     {"-motion_blur", "Fade blob images (higher number = faster fade)"},
180     {"-incremental", "Field summation method"},
181     {"-fade_time", "Number of frames to transistion to next image"},
182     {"-hold_time", "Number of frames before next image"},
183 };
184
185 ModeSpecOpt screensaver_opts = {countof(opts), opts, countof(vars), vars, desc};
186
187 #ifdef USE_MODULES
188 ModStruct   screensaver_description =
189 {"screensaver", "init_screensaver", "draw_screensaver", "release_screensaver",
190  "draw_screensaver", "init_screensaver", NULL, &screensaver_opts,
191  1000, 1, 2, 1, 4, 1.0, "",
192  "OpenGL screensaver", 0, NULL};
193 #endif
194
195 /* structure for holding the screensaver data */
196 typedef struct {
197     int screen_width, screen_height;
198     GLXContext *glx_context;
199     Window window;
200     XColor fg, bg;
201 } screensaverstruct;
202
203 static screensaverstruct *Screensaver = NULL;
204
205 /*****************************************************************************
206  * Types used in blob code
207  *****************************************************************************/
208
209 typedef struct
210 {
211     GLdouble x, y;
212 } Vector2D;
213
214 typedef struct
215 {
216     GLdouble x, y, z;
217 } Vector3D;
218
219 typedef struct
220 {
221     GLubyte red, green, blue, alpha;
222 } Colour;
223
224 /* Data used for sphere tessellation */
225 typedef struct
226 {
227     double cosyd, sinyd;
228
229     /* Number of x points at each row of the blob */
230     int num_x_points;
231 } Row_Data;
232
233 /* Structure to hold sphere distortion data */
234 typedef struct
235 {
236     double cx, cy, cpower;
237     double mx, my, mpower;
238     double ax, ay, apower;
239     double vx, vy, vpower;
240     Vector3D pos;
241 } Field_Data;
242
243 /*****************************************************************************
244  * Static blob data
245  *****************************************************************************/
246
247 static Row_Data *row_data;
248
249 /* Parameters controlling the position of the blob as a whole */
250 static Vector3D blob_center = {0.0, 0.0, 0.0};
251 static Vector3D blob_anchor = {0.0, 0.0, 0.0};
252 static Vector3D blob_velocity = {0.0, 0.0, 0.0};
253 static Vector3D blob_force = {0.0, 0.0, 0.0};
254
255 /* Count of the total number of points */
256 static int num_points;
257
258 static Vector3D *dots = NULL;
259 static Vector3D *normals = NULL;
260 static Colour   *colours = NULL;
261 static Vector2D *tex_coords = NULL;
262
263 /* Pointer to the field function results */
264 static double *field = 0, *wall_field = 0;
265
266 Field_Data *field_data;
267
268 /* Use 2 textures to allow a gradual fade between images */
269 #define NUM_TEXTURES 2
270 static int current_texture;
271
272 /* Ratio of used texture size to total texture size */
273 GLfloat tex_width[NUM_TEXTURES], tex_height[NUM_TEXTURES];
274 GLuint textures[NUM_TEXTURES];
275
276 typedef enum
277 {
278   HOLDING,
279   TRANSITIONING
280 } Frame_State;
281
282 static Frame_State state = HOLDING;
283 static double state_start_time = 0;
284
285 static int colour_cycle = 0;
286
287 /******************************************************************************
288  *
289  * Returns the current time in seconds as a double.  Shamelessly borrowed from
290  * glslideshow.
291  *
292  */
293 static double
294 double_time (void)
295 {
296   struct timeval now;
297 # ifdef GETTIMEOFDAY_TWO_ARGS
298   struct timezone tzp;
299   gettimeofday(&now, &tzp);
300 # else
301   gettimeofday(&now);
302 # endif
303
304   return (now.tv_sec + ((double) now.tv_usec * 0.000001));
305 }
306
307 /******************************************************************************
308  *
309  * Change to the projection matrix and set our viewing volume.
310  *
311  */
312 static void
313 reset_projection(int width, int height)
314 {
315     glMatrixMode (GL_PROJECTION);
316     glLoadIdentity ();
317     gluPerspective (60.0, 1.0, 1.0, 1024.0 );
318     glMatrixMode (GL_MODELVIEW);
319     glLoadIdentity ();
320 }
321
322 /****************************************************************************
323  *
324  * Load a texture using the screen_to_texture function.
325  */
326 void
327 grab_texture(ModeInfo *mi, int texture_index)
328 {
329     Bool mipmap_p = True;
330     int iw, ih, tw, th;
331
332     glBindTexture (GL_TEXTURE_2D, textures[texture_index]);
333
334     if (! screen_to_texture (mi->xgwa.screen, mi->window, 0, 0, mipmap_p,
335                              NULL, NULL, &iw, &ih, &tw, &th))
336       exit(1);
337
338     tex_width [texture_index] =  (GLfloat) iw / tw;
339     tex_height[texture_index] = -(GLfloat) ih / th;
340
341     glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
342     glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
343                      (mipmap_p ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR));
344
345     glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
346     glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
347     glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
348 }
349
350 /******************************************************************************
351  *
352  * Initialise the data used to calculate the blob shape.
353  */
354 static void
355 initialize_gl(ModeInfo *mi, GLsizei width, GLsizei height)
356 {
357     GLfloat fogColor[4] = { 0.1, 0.1, 0.1, 0.1 };
358     /* Lighting values */
359     GLfloat lightPos0[] = {500.0f, 100.0f, 200.0f, 1.0f };
360     GLfloat whiteLight0[] = { 0.1f, 0.1f, 0.1f, 1.0f };
361     GLfloat sourceLight0[] = { 1.0f, 1.0f, 1.0f, 1.0f };
362     GLfloat specularLight0[] = { 0.7f, 0.6f, 0.3f, 1.0f };
363
364     GLfloat lightPos1[] = {0.0f, -500.0f, 500.0f, 1.0f };
365     GLfloat whiteLight1[] = { 0.1f, 0.1f, 0.1f, 1.0f };
366     GLfloat sourceLight1[] = { 1.0f, 0.3f, 0.3f, 1.0f };
367     GLfloat specularLight1[] = { 0.7f, 0.6f, 0.3f, 1.0f };
368
369     GLfloat specref[] = { 1.0f, 1.0f, 1.0f, 1.0f };
370
371     /* Setup our viewport. */
372     glViewport (0, 0, width, height ); 
373
374     glEnable(GL_DEPTH_TEST);
375
376     if (do_antialias)
377     {
378         do_blend = 1;
379         glEnable(GL_LINE_SMOOTH);
380     }
381
382     /* The blend function is used for trasitioning between two images even when
383      * blend is not selected.
384      */
385     glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
386
387     if (do_wire)
388     {
389         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
390     }
391     else
392     {
393         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
394     }
395
396     if (do_fog)
397     {
398         glEnable(GL_FOG);
399         glFogi(GL_FOG_MODE, GL_LINEAR);
400         glFogfv(GL_FOG_COLOR, fogColor);
401         glFogf(GL_FOG_DENSITY, 0.35);
402         glFogf(GL_FOG_START, 2.0);
403         glFogf(GL_FOG_END, 10.0);
404     }
405
406     /* Our shading model--Gouraud (smooth). */
407     glShadeModel (GL_SMOOTH);
408
409     /* Culling. */
410     glCullFace (GL_BACK);
411     glEnable (GL_CULL_FACE);
412     glEnable (GL_DEPTH_TEST);
413     glFrontFace (GL_CCW);
414
415     /* Set the clear color. */
416     glClearColor( 0, 0, 0, 0 );
417
418     glViewport( 0, 0, width, height );
419
420     glLightfv (GL_LIGHT0, GL_AMBIENT, whiteLight0);
421     glLightfv (GL_LIGHT0, GL_DIFFUSE, sourceLight0);
422     glLightfv (GL_LIGHT0, GL_SPECULAR, specularLight0);
423     glLightfv (GL_LIGHT0, GL_POSITION, lightPos0);
424     glEnable (GL_LIGHT0);
425     glLightfv (GL_LIGHT1, GL_AMBIENT, whiteLight1);
426     glLightfv (GL_LIGHT1, GL_DIFFUSE, sourceLight1);
427     glLightfv (GL_LIGHT1, GL_SPECULAR, specularLight1);
428     glLightfv (GL_LIGHT1, GL_POSITION, lightPos1);
429     glEnable (GL_LIGHT1);
430     glEnable (GL_LIGHTING);
431
432     /* Enable color tracking */
433     glEnable (GL_COLOR_MATERIAL);
434
435     /* Set Material properties to follow glColor values */
436     glColorMaterial (GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
437
438     /* Set all materials to have specular reflectivity */
439     glMaterialfv (GL_FRONT, GL_SPECULAR, specref);
440     glMateriali (GL_FRONT, GL_SHININESS, 64);
441
442     glEnable (GL_NORMALIZE);
443
444     /* Enable Arrays */
445     if (do_texture)
446     {
447         glLightModeli (GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
448
449         glEnable (GL_TEXTURE_2D);
450         current_texture = 0;
451         glGenTextures (NUM_TEXTURES, textures);
452         grab_texture (mi, current_texture);
453
454         glEnableClientState (GL_TEXTURE_COORD_ARRAY);
455     }
456
457     if (do_colour)
458     {
459         glEnableClientState (GL_COLOR_ARRAY);
460     }
461     glEnableClientState (GL_NORMAL_ARRAY);
462     glEnableClientState (GL_VERTEX_ARRAY);
463
464     /* Clear the buffer since this is not done during a draw with motion blur */
465     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
466 }
467
468 /******************************************************************************
469  *
470  * Calculate the normal vector for a plane given three points in the plane.
471  */
472 static void
473 calculate_normal (Vector3D point1,
474                   Vector3D point2,
475                   Vector3D point3,
476                   Vector3D *normal)
477 {
478     Vector3D vector1, vector2;
479     double magnitude;
480
481     vector1.x = point2.x - point1.x;
482     vector1.y = point2.y - point1.y;
483     vector1.z = point2.z - point1.z;
484
485     vector2.x = point3.x - point2.x;
486     vector2.y = point3.y - point2.y;
487     vector2.z = point3.z - point2.z;
488
489     (*normal).x = vector1.y * vector2.z - vector1.z * vector2.y;
490     (*normal).y = vector1.z * vector2.x - vector1.x * vector2.z;
491     (*normal).z = vector1.x * vector2.y - vector1.y * vector2.x;
492
493     /* Adjust the normal to unit magnitude */
494     magnitude = sqrt ((*normal).x * (*normal).x
495                       + (*normal).y * (*normal).y
496                       + (*normal).z * (*normal).z);
497
498     /* Watch out for divide by zero/underflow */
499     if (magnitude > 1e-300)
500     {
501         (*normal).x /= magnitude;
502         (*normal).y /= magnitude;
503         (*normal).z /= magnitude;
504     }
505 }
506
507 /******************************************************************************
508  *
509  * Initialise the data required to draw the blob allocating the memory as
510  * necessary.
511  *
512  * Return 0 on success.
513  */
514 static int
515 initialise_blob(int width,
516                 int height,
517                 int field_array_size)
518 {
519     /* Loop variables */
520     int x, y, i;
521     double xd;
522
523     colour_cycle = 0;
524
525     row_data = (Row_Data *) malloc (y_resolution * sizeof (Row_Data));
526     if (!row_data)
527     {
528         fprintf(stderr, "Couldn't allocate row data buffer\n");
529         return -1;
530     }
531
532     field_data = (Field_Data *) malloc (field_points * sizeof (Field_Data));
533     if (!field_data)
534     {
535         fprintf(stderr, "Couldn't allocate field data buffer\n");
536         return -1;
537     }
538
539     field = (double *)malloc(field_array_size * sizeof(double));
540     if (!field)
541     {
542         fprintf(stderr, "Couldn't allocate field buffer\n");
543         return -1;
544     }
545
546     wall_field = (double *)malloc(field_array_size * sizeof(double));
547     if (!wall_field)
548     {
549         fprintf(stderr, "Couldn't allocate wall field buffer\n");
550         return -1;
551     }
552
553     dots = (Vector3D *)malloc(x_resolution * y_resolution * sizeof(Vector3D));
554     if (!dots)
555     {
556         fprintf(stderr, "Couldn't allocate points buffer\n");
557         return -1;
558     }
559     glVertexPointer (3, GL_DOUBLE, 0, (GLvoid *) dots);
560
561     normals = (Vector3D *)malloc(x_resolution * y_resolution * sizeof(Vector3D));
562     if (!normals)
563     {
564         fprintf(stderr, "Couldn't allocate normals buffer\n");
565         return -1;
566     }
567     glNormalPointer (GL_DOUBLE, 0, (GLvoid *) normals);
568
569     if (do_colour)
570     {
571         colours = (Colour *)malloc(x_resolution * y_resolution * sizeof(Colour));
572         if (!colours)
573         {
574             fprintf(stderr, "Couldn't allocate colours buffer\n");
575             return -1;
576         }
577         glColorPointer (4, GL_UNSIGNED_BYTE, 0, (GLvoid *) colours);
578     }
579
580     if (do_texture)
581     {
582         tex_coords = (Vector2D *)malloc(x_resolution * y_resolution
583                                         * sizeof(Vector2D));
584         if (!tex_coords)
585         {
586             fprintf(stderr, "Couldn't allocate tex_coords buffer\n");
587             return -1;
588         }
589         glTexCoordPointer (2, GL_DOUBLE, 0, (GLvoid *) tex_coords);
590     }
591
592     num_points = 0;
593     /* Generate constant row data and count of total number of points */
594     for (y = 0; y < y_resolution; y++)
595     {
596         row_data[y].cosyd = cos(PI * (double)(y * (y_resolution + 1))
597                                 / (double)(y_resolution * y_resolution));
598         row_data[y].sinyd = sin(PI * (double)(y * (y_resolution + 1))
599                                 / (double)(y_resolution * y_resolution));
600         row_data[y].num_x_points = (int)(x_resolution * row_data[y].sinyd + 1.0);
601         num_points += row_data[y].num_x_points;
602     }
603
604     /* Initialise field data */
605     for (i = 0; i < field_points; i++)
606     {
607         field_data[i].ax = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
608         field_data[i].ay = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
609         field_data[i].apower = (((double)random() / (double)RAND_MAX) - 0.5);
610
611         field_data[i].pos.x = 1.5 * sin(PI * field_data[i].ay)
612             * cos(PI *  field_data[i].ax);
613         field_data[i].pos.y = 1.5 * cos(PI * field_data[i].ay);
614         field_data[i].pos.z = 1.5 * sin(PI * field_data[i].ay)
615             * sin(PI *  field_data[i].ax);
616
617         field_data[i].cx = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
618         field_data[i].cy = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
619         field_data[i].cpower = (((double)random() / (double)RAND_MAX) - 0.5);
620
621         field_data[i].vx = 0.0;
622         field_data[i].vy = 0.0;
623         field_data[i].vpower = 0.0;
624
625         field_data[i].mx = 0.003 * ((double)random() / (double)RAND_MAX);
626         field_data[i].my = 0.003 * ((double)random() / (double)RAND_MAX);
627         field_data[i].mpower = 0.003 * ((double)random() / (double)RAND_MAX);
628     }
629
630     /* Initialise lookup table of field strength */
631     for (i = 0; i < field_array_size; i++)
632     {
633         xd = 2.0 * (((double)i / (double)field_array_size));
634
635         xd = 3.0 * xd * xd * xd * xd;
636         field[i] = 0.4 / (field_points * (xd + 0.1));
637
638         xd = 10.0 * (((double)i / (double)field_array_size));
639         wall_field[i] = 0.4 / (xd * xd * xd * xd + 1.0);
640     }
641
642     for (y = 0; y < y_resolution; y++)
643     {
644         for (x = 0; x < row_data[y].num_x_points; x++)
645         {
646             i = x + y * x_resolution;
647             xd = 2.0 * (((double)x / (double)row_data[y].num_x_points) - 0.5);
648
649             dots[i].x = row_data[y].sinyd * cos(PI * xd);
650             dots[i].y = row_data[y].cosyd;
651             dots[i].z = row_data[y].sinyd * sin(PI * xd);
652             normals[i].x = row_data[y].sinyd * cos(PI * xd);
653             normals[i].y = row_data[y].cosyd;
654             normals[i].z = row_data[y].sinyd * sin(PI * xd);
655             if (do_texture)
656             {
657                 tex_coords[i].x = 2.0 - 2.0 * x / (float) row_data[y].num_x_points;
658                 tex_coords[i].y = 1.0 - y / (float) y_resolution;
659             }
660         }
661     }
662     return 0;
663 }
664
665
666 /******************************************************************************
667  *
668  * Calculate the blob shape.
669  */
670 static void
671 calc_blob(int width,
672           int height,
673           int field_array_size,
674           float limit,
675           double fade)
676 {
677     static double freak = 0.0;
678
679     static double v_freak = 0.0007;
680
681     /* Loop variables */
682     int x, y, i, index, index1, index2, index3;
683     /* position of a point */
684     double xd, yd, zd, offset_x, offset_y, offset_z;
685     double strength, radius;
686     double xdist, ydist, zdist;
687     int dist;
688
689     /* Color components */
690
691     colour_cycle++;
692
693     /* Update position and strength of points used to distort the blob */
694     for (i = 0; i < field_points; i++)
695     {
696         field_data[i].vx += field_data[i].mx*(field_data[i].cx - field_data[i].ax);
697         field_data[i].vy += field_data[i].my*(field_data[i].cy - field_data[i].ay);
698         field_data[i].vpower += field_data[i].mpower
699             * (field_data[i].cpower - field_data[i].apower);
700
701         field_data[i].ax += 0.1 * field_data[i].vx;
702         field_data[i].ay += 0.1 * field_data[i].vy;
703         field_data[i].apower += 0.1 * field_data[i].vpower;
704
705         field_data[i].pos.x = 1.0 * sin(PI * field_data[i].ay)
706             * cos(PI * field_data[i].ax);
707         field_data[i].pos.y = 1.0 * cos(PI * field_data[i].ay);
708         field_data[i].pos.z = 1.0 * sin(PI * field_data[i].ay)
709             * sin(PI * field_data[i].ax);
710     }
711
712     blob_force.x = 0.0;
713     blob_force.y = 0.0;
714     blob_force.z = 0.0;
715     for (y = 0; y < y_resolution; y++)
716     {
717         for (x = 0; x < row_data[y].num_x_points; x++)
718         {
719             index = x + y * x_resolution;
720             xd = 2.0 * PI * (((double)x / (double)row_data[y].num_x_points) - 0.5);
721
722             radius = 1.0 + 0.0 * sin (xd * 10);
723
724             zd = radius * row_data[y].sinyd * sin(xd);
725             xd = radius * row_data[y].sinyd * cos(xd);
726             yd = radius * row_data[y].cosyd;
727
728             normals[index].x = xd;
729             normals[index].y = yd;
730             normals[index].z = zd;
731
732             offset_x = 0.0;
733             offset_y = 0.0;
734             offset_z = 0.0;
735             strength = 0.0;
736             for ( i = 0; i < field_points; i++)
737             {
738                 xdist = field_data[i].pos.x - xd;
739                 ydist = field_data[i].pos.y - yd;
740                 zdist = field_data[i].pos.z - zd;
741                 dist = field_array_size * (xdist * xdist + ydist * ydist
742                                            + zdist * zdist) * 0.1;
743
744                 strength += PI * field_data[i].apower;
745
746                 if (dist < field_array_size)
747                 {
748                     offset_x += xd * field_data[i].apower * field[dist];
749                     offset_y += yd * field_data[i].apower * field[dist];
750                     offset_z += zd * field_data[i].apower * field[dist];
751
752                     blob_force.x += 1.0 * xd * field_data[i].apower * field[dist];
753                     blob_force.y += 1.0 * yd * field_data[i].apower * field[dist];
754                     blob_force.z += 1.0 * zd * field_data[i].apower * field[dist];
755
756                     strength *= 2.0 * field[dist];
757                 }
758
759                 if (incremental)
760                 {
761                     xd += offset_x * freak * freak;
762                     yd += offset_y * freak * freak;
763                     zd += offset_z * freak * freak;
764                 }
765                 if (incremental == 1)
766                 {
767                     offset_x = 0.0;
768                     offset_y = 0.0;
769                     offset_z = 0.0;
770                 }
771             }
772
773             if (incremental < 3)
774             {
775                 xd += offset_x;
776                 yd += offset_y;
777                 zd += offset_z;
778             }
779             xd += blob_center.x;
780             yd += blob_center.y;
781             zd += blob_center.z;
782
783             if (do_colour)
784             {
785                 colours[index].red = 128 + (int)(sin(strength + colour_cycle * 0.01 + 2.0 * PI * x / row_data[y].num_x_points) * 127.0);
786                 colours[index].green = 128 + (int)(cos(strength + colour_cycle * 0.025) * 127.0);
787                 colours[index].blue = 128 + (int)(sin(strength + colour_cycle * 0.03 + 2.0 * PI * y / y_resolution) * 127.0);
788                 colours[index].alpha = (int)(255.0 * fade);
789             }
790
791             /* Add walls */
792             if (do_walls)
793             {
794                 if (zd < -limit) zd = -limit;
795                 if (zd > limit) zd = limit;
796
797                 dist = field_array_size * (zd + limit) * (zd + limit) * 0.5;
798                 if (dist < field_array_size)
799                 {
800                     xd += (xd - blob_center.x) * wall_field[dist];
801                     yd += (yd - blob_center.y) * wall_field[dist];
802                     blob_force.z += (zd + limit);
803                 }
804                 else
805                 {
806                     dist = field_array_size * (zd - limit) * (zd - limit) * 0.5;
807                     if (dist < field_array_size)
808                     {
809                         xd += (xd - blob_center.x) * wall_field[dist];
810                         yd += (yd - blob_center.y) * wall_field[dist];
811                         blob_force.z -= (zd - limit);
812                     }
813
814                     if (yd < -limit) yd = -limit;
815                     if (yd > limit) yd = limit;
816
817                     dist = field_array_size * (yd + limit) * (yd + limit) * 0.5;
818                     if (dist < field_array_size)
819                     {
820                         xd += (xd - blob_center.x) * wall_field[dist];
821                         zd += (zd - blob_center.z) * wall_field[dist];
822                         blob_force.y += (yd + limit);
823                     }
824                     else
825                     {
826                         dist = field_array_size * (yd - limit) * (yd - limit) * 0.5;
827                         if (dist < field_array_size)
828                         {
829                             xd += (xd - blob_center.x) * wall_field[dist];
830                             zd += (zd - blob_center.z) * wall_field[dist];
831                             blob_force.y -= (yd - limit);
832                         }
833                     }
834
835                     if (xd < -limit) xd = -limit;
836                     if (xd > limit) xd = limit;
837
838                     dist = field_array_size * (xd + limit) * (xd + limit) * 0.5;
839                     if (dist < field_array_size)
840                     {
841                         yd += (yd - blob_center.y) * wall_field[dist];
842                         zd += (zd - blob_center.z) * wall_field[dist];
843                         blob_force.x += (xd + limit);
844                     }
845                     else
846                     {
847                         dist = field_array_size * (xd - limit) * (xd - limit) * 0.5;
848                         if (dist < field_array_size)
849                         {
850                             yd += (yd - blob_center.y) * wall_field[dist];
851                             zd += (zd - blob_center.z) * wall_field[dist];
852                             blob_force.x -= (xd - limit);
853                         }
854                     }
855
856                     if (yd < -limit) yd = -limit;
857                     if (yd > limit) yd = limit;
858                 }
859             }
860
861             dots[index].x = xd;
862             dots[index].y = yd;
863             dots[index].z = zd;
864         }
865     }
866
867     /* Calculate the normals for each vertex and the texture mapping if required.
868      * Although the code actually calculates the normal for one of the triangles
869      * attached to a vertex rather than the vertex itself the results are not too
870      * bad for with a reasonable number of verticies.
871      */
872
873     /* The first point is treated as a special case since the loop expects to use
874      * points in the previous row to form the triangle.
875      */
876     index1 = 0;
877     index2 = y * x_resolution;
878     index3 = 1 + y * x_resolution;
879     calculate_normal (dots[index1], dots[index2], dots[index3], &normals[index1]);
880     if (do_texture)
881     {
882         if (offset_texture)
883         {
884             tex_coords[index1].x = dots[index1].x * 0.125 + 0.5
885                 * (1.0 + 0.25 * asin(normals[index1].x) / (0.5 * PI));
886             tex_coords[index1].y = dots[index1].y * 0.125 + 0.5
887                 * (1.0 + 0.25 * asin(normals[index1].y) / (0.5 * PI));
888         }
889         else
890         {
891             tex_coords[index1].x = 0.5 * (1.0 + (asin(normals[index1].x)
892                                                  / (0.5 * PI)));
893             tex_coords[index1].y = 0.5 * (1.0 + (asin(normals[index1].y)
894                                                  / (0.5 * PI)));
895         }
896         tex_coords[index1].x *= tex_width[current_texture];
897         tex_coords[index1].y *= tex_height[current_texture];
898     }
899
900     for (y = 1; y < y_resolution - 1; y++)
901     {
902         if (row_data[y - 1].num_x_points)
903         {
904             for (x = 0; x < row_data[y].num_x_points; x++)
905             {
906                 if (x == row_data[y].num_x_points - 1)
907                 {
908                     index1 = y * x_resolution;
909                 }
910                 else
911                 {
912                     index1 = x + 1 + y * x_resolution;
913                 }
914                 index2 = x + y * x_resolution;
915                 index3 = ((x + 0.5) * row_data[y - 1].num_x_points
916                           / row_data[y].num_x_points) + (y - 1) * x_resolution;
917                 calculate_normal (dots[index1], dots[index2], dots[index3],
918                                   &normals[index1]);
919                 if (do_texture)
920                 {
921                     if (offset_texture)
922                     {
923                         tex_coords[index1].x = dots[index1].x * 0.125 + 0.5
924                             * (1.0 + 0.25 * asin(normals[index1].x) / (0.5 * PI));
925                         tex_coords[index1].y = dots[index1].y * 0.125 + 0.5
926                             * (1.0 + 0.25 * asin(normals[index1].y) / (0.5 * PI));
927                     }
928                     else
929                     {
930                         tex_coords[index1].x = 0.5 * (1.0 + (asin(normals[index1].x)
931                                                              / (0.5 * PI)));
932                         tex_coords[index1].y = 0.5 * (1.0 + (asin(normals[index1].y)
933                                                              / (0.5 * PI)));
934                     }
935                     tex_coords[index1].x *= tex_width[current_texture];
936                     tex_coords[index1].y *= tex_height[current_texture];
937                 }
938             }
939         }
940     }
941     index1 = (y_resolution - 1) * x_resolution;
942     index2 = (y_resolution - 2) * x_resolution;
943     index3 = 1 + (y_resolution - 2) * x_resolution;
944     calculate_normal (dots[index1], dots[index2], dots[index3], &normals[index1]);
945     if (do_texture)
946     {
947         if (offset_texture)
948         {
949             tex_coords[index1].x = dots[index1].x * 0.125 + 0.5
950                 * (1.0 + 0.25 * asin(normals[index1].x) / (0.5 * PI));
951             tex_coords[index1].y = dots[index1].y * 0.125 + 0.5
952                 * (1.0 + 0.25 * asin(normals[index1].y) / (0.5 * PI));
953         }
954         else
955         {
956             tex_coords[index1].x = 0.5 * (1.0 + (asin(normals[index1].x)
957                                                  / (0.5 * PI)));
958             tex_coords[index1].y = 0.5 * (1.0 + (asin(normals[index1].y)
959                                                  / (0.5 * PI)));
960         }
961         tex_coords[index1].x *= tex_width[current_texture];
962         tex_coords[index1].y *= tex_height[current_texture];
963     }
964
965
966     freak += v_freak;
967     v_freak += -freak / 2000000.0;
968
969     /* Update the center of the whole blob */
970     blob_velocity.x += (blob_anchor.x - blob_center.x) / 80.0
971         + 0.01 * blob_force.x / num_points;
972     blob_velocity.y += (blob_anchor.y - blob_center.y) / 80.0
973         + 0.01 * blob_force.y / num_points;
974     blob_velocity.z += (blob_anchor.z - blob_center.z) / 80.0
975         + 0.01 * blob_force.z / num_points;
976
977     blob_center.x += blob_velocity.x * 0.5;
978     blob_center.y += blob_velocity.y * 0.5;
979     blob_center.z += blob_velocity.z * 0.5;
980
981     blob_velocity.x *= 0.99;
982     blob_velocity.y *= 0.99;
983     blob_velocity.z *= 0.99;
984 }
985
986 /******************************************************************************
987  *
988  * Draw the blob shape.
989  *
990  * The horrendous indexing to calculate the verticies that form a particular
991  * traiangle is the result of the conversion of my first non-openGL version of
992  * blob to this openGL version.  This may be tidied up when I finally playing
993  * with the more interesting bits of the code.
994  */
995 static void
996 draw_blob (void)
997 {
998     int x, y, x2, x3;
999     int index1, index2, index3;
1000     int lower, upper;
1001
1002     glMatrixMode (GL_MODELVIEW);
1003     glLoadIdentity ();
1004
1005     /* Move down the z-axis. */
1006     glTranslatef (0.0, 0.0, -5.0 );
1007
1008     for (y = 1; y < y_resolution; y++)
1009     {
1010         if (row_data[y - 1].num_x_points)
1011         {
1012             for (x = 0; x < row_data[y].num_x_points; x++)
1013             {
1014                 glBegin (GL_TRIANGLES);
1015                 if (x == row_data[y].num_x_points - 1)
1016                 {
1017                     index1 = y * x_resolution;
1018                 }
1019                 else
1020                 {
1021                     index1 = x + 1 + y * x_resolution;
1022                 }
1023                 index2 = x + y * x_resolution;
1024                 index3 = ((x + 0.5) * row_data[y - 1].num_x_points
1025                           / row_data[y].num_x_points) + (y - 1) * x_resolution;
1026                 glArrayElement(index1);
1027                 glArrayElement(index2);
1028                 glArrayElement(index3);
1029                 glEnd();
1030
1031                 lower = ((x - 0.5) * row_data[y - 1].num_x_points
1032                          / (float)row_data[y].num_x_points);
1033                 upper = ((x + 0.5) * row_data[y - 1].num_x_points
1034                          / (float)row_data[y].num_x_points);
1035
1036                 if (upper > lower)
1037                 {
1038                     glBegin (GL_TRIANGLE_FAN);
1039                     index1 = x + y * x_resolution;
1040
1041                     for (x2 = lower; x2 <= upper; x2++)
1042                     {
1043                         x3 = x2;
1044                         while (x3 < 0) x3 += row_data[y - 1].num_x_points;
1045                         while (x3 >= row_data[y - 1].num_x_points)
1046                             x3 -= row_data[y - 1].num_x_points;
1047                         index2 = x3 + (y - 1) * x_resolution;
1048
1049                         if (x2 < upper)
1050                         {
1051                             x3 = x2 + 1;
1052                             while (x3 < 0) x3 += row_data[y - 1].num_x_points;
1053                             while (x3 >= row_data[y - 1].num_x_points)
1054                                 x3 -= row_data[y - 1].num_x_points;
1055                             index3 = x3 + (y - 1) * x_resolution;
1056                             if (x2 == lower)
1057                             {
1058                                 glArrayElement(index1);
1059                             }
1060                         }
1061                         glArrayElement(index2);
1062                     }
1063                     glEnd ();
1064                 }
1065             }
1066         }
1067     }
1068 }
1069
1070 /******************************************************************************
1071  *
1072  * Draw the background image simply map a texture onto a full screen quad.
1073  */
1074 static void
1075 draw_background (ModeInfo *mi)
1076 {
1077     glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
1078     glEnable (GL_TEXTURE_2D);
1079     glDisable(GL_LIGHTING);
1080
1081     /* Reset the projection matrix to make it easier to get the size of the quad
1082      * correct
1083      */
1084     glMatrixMode(GL_PROJECTION);
1085     glPushMatrix();
1086     glLoadIdentity();
1087
1088     glOrtho(0.0, MI_WIDTH(mi), MI_HEIGHT(mi), 0.0, -1000.0, 1000.0);
1089
1090     glBegin (GL_QUADS);
1091     
1092     glTexCoord2f (0.0, 0.0);
1093     glVertex2i (0, 0);
1094     
1095     glTexCoord2f (0.0, -tex_height[current_texture]);
1096     glVertex2i (0, MI_HEIGHT(mi));
1097
1098     glTexCoord2f (tex_width[current_texture], -tex_height[current_texture]);
1099     glVertex2i (MI_WIDTH(mi), MI_HEIGHT(mi));
1100
1101     glTexCoord2f (tex_width[current_texture], 0.0);
1102     glVertex2i (MI_WIDTH(mi), 0);
1103     glEnd();
1104
1105     glPopMatrix ();
1106     glMatrixMode (GL_MODELVIEW);
1107     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1108 }
1109
1110 /******************************************************************************
1111  *
1112  * Update the scene.
1113  */
1114 static GLvoid
1115 draw_scene(ModeInfo * mi)
1116 {
1117     double fade = 0.0;
1118     double current_time;
1119     check_gl_error ("draw_scene");
1120
1121     glColor4d(1.0, 1.0, 1.0, 1.0);
1122
1123     current_time = double_time();
1124     switch (state)
1125     {
1126     case TRANSITIONING:
1127         fade = (current_time - state_start_time) / fade_time;
1128         break;
1129
1130     case HOLDING:
1131         fade = 0.0;
1132         break;
1133     }
1134
1135
1136     /* Set the correct texture, when transitioning this ensures that the first draw
1137      * is the original texture (which has the new texture drawn over it with decreasing
1138      * transparency)
1139      */
1140     if (do_texture)
1141     {
1142       glBindTexture (GL_TEXTURE_2D, textures[current_texture]);
1143     }
1144
1145     if (do_paint_background && !do_wire)
1146     {
1147         glClear(GL_DEPTH_BUFFER_BIT);
1148         if (motion_blur)
1149         {
1150             glEnable (GL_BLEND);
1151             glColor4ub (255, 255, 255, motion_blur);
1152         }
1153         draw_background (mi);
1154
1155         /* When transitioning between two images paint the new image over the old
1156          * image with a varying alpha value to get a smooth fade.
1157          */
1158         if (state == TRANSITIONING)
1159         {
1160             glDisable (GL_DEPTH_TEST);
1161             glEnable (GL_BLEND);
1162             /* Select the texture to transition to */
1163             glBindTexture (GL_TEXTURE_2D, textures[1 - current_texture]);
1164             glColor4d (1.0, 1.0, 1.0, fade);
1165
1166             draw_background (mi);
1167
1168             /* Select the original texture to draw the blob */
1169             glBindTexture (GL_TEXTURE_2D, textures[current_texture]);
1170             glEnable (GL_DEPTH_TEST);
1171         }
1172         /* Clear the depth buffer bit so the backgound is behind the blob */
1173         glClear(GL_DEPTH_BUFFER_BIT);
1174     }
1175     else if (motion_blur)
1176     {
1177         glDisable (GL_DEPTH_TEST);
1178         glEnable (GL_BLEND);
1179         glColor4ub (0, 0, 0, motion_blur);
1180         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1181         glRectd (-10.0, -10.0, 10.0, 10.0);
1182         if (do_wire)
1183         {
1184             glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1185         }
1186         glEnable (GL_DEPTH_TEST);
1187         glClear (GL_DEPTH_BUFFER_BIT);
1188     }
1189     else
1190     {
1191         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1192     }
1193
1194     if (do_blend)
1195     {
1196         fade = fade * 0.5;
1197     }
1198
1199     calc_blob(MI_WIDTH(mi), MI_HEIGHT(mi), 1024, 2.5, fade);
1200
1201     glEnable(GL_LIGHTING);
1202     glEnable(GL_LIGHT0);
1203     glEnable(GL_LIGHT1);
1204
1205     if (do_blend)
1206     {
1207         glEnable (GL_BLEND);
1208         if (do_colour)
1209         {
1210             glBlendFunc (GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
1211         }
1212         else
1213         {
1214             glColor4d (1.0, 1.0, 1.0, 0.5 - fade);
1215         }
1216     }
1217     else
1218     {
1219         glDisable (GL_BLEND);
1220         glColor4d (1.0, 1.0, 1.0, 1.0);
1221     }
1222     draw_blob();
1223
1224     if (do_blend && do_colour)
1225     {
1226         glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1227     }
1228
1229     /* While transitioning draw a second blob twice with a modified alpha channel.
1230      * The trasitioning state machine is very crude, it simply counts frames
1231      * rather than elapsed time but it works.
1232      */
1233     if (do_texture && (hold_time > 0))
1234     {
1235         switch (state)
1236         {
1237         case TRANSITIONING:
1238             glClear(GL_DEPTH_BUFFER_BIT);
1239             glEnable (GL_BLEND);
1240             /* Select the texture to transition to */
1241             glBindTexture (GL_TEXTURE_2D, textures[1 - current_texture]);
1242             glColor4d (1.0, 1.0, 1.0, fade);
1243             draw_blob ();
1244
1245             if ((current_time - state_start_time) > fade_time)
1246             {
1247                 state = HOLDING;
1248                 state_start_time = current_time;
1249                 current_texture = 1 - current_texture;
1250             }
1251             break;
1252
1253         case HOLDING:
1254             if ((current_time - state_start_time) > hold_time)
1255             {
1256                 grab_texture (mi, 1 - current_texture);
1257                 state = TRANSITIONING;
1258                 /* Get the time again rather than using the current time so
1259                  * that the time taken by the grab_texture function is not part
1260                  * of the fade time
1261                  */
1262                 state_start_time = double_time();
1263             }
1264             break;
1265         }
1266     }
1267 }
1268
1269 /******************************************************************************
1270  *
1271  * XScreensaver screen update entry
1272  */
1273 void
1274 draw_screensaver(ModeInfo * mi)
1275 {
1276     screensaverstruct *gp = &Screensaver[MI_SCREEN(mi)];
1277     Display    *display = MI_DISPLAY(mi);
1278     Window      window = MI_WINDOW(mi);
1279
1280     if (!gp->glx_context)
1281         return;
1282
1283     glXMakeCurrent(display, window, *(gp->glx_context));
1284     draw_scene(mi);
1285     if (mi->fps_p) do_fps (mi);
1286     glXSwapBuffers(display, window);
1287 }
1288
1289 /******************************************************************************
1290  *
1291  * XScreensaver screen resize entry
1292  */
1293 void
1294 reshape_screensaver(ModeInfo *mi, int width, int height)
1295 {
1296     glViewport( 0, 0, MI_WIDTH(mi), MI_HEIGHT(mi) );
1297     reset_projection(width, height);
1298 }
1299
1300 /******************************************************************************
1301  *
1302  * XScreensaver initialise entry
1303  */
1304 void
1305 init_screensaver(ModeInfo * mi)
1306 {
1307     int screen = MI_SCREEN(mi);
1308
1309     screensaverstruct *gp;
1310
1311     if (Screensaver == NULL)
1312     {
1313         if ((Screensaver = (screensaverstruct *)
1314              calloc(MI_NUM_SCREENS(mi), sizeof (screensaverstruct))) == NULL)
1315         {
1316             return;
1317         }
1318     }
1319     gp = &Screensaver[screen];
1320
1321     gp->window = MI_WINDOW(mi);
1322     if ((gp->glx_context = init_GL(mi)) != NULL)
1323     {
1324         reshape_screensaver(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1325         initialize_gl(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1326     }
1327     else
1328     {
1329         MI_CLEARWINDOW(mi);
1330     }
1331
1332     initialise_blob(MI_WIDTH(mi), MI_HEIGHT(mi), 1024);
1333     state_start_time = double_time();
1334 }
1335
1336 /******************************************************************************
1337  *
1338  * XScreensaver cleanup entry
1339  */
1340 void
1341 release_screensaver(ModeInfo * mi)
1342 {
1343     if (row_data) free(row_data);
1344     if (field_data) free(field_data);
1345     if (colours) free(colours);
1346     if (tex_coords) free(tex_coords);
1347     if (dots) free(dots);
1348     if (wall_field) free(wall_field);
1349     if (field) free(field);
1350
1351     if (Screensaver != NULL)
1352     {
1353         (void) free((void *) Screensaver);
1354         Screensaver = NULL;
1355     }
1356     FreeAllGL(mi);
1357 }
1358 #endif