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