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
69 w = fgStructure.CurrentWindow->Window.Handle;
\r
70 if(spnav_x11_open(fgDisplay.Display, w) == -1) {
\r
76 sball_initialized = 1;
\r
79 void fgSpaceballClose(void)
\r
81 #if TARGET_HOST_POSIX_X11
\r
86 int fgHasSpaceball(void)
\r
88 if(!sball_initialized) {
\r
89 fgInitialiseSpaceball();
\r
90 if(!sball_initialized) {
\r
91 fgWarning("fgInitialiseSpaceball failed\n");
\r
96 #if TARGET_HOST_POSIX_X11
\r
97 /* XXX this function should somehow query the driver if there's a device
\r
98 * plugged in, as opposed to just checking if there's a driver to talk to.
\r
100 return spnav_fd() == -1 ? 0 : 1;
\r
106 int fgSpaceballNumButtons(void)
\r
108 if(!sball_initialized) {
\r
109 fgInitialiseSpaceball();
\r
110 if(!sball_initialized) {
\r
111 fgWarning("fgInitialiseSpaceball failed\n");
\r
116 #if TARGET_HOST_POSIX_X11
\r
117 return 2; /* TODO implement this properly */
\r
123 void fgSpaceballSetWindow(SFG_Window *window)
\r
125 if(!sball_initialized) {
\r
126 fgInitialiseSpaceball();
\r
127 if(!sball_initialized) {
\r
132 #if TARGET_HOST_POSIX_X11
\r
133 if(spnav_win != window) {
\r
134 spnav_x11_window(window->Window.Handle);
\r
135 spnav_win = window;
\r
141 #if TARGET_HOST_POSIX_X11
\r
142 int fgIsSpaceballXEvent(const XEvent *xev)
\r
146 if(!sball_initialized) {
\r
147 fgInitialiseSpaceball();
\r
148 if(!sball_initialized) {
\r
153 return spnav_x11_event(xev, &sev);
\r
156 void fgSpaceballHandleXEvent(const XEvent *xev)
\r
160 if(!sball_initialized) {
\r
161 fgInitialiseSpaceball();
\r
162 if(!sball_initialized) {
\r
167 if(spnav_x11_event(xev, &sev)) {
\r
169 case SPNAV_EVENT_MOTION:
\r
170 if(sev.motion.x | sev.motion.y | sev.motion.z) {
\r
171 INVOKE_WCB(*spnav_win, SpaceMotion, (sev.motion.x, sev.motion.y, sev.motion.z));
\r
173 if(sev.motion.rx | sev.motion.ry | sev.motion.rz) {
\r
174 INVOKE_WCB(*spnav_win, SpaceRotation, (sev.motion.rx, sev.motion.ry, sev.motion.rz));
\r
176 spnav_remove_events(SPNAV_EVENT_MOTION);
\r
179 case SPNAV_EVENT_BUTTON:
\r
180 INVOKE_WCB(*spnav_win, SpaceButton, (sev.button.bnum, sev.button.press ? GLUT_DOWN : GLUT_UP));
\r
190 The following code is part of libspnav, part of the spacenav project (spacenav.sf.net)
\r
191 Copyright (C) 2007-2009 John Tsiombikas <nuclear@member.fsf.org>
\r
193 Redistribution and use in source and binary forms, with or without
\r
194 modification, are permitted provided that the following conditions are met:
\r
196 1. Redistributions of source code must retain the above copyright notice, this
\r
197 list of conditions and the following disclaimer.
\r
198 2. Redistributions in binary form must reproduce the above copyright notice,
\r
199 this list of conditions and the following disclaimer in the documentation
\r
200 and/or other materials provided with the distribution.
\r
201 3. The name of the author may not be used to endorse or promote products
\r
202 derived from this software without specific prior written permission.
\r
204 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
\r
205 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
\r
206 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
\r
207 EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
\r
208 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
\r
209 OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
\r
210 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
\r
211 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
\r
212 IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
\r
216 #include <stdlib.h>
\r
217 #include <string.h>
\r
220 #include <X11/Xlib.h>
\r
221 #include <X11/Xutil.h>
\r
223 static Window get_daemon_window(Display *dpy);
\r
224 static int catch_badwin(Display *dpy, XErrorEvent *err);
\r
226 static Display *dpy;
\r
227 static Window app_win;
\r
228 static Atom motion_event, button_press_event, button_release_event, command_event;
\r
231 CMD_APP_WINDOW = 27695,
\r
235 #define IS_OPEN dpy
\r
237 struct event_node {
\r
239 struct event_node *next;
\r
242 static int spnav_x11_open(Display *display, Window win)
\r
250 motion_event = XInternAtom(dpy, "MotionEvent", True);
\r
251 button_press_event = XInternAtom(dpy, "ButtonPressEvent", True);
\r
252 button_release_event = XInternAtom(dpy, "ButtonReleaseEvent", True);
\r
253 command_event = XInternAtom(dpy, "CommandEvent", True);
\r
255 if(!motion_event || !button_press_event || !button_release_event || !command_event) {
\r
257 return -1; /* daemon not started */
\r
260 if(spnav_x11_window(win) == -1) {
\r
262 return -1; /* daemon not started */
\r
269 static int spnav_close(void)
\r
272 spnav_x11_window(DefaultRootWindow(dpy));
\r
280 static int spnav_x11_window(Window win)
\r
282 int (*prev_xerr_handler)(Display*, XErrorEvent*);
\r
290 if(!(daemon_win = get_daemon_window(dpy))) {
\r
294 prev_xerr_handler = XSetErrorHandler(catch_badwin);
\r
296 xev.type = ClientMessage;
\r
297 xev.xclient.send_event = False;
\r
298 xev.xclient.display = dpy;
\r
299 xev.xclient.window = win;
\r
300 xev.xclient.message_type = command_event;
\r
301 xev.xclient.format = 16;
\r
302 xev.xclient.data.s[0] = ((unsigned int)win & 0xffff0000) >> 16;
\r
303 xev.xclient.data.s[1] = (unsigned int)win & 0xffff;
\r
304 xev.xclient.data.s[2] = CMD_APP_WINDOW;
\r
306 XSendEvent(dpy, daemon_win, False, 0, &xev);
\r
309 XSetErrorHandler(prev_xerr_handler);
\r
313 static int spnav_fd(void)
\r
316 return ConnectionNumber(dpy);
\r
321 /*static int spnav_wait_event(spnav_event *event)
\r
326 XNextEvent(dpy, &xev);
\r
328 if(spnav_x11_event(&xev, event) > 0) {
\r
329 return event->type;
\r
336 static int spnav_poll_event(spnav_event *event)
\r
339 if(XPending(dpy)) {
\r
341 XNextEvent(dpy, &xev);
\r
343 return spnav_x11_event(&xev, event);
\r
349 static Bool match_events(Display *dpy, XEvent *xev, char *arg)
\r
351 int evtype = *(int*)arg;
\r
353 if(xev->type != ClientMessage) {
\r
357 if(xev->xclient.message_type == motion_event) {
\r
358 return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False;
\r
360 if(xev->xclient.message_type == button_press_event ||
\r
361 xev->xclient.message_type == button_release_event) {
\r
362 return !evtype || evtype == SPNAV_EVENT_BUTTON ? True : False;
\r
367 static int spnav_remove_events(int type)
\r
374 while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) {
\r
382 static int spnav_x11_event(const XEvent *xev, spnav_event *event)
\r
387 if(xev->type != ClientMessage) {
\r
391 xmsg_type = xev->xclient.message_type;
\r
393 if(xmsg_type != motion_event && xmsg_type != button_press_event &&
\r
394 xmsg_type != button_release_event) {
\r
398 if(xmsg_type == motion_event) {
\r
399 event->type = SPNAV_EVENT_MOTION;
\r
400 event->motion.data = &event->motion.x;
\r
402 for(i=0; i<6; i++) {
\r
403 event->motion.data[i] = xev->xclient.data.s[i + 2];
\r
405 event->motion.period = xev->xclient.data.s[8];
\r
407 event->type = SPNAV_EVENT_BUTTON;
\r
408 event->button.press = xmsg_type == button_press_event ? 1 : 0;
\r
409 event->button.bnum = xev->xclient.data.s[2];
\r
411 return event->type;
\r
415 static Window get_daemon_window(Display *dpy)
\r
417 Window win, root_win;
\r
418 XTextProperty wname;
\r
421 unsigned long nitems, bytes_after;
\r
422 unsigned char *prop;
\r
424 root_win = DefaultRootWindow(dpy);
\r
426 XGetWindowProperty(dpy, root_win, command_event, 0, 1, False, AnyPropertyType, &type, &fmt, &nitems, &bytes_after, &prop);
\r
431 win = *(Window*)prop;
\r
434 if(!XGetWMName(dpy, win, &wname) || strcmp("Magellan Window", (char*)wname.value) != 0) {
\r
441 static int catch_badwin(Display *dpy, XErrorEvent *err)
\r
445 if(err->error_code == BadWindow) {
\r
448 XGetErrorText(dpy, err->error_code, buf, sizeof buf);
\r
449 fprintf(stderr, "Caught unexpected X error: %s\n", buf);
\r
454 #endif /* TARGET_HOST_POSIX_X11 */
\r