1 /* Spaceball support for Linux.
\r
2 * Written by John Tsiombikas <nuclear@member.fsf.org>
\r
4 * This code supports 3Dconnexion's 6-dof space-whatever devices.
\r
5 * It can communicate with either the proprietary 3Dconnexion daemon (3dxsrv)
\r
6 * free spacenavd (http://spacenav.sourceforge.net), through the "standard"
\r
7 * magellan X-based protocol.
\r
10 #include <GL/freeglut.h>
\r
11 #include "freeglut_internal.h"
\r
13 #if TARGET_HOST_POSIX_X11
\r
14 #include <X11/Xlib.h>
\r
17 SPNAV_EVENT_ANY, /* used by spnav_remove_events() */
\r
19 SPNAV_EVENT_BUTTON /* includes both press and release */
\r
22 struct spnav_event_motion {
\r
26 unsigned int period;
\r
30 struct spnav_event_button {
\r
36 typedef union spnav_event {
\r
38 struct spnav_event_motion motion;
\r
39 struct spnav_event_button button;
\r
43 static int spnav_x11_open(Display *dpy, Window win);
\r
44 static int spnav_x11_window(Window win);
\r
45 static int spnav_x11_event(const XEvent *xev, spnav_event *event);
\r
46 static int spnav_close(void);
\r
47 static int spnav_fd(void);
\r
48 static int spnav_remove_events(int type);
\r
50 static SFG_Window *spnav_win;
\r
53 static int sball_initialized;
\r
56 void fgInitialiseSpaceball(void)
\r
58 if(sball_initialized) {
\r
62 #if TARGET_HOST_POSIX_X11
\r
66 if(!fgStructure.CurrentWindow) {
\r
67 fgWarning("fgInitialiseSpaceball: no current window!\n");
\r
71 w = fgStructure.CurrentWindow->Window.Handle;
\r
72 if(spnav_x11_open(fgDisplay.Display, w) == -1) {
\r
78 sball_initialized = 1;
\r
81 void fgSpaceballClose(void)
\r
83 #if TARGET_HOST_POSIX_X11
\r
88 int fgHasSpaceball(void)
\r
90 if(!sball_initialized) {
\r
91 fgInitialiseSpaceball();
\r
92 if(!sball_initialized) {
\r
93 fgWarning("fgInitialiseSpaceball failed\n");
\r
98 #if TARGET_HOST_POSIX_X11
\r
99 /* XXX this function should somehow query the driver if there's a device
\r
100 * plugged in, as opposed to just checking if there's a driver to talk to.
\r
102 return spnav_fd() == -1 ? 0 : 1;
\r
108 int fgSpaceballNumButtons(void)
\r
110 if(!sball_initialized) {
\r
111 fgInitialiseSpaceball();
\r
112 if(!sball_initialized) {
\r
113 fgWarning("fgInitialiseSpaceball failed\n");
\r
118 #if TARGET_HOST_POSIX_X11
\r
119 return 2; /* TODO implement this properly */
\r
125 void fgSpaceballSetWindow(SFG_Window *window)
\r
127 if(!sball_initialized) {
\r
128 fgInitialiseSpaceball();
\r
129 if(!sball_initialized) {
\r
134 #if TARGET_HOST_POSIX_X11
\r
135 if(spnav_win != window) {
\r
136 spnav_x11_window(window->Window.Handle);
\r
137 spnav_win = window;
\r
143 #if TARGET_HOST_POSIX_X11
\r
144 int fgIsSpaceballXEvent(const XEvent *xev)
\r
148 if(!sball_initialized) {
\r
149 fgInitialiseSpaceball();
\r
150 if(!sball_initialized) {
\r
155 return spnav_x11_event(xev, &sev);
\r
158 void fgSpaceballHandleXEvent(const XEvent *xev)
\r
162 if(!sball_initialized) {
\r
163 fgInitialiseSpaceball();
\r
164 if(!sball_initialized) {
\r
169 if(spnav_x11_event(xev, &sev)) {
\r
171 case SPNAV_EVENT_MOTION:
\r
172 if(sev.motion.x | sev.motion.y | sev.motion.z) {
\r
173 INVOKE_WCB(*spnav_win, SpaceMotion, (sev.motion.x, sev.motion.y, sev.motion.z));
\r
175 if(sev.motion.rx | sev.motion.ry | sev.motion.rz) {
\r
176 INVOKE_WCB(*spnav_win, SpaceRotation, (sev.motion.rx, sev.motion.ry, sev.motion.rz));
\r
178 spnav_remove_events(SPNAV_EVENT_MOTION);
\r
181 case SPNAV_EVENT_BUTTON:
\r
182 INVOKE_WCB(*spnav_win, SpaceButton, (sev.button.bnum, sev.button.press ? GLUT_DOWN : GLUT_UP));
\r
192 The following code is part of libspnav, part of the spacenav project (spacenav.sf.net)
\r
193 Copyright (C) 2007-2009 John Tsiombikas <nuclear@member.fsf.org>
\r
195 Redistribution and use in source and binary forms, with or without
\r
196 modification, are permitted provided that the following conditions are met:
\r
198 1. Redistributions of source code must retain the above copyright notice, this
\r
199 list of conditions and the following disclaimer.
\r
200 2. Redistributions in binary form must reproduce the above copyright notice,
\r
201 this list of conditions and the following disclaimer in the documentation
\r
202 and/or other materials provided with the distribution.
\r
203 3. The name of the author may not be used to endorse or promote products
\r
204 derived from this software without specific prior written permission.
\r
206 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
\r
207 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
\r
208 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
\r
209 EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
\r
210 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
\r
211 OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
\r
212 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
\r
213 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
\r
214 IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
\r
218 #include <stdlib.h>
\r
219 #include <string.h>
\r
222 #include <X11/Xlib.h>
\r
223 #include <X11/Xutil.h>
\r
225 static Window get_daemon_window(Display *dpy);
\r
226 static int catch_badwin(Display *dpy, XErrorEvent *err);
\r
228 static Display *dpy;
\r
229 static Window app_win;
\r
230 static Atom motion_event, button_press_event, button_release_event, command_event;
\r
233 CMD_APP_WINDOW = 27695,
\r
237 #define IS_OPEN dpy
\r
239 struct event_node {
\r
241 struct event_node *next;
\r
244 static int spnav_x11_open(Display *display, Window win)
\r
252 motion_event = XInternAtom(dpy, "MotionEvent", True);
\r
253 button_press_event = XInternAtom(dpy, "ButtonPressEvent", True);
\r
254 button_release_event = XInternAtom(dpy, "ButtonReleaseEvent", True);
\r
255 command_event = XInternAtom(dpy, "CommandEvent", True);
\r
257 if(!motion_event || !button_press_event || !button_release_event || !command_event) {
\r
259 return -1; /* daemon not started */
\r
262 if(spnav_x11_window(win) == -1) {
\r
264 return -1; /* daemon not started */
\r
271 static int spnav_close(void)
\r
274 spnav_x11_window(DefaultRootWindow(dpy));
\r
282 static int spnav_x11_window(Window win)
\r
284 int (*prev_xerr_handler)(Display*, XErrorEvent*);
\r
292 if(!(daemon_win = get_daemon_window(dpy))) {
\r
296 prev_xerr_handler = XSetErrorHandler(catch_badwin);
\r
298 xev.type = ClientMessage;
\r
299 xev.xclient.send_event = False;
\r
300 xev.xclient.display = dpy;
\r
301 xev.xclient.window = win;
\r
302 xev.xclient.message_type = command_event;
\r
303 xev.xclient.format = 16;
\r
304 xev.xclient.data.s[0] = ((unsigned int)win & 0xffff0000) >> 16;
\r
305 xev.xclient.data.s[1] = (unsigned int)win & 0xffff;
\r
306 xev.xclient.data.s[2] = CMD_APP_WINDOW;
\r
308 XSendEvent(dpy, daemon_win, False, 0, &xev);
\r
311 XSetErrorHandler(prev_xerr_handler);
\r
315 static int spnav_fd(void)
\r
318 return ConnectionNumber(dpy);
\r
323 /*static int spnav_wait_event(spnav_event *event)
\r
328 XNextEvent(dpy, &xev);
\r
330 if(spnav_x11_event(&xev, event) > 0) {
\r
331 return event->type;
\r
338 static int spnav_poll_event(spnav_event *event)
\r
341 if(XPending(dpy)) {
\r
343 XNextEvent(dpy, &xev);
\r
345 return spnav_x11_event(&xev, event);
\r
351 static Bool match_events(Display *dpy, XEvent *xev, char *arg)
\r
353 int evtype = *(int*)arg;
\r
355 if(xev->type != ClientMessage) {
\r
359 if(xev->xclient.message_type == motion_event) {
\r
360 return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False;
\r
362 if(xev->xclient.message_type == button_press_event ||
\r
363 xev->xclient.message_type == button_release_event) {
\r
364 return !evtype || evtype == SPNAV_EVENT_BUTTON ? True : False;
\r
369 static int spnav_remove_events(int type)
\r
376 while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) {
\r
384 static int spnav_x11_event(const XEvent *xev, spnav_event *event)
\r
389 if(xev->type != ClientMessage) {
\r
393 xmsg_type = xev->xclient.message_type;
\r
395 if(xmsg_type != motion_event && xmsg_type != button_press_event &&
\r
396 xmsg_type != button_release_event) {
\r
400 if(xmsg_type == motion_event) {
\r
401 event->type = SPNAV_EVENT_MOTION;
\r
402 event->motion.data = &event->motion.x;
\r
404 for(i=0; i<6; i++) {
\r
405 event->motion.data[i] = xev->xclient.data.s[i + 2];
\r
407 event->motion.period = xev->xclient.data.s[8];
\r
409 event->type = SPNAV_EVENT_BUTTON;
\r
410 event->button.press = xmsg_type == button_press_event ? 1 : 0;
\r
411 event->button.bnum = xev->xclient.data.s[2];
\r
413 return event->type;
\r
417 static Window get_daemon_window(Display *dpy)
\r
419 Window win, root_win;
\r
420 XTextProperty wname;
\r
423 unsigned long nitems, bytes_after;
\r
424 unsigned char *prop;
\r
426 root_win = DefaultRootWindow(dpy);
\r
428 XGetWindowProperty(dpy, root_win, command_event, 0, 1, False, AnyPropertyType, &type, &fmt, &nitems, &bytes_after, &prop);
\r
433 win = *(Window*)prop;
\r
436 if(!XGetWMName(dpy, win, &wname) || strcmp("Magellan Window", (char*)wname.value) != 0) {
\r
443 static int catch_badwin(Display *dpy, XErrorEvent *err)
\r
447 if(err->error_code == BadWindow) {
\r
450 XGetErrorText(dpy, err->error_code, buf, sizeof buf);
\r
451 fprintf(stderr, "Caught unexpected X error: %s\n", buf);
\r
456 #endif /* TARGET_HOST_POSIX_X11 */
\r