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