From http://www.jwz.org/xscreensaver/xscreensaver-5.18.tar.gz
[xscreensaver] / hacks / glx / jigsaw.c
1 /* xscreensaver, Copyright (c) 1997-2012 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  *
11  * Written as an Xlib program some time in 1997.
12  * Rewritten as an OpenGL program 24-Aug-2008.
13  */
14
15 /*
16   Currently, we do this:
17
18     - start pieces off screen and move them in.
19     - when they land, they fill the puzzle grid with a shuffled
20       puzzle (pieces are rotated, too).
21     - swap random pairs of pieces until the puzzle is solved.
22     - scatter the pieces off screen (resulting in black).
23     - load new image and repeat.
24
25   Another idea would be to show the puzzle being solved the way
26   a person would do it:
27
28     - start off with black screen.
29     - assume knowledge of size of grid (position of corners).
30     - find a corner piece, and place it.
31     - while puzzle unsolved:
32       - pick a random piece;
33       - if it is the correct piece for any open edge, place it;
34       - if it fits physically in any rotation at any open edge,
35         place it, then toss it back (show the fake-out).
36       - if it doesn't fit at all, don't animate it at all.
37
38    This would take a long time to solve, I think...
39
40    An even harder idea would involve building up completed "clumps"
41    and sliding them around (a coral growth / accretion approach)
42  */
43
44 #define DEF_SPEED        "1.0"
45 #define DEF_COMPLEXITY   "1.0"
46 #define DEF_RESOLUTION   "100"
47 #define DEF_THICKNESS    "0.06"
48 #define DEF_WOBBLE       "True"
49 #define DEF_DEBUG        "False"
50
51 #define DEF_FONT "-*-helvetica-bold-r-normal-*-240-*"
52 #define DEFAULTS  "*delay:              20000   \n" \
53                   "*showFPS:            False   \n" \
54                   "*font:            " DEF_FONT"\n" \
55                   "*wireframe:          False   \n" \
56                   "*desktopGrabber:   xscreensaver-getimage -no-desktop %s\n" \
57                   "*grabDesktopImages:  False   \n" \
58                   "*chooseRandomImages: True    \n"
59
60
61 # define refresh_jigsaw 0
62 # define release_jigsaw 0
63 #undef countof
64 #define countof(x) (sizeof((x))/sizeof((*x)))
65
66 #ifdef HAVE_COCOA
67 # include "jwxyz.h"
68 #else
69 # include <X11/Xlib.h>
70 # include <GL/gl.h>
71 # include <GL/glu.h>
72 #endif
73
74 #include "xlockmore.h"
75 #include "rotator.h"
76 #include "gltrackball.h"
77 #include "spline.h"
78 #include "normals.h"
79 #include "grab-ximage.h"
80 #include "texfont.h"
81
82 #ifdef HAVE_JWZGLES
83 # include "jwzgles.h"
84 #else /* !HAVE_JWZGLES */
85 # define HAVE_TESS
86 #endif /* !HAVE_JWZGLES */
87
88 #undef BELLRAND
89 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
90
91 #ifdef USE_GL /* whole file */
92
93 #define TOP    0
94 #define RIGHT  1
95 #define BOTTOM 2
96 #define LEFT   3
97
98 #define IN    -1
99 #define FLAT   0
100 #define OUT    1
101
102 typedef struct jigsaw_configuration jigsaw_configuration;
103
104 typedef struct {
105   double x,y,z,r;       /* position and Z rotation (in degrees) */
106 } XYZR;
107
108
109 typedef struct {
110   jigsaw_configuration *jc;
111   int edge[4];
112   GLuint dlist;
113   int polys;
114
115   XYZR home;            /* correct position in puzzle */
116   XYZR current;         /* where it is right now */
117   XYZR from, to;        /* in transit from A to B */
118   double tick;          /* 0-1.0, how far from A to B */
119   double arc_height;    /* height of apex of curved path from A to B */
120   double tilt, max_tilt;
121
122 } puzzle_piece;
123
124
125 struct jigsaw_configuration {
126   GLXContext *glx_context;
127   trackball_state *trackball;
128   rotator *rot;
129   Bool button_down_p;
130   texture_font_data *texfont;
131   GLuint loading_dlist;
132
133   int puzzle_width;
134   int puzzle_height;
135   puzzle_piece *puzzle;
136
137   enum { PUZZLE_LOADING_MSG,
138          PUZZLE_LOADING,
139          PUZZLE_UNSCATTER,
140          PUZZLE_SOLVE, 
141          PUZZLE_SCATTER } state;
142   double pausing;
143   double tick_speed;
144
145   GLuint texid;
146   GLfloat tex_x, tex_y, tex_width, tex_height, aspect;
147
148   GLuint line_thickness;
149 };
150
151 static jigsaw_configuration *sps = NULL;
152
153 static GLfloat speed;
154 static GLfloat complexity_arg;
155 static int resolution_arg;
156 static GLfloat thickness_arg;
157 static Bool wobble_p;
158 static Bool debug_p;
159
160 static XrmOptionDescRec opts[] = {
161   { "-speed",      ".speed",       XrmoptionSepArg, 0 },
162   { "-complexity", ".complexity",  XrmoptionSepArg, 0 },
163   { "-resolution", ".resolution",  XrmoptionSepArg, 0 },
164   { "-thickness",  ".thickness",   XrmoptionSepArg, 0 },
165   { "-wobble",     ".wobble",      XrmoptionNoArg, "True" },
166   { "+wobble",     ".wobble",      XrmoptionNoArg, "False" },
167   { "-debug",      ".debug",       XrmoptionNoArg, "True" },
168 };
169
170 static argtype vars[] = {
171   {&speed,          "speed",       "Speed",       DEF_SPEED,      t_Float},
172   {&complexity_arg, "complexity",  "Complexity",  DEF_COMPLEXITY, t_Float},
173   {&resolution_arg, "resolution",  "Resolution",  DEF_RESOLUTION, t_Int},
174   {&thickness_arg,  "thickness",   "Thickness",   DEF_THICKNESS,  t_Float},
175   {&wobble_p,       "wobble",      "Wobble",      DEF_WOBBLE,     t_Bool},
176   {&debug_p,        "debug",       "Debug",       DEF_DEBUG,      t_Bool},
177 };
178
179 ENTRYPOINT ModeSpecOpt jigsaw_opts = {countof(opts), opts, countof(vars), vars, NULL};
180
181
182 /* Returns a spline describing one edge of a puzzle piece of the given length.
183  */
184 static spline *
185 make_puzzle_curve (int pixels)
186 {
187   double x0 = 0.0000, y0 =  0.0000;
188   double x1 = 0.3333, y1 =  0.1000;
189   double x2 = 0.4333, y2 =  0.0333;
190   double x3 = 0.4666, y3 = -0.0666;
191   double x4 = 0.3333, y4 = -0.1666;
192   double x5 = 0.3666, y5 = -0.2900;
193   double x6 = 0.5000, y6 = -0.3333;
194
195   spline *s = make_spline(20);
196   s->n_controls = 0;
197
198 # define PT(x,y) \
199     s->control_x[s->n_controls] = pixels * (x); \
200     s->control_y[s->n_controls] = pixels * (y); \
201     s->n_controls++
202   PT (  x0, y0);
203   PT (  x1, y1);
204   PT (  x2, y2);
205   PT (  x3, y3);
206   PT (  x4, y4);
207   PT (  x5, y5);
208   PT (  x6, y6);
209   PT (1-x5, y5);
210   PT (1-x4, y4);
211   PT (1-x3, y3);
212   PT (1-x2, y2);
213   PT (1-x1, y1);
214   PT (1-x0, y0);
215 # undef PT
216
217   compute_spline (s);
218   return s;
219 }
220
221
222 #ifdef HAVE_TESS
223
224 static void
225 tess_error_cb (GLenum errorCode)
226 {
227   fprintf (stderr, "%s: tesselation error: %s\n",
228            progname, gluErrorString(errorCode));
229   exit (0);
230 }
231
232
233 static void
234 tess_combine_cb (GLdouble coords[3], GLdouble *d[4], GLfloat w[4], 
235                  GLdouble **dataOut)
236 {
237   GLdouble *new = (GLdouble *) malloc (3 * sizeof(*new));
238   new[0] = coords[0];
239   new[1] = coords[1];
240   new[2] = coords[2];
241   *dataOut = new;
242 }
243
244
245 static void
246 tess_vertex_cb (void *vertex_data, void *closure)
247 {
248   puzzle_piece *p = (puzzle_piece *) closure;
249   GLdouble *v = (GLdouble *) vertex_data;
250   GLdouble x = v[0];
251   GLdouble y = v[1];
252   GLdouble z = v[2];
253
254   if (p)
255     {
256       GLfloat pw = p->jc->puzzle_width;
257       GLfloat ph = p->jc->puzzle_height;
258
259       GLfloat xx = x / (GLfloat) resolution_arg;  /* 0-1 from piece origin */
260       GLfloat yy = y / (GLfloat) resolution_arg;
261       GLdouble tx = (p->home.x + xx)      / pw;   /* 0-1 from puzzle origin */
262       GLdouble ty = (ph - p->home.y - yy) / ph;
263
264       tx = p->jc->tex_x + (tx * p->jc->tex_width);
265       ty = p->jc->tex_y + (ty * p->jc->tex_height);
266
267       glTexCoord2d (tx, ty);
268     }
269
270   glVertex3d (x, y, z);
271 }
272
273 #else  /* HAVE_TESS */
274
275 /* Writes triangles into the array of floats.
276    Returns the number of floats written (triangles * 9).
277  */
278 static int
279 make_piece_eighth (jigsaw_configuration *jc, const spline *s,
280                    int resolution, int type, GLfloat *out,
281                    Bool flip_x, Bool flip_y, Bool rotate_p)
282 {
283   GLfloat *oout = out;
284   int cx = resolution/2;
285   int cy = resolution/2;
286   int np = (s->n_points / 2) + 1;
287   int last_x = -999999, last_y = -999999;
288   Bool inflected = False;
289   int i;
290
291   if (type == FLAT)
292     {
293       *out++ = cx;
294       *out++ = 0;
295       *out++ = 0;
296
297       *out++ = cx;
298       *out++ = cy;
299       *out++ = 0;
300
301       *out++ = 0;
302       *out++ = 0;
303       *out++ = 0;
304
305       goto END;
306     }
307
308   for (i = (type == IN ? np-1 : 0); 
309        (type == IN ? i >= 0 : i < np);
310        i += (type == IN ? -1 : 1))
311     {
312       int x = s->points[i].x;
313       int y = s->points[i].y;
314
315       if (type == IN)
316         y = -y;
317
318       if (last_x != -999999)
319         {
320           if (!inflected &&
321               (type == IN
322                ? x >= last_x 
323                : x < last_x))
324             {
325               inflected = True;
326
327               *out++ = cx;
328               *out++ = cy;
329               *out++ = 0;
330
331               *out++ = last_x;
332               *out++ = last_y;
333               *out++ = 0;
334
335               if (type == IN)
336                 {
337                   cx = 0;
338                   cy = 0;
339                 }
340               else
341                 {
342                   cy = y;
343                 }
344
345               *out++ = cx;
346               *out++ = cy;
347               *out++ = 0;
348             }
349
350           *out++ = cx;
351           *out++ = cy;
352           *out++ = 0;
353
354           *out++ = last_x;
355           *out++ = last_y;
356           *out++ = 0;
357
358           *out++ = x;
359           *out++ = y;
360           *out++ = 0;
361         }
362
363       last_x = x;
364       last_y = y;
365     }
366  END:
367
368   {
369     int count = out - oout;
370     Bool cw_p;
371
372     if (flip_x)
373       for (i = 0; i < count; i += 3)
374         oout[i] = resolution - oout[i];
375
376     if (flip_y)
377       for (i = 0; i < count; i += 3)
378         oout[i+1] = resolution - oout[i+1];
379
380     cw_p = (type == IN);
381     if (flip_x) cw_p = !cw_p;
382     if (flip_y) cw_p = !cw_p;
383
384     if (cw_p)
385       for (i = 0; i < count; i += 9)
386         {
387           GLfloat x1 = oout[i+0];
388           GLfloat y1 = oout[i+1];
389           GLfloat x2 = oout[i+3];
390           GLfloat y2 = oout[i+4];
391           GLfloat x3 = oout[i+6];
392           GLfloat y3 = oout[i+7];
393           oout[i+0] = x2;
394           oout[i+1] = y2;
395           oout[i+3] = x1;
396           oout[i+4] = y1;
397           oout[i+6] = x3;
398           oout[i+7] = y3;
399         }
400
401     if (rotate_p)
402       for (i = 0; i < count; i += 3)
403         {
404           GLfloat x = oout[i];
405           GLfloat y = oout[i+1];
406           oout[i]   = resolution - y;
407           oout[i+1] = x;
408         }
409
410     return count;
411   }
412 }
413
414 #endif /* !HAVE_TESS */
415
416
417
418 /* Draws a puzzle piece.  The top/right/bottom/left_type args
419    indicate the direction the tabs point: 1 for out, -1 for in, 0 for flat.
420  */
421 static int
422 draw_piece (jigsaw_configuration *jc, puzzle_piece *p,
423             int resolution, GLfloat thickness,
424             int top_type, int right_type,
425             int bottom_type, int left_type,
426             Bool wire)
427 {
428   spline *s = make_puzzle_curve (resolution);
429   GLdouble *pts = (GLdouble *) malloc (s->n_points * 4 * 3 * sizeof(*pts));
430   int polys = 0;
431   int i, o;
432   GLdouble z = resolution * thickness;
433
434   o = 0;
435   if (top_type == 0) {
436     pts[o++] = 0;
437     pts[o++] = 0; 
438     pts[o++] = z;
439
440     pts[o++] = resolution;
441     pts[o++] = 0;
442     pts[o++] = z;
443   } else {
444     for (i = 0; i < s->n_points; i++) {
445       pts[o++] = s->points[i].x;
446       pts[o++] = s->points[i].y * top_type;
447       pts[o++] = z;
448     }
449   }
450
451   if (right_type == 0) {
452     pts[o++] = resolution;
453     pts[o++] = resolution;
454     pts[o++] = z;
455   } else {
456     for (i = 1; i < s->n_points; i++) {
457       pts[o++] = resolution + s->points[i].y * (-right_type);
458       pts[o++] = s->points[i].x;
459       pts[o++] = z;
460     }
461   }
462
463   if (bottom_type == 0) {
464     pts[o++] = 0;
465     pts[o++] = resolution;
466     pts[o++] = z;
467   } else {
468     for (i = 1; i < s->n_points; i++) {
469       pts[o++] = s->points[s->n_points-i-1].x;
470       pts[o++] = resolution + s->points[s->n_points-i-1].y * (-bottom_type);
471       pts[o++] = z;
472     }
473   }
474
475   if (left_type == 0) {
476     pts[o++] = 0;
477     pts[o++] = 0;
478     pts[o++] = z;
479   } else {
480     for (i = 1; i < s->n_points; i++) {
481       pts[o++] = s->points[s->n_points-i-1].y * left_type;
482       pts[o++] = s->points[s->n_points-i-1].x;
483       pts[o++] = z;
484     }
485   }
486
487   { GLfloat ss = 1.0 / resolution; glScalef (ss, ss, ss); }
488
489 # ifndef HAVE_JWZGLES
490   glPolygonMode (GL_FRONT_AND_BACK, wire ? GL_LINE : GL_FILL);
491 # endif
492
493   if (wire)
494     {
495       glDisable (GL_TEXTURE_2D);
496       glDisable (GL_BLEND);
497       glDisable (GL_LIGHTING);
498     }
499   else
500     {
501 # ifdef HAVE_TESS
502
503 #  ifndef  _GLUfuncptr
504 #   define _GLUfuncptr void(*)(void)
505 #  endif
506       GLUtesselator *tess = gluNewTess();
507       gluTessCallback(tess, GLU_TESS_BEGIN,      (_GLUfuncptr)glBegin);
508       gluTessCallback(tess, GLU_TESS_VERTEX_DATA,(_GLUfuncptr)tess_vertex_cb);
509       gluTessCallback(tess, GLU_TESS_END,        (_GLUfuncptr)glEnd);
510       gluTessCallback(tess, GLU_TESS_COMBINE,    (_GLUfuncptr)tess_combine_cb);
511       gluTessCallback(tess, GLU_TESS_ERROR,      (_GLUfuncptr)tess_error_cb);
512
513       /* front face */
514       glEnable (GL_TEXTURE_2D);
515       glEnable (GL_BLEND);
516       glEnable (GL_LIGHTING);
517       glBindTexture(GL_TEXTURE_2D, jc->texid);
518       glFrontFace (GL_CCW);
519       glNormal3f (0, 0, 1);
520       gluTessBeginPolygon (tess, p);
521       gluTessBeginContour (tess);
522       for (i = 0; i < o; i += 3)
523         {
524           GLdouble *p = pts + i;
525           gluTessVertex (tess, p, p);
526           polys++;  /* not quite right but close */
527         }
528       gluTessEndContour(tess);
529       gluTessEndPolygon(tess);
530
531       /* back face */
532       glDisable (GL_TEXTURE_2D);
533       glFrontFace (GL_CW);
534       glNormal3f (0, 0, -1);
535       gluTessBeginPolygon (tess, 0);
536       gluTessBeginContour (tess);
537       for (i = 0; i < o; i += 3)
538         {
539           GLdouble *p = pts + i;
540           p[2] = -p[2];
541           gluTessVertex (tess, p, p);
542           polys++;  /* not quite right but close */
543         }
544       gluTessEndContour(tess);
545       gluTessEndPolygon(tess);
546       gluDeleteTess(tess);
547
548       /* Put it back */
549       for (i = 0; i < o; i += 3)
550         {
551           GLdouble *p = pts + i;
552           p[2] = -p[2];
553         }
554
555 # else  /* !HAVE_TESS */
556
557       GLfloat *tri = (GLfloat *)
558         (GLfloat *) malloc (s->n_points * 4 * 3 * 3 * sizeof(*pts));
559       GLfloat *otri = tri;
560       int count;
561       GLdouble zz;
562
563       tri += make_piece_eighth (jc, s, resolution, top_type,    tri, 0, 0, 0);
564       tri += make_piece_eighth (jc, s, resolution, top_type,    tri, 1, 0, 0);
565       tri += make_piece_eighth (jc, s, resolution, left_type,   tri, 0, 1, 1);
566       tri += make_piece_eighth (jc, s, resolution, left_type,   tri, 1, 1, 1);
567       tri += make_piece_eighth (jc, s, resolution, bottom_type, tri, 0, 1, 0);
568       tri += make_piece_eighth (jc, s, resolution, bottom_type, tri, 1, 1, 0);
569       tri += make_piece_eighth (jc, s, resolution, right_type,  tri, 0, 0, 1);
570       tri += make_piece_eighth (jc, s, resolution, right_type,  tri, 1, 0, 1);
571       count = (tri - otri) / 9;
572
573       if (! wire)
574         {
575           glEnable (GL_TEXTURE_2D);
576           glEnable (GL_BLEND);
577           glEnable (GL_LIGHTING);
578           glBindTexture(GL_TEXTURE_2D, jc->texid);
579         }
580
581       for (zz = z; zz >= -z; zz -= 2*z)
582         {
583           int i;
584           glFrontFace (zz > 0 ? GL_CCW : GL_CW);
585           glNormal3f (0, 0, (zz > 0 ? 1 : -1));
586
587           if (zz < 0)
588             glDisable (GL_TEXTURE_2D);  /* back face */
589
590           glPushMatrix();
591           glTranslatef (0, 0, zz);
592
593           tri = otri;
594           if (wire)
595             {
596               for (i = 0; i < count; i++)
597                 {
598                   glBegin (GL_LINE_LOOP);
599                   glVertex3f (tri[0], tri[1], tri[2]); tri += 3;
600                   glVertex3f (tri[0], tri[1], tri[2]); tri += 3;
601                   glVertex3f (tri[0], tri[1], tri[2]); tri += 3;
602                   glEnd();
603                 }
604             }
605           else
606             {
607               GLfloat pw = p->jc->puzzle_width;
608               GLfloat ph = p->jc->puzzle_height;
609               GLfloat r = resolution;
610
611               glBegin (GL_TRIANGLES);
612               for (i = 0; i < count * 3; i++)
613                 {
614                   GLfloat x = *tri++;
615                   GLfloat y = *tri++;
616                   GLfloat z = *tri++;
617
618                   /* 0-1 from piece origin */
619                   GLfloat xx = x / r;
620                   GLfloat yy = y / r;
621
622                   /* 0-1 from puzzle origin */
623                   GLfloat tx = (p->home.x + xx)      / pw;
624                   GLfloat ty = (ph - p->home.y - yy) / ph;
625
626                   tx = p->jc->tex_x + (tx * p->jc->tex_width);
627                   ty = p->jc->tex_y + (ty * p->jc->tex_height);
628
629                   glTexCoord2f (tx, ty);
630                   glVertex3f (x, y, z);
631                 }
632               glEnd();
633             }
634
635           polys += count;
636           glPopMatrix();
637         }
638
639       free (otri);
640 # endif /* !HAVE_TESS */
641     }
642
643   /* side faces */
644
645   glFrontFace (GL_CCW);
646   glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
647   for (i = 0; i < o; i += 3)
648     {
649       int j = (i+o-3) % o;
650       int k = (i+3)   % o;
651       GLdouble *p  = pts + i;
652       GLdouble *pj = pts + j;
653       GLdouble *pk = pts + k;
654
655       do_normal  (pj[0], pj[1],  pj[2],
656                   pj[0], pj[1], -pj[2],
657                   pk[0], pk[1],  pk[2]);
658
659       glVertex3f (p[0], p[1],  p[2]);
660       glVertex3f (p[0], p[1], -p[2]);
661       polys++;
662     }
663   glEnd();
664
665   if (! wire)
666     glColor3f (0.3, 0.3, 0.3);
667
668   /* outline the edges in gray */
669
670   glDisable (GL_TEXTURE_2D);
671   glDisable (GL_LIGHTING);
672   glLineWidth (jc->line_thickness);
673
674   glBegin (GL_LINE_LOOP);
675   for (i = 0; i < o; i += 3)
676     glVertex3f (pts[i], pts[i+1], pts[i+2]);
677   glEnd();
678   polys += o/3;
679
680   glBegin (GL_LINE_LOOP);
681   for (i = 0; i < o; i += 3)
682     glVertex3f (pts[i], pts[i+1], -pts[i+2]);
683   glEnd();
684   polys += o/3;
685
686   free_spline (s);
687   free (pts);
688
689   return polys;
690 }
691
692
693 static void
694 free_puzzle_grid (jigsaw_configuration *jc)
695 {
696   int i;
697   for (i = 0; i < jc->puzzle_width * jc->puzzle_height; i++)
698     glDeleteLists (jc->puzzle[i].dlist,  1);
699   free (jc->puzzle);
700   jc->puzzle = 0;
701   jc->puzzle_width = 0;
702   jc->puzzle_height = 0;
703 }
704
705
706 static void
707 make_puzzle_grid (ModeInfo *mi)
708 {
709   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
710   int wire = MI_IS_WIREFRAME(mi);
711   int x, y;
712   GLfloat size = (8 + (random() % 8)) * complexity_arg;
713
714   if (jc->puzzle)
715     free_puzzle_grid (jc);
716
717   if (wire)
718     jc->aspect = MI_WIDTH(mi) / (float) MI_HEIGHT(mi);
719
720   if (jc->aspect >= 1.0)
721     {
722       jc->puzzle_width  = size;
723       jc->puzzle_height = (size + 0.5) / jc->aspect;
724     }
725   else
726     {
727       jc->puzzle_width  = (size + 0.5) * jc->aspect;
728       jc->puzzle_height = size;
729     }
730
731   if (jc->puzzle_width  < 1) jc->puzzle_width  = 1;
732   if (jc->puzzle_height < 1) jc->puzzle_height = 1;
733
734   if (debug_p)
735     fprintf (stderr, "%s: grid  %4d x %-4d               (%.2f)\n", progname,
736              jc->puzzle_width, jc->puzzle_height,
737              ((float) jc->puzzle_width / jc->puzzle_height));
738
739   jc->puzzle = (puzzle_piece *) 
740     calloc (jc->puzzle_width * (jc->puzzle_height+1), sizeof(*jc->puzzle));
741
742   /* Randomize the right and bottom edge of each piece.
743      Match the left edge of the piece to the right to our right edge.
744      Match the top edge of the piece to the bottom to our bottom edge.
745    */
746   for (y = 0; y < jc->puzzle_height; y++)
747     for (x = 0; x < jc->puzzle_width; x++)
748       {
749         puzzle_piece *p = &jc->puzzle [y     * jc->puzzle_width + x];
750         puzzle_piece *r = &jc->puzzle [y     * jc->puzzle_width + x+1];
751         puzzle_piece *b = &jc->puzzle [(y+1) * jc->puzzle_width + x];
752         p->edge[RIGHT]  = (random() & 1) ? IN : OUT;
753         p->edge[BOTTOM] = (random() & 1) ? IN : OUT;
754         r->edge[LEFT]   = p->edge[RIGHT]  == IN ? OUT : IN;
755         b->edge[TOP]    = p->edge[BOTTOM] == IN ? OUT : IN;
756       }
757
758   /* tell each piece where it belongs. */
759   for (y = 0; y < jc->puzzle_height; y++)
760     for (x = 0; x < jc->puzzle_width; x++)
761       {
762         puzzle_piece *p = &jc->puzzle [y * jc->puzzle_width + x];
763         p->jc = jc;
764         p->home.x = x;
765         p->home.y = y;
766         p->current = p->home;
767
768         /* make sure the outer border is flat */
769         if (p->home.x == 0)  p->edge[LEFT] = FLAT;
770         if (p->home.y == 0)  p->edge[TOP]  = FLAT;
771         if (p->home.x == jc->puzzle_width-1)  p->edge[RIGHT]  = FLAT;
772         if (p->home.y == jc->puzzle_height-1) p->edge[BOTTOM] = FLAT;
773
774         /* generate the polygons */
775         p->dlist = glGenLists (1);
776         check_gl_error ("generating lists");
777         if (p->dlist <= 0) abort();
778
779         glNewList (p->dlist, GL_COMPILE);
780         p->polys += draw_piece (jc, p,
781                                 resolution_arg, thickness_arg,
782                                 p->edge[TOP], p->edge[RIGHT],
783                                 p->edge[BOTTOM], p->edge[LEFT], 
784                                 wire);
785         glEndList();
786       }
787 }
788
789
790 static void shuffle_grid (ModeInfo *mi);
791
792
793 static void
794 image_loaded_cb (const char *filename, XRectangle *geometry,
795                  int image_width, int image_height, 
796                  int texture_width, int texture_height,
797                  void *closure)
798 {
799   ModeInfo *mi = (ModeInfo *) closure;
800   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
801
802   jc->tex_x      = geometry->x      / (float) texture_width;
803   jc->tex_y      = geometry->y      / (float) texture_height;
804   jc->tex_width  = geometry->width  / (float) texture_width; 
805   jc->tex_height = geometry->height / (float) texture_height;
806   jc->aspect     = geometry->width  / (float) geometry->height;
807
808   if (debug_p)
809     {
810       fprintf (stderr, "%s: image %s\n", progname, 
811                (filename ? filename : "(null)"));
812       fprintf (stderr, "%s: image %4d x %-4d + %4d + %-4d (%.2f)\n", progname,
813                geometry->width, geometry->height, geometry->x, geometry->y,
814                (float) geometry->width / geometry->height);
815       fprintf (stderr, "%s: tex   %4d x %-4d\n", progname,
816                texture_width, texture_height);
817       fprintf (stderr, "%s: tex   %4.2f x %4.2f + %4.2f + %4.2f (%.2f)\n",
818                progname,
819                jc->tex_width, jc->tex_height, jc->tex_x, jc->tex_y,
820                (jc->tex_width / jc->tex_height) *
821                (texture_width / texture_height));
822     }
823
824   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
825   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
826   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
827   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
828
829   make_puzzle_grid (mi);
830 }
831
832
833 static void
834 load_image (ModeInfo *mi)
835 {
836   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
837   load_texture_async (mi->xgwa.screen, mi->window,
838                       *jc->glx_context, 0, 0, 
839                       False, jc->texid,
840                       image_loaded_cb, mi);
841 }
842
843
844 /* Whether the two pieces are the same shape, when the second piece
845    is rotated by the given degrees.
846  */
847 static Bool
848 same_shape (puzzle_piece *p0, puzzle_piece *p1, int rotated_by)
849 {
850   switch (rotated_by) 
851     {
852     case 0:
853       return (p0->edge[0] == p1->edge[0] &&
854               p0->edge[1] == p1->edge[1] &&
855               p0->edge[2] == p1->edge[2] &&
856               p0->edge[3] == p1->edge[3]);
857     case 90:
858       return (p0->edge[0] == p1->edge[1] &&
859               p0->edge[1] == p1->edge[2] &&
860               p0->edge[2] == p1->edge[3] &&
861               p0->edge[3] == p1->edge[0]);
862     case 180:
863       return (p0->edge[0] == p1->edge[2] &&
864               p0->edge[1] == p1->edge[3] &&
865               p0->edge[2] == p1->edge[0] &&
866               p0->edge[3] == p1->edge[1]);
867     case 270:
868       return (p0->edge[0] == p1->edge[3] &&
869               p0->edge[1] == p1->edge[0] &&
870               p0->edge[2] == p1->edge[1] &&
871               p0->edge[3] == p1->edge[2]);
872     default:
873       abort();
874     }
875 }
876
877
878 /* Returns the proper rotation for the piece at the given position. 
879  */
880 static int
881 proper_rotation (jigsaw_configuration *jc, puzzle_piece *p, 
882                  double x, double y)
883 {
884   puzzle_piece *p1;
885   int cx = x;
886   int cy = y;
887   if (cx != x) abort();  /* must be in integral position! */
888   if (cy != y) abort();
889   p1 = &jc->puzzle [cy * jc->puzzle_width + cx];
890   if (same_shape (p, p1, 0))   return 0;
891   if (same_shape (p, p1, 90))  return 90;
892   if (same_shape (p, p1, 180)) return 180;
893   if (same_shape (p, p1, 270)) return 270;
894   abort();              /* these two pieces don't match in any rotation! */
895 }
896
897
898 /* Returns the piece currently at the given position. 
899  */
900 static puzzle_piece *
901 piece_at (jigsaw_configuration *jc, double x, double y)
902 {
903   int npieces = jc->puzzle_width * jc->puzzle_height;
904   int i;
905   int cx = x;
906   int cy = y;
907   if (cx != x) abort();  /* must be in integral position! */
908   if (cy != y) abort();
909
910   for (i = 0; i < npieces; i++)
911     {
912       puzzle_piece *p = &jc->puzzle [i];
913       if (p->current.x == cx &&
914           p->current.y == cy)
915         return p;
916     }
917   abort();   /* no piece at that position? */
918 }
919
920
921 static void
922 shuffle_grid (ModeInfo *mi)
923 {
924   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
925   int max_tries = jc->puzzle_width * jc->puzzle_height;
926   int npieces = jc->puzzle_width * jc->puzzle_height;
927   int i;
928
929   for (i = 0; i < npieces; i++)
930     {
931       puzzle_piece *p0 = &jc->puzzle [i];
932       puzzle_piece *p1 = 0;
933       int k;
934
935       for (k = 0; k < max_tries; k++)
936         {
937           p1 = &jc->puzzle [random() % npieces];
938           if (same_shape (p0, p1, 0))   break;
939           if (same_shape (p0, p1, 90))  break;
940           if (same_shape (p0, p1, 180)) break;
941           if (same_shape (p0, p1, 270)) break;
942           p1 = 0;  /* mismatch */
943         }
944       if (p1 && p0 != p1)
945         {
946           XYZR s;
947           s = p0->current; p0->current = p1->current; p1->current = s;
948           p0->current.r = 
949             proper_rotation (jc, p0, p0->current.x, p0->current.y);
950           p1->current.r = 
951             proper_rotation (jc, p1, p1->current.x, p1->current.y);
952         }
953     }
954 }
955
956
957 /* We tend to accumulate floating point errors, e.g., z being 0.000001
958    after a move.  This makes sure float values that should be integral are.
959  */
960 static void
961 smooth_grid (ModeInfo *mi)
962 {
963   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
964   int npieces = jc->puzzle_width * jc->puzzle_height;
965   int i;
966
967   for (i = 0; i < npieces; i++)
968     {
969       puzzle_piece *p = &jc->puzzle [i];
970 # define SMOOTH(P) \
971         P.x = (int) (P.x + 0.5);                  \
972         P.y = (int) (P.y + 0.5);                  \
973         P.z = (int) (P.z + 0.5);                  \
974         P.r = (int) (P.r + 0.5)
975       SMOOTH(p->home);
976       SMOOTH(p->current);
977       SMOOTH(p->from);
978       SMOOTH(p->to);
979       if (p->tick <= 0.0001) p->tick = 0.0;
980       if (p->tick >= 0.9999) p->tick = 1.0;
981     }
982 }
983
984
985 static void
986 begin_scatter (ModeInfo *mi, Bool unscatter_p)
987 {
988   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
989   int npieces = jc->puzzle_width * jc->puzzle_height;
990   int i;
991   XYZR ctr = { 0, };
992   ctr.x = jc->puzzle_width  / 2;
993   ctr.y = jc->puzzle_height / 2;
994
995   for (i = 0; i < npieces; i++)
996     {
997       puzzle_piece *p = &jc->puzzle [i];
998       XYZ a;
999       double d, r, th;
1000       p->tick = -frand(1.0);
1001       p->from = p->current;
1002
1003       a.x = p->from.x - ctr.x;  /* position relative to center */
1004       a.y = p->from.y - ctr.y;
1005
1006       r = sqrt (a.x*a.x + a.y*a.y);
1007       th = atan2 (a.x, a.y);
1008
1009       d = MAX (jc->puzzle_width, jc->puzzle_height) * 2;
1010       r = r*r + d;
1011
1012       p->to.x = ctr.x + (r * sin (th));
1013       p->to.y = ctr.y + (r * cos (th));
1014       p->to.z = p->from.z;
1015       p->to.r = ((int) p->from.r + (random() % 180)) % 360;
1016       p->arc_height = frand(10.0);
1017
1018       if (unscatter_p)
1019         {
1020           XYZR s = p->to; p->to = p->from; p->from = s;
1021           p->current = p->from;
1022         }
1023     }
1024 }
1025
1026
1027 static Bool
1028 solved_p (ModeInfo *mi)
1029 {
1030   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1031   int npieces = jc->puzzle_width * jc->puzzle_height;
1032   int i;
1033
1034   for (i = 0; i < npieces; i++)
1035     {
1036       puzzle_piece *p = &jc->puzzle [i];
1037       if (p->current.x != p->home.x ||
1038           p->current.y != p->home.y ||
1039           p->current.z != p->home.z)
1040         return False;
1041     }
1042   return True;
1043 }
1044
1045
1046 static void
1047 move_one_piece (ModeInfo *mi)
1048 {
1049   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1050   int npieces = jc->puzzle_width * jc->puzzle_height;
1051   int i;
1052
1053   for (i = 0; i < npieces * 100; i++)  /* shouldn't take that long */
1054     {
1055       int i = random() % npieces;
1056       puzzle_piece *p0 = &jc->puzzle [i];
1057       puzzle_piece *p1;
1058
1059       if (p0->current.x == p0->home.x &&
1060           p0->current.y == p0->home.y &&
1061           p0->current.z == p0->home.z)
1062         continue;               /* piece already solved - try again */
1063
1064       /* swap with the piece occupying p0's home cell. */
1065       p1 = piece_at (jc, p0->home.x, p0->home.y);
1066
1067       if (p0 == p1) abort();    /* should have caught this above */
1068
1069       p0->tick = 0;
1070       p0->from = p0->current;
1071       p0->to   = p1->current;
1072       p0->to.r = proper_rotation (jc, p0, p0->to.x, p0->to.y);
1073
1074       p1->tick = 0;
1075       p1->from = p1->current;
1076       p1->to   = p0->current;
1077       p1->to.r = proper_rotation (jc, p1, p1->to.x, p1->to.y);
1078
1079       /* Try to avoid having them intersect each other in the air. */
1080       p0->arc_height = 0;
1081       p1->arc_height = 0;
1082       while (fabs (p0->arc_height - p1->arc_height) < 1.5)
1083         {
1084           p0->arc_height = 0.5 + frand(3.0);
1085           p1->arc_height = 1.0 + frand(3.0);
1086         }
1087
1088 # define RTILT(V) \
1089          V = 90 - BELLRAND(180);       \
1090          if (! (random() % 5)) V *= 2; \
1091          if (! (random() % 5)) V *= 2; \
1092          if (! (random() % 5)) V *= 2
1093       RTILT (p0->max_tilt);
1094       RTILT (p1->max_tilt);
1095 # undef RTILT
1096
1097       if (debug_p)
1098         fprintf (stderr, "%s: swapping %2d,%-2d with %2d,%d\n", progname,
1099                  (int) p0->from.x, (int) p0->from.y,
1100                  (int) p1->from.x, (int) p1->from.y);
1101       return;
1102     }
1103
1104   abort();  /* infinite loop! */
1105 }
1106
1107
1108 static Bool
1109 anim_tick (ModeInfo *mi)
1110 {
1111   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1112   int npieces = jc->puzzle_width * jc->puzzle_height;
1113   int i;
1114   Bool finished_p = True;
1115
1116   if (jc->pausing > 0)
1117     {
1118       jc->pausing -= jc->tick_speed * speed;
1119       if (debug_p && jc->pausing <= 0)
1120         fprintf (stderr, "%s:   (done pausing)\n", progname);
1121       return False;
1122     }
1123
1124   for (i = 0; i < npieces; i++)
1125     {
1126       puzzle_piece *p = &jc->puzzle [i];
1127       double tt;
1128
1129       if (p->tick >= 1.0) continue;     /* this piece is done */
1130       finished_p = False;               /* not done */
1131
1132       p->tick += jc->tick_speed * speed;
1133       if (p->tick > 1.0) p->tick = 1.0;
1134
1135       if (p->tick < 0.0) continue;      /* not yet started */
1136
1137       tt = 1 - sin (M_PI/2 - p->tick * M_PI/2);
1138
1139       p->current.x = p->from.x + ((p->to.x - p->from.x) * tt);
1140       p->current.y = p->from.y + ((p->to.y - p->from.y) * tt);
1141       p->current.z = p->from.z + ((p->to.z - p->from.z) * tt);
1142       p->current.r = p->from.r + ((p->to.r - p->from.r) * tt);
1143
1144       p->current.z += p->arc_height * sin (p->tick * M_PI);
1145
1146       p->tilt = p->max_tilt * sin (p->tick * M_PI);
1147
1148     }
1149
1150   return finished_p;
1151 }
1152
1153
1154 static void
1155 loading_msg (ModeInfo *mi)
1156 {
1157   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1158   int wire = MI_IS_WIREFRAME(mi);
1159   const char *text = "Loading...";
1160   int h;
1161   int w = texture_string_width (jc->texfont, text, &h);
1162
1163   if (wire) return;
1164
1165   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1166
1167   if (! jc->loading_dlist)
1168     {
1169       GLfloat othick = jc->line_thickness;
1170       puzzle_piece P = { 0, };
1171       P.jc = jc;
1172       jc->loading_dlist = glGenLists (1);
1173       glNewList (jc->loading_dlist, GL_COMPILE);
1174       jc->line_thickness = 1;
1175       draw_piece (jc, &P,
1176                   resolution_arg, thickness_arg,
1177                   OUT, OUT, IN, OUT, True);
1178       jc->line_thickness = othick;
1179       glEndList();
1180     }
1181
1182   glColor3f (0.2, 0.2, 0.4);
1183
1184   glPushMatrix();
1185   {
1186     double x, y, z;
1187     get_position (jc->rot, &x, &y, &z, True);
1188     glRotatef (x * 360, 1, 0, 0);
1189     glRotatef (y * 360, 0, 1, 0);
1190     glRotatef (z * 360, 0, 0, 1);
1191     glScalef (5, 5, 5);
1192     glTranslatef (-0.5, -0.5, 0);
1193     glCallList (jc->loading_dlist);
1194   }
1195   glPopMatrix();
1196
1197   glColor3f (0.7, 0.7, 1);
1198
1199
1200   glMatrixMode(GL_PROJECTION);
1201   glPushMatrix();
1202   glLoadIdentity();
1203
1204   glMatrixMode(GL_MODELVIEW);
1205   glPushMatrix();
1206   glLoadIdentity();
1207
1208   {
1209     double rot = current_device_rotation();
1210     glRotatef(rot, 0, 0, 1);
1211     if ((rot >  45 && rot <  135) ||
1212         (rot < -45 && rot > -135))
1213       {
1214         GLfloat s = MI_WIDTH(mi) / (GLfloat) MI_HEIGHT(mi);
1215         glScalef (s, 1/s, 1);
1216       }
1217   }
1218
1219   glOrtho(0, MI_WIDTH(mi), 0, MI_HEIGHT(mi), -1, 1);
1220   glTranslatef ((MI_WIDTH(mi)  - w) / 2,
1221                 (MI_HEIGHT(mi) - h) / 2,
1222                 0);
1223   glEnable (GL_TEXTURE_2D);
1224   glPolygonMode (GL_FRONT, GL_FILL);
1225   glDisable (GL_LIGHTING);
1226   glEnable (GL_BLEND);
1227   glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1228   print_texture_string (jc->texfont, text);
1229   glEnable (GL_DEPTH_TEST);
1230   glPopMatrix();
1231
1232   glMatrixMode(GL_PROJECTION);
1233   glPopMatrix();
1234
1235   glMatrixMode(GL_MODELVIEW);
1236 }
1237
1238
1239 static void
1240 animate (ModeInfo *mi)
1241 {
1242   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1243   double slow = 0.01;
1244   double fast = 0.04;
1245
1246   if (jc->button_down_p && jc->state != PUZZLE_LOADING_MSG)
1247     return;
1248
1249   switch (jc->state)
1250     {
1251     case PUZZLE_LOADING_MSG:
1252       if (! jc->puzzle)
1253         loading_msg (mi);
1254       /* fall through */
1255
1256     case PUZZLE_LOADING:
1257       if (!jc->puzzle) break;   /* still loading */
1258       jc->tick_speed = slow;
1259       shuffle_grid (mi);
1260       smooth_grid (mi);
1261       begin_scatter (mi, True);
1262       jc->pausing = 0;
1263       jc->state = PUZZLE_UNSCATTER;
1264       if (debug_p) fprintf (stderr, "%s: unscattering\n", progname);
1265       break;
1266
1267     case PUZZLE_UNSCATTER:
1268       jc->tick_speed = slow;
1269       if (anim_tick (mi))
1270         {
1271           smooth_grid (mi);
1272           jc->pausing = 1.0;
1273           jc->state = PUZZLE_SOLVE;
1274           if (debug_p) fprintf (stderr, "%s: solving\n", progname);
1275         }
1276       break;
1277
1278     case PUZZLE_SOLVE:
1279       jc->tick_speed = fast;
1280       if (anim_tick (mi))
1281         {
1282           smooth_grid (mi);
1283           if (solved_p (mi))
1284             {
1285               if (debug_p) fprintf (stderr, "%s: solved!\n", progname);
1286               begin_scatter (mi, False);
1287               jc->state = PUZZLE_SCATTER;
1288               jc->pausing = 3.0;
1289               if (debug_p) fprintf (stderr, "%s: scattering\n", progname);
1290             }
1291           else
1292             {
1293               move_one_piece (mi);
1294               jc->pausing = 0.3;
1295             }
1296         }
1297       break;
1298
1299     case PUZZLE_SCATTER:
1300       jc->tick_speed = slow;
1301       if (anim_tick (mi))
1302         {
1303           free_puzzle_grid (jc);
1304           load_image (mi);
1305           jc->state = PUZZLE_LOADING;
1306           jc->pausing = 1.0;
1307           if (debug_p) fprintf (stderr, "%s: loading\n", progname);
1308         }
1309       break;
1310
1311     default:
1312       abort();
1313     }
1314 }
1315
1316
1317 /* Window management, etc
1318  */
1319 ENTRYPOINT void
1320 reshape_jigsaw (ModeInfo *mi, int width, int height)
1321 {
1322   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1323   GLfloat h = (GLfloat) height / (GLfloat) width;
1324
1325   glViewport (0, 0, (GLint) width, (GLint) height);
1326
1327   glMatrixMode(GL_PROJECTION);
1328   glLoadIdentity();
1329   gluPerspective (30.0, 1/h, 1.0, 100.0);
1330
1331   glMatrixMode(GL_MODELVIEW);
1332   glLoadIdentity();
1333   gluLookAt( 0.0, 0.0, 30.0,
1334              0.0, 0.0, 0.0,
1335              0.0, 1.0, 0.0);
1336
1337   glClear(GL_COLOR_BUFFER_BIT);
1338
1339   jc->line_thickness = (MI_IS_WIREFRAME (mi) ? 1 : MAX (1, height / 300.0));
1340 }
1341
1342
1343 ENTRYPOINT Bool
1344 jigsaw_handle_event (ModeInfo *mi, XEvent *event)
1345 {
1346   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1347
1348   if (event->xany.type == ButtonPress &&
1349       event->xbutton.button == Button1)
1350     {
1351       jc->button_down_p = True;
1352       gltrackball_start (jc->trackball,
1353                          event->xbutton.x, event->xbutton.y,
1354                          MI_WIDTH (mi), MI_HEIGHT (mi));
1355       return True;
1356     }
1357   else if (event->xany.type == ButtonRelease &&
1358            event->xbutton.button == Button1)
1359     {
1360       jc->button_down_p = False;
1361       return True;
1362     }
1363   else if (event->xany.type == ButtonPress &&
1364            (event->xbutton.button == Button4 ||
1365             event->xbutton.button == Button5 ||
1366             event->xbutton.button == Button6 ||
1367             event->xbutton.button == Button7))
1368     {
1369       gltrackball_mousewheel (jc->trackball, event->xbutton.button, 10,
1370                               !!event->xbutton.state);
1371       return True;
1372     }
1373   else if (event->xany.type == MotionNotify &&
1374            jc->button_down_p)
1375     {
1376       gltrackball_track (jc->trackball,
1377                          event->xmotion.x, event->xmotion.y,
1378                          MI_WIDTH (mi), MI_HEIGHT (mi));
1379       return True;
1380     }
1381   else if (event->xany.type == KeyPress)
1382     {
1383       KeySym keysym;
1384       char c = 0;
1385       XLookupString (&event->xkey, &c, 1, &keysym, 0);
1386
1387       if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
1388         {
1389           begin_scatter (mi, False);
1390           jc->state = PUZZLE_SCATTER;
1391           return True;
1392         }
1393     }
1394
1395
1396   return False;
1397 }
1398
1399
1400 ENTRYPOINT void 
1401 init_jigsaw (ModeInfo *mi)
1402 {
1403   jigsaw_configuration *jc;
1404   int wire = MI_IS_WIREFRAME(mi);
1405
1406   if (!sps) {
1407     sps = (jigsaw_configuration *)
1408       calloc (MI_NUM_SCREENS(mi), sizeof (jigsaw_configuration));
1409     if (!sps) {
1410       fprintf(stderr, "%s: out of memory\n", progname);
1411       exit(1);
1412     }
1413   }
1414   jc = &sps[MI_SCREEN(mi)];
1415   jc->glx_context = init_GL(mi);
1416
1417   reshape_jigsaw (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1418
1419   if (!wire)
1420     {
1421       GLfloat pos[4] = {0.05, 0.07, 1.00, 0.0};
1422       GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
1423       GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
1424       GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
1425
1426       glLightfv(GL_LIGHT0, GL_POSITION, pos);
1427       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
1428       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
1429       glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
1430     }
1431
1432   jc->trackball = gltrackball_init ();
1433   jc->rot = make_rotator (0, 0, 0, 0, speed * 0.002, True);
1434   jc->texfont = load_texture_font (MI_DISPLAY(mi), "font");
1435
1436   jc->state = PUZZLE_LOADING_MSG;
1437
1438   resolution_arg /= complexity_arg;
1439
1440 # ifndef HAVE_TESS
1441   /* If it's not even, we get crosses. */
1442   if (resolution_arg & 1)
1443     resolution_arg++;
1444 # endif /* !HAVE_TESS */
1445
1446   if (wire)
1447     make_puzzle_grid (mi);
1448   else
1449     load_image (mi);
1450 }
1451
1452
1453 ENTRYPOINT void
1454 draw_jigsaw (ModeInfo *mi)
1455 {
1456   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1457   Display *dpy = MI_DISPLAY(mi);
1458   Window window = MI_WINDOW(mi);
1459   int wire = MI_IS_WIREFRAME(mi);
1460
1461   if (!jc->glx_context)
1462     return;
1463
1464   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(jc->glx_context));
1465
1466   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1467
1468   mi->polygon_count = 0;
1469
1470   glPushMatrix ();
1471   glRotatef(current_device_rotation(), 0, 0, 1);
1472   gltrackball_rotate (jc->trackball);
1473
1474   animate (mi);
1475
1476   if (wobble_p && jc->puzzle)
1477     {
1478       double x, y, z;
1479       double max = 60;
1480       get_position (jc->rot, &x, &y, &z, !jc->button_down_p);
1481       x = 1; /* always lean back */
1482       glRotatef (max/2 - x*max, 1, 0, 0);
1483       glRotatef (max/2 - z*max, 0, 1, 0);
1484     }
1485
1486   if (jc->puzzle)
1487     {
1488       GLfloat s = 14.0 / jc->puzzle_height;
1489       int x, y;
1490
1491       glEnable(GL_CULL_FACE);
1492       glEnable(GL_DEPTH_TEST);
1493       glEnable(GL_NORMALIZE);
1494       glEnable(GL_LINE_SMOOTH);
1495
1496       glScalef (s, s, s);
1497       glTranslatef (-jc->puzzle_width / 2.0, -jc->puzzle_height / 2.0, 0);
1498
1499       if (! wire)
1500         {
1501           glEnable (GL_TEXTURE_2D);
1502           glEnable (GL_BLEND);
1503           glEnable (GL_LIGHTING);
1504           glEnable (GL_LIGHT0);
1505           glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1506         }
1507
1508       for (y = 0; y < jc->puzzle_height; y++)
1509         for (x = 0; x < jc->puzzle_width; x++)
1510           {
1511             puzzle_piece *p = &jc->puzzle [y * jc->puzzle_width + x];
1512             glPushMatrix();
1513             glTranslatef (p->current.x, p->current.y, p->current.z);
1514             glTranslatef (0.5, 0.5, 0);
1515             glRotatef (p->current.r, 0, 0, 1);
1516             glRotatef (p->tilt, 0, 1, 0);
1517             glTranslatef (-0.5, -0.5, 0);
1518             glCallList(p->dlist);
1519             mi->polygon_count += p->polys;
1520             glPopMatrix();
1521           }
1522     }
1523
1524   glPopMatrix ();
1525
1526   if (mi->fps_p) do_fps (mi);
1527   glFinish();
1528
1529   glXSwapBuffers(dpy, window);
1530 }
1531
1532 XSCREENSAVER_MODULE ("Jigsaw", jigsaw)
1533
1534 #endif /* USE_GL */