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 #if TARGET_HOST_POSIX_X11
\r
14 #include <X11/Xlib.h>
\r
17 SPNAV_EVENT_ANY, /* used by spnav_remove_events() */
\r
19 SPNAV_EVENT_BUTTON /* includes both press and release */
\r
22 struct spnav_event_motion {
\r
26 unsigned int period;
\r
30 struct spnav_event_button {
\r
36 typedef union spnav_event {
\r
38 struct spnav_event_motion motion;
\r
39 struct spnav_event_button button;
\r
43 static int spnav_x11_open(Display *dpy, Window win);
\r
44 static int spnav_x11_window(Window win);
\r
45 static int spnav_x11_event(const XEvent *xev, spnav_event *event);
\r
46 static int spnav_close(void);
\r
47 static int spnav_fd(void);
\r
48 static int spnav_remove_events(int type);
\r
50 static SFG_Window *spnav_win;
\r
53 static int sball_initialized;
\r
56 void fgInitialiseSpaceball(void)
\r
58 if(sball_initialized) {
\r
62 #if TARGET_HOST_POSIX_X11
\r
66 if(!fgStructure.CurrentWindow)
\r
69 w = fgStructure.CurrentWindow->Window.Handle;
\r
70 if(spnav_x11_open(fgDisplay.Display, w) == -1) {
\r
76 sball_initialized = 1;
\r
79 void fgSpaceballClose(void)
\r
81 #if TARGET_HOST_POSIX_X11
\r
86 int fgHasSpaceball(void)
\r
88 if(!sball_initialized) {
\r
89 fgInitialiseSpaceball();
\r
90 if(!sball_initialized) {
\r
91 fgWarning("fgInitialiseSpaceball failed\n");
\r
96 #if TARGET_HOST_POSIX_X11
\r
97 /* XXX this function should somehow query the driver if there's a device
\r
98 * plugged in, as opposed to just checking if there's a driver to talk to.
\r
100 return spnav_fd() == -1 ? 0 : 1;
\r
106 int fgSpaceballNumButtons(void)
\r
108 if(!sball_initialized) {
\r
109 fgInitialiseSpaceball();
\r
110 if(!sball_initialized) {
\r
111 fgWarning("fgInitialiseSpaceball failed\n");
\r
116 #if TARGET_HOST_POSIX_X11
\r
117 return 2; /* TODO implement this properly */
\r
123 void fgSpaceballSetWindow(SFG_Window *window)
\r
125 if(!sball_initialized) {
\r
126 fgInitialiseSpaceball();
\r
127 if(!sball_initialized) {
\r
132 #if TARGET_HOST_POSIX_X11
\r
133 if(spnav_win != window) {
\r
134 spnav_x11_window(window->Window.Handle);
\r
135 spnav_win = window;
\r
141 #if TARGET_HOST_POSIX_X11
\r
142 int fgIsSpaceballXEvent(const XEvent *xev)
\r
146 if(spnav_win != fgStructure.CurrentWindow) {
\r
147 /* this will also initialize spaceball if needed (first call) */
\r
148 fgSpaceballSetWindow(fgStructure.CurrentWindow);
\r
151 if(!sball_initialized) {
\r
155 return spnav_x11_event(xev, &sev);
\r
158 void fgSpaceballHandleXEvent(const XEvent *xev)
\r
162 if(!sball_initialized) {
\r
163 fgInitialiseSpaceball();
\r
164 if(!sball_initialized) {
\r
169 if(spnav_x11_event(xev, &sev)) {
\r
171 case SPNAV_EVENT_MOTION:
\r
172 if(sev.motion.x | sev.motion.y | sev.motion.z) {
\r
173 INVOKE_WCB(*spnav_win, SpaceMotion, (sev.motion.x, sev.motion.y, sev.motion.z));
\r
175 if(sev.motion.rx | sev.motion.ry | sev.motion.rz) {
\r
176 INVOKE_WCB(*spnav_win, SpaceRotation, (sev.motion.rx, sev.motion.ry, sev.motion.rz));
\r
178 spnav_remove_events(SPNAV_EVENT_MOTION);
\r
181 case SPNAV_EVENT_BUTTON:
\r
182 INVOKE_WCB(*spnav_win, SpaceButton, (sev.button.bnum, sev.button.press ? GLUT_DOWN : GLUT_UP));
\r
192 The following code is part of libspnav, part of the spacenav project (spacenav.sf.net)
\r
193 Copyright (C) 2007-2009 John Tsiombikas <nuclear@member.fsf.org>
\r
195 Redistribution and use in source and binary forms, with or without
\r
196 modification, are permitted provided that the following conditions are met:
\r
198 1. Redistributions of source code must retain the above copyright notice, this
\r
199 list of conditions and the following disclaimer.
\r
200 2. Redistributions in binary form must reproduce the above copyright notice,
\r
201 this list of conditions and the following disclaimer in the documentation
\r
202 and/or other materials provided with the distribution.
\r
203 3. The name of the author may not be used to endorse or promote products
\r
204 derived from this software without specific prior written permission.
\r
206 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
\r
207 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
\r
208 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
\r
209 EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
\r
210 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
\r
211 OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
\r
212 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
\r
213 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
\r
214 IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
\r
218 #include <stdlib.h>
\r
219 #include <string.h>
\r
225 #include <X11/Xlib.h>
\r
226 #include <X11/Xutil.h>
\r
228 static Window get_daemon_window(Display *dpy);
\r
229 static int catch_badwin(Display *dpy, XErrorEvent *err);
\r
231 static Display *dpy;
\r
232 static Window app_win;
\r
233 static Atom motion_event, button_press_event, button_release_event, command_event;
\r
236 CMD_APP_WINDOW = 27695,
\r
240 #define IS_OPEN dpy
\r
242 struct event_node {
\r
244 struct event_node *next;
\r
247 static int spnav_x11_open(Display *display, Window win)
\r
255 motion_event = XInternAtom(dpy, "MotionEvent", True);
\r
256 button_press_event = XInternAtom(dpy, "ButtonPressEvent", True);
\r
257 button_release_event = XInternAtom(dpy, "ButtonReleaseEvent", True);
\r
258 command_event = XInternAtom(dpy, "CommandEvent", True);
\r
260 if(!motion_event || !button_press_event || !button_release_event || !command_event) {
\r
262 return -1; /* daemon not started */
\r
265 if(spnav_x11_window(win) == -1) {
\r
267 return -1; /* daemon not started */
\r
274 static int spnav_close(void)
\r
277 spnav_x11_window(DefaultRootWindow(dpy));
\r
285 static int spnav_x11_window(Window win)
\r
287 int (*prev_xerr_handler)(Display*, XErrorEvent*);
\r
295 if(!(daemon_win = get_daemon_window(dpy))) {
\r
299 prev_xerr_handler = XSetErrorHandler(catch_badwin);
\r
301 xev.type = ClientMessage;
\r
302 xev.xclient.send_event = False;
\r
303 xev.xclient.display = dpy;
\r
304 xev.xclient.window = win;
\r
305 xev.xclient.message_type = command_event;
\r
306 xev.xclient.format = 16;
\r
307 xev.xclient.data.s[0] = ((unsigned int)win & 0xffff0000) >> 16;
\r
308 xev.xclient.data.s[1] = (unsigned int)win & 0xffff;
\r
309 xev.xclient.data.s[2] = CMD_APP_WINDOW;
\r
311 XSendEvent(dpy, daemon_win, False, 0, &xev);
\r
314 XSetErrorHandler(prev_xerr_handler);
\r
318 static int spnav_fd(void)
\r
321 return ConnectionNumber(dpy);
\r
326 /*static int spnav_wait_event(spnav_event *event)
\r
331 XNextEvent(dpy, &xev);
\r
333 if(spnav_x11_event(&xev, event) > 0) {
\r
334 return event->type;
\r
341 static int spnav_poll_event(spnav_event *event)
\r
344 if(XPending(dpy)) {
\r
346 XNextEvent(dpy, &xev);
\r
348 return spnav_x11_event(&xev, event);
\r
354 static Bool match_events(Display *dpy, XEvent *xev, char *arg)
\r
356 int evtype = *(int*)arg;
\r
358 if(xev->type != ClientMessage) {
\r
362 if(xev->xclient.message_type == motion_event) {
\r
363 return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False;
\r
365 if(xev->xclient.message_type == button_press_event ||
\r
366 xev->xclient.message_type == button_release_event) {
\r
367 return !evtype || evtype == SPNAV_EVENT_BUTTON ? True : False;
\r
372 static int spnav_remove_events(int type)
\r
379 while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) {
\r
387 static int spnav_x11_event(const XEvent *xev, spnav_event *event)
\r
392 if(xev->type != ClientMessage) {
\r
396 xmsg_type = xev->xclient.message_type;
\r
398 if(xmsg_type != motion_event && xmsg_type != button_press_event &&
\r
399 xmsg_type != button_release_event) {
\r
403 if(xmsg_type == motion_event) {
\r
404 event->type = SPNAV_EVENT_MOTION;
\r
405 event->motion.data = &event->motion.x;
\r
407 for(i=0; i<6; i++) {
\r
408 event->motion.data[i] = xev->xclient.data.s[i + 2];
\r
410 event->motion.period = xev->xclient.data.s[8];
\r
412 event->type = SPNAV_EVENT_BUTTON;
\r
413 event->button.press = xmsg_type == button_press_event ? 1 : 0;
\r
414 event->button.bnum = xev->xclient.data.s[2];
\r
416 return event->type;
\r
420 static Window get_daemon_window(Display *dpy)
\r
422 Window win, root_win;
\r
423 XTextProperty wname;
\r
426 unsigned long nitems, bytes_after;
\r
427 unsigned char *prop;
\r
429 root_win = DefaultRootWindow(dpy);
\r
431 XGetWindowProperty(dpy, root_win, command_event, 0, 1, False, AnyPropertyType, &type, &fmt, &nitems, &bytes_after, &prop);
\r
436 win = *(Window*)prop;
\r
439 if(!XGetWMName(dpy, win, &wname) || strcmp("Magellan Window", (char*)wname.value) != 0) {
\r
446 static int catch_badwin(Display *dpy, XErrorEvent *err)
\r
450 if(err->error_code == BadWindow) {
\r
453 XGetErrorText(dpy, err->error_code, buf, sizeof buf);
\r
454 fprintf(stderr, "Caught unexpected X error: %s\n", buf);
\r
459 #endif /* TARGET_HOST_POSIX_X11 */
\r