From http://www.jwz.org/xscreensaver/xscreensaver-5.30.tar.gz
[xscreensaver] / hacks / glx / rubikblocks.c
1 /* rubikblocks, Copyright (c) 2009 Vasek Potocek <vasek.potocek@post.cz>
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
12 /* RubikBlocks - a Rubik's Mirror Blocks puzzle introduced in 2008.
13  * No mirrors in this version, though, hence the altered name.
14  */
15
16 /* TODO:
17  * add reflection to the faces
18  */
19
20 #define DEFAULTS   "*delay:         20000         \n" \
21                    "*showFPS:       False         \n" \
22                    "*wireframe:     False         \n"
23
24 # define refresh_rubikblocks 0
25 #include "xlockmore.h"
26 #include "rotator.h"
27 #include "gltrackball.h"
28
29 #ifdef USE_GL
30
31 #define DEF_SPIN        "True"
32 #define DEF_WANDER      "True"
33 #define DEF_TEXTURE     "True"
34 #define DEF_RANDOMIZE   "False"
35 #define DEF_SPINSPEED   "0.1"
36 #define DEF_ROTSPEED    "3.0"
37 #define DEF_WANDERSPEED "0.005"
38 #define DEF_WAIT        "40.0"
39 #define DEF_CUBESIZE    "1.0"
40
41 #define SHUFFLE 100
42
43 #define TEX_WIDTH  64
44 #define TEX_HEIGHT 64
45 #define BORDER     5
46 #define BORDER2    (BORDER*BORDER)
47
48 #undef countof
49 #define countof(x) (sizeof((x))/sizeof((*x)))
50
51 #define rnd01() ((int)(random()%2))
52
53 /*************************************************************************/
54
55 static Bool spin, wander, rndstart, tex;
56 static float spinspeed, tspeed, wspeed, twait, size;
57
58 static argtype vars[] = {
59   { &spin,      "spin",        "Spin",        DEF_SPIN,        t_Bool},
60   { &wander,    "wander",      "Wander",      DEF_WANDER,      t_Bool},
61   { &rndstart,  "randomize",   "Randomize",   DEF_RANDOMIZE,   t_Bool},
62   { &tex,       "texture",     "Texture",     DEF_TEXTURE,     t_Bool},
63   { &spinspeed, "spinspeed",   "SpinSpeed",   DEF_SPINSPEED,   t_Float},
64   { &tspeed,    "rotspeed",    "RotSpeed",    DEF_ROTSPEED,    t_Float},
65   { &wspeed,    "wanderspeed", "WanderSpeed", DEF_WANDERSPEED, t_Float},
66   { &twait,     "wait",        "Wait",        DEF_WAIT,        t_Float},
67   { &size,      "cubesize",    "CubeSize",    DEF_CUBESIZE,    t_Float},
68 };
69
70 static XrmOptionDescRec opts[] = {
71   { "-spin",        ".spin",        XrmoptionNoArg,  "True" },
72   { "+spin",        ".spin",        XrmoptionNoArg,  "False" },
73   { "-wander",      ".wander",      XrmoptionNoArg,  "True" },
74   { "+wander",      ".wander",      XrmoptionNoArg,  "False" },
75   { "-randomize",   ".randomize",   XrmoptionNoArg,  "True" },
76   { "+randomize",   ".randomize",   XrmoptionNoArg,  "False" },
77   { "-texture",     ".texture",     XrmoptionNoArg,  "True" },
78   { "+texture",     ".texture",     XrmoptionNoArg,  "False" },
79   { "-spinspeed",   ".spinspeed",   XrmoptionSepArg, 0 },
80   { "-wanderspeed", ".wanderspeed", XrmoptionSepArg, 0 },
81   { "-rotspeed",    ".rotspeed",    XrmoptionSepArg, 0 },
82   { "-wait",        ".wait",        XrmoptionSepArg, 0 },
83   { "-cubesize",    ".cubesize",    XrmoptionSepArg, 0 },
84 };
85
86 ENTRYPOINT ModeSpecOpt rubikblocks_opts = {countof(opts), opts, countof(vars), vars, NULL};
87
88 #ifdef USE_MODULES
89 ModStruct   rubikblocks_description =
90 { "rubikblocks", "init_rubikblocks", "draw_rubikblocks", "release_rubikblocks",
91   "draw_rubikblocks", "change_rubikblocks", NULL, &rubikblocks_opts,
92   25000, 1, 1, 1, 1.0, 4, "",
93   "Shows randomly shuffling Rubik's Mirror Blocks puzzle", 0, NULL
94 };
95 #endif
96
97 typedef struct {
98   float         pos[3]; /* _original_ position */
99   float         qr[4];  /* quaternion of rotation */
100   Bool          act;    /* flag if it is undergoing the current rotation */
101 } piece_t;
102
103 typedef struct {
104   GLXContext    *glx_context;
105   rotator       *rot;
106   trackball_state *trackball;
107   GLfloat       ratio;
108   Bool          button_down;
109
110   Bool          pause;          /* pause between two rotations */
111   float         qfram[4];       /* quaternion describing the rotation in one anim. frame */
112   GLfloat       t, tmax;        /* rotation clock */
113   piece_t       pieces[27];     /* type and tilt of all the pieces */
114
115   unsigned char texture[TEX_HEIGHT][TEX_WIDTH];
116   GLuint        list_base;
117   Bool          wire;
118 } rubikblocks_conf;
119
120 static rubikblocks_conf *rubikblocks = NULL;
121
122 static const GLfloat shininess = 20.0;
123 static const GLfloat ambient[] = {0.0, 0.0, 0.0, 1.0};
124 static const GLfloat diffuse[] = {1.0, 1.0, 1.0, 1.0};
125 static const GLfloat position0[] = {1.0, 1.0, 1.0, 0.0};
126 static const GLfloat position1[] = {-1.0, -1.0, 1.0, 0.0};
127 static const GLfloat lmodel_ambient[] = {0.1, 0.1, 0.1, 1.0};
128 static const GLfloat material_ambient[] = {0.7, 0.7, 0.7, 1.0};
129 static const GLfloat material_diffuse[] = {0.7, 0.7, 0.7, 1.0};
130 static const GLfloat material_specular[] = {0.2, 0.2, 0.2, 1.0};
131
132 /*************************************************************************/
133
134 /* Multiplies two quaternions, src*dest, and stores the result in dest. */
135 static void
136 mult_quat(float src[4], float dest[4])
137 {
138   float r, i, j, k;
139   r = src[0]*dest[0] - src[1]*dest[1] - src[2]*dest[2] - src[3]*dest[3];
140   i = src[0]*dest[1] + src[1]*dest[0] + src[2]*dest[3] - src[3]*dest[2];
141   j = src[0]*dest[2] + src[2]*dest[0] + src[3]*dest[1] - src[1]*dest[3];
142   k = src[0]*dest[3] + src[3]*dest[0] + src[1]*dest[2] - src[2]*dest[1];
143   dest[0] = r;
144   dest[1] = i;
145   dest[2] = j;
146   dest[3] = k;
147 }
148
149 /* Sets the 'act' flag for pieces which will undergo the rotation. */
150 static void
151 flag_pieces(piece_t pieces[27], int axis, int side)
152 {
153   int i, j;
154   float q[4];
155   for(i = 0; i < 27; i++)
156   {
157     q[0] = 0;
158     q[1] = pieces[i].pos[0];
159     q[2] = pieces[i].pos[1];
160     q[3] = pieces[i].pos[2];
161     mult_quat(pieces[i].qr, q);
162     for(j = 1; j < 4; j++)
163       q[j] = -q[j];
164     mult_quat(pieces[i].qr, q);
165     for(j = 1; j < 4; j++)
166       q[j] = -q[j];
167     if(fabs(q[axis] - side) < 0.1)
168       pieces[i].act = True;
169     else
170       pieces[i].act = False;
171   }
172 }
173
174 /* "Rounds" the value to the nearest from the set {0, +-1/2, +-1/sqrt(2), +-1}.
175  * It is guaranteed to be pretty close to one when this function is called. */
176 static float 
177 settle_value(float v) 
178 {
179   if(v > 0.9) return 1;
180   else if(v < -0.9) return -1;
181   else if(v > 0.6) return M_SQRT1_2;
182   else if(v < -0.6) return -M_SQRT1_2;
183   else if(v > 0.4) return 0.5;
184   else if(v < -0.4) return -0.5;
185   else return 0;
186 }
187
188 static void 
189 randomize(rubikblocks_conf *cp) 
190 {
191   int axis, side;
192   int i, j;
193   for(i = 0; i < SHUFFLE; i++)
194   {
195     axis = (random()%3)+1;
196     side = rnd01()*2-1;
197     flag_pieces(cp->pieces, axis, side);
198     for(j = 1; j < 4; j++)
199       cp->qfram[j] = 0;
200     cp->qfram[0] = M_SQRT1_2;
201     cp->qfram[axis] = M_SQRT1_2;
202     for(j = 0; j < 27; j++)
203     {
204       if(cp->pieces[j].act)
205         mult_quat(cp->qfram, cp->pieces[j].qr);
206     }
207   }
208 }
209
210 static void 
211 finish(rubikblocks_conf *cp) 
212 {
213   static int axis = 1;
214   int side, angle;
215   int i, j;
216   if(cp->pause)
217   {
218     switch(axis) 
219     {
220       case 1:
221         axis = rnd01()+2;
222         break;
223       case 2:
224         axis = 2*rnd01()+1;
225         break;
226       default:
227         axis = rnd01()+1;
228     }
229     side = rnd01()*2-1;
230     angle = rnd01()+1;
231     flag_pieces(cp->pieces, axis, side);
232     cp->pause = False;
233     cp->tmax = 90.0*angle;
234     for(i = 1; i < 4; i++)
235       cp->qfram[i] = 0;
236     cp->qfram[0] = cos(tspeed*M_PI/360);
237     cp->qfram[axis] = sin((rnd01()*2-1)*tspeed*M_PI/360);
238   }
239   else
240   {
241     for(i = 0; i < 27; i++)
242     {
243       for(j = 0; j < 4; j++)
244       {
245         cp->pieces[i].qr[j] = settle_value(cp->pieces[i].qr[j]);
246       }
247     }
248     cp->pause = True;
249     cp->tmax = twait;
250   }
251   cp->t = 0;
252 }
253
254 static Bool 
255 draw_main(ModeInfo *mi, rubikblocks_conf *cp) 
256 {
257   int i;
258   double x, y, z;
259
260   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
261   glLoadIdentity();
262   get_position(cp->rot, &x, &y, &z, !cp->button_down);
263   glTranslatef((x-0.5)*6, (y-0.5)*6, -20);
264
265   gltrackball_rotate(cp->trackball);
266
267   get_rotation(cp->rot, &x, &y, &z, !cp->button_down);
268   glRotatef(x*360, 1, 0, 0);
269   glRotatef(y*360, 0, 1, 0);
270   glRotatef(z*360, 0, 0, 1);
271   glScalef(size, size, size);
272
273   if(cp->wire) glColor3f(0.7, 0.7, 0.7);
274   if(!cp->pause)
275     for(i = 0; i < 27; i++)
276       if(cp->pieces[i].act)
277         mult_quat(cp->qfram, cp->pieces[i].qr);
278   for(i = 0; i < 27; i++) 
279   {
280     glPushMatrix();
281     if(fabs(cp->pieces[i].qr[0]) < 1)
282       glRotatef(360/M_PI*acos(cp->pieces[i].qr[0]),
283           cp->pieces[i].qr[1], cp->pieces[i].qr[2], cp->pieces[i].qr[3]);
284     glCallList(cp->list_base + i);
285     glPopMatrix();
286   }
287   if((cp->t += tspeed) > cp->tmax) finish(cp);
288   return True;
289 }
290
291 static void 
292 draw_horz_line(rubikblocks_conf *cp, int x1, int x2, int y) 
293 {
294   int x, y0 = y, w;
295   if(y < BORDER) y = -y;
296   else y = -BORDER;
297   for(; y < BORDER; y++) {
298     if(y0+y >= TEX_HEIGHT) break;
299     w = y*y*255/BORDER2;
300     for(x = x1; x <= x2; x++)
301       if(cp->texture[y0+y][x]>w) cp->texture[y0+y][x] = w;
302   }
303 }
304
305 static void 
306 draw_vert_line(rubikblocks_conf *cp, int x, int y1, int y2) 
307 {
308   int x0 = x, y, w;
309   if(x<BORDER) x = -x;
310   else x = -BORDER;
311   for(; x < BORDER; x++) {
312     if(x0+x >= TEX_WIDTH) break;
313     w = x*x*255/BORDER2;
314     for(y = y1; y <= y2; y++)
315       if(cp->texture[y][x0+x]>w) cp->texture[y][x0+x] = w;
316   }
317 }
318
319 static void 
320 make_texture(rubikblocks_conf *cp) 
321 {
322   int x, y;
323   for(y = 0; y < TEX_HEIGHT; y++)
324     for(x = 0; x < TEX_WIDTH; x++)
325       cp->texture[y][x] = 255;
326   draw_horz_line(cp, 0, TEX_WIDTH-1, 0);
327   draw_horz_line(cp, 0, TEX_WIDTH-1, TEX_HEIGHT-1);
328   draw_vert_line(cp, 0, 0, TEX_HEIGHT-1);
329   draw_vert_line(cp, TEX_WIDTH-1, 0, TEX_HEIGHT-1);
330 }
331
332 /* These simple transforms make the actual shape of the pieces. The parameters
333  * A, B and C affect the excentricity of the pieces in each direction. */
334 static float 
335 fx(float x)
336 {
337   const float A = 0.5;
338   if(x > 1.4) return 1.5 - A;
339   else if(x < -1.4) return -1.5 - A;
340   else return x;
341 }
342
343 static float 
344 fy(float y)
345 {
346   const float B = 0.25;
347   if(y > 1.4) return 1.5 - B;
348   else if(y < -1.4) return -1.5 - B;
349   else return y;
350 }
351
352 static float 
353 fz(float z)
354 {
355   const float C = 0.0;
356   if(z > 1.4) return 1.5 - C;
357   else if(z < -1.4) return -1.5 - C;
358   else return z;
359 }
360
361 static void 
362 init_lists(rubikblocks_conf *cp)
363 {
364   GLuint base;
365   int i;
366   float x, y, z;
367   base = cp->list_base = glGenLists(27);
368   for(i = 0; i < 27; i++)
369   {
370     x = cp->pieces[i].pos[0];
371     y = cp->pieces[i].pos[1];
372     z = cp->pieces[i].pos[2];
373     glNewList(base+i, GL_COMPILE);
374     glBegin(GL_QUAD_STRIP);
375     glNormal3f(1, 0, 0);
376     glTexCoord2f(0, 0);
377     glVertex3f(fx(x+0.5), fy(y-0.5), fz(z-0.5));
378     glTexCoord2f(0, 1);
379     glVertex3f(fx(x+0.5), fy(y+0.5), fz(z-0.5));
380     glTexCoord2f(1, 0);
381     glVertex3f(fx(x+0.5), fy(y-0.5), fz(z+0.5));
382     glTexCoord2f(1, 1);
383     glVertex3f(fx(x+0.5), fy(y+0.5), fz(z+0.5));
384     glNormal3f(0, 0, 1);
385     glTexCoord2f(0, 0);
386     glVertex3f(fx(x-0.5), fy(y-0.5), fz(z+0.5));
387     glTexCoord2f(0, 1);
388     glVertex3f(fx(x-0.5), fy(y+0.5), fz(z+0.5));
389     glNormal3f(-1, 0, 0);
390     glTexCoord2f(1, 0);
391     glVertex3f(fx(x-0.5), fy(y-0.5), fz(z-0.5));
392     glTexCoord2f(1, 1);
393     glVertex3f(fx(x-0.5), fy(y+0.5), fz(z-0.5));
394     glNormal3f(0, 0, -1);
395     glTexCoord2f(0, 0);
396     glVertex3f(fx(x+0.5), fy(y-0.5), fz(z-0.5));
397     glTexCoord2f(0, 1);
398     glVertex3f(fx(x+0.5), fy(y+0.5), fz(z-0.5));
399     glEnd();
400     glBegin(GL_QUADS);
401     glNormal3f(0, 1, 0);
402     glTexCoord2f(0, 0);
403     glVertex3f(fx(x+0.5), fy(y+0.5), fz(z+0.5));
404     glTexCoord2f(0, 1);
405     glVertex3f(fx(x+0.5), fy(y+0.5), fz(z-0.5));
406     glTexCoord2f(1, 1);
407     glVertex3f(fx(x-0.5), fy(y+0.5), fz(z-0.5));
408     glTexCoord2f(1, 0);
409     glVertex3f(fx(x-0.5), fy(y+0.5), fz(z+0.5));
410     glNormal3f(0, -1, 0);
411     glTexCoord2f(0, 0);
412     glVertex3f(fx(x+0.5), fy(y-0.5), fz(z-0.5));
413     glTexCoord2f(0, 1);
414     glVertex3f(fx(x+0.5), fy(y-0.5), fz(z+0.5));
415     glTexCoord2f(1, 1);
416     glVertex3f(fx(x-0.5), fy(y-0.5), fz(z+0.5));
417     glTexCoord2f(1, 0);
418     glVertex3f(fx(x-0.5), fy(y-0.5), fz(z-0.5));
419     glEnd();
420     glEndList();
421   }
422 }
423
424 /* It looks terrible... FIXME: any other ideas, maybe some anisotropic filtering? */
425 /*#define MIPMAP*/
426
427 static void 
428 init_gl(ModeInfo *mi) 
429 {
430   rubikblocks_conf *cp = &rubikblocks[MI_SCREEN(mi)];
431 #ifdef MIPMAP
432   int status;
433 #endif
434   cp->wire = MI_IS_WIREFRAME(mi);
435
436 # ifdef HAVE_JWZGLES /* #### glPolygonMode other than GL_FILL unimplemented */
437   cp->wire = 0;
438 # endif
439
440   if(MI_IS_MONO(mi))
441     tex = False;
442   if(cp->wire) {
443     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
444     return;
445   }
446
447   glClearDepth(1.0);
448   glDrawBuffer(GL_BACK);
449   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
450   glShadeModel(GL_FLAT);
451   glDepthFunc(GL_LESS);
452   glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
453   glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
454   glLightfv(GL_LIGHT0, GL_POSITION, position0);
455   glLightfv(GL_LIGHT1, GL_AMBIENT, ambient);
456   glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse);
457   glLightfv(GL_LIGHT1, GL_POSITION, position1);
458   glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
459   glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
460   glEnable(GL_DEPTH_TEST);
461   glEnable(GL_LIGHT0);
462   glEnable(GL_LIGHT1);
463   glEnable(GL_LIGHTING);
464   glEnable(GL_NORMALIZE);
465   glEnable(GL_COLOR_MATERIAL);
466   glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, material_ambient);
467   glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, material_diffuse);
468   glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, material_specular);
469   glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess);
470   if (!tex) return;
471   glEnable(GL_TEXTURE_2D);
472 #ifdef MIPMAP
473   clear_gl_error();
474   status = gluBuild2DMipmaps(GL_TEXTURE_2D, 1, TEX_WIDTH, TEX_HEIGHT,
475       GL_LUMINANCE, GL_UNSIGNED_BYTE, cp->texture);
476   if (status) {
477     const char *s = (char *)gluErrorString(status);
478     fprintf (stderr, "%s: error mipmapping texture: %s\n", progname, (s?s:"(unknown)"));
479     exit (1);
480   }
481   check_gl_error("mipmapping");
482 #else    
483   glTexImage2D(GL_TEXTURE_2D, 0, 1, TEX_WIDTH, TEX_HEIGHT,
484       0, GL_LUMINANCE, GL_UNSIGNED_BYTE, cp->texture);
485 #endif  
486   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
487   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
488   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
489   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
490 #ifdef MIPMAP
491   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
492 #else
493   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
494 #endif
495 }
496
497 static void 
498 init_cp(rubikblocks_conf *cp) 
499 {
500   int i, j, k, m;
501
502   cp->pause = True;
503   cp->t = 0.0;
504   cp->tmax = twait;
505
506   for(i = -1, m = 0; i <= 1; i++)
507     for(j = -1; j <= 1; j++)
508       for(k = -1; k <= 1; k++)
509       {
510         cp->pieces[m].pos[0] = k;
511         cp->pieces[m].pos[1] = j;
512         cp->pieces[m].pos[2] = i;
513         cp->pieces[m].qr[0] = 1;
514         cp->pieces[m].qr[1] = 0;
515         cp->pieces[m].qr[2] = 0;
516         cp->pieces[m].qr[3] = 0;
517         m++;
518       }
519
520   cp->rot = make_rotator(spin?spinspeed:0, spin?spinspeed:0, spin?spinspeed:0,
521       0.1, wander?wspeed:0, True);
522   cp->trackball = gltrackball_init(True);
523
524   if(rndstart) randomize(cp);
525 }
526
527 /*************************************************************************/
528
529 ENTRYPOINT void 
530 reshape_rubikblocks(ModeInfo *mi, int width, int height) 
531 {
532   rubikblocks_conf *cp = &rubikblocks[MI_SCREEN(mi)];
533   if(!height) height = 1;
534   cp->ratio = (GLfloat)width/(GLfloat)height;
535   glViewport(0, 0, (GLint) width, (GLint) height);
536   glMatrixMode(GL_PROJECTION);
537   glLoadIdentity();
538   gluPerspective(30.0, cp->ratio, 1.0, 100.0);
539   glMatrixMode(GL_MODELVIEW);
540   glClear(GL_COLOR_BUFFER_BIT);
541 }
542
543 ENTRYPOINT void 
544 release_rubikblocks(ModeInfo *mi) 
545 {
546   if (rubikblocks != NULL) 
547   {
548     int screen;
549     for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) 
550     {
551       rubikblocks_conf *cp = &rubikblocks[screen];
552       if (cp->glx_context) {
553         cp->glx_context = NULL;
554       }
555     }
556     free((void *)rubikblocks);
557     rubikblocks = NULL;
558   }
559   FreeAllGL(mi);
560 }
561
562 ENTRYPOINT void 
563 init_rubikblocks(ModeInfo *mi) 
564 {
565   rubikblocks_conf *cp;
566   if(!rubikblocks) 
567   {
568     rubikblocks = (rubikblocks_conf *)calloc(MI_NUM_SCREENS(mi), sizeof(rubikblocks_conf));
569     if(!rubikblocks) return;
570   }
571   cp = &rubikblocks[MI_SCREEN(mi)];
572
573   if(tex)
574     make_texture(cp);
575
576   if ((cp->glx_context = init_GL(mi)) != NULL) 
577   {
578     init_gl(mi);
579     init_cp(cp);
580     init_lists(cp);
581     reshape_rubikblocks(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
582   }
583   else 
584   {
585     MI_CLEARWINDOW(mi);
586   }
587 }
588
589 ENTRYPOINT void 
590 draw_rubikblocks(ModeInfo * mi) 
591 {
592   Display *display = MI_DISPLAY(mi);
593   Window window = MI_WINDOW(mi);
594   rubikblocks_conf *cp;
595   if (!rubikblocks) return;
596   cp = &rubikblocks[MI_SCREEN(mi)];
597   MI_IS_DRAWN(mi) = True;
598   if (!cp->glx_context) return;
599   mi->polygon_count = 0;
600   glXMakeCurrent(display, window, *(cp->glx_context));
601   if (!draw_main(mi, cp)) 
602   {
603     release_rubikblocks(mi);
604     return;
605   }
606   if (MI_IS_FPS(mi)) do_fps (mi);
607   glFlush();
608   glXSwapBuffers(display, window);
609 }
610
611 #ifndef STANDALONE
612 ENTRYPOINT void 
613 change_rubikblocks(ModeInfo * mi) 
614 {
615   rubikblocks_conf *cp = &rubikblocks[MI_SCREEN(mi)];
616   if (!cp->glx_context) return;
617   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(cp->glx_context));
618   init_gl(mi);
619 }
620 #endif /* !STANDALONE */
621
622 ENTRYPOINT Bool
623 rubikblocks_handle_event (ModeInfo *mi, XEvent *event)
624 {
625   rubikblocks_conf *cp = &rubikblocks[MI_SCREEN(mi)];
626
627   if (gltrackball_event_handler (event, cp->trackball,
628                                  MI_WIDTH (mi), MI_HEIGHT (mi),
629                                  &cp->button_down))
630     return True;
631
632   return False;
633 }
634
635
636 XSCREENSAVER_MODULE ("RubikBlocks", rubikblocks)
637
638 #endif