Removing a set of deprecated joystick functions from "freeglutdll.def" per e-mail...
[freeglut] / src / freeglut_spaceball.c
1 /* Spaceball support for Linux.\r
2  * Written by John Tsiombikas <nuclear@member.fsf.org>\r
3  *\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
8  */\r
9 \r
10 #include <GL/freeglut.h>\r
11 #include "freeglut_internal.h"\r
12 \r
13 #if TARGET_HOST_POSIX_X11\r
14 #include <X11/Xlib.h>\r
15 \r
16 enum {\r
17     SPNAV_EVENT_ANY,  /* used by spnav_remove_events() */\r
18     SPNAV_EVENT_MOTION,\r
19     SPNAV_EVENT_BUTTON  /* includes both press and release */\r
20 };\r
21 \r
22 struct spnav_event_motion {\r
23     int type;\r
24     int x, y, z;\r
25     int rx, ry, rz;\r
26     unsigned int period;\r
27     int *data;\r
28 };\r
29 \r
30 struct spnav_event_button {\r
31     int type;\r
32     int press;\r
33     int bnum;\r
34 };\r
35 \r
36 typedef union spnav_event {\r
37     int type;\r
38     struct spnav_event_motion motion;\r
39     struct spnav_event_button button;\r
40 } spnav_event;\r
41 \r
42 \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
49 \r
50 static SFG_Window *spnav_win;\r
51 #endif\r
52 \r
53 static int sball_initialized;\r
54 \r
55 \r
56 void fgInitialiseSpaceball(void)\r
57 {\r
58     if(sball_initialized) {\r
59         return;\r
60     }\r
61 \r
62 #if TARGET_HOST_POSIX_X11\r
63     {\r
64         Window w;\r
65 \r
66         if(!fgStructure.CurrentWindow)\r
67             return;\r
68 \r
69         w = fgStructure.CurrentWindow->Window.Handle;\r
70         if(spnav_x11_open(fgDisplay.Display, w) == -1) {\r
71             return;\r
72         }\r
73     }\r
74 #endif\r
75 \r
76     sball_initialized = 1;\r
77 }\r
78 \r
79 void fgSpaceballClose(void)\r
80 {\r
81 #if TARGET_HOST_POSIX_X11\r
82     spnav_close();\r
83 #endif\r
84 }\r
85 \r
86 int fgHasSpaceball(void)\r
87 {\r
88     if(!sball_initialized) {\r
89         fgInitialiseSpaceball();\r
90         if(!sball_initialized) {\r
91             fgWarning("fgInitialiseSpaceball failed\n");\r
92             return 0;\r
93         }\r
94     }\r
95 \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
99      */\r
100     return spnav_fd() == -1 ? 0 : 1;\r
101 #else\r
102     return 0;\r
103 #endif\r
104 }\r
105 \r
106 int fgSpaceballNumButtons(void)\r
107 {\r
108     if(!sball_initialized) {\r
109         fgInitialiseSpaceball();\r
110         if(!sball_initialized) {\r
111             fgWarning("fgInitialiseSpaceball failed\n");\r
112             return 0;\r
113         }\r
114     }\r
115 \r
116 #if TARGET_HOST_POSIX_X11\r
117     return 2;  /* TODO implement this properly */\r
118 #else\r
119     return 0;\r
120 #endif\r
121 }\r
122 \r
123 void fgSpaceballSetWindow(SFG_Window *window)\r
124 {\r
125     if(!sball_initialized) {\r
126         fgInitialiseSpaceball();\r
127         if(!sball_initialized) {\r
128             return;\r
129         }\r
130     }\r
131 \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
136     }\r
137 #endif\r
138 }\r
139 \r
140 \r
141 #if TARGET_HOST_POSIX_X11\r
142 int fgIsSpaceballXEvent(const XEvent *xev)\r
143 {\r
144     spnav_event sev;\r
145 \r
146     if(!sball_initialized) {\r
147         fgInitialiseSpaceball();\r
148         if(!sball_initialized) {\r
149             return 0;\r
150         }\r
151     }\r
152 \r
153     return spnav_x11_event(xev, &sev);\r
154 }\r
155 \r
156 void fgSpaceballHandleXEvent(const XEvent *xev)\r
157 {\r
158     spnav_event sev;\r
159 \r
160     if(!sball_initialized) {\r
161         fgInitialiseSpaceball();\r
162         if(!sball_initialized) {\r
163             return;\r
164         }\r
165     }\r
166 \r
167     if(spnav_x11_event(xev, &sev)) {\r
168         switch(sev.type) {\r
169         case SPNAV_EVENT_MOTION:\r
170             if(sev.motion.x | sev.motion.y | sev.motion.z) {\r
171                 INVOKE_WCB(*spnav_win, SpaceMotion, (sev.motion.x, sev.motion.y, sev.motion.z));\r
172             }\r
173             if(sev.motion.rx | sev.motion.ry | sev.motion.rz) {\r
174                 INVOKE_WCB(*spnav_win, SpaceRotation, (sev.motion.rx, sev.motion.ry, sev.motion.rz));\r
175             }\r
176             spnav_remove_events(SPNAV_EVENT_MOTION);\r
177             break;\r
178 \r
179         case SPNAV_EVENT_BUTTON:\r
180             INVOKE_WCB(*spnav_win, SpaceButton, (sev.button.bnum, sev.button.press ? GLUT_DOWN : GLUT_UP));\r
181             break;\r
182 \r
183         default:\r
184             break;\r
185         }\r
186     }\r
187 }\r
188 \r
189 /*\r
190 The following code is part of libspnav, part of the spacenav project (spacenav.sf.net)\r
191 Copyright (C) 2007-2009 John Tsiombikas <nuclear@member.fsf.org>\r
192 \r
193 Redistribution and use in source and binary forms, with or without\r
194 modification, are permitted provided that the following conditions are met:\r
195 \r
196 1. Redistributions of source code must retain the above copyright notice, this\r
197    list of conditions and the following disclaimer.\r
198 2. Redistributions in binary form must reproduce the above copyright notice,\r
199    this list of conditions and the following disclaimer in the documentation\r
200    and/or other materials provided with the distribution.\r
201 3. The name of the author may not be used to endorse or promote products\r
202    derived from this software without specific prior written permission.\r
203 \r
204 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED\r
205 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
206 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO\r
207 EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\r
208 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT\r
209 OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
210 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r
211 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\r
212 IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY\r
213 OF SUCH DAMAGE.\r
214 */\r
215 #include <stdio.h>\r
216 #include <stdlib.h>\r
217 #include <string.h>\r
218 #include <errno.h>\r
219 \r
220 #include <X11/Xlib.h>\r
221 #include <X11/Xutil.h>\r
222 \r
223 static Window get_daemon_window(Display *dpy);\r
224 static int catch_badwin(Display *dpy, XErrorEvent *err);\r
225 \r
226 static Display *dpy;\r
227 static Window app_win;\r
228 static Atom motion_event, button_press_event, button_release_event, command_event;\r
229 \r
230 enum {\r
231   CMD_APP_WINDOW = 27695,\r
232   CMD_APP_SENS\r
233 };\r
234 \r
235 #define IS_OPEN    dpy\r
236 \r
237 struct event_node {\r
238   spnav_event event;\r
239   struct event_node *next;\r
240 };\r
241 \r
242 static int spnav_x11_open(Display *display, Window win)\r
243 {\r
244   if(IS_OPEN) {\r
245     return -1;\r
246   }\r
247 \r
248   dpy = display;\r
249 \r
250   motion_event = XInternAtom(dpy, "MotionEvent", True);\r
251   button_press_event = XInternAtom(dpy, "ButtonPressEvent", True);\r
252   button_release_event = XInternAtom(dpy, "ButtonReleaseEvent", True);\r
253   command_event = XInternAtom(dpy, "CommandEvent", True);\r
254 \r
255   if(!motion_event || !button_press_event || !button_release_event || !command_event) {\r
256     dpy = 0;\r
257     return -1;  /* daemon not started */\r
258   }\r
259 \r
260   if(spnav_x11_window(win) == -1) {\r
261     dpy = 0;\r
262     return -1;  /* daemon not started */\r
263   }\r
264 \r
265   app_win = win;\r
266   return 0;\r
267 }\r
268 \r
269 static int spnav_close(void)\r
270 {\r
271   if(dpy) {\r
272     spnav_x11_window(DefaultRootWindow(dpy));\r
273     app_win = 0;\r
274     dpy = 0;\r
275     return 0;\r
276   }\r
277   return -1;\r
278 }\r
279 \r
280 static int spnav_x11_window(Window win)\r
281 {\r
282   int (*prev_xerr_handler)(Display*, XErrorEvent*);\r
283   XEvent xev;\r
284   Window daemon_win;\r
285 \r
286   if(!IS_OPEN) {\r
287     return -1;\r
288   }\r
289 \r
290   if(!(daemon_win = get_daemon_window(dpy))) {\r
291     return -1;\r
292   }\r
293 \r
294   prev_xerr_handler = XSetErrorHandler(catch_badwin);\r
295 \r
296   xev.type = ClientMessage;\r
297   xev.xclient.send_event = False;\r
298   xev.xclient.display = dpy;\r
299   xev.xclient.window = win;\r
300   xev.xclient.message_type = command_event;\r
301   xev.xclient.format = 16;\r
302   xev.xclient.data.s[0] = ((unsigned int)win & 0xffff0000) >> 16;\r
303   xev.xclient.data.s[1] = (unsigned int)win & 0xffff;\r
304   xev.xclient.data.s[2] = CMD_APP_WINDOW;\r
305 \r
306   XSendEvent(dpy, daemon_win, False, 0, &xev);\r
307   XSync(dpy, False);\r
308 \r
309   XSetErrorHandler(prev_xerr_handler);\r
310   return 0;\r
311 }\r
312 \r
313 static int spnav_fd(void)\r
314 {\r
315   if(dpy) {\r
316     return ConnectionNumber(dpy);\r
317   }\r
318   return -1;\r
319 }\r
320 \r
321 /*static int spnav_wait_event(spnav_event *event)\r
322 {\r
323   if(dpy) {\r
324     for(;;) {\r
325       XEvent xev;\r
326       XNextEvent(dpy, &xev);\r
327 \r
328       if(spnav_x11_event(&xev, event) > 0) {\r
329         return event->type;\r
330       }\r
331     }\r
332   }\r
333   return 0;\r
334 }\r
335 \r
336 static int spnav_poll_event(spnav_event *event)\r
337 {\r
338   if(dpy) {\r
339     if(XPending(dpy)) {\r
340       XEvent xev;\r
341       XNextEvent(dpy, &xev);\r
342 \r
343       return spnav_x11_event(&xev, event);\r
344     }\r
345   }\r
346   return 0;\r
347 }*/\r
348 \r
349 static Bool match_events(Display *dpy, XEvent *xev, char *arg)\r
350 {\r
351   int evtype = *(int*)arg;\r
352 \r
353   if(xev->type != ClientMessage) {\r
354     return False;\r
355   }\r
356 \r
357   if(xev->xclient.message_type == motion_event) {\r
358     return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False;\r
359   }\r
360   if(xev->xclient.message_type == button_press_event ||\r
361       xev->xclient.message_type == button_release_event) {\r
362     return !evtype || evtype == SPNAV_EVENT_BUTTON ? True : False;\r
363   }\r
364   return False;\r
365 }\r
366 \r
367 static int spnav_remove_events(int type)\r
368 {\r
369   int rm_count = 0;\r
370 \r
371   if(dpy) {\r
372     XEvent xev;\r
373 \r
374     while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) {\r
375       rm_count++;\r
376     }\r
377     return rm_count;\r
378   }\r
379   return 0;\r
380 }\r
381 \r
382 static int spnav_x11_event(const XEvent *xev, spnav_event *event)\r
383 {\r
384   int i;\r
385   int xmsg_type;\r
386 \r
387   if(xev->type != ClientMessage) {\r
388     return 0;\r
389   }\r
390 \r
391   xmsg_type = xev->xclient.message_type;\r
392 \r
393   if(xmsg_type != motion_event && xmsg_type != button_press_event &&\r
394       xmsg_type != button_release_event) {\r
395     return 0;\r
396   }\r
397 \r
398   if(xmsg_type == motion_event) {\r
399     event->type = SPNAV_EVENT_MOTION;\r
400     event->motion.data = &event->motion.x;\r
401 \r
402     for(i=0; i<6; i++) {\r
403       event->motion.data[i] = xev->xclient.data.s[i + 2];\r
404     }\r
405     event->motion.period = xev->xclient.data.s[8];\r
406   } else {\r
407     event->type = SPNAV_EVENT_BUTTON;\r
408     event->button.press = xmsg_type == button_press_event ? 1 : 0;\r
409     event->button.bnum = xev->xclient.data.s[2];\r
410   }\r
411   return event->type;\r
412 }\r
413 \r
414 \r
415 static Window get_daemon_window(Display *dpy)\r
416 {\r
417   Window win, root_win;\r
418   XTextProperty wname;\r
419   Atom type;\r
420   int fmt;\r
421   unsigned long nitems, bytes_after;\r
422   unsigned char *prop;\r
423 \r
424   root_win = DefaultRootWindow(dpy);\r
425 \r
426   XGetWindowProperty(dpy, root_win, command_event, 0, 1, False, AnyPropertyType, &type, &fmt, &nitems, &bytes_after, &prop);\r
427   if(!prop) {\r
428     return 0;\r
429   }\r
430 \r
431   win = *(Window*)prop;\r
432   XFree(prop);\r
433 \r
434   if(!XGetWMName(dpy, win, &wname) || strcmp("Magellan Window", (char*)wname.value) != 0) {\r
435     return 0;\r
436   }\r
437 \r
438   return win;\r
439 }\r
440 \r
441 static int catch_badwin(Display *dpy, XErrorEvent *err)\r
442 {\r
443   char buf[256];\r
444 \r
445   if(err->error_code == BadWindow) {\r
446     /* do nothing? */\r
447   } else {\r
448     XGetErrorText(dpy, err->error_code, buf, sizeof buf);\r
449     fprintf(stderr, "Caught unexpected X error: %s\n", buf);\r
450   }\r
451   return 0;\r
452 }\r
453 \r
454 #endif  /* TARGET_HOST_POSIX_X11 */\r