1 /* Spaceball support for Linux.
\r
2 * Written by John Tsiombikas <nuclear@member.fsf.org>
\r
3 * Copied for Platform code by Evan Felix <karcaw at gmail.com>
\r
4 * Creation date: Thur Feb 2 2012
\r
6 * This code supports 3Dconnexion's 6-dof space-whatever devices.
\r
7 * It can communicate with either the proprietary 3Dconnexion daemon (3dxsrv)
\r
8 * free spacenavd (http://spacenav.sourceforge.net), through the "standard"
\r
9 * magellan X-based protocol.
\r
12 #include <GL/freeglut.h>
\r
13 #include "../Common/freeglut_internal.h"
\r
15 #include <X11/Xlib.h>
\r
17 extern int sball_initialized;
\r
20 SPNAV_EVENT_ANY, /* used by spnav_remove_events() */
\r
22 SPNAV_EVENT_BUTTON /* includes both press and release */
\r
25 struct spnav_event_motion {
\r
29 unsigned int period;
\r
33 struct spnav_event_button {
\r
39 typedef union spnav_event {
\r
41 struct spnav_event_motion motion;
\r
42 struct spnav_event_button button;
\r
46 static int spnav_x11_open(Display *dpy, Window win);
\r
47 static int spnav_x11_window(Window win);
\r
48 static int spnav_x11_event(const XEvent *xev, spnav_event *event);
\r
49 static int spnav_close(void);
\r
50 static int spnav_fd(void);
\r
51 static int spnav_remove_events(int type);
\r
53 static SFG_Window *spnav_win;
\r
55 void fgPlatformInitializeSpaceball(void)
\r
59 sball_initialized = 1;
\r
60 if(!fgStructure.CurrentWindow)
\r
62 sball_initialized = -1;
\r
66 w = fgStructure.CurrentWindow->Window.Handle;
\r
67 if(spnav_x11_open(fgDisplay.pDisplay.Display, w) == -1)
\r
69 sball_initialized = -1;
\r
74 void fgPlatformSpaceballClose(void)
\r
79 int fgPlatformHasSpaceball(void)
\r
81 /* XXX this function should somehow query the driver if there's a device
\r
82 * plugged in, as opposed to just checking if there's a driver to talk to.
\r
84 return spnav_fd() == -1 ? 0 : 1;
\r
87 int fgPlatformSpaceballNumButtons(void) {
\r
91 void fgPlatformSpaceballSetWindow(SFG_Window *window)
\r
93 if(spnav_win != window) {
\r
94 spnav_x11_window(window->Window.Handle);
\r
99 int fgIsSpaceballXEvent(const XEvent *xev)
\r
103 if(spnav_win != fgStructure.CurrentWindow) {
\r
104 /* this will also initialize spaceball if needed (first call) */
\r
105 fgSpaceballSetWindow(fgStructure.CurrentWindow);
\r
108 if(sball_initialized != 1) {
\r
112 return spnav_x11_event(xev, &sev);
\r
115 void fgSpaceballHandleXEvent(const XEvent *xev)
\r
119 if(sball_initialized == 0) {
\r
120 fgInitialiseSpaceball();
\r
121 if(sball_initialized != 1) {
\r
126 if(spnav_x11_event(xev, &sev)) {
\r
128 case SPNAV_EVENT_MOTION:
\r
129 if(sev.motion.x | sev.motion.y | sev.motion.z) {
\r
130 INVOKE_WCB(*spnav_win, SpaceMotion, (sev.motion.x, sev.motion.y, sev.motion.z));
\r
132 if(sev.motion.rx | sev.motion.ry | sev.motion.rz) {
\r
133 INVOKE_WCB(*spnav_win, SpaceRotation, (sev.motion.rx, sev.motion.ry, sev.motion.rz));
\r
135 spnav_remove_events(SPNAV_EVENT_MOTION);
\r
138 case SPNAV_EVENT_BUTTON:
\r
139 INVOKE_WCB(*spnav_win, SpaceButton, (sev.button.bnum, sev.button.press ? GLUT_DOWN : GLUT_UP));
\r
149 The following code is part of libspnav, part of the spacenav project (spacenav.sf.net)
\r
150 Copyright (C) 2007-2009 John Tsiombikas <nuclear@member.fsf.org>
\r
152 Redistribution and use in source and binary forms, with or without
\r
153 modification, are permitted provided that the following conditions are met:
\r
155 1. Redistributions of source code must retain the above copyright notice, this
\r
156 list of conditions and the following disclaimer.
\r
157 2. Redistributions in binary form must reproduce the above copyright notice,
\r
158 this list of conditions and the following disclaimer in the documentation
\r
159 and/or other materials provided with the distribution.
\r
160 3. The name of the author may not be used to endorse or promote products
\r
161 derived from this software without specific prior written permission.
\r
163 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
\r
164 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
\r
165 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
\r
166 EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
\r
167 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
\r
168 OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
\r
169 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
\r
170 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
\r
171 IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
\r
175 #include <stdlib.h>
\r
176 #include <string.h>
\r
178 #ifdef HAVE_ERRNO_H
\r
182 #include <X11/Xlib.h>
\r
183 #include <X11/Xutil.h>
\r
185 static Window get_daemon_window(Display *dpy);
\r
186 static int catch_badwin(Display *dpy, XErrorEvent *err);
\r
188 static Display *dpy;
\r
189 static Window app_win;
\r
190 static Atom motion_event, button_press_event, button_release_event, command_event;
\r
193 CMD_APP_WINDOW = 27695,
\r
197 #define IS_OPEN dpy
\r
199 struct event_node {
\r
201 struct event_node *next;
\r
204 static int spnav_x11_open(Display *display, Window win)
\r
212 motion_event = XInternAtom(dpy, "MotionEvent", True);
\r
213 button_press_event = XInternAtom(dpy, "ButtonPressEvent", True);
\r
214 button_release_event = XInternAtom(dpy, "ButtonReleaseEvent", True);
\r
215 command_event = XInternAtom(dpy, "CommandEvent", True);
\r
217 if(!motion_event || !button_press_event || !button_release_event || !command_event) {
\r
219 return -1; /* daemon not started */
\r
222 if(spnav_x11_window(win) == -1) {
\r
224 return -1; /* daemon not started */
\r
231 static int spnav_close(void)
\r
234 spnav_x11_window(DefaultRootWindow(dpy));
\r
242 static int spnav_x11_window(Window win)
\r
244 int (*prev_xerr_handler)(Display*, XErrorEvent*);
\r
252 if(!(daemon_win = get_daemon_window(dpy))) {
\r
256 prev_xerr_handler = XSetErrorHandler(catch_badwin);
\r
258 xev.type = ClientMessage;
\r
259 xev.xclient.send_event = False;
\r
260 xev.xclient.display = dpy;
\r
261 xev.xclient.window = win;
\r
262 xev.xclient.message_type = command_event;
\r
263 xev.xclient.format = 16;
\r
264 xev.xclient.data.s[0] = ((unsigned int)win & 0xffff0000) >> 16;
\r
265 xev.xclient.data.s[1] = (unsigned int)win & 0xffff;
\r
266 xev.xclient.data.s[2] = CMD_APP_WINDOW;
\r
268 XSendEvent(dpy, daemon_win, False, 0, &xev);
\r
271 XSetErrorHandler(prev_xerr_handler);
\r
275 static int spnav_fd(void)
\r
278 return ConnectionNumber(dpy);
\r
283 /*static int spnav_wait_event(spnav_event *event)
\r
288 XNextEvent(dpy, &xev);
\r
290 if(spnav_x11_event(&xev, event) > 0) {
\r
291 return event->type;
\r
298 static int spnav_poll_event(spnav_event *event)
\r
301 if(XPending(dpy)) {
\r
303 XNextEvent(dpy, &xev);
\r
305 return spnav_x11_event(&xev, event);
\r
311 static Bool match_events(Display *dpy, XEvent *xev, char *arg)
\r
313 int evtype = *(int*)arg;
\r
315 if(xev->type != ClientMessage) {
\r
319 if(xev->xclient.message_type == motion_event) {
\r
320 return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False;
\r
322 if(xev->xclient.message_type == button_press_event ||
\r
323 xev->xclient.message_type == button_release_event) {
\r
324 return !evtype || evtype == SPNAV_EVENT_BUTTON ? True : False;
\r
329 static int spnav_remove_events(int type)
\r
336 while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) {
\r
344 static int spnav_x11_event(const XEvent *xev, spnav_event *event)
\r
349 if(xev->type != ClientMessage) {
\r
353 xmsg_type = xev->xclient.message_type;
\r
355 if(xmsg_type != motion_event && xmsg_type != button_press_event &&
\r
356 xmsg_type != button_release_event) {
\r
360 if(xmsg_type == motion_event) {
\r
361 event->type = SPNAV_EVENT_MOTION;
\r
362 event->motion.data = &event->motion.x;
\r
364 for(i=0; i<6; i++) {
\r
365 event->motion.data[i] = xev->xclient.data.s[i + 2];
\r
367 event->motion.period = xev->xclient.data.s[8];
\r
369 event->type = SPNAV_EVENT_BUTTON;
\r
370 event->button.press = xmsg_type == button_press_event ? 1 : 0;
\r
371 event->button.bnum = xev->xclient.data.s[2];
\r
373 return event->type;
\r
377 static Window get_daemon_window(Display *dpy)
\r
379 Window win, root_win;
\r
380 XTextProperty wname;
\r
383 unsigned long nitems, bytes_after;
\r
384 unsigned char *prop;
\r
386 root_win = DefaultRootWindow(dpy);
\r
388 XGetWindowProperty(dpy, root_win, command_event, 0, 1, False, AnyPropertyType, &type, &fmt, &nitems, &bytes_after, &prop);
\r
393 win = *(Window*)prop;
\r
396 if(!XGetWMName(dpy, win, &wname) || strcmp("Magellan Window", (char*)wname.value) != 0) {
\r
403 static int catch_badwin(Display *dpy, XErrorEvent *err)
\r
407 if(err->error_code == BadWindow) {
\r
410 XGetErrorText(dpy, err->error_code, buf, sizeof buf);
\r
411 fprintf(stderr, "Caught unexpected X error: %s\n", buf);
\r