source: trunk/third/xscreensaver/hacks/fluidballs.c @ 20148

Revision 20148, 23.4 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r20147, which included commits to RCS files with non-trunk default branches.
Line 
1/* fluidballs, Copyright (c) 2000 by Peter Birtles <peter@bqdesign.com.au>
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 * Ported to X11 and xscreensaver by jwz, 27-Feb-2002.
12 *
13 * http://astronomy.swin.edu.au/~pbourke/modelling/fluid/
14 *
15 * Some physics improvements by Steven Barker <steve@blckknght.org>
16 */
17
18/* Future ideas:
19 * Specifying a distribution in the ball sizes (with a gamma curve, possibly).
20 * Brownian motion, for that extra touch of realism.
21 *
22 * It would be nice to detect when there are more balls than fit in
23 * the window, and scale the number of balls back.  Useful for the
24 * xscreensaver-demo preview, which is often too tight by default.
25 */
26
27#include <math.h>
28#include "screenhack.h"
29#include <stdio.h>
30
31#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
32#include "xdbe.h"
33#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
34
35typedef struct {
36  Display *dpy;
37  Window window;
38  XWindowAttributes xgwa;
39
40  Pixmap b, ba; /* double-buffer to reduce flicker */
41#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
42  XdbeBackBuffer backb;
43#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
44
45  GC draw_gc;           /* most of the balls */
46  GC draw_gc2;          /* the ball being dragged with the mouse */
47  GC erase_gc;
48  XColor fg;
49  XColor fg2;
50
51  int count;            /* number of balls */
52  float xmin, ymin;     /* rectangle of window, relative to root */
53  float xmax, ymax;
54
55  int mouse_ball;       /* index of ball being dragged, or 0 if none. */
56
57  float tc;             /* time constant (time-warp multiplier) */
58  float accx;           /* horizontal acceleration (wind) */
59  float accy;           /* vertical acceleration (gravity) */
60
61  float *vx,  *vy;      /* current ball velocities */
62  float *px,  *py;      /* current ball positions */
63  float *opx, *opy;     /* previous ball positions */
64  float *r;             /* ball radiuses */
65
66  float *m;             /* ball mass, precalculated */
67  float e;              /* coeficient of elasticity */
68  float max_radius;     /* largest radius of any ball */
69
70  Bool random_sizes_p;  /* Whether balls should be various sizes up to max. */
71  Bool shake_p;         /* Whether to mess with gravity when things settle. */
72  Bool dbuf;            /* Whether we're using double buffering. */
73  Bool dbeclear_p;      /* ? */
74  float shake_threshold;
75  int time_since_shake;
76
77  Bool fps_p;           /* Whether to draw some text at the bottom. */
78  GC font_gc;
79  int font_height;
80  int font_baseline;
81  int frame_count;
82  int collision_count;
83  char fps_str[1024];
84 
85} b_state;
86
87
88/* Draws the frames per second string */
89static void
90draw_fps_string (b_state *state)
91
92  XFillRectangle (state->dpy, state->b, state->erase_gc,
93                  0, state->xgwa.height - state->font_height,
94                  state->xgwa.width, state->font_height);
95  XDrawImageString (state->dpy, state->b, state->font_gc,
96                    0, state->xgwa.height - state->font_baseline,
97                    state->fps_str, strlen(state->fps_str));
98}
99
100/* Finds the origin of the window relative to the root window, by
101   walking up the window tree until it reaches the top.
102 */
103static void
104window_origin (Display *dpy, Window window, int *x, int *y)
105{
106  Window root, parent, *kids;
107  unsigned int nkids;
108  XWindowAttributes xgwa;
109  int wx, wy;
110  XGetWindowAttributes (dpy, window, &xgwa);
111
112  wx = xgwa.x;
113  wy = xgwa.y;
114
115  kids = 0;
116  *x = 0;
117  *y = 0;
118
119  if (XQueryTree (dpy, window, &root, &parent, &kids, &nkids))
120    {
121      if (parent && parent != root)
122        {
123          int px, py;
124          window_origin (dpy, parent, &px, &py);
125          wx += px;
126          wy += py;
127        }
128    }
129  if (kids) XFree (kids);
130  *x = wx;
131  *y = wy;
132}
133
134
135/* Queries the window position to see if the window has moved or resized.
136   We poll this instead of waiting for ConfigureNotify events, because
137   when the window manager moves the window, only one ConfigureNotify
138   comes in: at the end of the motion.  If we poll, we can react to the
139   new position while the window is still being moved.  (Assuming the WM
140   does OpaqueMove, of course.)
141 */
142static void
143check_window_moved (b_state *state)
144{
145  float oxmin = state->xmin;
146  float oxmax = state->xmax;
147  float oymin = state->ymin;
148  float oymax = state->ymax;
149  int wx, wy;
150  XGetWindowAttributes (state->dpy, state->window, &state->xgwa);
151  window_origin (state->dpy, state->window, &wx, &wy);
152  state->xmin = wx;
153  state->ymin = wy;
154  state->xmax = state->xmin + state->xgwa.width;
155  state->ymax = state->ymin + state->xgwa.height - state->font_height;
156
157  if (state->dbuf && (state->ba))
158    {
159      if (oxmax != state->xmax || oymax != state->ymax)
160        {
161          XFreePixmap (state->dpy, state->ba);
162          state->ba = XCreatePixmap (state->dpy, state->window,
163                                     state->xgwa.width, state->xgwa.height,
164                                     state->xgwa.depth);
165          XFillRectangle (state->dpy, state->ba, state->erase_gc, 0, 0,
166                          state->xgwa.width, state->xgwa.height);
167          state->b = state->ba;
168        }
169    }
170  else
171    {
172      /* Only need to erase the window if the origin moved */
173      if (oxmin != state->xmin || oymin != state->ymin)
174        XClearWindow (state->dpy, state->window);
175      else if (state->fps_p && oymax != state->ymax)
176        XFillRectangle (state->dpy, state->b, state->erase_gc,
177                        0, state->xgwa.height - state->font_height,
178                        state->xgwa.width, state->font_height);
179    }
180}
181
182
183/* Returns the position of the mouse relative to the root window.
184 */
185static void
186query_mouse (b_state *state, int *x, int *y)
187{
188  Window root1, child1;
189  int mouse_x, mouse_y, root_x, root_y;
190  unsigned int mask;
191  if (XQueryPointer (state->dpy, state->window, &root1, &child1,
192                     &root_x, &root_y, &mouse_x, &mouse_y, &mask))
193    {
194      *x = root_x;
195      *y = root_y;
196    }
197  else
198    {
199      *x = -9999;
200      *y = -9999;
201    }
202}
203
204/* Re-pick the colors of the balls, and the mouse-ball.
205 */
206static void
207recolor (b_state *state)
208{
209  if (state->fg.flags)
210    XFreeColors (state->dpy, state->xgwa.colormap, &state->fg.pixel, 1, 0);
211  if (state->fg2.flags)
212    XFreeColors (state->dpy, state->xgwa.colormap, &state->fg2.pixel, 1, 0);
213
214  state->fg.flags  = DoRed|DoGreen|DoBlue;
215  state->fg.red    = 0x8888 + (random() % 0x8888);
216  state->fg.green  = 0x8888 + (random() % 0x8888);
217  state->fg.blue   = 0x8888 + (random() % 0x8888);
218
219  state->fg2.flags = DoRed|DoGreen|DoBlue;
220  state->fg2.red   = 0x8888 + (random() % 0x8888);
221  state->fg2.green = 0x8888 + (random() % 0x8888);
222  state->fg2.blue  = 0x8888 + (random() % 0x8888);
223
224  if (XAllocColor (state->dpy, state->xgwa.colormap, &state->fg))
225    XSetForeground (state->dpy, state->draw_gc,  state->fg.pixel);
226
227  if (XAllocColor (state->dpy, state->xgwa.colormap, &state->fg2))
228    XSetForeground (state->dpy, state->draw_gc2, state->fg2.pixel);
229}
230
231/* Initialize the state structure and various X data.
232 */
233static b_state *
234init_balls (Display *dpy, Window window)
235{
236  int i;
237  float extx, exty;
238  b_state *state = (b_state *) calloc (1, sizeof(*state));
239  XGCValues gcv;
240
241  state->dpy = dpy;
242
243  state->window = window;
244
245  check_window_moved (state);
246
247  state->dbuf = get_boolean_resource ("doubleBuffer", "Boolean");
248  state->dbeclear_p = get_boolean_resource ("useDBEClear", "Boolean");
249
250  if (state->dbuf)
251    {
252#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
253      if (state->dbeclear_p)
254        state->b = xdbe_get_backbuffer (dpy, window, XdbeBackground);
255      else
256        state->b = xdbe_get_backbuffer (dpy, window, XdbeUndefined);
257      state->backb = state->b;
258#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
259
260      if (!state->b)
261        {
262          state->ba = XCreatePixmap (state->dpy, state->window,
263                                     state->xgwa.width, state->xgwa.height,
264                                     state->xgwa.depth);
265          state->b = state->ba;
266        }
267    }
268  else
269    {
270      state->b = state->window;
271    }
272
273  /* Select ButtonRelease events on the external window, if no other app has
274     already selected it (only one app can select it at a time: BadAccess. */
275  if (! (state->xgwa.all_event_masks & ButtonReleaseMask))
276    XSelectInput (state->dpy, state->window,
277                  state->xgwa.your_event_mask | ButtonReleaseMask);
278
279  gcv.foreground = get_pixel_resource("foreground", "Foreground",
280                                      state->dpy, state->xgwa.colormap);
281  gcv.background = get_pixel_resource("background", "Background",
282                                      state->dpy, state->xgwa.colormap);
283  state->draw_gc = XCreateGC (state->dpy, state->b,
284                              GCForeground|GCBackground, &gcv);
285
286  gcv.foreground = get_pixel_resource("mouseForeground", "MouseForeground",
287                                      state->dpy, state->xgwa.colormap);
288  state->draw_gc2 = XCreateGC (state->dpy, state->b,
289                               GCForeground|GCBackground, &gcv);
290
291  gcv.foreground = gcv.background;
292  state->erase_gc = XCreateGC (state->dpy, state->b,
293                               GCForeground|GCBackground, &gcv);
294
295
296  if (state->ba)
297    XFillRectangle (state->dpy, state->ba, state->erase_gc, 0, 0,
298                    state->xgwa.width, state->xgwa.height);
299
300  recolor (state);
301
302  extx = state->xmax - state->xmin;
303  exty = state->ymax - state->ymin;
304
305  state->count = get_integer_resource ("count", "Count");
306  if (state->count < 1) state->count = 20;
307
308  state->max_radius = get_float_resource ("size", "Size") / 2;
309  if (state->max_radius < 1.0) state->max_radius = 1.0;
310
311  state->random_sizes_p = get_boolean_resource ("random", "Random");
312
313  state->accx = get_float_resource ("wind", "Wind");
314  if (state->accx < -1.0 || state->accx > 1.0) state->accx = 0;
315
316  state->accy = get_float_resource ("gravity", "Gravity");
317  if (state->accy < -1.0 || state->accy > 1.0) state->accy = 0.01;
318
319  state->e = get_float_resource ("elasticity", "Elacitcity");
320  if (state->e < 0.2 || state->e > 1.0) state->e = 0.97;
321
322  state->tc = get_float_resource ("timeScale", "TimeScale");
323  if (state->tc <= 0 || state->tc > 10) state->tc = 1.0;
324
325  state->shake_p = get_boolean_resource ("shake", "Shake");
326  state->shake_threshold = get_float_resource ("shakeThreshold",
327                                               "ShakeThreshold");
328
329  state->fps_p = get_boolean_resource ("doFPS", "DoFPS");
330  if (state->fps_p)
331    {
332      XFontStruct *font;
333      char *fontname = get_string_resource ("font", "Font");
334      const char *def_font = "fixed";
335      if (!fontname || !*fontname) fontname = (char *)def_font;
336      font = XLoadQueryFont (dpy, fontname);
337      if (!font) font = XLoadQueryFont (dpy, def_font);
338      if (!font) exit(-1);
339      gcv.font = font->fid;
340      gcv.foreground = get_pixel_resource("textColor", "Foreground",
341                                          state->dpy, state->xgwa.colormap);
342      state->font_gc = XCreateGC(dpy, state->b,
343                                 GCFont|GCForeground|GCBackground, &gcv);
344      state->font_height = font->ascent + font->descent;
345      state->font_baseline = font->descent;
346    }
347
348  state->m   = (float *) malloc (sizeof (*state->m)   * (state->count + 1));
349  state->r   = (float *) malloc (sizeof (*state->r)   * (state->count + 1));
350  state->vx  = (float *) malloc (sizeof (*state->vx)  * (state->count + 1));
351  state->vy  = (float *) malloc (sizeof (*state->vy)  * (state->count + 1));
352  state->px  = (float *) malloc (sizeof (*state->px)  * (state->count + 1));
353  state->py  = (float *) malloc (sizeof (*state->py)  * (state->count + 1));
354  state->opx = (float *) malloc (sizeof (*state->opx) * (state->count + 1));
355  state->opy = (float *) malloc (sizeof (*state->opy) * (state->count + 1));
356
357  for (i=1; i<=state->count; i++)
358    {
359      state->px[i] = frand(extx) + state->xmin;
360      state->py[i] = frand(exty) + state->ymin;
361      state->vx[i] = frand(0.2) - 0.1;
362      state->vy[i] = frand(0.2) - 0.1;
363
364      state->r[i] = (state->random_sizes_p
365                     ? ((0.2 + frand(0.8)) * state->max_radius)
366                     : state->max_radius);
367      /*state->r[i] = pow(frand(1.0), state->sizegamma) * state->max_radius;*/
368
369      /* state->m[i] = pow(state->r[i],2) * M_PI; */
370      state->m[i] = pow(state->r[i],3) * M_PI * 1.3333;
371    }
372
373  memcpy (state->opx, state->px, sizeof (*state->opx) * (state->count + 1));
374  memcpy (state->opy, state->py, sizeof (*state->opx) * (state->count + 1));
375
376  return state;
377}
378
379
380/* Messes with gravity: permute "down" to be in a random direction.
381 */
382static void
383shake (b_state *state)
384{
385  float a = state->accx;
386  float b = state->accy;
387  int i = random() % 4;
388
389  switch (i)
390    {
391    case 0:
392      state->accx = a;
393      state->accy = b;
394      break;
395    case 1:
396      state->accx = -a;
397      state->accy = -b;
398      break;
399    case 2:
400      state->accx = b;
401      state->accy = a;
402      break;
403    case 3:
404      state->accx = -b;
405      state->accy = -a;
406      break;
407    default:
408      abort();
409      break;
410    }
411
412  state->time_since_shake = 0;
413  recolor (state);
414}
415
416
417/* Look at the current time, and update state->time_since_shake.
418   Draw the FPS display if desired.
419 */
420static void
421check_wall_clock (b_state *state, float max_d)
422{
423  static int tick = 0;
424  state->frame_count++;
425 
426  if (tick++ > 20)  /* don't call gettimeofday() too often -- it's slow. */
427    {
428      struct timeval now;
429      static struct timeval last = { 0, 0 };
430# ifdef GETTIMEOFDAY_TWO_ARGS
431      struct timezone tzp;
432      gettimeofday(&now, &tzp);
433# else
434      gettimeofday(&now);
435# endif
436
437      if (last.tv_sec == 0)
438        last = now;
439
440      tick = 0;
441      if (now.tv_sec == last.tv_sec)
442        return;
443
444      state->time_since_shake += (now.tv_sec - last.tv_sec);
445
446      if (state->fps_p)
447        {
448          float elapsed = ((now.tv_sec  + (now.tv_usec  / 1000000.0)) -
449                           (last.tv_sec + (last.tv_usec / 1000000.0)));
450          float fps = state->frame_count / elapsed;
451          float cps = state->collision_count / elapsed;
452         
453          sprintf (state->fps_str,
454                   " FPS: %.2f  Collisions: %.3f/frame  Max motion: %.3f",
455                   fps, cps/fps, max_d);
456         
457          draw_fps_string(state);
458        }
459
460      state->frame_count = 0;
461      state->collision_count = 0;
462      last = now;
463    }
464}
465
466/* Erases the balls at their previous positions, and draws the new ones.
467 */
468static void
469repaint_balls (b_state *state)
470{
471  int a;
472  int x1a, x2a, y1a, y2a;
473  int x1b, x2b, y1b, y2b;
474  float max_d = 0;
475
476  for (a=1; a <= state->count; a++)
477    {
478      GC gc;
479      x1a = (state->opx[a] - state->r[a] - state->xmin);
480      y1a = (state->opy[a] - state->r[a] - state->ymin);
481      x2a = (state->opx[a] + state->r[a] - state->xmin);
482      y2a = (state->opy[a] + state->r[a] - state->ymin);
483
484      x1b = (state->px[a] - state->r[a] - state->xmin);
485      y1b = (state->py[a] - state->r[a] - state->ymin);
486      x2b = (state->px[a] + state->r[a] - state->xmin);
487      y2b = (state->py[a] + state->r[a] - state->ymin);
488
489      if (!state->dbeclear_p
490#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
491          || !state->backb
492#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
493          )
494        {
495/*        if (x1a != x1b || y1a != y1b)   -- leaves turds if we optimize this */
496            {
497              gc = state->erase_gc;
498              XFillArc (state->dpy, state->b, gc,
499                        x1a, y1a, x2a-x1a, y2a-y1a,
500                        0, 360*64);
501            }
502        }
503      if (state->mouse_ball == a)
504        gc = state->draw_gc2;
505      else
506        gc = state->draw_gc;
507
508      XFillArc (state->dpy, state->b, gc,
509                x1b, y1b, x2b-x1b, y2b-y1b,
510                0, 360*64);
511
512      if (state->shake_p)
513        {
514          /* distance this ball moved this frame */
515          float d = ((state->px[a] - state->opx[a]) *
516                     (state->px[a] - state->opx[a]) +
517                     (state->py[a] - state->opy[a]) *
518                     (state->py[a] - state->opy[a]));
519          if (d > max_d) max_d = d;
520        }
521
522      state->opx[a] = state->px[a];
523      state->opy[a] = state->py[a];
524    }
525
526  if (state->fps_p && state->dbeclear_p)
527    draw_fps_string(state);
528
529#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
530  if (state->backb)
531    {
532      XdbeSwapInfo info[1];
533      info[0].swap_window = state->window;
534      info[0].swap_action = (state->dbeclear_p ? XdbeBackground : XdbeUndefined);
535      XdbeSwapBuffers (state->dpy, info, 1);
536    }
537  else
538#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
539  if (state->dbuf)
540    {
541      XCopyArea (state->dpy, state->b, state->window, state->erase_gc,
542                 0, 0, state->xgwa.width, state->xgwa.height, 0, 0);
543    }
544
545  if (state->shake_p && state->time_since_shake > 5)
546    {
547      max_d /= state->max_radius;
548      if (max_d < state->shake_threshold ||  /* when its stable */
549          state->time_since_shake > 30)      /* or when 30 secs has passed */
550        {
551          shake (state);
552        }
553    }
554
555  check_wall_clock (state, max_d);
556}
557
558
559/* Implements the laws of physics: move balls to their new positions.
560 */
561static void
562update_balls (b_state *state)
563{
564  int a, b;
565  float d, vxa, vya, vxb, vyb, dd, cdx, cdy;
566  float ma, mb, vca, vcb, dva, dvb;
567  float dee2;
568
569  check_window_moved (state);
570
571  /* If we're currently tracking the mouse, update that ball first.
572   */
573  if (state->mouse_ball != 0)
574    {
575      int mouse_x, mouse_y;
576      query_mouse (state, &mouse_x, &mouse_y);
577      state->px[state->mouse_ball] = mouse_x;
578      state->py[state->mouse_ball] = mouse_y;
579      state->vx[state->mouse_ball] =
580        (0.1 *
581         (state->px[state->mouse_ball] - state->opx[state->mouse_ball]) *
582         state->tc);
583      state->vy[state->mouse_ball] =
584        (0.1 *
585         (state->py[state->mouse_ball] - state->opy[state->mouse_ball]) *
586         state->tc);
587    }
588
589  /* For each ball, compute the influence of every other ball. */
590  for (a=1; a <= state->count -  1; a++)
591    for (b=a + 1; b <= state->count; b++)
592      {
593         d = ((state->px[a] - state->px[b]) *
594              (state->px[a] - state->px[b]) +
595              (state->py[a] - state->py[b]) *
596              (state->py[a] - state->py[b]));
597         dee2 = (state->r[a] + state->r[b]) *
598                (state->r[a] + state->r[b]);
599         if (d < dee2)
600         {
601            state->collision_count++;
602            d = sqrt(d);
603            dd = state->r[a] + state->r[b] - d;
604
605            cdx = (state->px[b] - state->px[a]) / d;
606            cdy = (state->py[b] - state->py[a]) / d;
607
608            /* Move each ball apart from the other by half the
609             * 'collision' distance.
610             */
611            state->px[a] -= 0.5 * dd * cdx;
612            state->py[a] -= 0.5 * dd * cdy;
613            state->px[b] += 0.5 * dd * cdx;
614            state->py[b] += 0.5 * dd * cdy;
615
616            ma = state->m[a];
617            mb = state->m[b];
618
619            vxa = state->vx[a];
620            vya = state->vy[a];
621            vxb = state->vx[b];
622            vyb = state->vy[b];
623
624            vca = vxa * cdx + vya * cdy; /* the component of each velocity */
625            vcb = vxb * cdx + vyb * cdy; /* along the axis of the collision */
626
627            /* elastic collison */
628            dva = (vca * (ma - mb) + vcb * 2 * mb) / (ma + mb) - vca;
629            dvb = (vcb * (mb - ma) + vca * 2 * ma) / (ma + mb) - vcb;
630
631            dva *= state->e; /* some energy lost to inelasticity */
632            dvb *= state->e;
633
634#if 0
635            dva += (frand (50) - 25) / ma;   /* q: why are elves so chaotic? */
636            dvb += (frand (50) - 25) / mb;   /* a: brownian motion. */
637#endif
638
639            vxa += dva * cdx;
640            vya += dva * cdy;
641            vxb += dvb * cdx;
642            vyb += dvb * cdy;
643
644            state->vx[a] = vxa;
645            state->vy[a] = vya;
646            state->vx[b] = vxb;
647            state->vy[b] = vyb;
648         }
649      }
650
651   /* Force all balls to be on screen.
652    */
653  for (a=1; a <= state->count; a++)
654    {
655      if (state->px[a] <= (state->xmin + state->r[a]))
656        {
657          state->px[a] = state->xmin + state->r[a];
658          state->vx[a] = -state->vx[a] * state->e;
659        }
660      if (state->px[a] >= (state->xmax - state->r[a]))
661        {
662          state->px[a] = state->xmax - state->r[a];
663          state->vx[a] = -state->vx[a] * state->e;
664        }
665      if (state->py[a] <= (state->ymin + state->r[a]))
666        {
667          state->py[a] = state->ymin + state->r[a];
668          state->vy[a] = -state->vy[a] * state->e;
669        }
670      if (state->py[a] >= (state->ymax - state->r[a]))
671        {
672          state->py[a] = state->ymax - state->r[a];
673          state->vy[a] = -state->vy[a] * state->e;
674        }
675    }
676
677  /* Apply gravity to all balls.
678   */
679  for (a=1; a <= state->count; a++)
680    if (a != state->mouse_ball)
681      {
682        state->vx[a] += state->accx * state->tc;
683        state->vy[a] += state->accy * state->tc;
684        state->px[a] += state->vx[a] * state->tc;
685        state->py[a] += state->vy[a] * state->tc;
686      }
687}
688
689
690/* Handle X events, specifically, allow a ball to be picked up with the mouse.
691 */
692static void
693handle_events (b_state *state)
694{
695  XSync (state->dpy, False);
696  while (XPending (state->dpy))
697    {
698      XEvent event;
699      XNextEvent (state->dpy, &event);
700      if (event.xany.type == ButtonPress)
701        {
702          int i;
703          float fmx = event.xbutton.x_root;
704          float fmy = event.xbutton.y_root;
705          if (state->mouse_ball != 0)  /* second down-click?  drop the ball. */
706            {
707              state->mouse_ball = 0;
708              return;
709            }
710          else
711            for (i=1; i <= state->count; i++)
712              {
713                float d = ((state->px[i] - fmx) * (state->px[i] - fmx) +
714                           (state->py[i] - fmy) * (state->py[i] - fmy));
715                float r = state->r[i];
716                if (d < r*r)
717                  {
718                    state->mouse_ball = i;
719                    return;
720                  }
721              }
722        }
723      else if (event.xany.type == ButtonRelease)   /* drop the ball */
724        {
725          state->mouse_ball = 0;
726          return;
727        }
728      else if (event.xany.type == ConfigureNotify)
729        {
730          /* This is redundant, since we poll this every iteration. */
731          check_window_moved (state);
732        }
733
734      screenhack_handle_event (state->dpy, &event);
735    }
736}
737
738
739char *progclass = "FluidBalls";
740
741char *defaults [] = {
742  ".background:         black",
743  ".textColor:          yellow",
744  ".font:               -*-helvetica-*-r-*-*-*-180-*-*-p-*-*-*",
745  "*delay:              10000",
746  "*count:              300",
747  "*size:               25",
748  "*random:             True",
749  "*gravity:            0.01",
750  "*wind:               0.00",
751  "*elasticity:         0.97",
752  "*timeScale:          1.0",
753  "*doFPS:              False",
754  "*shake:              True",
755  "*shakeThreshold:     0.015",
756  "*doubleBuffer:       True",
757#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
758  "*useDBE:             True",
759  "*useDBEClear:        True",
760#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
761  0
762};
763
764XrmOptionDescRec options [] = {
765  { "-delay",           ".delay",       XrmoptionSepArg, 0 },
766  { "-count",           ".count",       XrmoptionSepArg, 0 },
767  { "-size",            ".size",        XrmoptionSepArg, 0 },
768  { "-count",           ".count",       XrmoptionSepArg, 0 },
769  { "-gravity",         ".gravity",     XrmoptionSepArg, 0 },
770  { "-wind",            ".wind",        XrmoptionSepArg, 0 },
771  { "-elasticity",      ".elasticity",  XrmoptionSepArg, 0 },
772  { "-fps",             ".doFPS",       XrmoptionNoArg, "True" },
773  { "-no-fps",          ".doFPS",       XrmoptionNoArg, "False" },
774  { "-shake",           ".shake",       XrmoptionNoArg, "True" },
775  { "-no-shake",        ".shake",       XrmoptionNoArg, "False" },
776  { "-random",          ".random",      XrmoptionNoArg, "True" },
777  { "-nonrandom",       ".random",      XrmoptionNoArg, "False" },
778  { "-db",              ".doubleBuffer", XrmoptionNoArg,  "True" },
779  { "-no-db",           ".doubleBuffer", XrmoptionNoArg,  "False" },
780  { 0, 0, 0, 0 }
781};
782
783void
784screenhack (Display *dpy, Window window)
785{
786  b_state *state = init_balls(dpy, window);
787  int delay = get_integer_resource ("delay", "Integer");
788
789  while (1)
790    {
791      repaint_balls(state);
792      update_balls(state);
793
794      XSync (dpy, False);
795      handle_events (state);
796      if (delay) usleep (delay);
797    }
798}
Note: See TracBrowser for help on using the repository browser.