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