X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=hacks%2Fattraction.c;h=4fe22c237e00c6147b557af5dd0ad29d418eb790;hb=39809ded547bdbb08207d3e514950425215b4410;hp=0f0e5e73a8f01ca7287dd16c57152a51c6acef8a;hpb=49f5b54f312fe4ac2e9bc47581a72451bd0e8439;p=xscreensaver diff --git a/hacks/attraction.c b/hacks/attraction.c index 0f0e5e73..4fe22c23 100644 --- a/hacks/attraction.c +++ b/hacks/attraction.c @@ -1,5 +1,4 @@ -/* xscreensaver, Copyright (c) 1992, 1995, 1996, 1997, 1998, 2001, 2006 - * Jamie Zawinski +/* xscreensaver, Copyright (c) 1992-2013 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -12,8 +11,8 @@ /* Simulation of a pair of quasi-gravitational fields, maybe sorta kinda a little like the strong and weak electromagnetic forces. Derived from - a Lispm screensaver by John Pezaris . Mouse control and - viscosity added by "Philip Edward Cutone, III" . + a Lispm screensaver by John Pezaris . Viscosity added by + Philip Edward Cutone, III . John sez: @@ -136,12 +135,11 @@ struct state { int color_shift; int xlim, ylim; Bool no_erase_yet; /* for tail mode fix */ - - /*flip mods for mouse interaction*/ - Bool mouse_p; - int mouse_x, mouse_y, mouse_mass, root_x, root_y; double viscosity; + int mouse_ball; /* index of ball being dragged, or 0 if none. */ + unsigned long mouse_pixel; + enum object_mode mode; enum graph_mode graph_mode; @@ -164,6 +162,7 @@ attraction_init (Display *dpy, Window window) double th; Colormap cmap; char *mode_str, *graph_mode_str; + double size_scale; XGetWindowAttributes (dpy, window, &xgwa); st->xlim = xgwa.width; @@ -211,11 +210,6 @@ attraction_init (Display *dpy, Window window) st->color_shift = get_integer_resource (dpy, "colorShift", "Integer"); if (st->color_shift <= 0) st->color_shift = 5; - /*flip mods for mouse interaction*/ - st->mouse_p = get_boolean_resource (dpy, "mouse", "Boolean"); - st->mouse_mass = get_integer_resource (dpy, "mouseSize", "Integer"); - st->mouse_mass = st->mouse_mass * st->mouse_mass *10; - st->viscosity = get_float_resource (dpy, "viscosity", "Float"); mode_str = get_string_resource (dpy, "mode", "Mode"); @@ -282,14 +276,16 @@ attraction_init (Display *dpy, Window window) double S2 = 1.00; double V = frand(0.25) + 0.75; st->colors = (XColor *) malloc(sizeof(*st->colors) * (st->ncolors+1)); - make_color_ramp (dpy, cmap, H, S1, V, H, S2, V, st->colors, &st->ncolors, + make_color_ramp (xgwa.screen, xgwa.visual, cmap, + H, S1, V, H, S2, V, st->colors, &st->ncolors, False, True, False); } else { st->ncolors = st->npoints; st->colors = (XColor *) malloc(sizeof(*st->colors) * (st->ncolors+1)); - make_random_colormap (dpy, xgwa.visual, cmap, st->colors, &st->ncolors, + make_random_colormap (xgwa.screen, xgwa.visual, cmap, + st->colors, &st->ncolors, True, True, False, True); } break; @@ -299,7 +295,8 @@ attraction_init (Display *dpy, Window window) case spline_filled_mode: case tail_mode: st->colors = (XColor *) malloc(sizeof(*st->colors) * (st->ncolors+1)); - make_smooth_colormap (dpy, xgwa.visual, cmap, st->colors, &st->ncolors, + make_smooth_colormap (xgwa.screen, xgwa.visual, cmap, + st->colors, &st->ncolors, True, False, True); break; default: @@ -314,6 +311,10 @@ attraction_init (Display *dpy, Window window) mono_p = True; } + st->mouse_pixel = + get_pixel_resource (dpy, cmap, "mouseForeground", "MouseForeground"); + st->mouse_ball = -1; + if (st->mode != ball_mode) { int size = (st->segments ? st->segments : 1); @@ -337,13 +338,17 @@ attraction_init (Display *dpy, Window window) st->erase_gc = XCreateGC (dpy, window, GCForeground|GCLineWidth|GCCapStyle,&gcv); -#ifdef HAVE_COCOA +#ifdef HAVE_JWXYZ jwxyz_XSetAntiAliasing (dpy, st->draw_gc, False); jwxyz_XSetAntiAliasing (dpy, st->erase_gc, False); #endif + size_scale = 3; + if (xgwa.width < 100 || xgwa.height < 100) /* tiny windows */ + size_scale = 0.75; + /* let's make the balls bigger by default */ -#define rand_size() (3 * (8 + (random () % 7))) +#define rand_size() (size_scale * (8 + (random () % 7))) if (st->orbit_p && !st->global_size) /* To orbit, all objects must be the same mass, or the math gets @@ -460,28 +465,6 @@ compute_force (struct state *st, int i, double *dx_ret, double *dy_ret) *dy_ret += (frand (10.0) - 5.0); } } - - if (st->mouse_p) - { - x_dist = st->mouse_x - st->balls [i].x; - y_dist = st->mouse_y - st->balls [i].y; - dist2 = (x_dist * x_dist) + (y_dist * y_dist); - dist = sqrt (dist2); - - if (dist > 0.1) /* the balls are not overlapping */ - { - double new_acc = ((st->mouse_mass / dist2) * - ((dist < st->threshold) ? -1.0 : 1.0)); - double new_acc_dist = new_acc / dist; - *dx_ret += new_acc_dist * x_dist; - *dy_ret += new_acc_dist * y_dist; - } - else - { /* the balls are overlapping; move randomly */ - *dx_ret += (frand (10.0) - 5.0); - *dy_ret += (frand (10.0) - 5.0); - } - } } @@ -622,15 +605,33 @@ draw_meter_speed (Display *dpy, Window window, struct state *st, int i) XDrawRectangle(dpy,window,st->draw_gc, x2,y,w2,h); } +/* Returns the position of the mouse relative to the root window. + */ +static void +query_mouse (Display *dpy, Window win, int *x, int *y) +{ + Window root1, child1; + int mouse_x, mouse_y, root_x, root_y; + unsigned int mask; + if (XQueryPointer (dpy, win, &root1, &child1, + &root_x, &root_y, &mouse_x, &mouse_y, &mask)) + { + *x = mouse_x; + *y = mouse_y; + } + else + { + *x = -9999; + *y = -9999; + } +} + static unsigned long attraction_draw (Display *dpy, Window window, void *closure) { struct state *st = (struct state *) closure; int last_point_stack_fp = st->point_stack_fp; - Window root1, child1; /*flip mods for mouse interaction*/ - unsigned int mask; - int i, radius = st->global_size/2; st->total_ticks++; @@ -672,12 +673,6 @@ attraction_draw (Display *dpy, Window window, void *closure) } - if (st->mouse_p) - { - XQueryPointer(dpy, window, &root1, &child1, - &st->root_x, &st->root_y, &st->mouse_x, &st->mouse_y, &mask); - } - /* compute the force of attraction/repulsion among all balls */ for (i = 0; i < st->npoints; i++) compute_force (st, i, &st->balls[i].dx, &st->balls[i].dy); @@ -689,6 +684,7 @@ attraction_draw (Display *dpy, Window window, void *closure) double old_y = st->balls[i].y; double new_x, new_y; int size = st->balls[i].size; + st->balls[i].vx += st->balls[i].dx; st->balls[i].vy += st->balls[i].dy; @@ -729,12 +725,17 @@ attraction_draw (Display *dpy, Window window, void *closure) if(st->cbounce_p) /* with correct bouncing */ { /* so long as it's out of range, keep bouncing */ + /* limit the maximum number to bounce to 4.*/ + int bounce_allowed = 4; - while( (st->balls[i].x >= (st->xlim - st->balls[i].size)) || + while( bounce_allowed && ( + (st->balls[i].x >= (st->xlim - st->balls[i].size)) || (st->balls[i].y >= (st->ylim - st->balls[i].size)) || (st->balls[i].x <= 0) || (st->balls[i].y <= 0) ) + ) { + bounce_allowed--; if (st->balls[i].x >= (st->xlim - st->balls[i].size)) { st->balls[i].x = (2*(st->xlim - st->balls[i].size) - st->balls[i].x); @@ -785,6 +786,21 @@ attraction_draw (Display *dpy, Window window, void *closure) } } } + + if (i == st->mouse_ball) + { + int x, y; + query_mouse (dpy, window, &x, &y); + if (st->mode == ball_mode) + { + x -= st->balls[i].size / 2; + y -= st->balls[i].size / 2; + } + + st->balls[i].x = x; + st->balls[i].y = y; + } + new_x = st->balls[i].x; new_y = st->balls[i].y; @@ -809,7 +825,9 @@ attraction_draw (Display *dpy, Window window, void *closure) st->balls[i].pixel_index = (st->ncolors * s); } XSetForeground (dpy, st->draw_gc, - st->colors[st->balls[i].pixel_index].pixel); + (i == st->mouse_ball + ? st->mouse_pixel + : st->colors[st->balls[i].pixel_index].pixel)); } } @@ -963,6 +981,53 @@ attraction_reshape (Display *dpy, Window window, void *closure, static Bool attraction_event (Display *dpy, Window window, void *closure, XEvent *event) { + struct state *st = (struct state *) closure; + + if (event->xany.type == ButtonPress) + { + int i; + if (st->mouse_ball != -1) /* second down-click? drop the ball. */ + { + st->mouse_ball = -1; + return True; + } + else + { + /* When trying to pick up a ball, first look for a click directly + inside the ball; but if we don't find it, expand the radius + outward until we find something nearby. + */ + int x = event->xbutton.x; + int y = event->xbutton.y; + float max = 10 * (st->global_size ? st->global_size : MAX_SIZE); + float step = max / 100; + float r2; + for (r2 = step; r2 < max; r2 += step) + { + for (i = 0; i < st->npoints; i++) + { + float d = ((st->balls[i].x - x) * (st->balls[i].x - x) + + (st->balls[i].y - y) * (st->balls[i].y - y)); + float r = st->balls[i].size; + if (r2 > r) r = r2; + if (d < r*r) + { + st->mouse_ball = i; + return True; + } + } + } + } + return True; + } + else if (event->xany.type == ButtonRelease) /* drop the ball */ + { + st->mouse_ball = -1; + return True; + } + + + return False; } @@ -986,6 +1051,7 @@ attraction_free (Display *dpy, Window window, void *closure) static const char *attraction_defaults [] = { ".background: black", ".foreground: white", + "*fpsSolid: true", "*mode: balls", "*graphmode: none", "*points: 0", @@ -994,12 +1060,10 @@ static const char *attraction_defaults [] = { "*threshold: 200", "*delay: 10000", "*glow: false", - "*mouseSize: 10", "*walls: true", "*maxspeed: true", "*cbounce: true", - "*mouse: false", - "*viscosity: 1", + "*viscosity: 1.0", "*orbit: false", "*colorShift: 3", "*segments: 500", @@ -1007,6 +1071,10 @@ static const char *attraction_defaults [] = { "*radius: 0", "*vx: 0", "*vy: 0", + "*mouseForeground: white", +#ifdef HAVE_MOBILE + "*ignoreRotation: True", +#endif 0 }; @@ -1024,10 +1092,7 @@ static XrmOptionDescRec attraction_options [] = { { "-vx", ".vx", XrmoptionSepArg, 0 }, { "-vy", ".vy", XrmoptionSepArg, 0 }, { "-vmult", ".vMult", XrmoptionSepArg, 0 }, - { "-mouse-size", ".mouseSize", XrmoptionSepArg, 0 }, { "-viscosity", ".viscosity", XrmoptionSepArg, 0 }, - { "-mouse", ".mouse", XrmoptionNoArg, "true" }, - { "-nomouse", ".mouse", XrmoptionNoArg, "false" }, { "-glow", ".glow", XrmoptionNoArg, "true" }, { "-noglow", ".glow", XrmoptionNoArg, "false" }, { "-orbit", ".orbit", XrmoptionNoArg, "true" },