1
/* This example demonstrates how to use libspnav to get space navigator input,
2
* and use that to rotate and translate a 3D cube. The magellan X11 protocol is
3
* used (spnav_x11_open) which is compatible with both spacenavd and
4
* 3Dconnexion's 3dxsrv.
6
* The code is a bit cluttered with X11 and GLX calls, so the interesting bits
7
* are marked with XXX comments.
20
#define SQ(x) ((x) * (x))
22
int create_gfx(int xsz, int ysz);
23
void destroy_gfx(void);
24
void set_window_title(const char *title);
27
int handle_event(XEvent *xev);
31
Atom wm_prot, wm_del_win;
35
vec3_t pos = {0, 0, -6};
36
quat_t rot = {0, 0, 0, 1}; /* that's 1 + 0i + 0j + 0k */
42
if(!(dpy = XOpenDisplay(0))) {
43
fprintf(stderr, "failed to connect to the X server");
47
if(create_gfx(512, 512) == -1) {
51
/* XXX: This actually registers our window with the driver for receiving
52
* motion/button events through the 3dxsrv-compatible X11 protocol.
54
if(spnav_x11_open(dpy, win) == -1) {
55
fprintf(stderr, "failed to connect to the space navigator daemon\n");
59
glEnable(GL_DEPTH_TEST);
60
glEnable(GL_CULL_FACE);
64
XNextEvent(dpy, &xev);
66
if(handle_event(&xev) != 0) {
80
int create_gfx(int xsz, int ysz)
85
XSetWindowAttributes xattr;
87
XClassHint class_hint;
90
GLX_RGBA, GLX_DOUBLEBUFFER,
98
wm_prot = XInternAtom(dpy, "WM_PROTOCOLS", False);
99
wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
101
scr = DefaultScreen(dpy);
102
root = RootWindow(dpy, scr);
104
if(!(vis = glXChooseVisual(dpy, scr, attr))) {
105
fprintf(stderr, "requested GLX visual is not available\n");
109
if(!(ctx = glXCreateContext(dpy, vis, 0, True))) {
110
fprintf(stderr, "failed to create GLX context\n");
115
xattr.background_pixel = xattr.border_pixel = BlackPixel(dpy, scr);
116
xattr.colormap = XCreateColormap(dpy, root, vis->visual, AllocNone);
118
if(!(win = XCreateWindow(dpy, root, 0, 0, xsz, ysz, 0, vis->depth, InputOutput,
119
vis->visual, CWColormap | CWBackPixel | CWBorderPixel, &xattr))) {
120
fprintf(stderr, "failed to create X window\n");
125
/* set the window event mask */
126
events = ExposureMask | StructureNotifyMask | KeyPressMask | KeyReleaseMask |
127
ButtonReleaseMask | ButtonPressMask | PointerMotionMask;
128
XSelectInput(dpy, win, events);
130
XSetWMProtocols(dpy, win, &wm_del_win, 1);
132
set_window_title("libspnav cube");
134
class_hint.res_name = "cube";
135
class_hint.res_class = "cube";
136
XSetClassHint(dpy, win, &class_hint);
138
if(glXMakeCurrent(dpy, win, ctx) == False) {
139
fprintf(stderr, "glXMakeCurrent failed\n");
140
glXDestroyContext(dpy, ctx);
141
XDestroyWindow(dpy, win);
145
XMapWindow(dpy, win);
151
void destroy_gfx(void)
153
glXDestroyContext(dpy, ctx);
154
XDestroyWindow(dpy, win);
155
glXMakeCurrent(dpy, None, 0);
158
void set_window_title(const char *title)
160
XTextProperty wm_name;
162
XStringListToTextProperty((char**)&title, 1, &wm_name);
163
XSetWMName(dpy, win, &wm_name);
164
XSetWMIconName(dpy, win, &wm_name);
165
XFree(wm_name.value);
172
quat_to_mat(xform, rot);
174
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
176
glMatrixMode(GL_MODELVIEW);
178
glTranslatef(pos.x, pos.y, pos.z);
179
glMultTransposeMatrixf((float*)xform);
183
glXSwapBuffers(dpy, win);
192
glVertex3f(-1, -1, 1);
193
glVertex3f(1, -1, 1);
195
glVertex3f(-1, 1, 1);
199
glVertex3f(1, -1, 1);
200
glVertex3f(1, -1, -1);
201
glVertex3f(1, 1, -1);
204
glNormal3f(0, 0, -1);
206
glVertex3f(1, -1, -1);
207
glVertex3f(-1, -1, -1);
208
glVertex3f(-1, 1, -1);
209
glVertex3f(1, 1, -1);
211
glNormal3f(-1, 0, 0);
213
glVertex3f(-1, -1, -1);
214
glVertex3f(-1, -1, 1);
215
glVertex3f(-1, 1, 1);
216
glVertex3f(-1, 1, -1);
220
glVertex3f(-1, 1, 1);
222
glVertex3f(1, 1, -1);
223
glVertex3f(-1, 1, -1);
225
glNormal3f(0, -1, 0);
227
glVertex3f(-1, -1, -1);
228
glVertex3f(1, -1, -1);
229
glVertex3f(1, -1, 1);
230
glVertex3f(-1, -1, 1);
234
int handle_event(XEvent *xev)
236
static int win_mapped;
250
if(win_mapped && xev->xexpose.count == 0) {
256
/* XXX check if the event is a spacenav event */
257
if(spnav_x11_event(xev, &spev)) {
258
/* if so deal with motion and button events */
259
if(spev.type == SPNAV_EVENT_MOTION) {
260
/* apply axis/angle rotation to the quaternion */
261
float axis_len = sqrt(SQ(spev.motion.rx) + SQ(spev.motion.ry) + SQ(spev.motion.rz));
262
rot = quat_rotate(rot, axis_len * 0.001, -spev.motion.rx / axis_len,
263
-spev.motion.ry / axis_len, spev.motion.rz / axis_len);
265
/* add translation */
266
pos.x += spev.motion.x * 0.001;
267
pos.y += spev.motion.y * 0.001;
268
pos.z -= spev.motion.z * 0.001;
272
/* on button press, reset the cube */
273
if(spev.button.press) {
274
pos = v3_cons(0, 0, -6);
275
rot = quat_cons(1, 0, 0, 0);
280
/* finally remove any other queued motion events */
281
spnav_remove_events(SPNAV_EVENT_MOTION);
283
} else if(xev->xclient.message_type == wm_prot) {
284
if(xev->xclient.data.l[0] == wm_del_win) {
291
sym = XLookupKeysym((XKeyEvent*)&xev->xkey, 0);
292
if((sym & 0xff) == 27) {
297
case ConfigureNotify:
299
int x = xev->xconfigure.width;
300
int y = xev->xconfigure.height;
302
glMatrixMode(GL_PROJECTION);
304
gluPerspective(45.0, (float)x / (float)y, 1.0, 1000.0);
306
glViewport(0, 0, x, y);