From http://www.jwz.org/xscreensaver/xscreensaver-5.27.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   /* Do it twice because we don't track the device's orientation. */
266   glRotatef( current_device_rotation(), 0, 0, 1);
267   gltrackball_rotate(cp->trackball);
268   glRotatef(-current_device_rotation(), 0, 0, 1);
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   if(cp->wire) glColor3f(0.7, 0.7, 0.7);
277   if(!cp->pause)
278     for(i = 0; i < 27; i++)
279       if(cp->pieces[i].act)
280         mult_quat(cp->qfram, cp->pieces[i].qr);
281   for(i = 0; i < 27; i++) 
282   {
283     glPushMatrix();
284     if(fabs(cp->pieces[i].qr[0]) < 1)
285       glRotatef(360/M_PI*acos(cp->pieces[i].qr[0]),
286           cp->pieces[i].qr[1], cp->pieces[i].qr[2], cp->pieces[i].qr[3]);
287     glCallList(cp->list_base + i);
288     glPopMatrix();
289   }
290   if((cp->t += tspeed) > cp->tmax) finish(cp);
291   return True;
292 }
293
294 static void 
295 draw_horz_line(rubikblocks_conf *cp, int x1, int x2, int y) 
296 {
297   int x, y0 = y, w;
298   if(y < BORDER) y = -y;
299   else y = -BORDER;
300   for(; y < BORDER; y++) {
301     if(y0+y >= TEX_HEIGHT) break;
302     w = y*y*255/BORDER2;
303     for(x = x1; x <= x2; x++)
304       if(cp->texture[y0+y][x]>w) cp->texture[y0+y][x] = w;
305   }
306 }
307
308 static void 
309 draw_vert_line(rubikblocks_conf *cp, int x, int y1, int y2) 
310 {
311   int x0 = x, y, w;
312   if(x<BORDER) x = -x;
313   else x = -BORDER;
314   for(; x < BORDER; x++) {
315     if(x0+x >= TEX_WIDTH) break;
316     w = x*x*255/BORDER2;
317     for(y = y1; y <= y2; y++)
318       if(cp->texture[y][x0+x]>w) cp->texture[y][x0+x] = w;
319   }
320 }
321
322 static void 
323 make_texture(rubikblocks_conf *cp) 
324 {
325   int x, y;
326   for(y = 0; y < TEX_HEIGHT; y++)
327     for(x = 0; x < TEX_WIDTH; x++)
328       cp->texture[y][x] = 255;
329   draw_horz_line(cp, 0, TEX_WIDTH-1, 0);
330   draw_horz_line(cp, 0, TEX_WIDTH-1, TEX_HEIGHT-1);
331   draw_vert_line(cp, 0, 0, TEX_HEIGHT-1);
332   draw_vert_line(cp, TEX_WIDTH-1, 0, TEX_HEIGHT-1);
333 }
334
335 /* These simple transforms make the actual shape of the pieces. The parameters
336  * A, B and C affect the excentricity of the pieces in each direction. */
337 static float 
338 fx(float x)
339 {
340   const float A = 0.5;
341   if(x > 1.4) return 1.5 - A;
342   else if(x < -1.4) return -1.5 - A;
343   else return x;
344 }
345
346 static float 
347 fy(float y)
348 {
349   const float B = 0.25;
350   if(y > 1.4) return 1.5 - B;
351   else if(y < -1.4) return -1.5 - B;
352   else return y;
353 }
354
355 static float 
356 fz(float z)
357 {
358   const float C = 0.0;
359   if(z > 1.4) return 1.5 - C;
360   else if(z < -1.4) return -1.5 - C;
361   else return z;
362 }
363
364 static void 
365 init_lists(rubikblocks_conf *cp)
366 {
367   GLuint base;
368   int i;
369   float x, y, z;
370   base = cp->list_base = glGenLists(27);
371   for(i = 0; i < 27; i++)
372   {
373     x = cp->pieces[i].pos[0];
374     y = cp->pieces[i].pos[1];
375     z = cp->pieces[i].pos[2];
376     glNewList(base+i, GL_COMPILE);
377     glBegin(GL_QUAD_STRIP);
378     glNormal3f(1, 0, 0);
379     glTexCoord2f(0, 0);
380     glVertex3f(fx(x+0.5), fy(y-0.5), fz(z-0.5));
381     glTexCoord2f(0, 1);
382     glVertex3f(fx(x+0.5), fy(y+0.5), fz(z-0.5));
383     glTexCoord2f(1, 0);
384     glVertex3f(fx(x+0.5), fy(y-0.5), fz(z+0.5));
385     glTexCoord2f(1, 1);
386     glVertex3f(fx(x+0.5), fy(y+0.5), fz(z+0.5));
387     glNormal3f(0, 0, 1);
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     glNormal3f(-1, 0, 0);
393     glTexCoord2f(1, 0);
394     glVertex3f(fx(x-0.5), fy(y-0.5), fz(z-0.5));
395     glTexCoord2f(1, 1);
396     glVertex3f(fx(x-0.5), fy(y+0.5), fz(z-0.5));
397     glNormal3f(0, 0, -1);
398     glTexCoord2f(0, 0);
399     glVertex3f(fx(x+0.5), fy(y-0.5), fz(z-0.5));
400     glTexCoord2f(0, 1);
401     glVertex3f(fx(x+0.5), fy(y+0.5), fz(z-0.5));
402     glEnd();
403     glBegin(GL_QUADS);
404     glNormal3f(0, 1, 0);
405     glTexCoord2f(0, 0);
406     glVertex3f(fx(x+0.5), fy(y+0.5), fz(z+0.5));
407     glTexCoord2f(0, 1);
408     glVertex3f(fx(x+0.5), fy(y+0.5), fz(z-0.5));
409     glTexCoord2f(1, 1);
410     glVertex3f(fx(x-0.5), fy(y+0.5), fz(z-0.5));
411     glTexCoord2f(1, 0);
412     glVertex3f(fx(x-0.5), fy(y+0.5), fz(z+0.5));
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     glEnd();
423     glEndList();
424   }
425 }
426
427 /* It looks terrible... FIXME: any other ideas, maybe some anisotropic filtering? */
428 /*#define MIPMAP*/
429
430 static void 
431 init_gl(ModeInfo *mi) 
432 {
433   rubikblocks_conf *cp = &rubikblocks[MI_SCREEN(mi)];
434 #ifdef MIPMAP
435   int status;
436 #endif
437   cp->wire = MI_IS_WIREFRAME(mi);
438
439 # ifdef HAVE_JWZGLES /* #### glPolygonMode other than GL_FILL unimplemented */
440   cp->wire = 0;
441 # endif
442
443   if(MI_IS_MONO(mi))
444     tex = False;
445   if(cp->wire) {
446     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
447     return;
448   }
449
450   glClearDepth(1.0);
451   glDrawBuffer(GL_BACK);
452   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
453   glShadeModel(GL_FLAT);
454   glDepthFunc(GL_LESS);
455   glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
456   glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
457   glLightfv(GL_LIGHT0, GL_POSITION, position0);
458   glLightfv(GL_LIGHT1, GL_AMBIENT, ambient);
459   glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse);
460   glLightfv(GL_LIGHT1, GL_POSITION, position1);
461   glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
462   glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
463   glEnable(GL_DEPTH_TEST);
464   glEnable(GL_LIGHT0);
465   glEnable(GL_LIGHT1);
466   glEnable(GL_LIGHTING);
467   glEnable(GL_NORMALIZE);
468   glEnable(GL_COLOR_MATERIAL);
469   glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, material_ambient);
470   glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, material_diffuse);
471   glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, material_specular);
472   glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess);
473   if (!tex) return;
474   glEnable(GL_TEXTURE_2D);
475 #ifdef MIPMAP
476   clear_gl_error();
477   status = gluBuild2DMipmaps(GL_TEXTURE_2D, 1, TEX_WIDTH, TEX_HEIGHT,
478       GL_LUMINANCE, GL_UNSIGNED_BYTE, cp->texture);
479   if (status) {
480     const char *s = (char *)gluErrorString(status);
481     fprintf (stderr, "%s: error mipmapping texture: %s\n", progname, (s?s:"(unknown)"));
482     exit (1);
483   }
484   check_gl_error("mipmapping");
485 #else    
486   glTexImage2D(GL_TEXTURE_2D, 0, 1, TEX_WIDTH, TEX_HEIGHT,
487       0, GL_LUMINANCE, GL_UNSIGNED_BYTE, cp->texture);
488 #endif  
489   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
490   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
491   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
492   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
493 #ifdef MIPMAP
494   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
495 #else
496   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
497 #endif
498 }
499
500 static void 
501 init_cp(rubikblocks_conf *cp) 
502 {
503   int i, j, k, m;
504
505   cp->pause = True;
506   cp->t = 0.0;
507   cp->tmax = twait;
508
509   for(i = -1, m = 0; i <= 1; i++)
510     for(j = -1; j <= 1; j++)
511       for(k = -1; k <= 1; k++)
512       {
513         cp->pieces[m].pos[0] = k;
514         cp->pieces[m].pos[1] = j;
515         cp->pieces[m].pos[2] = i;
516         cp->pieces[m].qr[0] = 1;
517         cp->pieces[m].qr[1] = 0;
518         cp->pieces[m].qr[2] = 0;
519         cp->pieces[m].qr[3] = 0;
520         m++;
521       }
522
523   cp->rot = make_rotator(spin?spinspeed:0, spin?spinspeed:0, spin?spinspeed:0,
524       0.1, wander?wspeed:0, True);
525   cp->trackball = gltrackball_init();
526
527   if(rndstart) randomize(cp);
528 }
529
530 /*************************************************************************/
531
532 ENTRYPOINT void 
533 reshape_rubikblocks(ModeInfo *mi, int width, int height) 
534 {
535   rubikblocks_conf *cp = &rubikblocks[MI_SCREEN(mi)];
536   if(!height) height = 1;
537   cp->ratio = (GLfloat)width/(GLfloat)height;
538   glViewport(0, 0, (GLint) width, (GLint) height);
539   glMatrixMode(GL_PROJECTION);
540   glLoadIdentity();
541   gluPerspective(30.0, cp->ratio, 1.0, 100.0);
542   glMatrixMode(GL_MODELVIEW);
543   glClear(GL_COLOR_BUFFER_BIT);
544 }
545
546 ENTRYPOINT void 
547 release_rubikblocks(ModeInfo *mi) 
548 {
549   if (rubikblocks != NULL) 
550   {
551     int screen;
552     for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) 
553     {
554       rubikblocks_conf *cp = &rubikblocks[screen];
555       if (cp->glx_context) {
556         cp->glx_context = NULL;
557       }
558     }
559     free((void *)rubikblocks);
560     rubikblocks = NULL;
561   }
562   FreeAllGL(mi);
563 }
564
565 ENTRYPOINT void 
566 init_rubikblocks(ModeInfo *mi) 
567 {
568   rubikblocks_conf *cp;
569   if(!rubikblocks) 
570   {
571     rubikblocks = (rubikblocks_conf *)calloc(MI_NUM_SCREENS(mi), sizeof(rubikblocks_conf));
572     if(!rubikblocks) return;
573   }
574   cp = &rubikblocks[MI_SCREEN(mi)];
575
576   if(tex)
577     make_texture(cp);
578
579   if ((cp->glx_context = init_GL(mi)) != NULL) 
580   {
581     init_gl(mi);
582     init_cp(cp);
583     init_lists(cp);
584     reshape_rubikblocks(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
585   }
586   else 
587   {
588     MI_CLEARWINDOW(mi);
589   }
590 }
591
592 ENTRYPOINT void 
593 draw_rubikblocks(ModeInfo * mi) 
594 {
595   Display *display = MI_DISPLAY(mi);
596   Window window = MI_WINDOW(mi);
597   rubikblocks_conf *cp;
598   if (!rubikblocks) return;
599   cp = &rubikblocks[MI_SCREEN(mi)];
600   MI_IS_DRAWN(mi) = True;
601   if (!cp->glx_context) return;
602   mi->polygon_count = 0;
603   glXMakeCurrent(display, window, *(cp->glx_context));
604   if (!draw_main(mi, cp)) 
605   {
606     release_rubikblocks(mi);
607     return;
608   }
609   if (MI_IS_FPS(mi)) do_fps (mi);
610   glFlush();
611   glXSwapBuffers(display, window);
612 }
613
614 #ifndef STANDALONE
615 ENTRYPOINT void 
616 change_rubikblocks(ModeInfo * mi) 
617 {
618   rubikblocks_conf *cp = &rubikblocks[MI_SCREEN(mi)];
619   if (!cp->glx_context) return;
620   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(cp->glx_context));
621   init_gl(mi);
622 }
623 #endif /* !STANDALONE */
624
625 ENTRYPOINT Bool
626 rubikblocks_handle_event (ModeInfo *mi, XEvent *event)
627 {
628   rubikblocks_conf *cp = &rubikblocks[MI_SCREEN(mi)];
629   if(event->xany.type == ButtonPress && event->xbutton.button == Button1)
630   {
631     cp->button_down = True;
632     gltrackball_start(cp->trackball, event->xbutton.x, event->xbutton.y,
633         MI_WIDTH(mi), MI_HEIGHT(mi));
634     return True;
635   }
636   else if(event->xany.type == ButtonRelease && event->xbutton.button == Button1)
637   {
638     cp->button_down = False;
639     return True;
640   }
641   else if(event->xany.type == ButtonPress &&
642       (event->xbutton.button == Button4 || event->xbutton.button == Button5 ||
643        event->xbutton.button == Button6 || event->xbutton.button == Button7))
644   {
645     gltrackball_mousewheel(cp->trackball,
646         event->xbutton.button, 5, !!event->xbutton.state);
647     return True;
648   }
649   else if(event->xany.type == MotionNotify && cp->button_down)
650   {
651     gltrackball_track(cp->trackball, event->xmotion.x, event->xmotion.y,
652         MI_WIDTH (mi), MI_HEIGHT (mi));
653     return True;
654   }
655   return False;
656 }
657
658
659 XSCREENSAVER_MODULE ("RubikBlocks", rubikblocks)
660
661 #endif