X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=hacks%2Fglx%2Fgltrackball.c;h=57b4b99131e2ece5b7449166642c8bf620479176;hb=4361b69d3178d7fc98d0388f9a223af6c2651aba;hp=e3c0cd42402fdb781b03a48a0be8e502d4c7f9c9;hpb=6b1c86cf395f59389e4ece4ea8f4bea2c332745b;p=xscreensaver diff --git a/hacks/glx/gltrackball.c b/hacks/glx/gltrackball.c index e3c0cd42..57b4b991 100644 --- a/hacks/glx/gltrackball.c +++ b/hacks/glx/gltrackball.c @@ -1,4 +1,4 @@ -/* gltrackball, Copyright (c) 2002-2008 Jamie Zawinski +/* gltrackball, Copyright (c) 2002-2017 Jamie Zawinski * GL-flavored wrapper for trackball.c * * Permission to use, copy, modify, distribute, and sell this software and its @@ -12,37 +12,105 @@ #include #include +#include #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_COCOA -# include -#else +# include "jwxyz.h" +#elif defined(HAVE_ANDROID) +# include "jwxyz.h" +# include +#else /* real X11 */ +# include +# include # include -#endif +#endif /* !HAVE_COCOA */ + +#ifdef HAVE_JWZGLES +# include "jwzgles.h" +#endif /* HAVE_JWZGLES */ + +# define Button4 4 /* WTF */ +# define Button5 5 +# define Button6 6 +# define Button7 7 #include "trackball.h" #include "gltrackball.h" +#if defined(USE_IPHONE) || defined(HAVE_ANDROID) + /* Surely this should be defined somewhere more centrally... */ +# define HAVE_MOBILE +#endif + +/* Bah, copied from ../fps.h */ +#ifdef HAVE_MOBILE + extern double current_device_rotation (void); +#else +# define current_device_rotation() (0) +#endif + + struct trackball_state { - int x, y; + int ow, oh; + double x, y; + double dx, dy, ddx, ddy; GLfloat q[4]; + int button_down_p; + int ignore_device_rotation_p; }; /* Returns a trackball_state object, which encapsulates the stuff necessary to make dragging the mouse on the window of a GL program do the right thing. */ trackball_state * -gltrackball_init (void) +gltrackball_init (int ignore_device_rotation_p) { trackball_state *ts = (trackball_state *) calloc (1, sizeof (*ts)); if (!ts) return 0; + ts->ignore_device_rotation_p = ignore_device_rotation_p; trackball (ts->q, 0, 0, 0, 0); return ts; } +/* Device rotation interacts very strangely with mouse positions. + I'm not entirely sure this is the right fix. + */ +static void +adjust_for_device_rotation (trackball_state *ts, + double *x, double *y, double *w, double *h) +{ + int rot = (int) current_device_rotation(); + int swap; + + if (ts->ignore_device_rotation_p) return; + + while (rot <= -180) rot += 360; + while (rot > 180) rot -= 360; + + if (rot > 135 || rot < -135) /* 180 */ + { + *x = *w - *x; + *y = *h - *y; + } + else if (rot > 45) /* 90 */ + { + swap = *x; *x = *y; *y = swap; + swap = *w; *w = *h; *h = swap; + *x = *w - *x; + } + else if (rot < -45) /* 270 */ + { + swap = *x; *x = *y; *y = swap; + swap = *w; *w = *h; *h = swap; + *y = *h - *y; + } +} + + /* Begin tracking the mouse: Call this when the mouse button goes down. x and y are the mouse position relative to the window. w and h are the size of the window. @@ -52,26 +120,94 @@ gltrackball_start (trackball_state *ts, int x, int y, int w, int h) { ts->x = x; ts->y = y; + ts->button_down_p = 1; + ts->dx = ts->ddx = 0; + ts->dy = ts->ddy = 0; } -/* Track the mouse: Call this each time the mouse moves with the button down. - x and y are the new mouse position relative to the window. - w and h are the size of the window. +/* Stop tracking the mouse: Call this when the mouse button goes up. */ void -gltrackball_track (trackball_state *ts, int x, int y, int w, int h) +gltrackball_stop (trackball_state *ts) { + ts->button_down_p = 0; +} + +static void +gltrackball_track_1 (trackball_state *ts, + double x, double y, + int w, int h, + int ignore_device_rotation_p) +{ + double X = x; + double Y = y; + double W = w, W2 = w; + double H = h, H2 = h; float q2[4]; - trackball (q2, - (2.0 * ts->x - w) / w, - (h - 2.0 * ts->y) / h, - (2.0 * x - w) / w, - (h - 2.0 * y) / h); + double ox = ts->x; + double oy = ts->y; + ts->x = x; ts->y = y; + + if (! ignore_device_rotation_p) + { + adjust_for_device_rotation (ts, &ox, &oy, &W, &H); + adjust_for_device_rotation (ts, &X, &Y, &W2, &H2); + } + trackball (q2, + (2 * ox - W) / W, + (H - 2 * oy) / H, + (2 * X - W) / W, + (H - 2 * Y) / H); + add_quats (q2, ts->q, ts->q); } + +/* Track the mouse: Call this each time the mouse moves with the button down. + x and y are the new mouse position relative to the window. + w and h are the size of the window. + */ +void +gltrackball_track (trackball_state *ts, int x, int y, int w, int h) +{ + double dampen = 0.01; /* This keeps it going for about 3 sec */ + ts->dx = x - ts->x; + ts->dy = y - ts->y; + ts->ddx = ts->dx * dampen; + ts->ddy = ts->dy * dampen; + ts->ow = w; + ts->oh = h; + gltrackball_track_1 (ts, x, y, w, h, False); +} + + +static void +gltrackball_dampen (double *n, double *dn) +{ + int pos = (*n > 0); + *n -= *dn; + if (pos != (*n > 0)) + *n = *dn = 0; +} + + +/* Reset the trackball to the default unrotated state, + plus an optional initial rotation. + */ +void +gltrackball_reset (trackball_state *ts, float x, float y) +{ + int bd = ts->button_down_p; + int ig = ts->ignore_device_rotation_p; + memset (ts, 0, sizeof(*ts)); + ts->button_down_p = bd; + ts->ignore_device_rotation_p = ig; + trackball (ts->q, 0, 0, x, y); +} + + /* Execute the rotations current encapsulated in the trackball_state: this does something analagous to glRotatef(). */ @@ -79,16 +215,27 @@ void gltrackball_rotate (trackball_state *ts) { GLfloat m[4][4]; + if (!ts->button_down_p && + (ts->ddx != 0 || + ts->ddy != 0)) + { + /* Apply inertia: keep moving in the same direction as the last move. */ + gltrackball_track_1 (ts, + ts->x + ts->dx, + ts->y + ts->dy, + ts->ow, ts->oh, + False); + + /* Dampen inertia: gradually stop spinning. */ + gltrackball_dampen (&ts->dx, &ts->ddx); + gltrackball_dampen (&ts->dy, &ts->ddy); + } + build_rotmatrix (m, ts->q); glMultMatrixf (&m[0][0]); } -# define Button4 4 /* X11/Xlib.h */ -# define Button5 5 -# define Button6 6 -# define Button7 7 - /* Call this when a mouse-wheel click is detected. Clicks act like horizontal or vertical drags. Percent is the length of the drag as a percentage of the screen size. @@ -101,10 +248,10 @@ gltrackball_mousewheel (trackball_state *ts, int button, int percent, int flip_p) { int up_p; - double move; int horizontal_p; + int mx, my, move, scale; -#ifdef HAVE_COCOA +#ifdef HAVE_JWXYZ flip_p = 0; /* MacOS has already handled this. */ #endif @@ -122,15 +269,14 @@ gltrackball_mousewheel (trackball_state *ts, up_p = !up_p; } + scale = mx = my = 1000; move = (up_p - ? 1.0 - (percent / 100.0) - : 1.0 + (percent / 100.0)); - - gltrackball_start (ts, 50, 50, 100, 100); - if (horizontal_p) - gltrackball_track (ts, 50*move, 50, 100, 100); - else - gltrackball_track (ts, 50, 50*move, 100, 100); + ? floor (scale * (1.0 - (percent / 100.0))) + : ceil (scale * (1.0 + (percent / 100.0)))); + if (horizontal_p) mx = move; + else my = move; + gltrackball_start (ts, scale, scale, scale*2, scale*2); + gltrackball_track (ts, mx, my, scale*2, scale*2); } void @@ -140,3 +286,52 @@ gltrackball_get_quaternion (trackball_state *ts, float q[4]) for (i=0; i<4; i++) q[i] = ts->q[i]; } + + +/* A utility function for event-handler functions: + Handles the various motion and click events related to trackballs. + Returns True if the event was handled. + */ +Bool +gltrackball_event_handler (XEvent *event, + trackball_state *ts, + int window_width, int window_height, + Bool *button_down_p) +{ + if (event->xany.type == ButtonPress && + event->xbutton.button == Button1) + { + *button_down_p = True; + gltrackball_start (ts, + event->xbutton.x, event->xbutton.y, + window_width, window_height); + return True; + } + else if (event->xany.type == ButtonRelease && + event->xbutton.button == Button1) + { + *button_down_p = False; + gltrackball_stop (ts); + return True; + } + else if (event->xany.type == ButtonPress && + (event->xbutton.button == Button4 || + event->xbutton.button == Button5 || + event->xbutton.button == Button6 || + event->xbutton.button == Button7)) + { + gltrackball_mousewheel (ts, event->xbutton.button, 10, + !!event->xbutton.state); + return True; + } + else if (event->xany.type == MotionNotify && + *button_down_p) + { + gltrackball_track (ts, + event->xmotion.x, event->xmotion.y, + window_width, window_height); + return True; + } + + return False; +}