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
182 #include <X11/Xlib.h>
183 #include <X11/Xutil.h>
185 static Window get_daemon_window(Display *dpy);
186 static int catch_badwin(Display *dpy, XErrorEvent *err);
189 static Window app_win;
190 static Atom motion_event, button_press_event, button_release_event, command_event;
193 CMD_APP_WINDOW = 27695,
201 struct event_node *next;
204 static int spnav_x11_open(Display *display, Window win)
212 motion_event = XInternAtom(dpy, "MotionEvent", True);
213 button_press_event = XInternAtom(dpy, "ButtonPressEvent", True);
214 button_release_event = XInternAtom(dpy, "ButtonReleaseEvent", True);
215 command_event = XInternAtom(dpy, "CommandEvent", True);
217 if(!motion_event || !button_press_event || !button_release_event || !command_event) {
219 return -1; /* daemon not started */
222 if(spnav_x11_window(win) == -1) {
224 return -1; /* daemon not started */
231 static int spnav_close(void)
234 spnav_x11_window(DefaultRootWindow(dpy));
242 static int spnav_x11_window(Window win)
244 int (*prev_xerr_handler)(Display*, XErrorEvent*);
252 if(!(daemon_win = get_daemon_window(dpy))) {
256 prev_xerr_handler = XSetErrorHandler(catch_badwin);
258 xev.type = ClientMessage;
259 xev.xclient.send_event = False;
260 xev.xclient.display = dpy;
261 xev.xclient.window = win;
262 xev.xclient.message_type = command_event;
263 xev.xclient.format = 16;
264 xev.xclient.data.s[0] = ((unsigned int)win & 0xffff0000) >> 16;
265 xev.xclient.data.s[1] = (unsigned int)win & 0xffff;
266 xev.xclient.data.s[2] = CMD_APP_WINDOW;
268 XSendEvent(dpy, daemon_win, False, 0, &xev);
271 XSetErrorHandler(prev_xerr_handler);
275 static int spnav_fd(void)
278 return ConnectionNumber(dpy);
283 /*static int spnav_wait_event(spnav_event *event)
288 XNextEvent(dpy, &xev);
290 if(spnav_x11_event(&xev, event) > 0) {
298 static int spnav_poll_event(spnav_event *event)
303 XNextEvent(dpy, &xev);
305 return spnav_x11_event(&xev, event);
311 static Bool match_events(Display *dpy, XEvent *xev, char *arg)
313 int evtype = *(int*)arg;
315 if(xev->type != ClientMessage) {
319 if(xev->xclient.message_type == motion_event) {
320 return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False;
322 if(xev->xclient.message_type == button_press_event ||
323 xev->xclient.message_type == button_release_event) {
324 return !evtype || evtype == SPNAV_EVENT_BUTTON ? True : False;
329 static int spnav_remove_events(int type)
336 while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) {
344 static int spnav_x11_event(const XEvent *xev, spnav_event *event)
349 if(xev->type != ClientMessage) {
353 xmsg_type = xev->xclient.message_type;
355 if(xmsg_type != motion_event && xmsg_type != button_press_event &&
356 xmsg_type != button_release_event) {
360 if(xmsg_type == motion_event) {
361 event->type = SPNAV_EVENT_MOTION;
362 event->motion.data = &event->motion.x;
365 event->motion.data[i] = xev->xclient.data.s[i + 2];
367 event->motion.period = xev->xclient.data.s[8];
369 event->type = SPNAV_EVENT_BUTTON;
370 event->button.press = xmsg_type == button_press_event ? 1 : 0;
371 event->button.bnum = xev->xclient.data.s[2];
377 static Window get_daemon_window(Display *dpy)
379 Window win, root_win;
383 unsigned long nitems, bytes_after;
386 root_win = DefaultRootWindow(dpy);
388 XGetWindowProperty(dpy, root_win, command_event, 0, 1, False, AnyPropertyType, &type, &fmt, &nitems, &bytes_after, &prop);
393 win = *(Window*)prop;
396 if(!XGetWMName(dpy, win, &wname) || strcmp("Magellan Window", (char*)wname.value) != 0) {
403 static int catch_badwin(Display *dpy, XErrorEvent *err)
407 if(err->error_code == BadWindow) {
410 XGetErrorText(dpy, err->error_code, buf, sizeof buf);
411 fprintf(stderr, "Caught unexpected X error: %s\n", buf);