From http://www.jwz.org/xscreensaver/xscreensaver-5.22.tar.gz
[xscreensaver] / hacks / glx / gltrackball.c
1 /* gltrackball, Copyright (c) 2002-2012 Jamie Zawinski <jwz@jwz.org>
2  * GL-flavored wrapper for trackball.c
3  *
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 
10  * implied warranty.
11  */
12
13 #include <math.h>
14 #include <stdlib.h>
15 #include <string.h>
16
17 #ifdef HAVE_CONFIG_H
18 # include "config.h"
19 #endif
20
21 #ifndef HAVE_COCOA
22 # include <GL/gl.h>
23 #endif
24
25 #ifdef HAVE_JWZGLES
26 # include "jwzgles.h"
27 #endif /* HAVE_JWZGLES */
28
29 #include "trackball.h"
30 #include "gltrackball.h"
31
32 extern double current_device_rotation (void);  /* Bah, it's in fps.h */
33
34 struct trackball_state {
35   int x, y;
36   GLfloat q[4];
37 };
38
39 /* Returns a trackball_state object, which encapsulates the stuff necessary
40    to make dragging the mouse on the window of a GL program do the right thing.
41  */
42 trackball_state *
43 gltrackball_init (void)
44 {
45   trackball_state *ts = (trackball_state *) calloc (1, sizeof (*ts));
46   if (!ts) return 0;
47   trackball (ts->q, 0, 0, 0, 0);
48   return ts;
49 }
50
51 /* Reset the trackball to the default unrotated state.
52  */
53 void
54 gltrackball_reset (trackball_state *ts)
55 {
56   memset (ts, 0, sizeof(*ts));
57   trackball (ts->q, 0, 0, 0, 0);
58 }
59
60
61 /* Begin tracking the mouse: Call this when the mouse button goes down.
62    x and y are the mouse position relative to the window.
63    w and h are the size of the window.
64  */
65 void
66 gltrackball_start (trackball_state *ts, int x, int y, int w, int h)
67 {
68   ts->x = x;
69   ts->y = y;
70 }
71
72 /* Track the mouse: Call this each time the mouse moves with the button down.
73    x and y are the new mouse position relative to the window.
74    w and h are the size of the window.
75  */
76 void
77 gltrackball_track (trackball_state *ts, int x, int y, int w, int h)
78 {
79   float q2[4];
80   trackball (q2,
81              (2.0 * ts->x - w) / w,
82              (h - 2.0 * ts->y) / h,
83              (2.0 * x - w) / w,
84              (h - 2.0 * y) / h);
85   ts->x = x;
86   ts->y = y;
87   add_quats (q2, ts->q, ts->q);
88 }
89
90 /* Execute the rotations current encapsulated in the trackball_state:
91    this does something analagous to glRotatef().
92  */
93 void
94 gltrackball_rotate (trackball_state *ts)
95 {
96   GLfloat m[4][4];
97   build_rotmatrix (m, ts->q);
98   glMultMatrixf (&m[0][0]);
99 }
100
101
102 # define Button4 4  /* X11/Xlib.h */
103 # define Button5 5
104 # define Button6 6
105 # define Button7 7
106
107 /* Call this when a mouse-wheel click is detected.
108    Clicks act like horizontal or vertical drags.
109    Percent is the length of the drag as a percentage of the screen size.
110    Button is 'Button4' or 'Button5' (for the vertical wheel)
111    or 'Button5' or 'Button6' (for the horizontal wheel).
112    If `flip_p' is true, swap the horizontal and vertical axes.
113  */
114 void
115 gltrackball_mousewheel (trackball_state *ts,
116                         int button, int percent, int flip_p)
117 {
118   int up_p;
119   int horizontal_p;
120   int mx, my, move, scale;
121
122 #ifdef HAVE_COCOA
123   flip_p = 0;      /* MacOS has already handled this. */
124 #endif
125
126   switch (button) {
127   case Button4: up_p = 1; horizontal_p = 0; break;
128   case Button5: up_p = 0; horizontal_p = 0; break;
129   case Button6: up_p = 1; horizontal_p = 1; break;
130   case Button7: up_p = 0; horizontal_p = 1; break;
131   default: abort(); break;
132   }
133
134   if (flip_p)
135     {
136       horizontal_p = !horizontal_p;
137       up_p = !up_p;
138     }
139
140   scale = mx = my = 1000;
141   move = (up_p
142           ? floor (scale * (1.0 - (percent / 100.0)))
143           : ceil  (scale * (1.0 + (percent / 100.0))));
144   if (horizontal_p) mx = move;
145   else              my = move;
146   gltrackball_start (ts, scale, scale, scale*2, scale*2);
147   gltrackball_track (ts, mx, my, scale*2, scale*2);
148 }
149
150 void
151 gltrackball_get_quaternion (trackball_state *ts, float q[4])
152 {
153   int i;
154   for (i=0; i<4; i++)
155     q[i] = ts->q[i];
156 }