1 /* Sky Tentacles, Copyright (c) 2008 Jamie Zawinski <jwz@jwz.org>
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
12 #define DEFAULTS "*delay: 30000 \n" \
14 "*showFPS: False \n" \
15 "*wireframe: False \n" \
17 # define refresh_tentacles 0
18 # define release_tentacles 0
20 #define countof(x) (sizeof((x))/sizeof((*x)))
22 #include "xlockmore.h"
26 #include "gltrackball.h"
29 #ifdef USE_GL /* whole file */
31 #define DEF_SPEED "1.0"
32 #define DEF_SMOOTH "True"
33 #define DEF_SLICES "32"
34 #define DEF_SEGMENTS "32"
35 #define DEF_WIGGLINESS "0.35"
36 #define DEF_FLEXIBILITY "0.35"
37 #define DEF_THICKNESS "1.0"
38 #define DEF_LENGTH "9.0"
39 #define DEF_COLOR "#305A30"
40 #define DEF_STRIPE "#451A30"
41 #define DEF_SUCKER "#453E30"
42 #define DEF_DEBUG "False"
44 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
47 GLfloat length; /* length of the segment coming out of this segment */
48 GLfloat th; /* vector tilt (on yz plane) from previous segment */
49 GLfloat phi; /* vector rotation (on xy plane) from previous segment */
50 GLfloat thickness; /* radius of tentacle at this segment */
51 rotator *rot; /* motion modeller */
56 GLfloat x, y, z; /* position of the base */
59 GLfloat tentacle_color[4], stripe_color[4], sucker_color[4];
63 GLXContext *glx_context;
64 trackball_state *trackball;
70 GLfloat tentacle_color[4], stripe_color[4], sucker_color[4];
78 } tentacles_configuration;
80 static tentacles_configuration *tcs = NULL;
83 static GLfloat arg_speed;
85 static int arg_slices;
86 static int arg_segments;
87 static GLfloat arg_thickness;
88 static GLfloat arg_length;
89 static GLfloat arg_wiggliness;
90 static GLfloat arg_flexibility;
91 static char *arg_color, *arg_stripe, *arg_sucker;
93 static XrmOptionDescRec opts[] = {
94 { "-speed", ".speed", XrmoptionSepArg, 0 },
95 { "-no-smooth", ".smooth", XrmoptionNoArg, "False" },
96 { "-slices", ".slices", XrmoptionSepArg, 0 },
97 { "-segments", ".segments", XrmoptionSepArg, 0 },
98 { "-thickness", ".thickness", XrmoptionSepArg, 0 },
99 { "-length", ".length", XrmoptionSepArg, 0 },
100 { "-wiggliness", ".wiggliness", XrmoptionSepArg, 0 },
101 { "-flexibility", ".flexibility", XrmoptionSepArg, 0 },
102 { "-color", ".tentacleColor", XrmoptionSepArg, 0 },
103 { "-stripe-color", ".stripeColor", XrmoptionSepArg, 0 },
104 { "-sucker-color", ".suckerColor", XrmoptionSepArg, 0 },
105 { "-debug", ".debug", XrmoptionNoArg, "True" },
108 static argtype vars[] = {
109 {&arg_speed, "speed", "Speed", DEF_SPEED, t_Float},
110 {&smooth_p, "smooth", "Smooth", DEF_SMOOTH, t_Bool},
111 {&arg_slices, "slices", "Slices", DEF_SLICES, t_Int},
112 {&arg_segments, "segments", "Segments", DEF_SEGMENTS, t_Int},
113 {&arg_thickness, "thickness", "Thickness", DEF_THICKNESS, t_Float},
114 {&arg_length, "length", "Length", DEF_LENGTH, t_Float},
115 {&arg_wiggliness, "wiggliness", "Wiggliness", DEF_WIGGLINESS, t_Float},
116 {&arg_flexibility, "flexibility", "Flexibility", DEF_FLEXIBILITY, t_Float},
117 {&arg_color, "tentacleColor", "Color", DEF_COLOR, t_String},
118 {&arg_stripe, "stripeColor", "Color", DEF_STRIPE, t_String},
119 {&arg_sucker, "suckerColor", "Color", DEF_SUCKER, t_String},
120 {&debug_p, "debug", "Debug", DEF_DEBUG, t_Bool},
123 ENTRYPOINT ModeSpecOpt tentacles_opts = {countof(opts), opts, countof(vars), vars, NULL};
126 /* Window management, etc
129 reshape_tentacles (ModeInfo *mi, int width, int height)
131 GLfloat h = (GLfloat) height / (GLfloat) width;
133 glViewport (0, 0, (GLint) width, (GLint) height);
135 glMatrixMode(GL_PROJECTION);
137 gluPerspective (30.0, 1/h, 1.0, 100.0);
139 glMatrixMode(GL_MODELVIEW);
141 gluLookAt( 0.0, 0.0, 30.0,
145 glClear(GL_COLOR_BUFFER_BIT);
151 unit_torus (double ratio, int slices1, int slices2, Bool wire)
153 int i, j, k, polys = 0;
155 if (wire) slices1 /= 2;
156 if (wire) slices2 /= 4;
157 if (slices1 < 3) slices1 = 3;
158 if (slices2 < 3) slices2 = 3;
161 glBegin (wire ? GL_LINE_STRIP : GL_QUAD_STRIP);
162 for (i = 0; i < slices1; i++)
163 for (j = 0; j <= slices2; j++)
164 for (k = 0; k <= 1; k++)
166 double s = (i + k) % slices1 + 0.5;
167 double t = j % slices2;
169 double x = cos(t*M_PI*2/slices2) * cos(s*M_PI*2/slices1);
170 double y = sin(t*M_PI*2/slices2) * cos(s*M_PI*2/slices1);
171 double z = sin(s*M_PI*2/slices1);
174 x = (1 + ratio * cos(s*M_PI*2/slices1)) * cos(t*M_PI*2/slices2) / 2;
175 y = (1 + ratio * cos(s*M_PI*2/slices1)) * sin(t*M_PI*2/slices2) / 2;
176 z = ratio * sin(s*M_PI*2/slices1) / 2;
187 /* Initializes a new tentacle and stores it in the list.
190 make_tentacle (ModeInfo *mi, int which, int total)
192 tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
193 tentacle *t = (tentacle *) calloc (1, sizeof (*t));
199 /* position tentacles on a grid */
201 int cols = (int) (sqrt(total) + 0.5);
202 int rows = (total+cols-1) / cols;
203 int xx = which % cols;
204 int yy = which / cols;
205 double spc = arg_thickness * 0.8;
206 t->x = (cols * spc / 2) - (spc * (xx + 0.5));
207 t->y = (rows * spc / 2) - (spc * (yy + 0.5));
211 /* Brighten or darken the colors of this tentacle from the default.
213 brightness = 0.6 + frand(3.0);
214 memcpy (t->tentacle_color, tc->tentacle_color, 4 * sizeof(*t->tentacle_color));
215 memcpy (t->stripe_color, tc->stripe_color, 4 * sizeof(*t->stripe_color));
216 memcpy (t->sucker_color, tc->sucker_color, 4 * sizeof(*t->sucker_color));
218 t->X[0] *= brightness; if (t->X[0] > 1) t->X[0] = 1; \
219 t->X[1] *= brightness; if (t->X[1] > 1) t->X[1] = 1; \
220 t->X[2] *= brightness; if (t->X[2] > 1) t->X[2] = 1
221 FROB (tentacle_color);
226 t->nsegments = (arg_segments) + BELLRAND(arg_segments);
228 t->segments = (segment *) calloc (t->nsegments+1, sizeof(*t->segments));
229 for (i = 0; i < t->nsegments; i++)
231 double spin_speed = 0;
232 double spin_accel = 0;
233 double wander_speed = arg_speed * (0.02 + BELLRAND(0.1));
234 t->segments[i].rot = make_rotator (spin_speed, spin_speed, spin_speed,
235 spin_accel, wander_speed, True);
238 t->segments[0].thickness = (((arg_thickness * 0.5) +
239 BELLRAND(arg_thickness * 0.6))
242 if (tc->tentacles_size <= tc->ntentacles)
244 tc->tentacles_size = (tc->tentacles_size * 1.2) + tc->ntentacles;
245 tc->tentacles = (tentacle **)
246 realloc (tc->tentacles, tc->tentacles_size * sizeof(*tc->tentacles));
249 fprintf (stderr, "%s: out of memory (%d tentacles)\n",
250 progname, tc->tentacles_size);
255 tc->tentacles[tc->ntentacles++] = t;
261 draw_tentacle (tentacle *t)
263 tentacles_configuration *tc = &tcs[MI_SCREEN(t->mi)];
265 Bool wire = MI_IS_WIREFRAME (t->mi);
266 XYZ ctr = { 0,0,0 }; /* current position of base of segment */
267 double cth = 0; /* overall orientation of current segment */
269 double cth_cos = 1, cth_sin = 0;
270 double cphi_cos = 1, cphi_sin = 0;
272 XYZ *ring, *oring; /* points around the edge (this, and previous) */
273 XYZ *norm, *onorm; /* their normals */
275 /* Which portion of the radius the colored stripe takes up */
276 int indented_points = arg_slices * 0.2;
278 /* We do rotation way to minimize number of calls to sin/cos. */
279 # define ROT(P) do { \
281 _p.y = ((P.y * cth_sin - P.x * cth_cos)); \
282 _p.x = ((P.y * cth_cos + P.x * cth_sin) * cphi_sin - (P.z * cphi_cos)); \
283 _p.z = ((P.y * cth_cos + P.x * cth_sin) * cphi_cos + (P.z * cphi_sin)); \
287 ring = (XYZ *) malloc (arg_slices * sizeof(*ring));
288 norm = (XYZ *) malloc (arg_slices * sizeof(*norm));
289 oring = (XYZ *) malloc (arg_slices * sizeof(*oring));
290 onorm = (XYZ *) malloc (arg_slices * sizeof(*onorm));
293 glColor4fv (t->tentacle_color);
296 static const GLfloat bspec[4] = {1.0, 1.0, 1.0, 1.0};
297 static const GLfloat bshiny = 128.0;
298 glMaterialfv (GL_FRONT, GL_SPECULAR, bspec);
299 glMateriali (GL_FRONT, GL_SHININESS, bshiny);
303 glTranslatef (t->x, t->y, t->z);
307 if (!wire) glDisable(GL_LIGHTING);
308 glBegin(GL_LINE_LOOP);
309 for (i = 0; i < arg_slices; i++)
310 glVertex3f (arg_thickness / 2 * cos (M_PI * 2 * i / arg_slices),
311 arg_thickness / 2 * sin (M_PI * 2 * i / arg_slices),
314 if (!wire) glEnable(GL_LIGHTING);
317 for (i = 0; i < t->nsegments; i++)
322 for (j = 0; j < arg_slices; j++)
324 /* Construct a vertical disc at the origin, to use as the
325 base of this segment.
327 double r = t->segments[i].thickness / 2;
328 double a = M_PI * 2 * j / arg_slices;
330 if (j <= indented_points/2 || j >= arg_slices-indented_points/2)
333 ring[j].x = r * cos (a);
335 ring[j].z = r * sin (a);
337 /* Then rotate the points by the angle of the current segment. */
340 /* Then move the ring to the base of this segment. */
347 /* Compute the normals of the faces on this segment. We do this
348 first so that the normals of the vertexes can be the average
349 of the normals of the faces.
350 #### Uh, except I didn't actually implement that...
351 but it would be a good idea.
354 for (j = 0; j <= arg_slices; j++)
356 int j0 = j % arg_slices;
357 int j1 = (j+1) % arg_slices;
358 norm[j0] = calc_normal (oring[j0], ring[j0], ring[j1]);
367 glFrontFace (GL_CCW);
368 glBegin (wire ? GL_LINES : smooth_p ? GL_QUAD_STRIP : GL_QUADS);
369 for (j = 0; j <= arg_slices; j++)
371 int j0 = j % arg_slices;
372 int j1 = (j+1) % arg_slices;
374 if (j <= indented_points/2 || j >= arg_slices-indented_points/2)
375 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
378 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
381 glNormal3f (onorm[j0].x, onorm[j0].y, onorm[j0].z);
382 glVertex3f (oring[j0].x, oring[j0].y, oring[j0].z);
383 glNormal3f ( norm[j0].x, norm[j0].y, norm[j0].z);
384 glVertex3f ( ring[j0].x, ring[j0].y, ring[j0].z);
387 glVertex3f ( ring[j1].x, ring[j1].y, ring[j1].z);
388 glVertex3f (oring[j1].x, oring[j1].y, oring[j1].z);
390 t->mi->polygon_count++;
396 glBegin (GL_LINE_LOOP);
397 for (j = 0; j < arg_slices; j++)
398 glVertex3f (ring[j].x, ring[j].y, ring[j].z);
402 /* Now draw the suckers!
405 double seg_length = arg_length / t->nsegments;
406 double sucker_size = arg_thickness / 8;
407 double sucker_spacing = sucker_size * 1.5;
408 int nsuckers = seg_length / sucker_spacing;
409 double oth = cth - t->segments[i-1].th;
410 double ophi = cphi - t->segments[i-1].phi;
415 int segs_per_sucker =
416 (int) ((sucker_spacing / seg_length) + 0.5);
417 nsuckers = (i % segs_per_sucker) ? 0 : 1;
420 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, t->sucker_color);
422 for (k = 0; k < nsuckers; k++)
428 p.x = p0.x + (p1.x - p0.x) * (k + 0.5) / nsuckers;
429 p.y = p0.y + (p1.y - p0.y) * (k + 0.5) / nsuckers;
430 p.z = p0.z + (p1.z - p0.z) * (k + 0.5) / nsuckers;
433 glTranslatef (p.x, p.y, p.z);
434 glRotatef (ophi * 180 / M_PI, 0, 1, 0);
435 glRotatef (-oth * 180 / M_PI, 1, 0, 0);
436 glRotatef (90, 1, 0, 0);
438 { /* Not quite right: this is the slope of the outer edge
439 if the next segment was not tilted at all... If there
440 is any tilt, then the angle of this wall and the
441 opposite wall are very different.
443 double slope = ((t->segments[i-1].thickness -
444 t->segments[i].thickness) /
445 t->segments[i].length);
446 glRotatef (-45 * slope, 1, 0, 0);
449 scale = t->segments[i].thickness / arg_thickness;
451 glTranslatef (0, 0, -0.15 * sucker_size);
452 glScalef (scale, scale, scale*4);
454 double off = sucker_size * 1.4;
456 glTranslatef (off, 0, 0);
457 glCallList (tc->sucker_list);
458 t->mi->polygon_count += tc->sucker_polys;
462 glTranslatef (-off, 0, 0);
463 glCallList (tc->sucker_list);
464 t->mi->polygon_count += tc->sucker_polys;
473 /* Now draw the end caps.
475 if (i == 0 || i == t->nsegments-1)
478 GLfloat ctrz = ctr.z + ((i == 0 ? -1 : 1) *
479 t->segments[i].thickness / 4);
480 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, t->tentacle_color);
481 glFrontFace (i == 0 ? GL_CCW : GL_CW);
482 glBegin (wire ? GL_LINES : GL_TRIANGLE_FAN);
483 glNormal3f (0, 0, (i == 0 ? -1 : 1));
484 glVertex3f (ctr.x, ctr.y, ctrz);
485 for (j = 0; j <= arg_slices; j++)
487 int jj = j % arg_slices;
488 glNormal3f (norm[jj].x, norm[jj].y, norm[jj].z);
489 glVertex3f (ring[jj].x, ring[jj].y, ring[jj].z);
490 if (wire) glVertex3f (ctr.x, ctr.y, ctrz);
491 t->mi->polygon_count++;
496 /* Now move to the end of this segment in preparation for the next.
498 if (i != t->nsegments-1)
501 p.y = t->segments[i].length;
508 /* Accumulate the current angle and rotation, to keep track of the
509 rotation of the upcoming segment.
511 cth += t->segments[i].th;
512 cphi += t->segments[i].phi;
516 cphi_sin = sin (cphi);
517 cphi_cos = cos (cphi);
519 memcpy (oring, ring, arg_slices * sizeof(*ring));
520 memcpy (onorm, norm, arg_slices * sizeof(*norm));
534 move_tentacle (tentacle *t)
536 /* tentacles_configuration *tc = &tcs[MI_SCREEN(t->mi)]; */
540 int skip = t->nsegments * (1 - (arg_wiggliness + 0.5));
544 for (i = 0; i < t->nsegments; i++)
546 if (++tick >= skip || i == t->nsegments-1)
548 /* randomize the motion of this segment... */
550 double phi_range = M_PI * 0.8 * arg_flexibility;
551 double th_range = M_PI * 0.9 * arg_flexibility;
552 get_position (t->segments[i].rot, &x, &y, &z, True);
553 t->segments[i].phi = phi_range * (0.5 - y);
554 t->segments[i].th = th_range * (0.5 - z);
555 t->segments[i].length = ((0.8 + ((0.5 - x) * 0.4)) *
556 (arg_length / t->nsegments));
558 /* ...and make the previous N segments be interpolated
559 between this one and the previous randomized one. */
560 for (j = last+1; j <= i; j++)
562 t->segments[j].phi = (t->segments[i].phi / (i - last));
563 t->segments[j].th = (t->segments[i].th / (i - last));
564 t->segments[j].length = (t->segments[i].length);
570 len += t->segments[i].length;
573 /* thickness of segment is relative to current position on tentacle
574 (not just the index of the segment). */
575 for (i = 0; i < t->nsegments; i++)
579 double tt = (t->segments[0].thickness * (len - pos) / len);
580 if (tt < 0.001) tt = 0.001;
581 t->segments[i].thickness = tt;
583 pos += t->segments[i].length;
590 tentacles_handle_event (ModeInfo *mi, XEvent *event)
592 tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
594 if (event->xany.type == ButtonPress &&
595 event->xbutton.button == Button1)
597 tc->button_down_p = True;
598 gltrackball_start (tc->trackball,
599 event->xbutton.x, event->xbutton.y,
600 MI_WIDTH (mi), MI_HEIGHT (mi));
603 else if (event->xany.type == ButtonRelease &&
604 event->xbutton.button == Button1)
606 tc->button_down_p = False;
609 else if (event->xany.type == ButtonPress &&
610 (event->xbutton.button == Button4 ||
611 event->xbutton.button == Button5 ||
612 event->xbutton.button == Button6 ||
613 event->xbutton.button == Button7))
615 gltrackball_mousewheel (tc->trackball, event->xbutton.button, 10,
616 !!event->xbutton.state);
619 else if (event->xany.type == MotionNotify &&
622 gltrackball_track (tc->trackball,
623 event->xmotion.x, event->xmotion.y,
624 MI_WIDTH (mi), MI_HEIGHT (mi));
633 parse_color (ModeInfo *mi, const char *name, const char *s, GLfloat *a)
636 a[3] = 1.0; /* alpha */
638 if (! XParseColor (MI_DISPLAY(mi), MI_COLORMAP(mi), s, &c))
640 fprintf (stderr, "%s: can't parse %s color %s", progname, name, s);
643 a[0] = c.red / 65536.0;
644 a[1] = c.green / 65536.0;
645 a[2] = c.blue / 65536.0;
650 init_tentacles (ModeInfo *mi)
652 tentacles_configuration *tc;
653 int wire = MI_IS_WIREFRAME(mi);
657 tcs = (tentacles_configuration *)
658 calloc (MI_NUM_SCREENS(mi), sizeof (tentacles_configuration));
660 fprintf(stderr, "%s: out of memory\n", progname);
664 tc = &tcs[MI_SCREEN(mi)];
667 tc = &tcs[MI_SCREEN(mi)];
669 tc->glx_context = init_GL(mi);
671 reshape_tentacles (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
675 GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
676 GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
677 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
678 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
680 glEnable(GL_LIGHTING);
682 glEnable(GL_DEPTH_TEST);
683 glEnable(GL_CULL_FACE);
685 glLightfv(GL_LIGHT0, GL_POSITION, pos);
686 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
687 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
688 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
691 tc->trackball = gltrackball_init ();
693 tc->left_p = !(random() % 5);
695 if (arg_segments < 2) arg_segments = 2;
696 if (arg_slices < 3) arg_slices = 3;
697 if (arg_thickness < 0.1) arg_thickness = 0.1;
698 if (arg_wiggliness < 0) arg_wiggliness = 0;
699 if (arg_wiggliness > 1) arg_wiggliness = 1;
700 if (arg_flexibility < 0) arg_flexibility = 0;
701 if (arg_flexibility > 1) arg_flexibility = 1;
703 parse_color (mi, "tentacleColor", arg_color, tc->tentacle_color);
704 parse_color (mi, "stripeColor", arg_stripe, tc->stripe_color);
705 parse_color (mi, "suckerColor", arg_sucker, tc->sucker_color);
707 for (i = 0; i < MI_COUNT(mi); i++)
708 move_tentacle (make_tentacle (mi, i, MI_COUNT(mi)));
710 tc->sucker_list = glGenLists (1);
711 glNewList (tc->sucker_list, GL_COMPILE);
712 { GLfloat s = arg_thickness / 5; glScalef (s, s, s); }
713 tc->sucker_polys = unit_torus (0.5,
714 MIN(8, arg_slices/6),
715 MIN(12, arg_slices/3),
716 MI_IS_WIREFRAME(mi));
722 draw_tentacles (ModeInfo *mi)
724 tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
725 int wire = MI_IS_WIREFRAME(mi);
726 Display *dpy = MI_DISPLAY(mi);
727 Window window = MI_WINDOW(mi);
730 if (!tc->glx_context)
733 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tc->glx_context));
735 glShadeModel(GL_SMOOTH);
737 glEnable(GL_DEPTH_TEST);
738 glEnable(GL_NORMALIZE);
739 glEnable(GL_CULL_FACE);
741 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
750 { GLfloat s = 8.7/1600; glScalef(s,s,s); }
751 glTranslatef(-800,-514,0);
752 if (!wire) glDisable(GL_LIGHTING);
753 glBegin(GL_LINE_LOOP);
755 glVertex3f(0,1028,0);
756 glVertex3f(1600,1028,0);
757 glVertex3f(1600,0,0);
759 if (!wire) glEnable(GL_LIGHTING);
763 gltrackball_rotate (tc->trackball);
765 mi->polygon_count = 0;
769 if (!wire) glDisable(GL_LIGHTING);
771 glVertex3f(-0.5, 0, 0); glVertex3f(0.5, 0, 0);
772 glVertex3f(0, -0.5, 0); glVertex3f(0, 0.5, 0);
774 if (!wire) glEnable(GL_LIGHTING);
778 glRotatef ( 45, 0, 1, 0); /* upper left */
779 glRotatef ( 45, 1, 0, 0);
780 glRotatef (-70, 0, 0, 1);
781 glTranslatef (0, -2, -4.5);
785 glRotatef (-45, 0, 1, 0); /* upper right */
786 glRotatef ( 45, 1, 0, 0);
787 glRotatef ( 70, 0, 0, 1);
788 glTranslatef (0, -2, -4.5);
791 if (!tc->button_down_p)
792 for (i = 0; i < tc->ntentacles; i++)
793 move_tentacle (tc->tentacles[i]);
795 for (i = 0; i < tc->ntentacles; i++)
796 draw_tentacle (tc->tentacles[i]);
800 if (mi->fps_p) do_fps (mi);
803 glXSwapBuffers(dpy, window);
806 XSCREENSAVER_MODULE_2 ("SkyTentacles", skytentacles, tentacles)