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
223 #include <X11/Xlib.h>
\r
224 #include <X11/Xutil.h>
\r
226 static Window get_daemon_window(Display *dpy);
\r
227 static int catch_badwin(Display *dpy, XErrorEvent *err);
\r
229 static Display *dpy;
\r
230 static Window app_win;
\r
231 static Atom motion_event, button_press_event, button_release_event, command_event;
\r
234 CMD_APP_WINDOW = 27695,
\r
238 #define IS_OPEN dpy
\r
240 struct event_node {
\r
242 struct event_node *next;
\r
245 static int spnav_x11_open(Display *display, Window win)
\r
253 motion_event = XInternAtom(dpy, "MotionEvent", True);
\r
254 button_press_event = XInternAtom(dpy, "ButtonPressEvent", True);
\r
255 button_release_event = XInternAtom(dpy, "ButtonReleaseEvent", True);
\r
256 command_event = XInternAtom(dpy, "CommandEvent", True);
\r
258 if(!motion_event || !button_press_event || !button_release_event || !command_event) {
\r
260 return -1; /* daemon not started */
\r
263 if(spnav_x11_window(win) == -1) {
\r
265 return -1; /* daemon not started */
\r
272 static int spnav_close(void)
\r
275 spnav_x11_window(DefaultRootWindow(dpy));
\r
283 static int spnav_x11_window(Window win)
\r
285 int (*prev_xerr_handler)(Display*, XErrorEvent*);
\r
293 if(!(daemon_win = get_daemon_window(dpy))) {
\r
297 prev_xerr_handler = XSetErrorHandler(catch_badwin);
\r
299 xev.type = ClientMessage;
\r
300 xev.xclient.send_event = False;
\r
301 xev.xclient.display = dpy;
\r
302 xev.xclient.window = win;
\r
303 xev.xclient.message_type = command_event;
\r
304 xev.xclient.format = 16;
\r
305 xev.xclient.data.s[0] = ((unsigned int)win & 0xffff0000) >> 16;
\r
306 xev.xclient.data.s[1] = (unsigned int)win & 0xffff;
\r
307 xev.xclient.data.s[2] = CMD_APP_WINDOW;
\r
309 XSendEvent(dpy, daemon_win, False, 0, &xev);
\r
312 XSetErrorHandler(prev_xerr_handler);
\r
316 static int spnav_fd(void)
\r
319 return ConnectionNumber(dpy);
\r
324 /*static int spnav_wait_event(spnav_event *event)
\r
329 XNextEvent(dpy, &xev);
\r
331 if(spnav_x11_event(&xev, event) > 0) {
\r
332 return event->type;
\r
339 static int spnav_poll_event(spnav_event *event)
\r
342 if(XPending(dpy)) {
\r
344 XNextEvent(dpy, &xev);
\r
346 return spnav_x11_event(&xev, event);
\r
352 static Bool match_events(Display *dpy, XEvent *xev, char *arg)
\r
354 int evtype = *(int*)arg;
\r
356 if(xev->type != ClientMessage) {
\r
360 if(xev->xclient.message_type == motion_event) {
\r
361 return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False;
\r
363 if(xev->xclient.message_type == button_press_event ||
\r
364 xev->xclient.message_type == button_release_event) {
\r
365 return !evtype || evtype == SPNAV_EVENT_BUTTON ? True : False;
\r
370 static int spnav_remove_events(int type)
\r
377 while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) {
\r
385 static int spnav_x11_event(const XEvent *xev, spnav_event *event)
\r
390 if(xev->type != ClientMessage) {
\r
394 xmsg_type = xev->xclient.message_type;
\r
396 if(xmsg_type != motion_event && xmsg_type != button_press_event &&
\r
397 xmsg_type != button_release_event) {
\r
401 if(xmsg_type == motion_event) {
\r
402 event->type = SPNAV_EVENT_MOTION;
\r
403 event->motion.data = &event->motion.x;
\r
405 for(i=0; i<6; i++) {
\r
406 event->motion.data[i] = xev->xclient.data.s[i + 2];
\r
408 event->motion.period = xev->xclient.data.s[8];
\r
410 event->type = SPNAV_EVENT_BUTTON;
\r
411 event->button.press = xmsg_type == button_press_event ? 1 : 0;
\r
412 event->button.bnum = xev->xclient.data.s[2];
\r
414 return event->type;
\r
418 static Window get_daemon_window(Display *dpy)
\r
420 Window win, root_win;
\r
421 XTextProperty wname;
\r
424 unsigned long nitems, bytes_after;
\r
425 unsigned char *prop;
\r
427 root_win = DefaultRootWindow(dpy);
\r
429 XGetWindowProperty(dpy, root_win, command_event, 0, 1, False, AnyPropertyType, &type, &fmt, &nitems, &bytes_after, &prop);
\r
434 win = *(Window*)prop;
\r
437 if(!XGetWMName(dpy, win, &wname) || strcmp("Magellan Window", (char*)wname.value) != 0) {
\r
444 static int catch_badwin(Display *dpy, XErrorEvent *err)
\r
448 if(err->error_code == BadWindow) {
\r
451 XGetErrorText(dpy, err->error_code, buf, sizeof buf);
\r
452 fprintf(stderr, "Caught unexpected X error: %s\n", buf);
\r
457 #endif /* TARGET_HOST_POSIX_X11 */
\r