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 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 sball_initialized = 1;
60 if(!fgStructure.CurrentWindow)
62 sball_initialized = -1;
66 w = fgStructure.CurrentWindow->Window.Handle;
67 if(spnav_x11_open(fgDisplay.pDisplay.Display, w) == -1)
69 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(sball_initialized != 1) {
112 return spnav_x11_event(xev, &sev);
115 void fgSpaceballHandleXEvent(const XEvent *xev)
119 if(sball_initialized == 0) {
120 fgInitialiseSpaceball();
121 if(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 INVOKE_WCB(*spnav_win, SpaceButton, (sev.button.bnum, sev.button.press ? GLUT_DOWN : GLUT_UP));
149 The following code is part of libspnav, part of the spacenav project (spacenav.sf.net)
150 Copyright (C) 2007-2009 John Tsiombikas <nuclear@member.fsf.org>
152 Redistribution and use in source and binary forms, with or without
153 modification, are permitted provided that the following conditions are met:
155 1. Redistributions of source code must retain the above copyright notice, this
156 list of conditions and the following disclaimer.
157 2. Redistributions in binary form must reproduce the above copyright notice,
158 this list of conditions and the following disclaimer in the documentation
159 and/or other materials provided with the distribution.
160 3. The name of the author may not be used to endorse or promote products
161 derived from this software without specific prior written permission.
163 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
164 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
165 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
166 EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
167 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
168 OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
169 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
170 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
171 IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
179 #include <X11/Xlib.h>
180 #include <X11/Xutil.h>
182 static Window get_daemon_window(Display *dpy);
183 static int catch_badwin(Display *dpy, XErrorEvent *err);
186 static Window app_win;
187 static Atom motion_event, button_press_event, button_release_event, command_event;
190 CMD_APP_WINDOW = 27695,
198 struct event_node *next;
201 static int spnav_x11_open(Display *display, Window win)
209 motion_event = XInternAtom(dpy, "MotionEvent", True);
210 button_press_event = XInternAtom(dpy, "ButtonPressEvent", True);
211 button_release_event = XInternAtom(dpy, "ButtonReleaseEvent", True);
212 command_event = XInternAtom(dpy, "CommandEvent", True);
214 if(!motion_event || !button_press_event || !button_release_event || !command_event) {
216 return -1; /* daemon not started */
219 if(spnav_x11_window(win) == -1) {
221 return -1; /* daemon not started */
228 static int spnav_close(void)
231 spnav_x11_window(DefaultRootWindow(dpy));
239 static int spnav_x11_window(Window win)
241 int (*prev_xerr_handler)(Display*, XErrorEvent*);
249 if(!(daemon_win = get_daemon_window(dpy))) {
253 prev_xerr_handler = XSetErrorHandler(catch_badwin);
255 xev.type = ClientMessage;
256 xev.xclient.send_event = False;
257 xev.xclient.display = dpy;
258 xev.xclient.window = win;
259 xev.xclient.message_type = command_event;
260 xev.xclient.format = 16;
261 xev.xclient.data.s[0] = ((unsigned int)win & 0xffff0000) >> 16;
262 xev.xclient.data.s[1] = (unsigned int)win & 0xffff;
263 xev.xclient.data.s[2] = CMD_APP_WINDOW;
265 XSendEvent(dpy, daemon_win, False, 0, &xev);
268 XSetErrorHandler(prev_xerr_handler);
272 static int spnav_fd(void)
275 return ConnectionNumber(dpy);
280 /*static int spnav_wait_event(spnav_event *event)
285 XNextEvent(dpy, &xev);
287 if(spnav_x11_event(&xev, event) > 0) {
295 static int spnav_poll_event(spnav_event *event)
300 XNextEvent(dpy, &xev);
302 return spnav_x11_event(&xev, event);
308 static Bool match_events(Display *dpy, XEvent *xev, char *arg)
310 int evtype = *(int*)arg;
312 if(xev->type != ClientMessage) {
316 if(xev->xclient.message_type == motion_event) {
317 return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False;
319 if(xev->xclient.message_type == button_press_event ||
320 xev->xclient.message_type == button_release_event) {
321 return !evtype || evtype == SPNAV_EVENT_BUTTON ? True : False;
326 static int spnav_remove_events(int type)
333 while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) {
341 static int spnav_x11_event(const XEvent *xev, spnav_event *event)
346 if(xev->type != ClientMessage) {
350 xmsg_type = xev->xclient.message_type;
352 if(xmsg_type != motion_event && xmsg_type != button_press_event &&
353 xmsg_type != button_release_event) {
357 if(xmsg_type == motion_event) {
358 event->type = SPNAV_EVENT_MOTION;
359 event->motion.data = &event->motion.x;
362 event->motion.data[i] = xev->xclient.data.s[i + 2];
364 event->motion.period = xev->xclient.data.s[8];
366 event->type = SPNAV_EVENT_BUTTON;
367 event->button.press = xmsg_type == button_press_event ? 1 : 0;
368 event->button.bnum = xev->xclient.data.s[2];
374 static Window get_daemon_window(Display *dpy)
376 Window win, root_win;
380 unsigned long nitems, bytes_after;
383 root_win = DefaultRootWindow(dpy);
385 XGetWindowProperty(dpy, root_win, command_event, 0, 1, False, AnyPropertyType, &type, &fmt, &nitems, &bytes_after, &prop);
390 win = *(Window*)prop;
393 if(!XGetWMName(dpy, win, &wname) || strcmp("Magellan Window", (char*)wname.value) != 0) {
400 static int catch_badwin(Display *dpy, XErrorEvent *err)
404 if(err->error_code == BadWindow) {
407 XGetErrorText(dpy, err->error_code, buf, sizeof buf);
408 fprintf(stderr, "Caught unexpected X error: %s\n", buf);