1 /* xscreensaver, Copyright (c) 1992, 1995, 1996, 1997, 1998, 2001
2 * Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
13 /* Simulation of a pair of quasi-gravitational fields, maybe sorta kinda
14 a little like the strong and weak electromagnetic forces. Derived from
15 a Lispm screensaver by John Pezaris <pz@mit.edu>. Mouse control and
16 viscosity added by "Philip Edward Cutone, III" <pc2d+@andrew.cmu.edu>.
20 The simulation started out as a purely accurate gravitational
21 simulation, but, with constant simulation step size, I quickly
22 realized the field being simulated while grossly gravitational
23 was, in fact, non-conservative. It also had the rather annoying
24 behavior of dealing very badly with colliding orbs. Therefore,
25 I implemented a negative-gravity region (with two thresholds; as
26 I read your code, you only implemented one) to prevent orbs from
27 every coming too close together, and added a viscosity factor if
28 the speed of any orb got too fast. This provides a nice stable
29 system with interesting behavior.
31 I had experimented with a number of fields including the van der
32 Waals force (very interesting orbiting behavior) and 1/r^3
33 gravity (not as interesting as 1/r^2). An even normal viscosity
34 (rather than the thresholded version to bleed excess energy) is
35 also not interesting. The 1/r^2, -1/r^2, -10/r^2 thresholds
36 proved not only robust but also interesting -- the orbs never
37 collided and the threshold viscosity fixed the
38 non-conservational problem.
41 > An even normal viscosity (rather than the thresholded version to
42 > bleed excess energy) is also not interesting.
44 unless you make about 200 points.... set the viscosity to about .8
45 and drag the mouse through it. it makes a nice wave which travels
48 And (always the troublemaker) Joe Keane <jgk@jgk.org> sez:
50 Despite what John sez, the field being simulated is always
51 conservative. The real problem is that it uses a simple hack,
52 computing acceleration *based only on the starting position*,
53 instead of a real differential equation solver. Thus you'll
54 always have energy coming out of nowhere, although it's most
55 blatant when balls get close together. If it were done right,
56 you wouldn't need viscosity or artificial limits on how close
59 Matt <straitm@carleton.edu> sez:
61 Added a switch to remove the walls.
63 Added a switch to make the threshold viscosity optional. If
64 nomaxspeed is specified, then balls going really fast do not
65 recieve special treatment.
67 I've made tail mode prettier by eliminating the first erase line
68 that drew from the upper left corner to the starting position of
71 Made the balls in modes other than "balls" bounce exactly at the
72 walls. (Because the graphics for different modes are drawn
73 differently with respect to the "actual" position of the point,
74 they used to be able to run somewhat past the walls, or bounce
77 Added an option to output each ball's speed in the form of a bar
78 graph drawn on the same window as the balls. If only x or y is
79 selected, they will be represented on the appropriate axis down
80 the center of the window. If both are selected, they will both
81 be displayed along the diagonal such that the x and y bars for
82 each point start at the same place. If speed is selected, the
83 speed will be displayed down the left side. */
87 #include "screenhack.h"
100 static struct ball *balls;
101 static double *x_vels;
102 static double *y_vels;
103 static double *speeds;
105 static int threshold;
107 static int global_size;
112 static Bool maxspeed_p;
113 static Bool cbounce_p;
114 static XPoint *point_stack;
115 static int point_stack_size, point_stack_fp;
116 static XColor *colors;
119 static int color_shift;
120 Bool no_erase_yet; /* for tail mode fix */
122 /*flip mods for mouse interaction*/
124 int mouse_x, mouse_y, mouse_mass, root_x, root_y;
125 static double viscosity;
127 static enum object_mode {
128 ball_mode, line_mode, polygon_mode, spline_mode, spline_filled_mode,
132 static enum graph_mode {
133 graph_none, graph_x, graph_y, graph_both, graph_speed
136 static GC draw_gc, erase_gc;
138 /* The normal (and max) width for a graph bar */
141 #define min(a,b) ((a)<(b)?(a):(b))
142 #define max(a,b) ((a)>(b)?(a):(b))
145 init_balls (Display *dpy, Window window)
148 XWindowAttributes xgwa;
150 int xlim, ylim, midx, midy, r, vx, vy;
153 char *mode_str, *graph_mode_str;
155 XGetWindowAttributes (dpy, window, &xgwa);
158 cmap = xgwa.colormap;
161 walls_p = get_boolean_resource ("walls", "Boolean");
163 /* if there aren't walls, don't set a limit on the radius */
164 r = get_integer_resource ("radius", "Integer");
165 if (r <= 0 || (r > min (xlim/2, ylim/2) && walls_p) )
166 r = min (xlim/2, ylim/2) - 50;
168 vx = get_integer_resource ("vx", "Integer");
169 vy = get_integer_resource ("vy", "Integer");
171 npoints = get_integer_resource ("points", "Integer");
173 npoints = 3 + (random () % 5);
174 balls = (struct ball *) malloc (npoints * sizeof (struct ball));
176 no_erase_yet = 1; /* for tail mode fix */
178 segments = get_integer_resource ("segments", "Integer");
179 if (segments < 0) segments = 1;
181 threshold = get_integer_resource ("threshold", "Integer");
182 if (threshold < 0) threshold = 0;
184 delay = get_integer_resource ("delay", "Integer");
185 if (delay < 0) delay = 0;
187 global_size = get_integer_resource ("size", "Integer");
188 if (global_size < 0) global_size = 0;
190 glow_p = get_boolean_resource ("glow", "Boolean");
192 orbit_p = get_boolean_resource ("orbit", "Boolean");
194 maxspeed_p = get_boolean_resource ("maxspeed", "Boolean");
196 cbounce_p = get_boolean_resource ("cbounce", "Boolean");
198 color_shift = get_integer_resource ("colorShift", "Integer");
199 if (color_shift <= 0) color_shift = 5;
201 /*flip mods for mouse interaction*/
202 mouse_p = get_boolean_resource ("mouse", "Boolean");
203 mouse_mass = get_integer_resource ("mouseSize", "Integer");
204 mouse_mass = mouse_mass * mouse_mass *10;
206 viscosity = get_float_resource ("viscosity", "Float");
208 mode_str = get_string_resource ("mode", "Mode");
209 if (! mode_str) mode = ball_mode;
210 else if (!strcmp (mode_str, "balls")) mode = ball_mode;
211 else if (!strcmp (mode_str, "lines")) mode = line_mode;
212 else if (!strcmp (mode_str, "polygons")) mode = polygon_mode;
213 else if (!strcmp (mode_str, "tails")) mode = tail_mode;
214 else if (!strcmp (mode_str, "splines")) mode = spline_mode;
215 else if (!strcmp (mode_str, "filled-splines"))mode = spline_filled_mode;
218 "%s: mode must be balls, lines, tails, polygons, splines, or\n\
219 filled-splines, not \"%s\"\n",
224 graph_mode_str = get_string_resource ("graphmode", "Mode");
225 if (! graph_mode_str) graph_mode = graph_none;
226 else if (!strcmp (graph_mode_str, "x")) graph_mode = graph_x;
227 else if (!strcmp (graph_mode_str, "y")) graph_mode = graph_y;
228 else if (!strcmp (graph_mode_str, "both")) graph_mode = graph_both;
229 else if (!strcmp (graph_mode_str, "speed")) graph_mode = graph_speed;
230 else if (!strcmp (graph_mode_str, "none")) graph_mode = graph_none;
233 "%s: graphmode must be speed, x, y, both, or none, not \"%s\"\n",
234 progname, graph_mode_str);
238 /* only allocate memory if it is needed */
239 if(graph_mode != graph_none)
241 if(graph_mode == graph_x || graph_mode == graph_both)
242 x_vels = (double *) malloc (npoints * sizeof (double));
243 if(graph_mode == graph_y || graph_mode == graph_both)
244 y_vels = (double *) malloc (npoints * sizeof (double));
245 if(graph_mode == graph_speed)
246 speeds = (double *) malloc (npoints * sizeof (double));
249 if (mode != ball_mode && mode != tail_mode) glow_p = False;
251 if (mode == polygon_mode && npoints < 3)
254 ncolors = get_integer_resource ("colors", "Colors");
255 if (ncolors < 2) ncolors = 2;
256 if (ncolors <= 2) mono_p = True;
267 int H = random() % 360;
270 double V = frand(0.25) + 0.75;
271 colors = (XColor *) malloc(sizeof(*colors) * (ncolors+1));
272 make_color_ramp (dpy, cmap, H, S1, V, H, S2, V, colors, &ncolors,
278 colors = (XColor *) malloc(sizeof(*colors) * (ncolors+1));
279 make_random_colormap (dpy, xgwa.visual, cmap, colors, &ncolors,
280 True, True, False, True);
286 case spline_filled_mode:
288 colors = (XColor *) malloc(sizeof(*colors) * (ncolors+1));
289 make_smooth_colormap (dpy, xgwa.visual, cmap, colors, &ncolors,
297 if (!mono_p && ncolors <= 2)
299 if (colors) free (colors);
304 if (mode != ball_mode)
306 int size = (segments ? segments : 1);
307 point_stack_size = size * (npoints + 1);
308 point_stack = (XPoint *) calloc (point_stack_size, sizeof (XPoint));
312 gcv.line_width = (mode == tail_mode
313 ? (global_size ? global_size : (MAX_SIZE * 2 / 3))
315 gcv.cap_style = (mode == tail_mode ? CapRound : CapButt);
318 gcv.foreground = get_pixel_resource("foreground", "Foreground", dpy, cmap);
320 gcv.foreground = colors[fg_index].pixel;
321 draw_gc = XCreateGC (dpy, window, GCForeground|GCLineWidth|GCCapStyle, &gcv);
323 gcv.foreground = get_pixel_resource("background", "Background", dpy, cmap);
324 erase_gc = XCreateGC (dpy, window, GCForeground|GCLineWidth|GCCapStyle,&gcv);
327 #define rand_size() min (MAX_SIZE, 8 + (random () % (MAX_SIZE - 9)))
329 if (orbit_p && !global_size)
330 /* To orbit, all objects must be the same mass, or the math gets
332 global_size = rand_size ();
334 th = frand (M_PI+M_PI);
335 for (i = 0; i < npoints; i++)
337 int new_size = (global_size ? global_size : rand_size ());
340 balls [i].size = new_size;
341 balls [i].mass = (new_size * new_size * 10);
342 balls [i].x = midx + r * cos (i * ((M_PI+M_PI) / npoints) + th);
343 balls [i].y = midy + r * sin (i * ((M_PI+M_PI) / npoints) + th);
346 balls [i].vx = vx ? vx : ((6.0 - (random () % 11)) / 8.0);
347 balls [i].vy = vy ? vy : ((6.0 - (random () % 11)) / 8.0);
349 if (mono_p || mode != ball_mode)
350 balls [i].pixel_index = -1;
352 balls [i].pixel_index = 0;
354 balls [i].pixel_index = random() % ncolors;
357 /* This lets modes where the points don't really have any size use the whole
358 window. Otherwise, since the points still have a positive size
359 assigned to them, they will be bounced somewhat early. Mass and size are
360 seperate, so this shouldn't cause problems. It's a bit kludgy, tho.
362 if(mode == line_mode || mode == spline_mode ||
363 mode == spline_filled_mode || mode == polygon_mode)
365 for(i = 1; i < npoints; i++)
375 double v_mult = get_float_resource ("vMult", "Float");
376 if (v_mult == 0.0) v_mult = 1.0;
378 for (i = 1; i < npoints; i++)
380 double _2ipi_n = (2 * i * M_PI / npoints);
381 double x = r * cos (_2ipi_n);
382 double y = r * sin (_2ipi_n);
383 double distx = r - x;
384 double dist2 = (distx * distx) + (y * y);
385 double dist = sqrt (dist2);
386 double a1 = ((balls[i].mass / dist2) *
387 ((dist < threshold) ? -1.0 : 1.0) *
393 fprintf (stderr, "%s: domain error: forces on balls too great\n",
397 v = sqrt (a * r) * v_mult;
398 for (i = 0; i < npoints; i++)
400 double k = ((2 * i * M_PI / npoints) + th);
401 balls [i].vx = -v * sin (k);
402 balls [i].vy = v * cos (k);
406 if (mono_p) glow_p = False;
408 XClearWindow (dpy, window);
412 compute_force (int i, double *dx_ret, double *dy_ret)
415 double x_dist, y_dist, dist, dist2;
418 for (j = 0; j < npoints; j++)
420 if (i == j) continue;
421 x_dist = balls [j].x - balls [i].x;
422 y_dist = balls [j].y - balls [i].y;
423 dist2 = (x_dist * x_dist) + (y_dist * y_dist);
426 if (dist > 0.1) /* the balls are not overlapping */
428 double new_acc = ((balls[j].mass / dist2) *
429 ((dist < threshold) ? -1.0 : 1.0));
430 double new_acc_dist = new_acc / dist;
431 *dx_ret += new_acc_dist * x_dist;
432 *dy_ret += new_acc_dist * y_dist;
435 { /* the balls are overlapping; move randomly */
436 *dx_ret += (frand (10.0) - 5.0);
437 *dy_ret += (frand (10.0) - 5.0);
443 x_dist = mouse_x - balls [i].x;
444 y_dist = mouse_y - balls [i].y;
445 dist2 = (x_dist * x_dist) + (y_dist * y_dist);
448 if (dist > 0.1) /* the balls are not overlapping */
450 double new_acc = ((mouse_mass / dist2) *
451 ((dist < threshold) ? -1.0 : 1.0));
452 double new_acc_dist = new_acc / dist;
453 *dx_ret += new_acc_dist * x_dist;
454 *dy_ret += new_acc_dist * y_dist;
457 { /* the balls are overlapping; move randomly */
458 *dx_ret += (frand (10.0) - 5.0);
459 *dy_ret += (frand (10.0) - 5.0);
465 /* Draws meters along the diagonal for the x velocity */
467 draw_meter_x(Display *dpy, Window window, GC draw_gc,
468 struct ball *balls, int i, int alone)
470 XWindowAttributes xgwa;
472 XGetWindowAttributes (dpy, window, &xgwa);
474 /* set the width of the bars to use */
475 if(xgwa.height < BAR_SIZE*npoints)
477 y = i*(xgwa.height/npoints);
478 h = (xgwa.height/npoints) - 2;
502 w1 = (int)(20*x_vels[i]);
503 w2 = (int)(20*balls[i].vx);
504 x_vels[i] = balls[i].vx;
514 XDrawRectangle(dpy,window,erase_gc,x1+(h+2)/2,y,w1,h);
515 XDrawRectangle(dpy,window,draw_gc,x2+(h+2)/2,y,w2,h);
518 /* Draws meters along the diagonal for the y velocity.
519 Is there some way to make draw_meter_x and draw_meter_y
520 one function instead of two without making them completely unreadable?
523 draw_meter_y (Display *dpy, Window window, GC draw_gc,
524 struct ball *balls, int i, int alone)
526 XWindowAttributes xgwa;
528 XGetWindowAttributes (dpy, window, &xgwa);
530 if(xgwa.height < BAR_SIZE*npoints){ /*needs to be height still */
531 x = i*(xgwa.height/npoints);
532 w = (xgwa.height/npoints) - 2;
555 h1 = (int)(20*y_vels[i]);
556 h2 = (int)(20*balls[i].vy);
557 y_vels[i] = balls[i].vy;
567 XDrawRectangle(dpy,window,erase_gc,x,y1+(w+2)/2,w,h1);
568 XDrawRectangle(dpy,window,draw_gc,x,y2+(w+2)/2,w,h2);
572 /* Draws meters of the total speed of the balls */
574 draw_meter_speed (Display *dpy, Window window, GC draw_gc,
575 struct ball *balls, int i)
577 XWindowAttributes xgwa;
579 XGetWindowAttributes (dpy, window, &xgwa);
581 if(xgwa.height < BAR_SIZE*npoints)
583 y = i*(xgwa.height/npoints);
584 h = (xgwa.height/npoints) - 2;
597 w1 = (int)(5*speeds[i]);
598 w2 = (int)(5*(balls[i].vy*balls[i].vy+balls[i].vx*balls[i].vx));
599 speeds[i] = balls[i].vy*balls[i].vy+balls[i].vx*balls[i].vx;
601 XDrawRectangle(dpy,window,erase_gc,x1,y,w1,h);
602 XDrawRectangle(dpy,window,draw_gc, x2,y,w2,h);
606 run_balls (Display *dpy, Window window, int total_ticks)
608 int last_point_stack_fp = point_stack_fp;
609 static int tick = 500, xlim, ylim;
610 static Colormap cmap;
612 Window root1, child1; /*flip mods for mouse interaction*/
615 int i, radius = global_size/2;
617 radius = (MAX_SIZE / 3);
619 if(graph_mode != graph_none)
621 if(graph_mode == graph_both)
623 for(i = 0; i < npoints; i++)
625 draw_meter_x(dpy,window,draw_gc, balls, i, 0);
626 draw_meter_y(dpy,window,draw_gc, balls, i, 0);
629 else if(graph_mode == graph_x)
631 for(i = 0; i < npoints; i++)
633 draw_meter_x(dpy,window,draw_gc, balls, i, 1);
636 else if(graph_mode == graph_y)
638 for(i = 0; i < npoints; i++)
640 draw_meter_y(dpy,window,draw_gc, balls, i, 1);
643 else if(graph_mode == graph_speed)
645 for(i = 0; i < npoints; i++)
647 draw_meter_speed(dpy,window,draw_gc, balls, i);
655 XQueryPointer(dpy, window, &root1, &child1,
656 &root_x, &root_y, &mouse_x, &mouse_y, &mask);
661 XWindowAttributes xgwa;
662 XGetWindowAttributes (dpy, window, &xgwa);
666 cmap = xgwa.colormap;
669 /* compute the force of attraction/repulsion among all balls */
670 for (i = 0; i < npoints; i++)
671 compute_force (i, &balls[i].dx, &balls[i].dy);
673 /* move the balls according to the forces now in effect */
674 for (i = 0; i < npoints; i++)
676 double old_x = balls[i].x;
677 double old_y = balls[i].y;
679 int size = balls[i].size;
680 balls[i].vx += balls[i].dx;
681 balls[i].vy += balls[i].dy;
683 /* "don't let them get too fast: impose a terminal velocity
684 (actually, make the medium have friction)"
685 Well, what this first step really does is give the medium a
686 viscosity of .9 for balls going over the speed limit. Anyway,
689 if (balls[i].vx > 10 && maxspeed_p)
694 else if (viscosity != 1)
696 balls[i].vx *= viscosity;
699 if (balls[i].vy > 10 && maxspeed_p)
704 else if (viscosity != 1)
706 balls[i].vy *= viscosity;
709 balls[i].x += balls[i].vx;
710 balls[i].y += balls[i].vy;
713 /* bounce off the walls if desired
714 note: a ball is actually its upper left corner */
717 if(cbounce_p) /* with correct bouncing */
719 /* so long as it's out of range, keep bouncing */
721 while( (balls[i].x >= (xlim - balls[i].size)) ||
722 (balls[i].y >= (ylim - balls[i].size)) ||
726 if (balls[i].x >= (xlim - balls[i].size))
728 balls[i].x = (2*(xlim - balls[i].size) - balls[i].x);
729 balls[i].vx = -balls[i].vx;
731 if (balls[i].y >= (ylim - balls[i].size))
733 balls[i].y = (2*(ylim - balls[i].size) - balls[i].y);
734 balls[i].vy = -balls[i].vy;
738 balls[i].x = -balls[i].x;
739 balls[i].vx = -balls[i].vx;
743 balls[i].y = -balls[i].y;
744 balls[i].vy = -balls[i].vy;
748 else /* with old bouncing */
750 if (balls[i].x >= (xlim - balls[i].size))
752 balls[i].x = (xlim - balls[i].size - 1);
753 if (balls[i].vx > 0) /* why is this check here? */
754 balls[i].vx = -balls[i].vx;
756 if (balls[i].y >= (ylim - balls[i].size))
758 balls[i].y = (ylim - balls[i].size - 1);
760 balls[i].vy = -balls[i].vy;
766 balls[i].vx = -balls[i].vx;
772 balls[i].vy = -balls[i].vy;
781 if (mode == ball_mode)
785 /* make color saturation be related to particle
789 double vx = balls [i].dx;
790 double vy = balls [i].dy;
791 if (vx < 0) vx = -vx;
792 if (vy < 0) vy = -vy;
794 if (fraction > limit) fraction = limit;
796 s = 1 - (fraction / limit);
797 balls[i].pixel_index = (ncolors * s);
799 XSetForeground (dpy, draw_gc,
800 colors[balls[i].pixel_index].pixel);
804 if (mode == ball_mode)
806 XFillArc (dpy, window, erase_gc, (int) old_x, (int) old_y,
807 size, size, 0, 360*64);
808 XFillArc (dpy, window, draw_gc, (int) new_x, (int) new_y,
809 size, size, 0, 360*64);
813 point_stack [point_stack_fp].x = new_x;
814 point_stack [point_stack_fp].y = new_y;
819 /* draw the lines or polygons after computing all points */
820 if (mode != ball_mode)
822 point_stack [point_stack_fp].x = balls [0].x; /* close the polygon */
823 point_stack [point_stack_fp].y = balls [0].y;
825 if (point_stack_fp == point_stack_size)
827 else if (point_stack_fp > point_stack_size) /* better be aligned */
832 if (tick++ == color_shift)
835 fg_index = (fg_index + 1) % ncolors;
836 XSetForeground (dpy, draw_gc, colors[fg_index].pixel);
847 XDrawLines (dpy, window, erase_gc, point_stack + point_stack_fp,
848 npoints + 1, CoordModeOrigin);
849 XDrawLines (dpy, window, draw_gc, point_stack + last_point_stack_fp,
850 npoints + 1, CoordModeOrigin);
854 XFillPolygon (dpy, window, erase_gc, point_stack + point_stack_fp,
855 npoints + 1, (npoints == 3 ? Convex : Complex),
857 XFillPolygon (dpy, window, draw_gc, point_stack + last_point_stack_fp,
858 npoints + 1, (npoints == 3 ? Convex : Complex),
863 for (i = 0; i < npoints; i++)
865 int index = point_stack_fp + i;
866 int next_index = (index + (npoints + 1)) % point_stack_size;
867 if(no_erase_yet == 1)
869 if(total_ticks >= segments)
872 XDrawLine (dpy, window, erase_gc,
873 point_stack [index].x + radius,
874 point_stack [index].y + radius,
875 point_stack [next_index].x + radius,
876 point_stack [next_index].y + radius);
881 XDrawLine (dpy, window, erase_gc,
882 point_stack [index].x + radius,
883 point_stack [index].y + radius,
884 point_stack [next_index].x + radius,
885 point_stack [next_index].y + radius);
887 index = last_point_stack_fp + i;
888 next_index = (index - (npoints + 1)) % point_stack_size;
889 if (next_index < 0) next_index += point_stack_size;
890 if (point_stack [next_index].x == 0 &&
891 point_stack [next_index].y == 0)
893 XDrawLine (dpy, window, draw_gc,
894 point_stack [index].x + radius,
895 point_stack [index].y + radius,
896 point_stack [next_index].x + radius,
897 point_stack [next_index].y + radius);
902 case spline_filled_mode:
904 static spline *s = 0;
905 if (! s) s = make_spline (npoints);
908 for (i = 0; i < npoints; i++)
910 s->control_x [i] = point_stack [point_stack_fp + i].x;
911 s->control_y [i] = point_stack [point_stack_fp + i].y;
913 compute_closed_spline (s);
914 if (mode == spline_filled_mode)
915 XFillPolygon (dpy, window, erase_gc, s->points, s->n_points,
916 (s->n_points == 3 ? Convex : Complex),
919 XDrawLines (dpy, window, erase_gc, s->points, s->n_points,
922 for (i = 0; i < npoints; i++)
924 s->control_x [i] = point_stack [last_point_stack_fp + i].x;
925 s->control_y [i] = point_stack [last_point_stack_fp + i].y;
927 compute_closed_spline (s);
928 if (mode == spline_filled_mode)
929 XFillPolygon (dpy, window, draw_gc, s->points, s->n_points,
930 (s->n_points == 3 ? Convex : Complex),
933 XDrawLines (dpy, window, draw_gc, s->points, s->n_points,
945 char *progclass = "Attraction";
947 char *defaults [] = {
948 ".background: black",
949 ".foreground: white",
971 XrmOptionDescRec options [] = {
972 { "-mode", ".mode", XrmoptionSepArg, 0 },
973 { "-graphmode", ".graphmode", XrmoptionSepArg, 0 },
974 { "-colors", ".colors", XrmoptionSepArg, 0 },
975 { "-points", ".points", XrmoptionSepArg, 0 },
976 { "-color-shift", ".colorShift", XrmoptionSepArg, 0 },
977 { "-threshold", ".threshold", XrmoptionSepArg, 0 },
978 { "-segments", ".segments", XrmoptionSepArg, 0 },
979 { "-delay", ".delay", XrmoptionSepArg, 0 },
980 { "-size", ".size", XrmoptionSepArg, 0 },
981 { "-radius", ".radius", XrmoptionSepArg, 0 },
982 { "-vx", ".vx", XrmoptionSepArg, 0 },
983 { "-vy", ".vy", XrmoptionSepArg, 0 },
984 { "-vmult", ".vMult", XrmoptionSepArg, 0 },
985 { "-mouse-size", ".mouseSize", XrmoptionSepArg, 0 },
986 { "-viscosity", ".viscosity", XrmoptionSepArg, 0 },
987 { "-mouse", ".mouse", XrmoptionNoArg, "true" },
988 { "-nomouse", ".mouse", XrmoptionNoArg, "false" },
989 { "-glow", ".glow", XrmoptionNoArg, "true" },
990 { "-noglow", ".glow", XrmoptionNoArg, "false" },
991 { "-orbit", ".orbit", XrmoptionNoArg, "true" },
992 { "-nowalls", ".walls", XrmoptionNoArg, "false" },
993 { "-walls", ".walls", XrmoptionNoArg, "true" },
994 { "-nomaxspeed", ".maxspeed", XrmoptionNoArg, "false" },
995 { "-maxspeed", ".maxspeed", XrmoptionNoArg, "true" },
996 { "-correct-bounce", ".cbounce", XrmoptionNoArg, "false" },
997 { "-fast-bounce", ".cbounce", XrmoptionNoArg, "true" },
1002 screenhack (Display *dpy, Window window)
1004 /* for tail mode fix */
1005 int total_ticks = 0;
1007 init_balls (dpy, window);
1011 run_balls (dpy, window, total_ticks);
1012 screenhack_handle_events (dpy);