1 /* gltrackball, Copyright (c) 2002-2017 Jamie Zawinski <jwz@jwz.org>
2 * GL-flavored wrapper for trackball.c
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
23 #elif defined(HAVE_ANDROID)
28 # include <X11/Xlib.h>
30 #endif /* !HAVE_COCOA */
34 #endif /* HAVE_JWZGLES */
36 # define Button4 4 /* WTF */
41 #include "trackball.h"
42 #include "gltrackball.h"
44 #if defined(USE_IPHONE) || defined(HAVE_ANDROID)
45 /* Surely this should be defined somewhere more centrally... */
49 /* Bah, copied from ../fps.h */
51 extern double current_device_rotation (void);
53 # define current_device_rotation() (0)
57 struct trackball_state {
60 double dx, dy, ddx, ddy;
63 int ignore_device_rotation_p;
66 /* Returns a trackball_state object, which encapsulates the stuff necessary
67 to make dragging the mouse on the window of a GL program do the right thing.
70 gltrackball_init (int ignore_device_rotation_p)
72 trackball_state *ts = (trackball_state *) calloc (1, sizeof (*ts));
74 ts->ignore_device_rotation_p = ignore_device_rotation_p;
75 trackball (ts->q, 0, 0, 0, 0);
79 /* Device rotation interacts very strangely with mouse positions.
80 I'm not entirely sure this is the right fix.
83 adjust_for_device_rotation (trackball_state *ts,
84 double *x, double *y, double *w, double *h)
86 int rot = (int) current_device_rotation();
89 if (ts->ignore_device_rotation_p) return;
91 while (rot <= -180) rot += 360;
92 while (rot > 180) rot -= 360;
94 if (rot > 135 || rot < -135) /* 180 */
99 else if (rot > 45) /* 90 */
101 swap = *x; *x = *y; *y = swap;
102 swap = *w; *w = *h; *h = swap;
105 else if (rot < -45) /* 270 */
107 swap = *x; *x = *y; *y = swap;
108 swap = *w; *w = *h; *h = swap;
114 /* Begin tracking the mouse: Call this when the mouse button goes down.
115 x and y are the mouse position relative to the window.
116 w and h are the size of the window.
119 gltrackball_start (trackball_state *ts, int x, int y, int w, int h)
123 ts->button_down_p = 1;
124 ts->dx = ts->ddx = 0;
125 ts->dy = ts->ddy = 0;
128 /* Stop tracking the mouse: Call this when the mouse button goes up.
131 gltrackball_stop (trackball_state *ts)
133 ts->button_down_p = 0;
137 gltrackball_track_1 (trackball_state *ts,
140 int ignore_device_rotation_p)
144 double W = w, W2 = w;
145 double H = h, H2 = h;
153 if (! ignore_device_rotation_p)
155 adjust_for_device_rotation (ts, &ox, &oy, &W, &H);
156 adjust_for_device_rotation (ts, &X, &Y, &W2, &H2);
164 add_quats (q2, ts->q, ts->q);
168 /* Track the mouse: Call this each time the mouse moves with the button down.
169 x and y are the new mouse position relative to the window.
170 w and h are the size of the window.
173 gltrackball_track (trackball_state *ts, int x, int y, int w, int h)
175 double dampen = 0.01; /* This keeps it going for about 3 sec */
178 ts->ddx = ts->dx * dampen;
179 ts->ddy = ts->dy * dampen;
182 gltrackball_track_1 (ts, x, y, w, h, False);
187 gltrackball_dampen (double *n, double *dn)
196 /* Reset the trackball to the default unrotated state,
197 plus an optional initial rotation.
200 gltrackball_reset (trackball_state *ts, float x, float y)
202 int bd = ts->button_down_p;
203 int ig = ts->ignore_device_rotation_p;
204 memset (ts, 0, sizeof(*ts));
205 ts->button_down_p = bd;
206 ts->ignore_device_rotation_p = ig;
207 trackball (ts->q, 0, 0, x, y);
211 /* Execute the rotations current encapsulated in the trackball_state:
212 this does something analagous to glRotatef().
215 gltrackball_rotate (trackball_state *ts)
218 if (!ts->button_down_p &&
222 /* Apply inertia: keep moving in the same direction as the last move. */
223 gltrackball_track_1 (ts,
229 /* Dampen inertia: gradually stop spinning. */
230 gltrackball_dampen (&ts->dx, &ts->ddx);
231 gltrackball_dampen (&ts->dy, &ts->ddy);
234 build_rotmatrix (m, ts->q);
235 glMultMatrixf (&m[0][0]);
239 /* Call this when a mouse-wheel click is detected.
240 Clicks act like horizontal or vertical drags.
241 Percent is the length of the drag as a percentage of the screen size.
242 Button is 'Button4' or 'Button5' (for the vertical wheel)
243 or 'Button5' or 'Button6' (for the horizontal wheel).
244 If `flip_p' is true, swap the horizontal and vertical axes.
247 gltrackball_mousewheel (trackball_state *ts,
248 int button, int percent, int flip_p)
252 int mx, my, move, scale;
255 flip_p = 0; /* MacOS has already handled this. */
259 case Button4: up_p = 1; horizontal_p = 0; break;
260 case Button5: up_p = 0; horizontal_p = 0; break;
261 case Button6: up_p = 1; horizontal_p = 1; break;
262 case Button7: up_p = 0; horizontal_p = 1; break;
263 default: abort(); break;
268 horizontal_p = !horizontal_p;
272 scale = mx = my = 1000;
274 ? floor (scale * (1.0 - (percent / 100.0)))
275 : ceil (scale * (1.0 + (percent / 100.0))));
276 if (horizontal_p) mx = move;
278 gltrackball_start (ts, scale, scale, scale*2, scale*2);
279 gltrackball_track (ts, mx, my, scale*2, scale*2);
283 gltrackball_get_quaternion (trackball_state *ts, float q[4])
291 /* A utility function for event-handler functions:
292 Handles the various motion and click events related to trackballs.
293 Returns True if the event was handled.
296 gltrackball_event_handler (XEvent *event,
298 int window_width, int window_height,
301 if (event->xany.type == ButtonPress &&
302 event->xbutton.button == Button1)
304 *button_down_p = True;
305 gltrackball_start (ts,
306 event->xbutton.x, event->xbutton.y,
307 window_width, window_height);
310 else if (event->xany.type == ButtonRelease &&
311 event->xbutton.button == Button1)
313 *button_down_p = False;
314 gltrackball_stop (ts);
317 else if (event->xany.type == ButtonPress &&
318 (event->xbutton.button == Button4 ||
319 event->xbutton.button == Button5 ||
320 event->xbutton.button == Button6 ||
321 event->xbutton.button == Button7))
323 gltrackball_mousewheel (ts, event->xbutton.button, 10,
324 !!event->xbutton.state);
327 else if (event->xany.type == MotionNotify &&
330 gltrackball_track (ts,
331 event->xmotion.x, event->xmotion.y,
332 window_width, window_height);