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