Fixed namespace pollution due to a number of global symbols missing an fg prefix
[freeglut] / src / x11 / fg_spaceball_x11.c
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
5  *
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.
10  */
11
12 #include <GL/freeglut.h>
13 #include "../fg_internal.h"
14
15 #include <X11/Xlib.h>
16
17 extern int fg_sball_initialized;
18
19 enum {
20     SPNAV_EVENT_ANY,  /* used by spnav_remove_events() */
21     SPNAV_EVENT_MOTION,
22     SPNAV_EVENT_BUTTON  /* includes both press and release */
23 };
24
25 struct spnav_event_motion {
26     int type;
27     int x, y, z;
28     int rx, ry, rz;
29     unsigned int period;
30     int *data;
31 };
32
33 struct spnav_event_button {
34     int type;
35     int press;
36     int bnum;
37 };
38
39 typedef union spnav_event {
40     int type;
41     struct spnav_event_motion motion;
42     struct spnav_event_button button;
43 } spnav_event;
44
45
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);
52
53 static SFG_Window *spnav_win;
54
55 void fgPlatformInitializeSpaceball(void)
56 {
57     Window w;
58
59     fg_sball_initialized = 1;
60     if(!fgStructure.CurrentWindow)
61     {
62         fg_sball_initialized = -1;
63         return;
64     }
65
66     w = fgStructure.CurrentWindow->Window.Handle;
67     if(spnav_x11_open(fgDisplay.pDisplay.Display, w) == -1)
68     {
69         fg_sball_initialized = -1;
70         return;
71     }
72 }
73
74 void fgPlatformSpaceballClose(void) 
75 {
76     spnav_close();
77 }
78
79 int fgPlatformHasSpaceball(void) 
80 {
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.
83      */
84     return spnav_fd() == -1 ? 0 : 1;
85 }
86
87 int fgPlatformSpaceballNumButtons(void) {
88     return 2;
89 }
90
91 void fgPlatformSpaceballSetWindow(SFG_Window *window) 
92 {
93        if(spnav_win != window) {
94         spnav_x11_window(window->Window.Handle);
95         spnav_win = window;
96     }
97 }
98
99 int fgIsSpaceballXEvent(const XEvent *xev)
100 {
101     spnav_event sev;
102
103     if(spnav_win != fgStructure.CurrentWindow) {
104         /* this will also initialize spaceball if needed (first call) */
105         fgSpaceballSetWindow(fgStructure.CurrentWindow);
106     }
107
108     if(fg_sball_initialized != 1) {
109         return 0;
110     }
111
112     return spnav_x11_event(xev, &sev);
113 }
114
115 void fgSpaceballHandleXEvent(const XEvent *xev)
116 {
117     spnav_event sev;
118
119     if(fg_sball_initialized == 0) {
120         fgInitialiseSpaceball();
121         if(fg_sball_initialized != 1) {
122             return;
123         }
124     }
125
126     if(spnav_x11_event(xev, &sev)) {
127         switch(sev.type) {
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));
131             }
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));
134             }
135             spnav_remove_events(SPNAV_EVENT_MOTION);
136             break;
137
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));
141             break;
142
143         default:
144             break;
145         }
146     }
147 }
148
149 /*
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>
152
153 Redistribution and use in source and binary forms, with or without
154 modification, are permitted provided that the following conditions are met:
155
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.
163
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
173 OF SUCH DAMAGE.
174 */
175 #include <stdio.h>
176 #include <stdlib.h>
177 #include <string.h>
178 #include <errno.h>
179
180 #include <X11/Xlib.h>
181 #include <X11/Xutil.h>
182
183 static Window get_daemon_window(Display *dpy);
184 static int catch_badwin(Display *dpy, XErrorEvent *err);
185
186 static Display *dpy;
187 static Window app_win;
188 static Atom motion_event, button_press_event, button_release_event, command_event;
189
190 enum {
191   CMD_APP_WINDOW = 27695,
192   CMD_APP_SENS
193 };
194
195 #define IS_OPEN    dpy
196
197 struct event_node {
198   spnav_event event;
199   struct event_node *next;
200 };
201
202 static int spnav_x11_open(Display *display, Window win)
203 {
204   if(IS_OPEN) {
205     return -1;
206   }
207
208   dpy = display;
209
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);
214
215   if(!motion_event || !button_press_event || !button_release_event || !command_event) {
216     dpy = 0;
217     return -1;  /* daemon not started */
218   }
219
220   if(spnav_x11_window(win) == -1) {
221     dpy = 0;
222     return -1;  /* daemon not started */
223   }
224
225   app_win = win;
226   return 0;
227 }
228
229 static int spnav_close(void)
230 {
231   if(dpy) {
232     spnav_x11_window(DefaultRootWindow(dpy));
233     app_win = 0;
234     dpy = 0;
235     return 0;
236   }
237   return -1;
238 }
239
240 static int spnav_x11_window(Window win)
241 {
242   int (*prev_xerr_handler)(Display*, XErrorEvent*);
243   XEvent xev;
244   Window daemon_win;
245
246   if(!IS_OPEN) {
247     return -1;
248   }
249
250   if(!(daemon_win = get_daemon_window(dpy))) {
251     return -1;
252   }
253
254   prev_xerr_handler = XSetErrorHandler(catch_badwin);
255
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;
265
266   XSendEvent(dpy, daemon_win, False, 0, &xev);
267   XSync(dpy, False);
268
269   XSetErrorHandler(prev_xerr_handler);
270   return 0;
271 }
272
273 static int spnav_fd(void)
274 {
275   if(dpy) {
276     return ConnectionNumber(dpy);
277   }
278   return -1;
279 }
280
281 /*static int spnav_wait_event(spnav_event *event)
282 {
283   if(dpy) {
284     for(;;) {
285       XEvent xev;
286       XNextEvent(dpy, &xev);
287
288       if(spnav_x11_event(&xev, event) > 0) {
289         return event->type;
290       }
291     }
292   }
293   return 0;
294 }
295
296 static int spnav_poll_event(spnav_event *event)
297 {
298   if(dpy) {
299     if(XPending(dpy)) {
300       XEvent xev;
301       XNextEvent(dpy, &xev);
302
303       return spnav_x11_event(&xev, event);
304     }
305   }
306   return 0;
307 }*/
308
309 static Bool match_events(Display *dpy, XEvent *xev, char *arg)
310 {
311   int evtype = *(int*)arg;
312
313   if(xev->type != ClientMessage) {
314     return False;
315   }
316
317   if(xev->xclient.message_type == motion_event) {
318     return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False;
319   }
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;
323   }
324   return False;
325 }
326
327 static int spnav_remove_events(int type)
328 {
329   int rm_count = 0;
330
331   if(dpy) {
332     XEvent xev;
333
334     while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) {
335       rm_count++;
336     }
337     return rm_count;
338   }
339   return 0;
340 }
341
342 static int spnav_x11_event(const XEvent *xev, spnav_event *event)
343 {
344   int i;
345   int xmsg_type;
346
347   if(xev->type != ClientMessage) {
348     return 0;
349   }
350
351   xmsg_type = xev->xclient.message_type;
352
353   if(xmsg_type != motion_event && xmsg_type != button_press_event &&
354       xmsg_type != button_release_event) {
355     return 0;
356   }
357
358   if(xmsg_type == motion_event) {
359     event->type = SPNAV_EVENT_MOTION;
360     event->motion.data = &event->motion.x;
361
362     for(i=0; i<6; i++) {
363       event->motion.data[i] = xev->xclient.data.s[i + 2];
364     }
365     event->motion.period = xev->xclient.data.s[8];
366   } else {
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];
370   }
371   return event->type;
372 }
373
374
375 static Window get_daemon_window(Display *dpy)
376 {
377   Window win, root_win;
378   XTextProperty wname;
379   Atom type;
380   int fmt;
381   unsigned long nitems, bytes_after;
382   unsigned char *prop;
383
384   root_win = DefaultRootWindow(dpy);
385
386   XGetWindowProperty(dpy, root_win, command_event, 0, 1, False, AnyPropertyType, &type, &fmt, &nitems, &bytes_after, &prop);
387   if(!prop) {
388     return 0;
389   }
390
391   win = *(Window*)prop;
392   XFree(prop);
393
394   if(!XGetWMName(dpy, win, &wname) || strcmp("Magellan Window", (char*)wname.value) != 0) {
395     return 0;
396   }
397
398   return win;
399 }
400
401 static int catch_badwin(Display *dpy, XErrorEvent *err)
402 {
403   char buf[256];
404
405   if(err->error_code == BadWindow) {
406     /* do nothing? */
407   } else {
408     XGetErrorText(dpy, err->error_code, buf, sizeof buf);
409     fprintf(stderr, "Caught unexpected X error: %s\n", buf);
410   }
411   return 0;
412 }
413