http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.02.tar.gz
[xscreensaver] / hacks / glx / glsnake.c
1 /* glsnake, Copyright (c) 2001,2002
2  * Jamie Wilkinson,    Andrew Bennetts,     Peter Aylett
3  * jaq@spacepants.org, andrew@puzzling.org, peter@ylett.com
4  *
5  * ported to xscreensaver 15th Jan 2002 by Jamie Wilkinson
6  * http://spacepants.org/src/glsnake/
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  */
22
23 #include <X11/Intrinsic.h>
24
25 extern XtAppContext app;
26
27 #define PROGCLASS     "glsnake"
28 #define HACK_INIT     glsnake_init
29 #define HACK_DRAW     glsnake_draw
30 #define HACK_RESHAPE  glsnake_reshape
31 #define sws_opts      xlockmore_opts
32
33 #define DEF_SPEED       "0.05"
34 #define DEF_EXPLODE     "0.03"
35 #define DEF_VELOCITY    "1.0"
36 #define DEF_ACCEL       "0.1"
37 #define DEF_STATICTIME  "5000"
38 #define DEF_YSPIN       "0.10"
39 #define DEF_ZSPIN       "0.14"
40 #define DEF_SCARYCOLOUR "False"
41 #define DEF_LABELS              "True"
42
43 #define DEFAULTS        "*delay:        30000        \n" \
44                         "*count:        30            \n" \
45                         "*showFPS:      False         \n" \
46                         "*wireframe:    False         \n" \
47                         "*speed:      " DEF_SPEED "   \n" \
48                         "*explode:    " DEF_EXPLODE " \n" \
49                         "*velocity:   " DEF_VELOCITY " \n" \
50 /*                      "*accel:      " DEF_ACCEL "   \n" */ \
51                         "*statictime: " DEF_STATICTIME " \n" \
52                         "*yspin:      " DEF_YSPIN "   \n" \
53                         "*zspin:      " DEF_ZSPIN "   \n" \
54                         "*scarycolour:" DEF_SCARYCOLOUR " \n" \
55                         "*labels:     " DEF_LABELS "  \n" \
56                         "*labelfont:  -*-times-bold-r-normal-*-180-*\n" \
57
58
59
60 #undef countof
61 #define countof(x) (sizeof((x))/sizeof((*x)))
62
63 #include "xlockmore.h"
64
65 #ifdef USE_GL /* whole file */
66
67 #include <GL/glu.h>
68 #include <sys/time.h>
69 #include <string.h>
70 #include <unistd.h>
71 #include <stdio.h>
72 #include <stdlib.h>
73 #include <string.h>
74 #include <dirent.h>
75 #include <sys/stat.h>
76 #include <sys/types.h>
77
78 #define ZERO  0
79 #define LEFT  1
80 #define PIN   2
81 #define RIGHT 3
82
83 typedef struct model_s {
84         char * name;
85         float node[24];
86 } model_t;
87
88 typedef struct nodeang_s {
89         float cur_angle;
90         float dest_angle;
91 } nodeang_t;
92
93 typedef struct {
94         GLXContext * glx_context;
95
96         int node_list; /* name of the display list */
97         int is_cyclic;
98         int is_legal;
99         int last_turn;
100         int selected;
101         struct timeval last_iteration;
102         struct timeval last_morph;
103         int morphing;
104         nodeang_t node[24];
105         GLfloat roty;
106         GLfloat rotz;
107         int paused;
108         int dragging;
109         int m_count;
110         int m;
111         int cur_model;
112         int interactive;
113         GLfloat colour_t[3];
114         GLfloat colour_i[3];
115         GLfloat colour[3];
116         model_t * models;
117
118         XFontStruct * font;
119         GLuint font_list;
120 } glsnake_configuration;
121
122 static glsnake_configuration *glc = NULL;
123
124 static GLfloat speed;
125 static GLfloat explode;
126 static GLfloat velocity;
127 /* static GLfloat accel; */
128 static long statictime;
129 static GLfloat yspin;
130 static GLfloat zspin;
131 static Bool scarycolour;
132 static Bool labels;
133
134 static XrmOptionDescRec opts[] = {
135   { "-speed", ".speed", XrmoptionSepArg, 0 },
136   { "-explode", ".explode", XrmoptionSepArg, 0 },
137   { "-velocity", ".velocity", XrmoptionSepArg, 0 },
138 /*  { "-accel", ".accel", XrmoptionSepArg, 0 }, */
139   { "-statictime", ".statictime", XrmoptionSepArg, 0 },
140   { "-yspin", ".yspin", XrmoptionSepArg, 0 },
141   { "-zspin", ".zspin", XrmoptionSepArg, 0 },
142   { "-scarycolour", ".scarycolour", XrmoptionNoArg, "True" },
143   { "+scarycolour", ".scarycolour", XrmoptionNoArg, "False" },
144   { "-labels", ".labels", XrmoptionNoArg, "True" },
145   { "+labels", ".labels", XrmoptionNoArg, "False" },
146 };
147
148 static argtype vars[] = {
149   {(caddr_t *) &speed, "speed", "Speed", DEF_SPEED, t_Float},
150   {(caddr_t *) &explode, "explode", "Explode", DEF_EXPLODE, t_Float},
151   {(caddr_t *) &velocity, "velocity", "Velocity", DEF_VELOCITY, t_Float},
152 /*  {(caddr_t *) &accel, "accel", "Acceleration", DEF_ACCEL, t_Float}, */
153   {(caddr_t *) &statictime, "statictime", "Static Time", DEF_STATICTIME, t_Int},
154   {(caddr_t *) &yspin, "yspin", "Y Spin", DEF_YSPIN, t_Float},
155   {(caddr_t *) &zspin, "zspin", "Z Spin", DEF_ZSPIN, t_Float},
156 /*  {(caddr_t *) &interactive, "interactive", "Interactive", DEF_INTERACTIVE, t_Bool}, */
157   {(caddr_t *) &scarycolour, "scarycolour", "Scary Colour", DEF_SCARYCOLOUR, t_Bool},
158   {(caddr_t *) &labels, "labels", "Labels", DEF_LABELS, t_Bool},
159 };
160
161 ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
162
163 /* prism models */
164 #define VOFFSET 0.045
165 float solid_prism_v[][3] = {
166         /* first corner, bottom left front */
167         { VOFFSET, VOFFSET, 1.0 },
168         { VOFFSET, 0.0, 1.0 - VOFFSET },
169         { 0.0, VOFFSET, 1.0 - VOFFSET },
170         /* second corner, rear */
171         { VOFFSET, VOFFSET, 0.00 },
172         { VOFFSET, 0.0, VOFFSET },
173         { 0.0, VOFFSET, VOFFSET },
174         /* third, right front */
175         { 1.0 - VOFFSET / M_SQRT1_2, VOFFSET, 1.0 },
176         { 1.0 - VOFFSET / M_SQRT1_2, 0.0, 1.0 - VOFFSET },
177         { 1.0 - VOFFSET * M_SQRT1_2, VOFFSET, 1.0 - VOFFSET },
178         /* fourth, right rear */
179         { 1.0 - VOFFSET / M_SQRT1_2, VOFFSET, 0.0 },
180         { 1.0 - VOFFSET / M_SQRT1_2, 0.0, VOFFSET },
181         { 1.0 - VOFFSET * M_SQRT1_2, VOFFSET, VOFFSET },
182         /* fifth, upper front */
183         { VOFFSET, 1.0 - VOFFSET / M_SQRT1_2, 1.0 },
184         { VOFFSET / M_SQRT1_2, 1.0 - VOFFSET * M_SQRT1_2, 1.0 - VOFFSET },
185         { 0.0, 1.0 - VOFFSET / M_SQRT1_2, 1.0 - VOFFSET},
186         /* sixth, upper rear */
187         { VOFFSET, 1.0 - VOFFSET / M_SQRT1_2, 0.0 },
188         { VOFFSET / M_SQRT1_2, 1.0 - VOFFSET * M_SQRT1_2, VOFFSET },
189         { 0.0, 1.0 - VOFFSET / M_SQRT1_2, VOFFSET }
190 };
191
192 /* normals */
193 float solid_prism_n[][3] = {
194         /* corners */
195         { -VOFFSET, -VOFFSET, VOFFSET },
196         { VOFFSET, -VOFFSET, VOFFSET },
197         { -VOFFSET, VOFFSET, VOFFSET },
198         { -VOFFSET, -VOFFSET, -VOFFSET },
199         { VOFFSET, -VOFFSET, -VOFFSET },
200         { -VOFFSET, VOFFSET, -VOFFSET },
201         /* edges */
202         { -VOFFSET, 0.0, VOFFSET },
203         { 0.0, -VOFFSET, VOFFSET },
204         { VOFFSET, VOFFSET, VOFFSET },
205         { -VOFFSET, 0.0, -VOFFSET },
206         { 0.0, -VOFFSET, -VOFFSET },
207         { VOFFSET, VOFFSET, -VOFFSET },
208         { -VOFFSET, -VOFFSET, 0.0 },
209         { VOFFSET, -VOFFSET, 0.0 },
210         { -VOFFSET, VOFFSET, 0.0 },
211         /* faces */
212         { 0.0, 0.0, 1.0 },
213         { 0.0, -1.0, 0.0 },
214         { M_SQRT1_2, M_SQRT1_2, 0.0 },
215         { -1.0, 0.0, 0.0 },
216         { 0.0, 0.0, -1.0 }
217 };
218
219 /* vertices */
220 float wire_prism_v[][3] = {
221         { 0.0, 0.0, 1.0 },
222         { 1.0, 0.0, 1.0 },
223         { 0.0, 1.0, 1.0 },
224         { 0.0, 0.0, 0.0 },
225         { 1.0, 0.0, 0.0 },
226         { 0.0, 1.0, 0.0 }
227 };
228
229 /* normals */
230 float wire_prism_n[][3] = {
231         { 0.0, 0.0, 1.0},
232         { 0.0,-1.0, 0.0},
233         { M_SQRT1_2, M_SQRT1_2, 0.0},
234         {-1.0, 0.0, 0.0},
235         { 0.0, 0.0,-1.0}
236 };
237
238 /* default models */
239 #define Z   0.0
240 #define L  90.0
241 #define P 180.0
242 #define R 270.0
243
244 static model_t default_models[] = { 
245         { "Ball", 
246                 { R, R, L, L, R, L, R, R, L, R, L, L, R, R, L, L, R, L, R, R, L, R, L }
247         },
248         { "Snow",
249                 { R, R, R, R, L, L, L, L, R, R, R, R, L, L, L, L, R, R, R, R, L, L, L }
250         },
251         { "Propellor",
252                 { Z, Z, Z, R, L, R, Z, L, Z, Z, Z, R, L, R, Z, L, Z, Z, Z, R, L, R, Z }
253         },
254         { "Flamingo",
255                 { Z, P, Z, Z, Z, Z, Z, P, R, R, P, R, L, P, L, R, P, R, R, Z, Z, Z, P }
256         },
257         { "Cat",
258                 { Z, P, P, Z, P, P, Z, L, Z, P, P, Z, P, P, Z, P, P, Z, Z, Z, Z, Z, Z }
259         },
260         { "Rooster",
261                 { Z, Z, P, P, Z, L, Z, L, R, P, R, Z, P, P, Z, R, P, R, L, Z, L, Z, P }
262         }
263 };
264
265 /* add a model to the model list */
266 model_t * add_model(model_t * models, char * name, int * rotations, int * count) {
267         int i;
268         
269         (*count)++;
270         models = realloc(models, sizeof(model_t) * (*count));
271         models[(*count)-1].name = strdup(name);
272 #ifdef DEBUG
273         fprintf(stderr, "resized models to %d bytes for model %s\n", sizeof(model_t) * (*count), models[(*count)-1].name);
274 #endif
275         for (i = 0; i < 24; i++) {
276                 models[(*count)-1].node[i] = rotations[i] * 90.0;
277         }
278         return models;
279 }
280
281 /* filename is the name of the file to load
282  * models is the pointer to where the models will be kept
283  * returns a new pointer to models
284  * count is number of models read
285  */
286 model_t * load_modelfile(char * filename, model_t * models, int * count) {
287         int c;
288         FILE * f;
289         char buffy[256];
290         int rotations[24];
291         int name = 1;
292         int rots = 0;
293
294         f = fopen(filename, "r");
295         if (f == NULL) {
296                 int error_msg_len = strlen(filename) + 33 + 1;
297                 char * error_msg = (char *) malloc(sizeof(char) * error_msg_len);
298                 sprintf(error_msg, "Unable to open model data file \"%s\"", filename);
299                 perror(error_msg);
300                 free(error_msg);
301                 return models;
302         }
303                 
304         while ((c = getc(f)) != EOF) {
305                 switch (c) {
306                         /* ignore comments */
307                         case '#':
308                                 while (c != '\n')
309                                         c = getc(f);
310                                 break;
311                         case ':':
312                                 buffy[name-1] = '\0';
313                                 name = 0;
314                                 break;
315                         case '\n':
316                                 if (rots > 0) {
317 #ifdef DEBUG
318                                         /* print out the model we just read in */
319                                         int i;
320                                         printf("%s: ", buffy);
321                                         for (i = 0; i < rots; i++) {
322                                                 switch (rotations[i]) {
323                                                         case LEFT:
324                                                                 printf("L");
325                                                                 break;
326                                                         case RIGHT:
327                                                                 printf("R");
328                                                                 break;
329                                                         case PIN:
330                                                                 printf("P");
331                                                                 break;
332                                                         case ZERO:
333                                                                 printf("Z");
334                                                                 break;
335                                                 }
336                                         }
337                                         printf("\n");
338 #endif
339                                         models = add_model(models, buffy, rotations, count);
340                                 }
341                                 name = 1;
342                                 rots = 0;
343                                 break;
344                         default:
345                                 if (name) {
346                                         buffy[name-1] = c;
347                                         name++;
348                                         if (name > 255)
349                                                 fprintf(stderr, "buffy overflow warning\n");
350                                 } else {
351                                         switch (c) {
352                                                 case '0':
353                                                 case 'Z':
354                                                         rotations[rots] = ZERO;
355                                                         rots++;
356                                                         break;
357                                                 case '1':
358                                                 case 'L':
359                                                         rotations[rots] = LEFT;
360                                                         rots++;
361                                                         break;
362                                                 case '2':
363                                                 case 'P':
364                                                         rotations[rots] = PIN;
365                                                         rots++;
366                                                         break;
367                                                 case '3':
368                                                 case 'R':
369                                                         rotations[rots] = RIGHT;
370                                                         rots++;
371                                                         break;
372                                                 default:
373                                                         break;
374                                         }
375                                 }
376                                 break;
377                 }
378         }
379         return models;
380 }
381
382 model_t * load_models(char * dirpath, model_t * models, int * count) {
383         char name[1024];
384         struct dirent * dp;
385         DIR * dfd;
386
387         if ((dfd = opendir(dirpath)) == NULL) {
388                 if (strstr(dirpath, "data") == NULL)
389 /*                      fprintf(stderr, "load_models: can't read %s/\n", dirpath); */
390                 return models;
391         }
392         while ((dp = readdir(dfd)) != NULL) {
393                 if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
394                         continue;
395                 if (strlen(dirpath) + strlen(dp->d_name) + 2 > sizeof(name))
396                         fprintf(stderr, "load_models: name %s/%s too long\n", dirpath, dp->d_name);
397                 else {
398                         sprintf(name, "%s/%s", dirpath, dp->d_name);
399                         if (strcmp(&name[(int) strlen(name) - 7], "glsnake") == 0) {
400 #ifdef DEBUG
401                                 fprintf(stderr, "load_models: opening %s\n", name);     
402 #endif
403                                 models = load_modelfile(name, models, count);
404                         }
405                 }
406         }
407         closedir(dfd);
408         return models;
409 }
410
411 /* snake metrics */
412 #define X_MASK 1
413 #define Y_MASK 2
414 #define Z_MASK 4
415 #define GETSCALAR(vec,mask) ((vec)==(mask) ? 1 : ((vec)==-(mask) ? -1 : 0 ))
416
417 int cross_product(int src_dir, int dest_dir) {
418         return X_MASK * (GETSCALAR(src_dir, Y_MASK) * GETSCALAR(dest_dir, Z_MASK) -
419                         GETSCALAR(src_dir, Z_MASK) * GETSCALAR(dest_dir, Y_MASK)) +
420                 Y_MASK * (GETSCALAR(src_dir, Z_MASK) * GETSCALAR(dest_dir, X_MASK) -
421                         GETSCALAR(src_dir, X_MASK) * GETSCALAR(dest_dir, Z_MASK)) +
422                 Z_MASK * (GETSCALAR(src_dir, X_MASK) * GETSCALAR(dest_dir, Y_MASK) -
423                         GETSCALAR(src_dir, Y_MASK) * GETSCALAR(dest_dir, X_MASK));
424 }
425
426 void calc_snake_metrics(glsnake_configuration * bp) {
427         int src_dir, dest_dir;
428         int i, x, y, z;
429         int prev_src_dir = -Y_MASK;
430         int prev_dest_dir = Z_MASK;
431         int grid[25][25][25];
432
433         /* zero the grid */
434         memset(&grid, 0, sizeof(int) * 25*25*25);
435
436         bp->is_legal = 1;
437         x = y = z = 12;
438
439         /* trace path of snake and keep record for is_legal */
440         for (i = 0; i < 23; i++) {
441                 /* establish new state variables */
442                 src_dir = -prev_dest_dir;
443                 x += GETSCALAR(prev_dest_dir, X_MASK);
444                 y += GETSCALAR(prev_dest_dir, Y_MASK);
445                 z += GETSCALAR(prev_dest_dir, Z_MASK);
446
447                 switch ((int) bp->node[i].dest_angle) {
448                         case (int) (ZERO * 90.0):
449                                 dest_dir = -prev_src_dir;
450                                 break;
451                         case (int) (PIN * 90.0):
452                                 dest_dir = prev_src_dir;
453                                 break;
454                         case (int) (RIGHT * 90.):
455                         case (int) (LEFT * 90.0):
456                                 dest_dir = cross_product(prev_src_dir, prev_dest_dir);
457                                 if (bp->node[i].dest_angle == (int) (RIGHT * 90.0))
458                                         dest_dir = -dest_dir;
459                                 break;
460                         default:
461                                 /* prevent spurious "might be used uninitialised" warnings */
462                                 dest_dir = 0;
463                                 break;
464                 }
465
466                 if (grid[x][y][z] == 0)
467                         grid[x][y][z] = src_dir + dest_dir;
468                 else if (grid[x][y][z] + src_dir + dest_dir == 0)
469                         grid[x][y][z] = 8;
470                 else
471                         bp->is_legal = 0;
472
473                 prev_src_dir = src_dir;
474                 prev_dest_dir = dest_dir;
475         }
476
477         /* determine if the snake is cyclic */
478         bp->is_cyclic = (dest_dir == Y_MASK && x == 12 && y == 11 && x == 12);
479
480         /* determine last turn */
481         bp->last_turn = -1;
482         if (bp->is_cyclic) {
483                 switch (src_dir) {
484                         case -Z_MASK:
485                                 bp->last_turn = ZERO * 90.0;
486                                 break;
487                         case Z_MASK:
488                                 bp->last_turn = PIN * 90.0;
489                                 break;
490                         case X_MASK:
491                                 bp->last_turn = LEFT * 90.0;
492                                 break;
493                         case -X_MASK:
494                                 bp->last_turn = RIGHT * 90.0;
495                                 break;
496                 }
497         }
498 }
499
500 void set_colours(glsnake_configuration * bp, int immediate) {
501         /* set target colour */
502         if (!bp->is_legal) {
503                 bp->colour_t[0] = 0.5;
504                 bp->colour_t[1] = 0.5;
505                 bp->colour_t[2] = 0.5;
506         } else if (bp->is_cyclic) {
507                 bp->colour_t[0] = 0.4;
508                 bp->colour_t[1] = 0.8;
509                 bp->colour_t[2] = 0.2;
510         } else {
511                 bp->colour_t[0] = 0.3;
512                 bp->colour_t[1] = 0.1;
513                 bp->colour_t[2] = 0.9;
514         }
515         if (immediate) {
516                 bp->colour_i[0] = bp->colour_t[0] - bp->colour[0];
517                 bp->colour_i[1] = bp->colour_t[1] - bp->colour[1];
518                 bp->colour_i[2] = bp->colour_t[2] - bp->colour[2];
519         } else {
520                 /* instead of 50.0, I should actually work out how many times this gets
521                  * called during a morph */
522                 bp->colour_i[0] = (bp->colour_t[0] - bp->colour[0]) / 50.0;
523                 bp->colour_i[1] = (bp->colour_t[1] - bp->colour[1]) / 50.0;
524                 bp->colour_i[2] = (bp->colour_t[2] - bp->colour[2]) / 50.0;
525         }
526 }
527
528 void start_morph(int model_index, int immediate, glsnake_configuration * bp) {
529         int i;
530
531         for (i = 0; i < 23; i++) {
532                 bp->node[i].dest_angle = bp->models[model_index].node[i];
533                 if (immediate)
534                         bp->node[i].cur_angle = bp->models[model_index].node[i];
535         }
536         
537         calc_snake_metrics(bp);
538         set_colours(bp, 0);
539         bp->cur_model = model_index;
540         bp->morphing = 1;
541 }
542
543 /* convex hull */
544
545 /* model labels */
546 void draw_label(ModeInfo * mi) {
547         glsnake_configuration *bp = &glc[MI_SCREEN(mi)];
548         
549         glPushAttrib(GL_TRANSFORM_BIT | GL_ENABLE_BIT);
550         glDisable(GL_LIGHTING);
551         glDisable(GL_DEPTH_TEST);
552         glMatrixMode(GL_PROJECTION);
553         glPushMatrix();
554         glLoadIdentity();
555         glMatrixMode(GL_MODELVIEW);
556         glPushMatrix();
557         glLoadIdentity();
558         gluOrtho2D(0, mi->xgwa.width, 0, mi->xgwa.height);
559         glColor3f(1.0, 1.0, 0.0);
560         {
561                 char * s;
562                 int i, /* w, */ l;
563
564                 if (bp->interactive)
565                         s = "interactive";
566                 else
567                         s = bp->models[bp->cur_model].name;
568
569                 l = strlen(s);
570                 /*
571                 w = 0;
572                 for (i = 0; i < l; i++) {
573                         w += (bp->font->per_char 
574                                         ? bp->font->per_char[((int)s[i]) - bp->font->min_char_or_byte2].rbearing 
575                                         : bp->font->min_bounds.rbearing);
576                 }
577                 */
578                 
579                 glRasterPos2f(10, mi->xgwa.height - 10 - (bp->font->ascent + bp->font->descent));
580                                 /* mi->xgwa.width - w, bp->font->descent + bp->font->ascent); */
581
582                 /* fprintf(stderr, "afaf.width = %d, w = %d\n", mi->xgwa.width, w); */
583                 
584                 for (i = 0; i < l; i++)
585                         glCallList(bp->font_list + (int)s[i]);
586         }
587         glPopMatrix();
588         glMatrixMode(GL_PROJECTION);
589         glPopMatrix();
590         glPopAttrib();
591 }
592
593 /* load the fonts -- this function borrowed from molecule.c */
594 static void load_font(ModeInfo * mi, char * res, XFontStruct ** fontp, GLuint * dlistp) {
595         const char * font = get_string_resource(res, "Font");
596         XFontStruct * f;
597         Font id;
598         int first, last;
599
600         if (!font)
601                 font = "-*-helvetica-medium-r-*-*-*-120-*";
602
603         f = XLoadQueryFont(mi->dpy, font);
604         if (!f)
605                 f = XLoadQueryFont(mi->dpy, "fixed");
606
607         id = f->fid;
608         first = f->min_char_or_byte2;
609         last = f->max_char_or_byte2;
610         
611         clear_gl_error();
612         *dlistp = glGenLists((GLuint) last + 1);
613         check_gl_error("glGenLists");
614         glXUseXFont(id, first, last - first + 1, *dlistp + first);
615         check_gl_error("glXUseXFont");
616
617         *fontp = f;
618 }
619
620
621
622 /* window management */
623 void glsnake_reshape(ModeInfo *mi, int w, int h) {
624         glViewport (0, 0, (GLint) w, (GLint) h);
625         glMatrixMode(GL_PROJECTION);
626         glLoadIdentity();
627         gluPerspective(25.0, w/(GLfloat)h, 1.0, 100.0 );
628         glMatrixMode(GL_MODELVIEW);
629         glLoadIdentity();
630 }
631
632 static void gl_init(ModeInfo *mi) {
633         /* glsnake_configuration *bp = &glc[MI_SCREEN(mi)]; */
634         int wire = MI_IS_WIREFRAME(mi);
635         float light_pos[][3] = {{0.0,0.0,20.0},{0.0,20.0,0.0}};
636         float light_dir[][3] = {{0.0,0.0,-20.0},{0.0,-20.0,0.0}};
637
638         glClearColor(0.0, 0.0, 0.0, 0.0);
639         glEnable(GL_DEPTH_TEST);
640         glShadeModel(GL_SMOOTH);
641         glCullFace(GL_BACK);
642         glEnable(GL_CULL_FACE);
643         glEnable(GL_NORMALIZE);
644
645         if (!wire) {
646                 glColor3f(1.0, 1.0, 1.0);
647                 glLightfv(GL_LIGHT0, GL_POSITION, light_pos[0]);
648                 glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, light_dir[0]);
649                 glLightfv(GL_LIGHT1, GL_POSITION, light_pos[1]);
650                 glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, light_dir[1]);
651                 glEnable(GL_LIGHTING);
652                 glEnable(GL_LIGHT0);
653                 glEnable(GL_LIGHT1);
654                 glEnable(GL_COLOR_MATERIAL);
655         }
656 }
657
658 /* lifted from lament.c */
659 #define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n))))
660 #define RANDSIGN() ((random() & 1) ? 1 : -1)
661
662 void glsnake_init(ModeInfo *mi) {
663         glsnake_configuration * bp;
664         int wire = MI_IS_WIREFRAME(mi);
665
666         if (!glc) {
667                 glc = (glsnake_configuration *) calloc(MI_NUM_SCREENS(mi), sizeof(glsnake_configuration));
668                 if (!glc) {
669                         fprintf(stderr, "%s: out of memory\n", progname);
670                         exit(1);
671                 }
672                 bp = &glc[MI_SCREEN(mi)];
673         }
674
675         bp = &glc[MI_SCREEN(mi)];
676
677         if ((bp->glx_context = init_GL(mi)) != NULL) {
678                 gl_init(mi);
679                 glsnake_reshape(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
680         }
681
682         /* initialise config variables */
683         memset(&bp->node, 0, sizeof(nodeang_t) * 24);
684         bp->m_count = sizeof(default_models) / sizeof(model_t); /* overwrite this in a bit */
685         bp->selected = 11;
686         bp->is_cyclic = 0;
687         bp->is_legal = 1;
688         bp->last_turn = -1;
689         bp->roty = 0.0;
690         bp->rotz = 0.0;
691         bp->morphing = 0;
692         bp->paused = 0;
693         bp->dragging = 0;
694         bp->interactive = 0;
695
696         {
697 # ifdef GETTIMEOFDAY_TWO_ARGS
698           struct timezone tzp;
699           gettimeofday(&bp->last_iteration, &tzp);
700 # else
701           gettimeofday(&bp->last_iteration);
702 # endif
703         }
704
705         memcpy(&bp->last_morph, &(bp->last_iteration),
706                sizeof(bp->last_morph));
707         /* srand((unsigned int) bp->last_iteration.time); */
708
709         /* load the model files */
710         /* first copy the defaults to bp->m_count */
711         bp->models = (model_t *) malloc(sizeof(model_t) * bp->m_count);
712         memcpy(bp->models, default_models, bp->m_count * sizeof(model_t));
713         /* then add on models from the Debian model file location */
714         bp->models = load_models("/usr/share/glsnake", bp->models, &(bp->m_count));
715
716         bp->m = bp->cur_model = RAND(bp->m_count);
717         start_morph(bp->cur_model, 1, bp);
718
719         calc_snake_metrics(bp);
720         set_colours(bp, 1);
721
722         /* set up a font for the labels */
723         if (labels)
724                 load_font(mi, "labelfont", &bp->font, &bp->font_list);
725         
726         bp->node_list = glGenLists(1);
727         glNewList(bp->node_list, GL_COMPILE);
728         if (!wire) {
729                 /* corners */
730                 glBegin(GL_TRIANGLES);
731                 glNormal3fv(solid_prism_n[0]);
732                 glVertex3fv(solid_prism_v[0]);
733                 glVertex3fv(solid_prism_v[2]);
734                 glVertex3fv(solid_prism_v[1]);
735     
736                 glNormal3fv(solid_prism_n[1]);
737                 glVertex3fv(solid_prism_v[6]);
738                 glVertex3fv(solid_prism_v[7]);
739                 glVertex3fv(solid_prism_v[8]);
740
741                 glNormal3fv(solid_prism_n[2]);
742                 glVertex3fv(solid_prism_v[12]);
743                 glVertex3fv(solid_prism_v[13]);
744                 glVertex3fv(solid_prism_v[14]);
745     
746                 glNormal3fv(solid_prism_n[3]);
747                 glVertex3fv(solid_prism_v[3]);
748                 glVertex3fv(solid_prism_v[4]);
749                 glVertex3fv(solid_prism_v[5]);
750         
751                 glNormal3fv(solid_prism_n[4]);
752                 glVertex3fv(solid_prism_v[9]);
753                 glVertex3fv(solid_prism_v[11]);
754                 glVertex3fv(solid_prism_v[10]);
755
756                 glNormal3fv(solid_prism_n[5]);
757                 glVertex3fv(solid_prism_v[16]);
758                 glVertex3fv(solid_prism_v[15]);
759                 glVertex3fv(solid_prism_v[17]);
760                 glEnd();
761
762                 /* edges */
763                 glBegin(GL_QUADS);
764                 glNormal3fv(solid_prism_n[6]);
765                 glVertex3fv(solid_prism_v[0]);
766                 glVertex3fv(solid_prism_v[12]);
767                 glVertex3fv(solid_prism_v[14]);
768                 glVertex3fv(solid_prism_v[2]);
769         
770                 glNormal3fv(solid_prism_n[7]);
771                 glVertex3fv(solid_prism_v[0]);
772                 glVertex3fv(solid_prism_v[1]);
773                 glVertex3fv(solid_prism_v[7]);
774                 glVertex3fv(solid_prism_v[6]);
775         
776                 glNormal3fv(solid_prism_n[8]);
777                 glVertex3fv(solid_prism_v[6]);
778                 glVertex3fv(solid_prism_v[8]);
779                 glVertex3fv(solid_prism_v[13]);
780                 glVertex3fv(solid_prism_v[12]);
781         
782                 glNormal3fv(solid_prism_n[9]);
783                 glVertex3fv(solid_prism_v[3]);
784                 glVertex3fv(solid_prism_v[5]);
785                 glVertex3fv(solid_prism_v[17]);
786                 glVertex3fv(solid_prism_v[15]);
787         
788                 glNormal3fv(solid_prism_n[10]);
789                 glVertex3fv(solid_prism_v[3]);
790                 glVertex3fv(solid_prism_v[9]);
791                 glVertex3fv(solid_prism_v[10]);
792                 glVertex3fv(solid_prism_v[4]);
793         
794                 glNormal3fv(solid_prism_n[11]);
795                 glVertex3fv(solid_prism_v[15]);
796                 glVertex3fv(solid_prism_v[16]);
797                 glVertex3fv(solid_prism_v[11]);
798                 glVertex3fv(solid_prism_v[9]);
799         
800                 glNormal3fv(solid_prism_n[12]);
801                 glVertex3fv(solid_prism_v[1]);
802                 glVertex3fv(solid_prism_v[2]);
803                 glVertex3fv(solid_prism_v[5]);
804                 glVertex3fv(solid_prism_v[4]);
805         
806                 glNormal3fv(solid_prism_n[13]);
807                 glVertex3fv(solid_prism_v[8]);
808                 glVertex3fv(solid_prism_v[7]);
809                 glVertex3fv(solid_prism_v[10]);
810                 glVertex3fv(solid_prism_v[11]);
811         
812                 glNormal3fv(solid_prism_n[14]);
813                 glVertex3fv(solid_prism_v[13]);
814                 glVertex3fv(solid_prism_v[16]);
815                 glVertex3fv(solid_prism_v[17]);
816                 glVertex3fv(solid_prism_v[14]);
817                 glEnd();
818         
819                 /* faces */
820                 glBegin(GL_TRIANGLES);
821                 glNormal3fv(solid_prism_n[15]);
822                 glVertex3fv(solid_prism_v[0]);
823                 glVertex3fv(solid_prism_v[6]);
824                 glVertex3fv(solid_prism_v[12]);
825         
826                 glNormal3fv(solid_prism_n[19]);
827                 glVertex3fv(solid_prism_v[3]);
828                 glVertex3fv(solid_prism_v[15]);
829                 glVertex3fv(solid_prism_v[9]);
830                 glEnd();
831         
832                 glBegin(GL_QUADS);
833                 glNormal3fv(solid_prism_n[16]);
834                 glVertex3fv(solid_prism_v[1]);
835                 glVertex3fv(solid_prism_v[4]);
836                 glVertex3fv(solid_prism_v[10]);
837                 glVertex3fv(solid_prism_v[7]);
838         
839                 glNormal3fv(solid_prism_n[17]);
840                 glVertex3fv(solid_prism_v[8]);
841                 glVertex3fv(solid_prism_v[11]);
842                 glVertex3fv(solid_prism_v[16]);
843                 glVertex3fv(solid_prism_v[13]);
844         
845                 glNormal3fv(solid_prism_n[18]);
846                 glVertex3fv(solid_prism_v[2]);
847                 glVertex3fv(solid_prism_v[14]);
848                 glVertex3fv(solid_prism_v[17]);
849                 glVertex3fv(solid_prism_v[5]);
850                 glEnd();
851         } else {
852                 /* build wire display list */
853                 glBegin(GL_LINE_STRIP);
854                 glVertex3fv(wire_prism_v[0]);
855                 glVertex3fv(wire_prism_v[1]);
856                 glVertex3fv(wire_prism_v[2]);
857                 glVertex3fv(wire_prism_v[0]);
858                 glVertex3fv(wire_prism_v[3]);
859                 glVertex3fv(wire_prism_v[4]);
860                 glVertex3fv(wire_prism_v[5]);
861                 glVertex3fv(wire_prism_v[3]);
862                 glEnd();
863         
864                 glBegin(GL_LINES);
865                 glVertex3fv(wire_prism_v[1]);
866                 glVertex3fv(wire_prism_v[4]);
867                 glVertex3fv(wire_prism_v[2]);
868                 glVertex3fv(wire_prism_v[5]);
869                 glEnd();
870         }
871         glEndList();
872 }
873
874 /* "jwz?  no way man, he's my idle" -- Jaq, 2001.
875  * I forget the context :( */
876 void glsnake_idol(glsnake_configuration * bp) {
877         /* time since last iteration */
878         long iter_msec;
879         /* time since the beginning of last morph */
880         long morf_msec;
881         float iter_angle_max;
882         int i;
883         struct timeval current_time;
884         int still_morphing;
885
886         /* Do nothing to the model if we are paused */
887         if (bp->paused) {
888                 /* Avoid busy waiting when nothing is changing */
889                 usleep(1);
890                 return;
891         }
892
893         {
894 # ifdef GETTIMEOFDAY_TWO_ARGS
895           struct timezone tzp;
896           gettimeofday(&current_time, &tzp);
897 # else
898           gettimeofday(&current_time);
899 # endif
900         }
901
902         /* <spiv> Well, ftime gives time with millisecond resolution.
903          * <Jaq> if current time is exactly equal to last iteration, 
904          *       then don't do this block
905          * <spiv> (or worse, perhaps... who knows what the OS will do)
906          * <spiv> So if no discernable amount of time has passed:
907          * <spiv>   a) There's no point updating the screen, because
908          *             it would be the same
909          * <spiv>   b) The code will divide by zero
910          */
911         iter_msec = ((long) current_time.tv_usec - bp->last_iteration.tv_usec)/1000L + 
912                     ((long) current_time.tv_sec - bp->last_iteration.tv_sec) * 1000L;
913         if (iter_msec) {
914                 /* save the current time */
915                 memcpy(&bp->last_iteration, &current_time,
916                        sizeof(bp->last_iteration));
917                 
918                 /* work out if we have to switch models */
919                 morf_msec = (bp->last_iteration.tv_usec - bp->last_morph.tv_usec)/1000L +
920                         ((long) (bp->last_iteration.tv_sec - bp->last_morph.tv_sec) * 1000L);
921
922                 if ((morf_msec > statictime) && !bp->interactive) {
923                         memcpy(&bp->last_morph, &(bp->last_iteration),
924                                sizeof(bp->last_morph));
925                         start_morph(RAND(bp->m_count), 0, bp);
926                 }
927
928                 if (bp->interactive && !bp->morphing) {
929                         usleep(1);
930                         return;
931                 }
932
933                 if (!bp->dragging && !bp->interactive) {
934                         bp->roty += 360/((1000/yspin)/iter_msec);
935                         bp->rotz += 360/((1000/zspin)/iter_msec);
936                 }
937
938                 /* work out the maximum angle for this iteration */
939                 iter_angle_max = 90.0 * (velocity/1000.0) * iter_msec;
940
941                 still_morphing = 0;
942                 for (i = 0; i < 24; i++) {
943                         float cur_angle = bp->node[i].cur_angle;
944                         float dest_angle = bp->node[i].dest_angle;
945                         if (cur_angle != dest_angle) {
946                                 still_morphing = 1;
947                                 if (fabs(cur_angle - dest_angle) <= iter_angle_max)
948                                         bp->node[i].cur_angle = dest_angle;
949                                 else if (fmod(cur_angle - dest_angle + 360, 360) > 180)
950                                         bp->node[i].cur_angle = fmod(cur_angle + iter_angle_max, 360);
951                                 else
952                                         bp->node[i].cur_angle = fmod(cur_angle + 360 - iter_angle_max, 360);
953                         }
954                 }
955
956                 if (!still_morphing)
957                         bp->morphing = 0;
958
959                 /* colour cycling */
960                 if (fabs(bp->colour[0] - bp->colour_t[0]) <= fabs(bp->colour_i[0]))
961                         bp->colour[0] = bp->colour_t[0];
962                 else
963                         bp->colour[0] += bp->colour_i[0];
964                 if (fabs(bp->colour[1] - bp->colour_t[1]) <= fabs(bp->colour_i[1]))
965                         bp->colour[1] = bp->colour_t[1];
966                 else
967                         bp->colour[1] += bp->colour_i[1];
968                 if (fabs(bp->colour[2] - bp->colour_t[2]) <= fabs(bp->colour_i[2]))
969                         bp->colour[2] = bp->colour_t[2];
970                 else
971                         bp->colour[2] += bp->colour_i[2];
972         } else {
973                 /* We are going too fast, so we may as well let the 
974                  * cpu relax a little by sleeping for a millisecond. */
975                 usleep(1);
976         }
977 }
978
979 void glsnake_draw(ModeInfo *mi) {
980         glsnake_configuration *bp = &glc[MI_SCREEN(mi)];
981         Display *dpy = MI_DISPLAY(mi);
982         Window window = MI_WINDOW(mi);
983
984         int i;
985         float ang;
986
987         if (!bp->glx_context)
988         return;
989
990         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
991
992         glMatrixMode(GL_MODELVIEW);
993         glLoadIdentity();
994         gluLookAt(0.0, 0.0, 20.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
995
996         /* rotate and translate into snake space */
997         glRotatef(45.0, -5.0, 0.0, 1.0);
998         glTranslatef(-0.5, 0.0, 0.5);
999
1000         /* rotate the 0th junction */
1001         glTranslatef(0.5, 0.0, 0.5);
1002         /* glMultMatrix(rotation); -- quaternion rotation */
1003         glRotatef(bp->roty, 0.0, 1.0, 0.0);
1004         glRotatef(bp->rotz, 0.0, 0.0, 1.0);
1005         glTranslated(-0.5, 0.0, -0.5);
1006
1007         /* translate middle node to centre */
1008         for (i = 11; i >= 0; i--) {
1009                 ang = bp->node[i].cur_angle;
1010                 glTranslatef(0.5, 0.5, 0.5);
1011                 glRotatef(180+ang, -1.0, 0.0, 0.0);
1012                 glTranslatef(-1.0 - explode, 0.0, 0.0);
1013                 glRotatef(90, 0.0, 0.0, 1.0);
1014                 glTranslatef(-0.5, -0.5, -0.5);
1015         }
1016
1017         /* now draw each node along the snake */
1018         for (i = 0; i < 24; i++) {
1019                 glPushMatrix();
1020
1021                 /* choose a colour for this node */
1022                 if (bp->interactive && (i == bp->selected || i == bp->selected+1))
1023                         glColor3f(1.0, 1.0, 0.0);
1024                 else {
1025                         if (i % 2) {
1026                                 if (scarycolour)
1027                                         glColor3f(0.6, 0.0, 0.9);
1028                                 else
1029                                         glColor3fv(bp->colour);
1030                         } else {
1031                                 if (scarycolour)
1032                                         glColor3f(0.2, 0.9, 1.0);
1033                                 else
1034                                         glColor3f(1.0, 1.0, 1.0);
1035                         }
1036                 }
1037
1038                 /* draw the node */
1039                 glCallList(bp->node_list);
1040
1041                 /* now work out where to draw the next one */
1042
1043                 /* interpolate between models */
1044                 ang = bp->node[i].cur_angle;
1045
1046                 glTranslatef(0.5, 0.5, 0.5);
1047                 glRotatef(90, 0.0, 0.0, -1.0);
1048                 glTranslatef(1.0 + explode, 0.0, 0.0);
1049                 glRotatef(180 + ang, 1.0, 0.0, 0.0);
1050                 glTranslatef(-0.5, -0.5, -0.5);
1051         }
1052
1053         /* clear up the matrix stack */
1054         for (i = 0; i < 24; i++)
1055                 glPopMatrix();
1056
1057         if (labels)
1058                 draw_label(mi);
1059         
1060         if (mi->fps_p)
1061                 do_fps (mi);
1062
1063         glsnake_idol(bp);
1064         
1065         glFlush();
1066         glXSwapBuffers(dpy, window);
1067 }
1068
1069 #endif /* USE_GL */