1 /* topblock, Copyright (c) 2006-2012 rednuht <topblock.xscreensaver@jumpstation.co.uk>
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
14 topBlock - a simple openGL 3D hack of falling blocks
15 based on jwz's dangerball hack
17 The proporations of the blocks and their features is not even close to the commercial building block products offered by a variety companies.
19 information on this hack might be found at
20 http://www.jumpstation.co.uk/xscreensaver/topblock/
23 25/02/2006 v1.0 release
24 29/04/2006 v1.11 updated to better fit with xscreensaver v5
25 colors defaults to 7 (no black)
26 19/06/2006 v1.2 fixed dropSpeed = 7 bug, added gltrackball support and some code neatening, thanks to Valdis Kletnieks and JWZ for their input.
31 # define refresh_topBlock 0
33 #define DEFAULTS "*delay: 10000 \n" \
35 "*showFPS: False \n" \
36 "*wireframe: False \n" \
39 #define countof(x) (sizeof((x))/sizeof((*x)))
41 #include "xlockmore.h"
45 #include "gltrackball.h"
48 #ifdef USE_GL /* whole file */
56 GLXContext *glx_context;
57 trackball_state *trackball;
60 GLfloat highest,highestFalling;
61 GLfloat eyeLine,eyeX,eyeY,eyeZ;
62 int carpetWidth, carpetLength;
64 GLfloat followRadius,followAngle;
68 int carpet_polys, block_polys;
70 NODE *blockNodeFollow;
92 static XrmOptionDescRec opts[] = {
93 { "-size", ".size", XrmoptionSepArg, 0 },
94 { "-spawn", ".spawn", XrmoptionSepArg, 0 },
95 { "-camX", ".camX", XrmoptionSepArg, 0 },
96 { "-camY", ".camY", XrmoptionSepArg, 0 },
97 { "-camZ", ".camZ", XrmoptionSepArg, 0 },
98 { "+rotate", ".rotate", XrmoptionNoArg, "False" },
99 { "-rotate", ".rotate", XrmoptionNoArg, "True" },
100 { "+carpet", ".carpet", XrmoptionNoArg, "False" },
101 { "+nipples", ".nipples", XrmoptionNoArg, "False" },
102 { "-blob", ".blob", XrmoptionNoArg, "True" },
103 { "-rotateSpeed", ".rotateSpeed", XrmoptionSepArg, 0 },
104 { "-follow", ".follow", XrmoptionNoArg, "True" },
105 { "-maxFalling", ".maxFalling", XrmoptionSepArg, 0 },
106 { "-resolution", ".resolution", XrmoptionSepArg, 0 },
107 { "-maxColors", ".maxColors", XrmoptionSepArg, 0 },
108 { "-dropSpeed", ".dropSpeed", XrmoptionSepArg, 0 },
109 { "-override", ".override", XrmoptionNoArg, "True" },
112 #define DEF_OVERRIDE "False"
113 #define DEF_ROTATE "True"
114 #define DEF_FOLLOW "False"
115 #define DEF_CARPET "True"
116 #define DEF_BLOB "False"
117 #define DEF_NIPPLES "True"
118 #define DEF_ROTATE_SPEED "10"
119 #define DEF_MAX_FALLING "500"
120 #define DEF_MAX_COLORS "7"
122 #define DEF_SPAWN "50"
123 #define DEF_RESOLUTION "4"
124 #define DEF_CAM_X "1"
125 #define DEF_CAM_Y "20"
126 #define DEF_CAM_Z "25"
127 #define DEF_DROP_SPEED "4"
129 static argtype vars[] = {
130 {&override, "override", "Override", DEF_OVERRIDE, t_Bool},
131 {&rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool},
132 {&drawCarpet, "carpet", "Carpet", DEF_CARPET, t_Bool},
133 {&drawNipples, "nipples", "Nipples", DEF_NIPPLES, t_Bool},
134 {&drawBlob, "blob", "Blob", DEF_BLOB, t_Bool},
135 {&rotateSpeed, "rotateSpeed", "RotateSpeed", DEF_ROTATE_SPEED, t_Float},
136 {&follow, "follow", "Follow", DEF_FOLLOW, t_Bool},
137 {&camX, "camX", "camX", DEF_CAM_X, t_Float},
138 {&camY, "camY", "camY", DEF_CAM_Y, t_Float},
139 {&camZ, "camZ", "camZ", DEF_CAM_Z, t_Float},
140 {&size, "size", "size", DEF_SIZE, t_Int},
141 {&spawn, "spawn", "spawn", DEF_SPAWN, t_Int},
142 {&maxFalling, "maxFalling", "maxFalling", DEF_MAX_FALLING, t_Int},
143 {&resolution, "resolution", "resolution", DEF_RESOLUTION, t_Int},
144 {&maxColors, "maxColors", "maxColors", DEF_MAX_COLORS, t_Int},
145 {&dropSpeed, "dropSpeed", "DropSpeed", DEF_DROP_SPEED, t_Float},
148 static topBlockSTATE *tbs = NULL;
150 ModeSpecOpt topBlock_opts = {countof(opts), opts, countof(vars), vars, NULL};
152 /* Window management, etc */
154 reshape_topBlock (ModeInfo *mi, int width, int height)
156 GLfloat h = (GLfloat) height / (GLfloat) width;
157 glViewport (0, 0, (GLint) width, (GLint) height);
158 glMatrixMode(GL_PROJECTION);
160 gluPerspective (60.0, 1/h, 1.0, 1000.0);
161 glMatrixMode(GL_MODELVIEW);
163 glClear(GL_COLOR_BUFFER_BIT);
166 /* clean up on exit, not required ... */
168 release_topBlock(ModeInfo *mi)
170 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
171 NODE *llCurrent, *llOld;
172 llCurrent = tb->blockNodeRoot;
173 while (llCurrent != NULL) {
175 llCurrent = llCurrent->next;
182 init_topBlock (ModeInfo *mi)
185 int wire = MI_IS_WIREFRAME(mi);
188 tbs = (topBlockSTATE *)
189 calloc (MI_NUM_SCREENS(mi), sizeof (topBlockSTATE));
193 tb = &tbs[MI_SCREEN(mi)];
195 tb->glx_context = init_GL(mi);
197 reshape_topBlock (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
199 /* if (wire) { drawNipples=False; }*/
200 tb->numFallingBlocks=0;
202 if (size>10) { size = 10; }
203 if (size<1) { size = 2; }
204 tb->carpetWidth = 8 * size;
205 tb->carpetLength = tb->carpetWidth;
209 if (spawn<4) { spawn=4; }
210 if (spawn>1000) { spawn=1000; }
212 if (rotateSpeed<1) {rotateSpeed=1; }
213 if (rotateSpeed>1000) {rotateSpeed=1000;}
216 if (resolution<4) {resolution=4;}
217 if (resolution>20) {resolution=20;}
220 if (maxColors<1) {maxColors=1;}
221 if (maxColors>8) {maxColors=8;}
223 if (dropSpeed<1) {dropSpeed=1;}
224 if (dropSpeed>9) {dropSpeed=9;} /* 10+ produces blocks that can pass through each other */
226 dropSpeed = 80/dropSpeed;
227 dropSpeed = (blockHeight/dropSpeed);
229 reshape_topBlock (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
232 GLfloat pos[4] = {10.0, 10.0, 1.0, 0.0};
233 GLfloat amb[4] = {0.1, 0.1, 0.1, 1.0};
234 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
235 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
237 glEnable(GL_LIGHTING);
239 glLightfv(GL_LIGHT0, GL_POSITION, pos);
240 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
241 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
242 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
244 glDepthFunc(GL_LEQUAL);
245 glEnable(GL_DEPTH_TEST);
246 glDisable(GL_CULL_FACE); /* all objects exhibit a reverse side */
252 buildBlock(mi); /* build the display list holding the simple block */
254 buildCarpet(mi); /* build the base */
256 tb->highestFalling=0;
257 tb->eyeLine=tb->highest;
266 tb->rotation=random() % 360;
271 /* override camera settings */
282 tb->trackball = gltrackball_init ();
285 /* provides the per frame entertainment */
287 draw_topBlock (ModeInfo *mi)
289 Display *dpy = MI_DISPLAY(mi);
290 Window window = MI_WINDOW(mi);
293 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
294 GLfloat spcN1x,spcN1y,spcN2x,spcN2y;
295 GLfloat spcC1x,spcC1y,spcC2x,spcC2y;
296 int wire = MI_IS_WIREFRAME(mi);
299 if (!tb->glx_context)
301 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tb->glx_context));
302 mi->polygon_count = 0;
304 generateNewBlock(mi);
306 if (rotate && (!tb->button_down_p)) { tb->rotation += rotateSpeed; }
307 if (tb->rotation>=360) { tb->rotation=tb->rotation-360; }
309 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* clear current */
310 glLoadIdentity(); /* resets directions, do it every time ! */
311 glRotatef(current_device_rotation(), 0, 0, 1);
314 if (tb->highest>tb->eyeLine) { tb->eyeLine+=((tb->highest-tb->eyeLine)/100); } /* creates a smooth camera transition */
315 gluLookAt(camX, camY+tb->eyeLine, camZ, tb->eyeX, tb->eyeY+tb->eyeLine, tb->eyeZ, 0.0, 1.0, 0.0); /* setup viewer, xyz cam, xyz looking at and where is up normaly 0,1,0 */
316 glRotatef(90, 1.0, 0.0, 0.0); /* x axis */
318 glRotatef(90, 0.0, 0.0, 1.0); /* z axis */
323 glTranslatef (0, 0, -10);
324 glRotatef(-current_device_rotation(), 0, 0, 1);
325 gltrackball_rotate (tb->trackball);
326 glRotatef(current_device_rotation(), 0, 0, 1);
327 glTranslatef (0, 0, 10);
330 /* rotate the world */
331 glRotatef(tb->rotation, 0.0, 0.0, 1.0);
333 llCurrent = tb->blockNodeRoot;
336 glTranslatef(0.0-(tb->carpetWidth/2),0.0-(tb->carpetLength/2),0.0);
337 glCallList(tb->carpet);
338 mi->polygon_count += tb->carpet_polys;
339 glTranslatef(0.0+(tb->carpetWidth/2),0.0+(tb->carpetLength/2),0.0);
340 glTranslatef(0.0,0.0,-0.55);
342 tb->highestFalling=0;
343 while (llCurrent != NULL) { /* for each block */
344 glPushMatrix(); /* save state */
346 switch (llCurrent->color) {
396 if (wire) { glColor3fv(color); }
397 else { glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); }
399 if (llCurrent->falling==1) {
404 if (llCurrent->height>tb->highestFalling) {tb->highestFalling=llCurrent->height;}
405 /* all blocks fall at the same rate to avoid mid air collisions */
406 llCurrent->height-=dropSpeed;
407 if (llCurrent->height<=0) {
408 llCurrent->falling=0;
409 if (tb->highest==0) {
410 tb->highest+=blockHeight;
413 if ( (llCurrent->height<=tb->highest+1) && (llCurrent->falling==1) ) {
414 /* check for collision */
415 llNode = tb->blockNodeRoot;
416 spcC1x = llCurrent->x;
417 spcC1y = llCurrent->y;
418 switch(llCurrent->rotation) {
419 case getOrientation(0):
423 case getOrientation(1):
427 case getOrientation(2):
431 case getOrientation(3):
436 while (llNode != NULL) {
437 if ( (llNode->falling==0) && (llCurrent->falling==1) ) {
440 switch(llNode->rotation) {
441 case getOrientation(0):
445 case getOrientation(1):
449 case getOrientation(2):
453 case getOrientation(3):
459 ( (spcC1x==spcN1x) && (spcC1y==spcN1y) ) ||
460 ( (spcC1x==spcN2x) && (spcC1y==spcN2y) ) ||
461 ( (spcC2x==spcN2x) && (spcC2y==spcN2y) ) ||
462 ( (spcC2x==spcN1x) && (spcC2y==spcN1y) )
464 if ( fabs(llCurrent->height-(llNode->height+blockHeight)) <= TOLERANCE) {
466 llCurrent->falling=0;
467 llCurrent->height=llNode->height+blockHeight; /* if this is missing then small errors build up until the model fails */
468 if ( fabs(llCurrent->height-tb->highest) <= TOLERANCE+blockHeight ) {
469 tb->highest+=blockHeight;
478 /* set location in space */
479 glTranslatef(llCurrent->x,llCurrent->y,-llCurrent->height);
481 glRotatef(llCurrent->rotation, 0.0f, 0.0f, 1.0f);
482 if ((tb->followMode==0) && (llCurrent->next==NULL)) {
483 tb->blockNodeFollow = llCurrent;
486 llCurrent = llCurrent->next;
488 glCallList(tb->block);
489 mi->polygon_count += tb->block_polys;
490 glPopMatrix(); /* restore state */
492 if (mi->fps_p) do_fps (mi);
495 if (tb->highest>(5*maxFalling)) { drawCarpet=False; }
496 glXSwapBuffers(dpy, window);
501 /* camera is in follow mode, work out where we should be looking */
502 static void followBlock(ModeInfo *mi)
504 GLfloat xLen,yLen,cx,cy,rangle,xTarget,yTarget;
505 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
507 if ((tb->blockNodeFollow!=NULL) && (tb->followMode==1)){
509 if (tb->highest>tb->eyeLine) { tb->eyeLine+= ((tb->highest-tb->eyeLine)/100); }
510 /*tb->blockNodeFollow->color=1; only noticable if you set the colors to 1 */
512 if (tb->blockNodeFollow->height > tb->eyeZ) { tb->eyeZ+= ((tb->blockNodeFollow->height - tb->eyeZ)/100); }
513 if (tb->blockNodeFollow->height < tb->eyeZ) { tb->eyeZ-= ((tb->eyeZ - tb->blockNodeFollow->height)/100); }
516 /* when the scene is rotated we need to know where the block is in the 2 dimensional coordinates of the carpet area
517 (see http://www.jumpstation.co.uk/rotation/)
520 if (tb->followRadius==0) {
521 xLen = tb->blockNodeFollow->x-cx;
522 yLen = tb->blockNodeFollow->y-cy;
523 tb->followRadius=sqrt( (xLen*xLen) + (yLen*yLen) );
524 tb->followAngle = (180/M_PI) * asin(xLen/tb->followRadius);
525 tb->followAngle = quadrantCorrection(tb->followAngle,(int)cx,(int)cy,(int)tb->blockNodeFollow->x,(int)tb->blockNodeFollow->y);
527 rangle = (tb->followAngle+tb->rotation) * M_PI /180;
528 xTarget = cos(rangle) * tb->followRadius + cx;
529 yTarget = sin(rangle) * tb->followRadius + cy;
530 if (tb->followAngle>360) { tb->followAngle=tb->followAngle-360; }
532 if (xTarget < tb->eyeX) { tb->eyeX-= ((tb->eyeX - xTarget)/100); }
533 if (xTarget > tb->eyeX) { tb->eyeX+= ((xTarget - tb->eyeX)/100); }
535 if (yTarget < tb->eyeY) { tb->eyeY-= ((tb->eyeY - yTarget)/100); }
536 if (yTarget > tb->eyeY) { tb->eyeY+= ((yTarget - tb->eyeY)/100); }
537 if (!tb->blockNodeFollow->falling) {
539 /*tb->blockNodeFollow->color=2; only noticable if you set the colors to 1 */
543 gluLookAt(camX, camY, camZ-tb->eyeLine, tb->eyeX, tb->eyeY, -tb->eyeZ,-1.0,0.0,0.0);
546 /* each quater of the circle has to be adjusted for */
547 static double quadrantCorrection(double angle,int cx,int cy,int x,int y)
549 if ((x>=cx) && (y>=cy)) {
550 angle += (90-(angle-90) * 2);
551 } else if ((x>=cx) && (y<=cy)) {
553 } else if ((x<=cx) && (y<=cy)) {
555 } else if ((x<=cx) && (y>=cy)) {
556 angle += (90-(angle-90) * 2);
561 /* if random chance then create a new falling block */
562 static void generateNewBlock(ModeInfo *mi)
564 NODE *llCurrent, *llTail;
565 GLfloat startOffx, startOffy;
566 int endOffx, endOffy;
567 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
568 if ( ((random() % spawn) == 1) && (tb->highestFalling<getHeight((tb->plusheight-blockHeight)+tb->highest)) ) {
573 tb->numFallingBlocks++;
574 llTail = tb->blockNodeRoot;
575 if (llTail == NULL) {
576 llCurrent = ((NODE*) malloc(sizeof(NODE)));
577 if (!llCurrent) abort();
579 tb->blockNodeRoot = llCurrent;
581 if (tb->numFallingBlocks>=maxFalling) {
583 llCurrent=llTail->next;
584 tb->blockNodeRoot=llCurrent->next;
586 llCurrent = ((NODE*) malloc(sizeof(NODE)));
587 if (!llCurrent) abort();
589 while (llTail->next != NULL) { llTail = llTail->next; } /* find last item in list */
591 llCurrent->falling=1;
592 llCurrent->rotation=getOrientation(random() % 4);
593 if (llCurrent->rotation==getOrientation(0)) {
598 } else if (llCurrent->rotation==getOrientation(1)) {
603 } else if (llCurrent->rotation==getOrientation(2)) {
608 } else if (llCurrent->rotation==getOrientation(3)) {
615 llCurrent->x=(startOffx-(tb->carpetLength/2)) + getLocation(random() % ((tb->carpetLength/2)+endOffx) );
616 llCurrent->y=(startOffy-(tb->carpetLength/2)) + getLocation(random() % ((tb->carpetLength/2)+endOffy) );
617 llCurrent->color=(random() % maxColors);
618 llCurrent->height=getHeight(tb->plusheight+tb->highest);
619 if (tb->numFallingBlocks>=maxFalling) {
620 tb->numFallingBlocks--;
621 tb->numFallingBlocks--;
623 llTail->next = llCurrent;
630 /* called at init this creates the 'carpet' display list item */
631 static void buildCarpet(ModeInfo *mi)
635 int wire = MI_IS_WIREFRAME(mi);
636 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
641 tb->carpet=glGenLists(1); /* only one */
642 glNewList(tb->carpet,GL_COMPILE);
644 glPushMatrix(); /* save state */
647 if (wire) { glColor3fv(color); }
648 else { glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); }
649 /* draw carpet plane */
650 glBegin( wire ? GL_LINE_LOOP : GL_QUADS );
652 glNormal3f( 0, 0, -1 );
653 glVertex3f(0.0,0.0,0.0);
654 glVertex3f(x,0.0,0.0);
656 glVertex3f(0.0,y,0.0);
659 /* add edge pieces */
661 glNormal3f( 0, -1, 0 );
662 glVertex3f(0.0,0.0,0.0);
663 glVertex3f(x,0.0,0.0);
664 glVertex3f(x,0,singleThick);
665 glVertex3f(0.0,0,singleThick);
668 glNormal3f( -1, 0, 0 );
669 glVertex3f(0.0,0.0,0.0);
671 glVertex3f(0,y,singleThick);
672 glVertex3f(0.0,0,singleThick);
675 glNormal3f( 1, 0, 0 );
676 glVertex3f(x,0.0,0.0);
678 glVertex3f(x,y,singleThick);
679 glVertex3f(x,0,singleThick);
682 glNormal3f( 0, 1, 0 );
685 glVertex3f(x,y,singleThick);
686 glVertex3f(0,y,singleThick);
692 glTranslatef(0.5f,0.5f,-.25); /* move to the cylinder center */
694 glPushMatrix(); /* save state */
696 tb->carpet_polys += tube(0, 0, -0.1,
699 resolution, True, True,
701 glRotatef(180, 0.0f, 1.0f, 0.0f); /* they are upside down */
702 glRotatef(180, 0.0f, 1.0f, 0.0f); /* recover */
703 glTranslatef(0.0f,1.0f,0.0f); /* move to the next cylinder center (backward) */
705 glPopMatrix(); /* save state */
706 glTranslatef(1.0f,0.0f,0.0f); /* reset */
709 glPopMatrix(); /* restore state */
713 /* using the verticies arrays builds the plane, now with normals */
714 static void polygonPlane(int wire, int a, int b, int c , int d, int i)
716 GLfloat topBlockNormals[5][3] = { {0,0,-1}, {0,1,0}, {1,0,0}, {0,0,1}, {0,-1,0} };
717 GLfloat topBlockVertices[8][3] = { {-0.49,-2.97,-0.99}, {0.99,-2.97,-0.99}, {0.99,0.99,-0.99} , {-0.49,0.99,-0.99}, {-0.49,-2.97,0.99} , {0.99,-2.97,0.99}, {0.99,0.99,0.99} , {-0.49,0.99,0.99} };
718 glBegin( wire ? GL_LINE_LOOP : GL_POLYGON);
719 glNormal3fv(topBlockNormals[i] );
720 glVertex3fv(topBlockVertices[a]);
721 glVertex3fv(topBlockVertices[b]);
722 glVertex3fv(topBlockVertices[c]);
723 glVertex3fv(topBlockVertices[d]);
727 /* called at init this creates the 'block' display list item */
728 /* the spheres came about originaly as quick way to test the directional lighting/normals */
729 static void buildBlock(ModeInfo *mi)
732 int wire = MI_IS_WIREFRAME(mi);
733 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
734 tb->block=glGenLists(1); /* only one */
735 glNewList(tb->block,GL_COMPILE);
737 glPushMatrix(); /* save state */
738 glRotatef(90, 0.0f, 1.0f, 0.0f);
740 polygonPlane(wire, 0,3,2,1,0); tb->block_polys++;
741 polygonPlane(wire, 2,3,7,6,1); tb->block_polys++;
742 polygonPlane(wire, 1,2,6,5,2); tb->block_polys++;
743 polygonPlane(wire, 4,5,6,7,3); tb->block_polys++;
744 polygonPlane(wire, 0,1,5,4,4); tb->block_polys++;
747 /* draw 8 cylinders each with a disk cap */
748 glRotatef(90, 0.0f, 1.0f, 0.0f); /* 'aim' the pointer ready for the cylinder */
749 glTranslatef(0.5f,0.5f,0.99f); /* move to the cylinder center */
752 tb->block_polys += tube(0, 0, 0,
755 resolution, True, True,
757 glTranslatef(0.0f,0.0f,0.25f); /* move to the cylinder cap */
758 glTranslatef(0.0f,0.0f,-0.25f); /* move back from the cylinder cap */
760 glTranslatef(0.0f,-1.0f,0.0f); /* move to the next cylinder center (forward) */
762 glTranslatef(0.0f,1.0f,0.0f); /* move to the next cylinder center (backward) */
765 glTranslatef(-1.0f,1.0f,0.0f); /* move to the cylinder center */
768 /* 3 cylinders on the underside */
769 glTranslatef(1.5f,-2.5f,-1.5f); /* move to the center, under the top of the brick */
772 tb->block_polys += tube(0, 0, 0.1,
775 resolution, True, True, wire);
776 glTranslatef(0.0f,-1.0f,0.0f); /* move to the center */
779 glPopMatrix(); /* restore state */
784 rip off of the builBlock() function creating the GL compilied pointer "block" but only creates two spheres.
785 spheres are created with unit_sphere from spheres.h to allow wire frame
787 static void buildBlobBlock(ModeInfo *mi)
789 int wire = MI_IS_WIREFRAME(mi);
790 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
791 tb->block=glGenLists(1); /* only one */
792 glNewList(tb->block,GL_COMPILE);
794 glScalef(1.4,1.4,1.4);
795 unit_sphere (resolution/2,resolution, wire);
797 glTranslatef(0.0f,-2.0f,0.0f);
798 glScalef(1.4,1.4,1.4);
799 unit_sphere (resolution/2,resolution, wire);
804 /* handle input events or not if daemon running the show */
806 topBlock_handle_event (ModeInfo *mi, XEvent *event)
808 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
809 if (event->xany.type == KeyPress) {
812 XLookupString (&event->xkey, &c, 1, &keysym, 0);
816 } else if (c == 'z') {
819 } else if (c == 's') {
822 } else if (c == 'x') {
825 } else if (c == 'd') {
828 } else if (c == 'c') {
831 } else if (c == 'f') {
834 } else if (c == 'v') {
837 } else if (c == 'g') {
840 } else if (c == 'b') {
843 } else if (c == 'h') {
846 } else if (c == 'n') {
849 } else if (c == 'r') {
854 if (event->xany.type == ButtonPress &&
855 event->xbutton.button == Button1)
857 tb->button_down_p = True;
858 gltrackball_start (tb->trackball,
859 event->xbutton.x, event->xbutton.y,
860 MI_WIDTH (mi), MI_HEIGHT (mi));
863 else if (event->xany.type == ButtonRelease &&
864 event->xbutton.button == Button1)
866 tb->button_down_p = False;
869 else if (event->xany.type == ButtonPress &&
870 (event->xbutton.button == Button4 ||
871 event->xbutton.button == Button5 ||
872 event->xbutton.button == Button6 ||
873 event->xbutton.button == Button7))
875 gltrackball_mousewheel (tb->trackball, event->xbutton.button, 10,
876 !!event->xbutton.state);
879 else if (event->xany.type == MotionNotify &&
882 gltrackball_track (tb->trackball,
883 event->xmotion.x, event->xmotion.y,
884 MI_WIDTH (mi), MI_HEIGHT (mi));
890 /* this is tha main change for v5 compatability and acompanying ENTRYPOINTS */
891 XSCREENSAVER_MODULE_2 ("TopBlock", topblock, topBlock)