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 /* -- PRIVATE FUNCTIONS --------------------------------------------------- */
\r
15 #if TARGET_HOST_POSIX_X11
\r
16 #include <X11/Xlib.h>
\r
19 SPNAV_EVENT_ANY, /* used by spnav_remove_events() */
\r
21 SPNAV_EVENT_BUTTON /* includes both press and release */
\r
24 struct spnav_event_motion {
\r
28 unsigned int period;
\r
32 struct spnav_event_button {
\r
38 typedef union spnav_event {
\r
40 struct spnav_event_motion motion;
\r
41 struct spnav_event_button button;
\r
45 static int spnav_x11_open(Display *dpy, Window win);
\r
46 static int spnav_x11_window(Window win);
\r
47 static int spnav_x11_event(const XEvent *xev, spnav_event *event);
\r
48 static int spnav_close(void);
\r
49 static int spnav_fd(void);
\r
50 static int spnav_remove_events(int type);
\r
52 static SFG_Window *spnav_win;
\r
55 static int sball_initialized;
\r
58 void fgInitialiseSpaceball(void)
\r
60 if(sball_initialized) {
\r
64 #if TARGET_HOST_POSIX_X11
\r
68 if(!fgStructure.CurrentWindow)
\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(spnav_win != fgStructure.CurrentWindow) {
\r
149 /* this will also initialize spaceball if needed (first call) */
\r
150 fgSpaceballSetWindow(fgStructure.CurrentWindow);
\r
153 if(!sball_initialized) {
\r
157 return spnav_x11_event(xev, &sev);
\r
160 void fgSpaceballHandleXEvent(const XEvent *xev)
\r
164 if(!sball_initialized) {
\r
165 fgInitialiseSpaceball();
\r
166 if(!sball_initialized) {
\r
171 if(spnav_x11_event(xev, &sev)) {
\r
173 case SPNAV_EVENT_MOTION:
\r
174 if(sev.motion.x | sev.motion.y | sev.motion.z) {
\r
175 INVOKE_WCB(*spnav_win, SpaceMotion, (sev.motion.x, sev.motion.y, sev.motion.z));
\r
177 if(sev.motion.rx | sev.motion.ry | sev.motion.rz) {
\r
178 INVOKE_WCB(*spnav_win, SpaceRotation, (sev.motion.rx, sev.motion.ry, sev.motion.rz));
\r
180 spnav_remove_events(SPNAV_EVENT_MOTION);
\r
183 case SPNAV_EVENT_BUTTON:
\r
184 INVOKE_WCB(*spnav_win, SpaceButton, (sev.button.bnum, sev.button.press ? GLUT_DOWN : GLUT_UP));
\r
194 The following code is part of libspnav, part of the spacenav project (spacenav.sf.net)
\r
195 Copyright (C) 2007-2009 John Tsiombikas <nuclear@member.fsf.org>
\r
197 Redistribution and use in source and binary forms, with or without
\r
198 modification, are permitted provided that the following conditions are met:
\r
200 1. Redistributions of source code must retain the above copyright notice, this
\r
201 list of conditions and the following disclaimer.
\r
202 2. Redistributions in binary form must reproduce the above copyright notice,
\r
203 this list of conditions and the following disclaimer in the documentation
\r
204 and/or other materials provided with the distribution.
\r
205 3. The name of the author may not be used to endorse or promote products
\r
206 derived from this software without specific prior written permission.
\r
208 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
\r
209 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
\r
210 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
\r
211 EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
\r
212 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
\r
213 OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
\r
214 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
\r
215 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
\r
216 IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
\r
220 #include <stdlib.h>
\r
221 #include <string.h>
\r
223 #ifdef HAVE_ERRNO_H
\r
227 #include <X11/Xlib.h>
\r
228 #include <X11/Xutil.h>
\r
230 static Window get_daemon_window(Display *dpy);
\r
231 static int catch_badwin(Display *dpy, XErrorEvent *err);
\r
233 static Display *dpy;
\r
234 static Window app_win;
\r
235 static Atom motion_event, button_press_event, button_release_event, command_event;
\r
238 CMD_APP_WINDOW = 27695,
\r
242 #define IS_OPEN dpy
\r
244 struct event_node {
\r
246 struct event_node *next;
\r
249 static int spnav_x11_open(Display *display, Window win)
\r
257 motion_event = XInternAtom(dpy, "MotionEvent", True);
\r
258 button_press_event = XInternAtom(dpy, "ButtonPressEvent", True);
\r
259 button_release_event = XInternAtom(dpy, "ButtonReleaseEvent", True);
\r
260 command_event = XInternAtom(dpy, "CommandEvent", True);
\r
262 if(!motion_event || !button_press_event || !button_release_event || !command_event) {
\r
264 return -1; /* daemon not started */
\r
267 if(spnav_x11_window(win) == -1) {
\r
269 return -1; /* daemon not started */
\r
276 static int spnav_close(void)
\r
279 spnav_x11_window(DefaultRootWindow(dpy));
\r
287 static int spnav_x11_window(Window win)
\r
289 int (*prev_xerr_handler)(Display*, XErrorEvent*);
\r
297 if(!(daemon_win = get_daemon_window(dpy))) {
\r
301 prev_xerr_handler = XSetErrorHandler(catch_badwin);
\r
303 xev.type = ClientMessage;
\r
304 xev.xclient.send_event = False;
\r
305 xev.xclient.display = dpy;
\r
306 xev.xclient.window = win;
\r
307 xev.xclient.message_type = command_event;
\r
308 xev.xclient.format = 16;
\r
309 xev.xclient.data.s[0] = ((unsigned int)win & 0xffff0000) >> 16;
\r
310 xev.xclient.data.s[1] = (unsigned int)win & 0xffff;
\r
311 xev.xclient.data.s[2] = CMD_APP_WINDOW;
\r
313 XSendEvent(dpy, daemon_win, False, 0, &xev);
\r
316 XSetErrorHandler(prev_xerr_handler);
\r
320 static int spnav_fd(void)
\r
323 return ConnectionNumber(dpy);
\r
328 /*static int spnav_wait_event(spnav_event *event)
\r
333 XNextEvent(dpy, &xev);
\r
335 if(spnav_x11_event(&xev, event) > 0) {
\r
336 return event->type;
\r
343 static int spnav_poll_event(spnav_event *event)
\r
346 if(XPending(dpy)) {
\r
348 XNextEvent(dpy, &xev);
\r
350 return spnav_x11_event(&xev, event);
\r
356 static Bool match_events(Display *dpy, XEvent *xev, char *arg)
\r
358 int evtype = *(int*)arg;
\r
360 if(xev->type != ClientMessage) {
\r
364 if(xev->xclient.message_type == motion_event) {
\r
365 return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False;
\r
367 if(xev->xclient.message_type == button_press_event ||
\r
368 xev->xclient.message_type == button_release_event) {
\r
369 return !evtype || evtype == SPNAV_EVENT_BUTTON ? True : False;
\r
374 static int spnav_remove_events(int type)
\r
381 while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) {
\r
389 static int spnav_x11_event(const XEvent *xev, spnav_event *event)
\r
394 if(xev->type != ClientMessage) {
\r
398 xmsg_type = xev->xclient.message_type;
\r
400 if(xmsg_type != motion_event && xmsg_type != button_press_event &&
\r
401 xmsg_type != button_release_event) {
\r
405 if(xmsg_type == motion_event) {
\r
406 event->type = SPNAV_EVENT_MOTION;
\r
407 event->motion.data = &event->motion.x;
\r
409 for(i=0; i<6; i++) {
\r
410 event->motion.data[i] = xev->xclient.data.s[i + 2];
\r
412 event->motion.period = xev->xclient.data.s[8];
\r
414 event->type = SPNAV_EVENT_BUTTON;
\r
415 event->button.press = xmsg_type == button_press_event ? 1 : 0;
\r
416 event->button.bnum = xev->xclient.data.s[2];
\r
418 return event->type;
\r
422 static Window get_daemon_window(Display *dpy)
\r
424 Window win, root_win;
\r
425 XTextProperty wname;
\r
428 unsigned long nitems, bytes_after;
\r
429 unsigned char *prop;
\r
431 root_win = DefaultRootWindow(dpy);
\r
433 XGetWindowProperty(dpy, root_win, command_event, 0, 1, False, AnyPropertyType, &type, &fmt, &nitems, &bytes_after, &prop);
\r
438 win = *(Window*)prop;
\r
441 if(!XGetWMName(dpy, win, &wname) || strcmp("Magellan Window", (char*)wname.value) != 0) {
\r
448 static int catch_badwin(Display *dpy, XErrorEvent *err)
\r
452 if(err->error_code == BadWindow) {
\r
455 XGetErrorText(dpy, err->error_code, buf, sizeof buf);
\r
456 fprintf(stderr, "Caught unexpected X error: %s\n", buf);
\r
461 #endif /* TARGET_HOST_POSIX_X11 */
\r