54c4134137361bd820bc615942b3d790697cd686
[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 static const GLfloat zpos = -18.0;
132
133 /*************************************************************************/
134
135 /* Multiplies two quaternions, src*dest, and stores the result in dest. */
136 static void
137 mult_quat(float src[4], float dest[4])
138 {
139   float r, i, j, k;
140   r = src[0]*dest[0] - src[1]*dest[1] - src[2]*dest[2] - src[3]*dest[3];
141   i = src[0]*dest[1] + src[1]*dest[0] + src[2]*dest[3] - src[3]*dest[2];
142   j = src[0]*dest[2] + src[2]*dest[0] + src[3]*dest[1] - src[1]*dest[3];
143   k = src[0]*dest[3] + src[3]*dest[0] + src[1]*dest[2] - src[2]*dest[1];
144   dest[0] = r;
145   dest[1] = i;
146   dest[2] = j;
147   dest[3] = k;
148 }
149
150 /* Sets the 'act' flag for pieces which will undergo the rotation. */
151 static void
152 flag_pieces(piece_t pieces[27], int axis, int side)
153 {
154   int i, j;
155   float q[4];
156   for(i = 0; i < 27; i++)
157   {
158     q[0] = 0;
159     q[1] = pieces[i].pos[0];
160     q[2] = pieces[i].pos[1];
161     q[3] = pieces[i].pos[2];
162     mult_quat(pieces[i].qr, q);
163     for(j = 1; j < 4; j++)
164       q[j] = -q[j];
165     mult_quat(pieces[i].qr, q);
166     for(j = 1; j < 4; j++)
167       q[j] = -q[j];
168     if(fabs(q[axis] - side) < 0.1)
169       pieces[i].act = True;
170     else
171       pieces[i].act = False;
172   }
173 }
174
175 /* "Rounds" the value to the nearest from the set {0, +-1/2, +-1/sqrt(2), +-1}.
176  * It is guaranteed to be pretty close to one when this function is called. */
177 static float 
178 settle_value(float v) 
179 {
180   if(v > 0.9) return 1;
181   else if(v < -0.9) return -1;
182   else if(v > 0.6) return M_SQRT1_2;
183   else if(v < -0.6) return -M_SQRT1_2;
184   else if(v > 0.4) return 0.5;
185   else if(v < -0.4) return -0.5;
186   else return 0;
187 }
188
189 static void 
190 randomize(rubikblocks_conf *cp) 
191 {
192   int axis, side;
193   int i, j;
194   for(i = 0; i < SHUFFLE; i++)
195   {
196     axis = (random()%3)+1;
197     side = rnd01()*2-1;
198     flag_pieces(cp->pieces, axis, side);
199     for(j = 1; j < 4; j++)
200       cp->qfram[j] = 0;
201     cp->qfram[0] = M_SQRT1_2;
202     cp->qfram[axis] = M_SQRT1_2;
203     for(j = 0; j < 27; j++)
204     {
205       if(cp->pieces[j].act)
206         mult_quat(cp->qfram, cp->pieces[j].qr);
207     }
208   }
209 }
210
211 static void 
212 finish(rubikblocks_conf *cp) 
213 {
214   static int axis = 1;
215   int side, angle;
216   int i, j;
217   if(cp->pause)
218   {
219     switch(axis) 
220     {
221       case 1:
222         axis = rnd01()+2;
223         break;
224       case 2:
225         axis = 2*rnd01()+1;
226         break;
227       default:
228         axis = rnd01()+1;
229     }
230     side = rnd01()*2-1;
231     angle = rnd01()+1;
232     flag_pieces(cp->pieces, axis, side);
233     cp->pause = False;
234     cp->tmax = 90.0*angle;
235     for(i = 1; i < 4; i++)
236       cp->qfram[i] = 0;
237     cp->qfram[0] = cos(tspeed*M_PI/360);
238     cp->qfram[axis] = sin((rnd01()*2-1)*tspeed*M_PI/360);
239   }
240   else
241   {
242     for(i = 0; i < 27; i++)
243     {
244       for(j = 0; j < 4; j++)
245       {
246         cp->pieces[i].qr[j] = settle_value(cp->pieces[i].qr[j]);
247       }
248     }
249     cp->pause = True;
250     cp->tmax = twait;
251   }
252   cp->t = 0;
253 }
254
255 static Bool 
256 draw_main(ModeInfo *mi, rubikblocks_conf *cp) 
257 {
258   int i;
259   double x, y, z;
260
261   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
262   glLoadIdentity();
263   get_position(cp->rot, &x, &y, &z, !cp->button_down);
264   glTranslatef((x-0.5)*6, (y-0.5)*6, -20);
265
266   /* Do it twice because we don't track the device's orientation. */
267   glRotatef( current_device_rotation(), 0, 0, 1);
268   gltrackball_rotate(cp->trackball);
269   glRotatef(-current_device_rotation(), 0, 0, 1);
270
271   get_rotation(cp->rot, &x, &y, &z, !cp->button_down);
272   glRotatef(x*360, 1, 0, 0);
273   glRotatef(y*360, 0, 1, 0);
274   glRotatef(z*360, 0, 0, 1);
275   glScalef(size, size, size);
276
277   if(cp->wire) glColor3f(0.7, 0.7, 0.7);
278   if(!cp->pause)
279     for(i = 0; i < 27; i++)
280       if(cp->pieces[i].act)
281         mult_quat(cp->qfram, cp->pieces[i].qr);
282   for(i = 0; i < 27; i++) 
283   {
284     glPushMatrix();
285     if(fabs(cp->pieces[i].qr[0]) < 1)
286       glRotatef(360/M_PI*acos(cp->pieces[i].qr[0]),
287           cp->pieces[i].qr[1], cp->pieces[i].qr[2], cp->pieces[i].qr[3]);
288     glCallList(cp->list_base + i);
289     glPopMatrix();
290   }
291   if((cp->t += tspeed) > cp->tmax) finish(cp);
292   return True;
293 }
294
295 static void 
296 draw_horz_line(rubikblocks_conf *cp, int x1, int x2, int y) 
297 {
298   int x, y0 = y, w;
299   if(y < BORDER) y = -y;
300   else y = -BORDER;
301   for(; y < BORDER; y++) {
302     if(y0+y >= TEX_HEIGHT) break;
303     w = y*y*255/BORDER2;
304     for(x = x1; x <= x2; x++)
305       if(cp->texture[y0+y][x]>w) cp->texture[y0+y][x] = w;
306   }
307 }
308
309 static void 
310 draw_vert_line(rubikblocks_conf *cp, int x, int y1, int y2) 
311 {
312   int x0 = x, y, w;
313   if(x<BORDER) x = -x;
314   else x = -BORDER;
315   for(; x < BORDER; x++) {
316     if(x0+x >= TEX_WIDTH) break;
317     w = x*x*255/BORDER2;
318     for(y = y1; y <= y2; y++)
319       if(cp->texture[y][x0+x]>w) cp->texture[y][x0+x] = w;
320   }
321 }
322
323 static void 
324 make_texture(rubikblocks_conf *cp) 
325 {
326   int x, y;
327   for(y = 0; y < TEX_HEIGHT; y++)
328     for(x = 0; x < TEX_WIDTH; x++)
329       cp->texture[y][x] = 255;
330   draw_horz_line(cp, 0, TEX_WIDTH-1, 0);
331   draw_horz_line(cp, 0, TEX_WIDTH-1, TEX_HEIGHT-1);
332   draw_vert_line(cp, 0, 0, TEX_HEIGHT-1);
333   draw_vert_line(cp, TEX_WIDTH-1, 0, TEX_HEIGHT-1);
334 }
335
336 /* These simple transforms make the actual shape of the pieces. The parameters
337  * A, B and C affect the excentricity of the pieces in each direction. */
338 static float 
339 fx(float x)
340 {
341   const float A = 0.5;
342   if(x > 1.4) return 1.5 - A;
343   else if(x < -1.4) return -1.5 - A;
344   else return x;
345 }
346
347 static float 
348 fy(float y)
349 {
350   const float B = 0.25;
351   if(y > 1.4) return 1.5 - B;
352   else if(y < -1.4) return -1.5 - B;
353   else return y;
354 }
355
356 static float 
357 fz(float z)
358 {
359   const float C = 0.0;
360   if(z > 1.4) return 1.5 - C;
361   else if(z < -1.4) return -1.5 - C;
362   else return z;
363 }
364
365 static void 
366 init_lists(rubikblocks_conf *cp)
367 {
368   GLuint base;
369   int i;
370   float x, y, z;
371   base = cp->list_base = glGenLists(27);
372   for(i = 0; i < 27; i++)
373   {
374     x = cp->pieces[i].pos[0];
375     y = cp->pieces[i].pos[1];
376     z = cp->pieces[i].pos[2];
377     glNewList(base+i, GL_COMPILE);
378     glBegin(GL_QUAD_STRIP);
379     glNormal3f(1, 0, 0);
380     glTexCoord2f(0, 0);
381     glVertex3f(fx(x+0.5), fy(y-0.5), fz(z-0.5));
382     glTexCoord2f(0, 1);
383     glVertex3f(fx(x+0.5), fy(y+0.5), fz(z-0.5));
384     glTexCoord2f(1, 0);
385     glVertex3f(fx(x+0.5), fy(y-0.5), fz(z+0.5));
386     glTexCoord2f(1, 1);
387     glVertex3f(fx(x+0.5), fy(y+0.5), fz(z+0.5));
388     glNormal3f(0, 0, 1);
389     glTexCoord2f(0, 0);
390     glVertex3f(fx(x-0.5), fy(y-0.5), fz(z+0.5));
391     glTexCoord2f(0, 1);
392     glVertex3f(fx(x-0.5), fy(y+0.5), fz(z+0.5));
393     glNormal3f(-1, 0, 0);
394     glTexCoord2f(1, 0);
395     glVertex3f(fx(x-0.5), fy(y-0.5), fz(z-0.5));
396     glTexCoord2f(1, 1);
397     glVertex3f(fx(x-0.5), fy(y+0.5), fz(z-0.5));
398     glNormal3f(0, 0, -1);
399     glTexCoord2f(0, 0);
400     glVertex3f(fx(x+0.5), fy(y-0.5), fz(z-0.5));
401     glTexCoord2f(0, 1);
402     glVertex3f(fx(x+0.5), fy(y+0.5), fz(z-0.5));
403     glEnd();
404     glBegin(GL_QUADS);
405     glNormal3f(0, 1, 0);
406     glTexCoord2f(0, 0);
407     glVertex3f(fx(x+0.5), fy(y+0.5), fz(z+0.5));
408     glTexCoord2f(0, 1);
409     glVertex3f(fx(x+0.5), fy(y+0.5), fz(z-0.5));
410     glTexCoord2f(1, 1);
411     glVertex3f(fx(x-0.5), fy(y+0.5), fz(z-0.5));
412     glTexCoord2f(1, 0);
413     glVertex3f(fx(x-0.5), fy(y+0.5), fz(z+0.5));
414     glNormal3f(0, -1, 0);
415     glTexCoord2f(0, 0);
416     glVertex3f(fx(x+0.5), fy(y-0.5), fz(z-0.5));
417     glTexCoord2f(0, 1);
418     glVertex3f(fx(x+0.5), fy(y-0.5), fz(z+0.5));
419     glTexCoord2f(1, 1);
420     glVertex3f(fx(x-0.5), fy(y-0.5), fz(z+0.5));
421     glTexCoord2f(1, 0);
422     glVertex3f(fx(x-0.5), fy(y-0.5), fz(z-0.5));
423     glEnd();
424     glEndList();
425   }
426 }
427
428 /* It looks terrible... FIXME: any other ideas, maybe some anisotropic filtering? */
429 /*#define MIPMAP*/
430
431 static void 
432 init_gl(ModeInfo *mi) 
433 {
434   rubikblocks_conf *cp = &rubikblocks[MI_SCREEN(mi)];
435 #ifdef MIPMAP
436   int status;
437 #endif
438   cp->wire = MI_IS_WIREFRAME(mi);
439
440 # ifdef HAVE_JWZGLES /* #### glPolygonMode other than GL_FILL unimplemented */
441   cp->wire = 0;
442 # endif
443
444   if(MI_IS_MONO(mi))
445     tex = False;
446   if(cp->wire) {
447     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
448     return;
449   }
450
451   glClearDepth(1.0);
452   glDrawBuffer(GL_BACK);
453   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
454   glShadeModel(GL_FLAT);
455   glDepthFunc(GL_LESS);
456   glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
457   glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
458   glLightfv(GL_LIGHT0, GL_POSITION, position0);
459   glLightfv(GL_LIGHT1, GL_AMBIENT, ambient);
460   glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse);
461   glLightfv(GL_LIGHT1, GL_POSITION, position1);
462   glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
463   glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
464   glEnable(GL_DEPTH_TEST);
465   glEnable(GL_LIGHT0);
466   glEnable(GL_LIGHT1);
467   glEnable(GL_LIGHTING);
468   glEnable(GL_NORMALIZE);
469   glEnable(GL_COLOR_MATERIAL);
470   glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, material_ambient);
471   glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, material_diffuse);
472   glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, material_specular);
473   glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess);
474   if (!tex) return;
475   glEnable(GL_TEXTURE_2D);
476 #ifdef MIPMAP
477   clear_gl_error();
478   status = gluBuild2DMipmaps(GL_TEXTURE_2D, 1, TEX_WIDTH, TEX_HEIGHT,
479       GL_LUMINANCE, GL_UNSIGNED_BYTE, cp->texture);
480   if (status) {
481     const char *s = (char *)gluErrorString(status);
482     fprintf (stderr, "%s: error mipmapping texture: %s\n", progname, (s?s:"(unknown)"));
483     exit (1);
484   }
485   check_gl_error("mipmapping");
486 #else    
487   glTexImage2D(GL_TEXTURE_2D, 0, 1, TEX_WIDTH, TEX_HEIGHT,
488       0, GL_LUMINANCE, GL_UNSIGNED_BYTE, cp->texture);
489 #endif  
490   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
491   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
492   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
493   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
494 #ifdef MIPMAP
495   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
496 #else
497   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
498 #endif
499 }
500
501 static void 
502 init_cp(rubikblocks_conf *cp) 
503 {
504   int i, j, k, m;
505
506   cp->pause = True;
507   cp->t = 0.0;
508   cp->tmax = twait;
509
510   for(i = -1, m = 0; i <= 1; i++)
511     for(j = -1; j <= 1; j++)
512       for(k = -1; k <= 1; k++)
513       {
514         cp->pieces[m].pos[0] = k;
515         cp->pieces[m].pos[1] = j;
516         cp->pieces[m].pos[2] = i;
517         cp->pieces[m].qr[0] = 1;
518         cp->pieces[m].qr[1] = 0;
519         cp->pieces[m].qr[2] = 0;
520         cp->pieces[m].qr[3] = 0;
521         m++;
522       }
523
524   cp->rot = make_rotator(spin?spinspeed:0, spin?spinspeed:0, spin?spinspeed:0,
525       0.1, wander?wspeed:0, True);
526   cp->trackball = gltrackball_init();
527
528   if(rndstart) randomize(cp);
529 }
530
531 /*************************************************************************/
532
533 ENTRYPOINT void 
534 reshape_rubikblocks(ModeInfo *mi, int width, int height) 
535 {
536   rubikblocks_conf *cp = &rubikblocks[MI_SCREEN(mi)];
537   if(!height) height = 1;
538   cp->ratio = (GLfloat)width/(GLfloat)height;
539   glViewport(0, 0, (GLint) width, (GLint) height);
540   glMatrixMode(GL_PROJECTION);
541   glLoadIdentity();
542   gluPerspective(30.0, cp->ratio, 1.0, 100.0);
543   glMatrixMode(GL_MODELVIEW);
544   glClear(GL_COLOR_BUFFER_BIT);
545 }
546
547 ENTRYPOINT void 
548 release_rubikblocks(ModeInfo *mi) 
549 {
550   if (rubikblocks != NULL) 
551   {
552     int screen;
553     for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) 
554     {
555       rubikblocks_conf *cp = &rubikblocks[screen];
556       if (cp->glx_context) {
557         cp->glx_context = NULL;
558       }
559     }
560     free((void *)rubikblocks);
561     rubikblocks = NULL;
562   }
563   FreeAllGL(mi);
564 }
565
566 ENTRYPOINT void 
567 init_rubikblocks(ModeInfo *mi) 
568 {
569   rubikblocks_conf *cp;
570   if(!rubikblocks) 
571   {
572     rubikblocks = (rubikblocks_conf *)calloc(MI_NUM_SCREENS(mi), sizeof(rubikblocks_conf));
573     if(!rubikblocks) return;
574   }
575   cp = &rubikblocks[MI_SCREEN(mi)];
576
577   if(tex)
578     make_texture(cp);
579
580   if ((cp->glx_context = init_GL(mi)) != NULL) 
581   {
582     init_gl(mi);
583     init_cp(cp);
584     init_lists(cp);
585     reshape_rubikblocks(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
586   }
587   else 
588   {
589     MI_CLEARWINDOW(mi);
590   }
591 }
592
593 ENTRYPOINT void 
594 draw_rubikblocks(ModeInfo * mi) 
595 {
596   Display *display = MI_DISPLAY(mi);
597   Window window = MI_WINDOW(mi);
598   rubikblocks_conf *cp;
599   if (!rubikblocks) return;
600   cp = &rubikblocks[MI_SCREEN(mi)];
601   MI_IS_DRAWN(mi) = True;
602   if (!cp->glx_context) return;
603   mi->polygon_count = 0;
604   glXMakeCurrent(display, window, *(cp->glx_context));
605   if (!draw_main(mi, cp)) 
606   {
607     release_rubikblocks(mi);
608     return;
609   }
610   if (MI_IS_FPS(mi)) do_fps (mi);
611   glFlush();
612   glXSwapBuffers(display, window);
613 }
614
615 #ifndef STANDALONE
616 ENTRYPOINT void 
617 change_rubikblocks(ModeInfo * mi) 
618 {
619   rubikblocks_conf *cp = &rubikblocks[MI_SCREEN(mi)];
620   if (!cp->glx_context) return;
621   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(cp->glx_context));
622   init_gl(mi);
623 }
624 #endif /* !STANDALONE */
625
626 ENTRYPOINT Bool
627 rubikblocks_handle_event (ModeInfo *mi, XEvent *event)
628 {
629   rubikblocks_conf *cp = &rubikblocks[MI_SCREEN(mi)];
630   if(event->xany.type == ButtonPress && event->xbutton.button == Button1)
631   {
632     cp->button_down = True;
633     gltrackball_start(cp->trackball, event->xbutton.x, event->xbutton.y,
634         MI_WIDTH(mi), MI_HEIGHT(mi));
635     return True;
636   }
637   else if(event->xany.type == ButtonRelease && event->xbutton.button == Button1)
638   {
639     cp->button_down = False;
640     return True;
641   }
642   else if(event->xany.type == ButtonPress &&
643       (event->xbutton.button == Button4 || event->xbutton.button == Button5 ||
644        event->xbutton.button == Button6 || event->xbutton.button == Button7))
645   {
646     gltrackball_mousewheel(cp->trackball,
647         event->xbutton.button, 5, !!event->xbutton.state);
648     return True;
649   }
650   else if(event->xany.type == MotionNotify && cp->button_down)
651   {
652     gltrackball_track(cp->trackball, event->xmotion.x, event->xmotion.y,
653         MI_WIDTH (mi), MI_HEIGHT (mi));
654     return True;
655   }
656   return False;
657 }
658
659
660 XSCREENSAVER_MODULE ("RubikBlocks", rubikblocks)
661
662 #endif