1 /* Spaceball support for Linux.
2 * Written by John Tsiombikas <nuclear@member.fsf.org>
3 * Copied for Platform code by Evan Felix <karcaw at gmail.com>
4 * Creation date: Thur Feb 2 2012
6 * This code supports 3Dconnexion's 6-dof space-whatever devices.
7 * It can communicate with either the proprietary 3Dconnexion daemon (3dxsrv)
8 * free spacenavd (http://spacenav.sourceforge.net), through the "standard"
9 * magellan X-based protocol.
12 #include <GL/freeglut.h>
13 #include "../fg_internal.h"
17 extern int fg_sball_initialized;
20 SPNAV_EVENT_ANY, /* used by spnav_remove_events() */
22 SPNAV_EVENT_BUTTON /* includes both press and release */
25 struct spnav_event_motion {
33 struct spnav_event_button {
39 typedef union spnav_event {
41 struct spnav_event_motion motion;
42 struct spnav_event_button button;
46 static int spnav_x11_open(Display *dpy, Window win);
47 static int spnav_x11_window(Window win);
48 static int spnav_x11_event(const XEvent *xev, spnav_event *event);
49 static int spnav_close(void);
50 static int spnav_fd(void);
51 static int spnav_remove_events(int type);
53 static SFG_Window *spnav_win;
55 void fgPlatformInitializeSpaceball(void)
59 fg_sball_initialized = 1;
60 if(!fgStructure.CurrentWindow)
62 fg_sball_initialized = -1;
66 w = fgStructure.CurrentWindow->Window.Handle;
67 if(spnav_x11_open(fgDisplay.pDisplay.Display, w) == -1)
69 fg_sball_initialized = -1;
74 void fgPlatformSpaceballClose(void)
79 int fgPlatformHasSpaceball(void)
81 /* XXX this function should somehow query the driver if there's a device
82 * plugged in, as opposed to just checking if there's a driver to talk to.
84 return spnav_fd() == -1 ? 0 : 1;
87 int fgPlatformSpaceballNumButtons(void) {
91 void fgPlatformSpaceballSetWindow(SFG_Window *window)
93 if(spnav_win != window) {
94 spnav_x11_window(window->Window.Handle);
99 int fgIsSpaceballXEvent(const XEvent *xev)
103 if(spnav_win != fgStructure.CurrentWindow) {
104 /* this will also initialize spaceball if needed (first call) */
105 fgSpaceballSetWindow(fgStructure.CurrentWindow);
108 if(fg_sball_initialized != 1) {
112 return spnav_x11_event(xev, &sev);
115 void fgSpaceballHandleXEvent(const XEvent *xev)
119 if(fg_sball_initialized == 0) {
120 fgInitialiseSpaceball();
121 if(fg_sball_initialized != 1) {
126 if(spnav_x11_event(xev, &sev)) {
128 case SPNAV_EVENT_MOTION:
129 if(sev.motion.x | sev.motion.y | sev.motion.z) {
130 INVOKE_WCB(*spnav_win, SpaceMotion, (sev.motion.x, sev.motion.y, sev.motion.z));
132 if(sev.motion.rx | sev.motion.ry | sev.motion.rz) {
133 INVOKE_WCB(*spnav_win, SpaceRotation, (sev.motion.rx, sev.motion.ry, sev.motion.rz));
135 spnav_remove_events(SPNAV_EVENT_MOTION);
138 case SPNAV_EVENT_BUTTON:
139 /* button numbers are 1-based in glutSpaceballButtonFunc */
140 INVOKE_WCB(*spnav_win, SpaceButton, (sev.button.bnum + 1, sev.button.press ? GLUT_DOWN : GLUT_UP));
150 The following code is part of libspnav, part of the spacenav project (spacenav.sf.net)
151 Copyright (C) 2007-2009 John Tsiombikas <nuclear@member.fsf.org>
153 Redistribution and use in source and binary forms, with or without
154 modification, are permitted provided that the following conditions are met:
156 1. Redistributions of source code must retain the above copyright notice, this
157 list of conditions and the following disclaimer.
158 2. Redistributions in binary form must reproduce the above copyright notice,
159 this list of conditions and the following disclaimer in the documentation
160 and/or other materials provided with the distribution.
161 3. The name of the author may not be used to endorse or promote products
162 derived from this software without specific prior written permission.
164 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
165 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
166 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
167 EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
168 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
169 OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
170 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
171 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
172 IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
180 #include <X11/Xlib.h>
181 #include <X11/Xutil.h>
183 static Window get_daemon_window(Display *dpy);
184 static int catch_badwin(Display *dpy, XErrorEvent *err);
187 static Window app_win;
188 static Atom motion_event, button_press_event, button_release_event, command_event;
191 CMD_APP_WINDOW = 27695,
199 struct event_node *next;
202 static int spnav_x11_open(Display *display, Window win)
210 motion_event = XInternAtom(dpy, "MotionEvent", True);
211 button_press_event = XInternAtom(dpy, "ButtonPressEvent", True);
212 button_release_event = XInternAtom(dpy, "ButtonReleaseEvent", True);
213 command_event = XInternAtom(dpy, "CommandEvent", True);
215 if(!motion_event || !button_press_event || !button_release_event || !command_event) {
217 return -1; /* daemon not started */
220 if(spnav_x11_window(win) == -1) {
222 return -1; /* daemon not started */
229 static int spnav_close(void)
232 spnav_x11_window(DefaultRootWindow(dpy));
240 static int spnav_x11_window(Window win)
242 int (*prev_xerr_handler)(Display*, XErrorEvent*);
250 if(!(daemon_win = get_daemon_window(dpy))) {
254 prev_xerr_handler = XSetErrorHandler(catch_badwin);
256 xev.type = ClientMessage;
257 xev.xclient.send_event = False;
258 xev.xclient.display = dpy;
259 xev.xclient.window = win;
260 xev.xclient.message_type = command_event;
261 xev.xclient.format = 16;
262 xev.xclient.data.s[0] = ((unsigned int)win & 0xffff0000) >> 16;
263 xev.xclient.data.s[1] = (unsigned int)win & 0xffff;
264 xev.xclient.data.s[2] = CMD_APP_WINDOW;
266 XSendEvent(dpy, daemon_win, False, 0, &xev);
269 XSetErrorHandler(prev_xerr_handler);
273 static int spnav_fd(void)
276 return ConnectionNumber(dpy);
281 /*static int spnav_wait_event(spnav_event *event)
286 XNextEvent(dpy, &xev);
288 if(spnav_x11_event(&xev, event) > 0) {
296 static int spnav_poll_event(spnav_event *event)
301 XNextEvent(dpy, &xev);
303 return spnav_x11_event(&xev, event);
309 static Bool match_events(Display *dpy, XEvent *xev, char *arg)
311 int evtype = *(int*)arg;
313 if(xev->type != ClientMessage) {
317 if(xev->xclient.message_type == motion_event) {
318 return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False;
320 if(xev->xclient.message_type == button_press_event ||
321 xev->xclient.message_type == button_release_event) {
322 return !evtype || evtype == SPNAV_EVENT_BUTTON ? True : False;
327 static int spnav_remove_events(int type)
334 while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) {
342 static int spnav_x11_event(const XEvent *xev, spnav_event *event)
347 if(xev->type != ClientMessage) {
351 xmsg_type = xev->xclient.message_type;
353 if(xmsg_type != motion_event && xmsg_type != button_press_event &&
354 xmsg_type != button_release_event) {
358 if(xmsg_type == motion_event) {
359 event->type = SPNAV_EVENT_MOTION;
360 event->motion.data = &event->motion.x;
363 event->motion.data[i] = xev->xclient.data.s[i + 2];
365 event->motion.period = xev->xclient.data.s[8];
367 event->type = SPNAV_EVENT_BUTTON;
368 event->button.press = xmsg_type == button_press_event ? 1 : 0;
369 event->button.bnum = xev->xclient.data.s[2];
375 static Window get_daemon_window(Display *dpy)
377 Window win, root_win;
381 unsigned long nitems, bytes_after;
384 root_win = DefaultRootWindow(dpy);
386 XGetWindowProperty(dpy, root_win, command_event, 0, 1, False, AnyPropertyType, &type, &fmt, &nitems, &bytes_after, &prop);
391 win = *(Window*)prop;
394 if(!XGetWMName(dpy, win, &wname) || strcmp("Magellan Window", (char*)wname.value) != 0) {
401 static int catch_badwin(Display *dpy, XErrorEvent *err)
405 if(err->error_code == BadWindow) {
408 XGetErrorText(dpy, err->error_code, buf, sizeof buf);
409 fprintf(stderr, "Caught unexpected X error: %s\n", buf);